[PATCH weston] shell: handle multiple popups

Giulio Camuffo giuliocamuffo at gmail.com
Fri Mar 22 07:51:55 PDT 2013


This patch implements a popup stack. When the first popup is opened
the grab is started, and it is added to a list. Further popups will
be added to this list but the grab won't change. When a popup is
closed it is removed from the list and, if it is now empty, the grab
is ended.
A click outside the client area will send the popup_done event to
all the popups in the list, and the grab will end.
---
 src/compositor.c |   5 ++
 src/compositor.h |   3 ++
 src/shell.c      | 161 ++++++++++++++++++++++++++++++++++++++++++++++---------
 3 files changed, 144 insertions(+), 25 deletions(-)

diff --git a/src/compositor.c b/src/compositor.c
index b734f67..3fc3ccb 100644
--- a/src/compositor.c
+++ b/src/compositor.c
@@ -2705,7 +2705,11 @@ weston_seat_init(struct weston_seat *seat, struct weston_compositor *ec)
 		      &seat->new_drag_icon_listener);
 
 	clipboard_create(seat);
+
 	wl_signal_emit(&ec->seat_created_signal, seat);
+	wl_signal_init(&seat->destroy_signal);
+
+	seat->private = NULL;
 }
 
 WL_EXPORT void
@@ -2722,6 +2726,7 @@ weston_seat_release(struct weston_seat *seat)
 	xkb_info_destroy(&seat->xkb_info);
 
 	wl_seat_release(&seat->seat);
+	wl_signal_emit(&seat->destroy_signal, seat);
 }
 
 static void
diff --git a/src/compositor.h b/src/compositor.h
index 58ab58d..80e0398 100644
--- a/src/compositor.h
+++ b/src/compositor.h
@@ -227,6 +227,7 @@ struct weston_seat {
 	int has_keyboard;
 	struct wl_touch touch;
 	int has_touch;
+	struct wl_signal destroy_signal;
 
 	struct weston_compositor *compositor;
 	struct weston_surface *sprite;
@@ -252,6 +253,8 @@ struct weston_seat {
 	} xkb_state;
 
 	struct input_method *input_method;
+
+	void *private;
 };
 
 enum {
diff --git a/src/shell.c b/src/shell.c
index 5ff25e6..1e3ee93 100644
--- a/src/shell.c
+++ b/src/shell.c
@@ -188,10 +188,9 @@ struct shell_surface {
 	} rotation;
 
 	struct {
-		struct wl_pointer_grab grab;
+		struct wl_list grab_link;
 		int32_t x, y;
-		int32_t initial_up;
-		struct wl_seat *seat;
+		struct shell_seat *shseat;
 		uint32_t serial;
 	} popup;
 
@@ -239,6 +238,18 @@ struct rotate_grab {
 	} center;
 };
 
+struct shell_seat {
+	struct weston_seat *seat;
+	struct wl_listener seat_destroy_listener;
+
+	struct {
+		struct wl_pointer_grab grab;
+		struct wl_list surfaces_list;
+		struct wl_client *client;
+		int32_t initial_up;
+	} popup_grab;
+};
+
 static void
 activate(struct desktop_shell *shell, struct weston_surface *es,
 	 struct weston_seat *seat);
@@ -1836,6 +1847,64 @@ shell_surface_set_fullscreen(struct wl_client *client,
 	set_fullscreen(shsurf, method, framerate, output);
 }
 
