[weston,5/6] Display the window menu using the desktop shell

Submitted by Dima Ryazanov on Dec. 5, 2016, 3:36 a.m.

Details

Message ID 20161205033635.20086-6-dima@gmail.com
State New
Headers show
Series "Implement xdg-shell's show_window_menu API" ( rev: 1 ) in Wayland

Not browsing as part of any series.

Commit Message

Dima Ryazanov Dec. 5, 2016, 3:36 a.m.
The compositor creates a resource that gives the desktop shell limited access
to the window that requested a window menu, and sends the desktop shell a
"show_window_menu" event. The desktop shell then displays a popup menu using
the usual window APIs, and sends back a request with the result (only "Close"
for now).

This patch is hacky and not quite finished - but I'd like to get some feedback
first, to make sure this is the correct approach.

Signed-off-by: Dima Ryazanov <dima@gmail.com>
---
 clients/desktop-shell.c               | 45 +++++++++++++++++++++++++++--
 clients/window.c                      | 47 +++++++++++++++++++++++++++++--
 clients/window.h                      | 10 +++++++
 desktop-shell/shell.c                 | 49 +++++++++++++++++++++++++++++++-
 desktop-shell/shell.h                 |  2 ++
 libweston-desktop/internal.h          |  5 ++++
 libweston-desktop/libweston-desktop.h |  7 +++++
 libweston-desktop/surface.c           | 30 ++++++++++++++++++++
 libweston-desktop/xdg-shell-v6.c      | 53 +++++++++++++++++++++++++++++++++++
 protocol/weston-desktop-shell.xml     | 12 ++++++++
 10 files changed, 255 insertions(+), 5 deletions(-)

Patch hide | download patch | download mbox

diff --git a/clients/desktop-shell.c b/clients/desktop-shell.c
index a1cf51d..e12171f 100644
--- a/clients/desktop-shell.c
+++ b/clients/desktop-shell.c
@@ -50,7 +50,8 @@ 
 #include "shared/xalloc.h"
 #include "shared/zalloc.h"
 
-#include "weston-desktop-shell-client-protocol.h"
+#include "xdg-shell-unstable-v6-client-protocol.h"
+#include "weston-desktop-shell-client-protocol.h"  /* XXX: Relies on the include order */
 
 #define DEFAULT_CLOCK_FORMAT CLOCK_FORMAT_MINUTES
 
@@ -1071,10 +1072,50 @@  desktop_shell_grab_cursor(void *data,
 	}
 }
 
+static void
+frame_menu_func(void *data, struct input *input, int index)
+{
+	struct window *window = data;
+	struct weston_desktop_shell *desktop_shell = window_get_user_data(window);
+
+	switch (index) {
+	case 0: /* close */
+		weston_desktop_shell_window_menu_close(desktop_shell);
+		break;
+	}
+
+	window_destroy(window);
+}
+
+static void
+desktop_shell_show_window_menu(void *data,
+			       struct weston_desktop_shell *desktop_shell,
+			       struct zxdg_surface_v6 *parent_surface,
+			       struct wl_seat *seat, uint32_t serial, uint32_t time,
+			       int32_t x, int32_t y)
+{
+	struct desktop *desktop = data;
+	struct window *parent = window_create_foreign(desktop->display,
+						      parent_surface);
+	struct input *input = find_input_for_seat(desktop->display, seat);
+
+	static const char *entries[] = {
+		"Close",
+	};
+
+	assert(input);
+
+	window_set_user_data(parent, desktop_shell);
+
+	window_show_menu(desktop->display, input, serial, time, parent, x, y,
+			 frame_menu_func, entries, ARRAY_LENGTH(entries));
+}
+
 static const struct weston_desktop_shell_listener listener = {
 	desktop_shell_configure,
 	desktop_shell_prepare_lock_surface,
-	desktop_shell_grab_cursor
+	desktop_shell_grab_cursor,
+	desktop_shell_show_window_menu
 };
 
 static void
diff --git a/clients/window.c b/clients/window.c
index 12884f4..d4eeca6 100644
--- a/clients/window.c
+++ b/clients/window.c
@@ -1558,7 +1558,8 @@  surface_destroy(struct surface *surface)
 	if (surface->subsurface)
 		wl_subsurface_destroy(surface->subsurface);
 
