diff options
| author | emersion <contact@emersion.fr> | 2018-08-02 23:49:25 +0100 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-08-02 23:49:25 +0100 | 
| commit | 3a54e2291c017397ceff60511c29fe70d229bc8b (patch) | |
| tree | d340b7776f945462f5ecffc830ada4d5fbe82f51 /sway/tree | |
| parent | c35a34262f8da368f65d37f811a2264647e0dae6 (diff) | |
| parent | e07da5fc5c6ac5c186662b56b08ca71531119de0 (diff) | |
| download | sway-3a54e2291c017397ceff60511c29fe70d229bc8b.tar.xz | |
Merge branch 'master' into wlr-gamma-control
Diffstat (limited to 'sway/tree')
| -rw-r--r-- | sway/tree/arrange.c | 18 | ||||
| -rw-r--r-- | sway/tree/container.c | 369 | ||||
| -rw-r--r-- | sway/tree/layout.c | 122 | ||||
| -rw-r--r-- | sway/tree/output.c | 2 | ||||
| -rw-r--r-- | sway/tree/view.c | 315 | ||||
| -rw-r--r-- | sway/tree/workspace.c | 268 | 
6 files changed, 762 insertions, 332 deletions
| diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index 533cf71c..5452b13c 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c @@ -220,8 +220,22 @@ static void arrange_workspace(struct sway_container *workspace) {  	container_set_dirty(workspace);  	wlr_log(WLR_DEBUG, "Arranging workspace '%s' at %f, %f", workspace->name,  			workspace->x, workspace->y); -	arrange_floating(workspace->sway_workspace->floating); -	arrange_children_of(workspace); +	if (workspace->sway_workspace->fullscreen) { +		struct sway_container *fs = workspace->sway_workspace->fullscreen; +		fs->x = workspace->parent->x; +		fs->y = workspace->parent->y; +		fs->width = workspace->parent->width; +		fs->height = workspace->parent->height; +		if (fs->type == C_VIEW) { +			view_autoconfigure(fs->sway_view); +		} else { +			arrange_children_of(fs); +		} +		container_set_dirty(fs); +	} else { +		arrange_floating(workspace->sway_workspace->floating); +		arrange_children_of(workspace); +	}  }  static void arrange_output(struct sway_container *output) { diff --git a/sway/tree/container.c b/sway/tree/container.c index 4dbfbb29..46c54e2d 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -17,6 +17,7 @@  #include "sway/input/seat.h"  #include "sway/ipc-server.h"  #include "sway/output.h" +#include "sway/scratchpad.h"  #include "sway/server.h"  #include "sway/tree/arrange.h"  #include "sway/tree/layout.h" @@ -61,13 +62,17 @@ void container_create_notify(struct sway_container *container) {  	// TODO send ipc event type based on the container type  	wl_signal_emit(&root_container.sway_root->events.new_container, container); -	if (container->type == C_VIEW || container->type == C_CONTAINER) { +	if (container->type == C_VIEW) {  		ipc_event_window(container, "new"); +	} else if (container->type == C_WORKSPACE) { +		ipc_event_workspace(NULL, container, "init");  	}  } -static void container_update_textures_recursive(struct sway_container *con) { -	container_update_title_textures(con); +void container_update_textures_recursive(struct sway_container *con) { +	if (con->type == C_CONTAINER || con->type == C_VIEW) { +		container_update_title_textures(con); +	}  	if (con->type == C_VIEW) {  		view_update_marks_textures(con->sway_view); @@ -76,6 +81,10 @@ static void container_update_textures_recursive(struct sway_container *con) {  			struct sway_container *child = con->children->items[i];  			container_update_textures_recursive(child);  		} + +		if (con->type == C_WORKSPACE) { +			container_update_textures_recursive(con->sway_workspace->floating); +		}  	}  } @@ -139,8 +148,6 @@ struct sway_container *container_create(enum sway_container_type type) {  static void container_workspace_free(struct sway_workspace *ws) {  	list_foreach(ws->output_priority, free);  	list_free(ws->output_priority); -	ws->floating->destroying = true; -	container_free(ws->floating);  	free(ws);  } @@ -193,6 +200,9 @@ void container_free(struct sway_container *cont) {  	free(cont);  } +static struct sway_container *container_destroy_noreaping( +		struct sway_container *con); +  static struct sway_container *container_workspace_destroy(  		struct sway_container *workspace) {  	if (!sway_assert(workspace, "cannot destroy null workspace")) { @@ -237,6 +247,8 @@ static struct sway_container *container_workspace_destroy(  		}  	} +	container_destroy_noreaping(workspace->sway_workspace->floating); +  	return output;  } @@ -271,7 +283,7 @@ static struct sway_container *container_output_destroy(  				container_remove_child(workspace);  				if (!workspace_is_empty(workspace)) {  					container_add_child(new_output, workspace); -					ipc_event_workspace(workspace, NULL, "move"); +					ipc_event_workspace(NULL, workspace, "move");  				} else {  					container_destroy(workspace);  				} @@ -309,7 +321,13 @@ static struct sway_container *container_destroy_noreaping(  	}  	wl_signal_emit(&con->events.destroy, con); -	ipc_event_window(con, "close"); + +	// emit IPC event +	if (con->type == C_VIEW) { +		ipc_event_window(con, "close"); +	} else if (con->type == C_WORKSPACE) { +		ipc_event_workspace(NULL, con, "empty"); +	}  	// The below functions move their children to somewhere else.  	if (con->type == C_OUTPUT) { @@ -323,9 +341,15 @@ static struct sway_container *container_destroy_noreaping(  		}  	} +	container_end_mouse_operation(con); +  	con->destroying = true;  	container_set_dirty(con); +	if (con->scratchpad) { +		scratchpad_remove_container(con); +	} +  	if (!con->parent) {  		return NULL;  	} @@ -398,6 +422,10 @@ struct sway_container *container_flatten(struct sway_container *container) {   * This function just wraps container_destroy_noreaping(), then does reaping.   */  struct sway_container *container_destroy(struct sway_container *con) { +	if (con->is_fullscreen) { +		struct sway_container *ws = container_parent(con, C_WORKSPACE); +		ws->sway_workspace->fullscreen = NULL; +	}  	struct sway_container *parent = container_destroy_noreaping(con);  	if (!parent) { @@ -507,7 +535,7 @@ struct sway_container *container_parent(struct sway_container *container,  	return container;  } -static struct sway_container *container_at_view(struct sway_container *swayc, +struct sway_container *container_at_view(struct sway_container *swayc,  		double lx, double ly,  		struct wlr_surface **surface, double *sx, double *sy) {  	if (!sway_assert(swayc->type == C_VIEW, "Expected a view")) { @@ -520,10 +548,12 @@ static struct sway_container *container_at_view(struct sway_container *swayc,  	double _sx, _sy;  	struct wlr_surface *_surface = NULL;  	switch (sview->type) { +#ifdef HAVE_XWAYLAND  	case SWAY_VIEW_XWAYLAND:  		_surface = wlr_surface_surface_at(sview->surface,  				view_sx, view_sy, &_sx, &_sy);  		break; +#endif  	case SWAY_VIEW_XDG_SHELL_V6:  		_surface = wlr_xdg_surface_v6_surface_at(  				sview->wlr_xdg_surface_v6, @@ -539,10 +569,15 @@ static struct sway_container *container_at_view(struct sway_container *swayc,  		*sx = _sx;  		*sy = _sy;  		*surface = _surface; +		return swayc;  	} -	return swayc; +	return NULL;  } +static struct sway_container *tiling_container_at( +		struct sway_container *con, double lx, double ly, +		struct wlr_surface **surface, double *sx, double *sy); +  /**   * container_at for a container with layout L_TABBED.   */ @@ -569,7 +604,7 @@ static struct sway_container *container_at_tabbed(struct sway_container *parent,  	// Surfaces  	struct sway_container *current = seat_get_active_child(seat, parent); -	return container_at(current, lx, ly, surface, sx, sy); +	return tiling_container_at(current, lx, ly, surface, sx, sy);  }  /** @@ -594,7 +629,7 @@ static struct sway_container *container_at_stacked(  	// Surfaces  	struct sway_container *current = seat_get_active_child(seat, parent); -	return container_at(current, lx, ly, surface, sx, sy); +	return tiling_container_at(current, lx, ly, surface, sx, sy);  }  /** @@ -612,45 +647,13 @@ static struct sway_container *container_at_linear(struct sway_container *parent,  			.height = child->height,  		};  		if (wlr_box_contains_point(&box, lx, ly)) { -			return container_at(child, lx, ly, surface, sx, sy); +			return tiling_container_at(child, lx, ly, surface, sx, sy);  		}  	}  	return NULL;  } -struct sway_container *container_at(struct sway_container *parent, -		double lx, double ly, -		struct wlr_surface **surface, double *sx, double *sy) { -	if (!sway_assert(parent->type >= C_WORKSPACE, -				"Expected workspace or deeper")) { -		return NULL; -	} -	if (parent->type == C_VIEW) { -		return container_at_view(parent, lx, ly, surface, sx, sy); -	} -	if (!parent->children->length) { -		return NULL; -	} - -	switch (parent->layout) { -	case L_HORIZ: -	case L_VERT: -		return container_at_linear(parent, lx, ly, surface, sx, sy); -	case L_TABBED: -		return container_at_tabbed(parent, lx, ly, surface, sx, sy); -	case L_STACKED: -		return container_at_stacked(parent, lx, ly, surface, sx, sy); -	case L_FLOATING: -		sway_assert(false, "Didn't expect to see floating here"); -		return NULL; -	case L_NONE: -		return NULL; -	} - -	return NULL; -} - -struct sway_container *floating_container_at(double lx, double ly, +static struct sway_container *floating_container_at(double lx, double ly,  		struct wlr_surface **surface, double *sx, double *sy) {  	for (int i = 0; i < root_container.children->length; ++i) {  		struct sway_container *output = root_container.children->items[i]; @@ -672,7 +675,8 @@ struct sway_container *floating_container_at(double lx, double ly,  					.height = floater->height,  				};  				if (wlr_box_contains_point(&box, lx, ly)) { -					return container_at(floater, lx, ly, surface, sx, sy); +					return tiling_container_at(floater, lx, ly, +							surface, sx, sy);  				}  			}  		} @@ -680,6 +684,90 @@ struct sway_container *floating_container_at(double lx, double ly,  	return NULL;  } +static struct sway_container *tiling_container_at( +		struct sway_container *con, double lx, double ly, +		struct wlr_surface **surface, double *sx, double *sy) { +	if (con->type == C_VIEW) { +		return container_at_view(con, lx, ly, surface, sx, sy); +	} +	if (!con->children->length) { +		return NULL; +	} + +	switch (con->layout) { +	case L_HORIZ: +	case L_VERT: +		return container_at_linear(con, lx, ly, surface, sx, sy); +	case L_TABBED: +		return container_at_tabbed(con, lx, ly, surface, sx, sy); +	case L_STACKED: +		return container_at_stacked(con, lx, ly, surface, sx, sy); +	case L_FLOATING: +		sway_assert(false, "Didn't expect to see floating here"); +		return NULL; +	case L_NONE: +		return NULL; +	} +	return NULL; +} + +static bool surface_is_popup(struct wlr_surface *surface) { +	if (wlr_surface_is_xdg_surface(surface)) { +		struct wlr_xdg_surface *xdg_surface = +			wlr_xdg_surface_from_wlr_surface(surface); +		while (xdg_surface) { +			if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { +				return true; +			} +			xdg_surface = xdg_surface->toplevel->parent; +		} +		return false; +	} + +	if (wlr_surface_is_xdg_surface_v6(surface)) { +		struct wlr_xdg_surface_v6 *xdg_surface_v6 = +			wlr_xdg_surface_v6_from_wlr_surface(surface); +		while (xdg_surface_v6) { +			if (xdg_surface_v6->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) { +				return true; +			} +			xdg_surface_v6 = xdg_surface_v6->toplevel->parent; +		} +		return false; +	} + +	return false; +} + +struct sway_container *container_at(struct sway_container *workspace, +		double lx, double ly, +		struct wlr_surface **surface, double *sx, double *sy) { +	if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { +		return NULL; +	} +	struct sway_container *c; +	// Focused view's popups +	struct sway_seat *seat = input_manager_current_seat(input_manager); +	struct sway_container *focus = +		seat_get_focus_inactive(seat, &root_container); +	if (focus && focus->type == C_VIEW) { +		container_at_view(focus, lx, ly, surface, sx, sy); +		if (*surface && surface_is_popup(*surface)) { +			return focus; +		} +		*surface = NULL; +	} +	// Floating +	if ((c = floating_container_at(lx, ly, surface, sx, sy))) { +		return c; +	} +	// Tiling +	if ((c = tiling_container_at(workspace, lx, ly, surface, sx, sy))) { +		return c; +	} +	return NULL; +} +  void container_for_each_descendant_dfs(struct sway_container *container,  		void (*f)(struct sway_container *container, void *data),  		void *data) { @@ -934,36 +1022,104 @@ size_t container_titlebar_height() {  	return config->font_height + TITLEBAR_V_PADDING * 2;  } +void container_init_floating(struct sway_container *con) { +	if (!sway_assert(con->type == C_VIEW || con->type == C_CONTAINER, +			"Expected a view or container")) { +		return; +	} +	struct sway_container *ws = container_parent(con, C_WORKSPACE); +	int min_width, min_height; +	int max_width, max_height; + +	if (config->floating_minimum_width == -1) { // no minimum +		min_width = 0; +	} else if (config->floating_minimum_width == 0) { // automatic +		min_width = 75; +	} else { +		min_width = config->floating_minimum_width; +	} + +	if (config->floating_minimum_height == -1) { // no minimum +		min_height = 0; +	} else if (config->floating_minimum_height == 0) { // automatic +		min_height = 50; +	} else { +		min_height = config->floating_minimum_height; +	} + +	if (config->floating_maximum_width == -1) { // no maximum +		max_width = INT_MAX; +	} else if (config->floating_maximum_width == 0) { // automatic +		max_width = ws->width * 0.6666; +	} else { +		max_width = config->floating_maximum_width; +	} + +	if (config->floating_maximum_height == -1) { // no maximum +		max_height = INT_MAX; +	} else if (config->floating_maximum_height == 0) { // automatic +		max_height = ws->height * 0.6666; +	} else { +		max_height = config->floating_maximum_height; +	} + +	if (con->type == C_CONTAINER) { +		con->width = max_width; +		con->height = max_height; +		con->x = ws->x + (ws->width - con->width) / 2; +		con->y = ws->y + (ws->height - con->height) / 2; +	} else { +		struct sway_view *view = con->sway_view; +		view->width = fmax(min_width, fmin(view->natural_width, max_width)); +		view->height = fmax(min_height, fmin(view->natural_height, max_height)); +		view->x = ws->x + (ws->width - view->width) / 2; +		view->y = ws->y + (ws->height - view->height) / 2; + +		// If the view's border is B_NONE then these properties are ignored. +		view->border_top = view->border_bottom = true; +		view->border_left = view->border_right = true; + +		container_set_geometry_from_floating_view(view->swayc); +	} +} +  void container_set_floating(struct sway_container *container, bool enable) {  	if (container_is_floating(container) == enable) {  		return;  	} -	struct sway_container *workspace = container_parent(container, C_WORKSPACE);  	struct sway_seat *seat = input_manager_current_seat(input_manager); +	struct sway_container *workspace = container_parent(container, C_WORKSPACE);  	if (enable) {  		container_remove_child(container);  		container_add_child(workspace->sway_workspace->floating, container); +		container_init_floating(container);  		if (container->type == C_VIEW) { -			view_init_floating(container->sway_view);  			view_set_tiled(container->sway_view, false);  		} -		seat_set_focus(seat, seat_get_focus_inactive(seat, container)); -		container_reap_empty_recursive(workspace);  	} else {  		// Returning to tiled +		if (container->scratchpad) { +			scratchpad_remove_container(container); +		}  		container_remove_child(container); -		container_add_child(workspace, container); +		struct sway_container *reference = +			seat_get_focus_inactive_tiling(seat, workspace); +		if (reference->type == C_VIEW) { +			reference = reference->parent; +		} +		container_add_child(reference, container);  		container->width = container->parent->width;  		container->height = container->parent->height;  		if (container->type == C_VIEW) {  			view_set_tiled(container->sway_view, true);  		}  		container->is_sticky = false; -		container_reap_empty_recursive(workspace->sway_workspace->floating);  	} +	container_end_mouse_operation(container); +  	ipc_event_window(container, "floating");  } @@ -1009,7 +1165,7 @@ void container_get_box(struct sway_container *container, struct wlr_box *box) {  /**   * Translate the container's position as well as all children.   */ -static void container_floating_translate(struct sway_container *con, +void container_floating_translate(struct sway_container *con,  		double x_amount, double y_amount) {  	con->x += x_amount;  	con->y += y_amount; @@ -1105,3 +1261,110 @@ static bool find_urgent_iterator(struct sway_container *con,  bool container_has_urgent_child(struct sway_container *container) {  	return container_find(container, find_urgent_iterator, NULL);  } + +void container_end_mouse_operation(struct sway_container *container) { +	struct sway_seat *seat; +	wl_list_for_each(seat, &input_manager->seats, link) { +		if (seat->op_container == container) { +			seat_end_mouse_operation(seat); +		} +	} +} + +static void set_fullscreen_iterator(struct sway_container *con, void *data) { +	if (con->type != C_VIEW) { +		return; +	} +	if (con->sway_view->impl->set_fullscreen) { +		bool *enable = data; +		con->sway_view->impl->set_fullscreen(con->sway_view, *enable); +	} +} + +void container_set_fullscreen(struct sway_container *container, bool enable) { +	if (container->is_fullscreen == enable) { +		return; +	} + +	struct sway_container *workspace = container_parent(container, C_WORKSPACE); +	if (enable && workspace->sway_workspace->fullscreen) { +		container_set_fullscreen(workspace->sway_workspace->fullscreen, false); +	} + +	container_for_each_descendant_dfs(container, +			set_fullscreen_iterator, &enable); + +	container->is_fullscreen = enable; + +	if (enable) { +		workspace->sway_workspace->fullscreen = container; +		container->saved_x = container->x; +		container->saved_y = container->y; +		container->saved_width = container->width; +		container->saved_height = container->height; + +		struct sway_seat *seat; +		struct sway_container *focus, *focus_ws; +		wl_list_for_each(seat, &input_manager->seats, link) { +			focus = seat_get_focus(seat); +			if (focus) { +				focus_ws = focus; +				if (focus_ws->type != C_WORKSPACE) { +					focus_ws = container_parent(focus_ws, C_WORKSPACE); +				} +				if (focus_ws == workspace) { +					seat_set_focus(seat, container); +				} +			} +		} +	} else { +		workspace->sway_workspace->fullscreen = NULL; +		if (container_is_floating(container)) { +			container->x = container->saved_x; +			container->y = container->saved_y; +			container->width = container->saved_width; +			container->height = container->saved_height; +		} else { +			container->width = container->saved_width; +			container->height = container->saved_height; +		} +	} + +	container_end_mouse_operation(container); + +	ipc_event_window(container, "fullscreen_mode"); +} + +bool container_is_floating_or_child(struct sway_container *container) { +	do { +		if (container->parent && container->parent->layout == L_FLOATING) { +			return true; +		} +		container = container->parent; +	} while (container && container->type != C_WORKSPACE); + +	return false; +} + +bool container_is_fullscreen_or_child(struct sway_container *container) { +	do { +		if (container->is_fullscreen) { +			return true; +		} +		container = container->parent; +	} while (container && container->type != C_WORKSPACE); + +	return false; +} + +struct sway_container *container_wrap_children(struct sway_container *parent) { +	struct sway_container *middle = container_create(C_CONTAINER); +	middle->layout = parent->layout; +	while (parent->children->length) { +		struct sway_container *child = parent->children->items[0]; +		container_remove_child(child); +		container_add_child(middle, child); +	} +	container_add_child(parent, middle); +	return middle; +} diff --git a/sway/tree/layout.c b/sway/tree/layout.c index 1f898f8a..1f82e534 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -6,6 +6,7 @@  #include <string.h>  #include <wlr/types/wlr_output.h>  #include <wlr/types/wlr_output_layout.h> +#include "config.h"  #include "sway/debug.h"  #include "sway/tree/arrange.h"  #include "sway/tree/container.h" @@ -39,9 +40,12 @@ void layout_init(void) {  	root_container.sway_root = calloc(1, sizeof(*root_container.sway_root));  	root_container.sway_root->output_layout = wlr_output_layout_create();  	wl_list_init(&root_container.sway_root->outputs); +#ifdef HAVE_XWAYLAND  	wl_list_init(&root_container.sway_root->xwayland_unmanaged); +#endif  	wl_list_init(&root_container.sway_root->drag_icons);  	wl_signal_init(&root_container.sway_root->events.new_container); +	root_container.sway_root->scratchpad = create_list();  	root_container.sway_root->output_layout_change.notify =  		output_layout_handle_change; @@ -62,10 +66,9 @@ static int index_child(const struct sway_container *child) {  static void container_handle_fullscreen_reparent(struct sway_container *con,  		struct sway_container *old_parent) { -	if (con->type != C_VIEW || !con->sway_view->is_fullscreen) { +	if (!con->is_fullscreen) {  		return;  	} -	struct sway_view *view = con->sway_view;  	struct sway_container *old_workspace = old_parent;  	if (old_workspace && old_workspace->type != C_WORKSPACE) {  		old_workspace = container_parent(old_workspace, C_WORKSPACE); @@ -81,19 +84,27 @@ static void container_handle_fullscreen_reparent(struct sway_container *con,  	// Mark the new workspace as fullscreen  	if (new_workspace->sway_workspace->fullscreen) { -		view_set_fullscreen(new_workspace->sway_workspace->fullscreen, false); +		container_set_fullscreen( +				new_workspace->sway_workspace->fullscreen, false);  	} -	new_workspace->sway_workspace->fullscreen = view; -	// Resize view to new output dimensions +	new_workspace->sway_workspace->fullscreen = con; + +	// Resize container to new output dimensions  	struct sway_container *output = new_workspace->parent; -	view->x = output->x; -	view->y = output->y; -	view->width = output->width; -	view->height = output->height;  	con->x = output->x;  	con->y = output->y;  	con->width = output->width;  	con->height = output->height; + +	if (con->type == C_VIEW) { +		struct sway_view *view = con->sway_view; +		view->x = output->x; +		view->y = output->y; +		view->width = output->width; +		view->height = output->height; +	} else { +		arrange_windows(new_workspace); +	}  }  void container_insert_child(struct sway_container *parent, @@ -135,10 +146,14 @@ void container_add_child(struct sway_container *parent,  	list_add(parent->children, child);  	child->parent = parent;  	container_handle_fullscreen_reparent(child, old_parent); +	if (old_parent) { +		container_set_dirty(old_parent); +	} +	container_set_dirty(child);  }  struct sway_container *container_remove_child(struct sway_container *child) { -	if (child->type == C_VIEW && child->sway_view->is_fullscreen) { +	if (child->is_fullscreen) {  		struct sway_container *workspace = container_parent(child, C_WORKSPACE);  		workspace->sway_workspace->fullscreen = NULL;  	} @@ -153,6 +168,9 @@ struct sway_container *container_remove_child(struct sway_container *child) {  	child->parent = NULL;  	container_notify_subtree_changed(parent); +	container_set_dirty(parent); +	container_set_dirty(child); +  	return parent;  } @@ -199,7 +217,9 @@ void container_move_to(struct sway_container *container,  		container_sort_workspaces(new_parent);  		seat_set_focus(seat, new_parent);  		workspace_output_raise_priority(container, old_parent, new_parent); -		ipc_event_workspace(container, NULL, "move"); +		ipc_event_workspace(NULL, container, "move"); +	} else if (container->type == C_VIEW) { +		ipc_event_window(container, "move");  	}  	container_notify_subtree_changed(old_parent);  	container_notify_subtree_changed(new_parent); @@ -218,10 +238,10 @@ void container_move_to(struct sway_container *container,  			if (focus_ws->type != C_WORKSPACE) {  				focus_ws = container_parent(focus_ws, C_WORKSPACE);  			} -			seat_set_focus(seat, -					new_workspace->sway_workspace->fullscreen->swayc); -			if (focus_ws != new_workspace) { -				seat_set_focus(seat, focus); +			if (focus_ws == new_workspace) { +				struct sway_container *new_focus = seat_get_focus_inactive(seat, +						new_workspace->sway_workspace->fullscreen); +				seat_set_focus(seat, new_focus);  			}  		}  	} @@ -364,10 +384,18 @@ void container_move(struct sway_container *container,  	struct sway_container *sibling = NULL;  	struct sway_container *current = container;  	struct sway_container *parent = current->parent; +	struct sway_container *top = &root_container;  	// If moving a fullscreen view, only consider outputs -	if (container->type == C_VIEW && container->sway_view->is_fullscreen) { +	if (container->is_fullscreen) {  		current = container_parent(container, C_OUTPUT); +	} else if (container_is_fullscreen_or_child(container) || +			container_is_floating_or_child(container)) { +		// If we've fullscreened a split container, only allow the child to move +		// around within the fullscreen parent. +		// Same with floating a split container. +		struct sway_container *ws = container_parent(container, C_WORKSPACE); +		top = ws->sway_workspace->fullscreen;  	}  	struct sway_container *new_parent = container_flatten(parent); @@ -377,7 +405,7 @@ void container_move(struct sway_container *container,  	}  	while (!sibling) { -		if (current->type == C_ROOT) { +		if (current == top) {  			return;  		} @@ -441,8 +469,12 @@ void container_move(struct sway_container *container,  				if ((index == parent->children->length - 1 && offs > 0)  						|| (index == 0 && offs < 0)) {  					if (current->parent == container->parent) { -						if (parent->layout == L_TABBED -								|| parent->layout == L_STACKED) { +						if (parent->parent->layout == L_FLOATING) { +							return; +						} +						if (!parent->is_fullscreen && +								(parent->layout == L_TABBED || +								 parent->layout == L_STACKED)) {  							move_out_of_tabs_stacks(container, current,  									move_dir, offs);  							return; @@ -463,10 +495,14 @@ void container_move(struct sway_container *container,  					sibling = parent->children->items[index + offs];  					wlr_log(WLR_DEBUG, "Selecting sibling id:%zd", sibling->id);  				} -			} else if (parent->layout == L_TABBED -					|| parent->layout == L_STACKED) { +			} else if (!parent->is_fullscreen && +					parent->parent->layout != L_FLOATING && +					(parent->layout == L_TABBED || +						parent->layout == L_STACKED)) {  				move_out_of_tabs_stacks(container, current, move_dir, offs);  				return; +			} else if (parent->parent->layout == L_FLOATING) { +				return;  			} else {  				wlr_log(WLR_DEBUG, "Moving up to find a parallel container");  				current = current->parent; @@ -544,6 +580,10 @@ void container_move(struct sway_container *container,  	container_notify_subtree_changed(old_parent);  	container_notify_subtree_changed(container->parent); +	if (container->type == C_VIEW) { +		ipc_event_window(container, "move"); +	} +  	if (old_parent) {  		seat_set_focus(config->handler_context.seat, old_parent);  		seat_set_focus(config->handler_context.seat, container); @@ -558,10 +598,11 @@ void container_move(struct sway_container *container,  		next_ws = container_parent(next_ws, C_WORKSPACE);  	}  	if (last_ws && next_ws && last_ws != next_ws) { -		ipc_event_workspace(last_ws, container, "focus"); +		ipc_event_workspace(last_ws, next_ws, "focus");  		workspace_detect_urgent(last_ws);  		workspace_detect_urgent(next_ws);  	} +	container_end_mouse_operation(container);  }  enum sway_container_layout container_get_default_layout( @@ -691,22 +732,18 @@ struct sway_container *container_get_in_direction(  		enum movement_direction dir) {  	struct sway_container *parent = container->parent; -	if (container_is_floating(container)) { -		return NULL; +	if (dir == MOVE_CHILD) { +		return seat_get_focus_inactive(seat, container);  	} - -	if (container->type == C_VIEW && container->sway_view->is_fullscreen) { -		if (dir == MOVE_PARENT || dir == MOVE_CHILD) { +	if (container->is_fullscreen) { +		if (dir == MOVE_PARENT) {  			return NULL;  		}  		container = container_parent(container, C_OUTPUT);  		parent = container->parent;  	} else { -		if (dir == MOVE_CHILD) { -			return seat_get_focus_inactive(seat, container); -		}  		if (dir == MOVE_PARENT) { -			if (parent->type == C_OUTPUT) { +			if (parent->type == C_OUTPUT || container_is_floating(container)) {  				return NULL;  			} else {  				return parent; @@ -755,7 +792,8 @@ struct sway_container *container_get_in_direction(  			}  			sway_assert(next_workspace, "Next container has no workspace");  			if (next_workspace->sway_workspace->fullscreen) { -				return next_workspace->sway_workspace->fullscreen->swayc; +				return seat_get_focus_inactive(seat, +						next_workspace->sway_workspace->fullscreen);  			}  			if (next->children && next->children->length) {  				// TODO consider floating children as well @@ -963,13 +1001,13 @@ static void swap_focus(struct sway_container *con1,  		if (focus == con1 && (con2->parent->layout == L_TABBED  					|| con2->parent->layout == L_STACKED)) {  			if (workspace_is_visible(ws2)) { -				seat_set_focus_warp(seat, con2, false); +				seat_set_focus_warp(seat, con2, false, true);  			}  			seat_set_focus(seat, ws1 != ws2 ? con2 : con1);  		} else if (focus == con2 && (con1->parent->layout == L_TABBED  					|| con1->parent->layout == L_STACKED)) {  			if (workspace_is_visible(ws1)) { -				seat_set_focus_warp(seat, con1, false); +				seat_set_focus_warp(seat, con1, false, true);  			}  			seat_set_focus(seat, ws1 != ws2 ? con1 : con2);  		} else if (ws1 != ws2) { @@ -1002,13 +1040,13 @@ void container_swap(struct sway_container *con1, struct sway_container *con2) {  	wlr_log(WLR_DEBUG, "Swapping containers %zu and %zu", con1->id, con2->id); -	int fs1 = con1->type == C_VIEW && con1->sway_view->is_fullscreen; -	int fs2 = con2->type == C_VIEW && con2->sway_view->is_fullscreen; +	int fs1 = con1->is_fullscreen; +	int fs2 = con2->is_fullscreen;  	if (fs1) { -		view_set_fullscreen(con1->sway_view, false); +		container_set_fullscreen(con1, false);  	}  	if (fs2) { -		view_set_fullscreen(con2->sway_view, false); +		container_set_fullscreen(con2, false);  	}  	struct sway_seat *seat = input_manager_get_default_seat(input_manager); @@ -1041,10 +1079,10 @@ void container_swap(struct sway_container *con1, struct sway_container *con2) {  		prev_workspace_name = stored_prev_name;  	} -	if (fs1 && con2->type == C_VIEW) { -		view_set_fullscreen(con2->sway_view, true); +	if (fs1) { +		container_set_fullscreen(con2, true);  	} -	if (fs2 && con1->type == C_VIEW) { -		view_set_fullscreen(con1->sway_view, true); +	if (fs2) { +		container_set_fullscreen(con1, true);  	}  } diff --git a/sway/tree/output.c b/sway/tree/output.c index da535c18..31e3bf9b 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -22,7 +22,7 @@ static void restore_workspaces(struct sway_container *output) {  			if (highest == output) {  				container_remove_child(ws);  				container_add_child(output, ws); -				ipc_event_workspace(ws, NULL, "move"); +				ipc_event_workspace(NULL, ws, "move");  				j--;  			}  		} diff --git a/sway/tree/view.c b/sway/tree/view.c index 7881e6d7..97318daa 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -2,7 +2,12 @@  #include <stdlib.h>  #include <wayland-server.h>  #include <wlr/render/wlr_renderer.h> +#include <wlr/types/wlr_buffer.h>  #include <wlr/types/wlr_output_layout.h> +#include "config.h" +#ifdef HAVE_XWAYLAND +#include <wlr/xwayland.h> +#endif  #include "list.h"  #include "log.h"  #include "sway/criteria.h" @@ -107,14 +112,14 @@ const char *view_get_instance(struct sway_view *view) {  	}  	return NULL;  } - +#ifdef HAVE_XWAYLAND  uint32_t view_get_x11_window_id(struct sway_view *view) {  	if (view->impl->get_int_prop) {  		return view->impl->get_int_prop(view, VIEW_PROP_X11_WINDOW_ID);  	}  	return 0;  } - +#endif  const char *view_get_window_role(struct sway_view *view) {  	if (view->impl->get_string_prop) {  		return view->impl->get_string_prop(view, VIEW_PROP_WINDOW_ROLE); @@ -135,12 +140,27 @@ const char *view_get_shell(struct sway_view *view) {  		return "xdg_shell_v6";  	case SWAY_VIEW_XDG_SHELL:  		return "xdg_shell"; +#ifdef HAVE_XWAYLAND  	case SWAY_VIEW_XWAYLAND:  		return "xwayland"; +#endif  	}  	return "unknown";  } +void view_get_constraints(struct sway_view *view, double *min_width, +		double *max_width, double *min_height, double *max_height) { +	if (view->impl->get_constraints) { +		view->impl->get_constraints(view, +				min_width, max_width, min_height, max_height); +	} else { +		*min_width = DBL_MIN; +		*max_width = DBL_MAX; +		*min_height = DBL_MIN; +		*max_height = DBL_MAX; +	} +} +  uint32_t view_configure(struct sway_view *view, double lx, double ly, int width,  		int height) {  	if (view->impl->configure) { @@ -149,55 +169,6 @@ uint32_t view_configure(struct sway_view *view, double lx, double ly, int width,  	return 0;  } -void view_init_floating(struct sway_view *view) { -	struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); -	int min_width, min_height; -	int max_width, max_height; - -	if (config->floating_minimum_width == -1) { // no minimum -		min_width = 0; -	} else if (config->floating_minimum_width == 0) { // automatic -		min_width = 75; -	} else { -		min_width = config->floating_minimum_width; -	} - -	if (config->floating_minimum_height == -1) { // no minimum -		min_height = 0; -	} else if (config->floating_minimum_height == 0) { // automatic -		min_height = 50; -	} else { -		min_height = config->floating_minimum_height; -	} - -	if (config->floating_maximum_width == -1) { // no maximum -		max_width = INT_MAX; -	} else if (config->floating_maximum_width == 0) { // automatic -		max_width = ws->width * 0.6666; -	} else { -		max_width = config->floating_maximum_width; -	} - -	if (config->floating_maximum_height == -1) { // no maximum -		max_height = INT_MAX; -	} else if (config->floating_maximum_height == 0) { // automatic -		max_height = ws->height * 0.6666; -	} else { -		max_height = config->floating_maximum_height; -	} - -	view->width = fmax(min_width, fmin(view->natural_width, max_width)); -	view->height = fmax(min_height, fmin(view->natural_height, max_height)); -	view->x = ws->x + (ws->width - view->width) / 2; -	view->y = ws->y + (ws->height - view->height) / 2; - -	// If the view's border is B_NONE then these properties are ignored. -	view->border_top = view->border_bottom = true; -	view->border_left = view->border_right = true; - -	container_set_geometry_from_floating_view(view->swayc); -} -  void view_autoconfigure(struct sway_view *view) {  	if (!sway_assert(view->swayc,  				"Called view_autoconfigure() on a view without a swayc")) { @@ -206,7 +177,7 @@ void view_autoconfigure(struct sway_view *view) {  	struct sway_container *output = container_parent(view->swayc, C_OUTPUT); -	if (view->is_fullscreen) { +	if (view->swayc->is_fullscreen) {  		view->x = output->x;  		view->y = output->y;  		view->width = output->width; @@ -214,10 +185,6 @@ void view_autoconfigure(struct sway_view *view) {  		return;  	} -	if (container_is_floating(view->swayc)) { -		return; -	} -  	struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);  	int other_views = 0; @@ -330,72 +297,18 @@ void view_set_tiled(struct sway_view *view, bool tiled) {  	}  } -void view_set_fullscreen(struct sway_view *view, bool fullscreen) { -	if (view->is_fullscreen == fullscreen) { -		return; -	} - -	struct sway_container *workspace = -		container_parent(view->swayc, C_WORKSPACE); - -	if (view->impl->set_fullscreen) { -		view->impl->set_fullscreen(view, fullscreen); -	} - -	view->is_fullscreen = fullscreen; - -	if (fullscreen) { -		if (workspace->sway_workspace->fullscreen) { -			view_set_fullscreen(workspace->sway_workspace->fullscreen, false); -		} -		workspace->sway_workspace->fullscreen = view; -		view->saved_x = view->x; -		view->saved_y = view->y; -		view->saved_width = view->width; -		view->saved_height = view->height; -		view->swayc->saved_x = view->swayc->x; -		view->swayc->saved_y = view->swayc->y; -		view->swayc->saved_width = view->swayc->width; -		view->swayc->saved_height = view->swayc->height; - -		struct sway_seat *seat; -		struct sway_container *focus, *focus_ws; -		wl_list_for_each(seat, &input_manager->seats, link) { -			focus = seat_get_focus(seat); -			if (focus) { -				focus_ws = focus; -				if (focus && focus_ws->type != C_WORKSPACE) { -					focus_ws = container_parent(focus_ws, C_WORKSPACE); -				} -				seat_set_focus(seat, view->swayc); -				if (focus_ws != workspace) { -					seat_set_focus(seat, focus); -				} -			} -		} -	} else { -		workspace->sway_workspace->fullscreen = NULL; -		if (container_is_floating(view->swayc)) { -			view->x = view->saved_x; -			view->y = view->saved_y; -			view->width = view->saved_width; -			view->height = view->saved_height; -			container_set_geometry_from_floating_view(view->swayc); -		} else { -			view->swayc->width = view->swayc->saved_width; -			view->swayc->height = view->swayc->saved_height; -		} -	} - -	ipc_event_window(view->swayc, "fullscreen_mode"); -} -  void view_close(struct sway_view *view) {  	if (view->impl->close) {  		view->impl->close(view);  	}  } +void view_close_popups(struct sway_view *view) { +	if (view->impl->close_popups) { +		view->impl->close_popups(view); +	} +} +  void view_damage_from(struct sway_view *view) {  	for (int i = 0; i < root_container.children->length; ++i) {  		struct sway_container *cont = root_container.children->items[i]; @@ -426,6 +339,16 @@ void view_for_each_surface(struct sway_view *view,  	}  } +void view_for_each_popup(struct sway_view *view, +		wlr_surface_iterator_func_t iterator, void *user_data) { +	if (!view->surface) { +		return; +	} +	if (view->impl->for_each_popup) { +		view->impl->for_each_popup(view, iterator, user_data); +	} +} +  static void view_subsurface_create(struct sway_view *view,  	struct wlr_subsurface *subsurface); @@ -521,12 +444,82 @@ void view_execute_criteria(struct sway_view *view) {  	seat_set_focus(seat, prior_focus);  } +static struct sway_container *select_workspace(struct sway_view *view) { +	struct sway_seat *seat = input_manager_current_seat(input_manager); + +	// Check if there's any `assign` criteria for the view +	list_t *criterias = criteria_for_view(view, +			CT_ASSIGN_WORKSPACE | CT_ASSIGN_OUTPUT); +	struct sway_container *ws = NULL; +	for (int i = 0; i < criterias->length; ++i) { +		struct criteria *criteria = criterias->items[i]; +		if (criteria->type == CT_ASSIGN_WORKSPACE) { +			ws = workspace_by_name(criteria->target); +			if (!ws) { +				ws = workspace_create(NULL, criteria->target); +			} +			break; +		} else { +			// CT_ASSIGN_OUTPUT +			struct sway_container *output = output_by_name(criteria->target); +			if (output) { +				ws = seat_get_active_child(seat, output); +				break; +			} +		} +	} +	list_free(criterias); +	if (ws) { +		return ws; +	} + +	// Check if there's a PID mapping +	pid_t pid; +#ifdef HAVE_XWAYLAND +	if (view->type == SWAY_VIEW_XWAYLAND) { +		struct wlr_xwayland_surface *surf = +			wlr_xwayland_surface_from_wlr_surface(view->surface); +		pid = surf->pid; +	} else { +		struct wl_client *client = +			wl_resource_get_client(view->surface->resource); +		wl_client_get_credentials(client, &pid, NULL, NULL); +	} +#else +	struct wl_client *client = +		wl_resource_get_client(view->surface->resource); +	wl_client_get_credentials(client, &pid, NULL, NULL); +#endif +	ws = workspace_for_pid(pid); +	if (ws) { +		return ws; +	} + +	// Use the focused workspace +	ws = seat_get_focus_inactive(seat, &root_container); +	if (ws->type != C_WORKSPACE) { +		ws = container_parent(ws, C_WORKSPACE); +	} +	return ws; +} +  static bool should_focus(struct sway_view *view) { +	struct sway_seat *seat = input_manager_current_seat(input_manager); +	struct sway_container *prev_focus = +		seat_get_focus_inactive(seat, &root_container); +	struct sway_container *prev_ws = prev_focus->type == C_WORKSPACE ? +		prev_focus : container_parent(prev_focus, C_WORKSPACE); +	struct sway_container *map_ws = container_parent(view->swayc, C_WORKSPACE); + +	// Views can only take focus if they are mapped into the active workspace +	if (prev_ws != map_ws) { +		return false; +	} +  	// If the view is the only one in the focused workspace, it'll get focus  	// regardless of any no_focus criteria.  	struct sway_container *parent = view->swayc->parent; -	struct sway_seat *seat = input_manager_current_seat(input_manager); -	if (parent->type == C_WORKSPACE && seat_get_focus(seat) == parent) { +	if (parent->type == C_WORKSPACE && prev_focus == parent) {  		size_t num_children = parent->children->length +  			parent->sway_workspace->floating->children->length;  		if (num_children == 1) { @@ -545,42 +538,19 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {  	if (!sway_assert(view->surface == NULL, "cannot map mapped view")) {  		return;  	} +	view->surface = wlr_surface;  	struct sway_seat *seat = input_manager_current_seat(input_manager); -	struct sway_container *focus = -		seat_get_focus_inactive(seat, &root_container); -	struct sway_container *cont = NULL; +	struct sway_container *ws = select_workspace(view); +	struct sway_container *target_sibling = seat_get_focus_inactive(seat, ws); -	// Check if there's any `assign` criteria for the view -	list_t *criterias = criteria_for_view(view, -			CT_ASSIGN_WORKSPACE | CT_ASSIGN_OUTPUT); -	struct sway_container *workspace = NULL; -	if (criterias->length) { -		struct criteria *criteria = criterias->items[0]; -		if (criteria->type == CT_ASSIGN_WORKSPACE) { -			workspace = workspace_by_name(criteria->target); -			if (!workspace) { -				workspace = workspace_create(NULL, criteria->target); -			} -			focus = seat_get_focus_inactive(seat, workspace); -		} else { -			// CT_ASSIGN_OUTPUT -			struct sway_container *output = output_by_name(criteria->target); -			if (output) { -				focus = seat_get_focus_inactive(seat, output); -			} -		} -	}  	// If we're about to launch the view into the floating container, then  	// launch it as a tiled view in the root of the workspace instead. -	if (container_is_floating(focus)) { -		focus = focus->parent->parent; +	if (container_is_floating(target_sibling)) { +		target_sibling = target_sibling->parent->parent;  	} -	list_free(criterias); -	cont = container_view_create(focus, view); -	view->surface = wlr_surface; -	view->swayc = cont; +	view->swayc = container_view_create(target_sibling, view);  	view_init_subsurfaces(view, wlr_surface);  	wl_signal_add(&wlr_surface->events.new_subsurface, @@ -601,10 +571,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {  	}  	if (should_focus(view)) { -		input_manager_set_focus(input_manager, cont); -		if (workspace) { -			workspace_switch(workspace); -		} +		input_manager_set_focus(input_manager, view->swayc);  	}  	view_update_title(view, false); @@ -628,10 +595,8 @@ void view_unmap(struct sway_view *view) {  	struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);  	struct sway_container *parent; -	if (view->is_fullscreen) { -		ws->sway_workspace->fullscreen = NULL; +	if (container_is_fullscreen_or_child(view->swayc)) {  		parent = container_destroy(view->swayc); -  		arrange_windows(ws->parent);  	} else {  		parent = container_destroy(view->swayc); @@ -784,11 +749,13 @@ struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) {  			wlr_xdg_surface_v6_from_wlr_surface(wlr_surface);  		return view_from_wlr_xdg_surface_v6(xdg_surface_v6);  	} +#ifdef HAVE_XWAYLAND  	if (wlr_surface_is_xwayland_surface(wlr_surface)) {  		struct wlr_xwayland_surface *xsurface =  			wlr_xwayland_surface_from_wlr_surface(wlr_surface);  		return view_from_wlr_xwayland_surface(xsurface);  	} +#endif  	if (wlr_surface_is_subsurface(wlr_surface)) {  		struct wlr_subsurface *subsurface =  			wlr_subsurface_from_wlr_surface(wlr_surface); @@ -915,6 +882,8 @@ void view_update_title(struct sway_view *view, bool force) {  	// Update title after the global font height is updated  	container_update_title_textures(view->swayc); + +	ipc_event_window(view->swayc, "title");  }  static bool find_by_mark_iterator(struct sway_container *con, @@ -937,6 +906,7 @@ bool view_find_and_unmark(char *mark) {  			free(view_mark);  			list_del(view->marks, i);  			view_update_marks_textures(view); +			ipc_event_window(container, "mark");  			return true;  		}  	} @@ -944,11 +914,10 @@ bool view_find_and_unmark(char *mark) {  }  void view_clear_marks(struct sway_view *view) { -	for (int i = 0; i < view->marks->length; ++i) { -		free(view->marks->items[i]); +	while (view->marks->length) { +		list_del(view->marks, 0); +		ipc_event_window(view->swayc, "mark");  	} -	list_free(view->marks); -	view->marks = create_list();  }  bool view_has_mark(struct sway_view *view, char *mark) { @@ -961,6 +930,11 @@ bool view_has_mark(struct sway_view *view, char *mark) {  	return false;  } +void view_add_mark(struct sway_view *view, char *mark) { +	list_add(view->marks, strdup(mark)); +	ipc_event_window(view->swayc, "mark"); +} +  static void update_marks_texture(struct sway_view *view,  		struct wlr_texture **texture, struct border_colors *class) {  	struct sway_container *output = container_parent(view->swayc, C_OUTPUT); @@ -1055,6 +1029,9 @@ bool view_is_visible(struct sway_view *view) {  	}  	struct sway_container *workspace =  		container_parent(view->swayc, C_WORKSPACE); +	if (!workspace) { +		return false; +	}  	// Determine if view is nested inside a floating container which is sticky.  	// A simple floating view will have this ancestry:  	// C_VIEW -> floating -> workspace @@ -1079,7 +1056,8 @@ bool view_is_visible(struct sway_view *view) {  		container = container->parent;  	}  	// Check view isn't hidden by another fullscreen view -	if (workspace->sway_workspace->fullscreen && !view->is_fullscreen) { +	if (workspace->sway_workspace->fullscreen && +			!container_is_fullscreen_or_child(view->swayc)) {  		return false;  	}  	// Check the workspace is visible @@ -1117,3 +1095,22 @@ void view_set_urgent(struct sway_view *view, bool enable) {  bool view_is_urgent(struct sway_view *view) {  	return view->urgent.tv_sec || view->urgent.tv_nsec;  } + +void view_remove_saved_buffer(struct sway_view *view) { +	if (!sway_assert(view->saved_buffer, "Expected a saved buffer")) { +		return; +	} +	wlr_buffer_unref(view->saved_buffer); +	view->saved_buffer = NULL; +} + +void view_save_buffer(struct sway_view *view) { +	if (!sway_assert(!view->saved_buffer, "Didn't expect saved buffer")) { +		view_remove_saved_buffer(view); +	} +	if (view->surface && wlr_surface_has_buffer(view->surface)) { +		view->saved_buffer = wlr_buffer_ref(view->surface->buffer); +		view->saved_buffer_width = view->surface->current.width; +		view->saved_buffer_height = view->surface->current.height; +	} +} diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 622f01ec..588e2aae 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -9,6 +9,7 @@  #include "sway/input/input-manager.h"  #include "sway/input/seat.h"  #include "sway/ipc-server.h" +#include "sway/output.h"  #include "sway/tree/arrange.h"  #include "sway/tree/container.h"  #include "sway/tree/view.h" @@ -107,96 +108,100 @@ static bool workspace_valid_on_output(const char *output_name,  	return true;  } -char *workspace_next_name(const char *output_name) { -	wlr_log(WLR_DEBUG, "Workspace: Generating new workspace name for output %s", -			output_name); -	// Scan all workspace bindings to find the next available workspace name, -	// if none are found/available then default to a number -	struct sway_mode *mode = config->current_mode; +static void workspace_name_from_binding(const struct sway_binding * binding, +		const char* output_name, int *min_order, char **earliest_name) { +	char *cmdlist = strdup(binding->command); +	char *dup = cmdlist; +	char *name = NULL; -	// TODO: iterate over keycode bindings too -	int order = INT_MAX; -	char *target = NULL; -	for (int i = 0; i < mode->keysym_bindings->length; ++i) { -		struct sway_binding *binding = mode->keysym_bindings->items[i]; -		char *cmdlist = strdup(binding->command); -		char *dup = cmdlist; -		char *name = NULL; - -		// workspace n -		char *cmd = argsep(&cmdlist, " "); -		if (cmdlist) { -			name = argsep(&cmdlist, ",;"); -		} +	// workspace n +	char *cmd = argsep(&cmdlist, " "); +	if (cmdlist) { +		name = argsep(&cmdlist, ",;"); +	} -		if (strcmp("workspace", cmd) == 0 && name) { -			char *_target = strdup(name); -			_target = do_var_replacement(_target); -			strip_quotes(_target); -			while (isspace(*_target)) { -				memmove(_target, _target+1, strlen(_target+1)); -			} -			wlr_log(WLR_DEBUG, "Got valid workspace command for target: '%s'", -					_target); +	if (strcmp("workspace", cmd) == 0 && name) { +		char *_target = strdup(name); +		_target = do_var_replacement(_target); +		strip_quotes(_target); +		wlr_log(WLR_DEBUG, "Got valid workspace command for target: '%s'", +				_target); -			// Make sure that the command references an actual workspace -			// not a command about workspaces -			if (strcmp(_target, "next") == 0 || +		// Make sure that the command references an actual workspace +		// not a command about workspaces +		if (strcmp(_target, "next") == 0 ||  				strcmp(_target, "prev") == 0 ||  				strcmp(_target, "next_on_output") == 0 ||  				strcmp(_target, "prev_on_output") == 0 ||  				strcmp(_target, "number") == 0 ||  				strcmp(_target, "back_and_forth") == 0 || -				strcmp(_target, "current") == 0) -			{ -				free(_target); -				free(dup); -				continue; -			} - -			// If the command is workspace number <name>, isolate the name -			if (strncmp(_target, "number ", strlen("number ")) == 0) { -				size_t length = strlen(_target) - strlen("number ") + 1; -				char *temp = malloc(length); -				strncpy(temp, _target + strlen("number "), length - 1); -				temp[length - 1] = '\0'; -				free(_target); -				_target = temp; -				wlr_log(WLR_DEBUG, "Isolated name from workspace number: '%s'", _target); - -				// Make sure the workspace number doesn't already exist -				if (workspace_by_number(_target)) { -					free(_target); -					free(dup); -					continue; -				} -			} +				strcmp(_target, "current") == 0) { +			free(_target); +			free(dup); +			return; +		} -			// Make sure that the workspace doesn't already exist -			if (workspace_by_name(_target)) { +		// If the command is workspace number <name>, isolate the name +		if (strncmp(_target, "number ", strlen("number ")) == 0) { +			size_t length = strlen(_target) - strlen("number ") + 1; +			char *temp = malloc(length); +			strncpy(temp, _target + strlen("number "), length - 1); +			temp[length - 1] = '\0'; +			free(_target); +			_target = temp; +			wlr_log(WLR_DEBUG, "Isolated name from workspace number: '%s'", _target); + +			// Make sure the workspace number doesn't already exist +			if (workspace_by_number(_target)) {  				free(_target);  				free(dup); -				continue; +				return;  			} +		} -			// make sure that the workspace can appear on the given -			// output -			if (!workspace_valid_on_output(output_name, _target)) { -				free(_target); -				free(dup); -				continue; -			} +		// Make sure that the workspace doesn't already exist +		if (workspace_by_name(_target)) { +			free(_target); +			free(dup); +			return; +		} -			if (binding->order < order) { -				order = binding->order; -				free(target); -				target = _target; -				wlr_log(WLR_DEBUG, "Workspace: Found free name %s", _target); -			} else { -				free(_target); -			} +		// make sure that the workspace can appear on the given +		// output +		if (!workspace_valid_on_output(output_name, _target)) { +			free(_target); +			free(dup); +			return;  		} -		free(dup); + +		if (binding->order < *min_order) { +			*min_order = binding->order; +			free(*earliest_name); +			*earliest_name = _target; +			wlr_log(WLR_DEBUG, "Workspace: Found free name %s", _target); +		} else { +			free(_target); +		} +	} +	free(dup); +} + +char *workspace_next_name(const char *output_name) { +	wlr_log(WLR_DEBUG, "Workspace: Generating new workspace name for output %s", +			output_name); +	// Scan all workspace bindings to find the next available workspace name, +	// if none are found/available then default to a number +	struct sway_mode *mode = config->current_mode; + +	int order = INT_MAX; +	char *target = NULL; +	for (int i = 0; i < mode->keysym_bindings->length; ++i) { +		workspace_name_from_binding(mode->keysym_bindings->items[i], +				output_name, &order, &target); +	} +	for (int i = 0; i < mode->keycode_bindings->length; ++i) { +		workspace_name_from_binding(mode->keycode_bindings->items[i], +				output_name, &order, &target);  	}  	if (target != NULL) {  		return target; @@ -529,3 +534,116 @@ void workspace_detect_urgent(struct sway_container *workspace) {  		container_damage_whole(workspace);  	}  } + +struct pid_workspace { +	pid_t pid; +	char *workspace; +	struct timespec time_added; + +	struct sway_container *output; +	struct wl_listener output_destroy; + +	struct wl_list link; +}; + +static struct wl_list pid_workspaces; + +struct sway_container *workspace_for_pid(pid_t pid) { +	if (!pid_workspaces.prev && !pid_workspaces.next) { +		wl_list_init(&pid_workspaces); +		return NULL; +	} + +	struct sway_container *ws = NULL; +	struct pid_workspace *pw = NULL; + +	wlr_log(WLR_DEBUG, "Looking up workspace for pid %d", pid); + +	do { +		struct pid_workspace *_pw = NULL; +		wl_list_for_each(_pw, &pid_workspaces, link) { +			if (pid == _pw->pid) { +				pw = _pw; +				wlr_log(WLR_DEBUG, +						"found pid_workspace for pid %d, workspace %s", +						pid, pw->workspace); +				goto found; +			} +		} +		pid = get_parent_pid(pid); +	} while (pid > 1); +found: + +	if (pw && pw->workspace) { +		ws = workspace_by_name(pw->workspace); + +		if (!ws) { +			wlr_log(WLR_DEBUG, +					"Creating workspace %s for pid %d because it disappeared", +					pw->workspace, pid); +			ws = workspace_create(pw->output, pw->workspace); +		} + +		wl_list_remove(&pw->output_destroy.link); +		wl_list_remove(&pw->link); +		free(pw->workspace); +		free(pw); +	} + +	return ws; +} + +static void pw_handle_output_destroy(struct wl_listener *listener, void *data) { +	struct pid_workspace *pw = wl_container_of(listener, pw, output_destroy); +	pw->output = NULL; +	wl_list_remove(&pw->output_destroy.link); +	wl_list_init(&pw->output_destroy.link); +} + +void workspace_record_pid(pid_t pid) { +	wlr_log(WLR_DEBUG, "Recording workspace for process %d", pid); +	if (!pid_workspaces.prev && !pid_workspaces.next) { +		wl_list_init(&pid_workspaces); +	} + +	struct sway_seat *seat = input_manager_current_seat(input_manager); +	struct sway_container *ws = +		seat_get_focus_inactive(seat, &root_container); +	if (ws && ws->type != C_WORKSPACE) { +		ws = container_parent(ws, C_WORKSPACE); +	} +	if (!ws) { +		wlr_log(WLR_DEBUG, "Bailing out, no workspace"); +		return; +	} +	struct sway_container *output = ws->parent; +	if (!output) { +		wlr_log(WLR_DEBUG, "Bailing out, no output"); +		return; +	} + +	struct timespec now; +	clock_gettime(CLOCK_MONOTONIC, &now); + +	// Remove expired entries +	static const int timeout = 60; +	struct pid_workspace *old, *_old; +	wl_list_for_each_safe(old, _old, &pid_workspaces, link) { +		if (now.tv_sec - old->time_added.tv_sec >= timeout) { +			wl_list_remove(&old->output_destroy.link); +			wl_list_remove(&old->link); +			free(old->workspace); +			free(old); +		} +	} + +	struct pid_workspace *pw = calloc(1, sizeof(struct pid_workspace)); +	pw->workspace = strdup(ws->name); +	pw->output = output; +	pw->pid = pid; +	memcpy(&pw->time_added, &now, sizeof(struct timespec)); +	pw->output_destroy.notify = pw_handle_output_destroy; +	wl_signal_add(&output->sway_output->wlr_output->events.destroy, +			&pw->output_destroy); +	wl_list_insert(&pid_workspaces, &pw->link); +} | 
