parent
							
								
									a671fc51d2
								
							
						
					
					
						commit
						736632ad4e
					
				| @ -1,183 +0,0 @@ | ||||
| /*
 | ||||
|  * This protocol is obsolete and will be removed in a future version. The | ||||
|  * recommended replacement is xdg-shell. | ||||
|  */ | ||||
| 
 | ||||
| /*
 | ||||
|  * This an unstable interface of wlroots. No guarantees are made regarding the | ||||
|  * future consistency of this API. | ||||
|  */ | ||||
| #ifndef WLR_USE_UNSTABLE | ||||
| #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" | ||||
| #endif | ||||
| 
 | ||||
| #ifndef WLR_TYPES_WLR_WL_SHELL_H | ||||
| #define WLR_TYPES_WLR_WL_SHELL_H | ||||
| 
 | ||||
| #include <stdbool.h> | ||||
| #include <wayland-server.h> | ||||
| #include <wlr/types/wlr_seat.h> | ||||
| 
 | ||||
| /**
 | ||||
|  * A very basic interface to provide desktop-style user interfaces. | ||||
|  */ | ||||
| struct wlr_wl_shell { | ||||
| 	struct wl_global *global; | ||||
| 	struct wl_list resources; | ||||
| 	struct wl_list surfaces; | ||||
| 	struct wl_list popup_grabs; | ||||
| 	uint32_t ping_timeout; | ||||
| 
 | ||||
| 	struct wl_listener display_destroy; | ||||
| 
 | ||||
| 	struct { | ||||
| 		struct wl_signal new_surface; | ||||
| 		struct wl_signal destroy; | ||||
| 	} events; | ||||
| 
 | ||||
| 	void *data; | ||||
| }; | ||||
| 
 | ||||
| struct wlr_wl_shell_surface_transient_state { | ||||
| 	int32_t x; | ||||
| 	int32_t y; | ||||
| 	enum wl_shell_surface_transient flags; | ||||
| }; | ||||
| 
 | ||||
| struct wlr_wl_shell_surface_popup_state { | ||||
| 	struct wlr_seat *seat; | ||||
| 	uint32_t serial; | ||||
| }; | ||||
| 
 | ||||
| // each seat gets a popup grab
 | ||||
| struct wlr_wl_shell_popup_grab { | ||||
| 	struct wl_client *client; | ||||
| 	struct wlr_seat_pointer_grab pointer_grab; | ||||
| 	struct wlr_seat *seat; | ||||
| 	struct wl_list popups; | ||||
| 	struct wl_list link; // wlr_wl_shell::popup_grabs
 | ||||
| }; | ||||
| 
 | ||||
| enum wlr_wl_shell_surface_state { | ||||
| 	WLR_WL_SHELL_SURFACE_STATE_NONE, | ||||
| 	WLR_WL_SHELL_SURFACE_STATE_TOPLEVEL, | ||||
| 	WLR_WL_SHELL_SURFACE_STATE_MAXIMIZED, | ||||
| 	WLR_WL_SHELL_SURFACE_STATE_FULLSCREEN, | ||||
| 	WLR_WL_SHELL_SURFACE_STATE_TRANSIENT, | ||||
| 	WLR_WL_SHELL_SURFACE_STATE_POPUP, | ||||
| }; | ||||
| 
 | ||||
| struct wlr_wl_shell_surface { | ||||
| 	struct wlr_wl_shell *shell; | ||||
| 	struct wl_client *client; | ||||
| 	struct wl_resource *resource; | ||||
| 	struct wlr_surface *surface; | ||||
| 	bool configured; | ||||
| 	struct wl_list link; // wlr_wl_shell::surfaces
 | ||||
| 
 | ||||
| 	uint32_t ping_serial; | ||||
| 	struct wl_event_source *ping_timer; | ||||
| 
 | ||||
| 	enum wlr_wl_shell_surface_state state; | ||||
| 	struct wlr_wl_shell_surface_transient_state *transient_state; | ||||
| 	struct wlr_wl_shell_surface_popup_state *popup_state; | ||||
| 	struct wl_list grab_link; // wlr_wl_shell_popup_grab::popups
 | ||||
| 
 | ||||
| 	char *title; | ||||
| 	char *class; | ||||
| 
 | ||||
| 	struct wl_listener surface_destroy; | ||||
| 
 | ||||
| 	struct wlr_wl_shell_surface *parent; | ||||
| 	struct wl_list popup_link; | ||||
| 	struct wl_list popups; | ||||
| 	bool popup_mapped; | ||||
| 
 | ||||
| 	struct { | ||||
| 		struct wl_signal destroy; | ||||
| 		struct wl_signal ping_timeout; | ||||
| 		struct wl_signal new_popup; | ||||
| 
 | ||||
| 		struct wl_signal request_move; | ||||
| 		struct wl_signal request_resize; | ||||
| 		struct wl_signal request_fullscreen; | ||||
| 		struct wl_signal request_maximize; | ||||
| 
 | ||||
| 		struct wl_signal set_state; | ||||
| 		struct wl_signal set_title; | ||||
| 		struct wl_signal set_class; | ||||
| 	} events; | ||||
| 
 | ||||
| 	void *data; | ||||
| }; | ||||
| 
 | ||||
| struct wlr_wl_shell_surface_move_event { | ||||
| 	struct wlr_wl_shell_surface *surface; | ||||
| 	struct wlr_seat_client *seat; | ||||
| 	uint32_t serial; | ||||
| }; | ||||
| 
 | ||||
| struct wlr_wl_shell_surface_resize_event { | ||||
| 	struct wlr_wl_shell_surface *surface; | ||||
| 	struct wlr_seat_client *seat; | ||||
| 	uint32_t serial; | ||||
| 	enum wl_shell_surface_resize edges; | ||||
| }; | ||||
| 
 | ||||
| struct wlr_wl_shell_surface_set_fullscreen_event { | ||||
| 	struct wlr_wl_shell_surface *surface; | ||||
| 	enum wl_shell_surface_fullscreen_method method; | ||||
| 	uint32_t framerate; | ||||
| 	struct wlr_output *output; | ||||
| }; | ||||
| 
 | ||||