-	wl_surface_destroy(surface->surface);
+	if (surface->surface)
+		wl_surface_destroy(surface->surface);
 
 	if (surface->toysurface)
 		surface->toysurface->destroy(surface->toysurface);
@@ -3540,6 +3541,18 @@  input_get_focus_widget(struct input *input)
 	return input->focus_widget;
 }
 
+struct input *
+find_input_for_seat(struct display *display, struct wl_seat *seat)
+{
+	struct input *input = NULL;
+
+	wl_list_for_each(input, &display->input_list, link) {
+		if (input_get_seat(input) == seat)
+			return input;
+	}
+	return NULL;
+}
+
 struct data_offer {
 	struct wl_data_offer *offer;
 	struct input *input;
@@ -5213,7 +5226,7 @@  window_create_internal(struct display *display, int custom)
 	wl_list_insert(display->window_list.prev, &window->link);
 	wl_list_init(&window->redraw_task.link);
 
-	wl_list_init (&window->window_output_list);
+	wl_list_init(&window->window_output_list);
 
 	return window;
 }
@@ -5266,6 +5279,36 @@  window_create_custom(struct display *display)
 	return window_create_internal(display, 1);
 }
 
+struct window *
+window_create_foreign(struct display *display, struct zxdg_surface_v6 *xdg_surface)
+{
+	struct window *window;
+	struct surface *surface;
+
+	window = xzalloc(sizeof *window);
+	wl_list_init(&window->subsurface_list);
+	window->display = display;
+
+	surface = xzalloc(sizeof *surface);
+	surface->window = window;
+	surface->buffer_scale = 1;
+	wl_list_insert(&window->subsurface_list, &surface->link);
+	window->main_surface = surface;
+
+	window->xdg_surface = xdg_surface;
+
+	window->custom = 1;
+	window->preferred_format = WINDOW_PREFERRED_FORMAT_NONE;
+
+	surface->buffer_type = get_preferred_buffer_type(display);
+
+	wl_list_insert(display->window_list.prev, &window->link);
+	wl_list_init(&window->redraw_task.link);
+	wl_list_init(&window->window_output_list);
+
+	return window;
+}
+
 void
 window_set_parent(struct window *window,
 		  struct window *parent_window)
diff --git a/clients/window.h b/clients/window.h
index 1cb3d27..8cf4aa6 100644
--- a/clients/window.h
+++ b/clients/window.h
@@ -1,3 +1,4 @@ 
+
 /*
  * Copyright © 2008 Kristian Høgsberg
  *
@@ -713,4 +714,13 @@  xkb_mod_mask_t
 keysym_modifiers_get_mask(struct wl_array *modifiers_map,
 			  const char *name);
 
+/* Used interally by the desktop shell. */
+
+struct zxdg_surface_v6;
+struct window *
+window_create_foreign(struct display *display, struct zxdg_surface_v6 *surface);
+
+struct input *
+find_input_for_seat(struct display *display, struct wl_seat *seat);
+
 #endif
diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c
index 3913f95..96366d1 100644
--- a/desktop-shell/shell.c
+++ b/desktop-shell/shell.c
@@ -2531,6 +2531,42 @@  desktop_surface_committed(struct weston_desktop_surface *desktop_surface,
 }
 
 static void