+static const struct wl_pointer_grab_interface popup_grab_interface;
+
+static void
+destroy_shell_seat(struct wl_listener *listener, void *data)
+{
+	struct weston_seat *seat = data;
+	struct shell_seat *shseat = seat->private;
+	struct shell_surface *shsurf, *prev = NULL;
+
+	if (shseat->popup_grab.grab.interface == &popup_grab_interface) {
+		wl_pointer_end_grab(shseat->popup_grab.grab.pointer);
+		shseat->popup_grab.client = NULL;
+
+		wl_list_for_each(shsurf, &shseat->popup_grab.surfaces_list, popup.grab_link) {
+			shsurf->popup.shseat = NULL;
+			if (prev) {
+				wl_list_init(&prev->popup.grab_link);
+			}
+			prev = shsurf;
+		}
+		wl_list_init(&prev->popup.grab_link);
+	}
+
+	wl_list_remove(&shseat->seat_destroy_listener.link);
+	seat->private = NULL;
+	free(shseat);
+}
+
+static void
+create_shell_seat(struct weston_seat *seat)
+{
+	struct shell_seat *shseat;
+
+	shseat = calloc(1, sizeof *shseat);
+	if (!shseat) {
+		weston_log("no memory to allocate shell seat\n");
+		return;
+	}
+
+	shseat->seat = seat;
+	wl_list_init(&shseat->popup_grab.surfaces_list);
+
+	shseat->seat_destroy_listener.notify = destroy_shell_seat;
+	wl_signal_add(&seat->destroy_signal,
+	              &shseat->seat_destroy_listener);
+
+	seat->private = shseat;
+}
+
+static struct shell_seat *
+get_shell_seat(struct weston_seat *seat)
+{
+	if (seat->private == NULL) {
+		create_shell_seat(seat);
+	}
+	return seat->private;
+}
+
 static void
 popup_grab_focus(struct wl_pointer_grab *grab,
 		 struct wl_surface *surface,
@@ -1843,9 +1912,9 @@ popup_grab_focus(struct wl_pointer_grab *grab,
 		 wl_fixed_t y)
 {
 	struct wl_pointer *pointer = grab->pointer;
-	struct shell_surface *priv =
-		container_of(grab, struct shell_surface, popup.grab);
-	struct wl_client *client = priv->surface->surface.resource.client;
+	struct shell_seat *shseat =
+	    container_of(grab, struct shell_seat, popup_grab.grab);
+	struct wl_client *client = shseat->popup_grab.client;
 
 	if (surface && surface->resource.client == client) {
 		wl_pointer_set_focus(pointer, surface, x, y);
@@ -1874,8 +1943,8 @@ popup_grab_button(struct wl_pointer_grab *grab,
 		  uint32_t time, uint32_t button, uint32_t state_w)
 {
 	struct wl_resource *resource;
-	struct shell_surface *shsurf =
-		container_of(grab, struct shell_surface, popup.grab);
+	struct shell_seat *shseat =
+	    container_of(grab, struct shell_seat, popup_grab.grab);
 	struct wl_display *display;
 	enum wl_pointer_button_state state = state_w;
 	uint32_t serial;
@@ -1886,13 +1955,13 @@ popup_grab_button(struct wl_pointer_grab *grab,
 		serial = wl_display_get_serial(display);
 		wl_pointer_send_button(resource, serial, time, button, state);
 	} else if (state == WL_POINTER_BUTTON_STATE_RELEASED &&
-		   (shsurf->popup.initial_up ||
-		    time - shsurf->popup.seat->pointer->grab_time > 500)) {
+		   (shseat->popup_grab.initial_up ||
+		    time - shseat->seat->pointer.grab_time > 500)) {
 		popup_grab_end(grab->pointer);
 	}
 
 	if (state == WL_POINTER_BUTTON_STATE_RELEASED)
-		shsurf->popup.initial_up = 1;
+		shseat->popup_grab.initial_up = 1;
 }
 
 static const struct wl_pointer_grab_interface popup_grab_interface = {
@@ -1905,37 +1974,73 @@ static void
 popup_grab_end(struct wl_pointer *pointer)
 {
 	struct wl_pointer_grab *grab = pointer->grab;
-	struct shell_surface *shsurf =
-		container_of(grab, struct shell_surface, popup.grab);
+	struct shell_seat *shseat =
+	    container_of(grab, struct shell_seat, popup_grab.grab);
+	struct shell_surface *shsurf;
+	struct shell_surface *prev = NULL;
 
 	if (pointer->grab->interface == &popup_grab_interface) {
-		wl_shell_surface_send_popup_done(&shsurf->resource);
 		wl_pointer_end_grab(grab->pointer);
-		shsurf->popup.grab.pointer = NULL;
+		shseat->popup_grab.client = NULL;
+		/* Send the popup_done event to all the popups open */
+		wl_list_for_each(shsurf, &shseat->popup_grab.surfaces_list, popup.grab_link) {
+			wl_shell_surface_send_popup_done(&shsurf->resource);
+			shsurf->popup.shseat = NULL;
+			if (prev) {
+				wl_list_init(&prev->popup.grab_link);
+			}
+			prev = shsurf;
+		}
+		wl_list_init(&prev->popup.grab_link);
+		wl_list_init(&shseat->popup_grab.surfaces_list);
+	}
+}
+
+static void
+push_popup_grab(struct shell_surface *shsurf, struct shell_seat *shseat)
+{
+	struct wl_seat *seat = &shseat->seat->seat;
+
+	if (wl_list_empty(&shseat->popup_grab.surfaces_list)) {
+		shseat->popup_grab.client = shsurf->surface->surface.resource.client;
+		shseat->popup_grab.grab.interface = &popup_grab_interface;
+		shseat->popup_grab.initial_up = 0;
+
+		wl_pointer_start_grab(seat->pointer, &shseat->popup_grab.grab);
+	}
+	wl_list_insert(&shseat->popup_grab.surfaces_list, &shsurf->popup.grab_link);
+}
+
+static void
+pop_popup_grab(struct shell_surface *shsurf)
+{
+	struct shell_seat *shseat = shsurf->popup.shseat;
+
+	wl_list_remove(&shsurf->popup.grab_link);
+	wl_list_init(&shsurf->popup.grab_link);
+	if (wl_list_empty(&shseat->popup_grab.surfaces_list)) {
+		wl_pointer_end_grab(shseat->popup_grab.grab.pointer);
 	}
 }
 
 static void
 shell_map_popup(struct shell_surface *shsurf)
 {
-	struct wl_seat *seat = shsurf->popup.seat;
+	struct shell_seat *shseat = shsurf->popup.shseat;
 	struct weston_surface *es = shsurf->surface;
 	struct weston_surface *parent = shsurf->parent;
 
 	es->output = parent->output;
-	shsurf->popup.grab.interface = &popup_grab_interface;
 
-	shsurf->popup.initial_up = 0;
 	weston_surface_set_transform_parent(es, parent);
 	weston_surface_set_position(es, shsurf->popup.x, shsurf->popup.y);
 	weston_surface_update_transform(es);
 
-	/* We don't require the grab to still be active, but if another
-	 * grab has started in the meantime, we end the popup now. */
-	if (seat->pointer->grab_serial == shsurf->popup.serial) {
-		wl_pointer_start_grab(seat->pointer, &shsurf->popup.grab);
+	if (shseat->seat->pointer.grab_serial == shsurf->popup.serial) {
+		push_popup_grab(shsurf, shseat);
 	} else {
 		wl_shell_surface_send_popup_done(&shsurf->resource);
+		shseat->popup_grab.client = NULL;
 	}
 }
 
@@ -1951,7 +2056,7 @@ shell_surface_set_popup(struct wl_client *client,
 
 	shsurf->type = SHELL_SURFACE_POPUP;
 	shsurf->parent = parent_resource->data;
-	shsurf->popup.seat = seat_resource->data;
+	shsurf->popup.shseat = get_shell_seat(seat_resource->data);
 	shsurf->popup.serial = serial;
 	shsurf->popup.x = x;
 	shsurf->popup.y = y;
@@ -1973,8 +2078,9 @@ static const struct wl_shell_surface_interface shell_surface_implementation = {
 static void
 destroy_shell_surface(struct shell_surface *shsurf)
 {
-	if (shsurf->popup.grab.pointer)
-		wl_pointer_end_grab(shsurf->popup.grab.pointer);
+	if (!wl_list_empty(&shsurf->popup.grab_link)) {
+		pop_popup_grab(shsurf);
+	}
 
 	if (shsurf->fullscreen.type == WL_SHELL_SURFACE_FULLSCREEN_METHOD_DRIVER &&
 	    shell_surface_is_top_fullscreen(shsurf)) {
@@ -2071,6 +2177,7 @@ create_shell_surface(void *shell, struct weston_surface *surface,
 
 	/* init link so its safe to always remove it in destroy_shell_surface */
 	wl_list_init(&shsurf->link);
+	wl_list_init(&shsurf->popup.grab_link);
 
 	/* empty when not in use */
 	wl_list_init(&shsurf->rotation.transform.link);
@@ -3177,6 +3284,10 @@ shell_surface_configure(struct weston_surface *es, int32_t sx, int32_t sy, int32
 
 	int type_changed = 0;
 
+	if (!weston_surface_is_mapped(es) && !wl_list_empty(&shsurf->popup.grab_link)) {
+		pop_popup_grab(shsurf);
+	}
+
 	if (width == 0)
 		return;
 
-- 
1.8.2



More information about the wayland-devel mailing list