mirror of
				https://github.com/labwc/labwc.git
				synced 2025-10-29 05:40:24 -04:00 
			
		
		
		
	foreign-toplevel: create generic abstraction
This commit is contained in:
		
							parent
							
								
									5e1f91c9d1
								
							
						
					
					
						commit
						2a825008c6
					
				
					 15 changed files with 463 additions and 196 deletions
				
			
		
							
								
								
									
										61
									
								
								include/foreign-toplevel-internal.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								include/foreign-toplevel-internal.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,61 @@ | |||
| /* SPDX-License-Identifier: GPL-2.0-only */ | ||||
| #ifndef LABWC_FOREIGN_TOPLEVEL_INTERNAL_H | ||||
| #define LABWC_FOREIGN_TOPLEVEL_INTERNAL_H | ||||
| 
 | ||||
| #include <stdbool.h> | ||||
| #include <wayland-server-core.h> | ||||
| #include "foreign-toplevel.h" | ||||
| 
 | ||||
| struct foreign_toplevel { | ||||
| 	struct view *view; | ||||
| 
 | ||||
| 	/* *-toplevel implementations */ | ||||
| 	struct wlr_foreign_toplevel { | ||||
| 		struct wlr_foreign_toplevel_handle_v1 *handle; | ||||
| 
 | ||||
| 		/* Client side events */ | ||||
| 		struct { | ||||
| 			struct wl_listener request_maximize; | ||||
| 			struct wl_listener request_minimize; | ||||
| 			struct wl_listener request_fullscreen; | ||||
| 			struct wl_listener request_activate; | ||||
| 			struct wl_listener request_close; | ||||
| 			struct wl_listener handle_destroy; | ||||
| 		} on; | ||||
| 
 | ||||
| 		/* Compositor side state updates */ | ||||
| 		struct { | ||||
| 			struct wl_listener new_app_id; | ||||
| 			struct wl_listener new_title; | ||||
| 			struct wl_listener new_outputs; | ||||
| 			struct wl_listener maximized; | ||||
| 			struct wl_listener minimized; | ||||
| 			struct wl_listener fullscreened; | ||||
| 			struct wl_listener activated; | ||||
| 		} on_view; | ||||
| 
 | ||||
| 		/* Internal signals */ | ||||
| 		struct { | ||||
| 			struct wl_listener toplevel_parent; | ||||
| 			struct wl_listener toplevel_destroy; | ||||
| 		} on_foreign_toplevel; | ||||
| 
 | ||||
| 	} wlr_toplevel; | ||||
| 
 | ||||
| 	/* TODO: add struct xdg_x11_mapped_toplevel at some point */ | ||||
| 
 | ||||
| 	struct { | ||||
| 		struct wl_signal toplevel_parent;  /* struct view *parent */ | ||||
| 		struct wl_signal toplevel_destroy; | ||||
| 	} events; | ||||
| }; | ||||
| 
 | ||||
| void wlr_foreign_toplevel_init(struct foreign_toplevel *toplevel); | ||||
| 
 | ||||
| void foreign_request_minimize(struct foreign_toplevel *toplevel, bool minimized); | ||||
| void foreign_request_maximize(struct foreign_toplevel *toplevel, enum view_axis axis); | ||||
| void foreign_request_fullscreen(struct foreign_toplevel *toplevel, bool fullscreen); | ||||
| void foreign_request_activate(struct foreign_toplevel *toplevel); | ||||
| void foreign_request_close(struct foreign_toplevel *toplevel); | ||||
| 
 | ||||
| #endif /* LABWC_FOREIGN_TOPLEVEL_INTERNAL_H */ | ||||
							
								
								
									
										13
									
								
								include/foreign-toplevel.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								include/foreign-toplevel.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,13 @@ | |||
| /* SPDX-License-Identifier: GPL-2.0-only */ | ||||
| #ifndef LABWC_FOREIGN_TOPLEVEL_H | ||||
| #define LABWC_FOREIGN_TOPLEVEL_H | ||||
| 
 | ||||
| struct view; | ||||
| struct foreign_toplevel; | ||||
| 
 | ||||
| struct foreign_toplevel *foreign_toplevel_create(struct view *view); | ||||
| void foreign_toplevel_set_parent(struct foreign_toplevel *toplevel, | ||||
| 	struct foreign_toplevel *parent); | ||||
| void foreign_toplevel_destroy(struct foreign_toplevel *toplevel); | ||||
| 
 | ||||