+desktop_show_window_menu(struct weston_desktop_surface *desktop_surface,
+			 struct weston_seat *seat, uint32_t serial,
+			 int32_t x, int32_t y, void *data)
+{
+	struct desktop_shell *shell = data;
+	struct wl_resource *shell_resource = shell->child.desktop_shell;
+	struct wl_resource *parent_resource;
+	struct wl_resource *shell_seat_resource = NULL;
+	struct wl_resource *resource;
+
+	parent_resource = weston_desktop_create_window_menu_parent_resource(
+		shell->child.client, desktop_surface, 0);
+
+	if (!parent_resource)
+		return;
+
+	shell->window_menu_parent_surface = desktop_surface;
+
+	wl_resource_for_each(resource, &seat->base_resource_list) {
+		if (wl_resource_get_client(resource) == shell->child.client) {
+			shell_seat_resource = resource;
+			break;
+		}
+	}
+
+	assert(shell_seat_resource);
+
+	uint32_t time = weston_compositor_get_time();
+
+	weston_desktop_shell_send_show_window_menu(shell_resource,
+						   parent_resource,
+						   shell_seat_resource,
+						   serial, time, x, y);
+}
+
+static void
 set_fullscreen(struct shell_surface *shsurf, bool fullscreen,
 	       struct weston_output *output)
 {
@@ -2788,6 +2824,7 @@  static const struct weston_desktop_api shell_desktop_api = {
 	.surface_added = desktop_surface_added,
 	.surface_removed = desktop_surface_removed,
 	.committed = desktop_surface_committed,
+	.show_window_menu = desktop_show_window_menu,
 	.move = desktop_surface_move,
 	.resize = desktop_surface_resize,
 	.fullscreen_requested = desktop_surface_fullscreen_requested,
@@ -3138,6 +3175,15 @@  desktop_shell_set_panel_position(struct wl_client *client,
 	shell->panel_position = position;
 }
 
+static void
+desktop_shell_window_menu_close(struct wl_client *client,
+				struct wl_resource *resource)
+{
+	struct desktop_shell *shell = wl_resource_get_user_data(resource);
+
+	weston_desktop_surface_close(shell->window_menu_parent_surface);
+}
+
 static const struct weston_desktop_shell_interface desktop_shell_implementation = {
 	desktop_shell_set_background,
 	desktop_shell_set_panel,
@@ -3145,7 +3191,8 @@  static const struct weston_desktop_shell_interface desktop_shell_implementation
 	desktop_shell_unlock,
 	desktop_shell_set_grab_surface,
 	desktop_shell_desktop_ready,
-	desktop_shell_set_panel_position
+	desktop_shell_set_panel_position,
+	desktop_shell_window_menu_close
 };
 
 static void
diff --git a/desktop-shell/shell.h b/desktop-shell/shell.h
index a1cea75..0f3c2bc 100644
--- a/desktop-shell/shell.h
+++ b/desktop-shell/shell.h
@@ -171,6 +171,8 @@  struct desktop_shell {
 	struct weston_surface *lock_surface;
 	struct wl_listener lock_surface_listener;
 
+	struct weston_desktop_surface *window_menu_parent_surface;
+
 	struct {
 		struct wl_array array;
 		unsigned int current;
diff --git a/libweston-desktop/internal.h b/libweston-desktop/internal.h
index e7044a4..2ecc8c2 100644
--- a/libweston-desktop/internal.h
+++ b/libweston-desktop/internal.h
@@ -159,6 +159,11 @@  weston_desktop_surface_add_resource(struct weston_desktop_surface *surface,
 				    const struct wl_interface *interface,
 				    const void *implementation, uint32_t id,
 				    wl_resource_destroy_func_t destroy);
+struct wl_resource *
+weston_desktop_surface_add_foreign_resource(struct wl_client *wl_client,
+					    struct weston_desktop_surface *surface,
+					    const struct wl_interface *interface,
+					    const void *implementation, uint32_t id);
 struct weston_desktop_surface *
 weston_desktop_surface_from_grab_link(struct wl_list *grab_link);
 
diff --git a/libweston-desktop/libweston-desktop.h b/libweston-desktop/libweston-desktop.h
index befecdf..d25359b 100644
--- a/libweston-desktop/libweston-desktop.h
+++ b/libweston-desktop/libweston-desktop.h
@@ -159,6 +159,13 @@  weston_desktop_surface_get_max_size(struct weston_desktop_surface *surface);
 struct weston_size
 weston_desktop_surface_get_min_size(struct weston_desktop_surface *surface);
 
+struct wl_resource;
+
+WL_EXPORT struct wl_resource *
+weston_desktop_create_window_menu_parent_resource(struct wl_client *wl_client,
+						  struct weston_desktop_surface *surface,
+						  uint32_t id);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/libweston-desktop/surface.c b/libweston-desktop/surface.c
index 2205107..7c3fffe 100644
--- a/libweston-desktop/surface.c
+++ b/libweston-desktop/surface.c
@@ -317,6 +317,36 @@  weston_desktop_surface_add_resource(struct weston_desktop_surface *surface,
 	return resource;
 }
 
+static void
+destroy_foreign_resource(struct wl_resource *resource)
+{
+	struct weston_desktop_surface *surface =
+		wl_resource_get_user_data(resource);
+
+	if (surface != NULL)
+		wl_list_remove(wl_resource_get_link(resource));
+}
+
+struct wl_resource *
+weston_desktop_surface_add_foreign_resource(struct wl_client *wl_client,
+					    struct weston_desktop_surface *surface,
+					    const struct wl_interface *interface,
+					    const void *implementation, uint32_t id)
+{
+	struct wl_resource *resource;
+
+	resource = wl_resource_create(wl_client, interface, 1, id);
+	if (resource == NULL) {
+		/* Probably don't want to kill the shell here. Kill the client? Ignore? */
+		return NULL;
+	}
+	wl_resource_set_implementation(resource, implementation, surface,
+				       destroy_foreign_resource);
+	wl_list_insert(&surface->resource_list, wl_resource_get_link(resource));
+
+	return resource;
+}
+
 struct weston_desktop_surface *
 weston_desktop_surface_from_grab_link(struct wl_list *grab_link)
 {
diff --git a/libweston-desktop/xdg-shell-v6.c b/libweston-desktop/xdg-shell-v6.c
index cab9808..59bb954 100644
--- a/libweston-desktop/xdg-shell-v6.c
+++ b/libweston-desktop/xdg-shell-v6.c
@@ -1346,3 +1346,56 @@  weston_desktop_xdg_shell_v6_create(struct weston_desktop *desktop, struct wl_dis
 				WD_XDG_SHELL_PROTOCOL_VERSION, desktop,
 				weston_desktop_xdg_shell_bind);
 }
+
+static void
+weston_desktop_window_menu_parent_get_toplevel(struct wl_client *wl_client,
+					       struct wl_resource *resource,
+					       uint32_t id)
+{
+	wl_resource_post_error(resource,
+			       WL_DISPLAY_ERROR_INVALID_OBJECT,
+			       "not allowed");
+}
+
+static void
+weston_desktop_window_menu_parent_set_window_geometry(struct wl_client *wl_client,
+						      struct wl_resource *resource,
+						      int32_t x, int32_t y,
+						      int32_t width, int32_t height)
+{
+	wl_resource_post_error(resource,
+			       WL_DISPLAY_ERROR_INVALID_OBJECT,
+			       "not allowed");
+}
+
+static void
+weston_desktop_window_menu_parent_ack_configure(struct wl_client *wl_client,
+						struct wl_resource *resource,
+						uint32_t serial)
+{
+	wl_resource_post_error(resource,
+			       WL_DISPLAY_ERROR_INVALID_OBJECT,
+			       "not allowed");
+}
+
+static const struct zxdg_surface_v6_interface weston_desktop_window_menu_parent_implementation = {
+	.destroy             = weston_desktop_destroy_request,
+	.get_toplevel        = weston_desktop_window_menu_parent_get_toplevel,
+	.get_popup           = weston_desktop_xdg_surface_protocol_get_popup,
+	.set_window_geometry = weston_desktop_window_menu_parent_set_window_geometry,
+	.ack_configure       = weston_desktop_window_menu_parent_ack_configure,
+};
+
+WL_EXPORT struct wl_resource *
+weston_desktop_create_window_menu_parent_resource(struct wl_client *wl_client,
+						  struct weston_desktop_surface *surface,
+						  uint32_t id)
+{
+	return weston_desktop_surface_add_foreign_resource(
+		wl_client,
+		surface,
+		&zxdg_surface_v6_interface,
+		&weston_desktop_window_menu_parent_implementation,
+		id
+	);
+}
diff --git a/protocol/weston-desktop-shell.xml b/protocol/weston-desktop-shell.xml
index 91c5eb4..9f717b5 100644
--- a/protocol/weston-desktop-shell.xml
+++ b/protocol/weston-desktop-shell.xml
@@ -114,6 +114,18 @@ 
       <arg name="position" type="uint"/>
     </request>
 
+    <event name="show_window_menu">
+      <arg name="parent" type="new_id" interface="zxdg_surface_v6"/>
+      <arg name="seat" type="object" interface="wl_seat"/>
+      <arg name="serial" type="uint"/>
+      <arg name="time" type="uint"/>
+      <arg name="x" type="int"/>
+      <arg name="y" type="int"/>
+    </event>
+
+    <request name="window_menu_close">
+    </request>
+
   </interface>
 
   <interface name="weston_screensaver" version="1">