From a26ded47d9e80a2de4b8ade86e33f0c4f6b1b965 Mon Sep 17 00:00:00 2001 From: Giovanni Campagna Date: Fri, 30 Aug 2013 09:40:36 +0200 Subject: [PATCH] Add a private gtk-mutter protocol Add a new interface, gtk_shell, than can be used by gtk to retrieve a surface extension called gtk_surface, which will be used to communicate with mutter all the GTK extensions to EWMH https://bugzilla.gnome.org/show_bug.cgi?id=707128 Add support for GTK application menus To do so, we need to be able to set surface state before creating the MetaWindow, so we introduce MetaWaylandSurfaceInitialState as a staging area. The gtk-shell-surface implementation would either write to the initial state, or directly to the window. At the same, implement set_title and set_class too, because it's easy enough. https://bugzilla.gnome.org/show_bug.cgi?id=707128 --- protocol/gtk-shell.xml | 30 +++++ src/Makefile.am | 3 + src/core/window-private.h | 13 ++ src/core/window-props.c | 41 ++----- src/core/window.c | 80 +++++++++++- src/wayland/meta-wayland-surface.c | 188 ++++++++++++++++++++++++++++- src/wayland/meta-wayland-surface.h | 21 ++++ 7 files changed, 344 insertions(+), 32 deletions(-) create mode 100644 protocol/gtk-shell.xml diff --git a/protocol/gtk-shell.xml b/protocol/gtk-shell.xml new file mode 100644 index 000000000..e2cc4f455 --- /dev/null +++ b/protocol/gtk-shell.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Makefile.am b/src/Makefile.am index c07e44331..1c6195d37 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -40,6 +40,9 @@ mutter_built_sources = \ $(dbus_xrandr_built_sources) \ mutter-enum-types.h \ mutter-enum-types.c \ + wayland/gtk-shell-protocol.c \ + wayland/gtk-shell-server-protocol.h \ + wayland/gtk-shell-client-protocol.h \ wayland/xserver-protocol.c \ wayland/xserver-server-protocol.h \ wayland/xserver-client-protocol.h diff --git a/src/core/window-private.h b/src/core/window-private.h index f7769d091..3ca2b0a99 100644 --- a/src/core/window-private.h +++ b/src/core/window-private.h @@ -703,4 +703,17 @@ void meta_window_set_shape_region (MetaWindow *window, cairo_region_t *region); void meta_window_update_shape_region_x11 (MetaWindow *window); +void meta_window_set_title (MetaWindow *window, + const char *title); +void meta_window_set_wm_class (MetaWindow *window, + const char *wm_class, + const char *wm_instance); +void meta_window_set_gtk_dbus_properties (MetaWindow *window, + const char *application_id, + const char *unique_bus_name, + const char *appmenu_path, + const char *menubar_path, + const char *application_object_path, + const char *window_object_path); + #endif diff --git a/src/core/window-props.c b/src/core/window-props.c index 13ee3bbdb..73452cc27 100644 --- a/src/core/window-props.c +++ b/src/core/window-props.c @@ -489,28 +489,19 @@ static void set_window_title (MetaWindow *window, const char *title) { - char *str; + char *new_title = NULL; gboolean modified = set_title_text (window, window->using_net_wm_visible_name, title, window->display->atom__NET_WM_VISIBLE_NAME, - &window->title); + &new_title); window->using_net_wm_visible_name = modified; - /* strndup is a hack since GNU libc has broken %.10s */ - str = g_strndup (window->title, 10); - g_free (window->desc); - window->desc = g_strdup_printf ("0x%lx (%s)", window->xwindow, str); - g_free (str); + meta_window_set_title (window, new_title); - if (window->frame) - meta_ui_set_frame_title (window->screen->ui, - window->frame->xwindow, - window->title); - - g_object_notify (G_OBJECT (window), "title"); + g_free (new_title); } static void @@ -875,23 +866,15 @@ reload_wm_class (MetaWindow *window, MetaPropValue *value, gboolean initial) { - if (window->res_class) - g_free (window->res_class); - if (window->res_name) - g_free (window->res_name); - - window->res_class = NULL; - window->res_name = NULL; - if (value->type != META_PROP_VALUE_INVALID) - { - if (value->v.class_hint.res_name) - window->res_name = g_strdup (value->v.class_hint.res_name); - - if (value->v.class_hint.res_class) - window->res_class = g_strdup (value->v.class_hint.res_class); - - g_object_notify (G_OBJECT (window), "wm-class"); + { + meta_window_set_wm_class (window, + value->v.class_hint.res_class, + value->v.class_hint.res_name); + } + else + { + meta_window_set_wm_class (window, NULL, NULL); } meta_verbose ("Window %s class: '%s' name: '%s'\n", diff --git a/src/core/window.c b/src/core/window.c index 5e87e5881..199799d24 100644 --- a/src/core/window.c +++ b/src/core/window.c @@ -1064,7 +1064,10 @@ meta_window_new_shared (MetaDisplay *display, window->xgroup_leader = None; meta_window_compute_group (window); - meta_window_load_initial_properties (window); + if (client_type == META_WINDOW_CLIENT_TYPE_X11) + meta_window_load_initial_properties (window); + else + meta_wayland_surface_set_initial_state (window->surface, window); if (!window->override_redirect && client_type == META_WINDOW_CLIENT_TYPE_X11) @@ -11467,3 +11470,78 @@ meta_window_can_close (MetaWindow *window) { return window->has_close_func; } + +void +meta_window_set_title (MetaWindow *window, + const char *title) +{ + char *str; + + g_free (window->title); + window->title = g_strdup (title); + + /* strndup is a hack since GNU libc has broken %.10s */ + str = g_strndup (window->title, 10); + g_free (window->desc); + window->desc = g_strdup_printf ("0x%lx (%s)", window->xwindow, str); + g_free (str); + + if (window->frame) + meta_ui_set_frame_title (window->screen->ui, + window->frame->xwindow, + window->title); + + g_object_notify (G_OBJECT (window), "title"); +} + +void +meta_window_set_wm_class (MetaWindow *window, + const char *wm_class, + const char *wm_instance) +{ + g_free (window->res_class); + g_free (window->res_name); + + window->res_name = g_strdup (wm_instance); + window->res_class = g_strdup (wm_class); + + g_object_notify (G_OBJECT (window), "wm-class"); +} + +void +meta_window_set_gtk_dbus_properties (MetaWindow *window, + const char *application_id, + const char *unique_bus_name, + const char *appmenu_path, + const char *menubar_path, + const char *application_object_path, + const char *window_object_path) +{ + g_object_freeze_notify (G_OBJECT (window)); + + g_free (window->gtk_application_id); + window->gtk_application_id = g_strdup (application_id); + g_object_notify (G_OBJECT (window), "gtk-application-id"); + + g_free (window->gtk_unique_bus_name); + window->gtk_unique_bus_name = g_strdup (unique_bus_name); + g_object_notify (G_OBJECT (window), "gtk-unique-bus-name"); + + g_free (window->gtk_app_menu_object_path); + window->gtk_app_menu_object_path = g_strdup (appmenu_path); + g_object_notify (G_OBJECT (window), "gtk-app-menu-object-path"); + + g_free (window->gtk_menubar_object_path); + window->gtk_menubar_object_path = g_strdup (menubar_path); + g_object_notify (G_OBJECT (window), "gtk-menubar-object-path"); + + g_free (window->gtk_application_object_path); + window->gtk_application_object_path = g_strdup (application_object_path); + g_object_notify (G_OBJECT (window), "gtk-application-object-path"); + + g_free (window->gtk_window_object_path); + window->gtk_window_object_path = g_strdup (window_object_path); + g_object_notify (G_OBJECT (window), "gtk-window-object-path"); + + g_object_thaw_notify (G_OBJECT (window)); +} diff --git a/src/wayland/meta-wayland-surface.c b/src/wayland/meta-wayland-surface.c index deccf3474..1ce77f72d 100644 --- a/src/wayland/meta-wayland-surface.c +++ b/src/wayland/meta-wayland-surface.c @@ -37,6 +37,7 @@ #include #include +#include "gtk-shell-server-protocol.h" #include "meta-wayland-private.h" #include "meta-xwayland-private.h" @@ -56,6 +57,9 @@ #include "meta-weston-launch.h" #include "monitor-private.h" +static void ensure_initial_state (MetaWaylandSurface *surface); +static void free_initial_state (MetaWaylandSurfaceInitialState *surface); + static void surface_process_damage (MetaWaylandSurface *surface, cairo_region_t *region) @@ -336,6 +340,9 @@ meta_wayland_surface_free (MetaWaylandSurface *surface) MetaWaylandCompositor *compositor = surface->compositor; MetaWaylandFrameCallback *cb, *next; + if (surface->initial_state) + free_initial_state (surface->initial_state); + compositor->surfaces = g_list_remove (compositor->surfaces, surface); meta_wayland_buffer_reference (&surface->buffer_ref, NULL); @@ -737,7 +744,15 @@ shell_surface_set_title (struct wl_client *client, MetaWaylandSurfaceExtension *extension = wl_resource_get_user_data (resource); MetaWaylandSurface *surface = extension->surface; - g_warning ("TODO: support shell_surface_set_title request"); + if (surface->window) + meta_window_set_title (surface->window, title); + else + { + ensure_initial_state (surface); + + g_free (surface->initial_state->title); + surface->initial_state->title = g_strdup (title); + } } static void @@ -748,7 +763,15 @@ shell_surface_set_class (struct wl_client *client, MetaWaylandSurfaceExtension *extension = wl_resource_get_user_data (resource); MetaWaylandSurface *surface = extension->surface; - g_warning ("TODO: support shell_surface_set_class request"); + if (surface->window) + meta_window_set_wm_class (surface->window, class_, class_); + else + { + ensure_initial_state (surface); + + g_free (surface->initial_state->wm_class); + surface->initial_state->wm_class = g_strdup (class_); + } } static const struct wl_shell_surface_interface meta_wayland_shell_surface_interface = @@ -852,6 +875,111 @@ bind_shell (struct wl_client *client, wl_resource_set_implementation (resource, &meta_wayland_shell_interface, data, NULL); } +static void +set_dbus_properties (struct wl_client *client, + struct wl_resource *resource, + const char *application_id, + const char *app_menu_path, + const char *menubar_path, + const char *window_object_path, + const char *application_object_path, + const char *unique_bus_name) +{ + MetaWaylandSurfaceExtension *extension = wl_resource_get_user_data (resource); + MetaWaylandSurface *surface = extension->surface; + + if (surface == NULL) + { + wl_resource_post_error (resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "object is not associated with a toplevel surface"); + return; + } + + if (surface->window) + { + meta_window_set_gtk_dbus_properties (surface->window, + application_id, + unique_bus_name, + app_menu_path, + menubar_path, + application_object_path, + window_object_path); + } + else + { + MetaWaylandSurfaceInitialState *initial; + + ensure_initial_state (surface); + initial = surface->initial_state; + + g_free (initial->gtk_application_id); + initial->gtk_application_id = g_strdup (application_id); + + g_free (initial->gtk_unique_bus_name); + initial->gtk_unique_bus_name = g_strdup (unique_bus_name); + + g_free (initial->gtk_app_menu_path); + initial->gtk_app_menu_path = g_strdup (app_menu_path); + + g_free (initial->gtk_menubar_path); + initial->gtk_menubar_path = g_strdup (menubar_path); + + g_free (initial->gtk_application_object_path); + initial->gtk_application_object_path = g_strdup (application_object_path); + + g_free (initial->gtk_window_object_path); + initial->gtk_window_object_path = g_strdup (window_object_path); + } +} + +static const struct gtk_surface_interface meta_wayland_gtk_surface_interface = +{ + set_dbus_properties +}; + +static void +get_gtk_surface (struct wl_client *client, + struct wl_resource *resource, + guint32 id, + struct wl_resource *surface_resource) +{ + MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource); + + if (surface->has_gtk_surface) + { + wl_resource_post_error (surface_resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "wl_shell::get_gtk_surface already requested"); + return; + } + + create_surface_extension (client, resource, id, surface, + >k_surface_interface, + &meta_wayland_gtk_surface_interface); + surface->has_gtk_surface = TRUE; +} + +static const struct gtk_shell_interface meta_wayland_gtk_shell_interface = +{ + get_gtk_surface +}; + +static void +bind_gtk_shell (struct wl_client *client, + void *data, + guint32 version, + guint32 id) +{ + struct wl_resource *resource; + + resource = wl_resource_create (client, >k_shell_interface, version, id); + wl_resource_set_implementation (resource, &meta_wayland_gtk_shell_interface, data, NULL); + + /* FIXME: ask the plugin */ + gtk_shell_send_capabilities (resource, GTK_SHELL_CAPABILITY_GLOBAL_APP_MENU); +} + void meta_wayland_init_shell (MetaWaylandCompositor *compositor) { @@ -859,5 +987,61 @@ meta_wayland_init_shell (MetaWaylandCompositor *compositor) &wl_shell_interface, 1, compositor, bind_shell) == NULL) g_error ("Failed to register a global shell object"); + + if (wl_global_create (compositor->wayland_display, + >k_shell_interface, 1, + compositor, bind_gtk_shell) == NULL) + g_error ("Failed to register a global gtk-shell object"); } +void +meta_wayland_surface_set_initial_state (MetaWaylandSurface *surface, + MetaWindow *window) +{ + MetaWaylandSurfaceInitialState *initial = surface->initial_state; + + if (initial == NULL) + return; + + if (initial->title) + meta_window_set_title (window, initial->title); + + if (initial->wm_class) + meta_window_set_wm_class (window, initial->wm_class, initial->wm_class); + + meta_window_set_gtk_dbus_properties (window, + initial->gtk_application_id, + initial->gtk_unique_bus_name, + initial->gtk_app_menu_path, + initial->gtk_menubar_path, + initial->gtk_application_object_path, + initial->gtk_window_object_path); + + free_initial_state (initial); + surface->initial_state = NULL; +} + +static void +ensure_initial_state (MetaWaylandSurface *surface) +{ + if (surface->initial_state) + return; + + surface->initial_state = g_slice_new0 (MetaWaylandSurfaceInitialState); +} + +static void +free_initial_state (MetaWaylandSurfaceInitialState *initial) +{ + g_free (initial->title); + g_free (initial->wm_class); + + g_free (initial->gtk_application_id); + g_free (initial->gtk_unique_bus_name); + g_free (initial->gtk_app_menu_path); + g_free (initial->gtk_menubar_path); + g_free (initial->gtk_application_object_path); + g_free (initial->gtk_window_object_path); + + g_slice_free (MetaWaylandSurfaceInitialState, initial); +} diff --git a/src/wayland/meta-wayland-surface.h b/src/wayland/meta-wayland-surface.h index 3926091c7..5957bceae 100644 --- a/src/wayland/meta-wayland-surface.h +++ b/src/wayland/meta-wayland-surface.h @@ -65,6 +65,19 @@ typedef struct struct wl_list frame_callback_list; } MetaWaylandDoubleBufferedState; +typedef struct +{ + char *title; + char *wm_class; + + char *gtk_application_id; + char *gtk_unique_bus_name; + char *gtk_app_menu_path; + char *gtk_menubar_path; + char *gtk_application_object_path; + char *gtk_window_object_path; +} MetaWaylandSurfaceInitialState; + struct _MetaWaylandSurface { struct wl_resource *resource; @@ -75,9 +88,14 @@ struct _MetaWaylandSurface MetaWaylandBufferReference buffer_ref; MetaWindow *window; gboolean has_shell_surface; + gboolean has_gtk_surface; /* All the pending state, that wl_surface.commit will apply. */ MetaWaylandDoubleBufferedState pending; + + /* All the initial state, that wl_shell_surface.set_* will apply + (through meta_window_new_for_wayland) */ + MetaWaylandSurfaceInitialState *initial_state; }; typedef struct @@ -95,4 +113,7 @@ MetaWaylandSurface *meta_wayland_surface_create (MetaWaylandCompositor *composit guint32 version); void meta_wayland_surface_free (MetaWaylandSurface *surface); +void meta_wayland_surface_set_initial_state (MetaWaylandSurface *surface, + MetaWindow *window); + #endif