| struct wlr_wl_shell_surface_maximize_event { | ||||
| 	struct wlr_wl_shell_surface *surface; | ||||
| 	struct wlr_output *output; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * Create a wl_shell for this display. | ||||
|  */ | ||||
| struct wlr_wl_shell *wlr_wl_shell_create(struct wl_display *display); | ||||
| 
 | ||||
| /**
 | ||||
|  * Destroy this surface. | ||||
|  */ | ||||
| void wlr_wl_shell_destroy(struct wlr_wl_shell *wlr_wl_shell); | ||||
| 
 | ||||
| /**
 | ||||
|  * Send a ping to the surface. If the surface does not respond with a pong | ||||
|  * within a reasonable amount of time, the ping timeout event will be emitted. | ||||
|  */ | ||||
| void wlr_wl_shell_surface_ping(struct wlr_wl_shell_surface *surface); | ||||
| 
 | ||||
| /**
 | ||||
|  * Request that the surface configure itself to be the given size. | ||||
|  */ | ||||
| void wlr_wl_shell_surface_configure(struct wlr_wl_shell_surface *surface, | ||||
| 	enum wl_shell_surface_resize edges, int32_t width, int32_t height); | ||||
| 
 | ||||
| /**
 | ||||
|  * Find a surface within this wl-shell surface tree at the given surface-local | ||||
|  * coordinates. Returns the surface and coordinates in the leaf surface | ||||
|  * coordinate system or NULL if no surface is found at that location. | ||||
|  */ | ||||
| struct wlr_surface *wlr_wl_shell_surface_surface_at( | ||||
| 		struct wlr_wl_shell_surface *surface, double sx, double sy, | ||||
| 		double *sub_sx, double *sub_sy); | ||||
| 
 | ||||
| bool wlr_surface_is_wl_shell_surface(struct wlr_surface *surface); | ||||
| 
 | ||||
| struct wlr_wl_shell_surface *wlr_wl_shell_surface_from_wlr_surface( | ||||
| 		struct wlr_surface *surface); | ||||
| 
 | ||||
| /**
 | ||||
|  * Call `iterator` on each surface in the shell surface tree, with the surface's | ||||
|  * position relative to the root xdg-surface. The function is called from root to | ||||
|  * leaves (in rendering order). | ||||
|  */ | ||||
| void wlr_wl_shell_surface_for_each_surface(struct wlr_wl_shell_surface *surface, | ||||
| 	wlr_surface_iterator_func_t iterator, void *user_data); | ||||
| 
 | ||||
| #endif | ||||
| @ -1,312 +0,0 @@ | ||||
| #include <assert.h> | ||||
| #include <stdbool.h> | ||||
| #include <stdlib.h> | ||||
| #include <wayland-server.h> | ||||
| #include <wlr/types/wlr_box.h> | ||||
| #include <wlr/types/wlr_surface.h> | ||||
| #include <wlr/types/wlr_wl_shell.h> | ||||
| #include <wlr/util/log.h> | ||||
| #include "rootston/desktop.h" | ||||
| #include "rootston/input.h" | ||||
| #include "rootston/server.h" | ||||
| 
 | ||||
| static const struct roots_view_child_interface popup_impl; | ||||
| 
 | ||||
| static void popup_destroy(struct roots_view_child *child) { | ||||
| 	assert(child->impl == &popup_impl); | ||||
| 	struct roots_wl_shell_popup *popup = (struct roots_wl_shell_popup *)child; | ||||
| 	wl_list_remove(&popup->destroy.link); | ||||
| 	wl_list_remove(&popup->set_state.link); | ||||
| 	wl_list_remove(&popup->new_popup.link); | ||||
| 	free(popup); | ||||
| } | ||||
| 
 | ||||
| static const struct roots_view_child_interface popup_impl = { | ||||
| 	.destroy = popup_destroy, | ||||
| }; | ||||
| 
 | ||||
| static void popup_handle_destroy(struct wl_listener *listener, void *data) { | ||||
| 	struct roots_wl_shell_popup *popup = | ||||
| 		wl_container_of(listener, popup, destroy); | ||||
| 	view_child_destroy(&popup->view_child); | ||||
| } | ||||
| 
 | ||||
| static void popup_handle_set_state(struct wl_listener *listener, void *data) { | ||||
| 	struct roots_wl_shell_popup *popup = | ||||
| 		wl_container_of(listener, popup, set_state); | ||||
| 	view_child_destroy(&popup->view_child); | ||||
| } | ||||
| 
 | ||||
| static struct roots_wl_shell_popup *popup_create(struct roots_view *view, | ||||
| 	struct wlr_wl_shell_surface *wlr_wl_shell_surface); | ||||
| 
 | ||||
| static void popup_handle_new_popup(struct wl_listener *listener, void *data) { | ||||
| 	struct roots_wl_shell_popup *popup = | ||||
| 		wl_container_of(listener, popup, new_popup); | ||||
| 	struct wlr_wl_shell_surface *wlr_wl_shell_surface = data; | ||||
| 	popup_create(popup->view_child.view, wlr_wl_shell_surface); | ||||
| } | ||||
| 
 | ||||
| static struct roots_wl_shell_popup *popup_create(struct roots_view *view, | ||||
| 		struct wlr_wl_shell_surface *wlr_wl_shell_surface) { | ||||
| 	struct roots_wl_shell_popup *popup = | ||||
| 		calloc(1, sizeof(struct roots_wl_shell_popup)); | ||||
| 	if (popup == NULL) { | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	popup->wlr_wl_shell_surface = wlr_wl_shell_surface; | ||||
| 	view_child_init(&popup->view_child, &popup_impl, | ||||
| 		view, wlr_wl_shell_surface->surface); | ||||
| 	popup->destroy.notify = popup_handle_destroy; | ||||
| 	wl_signal_add(&wlr_wl_shell_surface->events.destroy, &popup->destroy); | ||||
| 	popup->set_state.notify = popup_handle_set_state; | ||||
| 	wl_signal_add(&wlr_wl_shell_surface->events.set_state, &popup->set_state); | ||||
| 	popup->new_popup.notify = popup_handle_new_popup; | ||||
| 	wl_signal_add(&wlr_wl_shell_surface->events.new_popup, &popup->new_popup); | ||||
| 	return popup; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static void resize(struct roots_view *view, uint32_t width, uint32_t height) { | ||||
| 	struct wlr_wl_shell_surface *surf = | ||||
| 		roots_wl_shell_surface_from_view(view)->wl_shell_surface; | ||||
| 	wlr_wl_shell_surface_configure(surf, WL_SHELL_SURFACE_RESIZE_NONE, width, | ||||
| 		height); | ||||
| } | ||||
| 
 | ||||
| static void close(struct roots_view *view) { | ||||
| 	struct wlr_wl_shell_surface *surf = | ||||
| 		roots_wl_shell_surface_from_view(view)->wl_shell_surface; | ||||
| 	wl_client_destroy(surf->client); | ||||
| } | ||||
| 
 | ||||
| static void for_each_surface(struct roots_view *view, | ||||
| 		wlr_surface_iterator_func_t iterator, void *user_data) { | ||||
| 	struct wlr_wl_shell_surface *surf = | ||||
| 		roots_wl_shell_surface_from_view(view)->wl_shell_surface; | ||||
| 	wlr_wl_shell_surface_for_each_surface(surf, iterator, user_data); | ||||
| } | ||||
| 
 | ||||
| static void destroy(struct roots_view *view) { | ||||
| 	struct roots_wl_shell_surface *roots_surface = | ||||
| 		roots_wl_shell_surface_from_view(view); | ||||
| 	wl_list_remove(&roots_surface->destroy.link); | ||||
| 	wl_list_remove(&roots_surface->request_move.link); | ||||
| 	wl_list_remove(&roots_surface->request_resize.link); | ||||
| 	wl_list_remove(&roots_surface->request_maximize.link); | ||||
| 	wl_list_remove(&roots_surface->request_fullscreen.link); | ||||
| 	wl_list_remove(&roots_surface->set_state.link); | ||||
| 	wl_list_remove(&roots_surface->set_title.link); | ||||
| 	wl_list_remove(&roots_surface->set_class.link); | ||||
| 	wl_list_remove(&roots_surface->surface_commit.link); | ||||
| 	free(roots_surface); | ||||
| } | ||||
| 
 | ||||
| static const struct roots_view_interface view_impl = { | ||||
| 	.resize = resize, | ||||
| 	.close = close, | ||||
| 	.for_each_surface = for_each_surface, | ||||
| 	.destroy = destroy, | ||||
| }; | ||||
| 
 | ||||
| static void handle_request_move(struct wl_listener *listener, void *data) { | ||||
| 	struct roots_wl_shell_surface *roots_surface = | ||||
| 		wl_container_of(listener, roots_surface, request_move); | ||||
| 	struct roots_view *view = &roots_surface->view; | ||||
| 	struct roots_input *input = view->desktop->server->input; | ||||
| 	struct wlr_wl_shell_surface_move_event *e = data; | ||||
| 	struct roots_seat *seat = input_seat_from_wlr_seat(input, e->seat->seat); | ||||
| 	if (!seat || seat->cursor->mode != ROOTS_CURSOR_PASSTHROUGH) { | ||||
| 		return; | ||||
| 	} | ||||
| 	roots_seat_begin_move(seat, view); | ||||
| } | ||||
| 
 | ||||
| static void handle_request_resize(struct wl_listener *listener, void *data) { | ||||
| 	struct roots_wl_shell_surface *roots_surface = | ||||
| 		wl_container_of(listener, roots_surface, request_resize); | ||||
| 	struct roots_view *view = &roots_surface->view; | ||||
| 	struct roots_input *input = view->desktop->server->input; | ||||
| 	struct wlr_wl_shell_surface_resize_event *e = data; | ||||
| 	struct roots_seat *seat = input_seat_from_wlr_seat(input, e->seat->seat); | ||||
| 	if (!seat || seat->cursor->mode != ROOTS_CURSOR_PASSTHROUGH) { | ||||
| 		return; | ||||
| 	} | ||||
| 	roots_seat_begin_resize(seat, view, e->edges); | ||||
| } | ||||
| 
 | ||||
| static void handle_request_maximize(struct wl_listener *listener, | ||||
| 		void *data) { | ||||
| 	struct roots_wl_shell_surface *roots_surface = | ||||
| 		wl_container_of(listener, roots_surface, request_maximize); | ||||
| 	struct roots_view *view = &roots_surface->view; | ||||
| 	//struct wlr_wl_shell_surface_maximize_event *e = data;
 | ||||
| 	view_maximize(view, true); | ||||
| } | ||||
| 
 | ||||
| static void handle_request_fullscreen(struct wl_listener *listener, | ||||
| 		void *data) { | ||||
| 	struct roots_wl_shell_surface *roots_surface = | ||||
| 		wl_container_of(listener, roots_surface, request_fullscreen); | ||||
| 	struct roots_view *view = &roots_surface->view; | ||||
| 	struct wlr_wl_shell_surface_set_fullscreen_event *e = data; | ||||
| 	view_set_fullscreen(view, true, e->output); | ||||
| } | ||||
| 
 | ||||
| static void handle_set_state(struct wl_listener *listener, void *data) { | ||||
| 	struct roots_wl_shell_surface *roots_surface = | ||||
| 		wl_container_of(listener, roots_surface, set_state); | ||||
| 	struct roots_view *view = &roots_surface->view; | ||||
| 	struct wlr_wl_shell_surface *surface = roots_surface->wl_shell_surface; | ||||
| 	if (view->maximized && | ||||
| 			surface->state != WLR_WL_SHELL_SURFACE_STATE_MAXIMIZED) { | ||||
| 		view_maximize(view, false); | ||||
| 	} | ||||
| 	if (view->fullscreen_output != NULL && | ||||
| 			surface->state != WLR_WL_SHELL_SURFACE_STATE_FULLSCREEN) { | ||||
| 		view_set_fullscreen(view, false, NULL); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void handle_set_title(struct wl_listener *listener, void *data) { | ||||
| 	struct roots_wl_shell_surface *roots_surface = | ||||
| 		wl_container_of(listener, roots_surface, set_state); | ||||
| 	view_set_title(&roots_surface->view, | ||||
| 		roots_surface->wl_shell_surface->title); | ||||
| } | ||||
| 
 | ||||
| static void handle_set_class(struct wl_listener *listener, void *data) { | ||||
| 	struct roots_wl_shell_surface *roots_surface = | ||||
| 		wl_container_of(listener, roots_surface, set_state); | ||||
| 	view_set_app_id(&roots_surface->view, | ||||
| 		roots_surface->wl_shell_surface->class); | ||||
| } | ||||
| 
 | ||||
| static void handle_surface_commit(struct wl_listener *listener, void *data) { | ||||
| 	struct roots_wl_shell_surface *roots_surface = | ||||
| 		wl_container_of(listener, roots_surface, surface_commit); | ||||
| 	struct roots_view *view = &roots_surface->view; | ||||
| 	struct wlr_surface *wlr_surface = view->wlr_surface; | ||||
| 
 | ||||
| 	view_apply_damage(view); | ||||
| 
 | ||||
| 	int width = wlr_surface->current.width; | ||||
| 	int height = wlr_surface->current.height; | ||||
| 	view_update_size(view, width, height); | ||||
| 
 | ||||
| 	double x = view->box.x; | ||||
| 	double y = view->box.y; | ||||
| 	if (view->pending_move_resize.update_x) { | ||||
| 		x = view->pending_move_resize.x + view->pending_move_resize.width - | ||||
| 			width; | ||||
| 		view->pending_move_resize.update_x = false; | ||||
| 	} | ||||
| 	if (view->pending_move_resize.update_y) { | ||||
| 		y = view->pending_move_resize.y + view->pending_move_resize.height - | ||||
| 			height; | ||||
| 		view->pending_move_resize.update_y = false; | ||||
| 	} | ||||
| 	view_update_position(view, x, y); | ||||
| } | ||||
| 
 | ||||
| static void handle_new_popup(struct wl_listener *listener, void *data) { | ||||
| 	struct roots_wl_shell_surface *roots_surface = | ||||
| 		wl_container_of(listener, roots_surface, new_popup); | ||||
| 	struct wlr_wl_shell_surface *wlr_wl_shell_surface = data; | ||||
| 	popup_create(&roots_surface->view, wlr_wl_shell_surface); | ||||
| } | ||||
| 
 | ||||
| static void handle_destroy(struct wl_listener *listener, void *data) { | ||||
| 	struct roots_wl_shell_surface *roots_surface = | ||||
| 		wl_container_of(listener, roots_surface, destroy); | ||||
| 	view_destroy(&roots_surface->view); | ||||
| } | ||||
| 
 | ||||
| void handle_wl_shell_surface(struct wl_listener *listener, void *data) { | ||||
| 	struct roots_desktop *desktop = | ||||
| 		wl_container_of(listener, desktop, wl_shell_surface); | ||||
| 	struct wlr_wl_shell_surface *surface = data; | ||||
| 
 | ||||
| 	if (surface->state == WLR_WL_SHELL_SURFACE_STATE_POPUP) { | ||||
| 		wlr_log(WLR_DEBUG, "new wl shell popup"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	wlr_log(WLR_DEBUG, "new wl shell surface: title=%s, class=%s", | ||||
| 		surface->title, surface->class); | ||||
| 	wlr_wl_shell_surface_ping(surface); | ||||
| 
 | ||||
| 	struct roots_wl_shell_surface *roots_surface = | ||||
| 		calloc(1, sizeof(struct roots_wl_shell_surface)); | ||||
| 	if (!roots_surface) { | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	view_init(&roots_surface->view, &view_impl, ROOTS_WL_SHELL_VIEW, desktop); | ||||
| 	roots_surface->view.box.width = surface->surface->current.width; | ||||
| 	roots_surface->view.box.height = surface->surface->current.height; | ||||
| 	roots_surface->wl_shell_surface = surface; | ||||
| 
 | ||||
| 	roots_surface->destroy.notify = handle_destroy; | ||||
| 	wl_signal_add(&surface->events.destroy, &roots_surface->destroy); | ||||
| 	roots_surface->new_popup.notify = handle_new_popup; | ||||
| 	wl_signal_add(&surface->events.new_popup, &roots_surface->new_popup); | ||||
| 	roots_surface->request_move.notify = handle_request_move; | ||||
| 	wl_signal_add(&surface->events.request_move, &roots_surface->request_move); | ||||
| 	roots_surface->request_resize.notify = handle_request_resize; | ||||
| 	wl_signal_add(&surface->events.request_resize, | ||||
| 		&roots_surface->request_resize); | ||||
| 	roots_surface->request_maximize.notify = handle_request_maximize; | ||||
| 	wl_signal_add(&surface->events.request_maximize, | ||||
| 		&roots_surface->request_maximize); | ||||
| 	roots_surface->request_fullscreen.notify = | ||||
| 		handle_request_fullscreen; | ||||
| 	wl_signal_add(&surface->events.request_fullscreen, | ||||
| 		&roots_surface->request_fullscreen); | ||||
| 
 | ||||
| 	roots_surface->set_state.notify = handle_set_state; | ||||
| 	wl_signal_add(&surface->events.set_state, &roots_surface->set_state); | ||||
| 	roots_surface->set_title.notify = handle_set_title; | ||||
| 	wl_signal_add(&surface->events.set_title, &roots_surface->set_title); | ||||
| 	roots_surface->set_class.notify = handle_set_class; | ||||
| 	wl_signal_add(&surface->events.set_class, &roots_surface->set_class); | ||||
| 	roots_surface->surface_commit.notify = handle_surface_commit; | ||||
| 	wl_signal_add(&surface->surface->events.commit, &roots_surface->surface_commit); | ||||
| 
 | ||||
| 	view_map(&roots_surface->view, surface->surface); | ||||
| 	view_setup(&roots_surface->view); | ||||
| 
 | ||||
| 	wlr_foreign_toplevel_handle_v1_set_title(roots_surface->view.toplevel_handle, | ||||
| 		surface->title ?: "none"); | ||||
| 	wlr_foreign_toplevel_handle_v1_set_app_id(roots_surface->view.toplevel_handle, | ||||
| 		surface->class ?: "none"); | ||||
| 
 | ||||
| 	if (surface->state == WLR_WL_SHELL_SURFACE_STATE_TRANSIENT) { | ||||
| 		// We need to map it relative to the parent
 | ||||
| 		bool found = false; | ||||
| 		struct roots_view *parent; | ||||
| 		wl_list_for_each(parent, &desktop->views, link) { | ||||
| 			if (parent->type != ROOTS_WL_SHELL_VIEW) { | ||||
| 				continue; | ||||
| 			} | ||||
| 
 | ||||
| 			struct roots_wl_shell_surface *parent_surf = | ||||
| 				roots_wl_shell_surface_from_view(parent); | ||||
| 			if (parent_surf->wl_shell_surface == surface->parent) { | ||||
| 				found = true; | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 		if (found) { | ||||
| 			view_move(&roots_surface->view, | ||||
| 				parent->box.x + surface->transient_state->x, | ||||
| 				parent->box.y + surface->transient_state->y); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| struct roots_wl_shell_surface *roots_wl_shell_surface_from_view( | ||||
| 		struct roots_view *view) { | ||||
| 	assert(view->impl == &view_impl); | ||||
| 	return (struct roots_wl_shell_surface *)view; | ||||
| } | ||||
| @ -1,736 +0,0 @@ | ||||
| #ifndef _POSIX_C_SOURCE | ||||
| #define _POSIX_C_SOURCE 200809L | ||||
| #endif | ||||
| #include <assert.h> | ||||
| #include <stdlib.h> | ||||
| #include <wayland-server-protocol.h> | ||||
| #include <wayland-server.h> | ||||
| #include <wlr/types/wlr_surface.h> | ||||
| #include <wlr/types/wlr_wl_shell.h> | ||||
| #include <wlr/util/log.h> | ||||
| #include "util/signal.h" | ||||
| 
 | ||||
| static const struct wlr_surface_role shell_surface_role; | ||||
| 
 | ||||
| bool wlr_surface_is_wl_shell_surface(struct wlr_surface *surface) { | ||||
| 	return surface->role == &shell_surface_role; | ||||
| } | ||||
| 
 | ||||
| struct wlr_wl_shell_surface *wlr_wl_shell_surface_from_wlr_surface( | ||||
| 		struct wlr_surface *surface) { | ||||
| 	assert(wlr_surface_is_wl_shell_surface(surface)); | ||||
| 	return (struct wlr_wl_shell_surface *)surface->role_data; | ||||
| } | ||||
| 
 | ||||
| static void shell_pointer_grab_end(struct wlr_seat_pointer_grab *grab) { | ||||
| 	struct wlr_wl_shell_popup_grab *popup_grab = grab->data; | ||||
| 
 | ||||
| 	struct wlr_wl_shell_surface *popup, *tmp = NULL; | ||||
| 	wl_list_for_each_safe(popup, tmp, &popup_grab->popups, grab_link) { | ||||
| 		if (popup->popup_mapped) { | ||||
| 			wl_shell_surface_send_popup_done(popup->resource); | ||||
| 			popup->popup_mapped = false; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (grab->seat->pointer_state.grab == grab) { | ||||
| 		wlr_seat_pointer_end_grab(grab->seat); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void shell_pointer_grab_maybe_end(struct wlr_seat_pointer_grab *grab) { | ||||
| 	struct wlr_wl_shell_popup_grab *popup_grab = grab->data; | ||||
| 
 | ||||
| 	if (grab->seat->pointer_state.grab != grab) { | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	bool has_mapped = false; | ||||
| 
 | ||||
| 	struct wlr_wl_shell_surface *popup, *tmp = NULL; | ||||
| 	wl_list_for_each_safe(popup, tmp, &popup_grab->popups, grab_link) { | ||||
| 		if (popup->popup_mapped) { | ||||
| 			has_mapped = true; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (!has_mapped) { | ||||
| 		shell_pointer_grab_end(grab); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void shell_pointer_grab_enter(struct wlr_seat_pointer_grab *grab, | ||||
| 		struct wlr_surface *surface, double sx, double sy) { | ||||
| 	struct wlr_wl_shell_popup_grab *popup_grab = grab->data; | ||||
| 	if (wl_resource_get_client(surface->resource) == popup_grab->client) { | ||||
| 		wlr_seat_pointer_enter(grab->seat, surface, sx, sy); | ||||
| 	} else { | ||||
| 		wlr_seat_pointer_clear_focus(grab->seat); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void shell_pointer_grab_motion(struct wlr_seat_pointer_grab *grab, | ||||
| 		uint32_t time, double sx, double sy) { | ||||
| 	wlr_seat_pointer_send_motion(grab->seat, time, sx, sy); | ||||
| } | ||||
| 
 | ||||
| static uint32_t shell_pointer_grab_button(struct wlr_seat_pointer_grab *grab, | ||||
| 		uint32_t time, uint32_t button, uint32_t state) { | ||||
| 	uint32_t serial = | ||||
| 		wlr_seat_pointer_send_button(grab->seat, time, button, state); | ||||
| 	if (serial) { | ||||
| 		return serial; | ||||
| 	} else { | ||||
| 		shell_pointer_grab_end(grab); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void shell_pointer_grab_cancel(struct wlr_seat_pointer_grab *grab) { | ||||
| 	shell_pointer_grab_end(grab); | ||||
| } | ||||
| 
 | ||||
| static void shell_pointer_grab_axis(struct wlr_seat_pointer_grab *grab, | ||||
| 		uint32_t time, enum wlr_axis_orientation orientation, double value, | ||||
| 		int32_t value_discrete, enum wlr_axis_source source) { | ||||
| 	wlr_seat_pointer_send_axis(grab->seat, time, orientation, value, | ||||
| 		value_discrete, source); | ||||
| } | ||||
| 
 | ||||
| static void shell_pointer_grab_frame(struct wlr_seat_pointer_grab *grab) { | ||||
| 	wlr_seat_pointer_send_frame(grab->seat); | ||||
| } | ||||
| 
 | ||||
| static const struct wlr_pointer_grab_interface shell_pointer_grab_impl = { | ||||
| 	.enter = shell_pointer_grab_enter, | ||||
| 	.motion = shell_pointer_grab_motion, | ||||
| 	.button = shell_pointer_grab_button, | ||||
| 	.cancel = shell_pointer_grab_cancel, | ||||
| 	.axis = shell_pointer_grab_axis, | ||||
| 	.frame = shell_pointer_grab_frame, | ||||
| }; | ||||
| 
 | ||||
| static const struct wl_shell_surface_interface shell_surface_impl; | ||||
| 
 | ||||
| static struct wlr_wl_shell_surface *shell_surface_from_resource( | ||||
| 		struct wl_resource *resource) { | ||||
| 	assert(wl_resource_instance_of(resource, &wl_shell_surface_interface, | ||||
| 		&shell_surface_impl)); | ||||
| 	return wl_resource_get_user_data(resource); | ||||
| } | ||||
| 
 | ||||
| static void shell_surface_protocol_pong(struct wl_client *client, | ||||
| 		struct wl_resource *resource, uint32_t serial) { | ||||
| 	wlr_log(WLR_DEBUG, "got shell surface pong"); | ||||
| 	struct wlr_wl_shell_surface *surface = shell_surface_from_resource(resource); | ||||
| 	if (surface->ping_serial != serial) { | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	wl_event_source_timer_update(surface->ping_timer, 0); | ||||
| 	surface->ping_serial = 0; | ||||
| } | ||||
| 
 | ||||
| static void shell_surface_protocol_move(struct wl_client *client, | ||||
| 		struct wl_resource *resource, struct wl_resource *seat_resource, | ||||
| 		uint32_t serial) { | ||||
| 	struct wlr_wl_shell_surface *surface = shell_surface_from_resource(resource); | ||||
| 	struct wlr_seat_client *seat = wlr_seat_client_from_resource(seat_resource); | ||||
| 
 | ||||
| 	if (!wlr_seat_validate_grab_serial(seat->seat, serial)) { | ||||
| 		wlr_log(WLR_DEBUG, "invalid serial for grab"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	struct wlr_wl_shell_surface_move_event event = { | ||||
| 		.surface = surface, | ||||
| 		.seat = seat, | ||||
| 		.serial = serial, | ||||
| 	}; | ||||
| 
 | ||||
| 	wlr_signal_emit_safe(&surface->events.request_move, &event); | ||||
| } | ||||
| 
 | ||||
| static struct wlr_wl_shell_popup_grab *shell_popup_grab_from_seat( | ||||
| 		struct wlr_wl_shell *shell, struct wlr_seat *seat) { | ||||
| 	struct wlr_wl_shell_popup_grab *shell_grab; | ||||
| 	wl_list_for_each(shell_grab, &shell->popup_grabs, link) { | ||||
| 		if (shell_grab->seat == seat) { | ||||
| 			return shell_grab; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	shell_grab = calloc(1, sizeof(struct wlr_wl_shell_popup_grab)); | ||||
| 	if (!shell_grab) { | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	shell_grab->pointer_grab.data = shell_grab; | ||||
| 	shell_grab->pointer_grab.interface = &shell_pointer_grab_impl; | ||||
| 
 | ||||
| 	wl_list_init(&shell_grab->popups); | ||||
| 
 | ||||
| 	wl_list_insert(&shell->popup_grabs, &shell_grab->link); | ||||
| 	shell_grab->seat = seat; | ||||
| 
 | ||||
| 	return shell_grab; | ||||
| } | ||||
| 
 | ||||
| static void shell_surface_destroy_popup_state( | ||||
| 		struct wlr_wl_shell_surface *surface) { | ||||
| 	if (surface->popup_state) { | ||||
| 		wl_list_remove(&surface->grab_link); | ||||
| 		struct wlr_wl_shell_popup_grab *grab = | ||||
| 			shell_popup_grab_from_seat(surface->shell, | ||||
| 					surface->popup_state->seat); | ||||
| 		if (wl_list_empty(&grab->popups)) { | ||||
| 			if (grab->seat->pointer_state.grab == &grab->pointer_grab) { | ||||
| 				wlr_seat_pointer_end_grab(grab->seat); | ||||
| 			} | ||||
| 		} | ||||
| 		free(surface->popup_state); | ||||
| 		surface->popup_state = NULL; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void shell_surface_protocol_resize(struct wl_client *client, | ||||
| 		struct wl_resource *resource, struct wl_resource *seat_resource, | ||||
| 		uint32_t serial, enum wl_shell_surface_resize edges) { | ||||
| 	struct wlr_wl_shell_surface *surface = shell_surface_from_resource(resource); | ||||
| 	struct wlr_seat_client *seat = wlr_seat_client_from_resource(seat_resource); | ||||
| 
 | ||||
| 	if (!wlr_seat_validate_grab_serial(seat->seat, serial)) { | ||||
| 		wlr_log(WLR_DEBUG, "invalid serial for grab"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	struct wlr_wl_shell_surface_resize_event event = { | ||||
| 		.surface = surface, | ||||
| 		.seat = seat, | ||||
| 		.serial = serial, | ||||
| 		.edges = edges, | ||||
| 	}; | ||||
| 
 | ||||
| 	wlr_signal_emit_safe(&surface->events.request_resize, &event); | ||||
| } | ||||
| 
 | ||||
| static void shell_surface_set_state(struct wlr_wl_shell_surface *surface, | ||||
| 		enum wlr_wl_shell_surface_state state, | ||||
| 		struct wlr_wl_shell_surface_transient_state *transient_state, | ||||
| 		struct wlr_wl_shell_surface_popup_state *popup_state) { | ||||
| 	surface->state = state; | ||||
| 	free(surface->transient_state); | ||||
| 	surface->transient_state = transient_state; | ||||
| 	shell_surface_destroy_popup_state(surface); | ||||
| 	surface->popup_state = popup_state; | ||||
| 
 | ||||
| 	wlr_signal_emit_safe(&surface->events.set_state, surface); | ||||
| } | ||||
| 
 | ||||
| static void shell_surface_protocol_set_toplevel(struct wl_client *client, | ||||
| 		struct wl_resource *resource) { | ||||
| 	wlr_log(WLR_DEBUG, "got shell surface toplevel"); | ||||
| 	struct wlr_wl_shell_surface *surface = shell_surface_from_resource(resource); | ||||
| 	shell_surface_set_state(surface, WLR_WL_SHELL_SURFACE_STATE_TOPLEVEL, NULL, | ||||
| 		NULL); | ||||
| } | ||||
| 
 | ||||
| static void shell_surface_popup_set_parent(struct wlr_wl_shell_surface *surface, | ||||
| 		struct wlr_wl_shell_surface *parent) { | ||||
| 	assert(surface && surface->state == WLR_WL_SHELL_SURFACE_STATE_POPUP); | ||||
| 	if (surface->parent == parent) { | ||||
| 		return; | ||||
| 	} | ||||
| 	surface->parent = parent; | ||||
| 	if (parent) { | ||||
| 		wl_list_remove(&surface->popup_link); | ||||
| 		wl_list_insert(&parent->popups, &surface->popup_link); | ||||
| 		wlr_signal_emit_safe(&parent->events.new_popup, surface); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static struct wlr_wl_shell_surface *shell_find_shell_surface( | ||||
| 		struct wlr_wl_shell *shell, struct wlr_surface *surface) { | ||||
| 	if (surface) { | ||||
| 		struct wlr_wl_shell_surface *wl_surface; | ||||
| 		wl_list_for_each(wl_surface, &shell->surfaces, link) { | ||||
| 			if (wl_surface->surface == surface) { | ||||
| 				return wl_surface; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| static void shell_surface_protocol_set_transient(struct wl_client *client, | ||||
| 		struct wl_resource *resource, struct wl_resource *parent_resource, | ||||
| 		int32_t x, int32_t y, enum wl_shell_surface_transient flags) { | ||||
| 	wlr_log(WLR_DEBUG, "got shell surface transient"); | ||||
| 	struct wlr_wl_shell_surface *surface = shell_surface_from_resource(resource); | ||||
| 	struct wlr_surface *parent = wlr_surface_from_resource(parent_resource); | ||||
| 	// TODO: check if parent_resource == NULL?
 | ||||
| 
 | ||||
| 	struct wlr_wl_shell_surface *wl_parent = | ||||
| 		shell_find_shell_surface(surface->shell, parent); | ||||
| 
 | ||||
| 	if (!wl_parent) { | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	struct wlr_wl_shell_surface_transient_state *transient_state = | ||||
| 		calloc(1, sizeof(struct wlr_wl_shell_surface_transient_state)); | ||||
| 	if (transient_state == NULL) { | ||||
| 		wl_client_post_no_memory(client); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	surface->parent = wl_parent; | ||||
| 	transient_state->x = x; | ||||
| 	transient_state->y = y; | ||||
| 	transient_state->flags = flags; | ||||
| 
 | ||||
| 	shell_surface_set_state(surface, WLR_WL_SHELL_SURFACE_STATE_TRANSIENT, | ||||
| 		transient_state, NULL); | ||||
| } | ||||
| 
 | ||||
| static void shell_surface_protocol_set_fullscreen(struct wl_client *client, | ||||
| 		struct wl_resource *resource, | ||||
| 		enum wl_shell_surface_fullscreen_method method, uint32_t framerate, | ||||
| 		struct wl_resource *output_resource) { | ||||
| 	struct wlr_wl_shell_surface *surface = shell_surface_from_resource(resource); | ||||
| 	struct wlr_output *output = NULL; | ||||
| 	if (output_resource != NULL) { | ||||
| 		output = wlr_output_from_resource(output_resource); | ||||
| 	} | ||||
| 
 | ||||
| 	shell_surface_set_state(surface, WLR_WL_SHELL_SURFACE_STATE_FULLSCREEN, | ||||
| 		NULL, NULL); | ||||
| 
 | ||||
| 	struct wlr_wl_shell_surface_set_fullscreen_event event = { | ||||
| 		.surface = surface, | ||||
| 		.method = method, | ||||
| 		.framerate = framerate, | ||||
| 		.output = output, | ||||
| 	}; | ||||
| 
 | ||||
| 	wlr_signal_emit_safe(&surface->events.request_fullscreen, &event); | ||||
| } | ||||
| 
 | ||||
| static void shell_surface_protocol_set_popup(struct wl_client *client, | ||||
| 		struct wl_resource *resource, struct wl_resource *seat_resource, | ||||
| 		uint32_t serial, struct wl_resource *parent_resource, int32_t x, | ||||
| 		int32_t y, enum wl_shell_surface_transient flags) { | ||||
| 	struct wlr_wl_shell_surface *surface = shell_surface_from_resource(resource); | ||||
| 	struct wlr_seat_client *seat_client = | ||||
| 		wlr_seat_client_from_resource(seat_resource); | ||||
| 	struct wlr_surface *parent = wlr_surface_from_resource(parent_resource); | ||||
| 	struct wlr_wl_shell_popup_grab *grab = | ||||
| 		shell_popup_grab_from_seat(surface->shell, seat_client->seat); | ||||
| 	if (!grab) { | ||||
| 		wl_client_post_no_memory(client); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	struct wlr_wl_shell_surface *wl_parent = | ||||
| 		shell_find_shell_surface(surface->shell, parent); | ||||
| 
 | ||||
| 	if (surface->state == WLR_WL_SHELL_SURFACE_STATE_POPUP) { | ||||
| 		surface->transient_state->x = x; | ||||
| 		surface->transient_state->y = y; | ||||
| 		shell_surface_popup_set_parent(surface, wl_parent); | ||||
| 		grab->client = surface->client; | ||||
| 		surface->popup_mapped = true; | ||||
| 		wlr_seat_pointer_start_grab(seat_client->seat, &grab->pointer_grab); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	struct wlr_wl_shell_surface_transient_state *transient_state = | ||||
| 		calloc(1, sizeof(struct wlr_wl_shell_surface_transient_state)); | ||||
| 	if (transient_state == NULL) { | ||||
| 		wl_client_post_no_memory(client); | ||||
| 		return; | ||||
| 	} | ||||
| 	transient_state->x = x; | ||||
| 	transient_state->y = y; | ||||
| 	transient_state->flags = flags; | ||||
| 
 | ||||
| 	struct wlr_wl_shell_surface_popup_state *popup_state = | ||||
| 		calloc(1, sizeof(struct wlr_wl_shell_surface_popup_state)); | ||||
| 	if (popup_state == NULL) { | ||||
| 		free(transient_state); | ||||
| 		wl_client_post_no_memory(client); | ||||
| 		return; | ||||
| 	} | ||||
| 	popup_state->seat = seat_client->seat; | ||||
| 	popup_state->serial = serial; | ||||
| 
 | ||||
| 	shell_surface_set_state(surface, WLR_WL_SHELL_SURFACE_STATE_POPUP, | ||||
| 		transient_state, popup_state); | ||||
| 
 | ||||
| 	shell_surface_popup_set_parent(surface, wl_parent); | ||||
| 	grab->client = surface->client; | ||||
| 	wl_list_insert(&grab->popups, &surface->grab_link); | ||||
| 	surface->popup_mapped = true; | ||||
| 	wlr_seat_pointer_start_grab(seat_client->seat, &grab->pointer_grab); | ||||
| } | ||||
| 
 | ||||
| static void shell_surface_protocol_set_maximized(struct wl_client *client, | ||||
| 		struct wl_resource *resource, struct wl_resource *output_resource) { | ||||
| 	struct wlr_wl_shell_surface *surface = shell_surface_from_resource(resource); | ||||
| 	struct wlr_output *output = NULL; | ||||
| 	if (output_resource != NULL) { | ||||
| 		output = wlr_output_from_resource(output_resource); | ||||
| 	} | ||||
| 
 | ||||
| 	shell_surface_set_state(surface, WLR_WL_SHELL_SURFACE_STATE_MAXIMIZED, | ||||
| 		NULL, NULL); | ||||
| 
 | ||||
| 	struct wlr_wl_shell_surface_maximize_event event = { | ||||
| 		.surface = surface, | ||||
| 		.output = output, | ||||
| 	}; | ||||
| 
 | ||||
| 	wlr_signal_emit_safe(&surface->events.request_maximize, &event); | ||||
| } | ||||
| 
 | ||||
| static void shell_surface_protocol_set_title(struct wl_client *client, | ||||
| 		struct wl_resource *resource, const char *title) { | ||||
| 	wlr_log(WLR_DEBUG, "new shell surface title: %s", title); | ||||
| 	struct wlr_wl_shell_surface *surface = shell_surface_from_resource(resource); | ||||
| 
 | ||||
| 	char *tmp = strdup(title); | ||||
| 	if (tmp == NULL) { | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	free(surface->title); | ||||
| 	surface->title = tmp; | ||||
| 
 | ||||
| 	wlr_signal_emit_safe(&surface->events.set_title, surface); | ||||
| } | ||||
| 
 | ||||
| static void shell_surface_protocol_set_class(struct wl_client *client, | ||||
| 		struct wl_resource *resource, const char *class) { | ||||
| 	wlr_log(WLR_DEBUG, "new shell surface class: %s", class); | ||||
| 	struct wlr_wl_shell_surface *surface = shell_surface_from_resource(resource); | ||||
| 
 | ||||
| 	char *tmp = strdup(class); | ||||
| 	if (tmp == NULL) { | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	free(surface->class); | ||||
| 	surface->class = tmp; | ||||
| 
 | ||||
| 	wlr_signal_emit_safe(&surface->events.set_class, surface); | ||||
| } | ||||
| 
 | ||||
| static const struct wl_shell_surface_interface shell_surface_impl = { | ||||
| 	.pong = shell_surface_protocol_pong, | ||||
| 	.move = shell_surface_protocol_move, | ||||
| 	.resize = shell_surface_protocol_resize, | ||||
| 	.set_toplevel = shell_surface_protocol_set_toplevel, | ||||
| 	.set_transient = shell_surface_protocol_set_transient, | ||||
| 	.set_fullscreen = shell_surface_protocol_set_fullscreen, | ||||
| 	.set_popup = shell_surface_protocol_set_popup, | ||||
| 	.set_maximized = shell_surface_protocol_set_maximized, | ||||
| 	.set_title = shell_surface_protocol_set_title, | ||||
| 	.set_class = shell_surface_protocol_set_class, | ||||
| }; | ||||
| 
 | ||||
| static void shell_surface_destroy(struct wlr_wl_shell_surface *surface) { | ||||
| 	wlr_signal_emit_safe(&surface->events.destroy, surface); | ||||
| 	shell_surface_destroy_popup_state(surface); | ||||
| 	wl_resource_set_user_data(surface->resource, NULL); | ||||
| 
 | ||||
| 	struct wlr_wl_shell_surface *child; | ||||
| 	wl_list_for_each(child, &surface->popups, popup_link) { | ||||
| 		shell_surface_popup_set_parent(child, NULL); | ||||
| 	} | ||||
| 	wl_list_remove(&surface->popup_link); | ||||
| 
 | ||||
| 	surface->surface->role_data = NULL; | ||||
| 	wl_list_remove(&surface->link); | ||||
| 	wl_list_remove(&surface->surface_destroy.link); | ||||
| 	wl_event_source_remove(surface->ping_timer); | ||||
| 	free(surface->transient_state); | ||||
| 	free(surface->title); | ||||
| 	free(surface->class); | ||||
| 	free(surface); | ||||
| } | ||||
| 
 | ||||
| static void shell_surface_resource_destroy(struct wl_resource *resource) { | ||||
| 	struct wlr_wl_shell_surface *surface = shell_surface_from_resource(resource); | ||||
| 	if (surface != NULL) { | ||||
| 		shell_surface_destroy(surface); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void shell_surface_handle_surface_destroy(struct wl_listener *listener, | ||||
| 		void *data) { | ||||
| 	struct wlr_wl_shell_surface *surface = | ||||
| 		wl_container_of(listener, surface, surface_destroy); | ||||
| 	shell_surface_destroy(surface); | ||||
| } | ||||
| 
 | ||||
| static void shell_surface_role_commit(struct wlr_surface *wlr_surface) { | ||||
| 	struct wlr_wl_shell_surface *surface = | ||||
| 		wlr_wl_shell_surface_from_wlr_surface(wlr_surface); | ||||
| 	if (surface == NULL) { | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!surface->configured && | ||||
| 			wlr_surface_has_buffer(surface->surface) && | ||||
| 			surface->state != WLR_WL_SHELL_SURFACE_STATE_NONE) { | ||||
| 		surface->configured = true; | ||||
| 		wlr_signal_emit_safe(&surface->shell->events.new_surface, surface); | ||||
| 	} | ||||
| 
 | ||||
| 	if (surface->popup_mapped && | ||||
| 			surface->state == WLR_WL_SHELL_SURFACE_STATE_POPUP && | ||||
| 			!wlr_surface_has_buffer(surface->surface)) { | ||||
| 		surface->popup_mapped = false; | ||||
| 		struct wlr_wl_shell_popup_grab *grab = | ||||
| 			shell_popup_grab_from_seat(surface->shell, | ||||
| 				surface->popup_state->seat); | ||||
| 		shell_pointer_grab_maybe_end(&grab->pointer_grab); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static const struct wlr_surface_role shell_surface_role = { | ||||
| 	.name = "wl_shell_surface", | ||||
| 	.commit = shell_surface_role_commit, | ||||
| }; | ||||
| 
 | ||||
| static int shell_surface_ping_timeout(void *user_data) { | ||||
| 	struct wlr_wl_shell_surface *surface = user_data; | ||||
| 	wlr_signal_emit_safe(&surface->events.ping_timeout, surface); | ||||
| 
 | ||||
| 	surface->ping_serial = 0; | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| static const struct wl_shell_interface shell_impl; | ||||
| 
 | ||||
| static struct wlr_wl_shell *shell_from_resource( | ||||
| 		struct wl_resource *resource) { | ||||
| 	assert(wl_resource_instance_of(resource, &wl_shell_interface, &shell_impl)); | ||||
| 	return wl_resource_get_user_data(resource); | ||||
| } | ||||
| 
 | ||||
| static void shell_protocol_get_shell_surface(struct wl_client *client, | ||||
| 		struct wl_resource *shell_resource, uint32_t id, | ||||
| 		struct wl_resource *surface_resource) { | ||||
| 	struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); | ||||
| 
 | ||||
| 	struct wlr_wl_shell *wl_shell = shell_from_resource(shell_resource); | ||||
| 	struct wlr_wl_shell_surface *wl_surface = | ||||
| 		calloc(1, sizeof(struct wlr_wl_shell_surface)); | ||||
| 	if (wl_surface == NULL) { | ||||
| 		wl_resource_post_no_memory(shell_resource); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!wlr_surface_set_role(surface, &shell_surface_role, wl_surface, | ||||
| 			shell_resource, WL_SHELL_ERROR_ROLE)) { | ||||
| 		free(wl_surface); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	wl_list_init(&wl_surface->grab_link); | ||||
| 	wl_list_init(&wl_surface->popup_link); | ||||
| 	wl_list_init(&wl_surface->popups); | ||||
| 
 | ||||
| 	wl_surface->shell = wl_shell; | ||||
| 	wl_surface->client = client; | ||||
| 	wl_surface->surface = surface; | ||||
| 
 | ||||
| 	wl_surface->resource = wl_resource_create(client, | ||||
| 		&wl_shell_surface_interface, wl_resource_get_version(shell_resource), | ||||
| 		id); | ||||
| 	if (wl_surface->resource == NULL) { | ||||
| 		free(wl_surface); | ||||
| 		wl_resource_post_no_memory(shell_resource); | ||||
| 		return; | ||||
| 	} | ||||
| 	wl_resource_set_implementation(wl_surface->resource, | ||||
| 		&shell_surface_impl, wl_surface, | ||||
| 		shell_surface_resource_destroy); | ||||
| 
 | ||||
| 	wlr_log(WLR_DEBUG, "new wl_shell %p (res %p)", wl_surface, | ||||
| 		wl_surface->resource); | ||||
| 
 | ||||
| 	wl_signal_init(&wl_surface->events.destroy); | ||||
| 	wl_signal_init(&wl_surface->events.ping_timeout); | ||||
| 	wl_signal_init(&wl_surface->events.new_popup); | ||||
| 	wl_signal_init(&wl_surface->events.request_move); | ||||
| 	wl_signal_init(&wl_surface->events.request_resize); | ||||
| 	wl_signal_init(&wl_surface->events.request_fullscreen); | ||||
| 	wl_signal_init(&wl_surface->events.request_maximize); | ||||
| 	wl_signal_init(&wl_surface->events.set_state); | ||||
| 	wl_signal_init(&wl_surface->events.set_title); | ||||
| 	wl_signal_init(&wl_surface->events.set_class); | ||||
| 
 | ||||
| 	wl_signal_add(&wl_surface->surface->events.destroy, | ||||
| 		&wl_surface->surface_destroy); | ||||
| 	wl_surface->surface_destroy.notify = shell_surface_handle_surface_destroy; | ||||
| 
 | ||||
| 	struct wl_display *display = wl_client_get_display(client); | ||||
| 	struct wl_event_loop *loop = wl_display_get_event_loop(display); | ||||
| 	wl_surface->ping_timer = wl_event_loop_add_timer(loop, | ||||
| 		shell_surface_ping_timeout, wl_surface); | ||||
| 	if (wl_surface->ping_timer == NULL) { | ||||
| 		wl_client_post_no_memory(client); | ||||
| 	} | ||||
| 
 | ||||
| 	wl_list_insert(&wl_shell->surfaces, &wl_surface->link); | ||||
| } | ||||
| 
 | ||||
| static const struct wl_shell_interface shell_impl = { | ||||
| 	.get_shell_surface = shell_protocol_get_shell_surface | ||||
| }; | ||||
| 
 | ||||
| static void shell_destroy(struct wl_resource *resource) { | ||||
| 	wl_list_remove(wl_resource_get_link(resource)); | ||||
| } | ||||
| 
 | ||||
| static void shell_bind(struct wl_client *wl_client, void *data, | ||||
| 		uint32_t version, uint32_t id) { | ||||
| 	struct wlr_wl_shell *wl_shell = data; | ||||
| 	assert(wl_client && wl_shell); | ||||
| 
 | ||||
| 	struct wl_resource *wl_resource = wl_resource_create(wl_client, | ||||
| 		&wl_shell_interface, version, id); | ||||
| 	if (wl_resource == NULL) { | ||||
| 		wl_client_post_no_memory(wl_client); | ||||
| 		return; | ||||
| 	} | ||||
| 	wl_resource_set_implementation(wl_resource, &shell_impl, wl_shell, | ||||
| 		shell_destroy); | ||||
| 	wl_list_insert(&wl_shell->resources, wl_resource_get_link(wl_resource)); | ||||
| } | ||||
| 
 | ||||
| static void handle_display_destroy(struct wl_listener *listener, void *data) { | ||||
| 	struct wlr_wl_shell *wl_shell = | ||||
| 		wl_container_of(listener, wl_shell, display_destroy); | ||||
| 	wlr_wl_shell_destroy(wl_shell); | ||||
| } | ||||
| 
 | ||||
| struct wlr_wl_shell *wlr_wl_shell_create(struct wl_display *display) { | ||||
| 	struct wlr_wl_shell *wl_shell = calloc(1, sizeof(struct wlr_wl_shell)); | ||||
| 	if (!wl_shell) { | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	wl_shell->ping_timeout = 10000; | ||||
| 	struct wl_global *global = wl_global_create(display, &wl_shell_interface, | ||||
| 		1, wl_shell, shell_bind); | ||||
| 	if (!global) { | ||||
| 		free(wl_shell); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	wl_shell->global = global; | ||||
| 	wl_list_init(&wl_shell->resources); | ||||
| 	wl_list_init(&wl_shell->surfaces); | ||||
| 	wl_list_init(&wl_shell->popup_grabs); | ||||
| 	wl_signal_init(&wl_shell->events.new_surface); | ||||
| 
 | ||||
| 	wl_shell->display_destroy.notify = handle_display_destroy; | ||||
| 	wl_display_add_destroy_listener(display, &wl_shell->display_destroy); | ||||
| 
 | ||||
| 	return wl_shell; | ||||
| } | ||||
| 
 | ||||
| void wlr_wl_shell_destroy(struct wlr_wl_shell *wlr_wl_shell) { | ||||
| 	if (!wlr_wl_shell) { | ||||
| 		return; | ||||
| 	} | ||||
| 	wl_list_remove(&wlr_wl_shell->display_destroy.link); | ||||
| 	struct wl_resource *resource = NULL, *temp = NULL; | ||||
| 	wl_resource_for_each_safe(resource, temp, &wlr_wl_shell->resources) { | ||||
| 		// shell_destroy will remove the resource from the list
 | ||||
| 		wl_resource_destroy(resource); | ||||
| 	} | ||||
| 	// TODO: destroy surfaces
 | ||||
| 	wl_global_destroy(wlr_wl_shell->global); | ||||
| 	free(wlr_wl_shell); | ||||
| } | ||||
| 
 | ||||
| void wlr_wl_shell_surface_ping(struct wlr_wl_shell_surface *surface) { | ||||
| 	if (surface->ping_serial != 0) { | ||||
| 		// already pinged
 | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	surface->ping_serial = | ||||
| 		wl_display_next_serial(wl_client_get_display(surface->client)); | ||||
| 	wl_event_source_timer_update(surface->ping_timer, | ||||
| 		surface->shell->ping_timeout); | ||||
| 	wl_shell_surface_send_ping(surface->resource, surface->ping_serial); | ||||
| } | ||||
| 
 | ||||
| void wlr_wl_shell_surface_configure(struct wlr_wl_shell_surface *surface, | ||||
| 		uint32_t edges, int32_t width, int32_t height) { | ||||
| 	wl_shell_surface_send_configure(surface->resource, edges, width, height); | ||||
| } | ||||
| 
 | ||||
| struct wlr_surface *wlr_wl_shell_surface_surface_at( | ||||
| 		struct wlr_wl_shell_surface *surface, double sx, double sy, | ||||
| 		double *sub_sx, double *sub_sy) { | ||||
| 	struct wlr_wl_shell_surface *popup; | ||||
| 	wl_list_for_each(popup, &surface->popups, popup_link) { | ||||
| 		if (!popup->popup_mapped) { | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		double popup_sx = popup->transient_state->x; | ||||
| 		double popup_sy = popup->transient_state->y; | ||||
| 		struct wlr_surface *sub = wlr_wl_shell_surface_surface_at(popup, | ||||
| 			sx - popup_sx, sy - popup_sy, sub_sx, sub_sy); | ||||
| 		if (sub != NULL) { | ||||
| 			return sub; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return wlr_surface_surface_at(surface->surface, sx, sy, sub_sx, sub_sy); | ||||
| } | ||||
| 
 | ||||
| struct wl_shell_surface_iterator_data { | ||||
| 	wlr_surface_iterator_func_t user_iterator; | ||||
| 	void *user_data; | ||||
| 	int x, y; | ||||
| }; | ||||
| 
 | ||||
| static void wl_shell_surface_iterator(struct wlr_surface *surface, | ||||
| 		int sx, int sy, void *data) { | ||||
| 	struct wl_shell_surface_iterator_data *iter_data = data; | ||||
| 	iter_data->user_iterator(surface, iter_data->x + sx, iter_data->y + sy, | ||||
| 		iter_data->user_data); | ||||
| } | ||||
| 
 | ||||
| static void wl_shell_surface_for_each_surface( | ||||
| 		struct wlr_wl_shell_surface *surface, int x, int y, | ||||
| 		wlr_surface_iterator_func_t iterator, void *user_data) { | ||||
| 	struct wl_shell_surface_iterator_data data = { | ||||
| 		.user_iterator = iterator, | ||||
| 		.user_data = user_data, | ||||
| 		.x = x, .y = y, | ||||
| 	}; | ||||
| 	wlr_surface_for_each_surface(surface->surface, wl_shell_surface_iterator, | ||||
| 		&data); | ||||
| 
 | ||||
| 	struct wlr_wl_shell_surface *popup; | ||||
| 	wl_list_for_each(popup, &surface->popups, popup_link) { | ||||
| 		double popup_x = popup->transient_state->x; | ||||
| 		double popup_y = popup->transient_state->y; | ||||
| 
 | ||||
| 		wl_shell_surface_for_each_surface(popup, x + popup_x, y + popup_y, | ||||
| 			iterator, user_data); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void wlr_wl_shell_surface_for_each_surface(struct wlr_wl_shell_surface *surface, | ||||
| 		wlr_surface_iterator_func_t iterator, void *user_data) { | ||||
| 	wl_shell_surface_for_each_surface(surface, 0, 0, iterator, user_data); | ||||
| } | ||||
					Loading…
					
					
				
		Reference in new issue