| #endif /* LABWC_FOREIGN_TOPLEVEL_H */ | ||||
|  | @ -16,7 +16,6 @@ | |||
| #include <wlr/types/wlr_buffer.h> | ||||
| #include <wlr/types/wlr_cursor.h> | ||||
| #include <wlr/types/wlr_data_device.h> | ||||
| #include <wlr/types/wlr_foreign_toplevel_management_v1.h> | ||||
| #include <wlr/types/wlr_gamma_control_v1.h> | ||||
| #include <wlr/types/wlr_input_device.h> | ||||
| #include <wlr/types/wlr_keyboard.h> | ||||
|  | @ -419,9 +418,6 @@ struct constraint { | |||
| void xdg_popup_create(struct view *view, struct wlr_xdg_popup *wlr_popup); | ||||
| void xdg_shell_init(struct server *server); | ||||
| 
 | ||||
| void foreign_toplevel_handle_create(struct view *view); | ||||
| void foreign_toplevel_update_outputs(struct view *view); | ||||
| 
 | ||||
| /*
 | ||||
|  * desktop.c routines deal with a collection of views | ||||
|  * | ||||
|  |  | |||
|  | @ -111,6 +111,7 @@ enum window_type { | |||
| 
 | ||||
| struct view; | ||||
| struct wlr_surface; | ||||
| struct foreign_toplevel; | ||||
| 
 | ||||
| /* Common to struct view and struct xwayland_unmanaged */ | ||||
| struct mappable { | ||||
|  | @ -265,16 +266,6 @@ struct view { | |||
| 		struct multi_rect *rect; | ||||
| 	} resize_outlines; | ||||
| 
 | ||||
| 	struct foreign_toplevel { | ||||
| 		struct wlr_foreign_toplevel_handle_v1 *handle; | ||||
| 		struct wl_listener maximize; | ||||
| 		struct wl_listener minimize; | ||||
| 		struct wl_listener fullscreen; | ||||
| 		struct wl_listener activate; | ||||
| 		struct wl_listener close; | ||||
| 		struct wl_listener destroy; | ||||
| 	} toplevel; | ||||
| 
 | ||||
| 	struct mappable mappable; | ||||
| 
 | ||||
| 	struct wl_listener destroy; | ||||
|  | @ -287,6 +278,8 @@ struct view { | |||
| 	struct wl_listener request_fullscreen; | ||||
| 	struct wl_listener set_title; | ||||
| 
 | ||||
| 	struct foreign_toplevel *foreign_toplevel; | ||||
| 
 | ||||
| 	struct { | ||||
| 		struct wl_signal new_app_id; | ||||
| 		struct wl_signal new_title; | ||||
|  |  | |||
							
								
								
									
										78
									
								
								src/foreign-toplevel/foreign.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								src/foreign-toplevel/foreign.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,78 @@ | |||
| // SPDX-License-Identifier: GPL-2.0-only
 | ||||
| #include <assert.h> | ||||
| #include <wayland-server-core.h> | ||||
| #include "common/macros.h" | ||||
| #include "common/mem.h" | ||||
| #include "labwc.h" | ||||
| #include "view.h" | ||||
| #include "foreign-toplevel-internal.h" | ||||
| 
 | ||||
| /* Internal API */ | ||||
| void | ||||
| foreign_request_minimize(struct foreign_toplevel *toplevel, bool minimized) | ||||
| { | ||||
| 	view_minimize(toplevel->view, minimized); | ||||
| } | ||||
| 
 | ||||
| void | ||||
| foreign_request_maximize(struct foreign_toplevel *toplevel, enum view_axis axis) | ||||
| { | ||||
| 	view_maximize(toplevel->view, axis, /*store_natural_geometry*/ true); | ||||
| } | ||||
| 
 | ||||
| void | ||||
| foreign_request_fullscreen(struct foreign_toplevel *toplevel, bool fullscreen) | ||||
| { | ||||
| 	view_set_fullscreen(toplevel->view, fullscreen); | ||||
| } | ||||
| 
 | ||||
| void | ||||
| foreign_request_activate(struct foreign_toplevel *toplevel) | ||||
| { | ||||
| 	if (toplevel->view->server->osd_state.cycle_view) { | ||||
| 		wlr_log(WLR_INFO, "Preventing focus request while in window switcher"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	desktop_focus_view(toplevel->view, /*raise*/ true); | ||||
| } | ||||
| 
 | ||||
| void | ||||
| foreign_request_close(struct foreign_toplevel *toplevel) | ||||
| { | ||||
| 	view_close(toplevel->view); | ||||
| } | ||||
| 
 | ||||
| /* Public API */ | ||||
| struct foreign_toplevel * | ||||
| foreign_toplevel_create(struct view *view) | ||||
| { | ||||
| 	assert(view); | ||||
| 	assert(view->mapped); | ||||
| 
 | ||||
| 	struct foreign_toplevel *toplevel = znew(*toplevel); | ||||
| 	toplevel->view = view; | ||||
| 
 | ||||
| 	wl_signal_init(&toplevel->events.toplevel_parent); | ||||
| 	wl_signal_init(&toplevel->events.toplevel_destroy); | ||||
| 
 | ||||
| 	wlr_foreign_toplevel_init(toplevel); | ||||
| 
 | ||||
| 	return toplevel; | ||||
| } | ||||
| 
 | ||||
| void | ||||
| foreign_toplevel_set_parent(struct foreign_toplevel *toplevel, struct foreign_toplevel *parent) | ||||
| { | ||||
| 	assert(toplevel); | ||||
| 	wl_signal_emit_mutable(&toplevel->events.toplevel_parent, parent); | ||||
| } | ||||
| 
 | ||||
| void | ||||
| foreign_toplevel_destroy(struct foreign_toplevel *toplevel) | ||||
| { | ||||
| 	assert(toplevel); | ||||
| 	wl_signal_emit_mutable(&toplevel->events.toplevel_destroy, NULL); | ||||
| 	assert(!toplevel->wlr_toplevel.handle); | ||||
| 	free(toplevel); | ||||
| } | ||||
							
								
								
									
										4
									
								
								src/foreign-toplevel/meson.build
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								src/foreign-toplevel/meson.build
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,4 @@ | |||
| labwc_sources += files( | ||||
|   'foreign.c', | ||||
|   'wlr-foreign.c', | ||||
| ) | ||||
							
								
								
									
										249
									
								
								src/foreign-toplevel/wlr-foreign.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										249
									
								
								src/foreign-toplevel/wlr-foreign.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,249 @@ | |||
| // SPDX-License-Identifier: GPL-2.0-only
 | ||||
| #include <assert.h> | ||||
| #include <wlr/types/wlr_foreign_toplevel_management_v1.h> | ||||
| #include "common/macros.h" | ||||
| #include "labwc.h" | ||||
| #include "view.h" | ||||
| #include "foreign-toplevel-internal.h" | ||||
| 
 | ||||
| /* wlr signals */ | ||||
| static void | ||||
| handle_request_minimize(struct wl_listener *listener, void *data) | ||||
| { | ||||
| 	struct foreign_toplevel *toplevel = wl_container_of( | ||||
| 		listener, toplevel, wlr_toplevel.on.request_minimize); | ||||
| 	struct wlr_foreign_toplevel_handle_v1_minimized_event *event = data; | ||||
| 
 | ||||
| 	foreign_request_minimize(toplevel, event->minimized); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| handle_request_maximize(struct wl_listener *listener, void *data) | ||||
| { | ||||
| 	struct foreign_toplevel *toplevel = wl_container_of( | ||||
| 		listener, toplevel, wlr_toplevel.on.request_maximize); | ||||
| 	struct wlr_foreign_toplevel_handle_v1_maximized_event *event = data; | ||||
| 
 | ||||
| 	foreign_request_maximize(toplevel, | ||||
| 		event->maximized ? VIEW_AXIS_BOTH : VIEW_AXIS_NONE); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| handle_request_fullscreen(struct wl_listener *listener, void *data) | ||||
| { | ||||
| 	struct foreign_toplevel *toplevel = wl_container_of( | ||||
| 		listener, toplevel, wlr_toplevel.on.request_fullscreen); | ||||
| 	struct wlr_foreign_toplevel_handle_v1_fullscreen_event *event = data; | ||||
| 
 | ||||
| 	/* TODO: This ignores event->output */ | ||||
| 	foreign_request_fullscreen(toplevel, event->fullscreen); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| handle_request_activate(struct wl_listener *listener, void *data) | ||||
| { | ||||
| 	struct foreign_toplevel *toplevel = wl_container_of( | ||||
| 		listener, toplevel, wlr_toplevel.on.request_activate); | ||||
| 
 | ||||
| 	/* In a multi-seat world we would select seat based on event->seat here. */ | ||||
| 	foreign_request_activate(toplevel); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| handle_request_close(struct wl_listener *listener, void *data) | ||||
| { | ||||
| 	struct foreign_toplevel *toplevel = wl_container_of( | ||||
| 		listener, toplevel, wlr_toplevel.on.request_close); | ||||
| 
 | ||||
| 	foreign_request_close(toplevel); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| handle_handle_destroy(struct wl_listener *listener, void *data) | ||||
| { | ||||
| 	struct wlr_foreign_toplevel *wlr_toplevel = | ||||
| 		wl_container_of(listener, wlr_toplevel, on.handle_destroy); | ||||
| 
 | ||||
| 	/* Client side requests */ | ||||
| 	wl_list_remove(&wlr_toplevel->on.request_maximize.link); | ||||
| 	wl_list_remove(&wlr_toplevel->on.request_minimize.link); | ||||
| 	wl_list_remove(&wlr_toplevel->on.request_fullscreen.link); | ||||
| 	wl_list_remove(&wlr_toplevel->on.request_activate.link); | ||||
| 	wl_list_remove(&wlr_toplevel->on.request_close.link); | ||||
| 	wl_list_remove(&wlr_toplevel->on.handle_destroy.link); | ||||
| 	wlr_toplevel->handle = NULL; | ||||
| } | ||||
| 
 | ||||
| /* Compositor signals */ | ||||
| static void | ||||
| handle_new_app_id(struct wl_listener *listener, void *data) | ||||
| { | ||||
| 	struct foreign_toplevel *toplevel = | ||||
| 		wl_container_of(listener, toplevel, wlr_toplevel.on_view.new_app_id); | ||||
| 	assert(toplevel->wlr_toplevel.handle); | ||||
| 
 | ||||
| 	const char *app_id = view_get_string_prop(toplevel->view, "app_id"); | ||||
| 	wlr_foreign_toplevel_handle_v1_set_app_id(toplevel->wlr_toplevel.handle, app_id); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| handle_new_title(struct wl_listener *listener, void *data) | ||||
| { | ||||
| 	struct foreign_toplevel *toplevel = | ||||
| 		wl_container_of(listener, toplevel, wlr_toplevel.on_view.new_title); | ||||
| 	assert(toplevel->wlr_toplevel.handle); | ||||
| 
 | ||||
| 	const char *title = view_get_string_prop(toplevel->view, "title"); | ||||
| 	wlr_foreign_toplevel_handle_v1_set_title(toplevel->wlr_toplevel.handle, title); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| handle_new_outputs(struct wl_listener *listener, void *data) | ||||
| { | ||||
| 	struct foreign_toplevel *toplevel = | ||||
| 		wl_container_of(listener, toplevel, wlr_toplevel.on_view.new_outputs); | ||||
| 	assert(toplevel->wlr_toplevel.handle); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Loop over all outputs and notify foreign_toplevel clients about changes. | ||||
| 	 * wlr_foreign_toplevel_handle_v1_output_xxx() keeps track of the active | ||||
| 	 * outputs internally and merges the events. It also listens to output | ||||
| 	 * destroy events so its fine to just relay the current state and let | ||||
| 	 * wlr_foreign_toplevel handle the rest. | ||||
| 	 */ | ||||
| 	struct output *output; | ||||
| 	wl_list_for_each(output, &toplevel->view->server->outputs, link) { | ||||
| 		if (view_on_output(toplevel->view, output)) { | ||||
| 			wlr_foreign_toplevel_handle_v1_output_enter( | ||||
| 				toplevel->wlr_toplevel.handle, output->wlr_output); | ||||
| 		} else { | ||||
| 			wlr_foreign_toplevel_handle_v1_output_leave( | ||||
| 				toplevel->wlr_toplevel.handle, output->wlr_output); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| handle_maximized(struct wl_listener *listener, void *data) | ||||
| { | ||||
| 	struct foreign_toplevel *toplevel = | ||||
| 		wl_container_of(listener, toplevel, wlr_toplevel.on_view.maximized); | ||||
| 	assert(toplevel->wlr_toplevel.handle); | ||||
| 
 | ||||
| 	wlr_foreign_toplevel_handle_v1_set_maximized( | ||||
| 		toplevel->wlr_toplevel.handle, | ||||
| 		toplevel->view->maximized == VIEW_AXIS_BOTH); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| handle_minimized(struct wl_listener *listener, void *data) | ||||
| { | ||||
| 	struct foreign_toplevel *toplevel = | ||||
| 		wl_container_of(listener, toplevel, wlr_toplevel.on_view.minimized); | ||||
| 	assert(toplevel->wlr_toplevel.handle); | ||||
| 
 | ||||
| 	wlr_foreign_toplevel_handle_v1_set_minimized( | ||||
| 		toplevel->wlr_toplevel.handle, toplevel->view->minimized); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| handle_fullscreened(struct wl_listener *listener, void *data) | ||||
| { | ||||
| 	struct foreign_toplevel *toplevel = | ||||
| 		wl_container_of(listener, toplevel, wlr_toplevel.on_view.fullscreened); | ||||
| 	assert(toplevel->wlr_toplevel.handle); | ||||
| 
 | ||||
| 	wlr_foreign_toplevel_handle_v1_set_fullscreen( | ||||
| 		toplevel->wlr_toplevel.handle, toplevel->view->fullscreen); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| handle_activated(struct wl_listener *listener, void *data) | ||||
| { | ||||
| 	struct foreign_toplevel *toplevel = | ||||
| 		wl_container_of(listener, toplevel, wlr_toplevel.on_view.activated); | ||||
| 	assert(toplevel->wlr_toplevel.handle); | ||||
| 
 | ||||
| 	bool *activated = data; | ||||
| 	wlr_foreign_toplevel_handle_v1_set_activated( | ||||
| 		toplevel->wlr_toplevel.handle, *activated); | ||||
| } | ||||
| 
 | ||||
| /* Internal signals */ | ||||
| static void | ||||
| handle_toplevel_parent(struct wl_listener *listener, void *data) | ||||
| { | ||||
| 	struct wlr_foreign_toplevel *wlr_toplevel = wl_container_of( | ||||
| 		listener, wlr_toplevel, on_foreign_toplevel.toplevel_parent); | ||||
| 	struct foreign_toplevel *parent = data; | ||||
| 
 | ||||
| 	assert(wlr_toplevel->handle); | ||||
| 
 | ||||
| 	/* The wlroots wlr-foreign-toplevel impl ensures parent is reset to NULL on destroy */ | ||||
| 	wlr_foreign_toplevel_handle_v1_set_parent(wlr_toplevel->handle, parent | ||||
| 		? parent->wlr_toplevel.handle | ||||
| 		: NULL); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| handle_toplevel_destroy(struct wl_listener *listener, void *data) | ||||
| { | ||||
| 	struct wlr_foreign_toplevel *wlr_toplevel = wl_container_of( | ||||
| 		listener, wlr_toplevel, on_foreign_toplevel.toplevel_destroy); | ||||
| 
 | ||||
| 	assert(wlr_toplevel->handle); | ||||
| 	wlr_foreign_toplevel_handle_v1_destroy(wlr_toplevel->handle); | ||||
| 
 | ||||
| 	/* Compositor side state changes */ | ||||
| 	wl_list_remove(&wlr_toplevel->on_view.new_app_id.link); | ||||
| 	wl_list_remove(&wlr_toplevel->on_view.new_title.link); | ||||
| 	wl_list_remove(&wlr_toplevel->on_view.new_outputs.link); | ||||
| 	wl_list_remove(&wlr_toplevel->on_view.maximized.link); | ||||
| 	wl_list_remove(&wlr_toplevel->on_view.minimized.link); | ||||
| 	wl_list_remove(&wlr_toplevel->on_view.fullscreened.link); | ||||
| 	wl_list_remove(&wlr_toplevel->on_view.activated.link); | ||||
| 
 | ||||
| 	/* Internal signals */ | ||||
| 	wl_list_remove(&wlr_toplevel->on_foreign_toplevel.toplevel_parent.link); | ||||
| 	wl_list_remove(&wlr_toplevel->on_foreign_toplevel.toplevel_destroy.link); | ||||
| } | ||||
| 
 | ||||
| /* Internal API */ | ||||
| void | ||||
| wlr_foreign_toplevel_init(struct foreign_toplevel *toplevel) | ||||
| { | ||||
| 	struct wlr_foreign_toplevel *wlr_toplevel = &toplevel->wlr_toplevel; | ||||
| 	struct view *view = toplevel->view; | ||||
| 
 | ||||
| 	assert(view->server->foreign_toplevel_manager); | ||||
| 
 | ||||
| 	wlr_toplevel->handle = wlr_foreign_toplevel_handle_v1_create( | ||||
| 		view->server->foreign_toplevel_manager); | ||||
| 	if (!wlr_toplevel->handle) { | ||||
| 		wlr_log(WLR_ERROR, "cannot create wlr foreign toplevel handle for (%s)", | ||||
| 			view_get_string_prop(view, "title")); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Client side requests */ | ||||
| 	CONNECT_SIGNAL(wlr_toplevel->handle, &wlr_toplevel->on, request_maximize); | ||||
| 	CONNECT_SIGNAL(wlr_toplevel->handle, &wlr_toplevel->on, request_minimize); | ||||
| 	CONNECT_SIGNAL(wlr_toplevel->handle, &wlr_toplevel->on, request_fullscreen); | ||||
| 	CONNECT_SIGNAL(wlr_toplevel->handle, &wlr_toplevel->on, request_activate); | ||||
| 	CONNECT_SIGNAL(wlr_toplevel->handle, &wlr_toplevel->on, request_close); | ||||
| 	wlr_toplevel->on.handle_destroy.notify = handle_handle_destroy; | ||||
| 	wl_signal_add(&wlr_toplevel->handle->events.destroy, &wlr_toplevel->on.handle_destroy); | ||||
| 
 | ||||
| 	/* Compositor side state changes */ | ||||
| 	CONNECT_SIGNAL(view, &wlr_toplevel->on_view, new_app_id); | ||||
| 	CONNECT_SIGNAL(view, &wlr_toplevel->on_view, new_title); | ||||
| 	CONNECT_SIGNAL(view, &wlr_toplevel->on_view, new_outputs); | ||||
| 	CONNECT_SIGNAL(view, &wlr_toplevel->on_view, maximized); | ||||
| 	CONNECT_SIGNAL(view, &wlr_toplevel->on_view, minimized); | ||||
| 	CONNECT_SIGNAL(view, &wlr_toplevel->on_view, fullscreened); | ||||
| 	CONNECT_SIGNAL(view, &wlr_toplevel->on_view, activated); | ||||
| 
 | ||||
| 	/* Internal signals */ | ||||
| 	CONNECT_SIGNAL(toplevel, &wlr_toplevel->on_foreign_toplevel, toplevel_parent); | ||||
| 	CONNECT_SIGNAL(toplevel, &wlr_toplevel->on_foreign_toplevel, toplevel_destroy); | ||||
| } | ||||
							
								
								
									
										127
									
								
								src/foreign.c
									
										
									
									
									
								
							
							
						
						
									
										127
									
								
								src/foreign.c
									
										
									
									
									
								
							|  | @ -1,127 +0,0 @@ | |||
| // SPDX-License-Identifier: GPL-2.0-only
 | ||||
| #include <assert.h> | ||||
| #include "labwc.h" | ||||
| #include "view.h" | ||||
| #include "workspaces.h" | ||||
| 
 | ||||
| static void | ||||
| handle_request_minimize(struct wl_listener *listener, void *data) | ||||
| { | ||||
| 	struct view *view = wl_container_of(listener, view, toplevel.minimize); | ||||
| 	struct wlr_foreign_toplevel_handle_v1_minimized_event *event = data; | ||||
| 	view_minimize(view, event->minimized); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| handle_request_maximize(struct wl_listener *listener, void *data) | ||||
| { | ||||
| 	struct view *view = wl_container_of(listener, view, toplevel.maximize); | ||||
| 	struct wlr_foreign_toplevel_handle_v1_maximized_event *event = data; | ||||
| 	view_maximize(view, event->maximized ? VIEW_AXIS_BOTH : VIEW_AXIS_NONE, | ||||
| 		/*store_natural_geometry*/ true); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| handle_request_fullscreen(struct wl_listener *listener, void *data) | ||||
| { | ||||
| 	struct view *view = wl_container_of(listener, view, toplevel.fullscreen); | ||||
| 	struct wlr_foreign_toplevel_handle_v1_fullscreen_event *event = data; | ||||
| 	view_set_fullscreen(view, event->fullscreen); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| handle_request_activate(struct wl_listener *listener, void *data) | ||||
| { | ||||
| 	struct view *view = wl_container_of(listener, view, toplevel.activate); | ||||
| 	// struct wlr_foreign_toplevel_handle_v1_activated_event *event = data;
 | ||||
| 
 | ||||
| 	if (view->server->osd_state.cycle_view) { | ||||
| 		wlr_log(WLR_INFO, "Preventing focus request while in window switcher"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	/* In a multi-seat world we would select seat based on event->seat here. */ | ||||
| 	desktop_focus_view(view, /*raise*/ true); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| handle_request_close(struct wl_listener *listener, void *data) | ||||
| { | ||||
| 	struct view *view = wl_container_of(listener, view, toplevel.close); | ||||
| 	view_close(view); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| handle_destroy(struct wl_listener *listener, void *data) | ||||
| { | ||||
| 	struct view *view = wl_container_of(listener, view, toplevel.destroy); | ||||
| 	struct foreign_toplevel *toplevel = &view->toplevel; | ||||
| 	wl_list_remove(&toplevel->maximize.link); | ||||
| 	wl_list_remove(&toplevel->minimize.link); | ||||
| 	wl_list_remove(&toplevel->fullscreen.link); | ||||
| 	wl_list_remove(&toplevel->activate.link); | ||||
| 	wl_list_remove(&toplevel->close.link); | ||||
| 	wl_list_remove(&toplevel->destroy.link); | ||||
| 	toplevel->handle = NULL; | ||||
| } | ||||
| 
 | ||||
| void | ||||
| foreign_toplevel_handle_create(struct view *view) | ||||
| { | ||||
| 	struct foreign_toplevel *toplevel = &view->toplevel; | ||||
| 
 | ||||
| 	toplevel->handle = wlr_foreign_toplevel_handle_v1_create( | ||||
| 		view->server->foreign_toplevel_manager); | ||||
| 	if (!toplevel->handle) { | ||||
| 		wlr_log(WLR_ERROR, "cannot create foreign toplevel handle for (%s)", | ||||
| 			view_get_string_prop(view, "title")); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	toplevel->maximize.notify = handle_request_maximize; | ||||
| 	wl_signal_add(&toplevel->handle->events.request_maximize, | ||||
| 		&toplevel->maximize); | ||||
| 
 | ||||
| 	toplevel->minimize.notify = handle_request_minimize; | ||||
| 	wl_signal_add(&toplevel->handle->events.request_minimize, | ||||
| 		&toplevel->minimize); | ||||
| 
 | ||||
| 	toplevel->fullscreen.notify = handle_request_fullscreen; | ||||
| 	wl_signal_add(&toplevel->handle->events.request_fullscreen, | ||||
| 		&toplevel->fullscreen); | ||||
| 
 | ||||
| 	toplevel->activate.notify = handle_request_activate; | ||||
| 	wl_signal_add(&toplevel->handle->events.request_activate, | ||||
| 		&toplevel->activate); | ||||
| 
 | ||||
| 	toplevel->close.notify = handle_request_close; | ||||
| 	wl_signal_add(&toplevel->handle->events.request_close, | ||||
| 		&toplevel->close); | ||||
| 
 | ||||
| 	toplevel->destroy.notify = handle_destroy; | ||||
| 	wl_signal_add(&toplevel->handle->events.destroy, &toplevel->destroy); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Loop over all outputs and notify foreign_toplevel clients about changes. | ||||
|  * wlr_foreign_toplevel_handle_v1_output_xxx() keeps track of the active | ||||
|  * outputs internally and merges the events. It also listens to output | ||||
|  * destroy events so its fine to just relay the current state and let | ||||
|  * wlr_foreign_toplevel handle the rest. | ||||
|  */ | ||||
| void | ||||
| foreign_toplevel_update_outputs(struct view *view) | ||||
| { | ||||
| 	assert(view->toplevel.handle); | ||||
| 
 | ||||
| 	struct output *output; | ||||
| 	wl_list_for_each(output, &view->server->outputs, link) { | ||||
| 		if (view_on_output(view, output)) { | ||||
| 			wlr_foreign_toplevel_handle_v1_output_enter( | ||||
| 				view->toplevel.handle, output->wlr_output); | ||||
| 		} else { | ||||
| 			wlr_foreign_toplevel_handle_v1_output_leave( | ||||
| 				view->toplevel.handle, output->wlr_output); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -1001,7 +1001,7 @@ update_client_list_combined_menu(struct server *server) | |||
| 		wl_list_for_each(view, &server->views, link) { | ||||
| 			if (view->workspace == workspace) { | ||||
| 				const char *title = view_get_string_prop(view, "title"); | ||||
| 				if (!view->toplevel.handle || string_null_or_empty(title)) { | ||||
| 				if (!view->foreign_toplevel || string_null_or_empty(title)) { | ||||
| 					continue; | ||||
| 				} | ||||
| 
 | ||||
|  |  | |||
|  | @ -5,7 +5,6 @@ labwc_sources = files( | |||
|   'desktop.c', | ||||
|   'dnd.c', | ||||
|   'edges.c', | ||||
|   'foreign.c', | ||||
|   'idle.c', | ||||
|   'interactive.c', | ||||
|   'layers.c', | ||||
|  | @ -54,6 +53,7 @@ subdir('img') | |||
| subdir('common') | ||||
| subdir('config') | ||||
| subdir('decorations') | ||||
| subdir('foreign-toplevel') | ||||
| subdir('input') | ||||
| subdir('menu') | ||||
| subdir('ssd') | ||||
|  |  | |||
|  | @ -9,6 +9,7 @@ | |||
| #include <wlr/types/wlr_data_control_v1.h> | ||||
| #include <wlr/types/wlr_drm.h> | ||||
| #include <wlr/types/wlr_export_dmabuf_v1.h> | ||||
| #include <wlr/types/wlr_foreign_toplevel_management_v1.h> | ||||
| #include <wlr/types/wlr_fractional_scale_v1.h> | ||||
| #include <wlr/types/wlr_gamma_control_v1.h> | ||||
| #include <wlr/types/wlr_presentation_time.h> | ||||
|  | @ -18,6 +19,7 @@ | |||
| #include <wlr/types/wlr_single_pixel_buffer_v1.h> | ||||
| #include <wlr/types/wlr_viewporter.h> | ||||
| #include <wlr/types/wlr_tablet_v2.h> | ||||
| 
 | ||||
| #if HAVE_XWAYLAND | ||||
| #include <wlr/xwayland.h> | ||||
| #include "xwayland-shell-v1-protocol.h" | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ | |||
| #include <stdio.h> | ||||
| #include <strings.h> | ||||
| #include "common/list.h" | ||||
| #include "foreign-toplevel.h" | ||||
| #include "labwc.h" | ||||
| #include "view.h" | ||||
| #include "view-impl-common.h" | ||||
|  | @ -41,8 +42,9 @@ view_impl_map(struct view *view) | |||
| 	 */ | ||||
| 	enum property ret = window_rules_get_property(view, "skipTaskbar"); | ||||
| 	if (ret == LAB_PROP_TRUE) { | ||||
| 		if (view->toplevel.handle) { | ||||
| 			wlr_foreign_toplevel_handle_v1_destroy(view->toplevel.handle); | ||||
| 		if (view->foreign_toplevel) { | ||||
| 			foreign_toplevel_destroy(view->foreign_toplevel); | ||||
| 			view->foreign_toplevel = NULL; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										34
									
								
								src/view.c
									
										
									
									
									
								
							
							
						
						
									
										34
									
								
								src/view.c
									
										
									
									
									
								
							|  | @ -10,6 +10,7 @@ | |||
| #include "common/mem.h" | ||||
| #include "common/parse-bool.h" | ||||
| #include "common/scene-helpers.h" | ||||
| #include "foreign-toplevel.h" | ||||
| #include "input/keyboard.h" | ||||
| #include "labwc.h" | ||||
| #include "menu/menu.h" | ||||
|  | @ -469,10 +470,6 @@ view_set_activated(struct view *view, bool activated) | |||
| 	if (view->impl->set_activated) { | ||||
| 		view->impl->set_activated(view, activated); | ||||
| 	} | ||||
| 	if (view->toplevel.handle) { | ||||
| 		wlr_foreign_toplevel_handle_v1_set_activated( | ||||
| 			view->toplevel.handle, activated); | ||||
| 	} | ||||
| 
 | ||||
| 	wl_signal_emit_mutable(&view->events.activated, &activated); | ||||
| 
 | ||||
|  | @ -529,9 +526,6 @@ view_update_outputs(struct view *view) | |||
| 
 | ||||
| 	if (new_outputs != view->outputs) { | ||||
| 		view->outputs = new_outputs; | ||||
| 		if (view->toplevel.handle) { | ||||
| 			foreign_toplevel_update_outputs(view); | ||||
| 		} | ||||
| 		wl_signal_emit_mutable(&view->events.new_outputs, NULL); | ||||
| 		desktop_update_top_layer_visiblity(view->server); | ||||
| 	} | ||||
|  | @ -738,10 +732,7 @@ _minimize(struct view *view, bool minimized) | |||
| 	if (view->minimized == minimized) { | ||||
| 		return; | ||||
| 	} | ||||
| 	if (view->toplevel.handle) { | ||||
| 		wlr_foreign_toplevel_handle_v1_set_minimized( | ||||
| 			view->toplevel.handle, minimized); | ||||
| 	} | ||||
| 
 | ||||
| 	if (view->impl->minimize) { | ||||
| 		view->impl->minimize(view, minimized); | ||||
| 	} | ||||
|  | @ -1312,10 +1303,6 @@ set_maximized(struct view *view, enum view_axis maximized) | |||
| 	if (view->impl->maximize) { | ||||
| 		view->impl->maximize(view, (maximized == VIEW_AXIS_BOTH)); | ||||
| 	} | ||||
| 	if (view->toplevel.handle) { | ||||
| 		wlr_foreign_toplevel_handle_v1_set_maximized( | ||||
| 			view->toplevel.handle, (maximized == VIEW_AXIS_BOTH)); | ||||
| 	} | ||||
| 
 | ||||
| 	view->maximized = maximized; | ||||
| 	wl_signal_emit_mutable(&view->events.maximized, NULL); | ||||
|  | @ -1676,10 +1663,6 @@ set_fullscreen(struct view *view, bool fullscreen) | |||
| 	if (view->impl->set_fullscreen) { | ||||
| 		view->impl->set_fullscreen(view, fullscreen); | ||||
| 	} | ||||
| 	if (view->toplevel.handle) { | ||||
| 		wlr_foreign_toplevel_handle_v1_set_fullscreen( | ||||
| 			view->toplevel.handle, fullscreen); | ||||
| 	} | ||||
| 
 | ||||
| 	view->fullscreen = fullscreen; | ||||
| 	wl_signal_emit_mutable(&view->events.fullscreened, NULL); | ||||
|  | @ -2337,11 +2320,11 @@ view_update_title(struct view *view) | |||
| { | ||||
| 	assert(view); | ||||
| 	const char *title = view_get_string_prop(view, "title"); | ||||
| 	if (!view->toplevel.handle || !title) { | ||||
| 	if (!title) { | ||||
| 		return; | ||||
| 	} | ||||
| 	ssd_update_title(view->ssd); | ||||
| 	wlr_foreign_toplevel_handle_v1_set_title(view->toplevel.handle, title); | ||||
| 
 | ||||
| 	wl_signal_emit_mutable(&view->events.new_title, NULL); | ||||
| } | ||||
| 
 | ||||
|  | @ -2350,11 +2333,9 @@ view_update_app_id(struct view *view) | |||
| { | ||||
| 	assert(view); | ||||
| 	const char *app_id = view_get_string_prop(view, "app_id"); | ||||
| 	if (!view->toplevel.handle || !app_id) { | ||||
| 	if (!app_id) { | ||||
| 		return; | ||||
| 	} | ||||
| 	wlr_foreign_toplevel_handle_v1_set_app_id( | ||||
| 		view->toplevel.handle, app_id); | ||||
| 
 | ||||
| 	if (view->ssd_enabled) { | ||||
| 		ssd_update_window_icon(view->ssd); | ||||
|  | @ -2512,8 +2493,9 @@ view_destroy(struct view *view) | |||
| 	wl_list_remove(&view->set_title.link); | ||||
| 	wl_list_remove(&view->destroy.link); | ||||
| 
 | ||||
| 	if (view->toplevel.handle) { | ||||
| 		wlr_foreign_toplevel_handle_v1_destroy(view->toplevel.handle); | ||||
| 	if (view->foreign_toplevel) { | ||||
| 		foreign_toplevel_destroy(view->foreign_toplevel); | ||||
| 		view->foreign_toplevel = NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	if (server->grabbed_view == view) { | ||||
|  |  | |||
							
								
								
									
										28
									
								
								src/xdg.c
									
										
									
									
									
								
							
							
						
						
									
										28
									
								
								src/xdg.c
									
										
									
									
									
								
							|  | @ -6,6 +6,7 @@ | |||
| #include "common/macros.h" | ||||
| #include "common/mem.h" | ||||
| #include "decorations.h" | ||||
| #include "foreign-toplevel.h" | ||||
| #include "labwc.h" | ||||
| #include "menu/menu.h" | ||||
| #include "node.h" | ||||
|  | @ -670,17 +671,19 @@ xdg_toplevel_view_get_string_prop(struct view *view, const char *prop) | |||
| static void | ||||
| init_foreign_toplevel(struct view *view) | ||||
| { | ||||
| 	foreign_toplevel_handle_create(view); | ||||
| 	assert(!view->foreign_toplevel); | ||||
| 	view->foreign_toplevel = foreign_toplevel_create(view); | ||||
| 
 | ||||
| 	struct wlr_xdg_toplevel *toplevel = xdg_toplevel_from_view(view); | ||||
| 	if (!toplevel->parent) { | ||||
| 		return; | ||||
| 	} | ||||
| 	struct wlr_xdg_surface *surface = toplevel->parent->base; | ||||
| 	struct view *parent = surface->data; | ||||
| 	if (!parent->toplevel.handle) { | ||||
| 	if (!parent->foreign_toplevel) { | ||||
| 		return; | ||||
| 	} | ||||
| 	wlr_foreign_toplevel_handle_v1_set_parent(view->toplevel.handle, parent->toplevel.handle); | ||||
| 	foreign_toplevel_set_parent(view->foreign_toplevel, parent->foreign_toplevel); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
|  | @ -701,6 +704,15 @@ xdg_toplevel_view_map(struct view *view) | |||
| 	} | ||||
| 	struct wlr_xdg_surface *xdg_surface = xdg_surface_from_view(view); | ||||
| 	wlr_scene_node_set_enabled(&view->scene_tree->node, true); | ||||
| 
 | ||||
| 	if (!view->foreign_toplevel) { | ||||
| 		init_foreign_toplevel(view); | ||||
| 		/*
 | ||||
| 		 * Initial outputs will be synced via | ||||
| 		 * view->events.new_outputs on view_moved() | ||||
| 		 */ | ||||
| 	} | ||||
| 
 | ||||
| 	if (!view->been_mapped) { | ||||
| 		if (view_wants_decorations(view)) { | ||||
| 			view_set_ssd_mode(view, LAB_SSD_MODE_FULL); | ||||
|  | @ -736,11 +748,6 @@ xdg_toplevel_view_map(struct view *view) | |||
| 		view_moved(view); | ||||
| 	} | ||||
| 
 | ||||
| 	if (!view->toplevel.handle) { | ||||
| 		init_foreign_toplevel(view); | ||||
| 		foreign_toplevel_update_outputs(view); | ||||
| 	} | ||||
| 
 | ||||
| 	view_impl_map(view); | ||||
| 	view->been_mapped = true; | ||||
| } | ||||
|  | @ -759,8 +766,9 @@ xdg_toplevel_view_unmap(struct view *view, bool client_request) | |||
| 	 * than just minimized), destroy the foreign toplevel handle so | ||||
| 	 * the unmapped view doesn't show up in panels and the like. | ||||
| 	 */ | ||||
| 	if (client_request && view->toplevel.handle) { | ||||
| 		wlr_foreign_toplevel_handle_v1_destroy(view->toplevel.handle); | ||||
| 	if (client_request && view->foreign_toplevel) { | ||||
| 		foreign_toplevel_destroy(view->foreign_toplevel); | ||||
| 		view->foreign_toplevel = NULL; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -8,6 +8,7 @@ | |||
| #include "common/mem.h" | ||||
| #include "config/rcxml.h" | ||||
| #include "config/session.h" | ||||
| #include "foreign-toplevel.h" | ||||
| #include "labwc.h" | ||||
| #include "node.h" | ||||
| #include "ssd.h" | ||||
|  | @ -669,17 +670,18 @@ set_initial_position(struct view *view, | |||
| static void | ||||
| init_foreign_toplevel(struct view *view) | ||||
| { | ||||
| 	foreign_toplevel_handle_create(view); | ||||
| 	assert(!view->foreign_toplevel); | ||||
| 	view->foreign_toplevel = foreign_toplevel_create(view); | ||||
| 
 | ||||
| 	struct wlr_xwayland_surface *surface = xwayland_surface_from_view(view); | ||||
| 	if (!surface->parent) { | ||||
| 		return; | ||||
| 	} | ||||
| 	struct view *parent = (struct view *)surface->parent->data; | ||||
| 	if (!parent || !parent->toplevel.handle) { | ||||
| 	if (!parent || !parent->foreign_toplevel) { | ||||
| 		return; | ||||
| 	} | ||||
| 	wlr_foreign_toplevel_handle_v1_set_parent(view->toplevel.handle, parent->toplevel.handle); | ||||
| 	foreign_toplevel_set_parent(view->foreign_toplevel, parent->foreign_toplevel); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
|  | @ -736,6 +738,19 @@ xwayland_view_map(struct view *view) | |||
| 		view->scene_node = &tree->node; | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Exclude unfocusable views from wlr-foreign-toplevel. These | ||||
| 	 * views (notifications, floating toolbars, etc.) should not be | ||||
| 	 * shown in taskbars/docks/etc. | ||||
| 	 */ | ||||
| 	if (!view->foreign_toplevel && view_is_focusable(view)) { | ||||
| 		init_foreign_toplevel(view); | ||||
| 		/*
 | ||||
| 		 * Initial outputs will be synced via | ||||
| 		 * view->events.new_outputs on view_moved() | ||||
| 		 */ | ||||
| 	} | ||||
| 
 | ||||
| 	if (!view->been_mapped) { | ||||
| 		check_natural_geometry(view); | ||||
| 		set_initial_position(view, xwayland_surface); | ||||
|  | @ -749,16 +764,6 @@ xwayland_view_map(struct view *view) | |||
| 		view_moved(view); | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Exclude unfocusable views from wlr-foreign-toplevel. These | ||||
| 	 * views (notifications, floating toolbars, etc.) should not be | ||||
| 	 * shown in taskbars/docks/etc. | ||||
| 	 */ | ||||
| 	if (!view->toplevel.handle && view_is_focusable(view)) { | ||||
| 		init_foreign_toplevel(view); | ||||
| 		foreign_toplevel_update_outputs(view); | ||||
| 	} | ||||
| 
 | ||||
| 	/* Add commit here, as xwayland map/unmap can change the wlr_surface */ | ||||
| 	wl_signal_add(&xwayland_surface->surface->events.commit, &view->commit); | ||||
| 	view->commit.notify = handle_commit; | ||||
|  | @ -794,8 +799,9 @@ xwayland_view_unmap(struct view *view, bool client_request) | |||
| 	 * the unmapped view doesn't show up in panels and the like. | ||||
| 	 */ | ||||
| out: | ||||
| 	if (client_request && view->toplevel.handle) { | ||||
| 		wlr_foreign_toplevel_handle_v1_destroy(view->toplevel.handle); | ||||
| 	if (client_request && view->foreign_toplevel) { | ||||
| 		foreign_toplevel_destroy(view->foreign_toplevel); | ||||
| 		view->foreign_toplevel = NULL; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Consolatis
						Consolatis