From 555a4b164a09c40ab3a66ff6a7b3f60c41aecd11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Bonithon?= Date: Wed, 17 Aug 2022 20:15:39 +0200 Subject: [PATCH 01/22] Various small preliminary cleanups and fixes Spotted during this work. To be applied on master when the time comes, in one form or another. --- docs/reference/Makefile.am | 4 ++-- libxfce4panel/xfce-arrow-button.c | 1 + panel/Makefile.am | 3 --- panel/panel-window.c | 5 +++-- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/reference/Makefile.am b/docs/reference/Makefile.am index 28334fa90..b132ef918 100644 --- a/docs/reference/Makefile.am +++ b/docs/reference/Makefile.am @@ -51,9 +51,9 @@ GTKDOC_FLAGS = \ $(GTK_DOC_EXTRA_CFLAGS) GTKDOC_LIBS = \ + $(top_builddir)/libxfce4panel/libxfce4panel-$(LIBXFCE4PANEL_VERSION_API).la \ $(GTK_LIBS) \ - $(LIBXFCE4UTIL_LIBS) \ - $(top_builddir)/libxfce4panel/libxfce4panel-$(LIBXFCE4PANEL_VERSION_API).la + $(LIBXFCE4UTIL_LIBS) include $(top_srcdir)/gtk-doc.make diff --git a/libxfce4panel/xfce-arrow-button.c b/libxfce4panel/xfce-arrow-button.c index bd4ef3c37..93d353982 100644 --- a/libxfce4panel/xfce-arrow-button.c +++ b/libxfce4panel/xfce-arrow-button.c @@ -194,6 +194,7 @@ xfce_arrow_button_init (XfceArrowButton *button) gtk_style_context_add_provider (context, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + g_object_unref (provider); } diff --git a/panel/Makefile.am b/panel/Makefile.am index 26724f3c1..b07fd9620 100644 --- a/panel/Makefile.am +++ b/panel/Makefile.am @@ -8,7 +8,6 @@ AM_CPPFLAGS = \ -DHELPERDIR=\"$(HELPER_PATH_PREFIX)/xfce4/panel\" \ -DPACKAGE_LOCALE_DIR=\"$(localedir)\" \ -DDBUS_API_SUBJECT_TO_CHANGE \ - -DWNCK_I_KNOW_THIS_IS_UNSTABLE \ $(PLATFORM_CPPFLAGS) bin_PROGRAMS = \ @@ -64,7 +63,6 @@ xfce4_panel_CFLAGS = \ $(LIBWNCK_CFLAGS) \ $(XFCONF_CFLAGS) \ $(LIBX11_CFLAGS) \ - $(LIBWNCK_CFLAGS) \ $(PLATFORM_CFLAGS) xfce4_panel_LDFLAGS = \ @@ -82,7 +80,6 @@ xfce4_panel_LDADD = \ $(LIBWNCK_LIBS) \ $(XFCONF_LIBS) \ $(LIBX11_LIBS) \ - $(LIBWNCK_LIBS) \ -lm xfce4_panel_DEPENDENCIES = \ diff --git a/panel/panel-window.c b/panel/panel-window.c index eff656084..e404879f6 100644 --- a/panel/panel-window.c +++ b/panel/panel-window.c @@ -1138,7 +1138,7 @@ panel_window_motion_notify_event (GtkWidget *widget, panel_window_opacity_enter_queue (window, TRUE); /* leave when the pointer is not grabbed */ - if (G_UNLIKELY (window->grab_time == 0)) + if (G_LIKELY (window->grab_time == 0)) return FALSE; /* get the pointer position from the event */ @@ -1239,7 +1239,8 @@ panel_window_button_press_event (GtkWidget *widget, GDK_SEAT_CAPABILITY_ALL_POINTING, FALSE, cursor, (GdkEvent*)event, NULL, NULL); - g_object_unref (cursor); + if (cursor != NULL) + g_object_unref (cursor); /* set the grab info if the grab was successfully made */ if (G_LIKELY (status == GDK_GRAB_SUCCESS)) -- GitLab From 372f0d51a4454a4dce833c4a633dc1c210396656 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Bonithon?= Date: Wed, 24 Aug 2022 12:09:36 +0200 Subject: [PATCH 02/22] panel: Always include borders to compute snap position If a motion event is sent twice in a row for the same pointer position around `SNAP_DISTANCE` (this can happen if the pointer moves slowly), the computation returns a snap position once, and `SNAP_POSITION_NONE` the other time, because the panel has lost a pixel in between. This makes the panel blink or bounce against the edge of the screen. Easier to reproduce on Wayland. --- panel/panel-window.c | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/panel/panel-window.c b/panel/panel-window.c index e404879f6..46b56eda3 100644 --- a/panel/panel-window.c +++ b/panel/panel-window.c @@ -2112,14 +2112,33 @@ panel_window_snap_edge_gravity (gint value, static SnapPosition panel_window_snap_position (PanelWindow *window) { - guint snap_horz, snap_vert; - GdkRectangle *alloc = &window->alloc; + guint snap_horz, snap_vert; + GdkRectangle alloc = window->alloc; + PanelBorders borders; + + /* make the same calculation whether the panel is snapped or not (avoids flickering + * when the pointer moves slowly) */ + borders = panel_base_window_get_borders (PANEL_BASE_WINDOW (window)); + if (! PANEL_HAS_FLAG (borders, PANEL_BORDER_TOP)) + alloc.height++; + if (! PANEL_HAS_FLAG (borders, PANEL_BORDER_BOTTOM)) + { + alloc.y--; + alloc.height++; + } + if (! PANEL_HAS_FLAG (borders, PANEL_BORDER_LEFT)) + alloc.width++; + if (! PANEL_HAS_FLAG (borders, PANEL_BORDER_RIGHT)) + { + alloc.x--; + alloc.width++; + } /* get the snap offsets */ - snap_horz = panel_window_snap_edge_gravity (alloc->x, window->area.x, - window->area.x + window->area.width - alloc->width); - snap_vert = panel_window_snap_edge_gravity (alloc->y, window->area.y, - window->area.y + window->area.height - alloc->height); + snap_horz = panel_window_snap_edge_gravity (alloc.x, window->area.x, + window->area.x + window->area.width - alloc.width); + snap_vert = panel_window_snap_edge_gravity (alloc.y, window->area.y, + window->area.y + window->area.height - alloc.height); /* detect the snap mode */ if (snap_horz == EDGE_GRAVITY_START) -- GitLab From 388507a1fcf6fcaae26f2e4a480af0ac15fe06d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Bonithon?= Date: Mon, 22 Aug 2022 20:00:17 +0200 Subject: [PATCH 03/22] panel: Remove useless function --- panel/panel-base-window.c | 17 ----------------- panel/panel-base-window.h | 6 ------ panel/panel-window.c | 10 ++++------ 3 files changed, 4 insertions(+), 29 deletions(-) diff --git a/panel/panel-base-window.c b/panel/panel-base-window.c index b30b78dfb..7917b2729 100644 --- a/panel/panel-base-window.c +++ b/panel/panel-base-window.c @@ -780,23 +780,6 @@ panel_base_window_set_plugin_background_image (GtkWidget *widget, -void -panel_base_window_move_resize (PanelBaseWindow *window, - gint x, - gint y, - gint width, - gint height) -{ - panel_return_if_fail (PANEL_IS_BASE_WINDOW (window)); - - if (width > 0 && height > 0) - gtk_window_resize (GTK_WINDOW (window), width, height); - - gtk_window_move (GTK_WINDOW (window), x, y); -} - - - void panel_base_window_set_borders (PanelBaseWindow *window, PanelBorders borders) diff --git a/panel/panel-base-window.h b/panel/panel-base-window.h index 2cb9a7b27..33badf6d1 100644 --- a/panel/panel-base-window.h +++ b/panel/panel-base-window.h @@ -78,12 +78,6 @@ struct _PanelBaseWindow GType panel_base_window_get_type (void) G_GNUC_CONST; -void panel_base_window_move_resize (PanelBaseWindow *window, - gint x, - gint y, - gint width, - gint height); - void panel_base_window_reset_background_css (PanelBaseWindow *window); void panel_base_window_orientation_changed (PanelBaseWindow *window, gint mode); diff --git a/panel/panel-window.c b/panel/panel-window.c index 46b56eda3..2b105bc25 100644 --- a/panel/panel-window.c +++ b/panel/panel-window.c @@ -1491,8 +1491,8 @@ panel_window_size_allocate (GtkWidget *widget, /* position the autohide window */ panel_window_size_allocate_set_xy (window, w, h, &x, &y); - panel_base_window_move_resize (PANEL_BASE_WINDOW (window->autohide_window), - x, y, w, h); + gtk_window_resize (GTK_WINDOW (window->autohide_window), w, h); + gtk_window_move (GTK_WINDOW (window->autohide_window), x, y); /* slide out the panel window with popdown_speed, but ignore panels that are floating, i.e. not attached to a GdkScreen border (i.e. including panels which are on a monitor border, but @@ -1542,8 +1542,7 @@ panel_window_size_allocate (GtkWidget *widget, /* move the autohide window offscreen */ if (window->autohide_window != NULL) - panel_base_window_move_resize (PANEL_BASE_WINDOW (window->autohide_window), - -9999, -9999, -1, -1); + gtk_window_move (GTK_WINDOW (window->autohide_window), -9999, -9999); gtk_window_move (GTK_WINDOW (window), window->alloc.x, window->alloc.y); } @@ -2916,8 +2915,7 @@ panel_window_set_autohide_behavior (PanelWindow *window, NULL); /* move the window offscreen */ - panel_base_window_move_resize (PANEL_BASE_WINDOW (popup), - -9999, -9999, 3, 3); + gtk_window_move (GTK_WINDOW (popup), -9999, -9999); /* bind some properties to sync the two windows */ for (i = 0; i < G_N_ELEMENTS (properties); i++) -- GitLab From 3cb2bd0206c86efcdaa2768f2e5b23bf538094f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Bonithon?= Date: Wed, 31 Aug 2022 17:24:29 +0200 Subject: [PATCH 04/22] panel: Share code that checks if pointer is outside the panel --- panel/panel-window.c | 53 ++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/panel/panel-window.c b/panel/panel-window.c index 2b105bc25..9860f1e83 100644 --- a/panel/panel-window.c +++ b/panel/panel-window.c @@ -2471,6 +2471,25 @@ panel_window_active_window_changed (WnckScreen *screen, +static gboolean +panel_window_pointer_is_outside (PanelWindow *window) +{ + GdkDevice *device; + GdkWindow *gdkwindow; + + device = gdk_seat_get_pointer (gdk_display_get_default_seat (window->display)); + if (device != NULL) + { + gdkwindow = gdk_device_get_window_at_position (device, NULL, NULL); + return gdkwindow == NULL || gdk_window_get_effective_toplevel (gdkwindow) + != gtk_widget_get_window (GTK_WIDGET (window)); + } + + return FALSE; +} + + + static void panel_window_active_window_geometry_changed (WnckWindow *active_window, PanelWindow *window) @@ -2565,23 +2584,9 @@ panel_window_active_window_geometry_changed (WnckWindow *active_window, * with its coordinates */ if (window->autohide_state != AUTOHIDE_HIDDEN) { - if (gdk_rectangle_intersect (&panel_area, &window_area, NULL)) - { - GdkDevice *device; - gint pointer_x, pointer_y; - - device = gdk_seat_get_pointer (gdk_display_get_default_seat (window->display)); - if (device == NULL) - return; - - /* check if the cursor is outside the panel area before proceeding */ - gdk_device_get_position (device, NULL, &pointer_x, &pointer_y); - if (pointer_x <= panel_area.x - || pointer_y <= panel_area.y - || pointer_x >= panel_area.x + panel_area.width - || pointer_y >= panel_area.y + panel_area.height) - panel_window_autohide_queue (window, AUTOHIDE_POPDOWN); - } + if (gdk_rectangle_intersect (&panel_area, &window_area, NULL) + && panel_window_pointer_is_outside (window)) + panel_window_autohide_queue (window, AUTOHIDE_POPDOWN); } else { @@ -3496,9 +3501,7 @@ panel_window_freeze_autohide (PanelWindow *window) void panel_window_thaw_autohide (PanelWindow *window) { - GdkDevice *device; - GdkWindow *gdkwindow; - gboolean outside = FALSE; + gboolean outside; panel_return_if_fail (PANEL_IS_WINDOW (window)); panel_return_if_fail (window->autohide_block > 0); @@ -3509,15 +3512,7 @@ panel_window_thaw_autohide (PanelWindow *window) if (window->autohide_block > 0) return; - /* check if pointer is outside the panel */ - device = gdk_seat_get_pointer (gdk_display_get_default_seat (window->display)); - if (device != NULL) - { - gdkwindow = gdk_device_get_window_at_position (device, NULL, NULL); - outside = (gdkwindow == NULL || gdk_window_get_effective_toplevel (gdkwindow) - != gtk_widget_get_window (GTK_WIDGET (window))); - } - + outside = panel_window_pointer_is_outside (window); if (outside) panel_window_opacity_enter_queue (window, FALSE); -- GitLab From e9a2eed6019d0ae37f27d3082c0a445eb6181045 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Bonithon?= Date: Fri, 23 Sep 2022 20:15:24 +0200 Subject: [PATCH 05/22] systray: cleanup: Consolidate `sn_plugin_button_toggled()` --- plugins/systray/sn-plugin.c | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/plugins/systray/sn-plugin.c b/plugins/systray/sn-plugin.c index 8c040e432..dbe3d8d1e 100644 --- a/plugins/systray/sn-plugin.c +++ b/plugins/systray/sn-plugin.c @@ -279,39 +279,28 @@ snbox_has_hidden_cb (SnBox *box, static void -sn_plugin_button_set_arrow(SnPlugin *plugin) +sn_plugin_button_toggled (GtkWidget *button, + SnPlugin *plugin) { GtkArrowType arrow_type; gboolean show_hidden; GtkOrientation orientation; - panel_return_if_fail(XFCE_IS_SN_PLUGIN(plugin)); + panel_return_if_fail (XFCE_IS_SN_PLUGIN (plugin)); + panel_return_if_fail (GTK_IS_TOGGLE_BUTTON (button)); + panel_return_if_fail (plugin->button == button); - show_hidden = systray_box_get_show_hidden(XFCE_SYSTRAY_BOX(plugin->systray_box)); - orientation = xfce_panel_plugin_get_orientation(XFCE_PANEL_PLUGIN(plugin)); + show_hidden = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)); + sn_box_set_show_hidden (XFCE_SN_BOX (plugin->sn_box), show_hidden); + systray_box_set_show_hidden (XFCE_SYSTRAY_BOX (plugin->systray_box), show_hidden); + + orientation = xfce_panel_plugin_get_orientation (XFCE_PANEL_PLUGIN (plugin)); if (orientation == GTK_ORIENTATION_HORIZONTAL) arrow_type = show_hidden ? GTK_ARROW_LEFT : GTK_ARROW_RIGHT; else arrow_type = show_hidden ? GTK_ARROW_UP : GTK_ARROW_DOWN; - xfce_arrow_button_set_arrow_type(XFCE_ARROW_BUTTON(plugin->button), arrow_type); -} - - - -static void -sn_plugin_button_toggled (GtkWidget *button, - SnPlugin *plugin) -{ - panel_return_if_fail (XFCE_IS_SN_PLUGIN (plugin)); - panel_return_if_fail (GTK_IS_TOGGLE_BUTTON (button)); - panel_return_if_fail (plugin->button == button); - - systray_box_set_show_hidden (XFCE_SYSTRAY_BOX (plugin->systray_box), - gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))); - sn_box_set_show_hidden (XFCE_SN_BOX (plugin->sn_box), - gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))); - sn_plugin_button_set_arrow (plugin); + xfce_arrow_button_set_arrow_type (XFCE_ARROW_BUTTON (plugin->button), arrow_type); } -- GitLab From 835604716af3f1ea1e07c3498989040f9a7bbae7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Bonithon?= Date: Wed, 17 Aug 2022 20:45:27 +0200 Subject: [PATCH 06/22] Guard X11-specific/Wayland-incompatible code This includes changes that allow the panel to start on Wayland without crashing, with its Wayland-compatible embedded plugins (in the sense that they don't crash the panel). The other embedded plugins are automatically disabled. The panel can still crash because of non-embedded plugins, because they are now run as internal, i.e. in the same process as the panel. See the changes to `panel-module.c` to add non-embedded plugins to the automatic exclusion list. Features disabled: * WM check at startup (panel-application.c) * Running plugins as external (panel-module.c) * Span monitors (panel-preferences-dialog.c, panel-window.c) * Fix for #227 "Ensure that handles are always grabbable" (panel-window.c) * Intellihide (panel-window.c) --- common/panel-private.h | 14 ++++++++ libxfce4panel/xfce-panel-plugin.c | 11 ++++-- panel/panel-application.c | 2 +- panel/panel-module.c | 12 +++++-- panel/panel-plugin-external-wrapper.c | 1 - panel/panel-plugin-external.c | 9 ++++- panel/panel-plugin-external.h | 11 ++++++ panel/panel-preferences-dialog.c | 5 ++- panel/panel-preferences-dialog.h | 5 +++ panel/panel-window.c | 51 ++++++++++++++++++++------- plugins/launcher/launcher-dialog.c | 1 - wrapper/wrapper-plug.h | 10 ++++++ 12 files changed, 111 insertions(+), 21 deletions(-) diff --git a/common/panel-private.h b/common/panel-private.h index 6908a1b01..b7e260ea0 100644 --- a/common/panel-private.h +++ b/common/panel-private.h @@ -20,6 +20,12 @@ #define __PANEL_PRIVATE_H__ #include +#ifdef GDK_WINDOWING_X11 +#include +#endif +#ifdef GDK_WINDOWING_WAYLAND +#include +#endif /* support macros for debugging (improved macro for better position indication) */ /*#ifndef NDEBUG*/ @@ -155,4 +161,12 @@ G_GNUC_END_IGNORE_DEPRECATIONS } #endif +/* facilitate X11/Wayland management */ +#ifndef GDK_WINDOWING_X11 +#define GDK_IS_X11_DISPLAY(display) FALSE +#endif +#ifndef GDK_WINDOWING_WAYLAND +#define GDK_IS_WAYLAND_DISPLAY(display) FALSE +#endif + #endif /* !__PANEL_PRIVATE_H__ */ diff --git a/libxfce4panel/xfce-panel-plugin.c b/libxfce4panel/xfce-panel-plugin.c index 0927e42e4..bad592d65 100644 --- a/libxfce4panel/xfce-panel-plugin.c +++ b/libxfce4panel/xfce-panel-plugin.c @@ -28,7 +28,9 @@ #endif #include +#ifdef GDK_WINDOWING_X11 #include +#endif #include #include @@ -2514,13 +2516,16 @@ xfce_panel_plugin_position_widget (XfcePanelPlugin *plugin, gint *x, gint *y) { +#ifdef GDK_WINDOWING_X11 + GtkWidget *plug; + gint px, py; +#endif GtkRequisition requisition; GdkScreen *screen; GdkRectangle geometry; GdkDisplay *display; GdkMonitor *monitor; - GtkWidget *toplevel, *plug; - gint px, py; + GtkWidget *toplevel; GtkAllocation alloc; g_return_if_fail (XFCE_IS_PANEL_PLUGIN (plugin)); @@ -2548,6 +2553,7 @@ xfce_panel_plugin_position_widget (XfcePanelPlugin *plugin, gtk_window_get_position (GTK_WINDOW (toplevel), x, y); /* correct position for external plugins */ +#ifdef GDK_WINDOWING_X11 plug = gtk_widget_get_ancestor (attach_widget, GTK_TYPE_PLUG); if (plug != NULL) { @@ -2556,6 +2562,7 @@ xfce_panel_plugin_position_widget (XfcePanelPlugin *plugin, *x += px; *y += py; } +#endif /* if the panel is hidden (auto hide is enabled) and we requested a * panel lock, wait for gtk to position the panel before we actually diff --git a/panel/panel-application.c b/panel/panel-application.c index 5f1f94243..f15720d42 100644 --- a/panel/panel-application.c +++ b/panel/panel-application.c @@ -1214,7 +1214,7 @@ panel_application_load (PanelApplication *application, guint i; gchar **atom_names; - if (!disable_wm_check) + if (!disable_wm_check && GDK_IS_X11_DISPLAY (gdk_display_get_default ())) { display = XOpenDisplay (NULL); if (display == NULL) diff --git a/panel/panel-module.c b/panel/panel-module.c index 02a7b2538..692fdbcc7 100644 --- a/panel/panel-module.c +++ b/panel/panel-module.c @@ -337,7 +337,14 @@ panel_module_new_from_desktop_file (const gchar *filename, /* read module location from the desktop file */ module_name = xfce_rc_read_entry_untranslated (rc, "X-XFCE-Module", NULL); - if (G_LIKELY (module_name != NULL)) + if (G_LIKELY (module_name != NULL) && ( + GDK_IS_X11_DISPLAY (gdk_display_get_default ()) || ( + /* Wayland-incompatible embedded plugins */ + g_strstr_len ("pager showdesktop systray tasklist windowmenu", -1, module_name) == NULL + /* Wayland-incompatible non-embedded plugins */ + && g_strstr_len ("clipman", -1, module_name) == NULL + ) + )) { #ifndef NDEBUG if (xfce_rc_has_entry (rc, "X-XFCE-Module-Path")) @@ -369,7 +376,8 @@ panel_module_new_from_desktop_file (const gchar *filename, /* run mode of the module, by default everything runs in * the wrapper, unless defined otherwise */ - if (force_external || !xfce_rc_read_bool_entry (rc, "X-XFCE-Internal", FALSE)) + if (GDK_IS_X11_DISPLAY (gdk_display_get_default ()) + && (force_external || !xfce_rc_read_bool_entry (rc, "X-XFCE-Internal", FALSE))) { module->mode = WRAPPER; g_free (module->api); diff --git a/panel/panel-plugin-external-wrapper.c b/panel/panel-plugin-external-wrapper.c index d2509afc7..ecbfb6da0 100644 --- a/panel/panel-plugin-external-wrapper.c +++ b/panel/panel-plugin-external-wrapper.c @@ -32,7 +32,6 @@ #endif #include -#include #include #include diff --git a/panel/panel-plugin-external.c b/panel/panel-plugin-external.c index b6904576a..de681c48b 100644 --- a/panel/panel-plugin-external.c +++ b/panel/panel-plugin-external.c @@ -31,7 +31,6 @@ #endif #include -#include #include #include @@ -65,8 +64,10 @@ static void panel_plugin_external_size_allocate (GtkWidget GtkAllocation *allocation); static void panel_plugin_external_realize (GtkWidget *widget); static void panel_plugin_external_unrealize (GtkWidget *widget); +#ifdef GDK_WINDOWING_X11 static void panel_plugin_external_plug_added (GtkSocket *socket); static gboolean panel_plugin_external_plug_removed (GtkSocket *socket); +#endif static gboolean panel_plugin_external_child_ask_restart (PanelPluginExternal *external); static void panel_plugin_external_child_spawn (PanelPluginExternal *external); static void panel_plugin_external_child_respawn_schedule (PanelPluginExternal *external); @@ -160,7 +161,9 @@ panel_plugin_external_class_init (PanelPluginExternalClass *klass) { GObjectClass *gobject_class; GtkWidgetClass *gtkwidget_class; +#ifdef GDK_WINDOWING_X11 GtkSocketClass *gtksocket_class; +#endif gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = panel_plugin_external_finalize; @@ -172,9 +175,11 @@ panel_plugin_external_class_init (PanelPluginExternalClass *klass) gtkwidget_class->realize = panel_plugin_external_realize; gtkwidget_class->unrealize = panel_plugin_external_unrealize; +#ifdef GDK_WINDOWING_X11 gtksocket_class = GTK_SOCKET_CLASS (klass); gtksocket_class->plug_added = panel_plugin_external_plug_added; gtksocket_class->plug_removed = panel_plugin_external_plug_removed; +#endif g_object_class_install_property (gobject_class, PROP_UNIQUE_ID, @@ -461,6 +466,7 @@ panel_plugin_external_unrealize (GtkWidget *widget) +#ifdef GDK_WINDOWING_X11 static void panel_plugin_external_plug_added (GtkSocket *socket) { @@ -497,6 +503,7 @@ panel_plugin_external_plug_removed (GtkSocket *socket) return TRUE; } +#endif diff --git a/panel/panel-plugin-external.h b/panel/panel-plugin-external.h index 1e48c97a1..3ccaef832 100644 --- a/panel/panel-plugin-external.h +++ b/panel/panel-plugin-external.h @@ -20,7 +20,18 @@ #define __PANEL_PLUGIN_EXTERNAL_H__ #include +#ifdef GDK_WINDOWING_X11 #include +#else +typedef GtkWidget GtkSocket; +typedef GtkWidgetClass GtkSocketClass; +#define GTK_TYPE_SOCKET GTK_TYPE_WIDGET +#define GTK_SOCKET GTK_WIDGET +#define GTK_IS_SOCKET GTK_IS_WIDGET +#define GTK_SOCKET_CLASS GTK_WIDGET_CLASS +#define gtk_socket_get_id(socket) 0LU +#define gtk_socket_get_plug_window(socket) NULL +#endif #include #include #include diff --git a/panel/panel-preferences-dialog.c b/panel/panel-preferences-dialog.c index 0618ff248..c94094350 100644 --- a/panel/panel-preferences-dialog.c +++ b/panel/panel-preferences-dialog.c @@ -627,7 +627,8 @@ panel_preferences_dialog_bindings_update (PanelPreferencesDialog *dialog) object = gtk_builder_get_object (GTK_BUILDER (dialog), "span-monitors"); panel_return_if_fail (GTK_IS_WIDGET (object)); gtk_widget_set_sensitive (GTK_WIDGET (object), span_monitors_sensitive); - g_object_set (G_OBJECT (object), "visible", n_monitors > 1, NULL); + g_object_set (G_OBJECT (object), "visible", + n_monitors > 1 && GDK_IS_X11_DISPLAY (display), NULL); g_free (output_name); @@ -1702,6 +1703,7 @@ panel_preferences_dialog_show_internal (PanelWindow *active, gtk_window_present (GTK_WINDOW (window)); panel_application_take_dialog (dialog_singleton->application, GTK_WINDOW (window)); } +#ifdef GDK_WINDOWING_X11 else { /* hide window */ @@ -1718,6 +1720,7 @@ panel_preferences_dialog_show_internal (PanelWindow *active, xfce_widget_reparent (GTK_WIDGET (plug_child), plug); gtk_widget_show (GTK_WIDGET (plug_child)); } +#endif } diff --git a/panel/panel-preferences-dialog.h b/panel/panel-preferences-dialog.h index f370381fe..62d4676b3 100644 --- a/panel/panel-preferences-dialog.h +++ b/panel/panel-preferences-dialog.h @@ -20,7 +20,12 @@ #define __PANEL_PREFERENCES_DIALOG_H__ #include +#ifdef GDK_WINDOWING_X11 #include +#else +typedef gulong Window; +#define GTK_IS_PLUG GTK_IS_WIDGET +#endif #include #include diff --git a/panel/panel-window.c b/panel/panel-window.c index 9860f1e83..c8cb480e9 100644 --- a/panel/panel-window.c +++ b/panel/panel-window.c @@ -28,7 +28,6 @@ #endif #ifdef GDK_WINDOWING_X11 -#include #include #include #endif @@ -861,6 +860,9 @@ panel_window_set_property (GObject *object, break; case PROP_SPAN_MONITORS: + if (! GDK_IS_X11_DISPLAY (window->display)) + break; + val_bool = g_value_get_boolean (value); if (window->span_monitors != val_bool) { @@ -1681,8 +1683,8 @@ panel_window_screen_changed (GtkWidget *widget, GdkScreen *previous_screen) { PanelWindow *window = PANEL_WINDOW (widget); - WnckWindow *wnck_window; - WnckScreen *wnck_screen; + WnckWindow *wnck_window = NULL; + WnckScreen *wnck_screen = NULL; GdkScreen *screen; if (G_LIKELY (GTK_WIDGET_CLASS (panel_window_parent_class)->screen_changed != NULL)) @@ -1711,8 +1713,12 @@ panel_window_screen_changed (GtkWidget *widget, panel_window_screen_layout_changed (screen, window); /* update wnck screen to be used for the autohide feature */ - wnck_screen = panel_wnck_screen_get_default (); - wnck_window = wnck_screen_get_active_window (wnck_screen); + if (GDK_IS_X11_DISPLAY (window->display)) + { + wnck_screen = panel_wnck_screen_get_default (); + wnck_window = wnck_screen_get_active_window (wnck_screen); + } + panel_window_update_autohide_window (window, wnck_screen, wnck_window); } @@ -1762,6 +1768,7 @@ panel_window_filter (GdkXEvent *xev, GdkEvent *gev, gpointer data) { +#ifdef GDK_WINDOWING_X11 PanelWindow *window = data; GdkEventButton *event = (GdkEventButton *) gev; XEvent *xevent = (XEvent *) xev; @@ -1810,6 +1817,9 @@ panel_window_filter (GdkXEvent *xev, /* force the panel to process the event instead of its child widgets */ return panel_window_button_press_event (GTK_WIDGET (window), event) ? GDK_FILTER_REMOVE : GDK_FILTER_CONTINUE; +#else + return GDK_FILTER_CONTINUE; +#endif } @@ -1962,6 +1972,7 @@ panel_window_screen_struts_set (PanelWindow *window) if (!update_struts) return; +#ifdef GDK_WINDOWING_X11 /* don't crash on x errors */ gdk_x11_display_error_trap_push (window->display); @@ -1983,6 +1994,7 @@ panel_window_screen_struts_set (PanelWindow *window) /* release the trap */ if (gdk_x11_display_error_trap_pop (window->display) != 0) g_critical ("Failed to set the struts"); +#endif if (panel_debug_has_domain (PANEL_DEBUG_YES)) { @@ -2496,9 +2508,7 @@ panel_window_active_window_geometry_changed (WnckWindow *active_window, { GdkRectangle panel_area; GdkRectangle window_area; - gboolean geometry_fixed = FALSE; - panel_return_if_fail (WNCK_IS_WINDOW (active_window)); panel_return_if_fail (PANEL_IS_WINDOW (window)); /* ignore if for some reason the active window does not match the one we know */ @@ -2510,10 +2520,15 @@ panel_window_active_window_geometry_changed (WnckWindow *active_window, if (window->autohide_behavior == AUTOHIDE_BEHAVIOR_INTELLIGENTLY && window->autohide_block == 0) { + if (! GDK_IS_X11_DISPLAY (window->display) || active_window == NULL) + return; + if (wnck_window_get_window_type (active_window) != WNCK_WINDOW_DESKTOP) { +#ifdef GDK_WINDOWING_X11 GdkWindow *gdkwindow; GtkBorder extents; + gboolean geometry_fixed = FALSE; /* obtain position and dimensions from the active window */ wnck_window_get_geometry (active_window, @@ -2563,6 +2578,7 @@ panel_window_active_window_geometry_changed (WnckWindow *active_window, XFree (data); } } +#endif /* apply scale factor */ window_area.x /= window->scale_factor; @@ -3550,8 +3566,8 @@ panel_window_get_locked (PanelWindow *window) -void -panel_window_focus (PanelWindow *window) +static void +panel_window_focus_x11 (PanelWindow *window) { #ifdef GDK_WINDOWING_X11 XClientMessageEvent event; @@ -3577,14 +3593,25 @@ panel_window_focus (PanelWindow *window) if (gdk_x11_display_error_trap_pop (window->display) != 0) g_critical ("Failed to focus panel window"); -#else - /* our best guess on non-x11 clients */ - gtk_window_present (GTK_WINDOW (window)); #endif } +void +panel_window_focus (PanelWindow *window) +{ + if (GDK_IS_X11_DISPLAY (window->display)) + panel_window_focus_x11 (window); + else + { + /* our best guess on non-x11 clients */ + gtk_window_present (GTK_WINDOW (window)); + } +} + + + void panel_window_migrate_autohide_property (PanelWindow *window, XfconfChannel *xfconf, diff --git a/plugins/launcher/launcher-dialog.c b/plugins/launcher/launcher-dialog.c index f792e99c2..aa8b5600a 100644 --- a/plugins/launcher/launcher-dialog.c +++ b/plugins/launcher/launcher-dialog.c @@ -39,7 +39,6 @@ #include "launcher-dialog_ui.h" #ifdef GDK_WINDOWING_X11 -#include #define LAUNCHER_WIDGET_XID(widget) ((guint) GDK_WINDOW_XID (gdk_screen_get_root_window (gtk_widget_get_screen (GTK_WIDGET (widget))))) #else #define LAUNCHER_WIDGET_XID(widget) (0) diff --git a/wrapper/wrapper-plug.h b/wrapper/wrapper-plug.h index 42975b477..e8e448d0e 100644 --- a/wrapper/wrapper-plug.h +++ b/wrapper/wrapper-plug.h @@ -20,7 +20,17 @@ #define __WRAPPER_PLUG_H__ #include +#ifdef GDK_WINDOWING_X11 #include +#else +typedef GtkWidget GtkPlug; +typedef GtkWidgetClass GtkPlugClass; +typedef gulong Window; +#define GTK_TYPE_PLUG GTK_TYPE_WIDGET +#define GTK_PLUG GTK_WIDGET +#define gtk_plug_construct(plug, socket_id) +#endif + #include #include -- GitLab From 844e295ced02ef541d3563242557a7d588f820b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Bonithon?= Date: Fri, 19 Aug 2022 09:45:49 +0200 Subject: [PATCH 07/22] wayland: Add GtkLayerShell dependency Calling `gtk_layer_init_for_window()` already makes the panel look like a panel: no decorations, panel over windows. --- configure.ac.in | 2 ++ panel/Makefile.am | 2 ++ panel/panel-window.c | 6 ++++++ 3 files changed, 10 insertions(+) diff --git a/configure.ac.in b/configure.ac.in index 5763d988e..5affd5b2a 100644 --- a/configure.ac.in +++ b/configure.ac.in @@ -155,6 +155,8 @@ XDT_CHECK_PACKAGE([GMODULE], [gmodule-2.0], [2.66.0]) XDT_CHECK_PACKAGE([CAIRO], [cairo], [1.0.0]) XDT_CHECK_PACKAGE([LIBWNCK], [libwnck-3.0], [3.0]) +XDT_CHECK_PACKAGE([GTK_LAYER_SHELL], [gtk-layer-shell-0], [0.7]) + dnl ********************************************* dnl *** Optional DBUSMENU for StatusNotifiers *** dnl ********************************************* diff --git a/panel/Makefile.am b/panel/Makefile.am index b07fd9620..29523a6d1 100644 --- a/panel/Makefile.am +++ b/panel/Makefile.am @@ -63,6 +63,7 @@ xfce4_panel_CFLAGS = \ $(LIBWNCK_CFLAGS) \ $(XFCONF_CFLAGS) \ $(LIBX11_CFLAGS) \ + $(GTK_LAYER_SHELL_CFLAGS) \ $(PLATFORM_CFLAGS) xfce4_panel_LDFLAGS = \ @@ -80,6 +81,7 @@ xfce4_panel_LDADD = \ $(LIBWNCK_LIBS) \ $(XFCONF_LIBS) \ $(LIBX11_LIBS) \ + $(GTK_LAYER_SHELL_LIBS) \ -lm xfce4_panel_DEPENDENCIES = \ diff --git a/panel/panel-window.c b/panel/panel-window.c index c8cb480e9..73fb62c21 100644 --- a/panel/panel-window.c +++ b/panel/panel-window.c @@ -36,6 +36,8 @@ #include +#include + #include #include #include @@ -608,6 +610,10 @@ panel_window_init (PanelWindow *window) /* create a 'fake' drop zone for autohide drag motion */ gtk_drag_dest_set (GTK_WIDGET (window), 0, NULL, 0, 0); + /* initialize layer-shell if supported (includes Wayland display check) */ + if (gtk_layer_is_supported ()) + gtk_layer_init_for_window (GTK_WINDOW (window)); + /* set the screen */ panel_window_screen_changed (GTK_WIDGET (window), NULL); } -- GitLab From 3d6e01bc6246fa17601fd5d80fb147f3d2c0306d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Bonithon?= Date: Fri, 19 Aug 2022 09:56:55 +0200 Subject: [PATCH 08/22] wayland: panel: Ability to move the panel Since there is no notion of global coordinates on Wayland, `gtk_window_move()` does not work. Also, once initialized layer-shell, the panel becomes static and it is no longer possible to use `gtk_window_begin_move_drag()`, whose unconstrained movement type is not suitable for the panel anyway (just a fallback if layer-shell is not supported). So we play with layer-shell anchors and margins to reproduce the panel movement as it occurs on X11. At first it is enough to use the top and left anchors, because we don't care about exclusive zones (X11 struts) yet. There is no need to manually grab the pointer anymore, as it is implied on Wayland when a surface is clicked. Known regressions: * The grab is lost when changing monitor in automatic mode. Keeping the manual grab as on X11 is not a solution, because it prevents the automatic Wayland grab from working properly. --- panel/panel-window.c | 59 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 50 insertions(+), 9 deletions(-) diff --git a/panel/panel-window.c b/panel/panel-window.c index 73fb62c21..855970a95 100644 --- a/panel/panel-window.c +++ b/panel/panel-window.c @@ -612,7 +612,11 @@ panel_window_init (PanelWindow *window) /* initialize layer-shell if supported (includes Wayland display check) */ if (gtk_layer_is_supported ()) - gtk_layer_init_for_window (GTK_WINDOW (window)); + { + gtk_layer_init_for_window (GTK_WINDOW (window)); + gtk_layer_set_anchor (GTK_WINDOW (window), GTK_LAYER_SHELL_EDGE_TOP, TRUE); + gtk_layer_set_anchor (GTK_WINDOW (window), GTK_LAYER_SHELL_EDGE_LEFT, TRUE); + } /* set the screen */ panel_window_screen_changed (GTK_WIDGET (window), NULL); @@ -1152,6 +1156,13 @@ panel_window_motion_notify_event (GtkWidget *widget, /* get the pointer position from the event */ pointer_x = event->x_root; pointer_y = event->y_root; + if (gtk_layer_is_supported ()) + { + pointer_x += window->area.x + gtk_layer_get_margin (GTK_WINDOW (window), + GTK_LAYER_SHELL_EDGE_LEFT); + pointer_y += window->area.y + gtk_layer_get_margin (GTK_WINDOW (window), + GTK_LAYER_SHELL_EDGE_TOP); + } /* the 0x0 coordinate is a sign the cursor is on another screen then * the panel that is currently dragged */ @@ -1167,6 +1178,8 @@ panel_window_motion_notify_event (GtkWidget *widget, window->grab_time = 0; panel_window_thaw_autohide (window); retval = FALSE; + if (gtk_layer_is_supported ()) + gdk_window_set_cursor (event->window, NULL); } } /* check if the pointer moved to another monitor */ @@ -1192,8 +1205,8 @@ panel_window_motion_notify_event (GtkWidget *widget, window_y = CLAMP (window_y, window->area.y, high); /* update the grab coordinates */ - window->grab_x = pointer_x - window_x; - window->grab_y = pointer_y - window_y; + window->grab_x = CLAMP (pointer_x - window_x, 0, window->alloc.width); + window->grab_y = CLAMP (pointer_y - window_y, 0, window->alloc.height); /* update the base coordinates */ window->base_x = window_x + window->alloc.width / 2; @@ -1241,11 +1254,24 @@ panel_window_button_press_event (GtkWidget *widget, cursor = gdk_cursor_new_for_display (window->display, GDK_FLEUR); /* grab the pointer for dragging the window */ - seat = gdk_device_get_seat (event->device); - - status = gdk_seat_grab (seat, event->window, - GDK_SEAT_CAPABILITY_ALL_POINTING, - FALSE, cursor, (GdkEvent*)event, NULL, NULL); + if (GDK_IS_X11_DISPLAY (window->display)) + { + seat = gdk_device_get_seat (event->device); + status = gdk_seat_grab (seat, event->window, + GDK_SEAT_CAPABILITY_ALL_POINTING, + FALSE, cursor, (GdkEvent*)event, NULL, NULL); + } + else if (gtk_layer_is_supported ()) + { + gdk_window_set_cursor (event->window, cursor); + status = GDK_GRAB_SUCCESS; + } + else + { + gtk_window_begin_move_drag (GTK_WINDOW (window), event->button, + event->x_root, event->y_root, event->time); + status = GDK_GRAB_FAILED; + } if (cursor != NULL) g_object_unref (cursor); @@ -1291,6 +1317,8 @@ panel_window_button_release_event (GtkWidget *widget, /* ungrab the pointer */ gdk_seat_ungrab (gdk_device_get_seat (event->device)); window->grab_time = 0; + if (gtk_layer_is_supported ()) + gdk_window_set_cursor (event->window, NULL); /* store the new position */ g_object_notify (G_OBJECT (widget), "position"); @@ -1552,7 +1580,15 @@ panel_window_size_allocate (GtkWidget *widget, if (window->autohide_window != NULL) gtk_window_move (GTK_WINDOW (window->autohide_window), -9999, -9999); - gtk_window_move (GTK_WINDOW (window), window->alloc.x, window->alloc.y); + if (gtk_layer_is_supported ()) + { + gtk_layer_set_margin (GTK_WINDOW (window), GTK_LAYER_SHELL_EDGE_TOP, + window->alloc.y - window->area.y); + gtk_layer_set_margin (GTK_WINDOW (window), GTK_LAYER_SHELL_EDGE_LEFT, + window->alloc.x - window->area.x); + } + else + gtk_window_move (GTK_WINDOW (window), window->alloc.x, window->alloc.y); } child = gtk_bin_get_child (GTK_BIN (widget)); @@ -2444,6 +2480,11 @@ panel_window_screen_layout_changed (GdkScreen *screen, if (window->struts_edge == STRUTS_EDGE_NONE) panel_debug (PANEL_DEBUG_POSITIONING, "%p: unset struts edge; between monitors", window); + + /* the compositor does not manage to display the panel on the right monitor + * by himself in general */ + if (gtk_layer_is_supported ()) + gtk_layer_set_monitor (GTK_WINDOW (window), monitor); } /* set the new working area of the panel */ -- GitLab From 59bc699de40be957e152b96fcd039892765299f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Bonithon?= Date: Fri, 19 Aug 2022 11:06:59 +0200 Subject: [PATCH 09/22] wayland: panel: Set exclusive zones (X11 struts) For exclusive zones to work properly, they must be set only on one edge, or on one edge and its two perpendicular edges. Since we need at least two edges to manage the movement of the panel, we are left with the second option. Also, all the margins must now be set so that the panel is the right size and its movement proceeds correctly. --- panel/panel-window.c | 196 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 190 insertions(+), 6 deletions(-) diff --git a/panel/panel-window.c b/panel/panel-window.c index 855970a95..b7135469d 100644 --- a/panel/panel-window.c +++ b/panel/panel-window.c @@ -143,6 +143,7 @@ static StrutsEgde panel_window_screen_struts_edge (PanelWind static void panel_window_screen_struts_set (PanelWindow *window); static void panel_window_screen_update_borders (PanelWindow *window); static SnapPosition panel_window_snap_position (PanelWindow *window); +static void panel_window_layer_set_anchor (PanelWindow *window); static void panel_window_display_layout_debug (GtkWidget *widget); static void panel_window_screen_layout_changed (GdkScreen *screen, PanelWindow *window); @@ -612,11 +613,7 @@ panel_window_init (PanelWindow *window) /* initialize layer-shell if supported (includes Wayland display check) */ if (gtk_layer_is_supported ()) - { - gtk_layer_init_for_window (GTK_WINDOW (window)); - gtk_layer_set_anchor (GTK_WINDOW (window), GTK_LAYER_SHELL_EDGE_TOP, TRUE); - gtk_layer_set_anchor (GTK_WINDOW (window), GTK_LAYER_SHELL_EDGE_LEFT, TRUE); - } + gtk_layer_init_for_window (GTK_WINDOW (window)); /* set the screen */ panel_window_screen_changed (GTK_WIDGET (window), NULL); @@ -902,6 +899,9 @@ panel_window_set_property (GObject *object, window->base_x = MAX (x, 0); window->base_y = MAX (y, 0); + if (gtk_layer_is_supported ()) + panel_window_layer_set_anchor (window); + panel_window_screen_layout_changed (window->screen, window); /* send the new screen position to the panel plugins */ @@ -1139,6 +1139,7 @@ panel_window_motion_notify_event (GtkWidget *widget, { PanelWindow *window = PANEL_WINDOW (widget); PanelBaseWindow *base_window = PANEL_BASE_WINDOW (widget); + SnapPosition snap_position; gint pointer_x, pointer_y; gint window_x, window_y; gint high; @@ -1217,7 +1218,13 @@ panel_window_motion_notify_event (GtkWidget *widget, window->alloc.y = window_y; /* update the snapping position */ - window->snap_position = panel_window_snap_position (window); + snap_position = panel_window_snap_position (window); + if (snap_position != window->snap_position) + { + window->snap_position = snap_position; + if (gtk_layer_is_supported ()) + panel_window_layer_set_anchor (window); + } /* update the working area */ panel_window_screen_layout_changed (window->screen, window); @@ -1446,6 +1453,21 @@ panel_window_get_preferred_height (GtkWidget *widget, if (natural_height != NULL) *natural_height = n_height; + + /* + * Disable left/right or top/bottom anchor pairs during allocation, so that the panel + * is not stretched between the two anchors, preventing it from shrinking. Quite an + * ugly hack but it works until it gets better. + */ + if (gtk_layer_is_supported () && window->snap_position != SNAP_POSITION_NONE) + { + GtkWindow *gtkwindow = GTK_WINDOW (widget); + + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_TOP, TRUE); + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_BOTTOM, FALSE); + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_LEFT, TRUE); + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_RIGHT, FALSE); + } } @@ -1485,6 +1507,10 @@ panel_window_size_allocate (GtkWidget *widget, gtk_widget_set_allocation (widget, alloc); window->alloc = *alloc; + /* re-enable anchor pairs, see above in get_preferred_height() */ + if (gtk_layer_is_supported () && window->snap_position != SNAP_POSITION_NONE) + panel_window_layer_set_anchor (window); + if (G_UNLIKELY (window->autohide_state == AUTOHIDE_HIDDEN || window->autohide_state == AUTOHIDE_POPUP)) { @@ -1584,8 +1610,14 @@ panel_window_size_allocate (GtkWidget *widget, { gtk_layer_set_margin (GTK_WINDOW (window), GTK_LAYER_SHELL_EDGE_TOP, window->alloc.y - window->area.y); + gtk_layer_set_margin (GTK_WINDOW (window), GTK_LAYER_SHELL_EDGE_BOTTOM, + window->area.y + window->area.height + - window->alloc.y - window->alloc.height); gtk_layer_set_margin (GTK_WINDOW (window), GTK_LAYER_SHELL_EDGE_LEFT, window->alloc.x - window->area.x); + gtk_layer_set_margin (GTK_WINDOW (window), GTK_LAYER_SHELL_EDGE_RIGHT, + window->area.x + window->area.width + - window->alloc.x - window->alloc.width); } else gtk_window_move (GTK_WINDOW (window), window->alloc.x, window->alloc.y); @@ -1964,6 +1996,28 @@ panel_window_screen_struts_set (PanelWindow *window) if (!gtk_widget_get_realized (GTK_WIDGET (window))) return; + if (gtk_layer_is_supported ()) + { + switch (window->struts_edge) + { + case STRUTS_EDGE_TOP: + case STRUTS_EDGE_BOTTOM: + gtk_layer_set_exclusive_zone (GTK_WINDOW (window), window->alloc.height); + break; + + case STRUTS_EDGE_LEFT: + case STRUTS_EDGE_RIGHT: + gtk_layer_set_exclusive_zone (GTK_WINDOW (window), window->alloc.width); + break; + + default: + gtk_layer_set_exclusive_zone (GTK_WINDOW (window), 0); + break; + } + + return; + } + /* set the struts */ /* Note that struts are relative to the screen edge! (NOT the monitor) This means we have no choice but to use deprecated GtkScreen calls. @@ -2212,6 +2266,136 @@ panel_window_snap_position (PanelWindow *window) +static void +panel_window_layer_set_anchor (PanelWindow *window) +{ + GtkWindow *gtkwindow = GTK_WINDOW (window); + + panel_return_if_fail (PANEL_IS_WINDOW (window)); + + /* only two anchors are needed to set the panel position, but three to set + * the exclusive zone */ + switch (window->snap_position) + { + case SNAP_POSITION_NONE: + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_TOP, TRUE); + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_BOTTOM, FALSE); + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_LEFT, TRUE); + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_RIGHT, FALSE); + break; + + case SNAP_POSITION_N: + case SNAP_POSITION_NC: + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_TOP, TRUE); + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_BOTTOM, FALSE); + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_LEFT, TRUE); + if (IS_HORIZONTAL (window)) + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_RIGHT, TRUE); + else + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_RIGHT, FALSE); + break; + + case SNAP_POSITION_NW: + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_TOP, TRUE); + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_LEFT, TRUE); + if (IS_HORIZONTAL (window)) + { + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_BOTTOM, FALSE); + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_RIGHT, TRUE); + } + else + { + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_BOTTOM, TRUE); + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_RIGHT, FALSE); + } + break; + + case SNAP_POSITION_NE: + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_TOP, TRUE); + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_RIGHT, TRUE); + if (IS_HORIZONTAL (window)) + { + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_BOTTOM, FALSE); + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_LEFT, TRUE); + } + else + { + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_BOTTOM, TRUE); + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_LEFT, FALSE); + } + break; + + case SNAP_POSITION_S: + case SNAP_POSITION_SC: + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_TOP, FALSE); + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_BOTTOM, TRUE); + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_LEFT, TRUE); + if (IS_HORIZONTAL (window)) + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_RIGHT, TRUE); + else + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_RIGHT, FALSE); + break; + + case SNAP_POSITION_SW: + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_BOTTOM, TRUE); + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_LEFT, TRUE); + if (IS_HORIZONTAL (window)) + { + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_TOP, FALSE); + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_RIGHT, TRUE); + } + else + { + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_TOP, TRUE); + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_RIGHT, FALSE); + } + break; + + case SNAP_POSITION_SE: + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_BOTTOM, TRUE); + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_RIGHT, TRUE); + if (IS_HORIZONTAL (window)) + { + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_TOP, FALSE); + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_LEFT, TRUE); + } + else + { + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_TOP, TRUE); + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_LEFT, FALSE); + } + break; + + case SNAP_POSITION_W: + case SNAP_POSITION_WC: + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_TOP, TRUE); + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_LEFT, TRUE); + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_RIGHT, FALSE); + if (IS_HORIZONTAL (window)) + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_BOTTOM, FALSE); + else + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_BOTTOM, TRUE); + break; + + case SNAP_POSITION_E: + case SNAP_POSITION_EC: + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_TOP, TRUE); + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_LEFT, FALSE); + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_RIGHT, TRUE); + if (IS_HORIZONTAL (window)) + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_BOTTOM, FALSE); + else + gtk_layer_set_anchor (gtkwindow, GTK_LAYER_SHELL_EDGE_BOTTOM, TRUE); + break; + + default: + panel_assert_not_reached (); + break; + } +} + + + static void panel_window_display_layout_debug (GtkWidget *widget) { -- GitLab From 819b1e57bf97459f70adbf260975730c1d741248 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Bonithon?= Date: Wed, 24 Aug 2022 11:13:44 +0200 Subject: [PATCH 10/22] wayland: panel: Autohide adaptations (layer-shell part) Just a generalization of what was done earlier for panel movement, since autohide relies on off-screen movement. Intellihide is left out, as it requires foreign-toplevel-management protocol features. Currently only works with Wayfire, as Labwc fails to correctly set layer-shell margins if they were previously set to a negative value larger in absolute value than the corresponding window dimension. See https://github.com/labwc/labwc/issues/499 --- panel/panel-window.c | 108 ++++++++++++++++++++++++++++++++----------- 1 file changed, 81 insertions(+), 27 deletions(-) diff --git a/panel/panel-window.c b/panel/panel-window.c index b7135469d..67b80bd0d 100644 --- a/panel/panel-window.c +++ b/panel/panel-window.c @@ -134,6 +134,10 @@ static void panel_window_size_allocate_set_xy (PanelWind gint window_height, gint *return_x, gint *return_y); +static void panel_window_move (PanelWindow *window, + GtkWindow *moved, + gint x, + gint y); static void panel_window_screen_changed (GtkWidget *widget, GdkScreen *previous_screen); static void panel_window_style_updated (GtkWidget *widget); @@ -595,7 +599,7 @@ panel_window_init (PanelWindow *window) window->popup_delay = DEFAULT_POPUP_DELAY; window->popdown_delay = DEFAULT_POPDOWN_DELAY; window->popdown_speed = DEFAULT_POPDOWN_SPEED; - window->popdown_progress = 0; + window->popdown_progress = - G_MAXINT; window->base_x = -1; window->base_y = -1; window->grab_time = 0; @@ -1515,7 +1519,7 @@ panel_window_size_allocate (GtkWidget *widget, || window->autohide_state == AUTOHIDE_POPUP)) { /* autohide timeout is already running, so let's wait with hiding the panel */ - if (window->autohide_timeout_id != 0) + if (window->autohide_timeout_id != 0 || window->popdown_progress != - G_MAXINT) return; /* window is invisible */ @@ -1552,9 +1556,13 @@ panel_window_size_allocate (GtkWidget *widget, } /* position the autohide window */ + if (gtk_layer_is_supported ()) + gtk_widget_set_size_request (window->autohide_window, w, h); + else + gtk_window_resize (GTK_WINDOW (window->autohide_window), w, h); + panel_window_size_allocate_set_xy (window, w, h, &x, &y); - gtk_window_resize (GTK_WINDOW (window->autohide_window), w, h); - gtk_window_move (GTK_WINDOW (window->autohide_window), x, y); + panel_window_move (window, GTK_WINDOW (window->autohide_window), x, y); /* slide out the panel window with popdown_speed, but ignore panels that are floating, i.e. not attached to a GdkScreen border (i.e. including panels which are on a monitor border, but @@ -1584,7 +1592,7 @@ panel_window_size_allocate (GtkWidget *widget, if (window->autohide_ease_out_id != 0) g_source_remove (window->autohide_ease_out_id); - gtk_window_move (GTK_WINDOW (window), window->alloc.x, window->alloc.y); + panel_window_move (window, GTK_WINDOW (window), window->alloc.x, window->alloc.y); } } else @@ -1604,23 +1612,9 @@ panel_window_size_allocate (GtkWidget *widget, /* move the autohide window offscreen */ if (window->autohide_window != NULL) - gtk_window_move (GTK_WINDOW (window->autohide_window), -9999, -9999); + panel_window_move (window, GTK_WINDOW (window->autohide_window), -9999, -9999); - if (gtk_layer_is_supported ()) - { - gtk_layer_set_margin (GTK_WINDOW (window), GTK_LAYER_SHELL_EDGE_TOP, - window->alloc.y - window->area.y); - gtk_layer_set_margin (GTK_WINDOW (window), GTK_LAYER_SHELL_EDGE_BOTTOM, - window->area.y + window->area.height - - window->alloc.y - window->alloc.height); - gtk_layer_set_margin (GTK_WINDOW (window), GTK_LAYER_SHELL_EDGE_LEFT, - window->alloc.x - window->area.x); - gtk_layer_set_margin (GTK_WINDOW (window), GTK_LAYER_SHELL_EDGE_RIGHT, - window->area.x + window->area.width - - window->alloc.x - window->alloc.width); - } - else - gtk_window_move (GTK_WINDOW (window), window->alloc.x, window->alloc.y); + panel_window_move (window, GTK_WINDOW (window), window->alloc.x, window->alloc.y); } child = gtk_bin_get_child (GTK_BIN (widget)); @@ -1752,6 +1746,30 @@ panel_window_size_allocate_set_xy (PanelWindow *window, +static void +panel_window_move (PanelWindow *window, + GtkWindow *moved, + gint x, + gint y) +{ + if (gtk_layer_is_supported ()) + { + gtk_layer_set_margin (moved, GTK_LAYER_SHELL_EDGE_TOP, y - window->area.y); + gtk_layer_set_margin (moved, GTK_LAYER_SHELL_EDGE_LEFT, x - window->area.x); + if (moved == GTK_WINDOW (window)) + { + gtk_layer_set_margin (moved, GTK_LAYER_SHELL_EDGE_BOTTOM, + window->area.y + window->area.height - y - window->alloc.height); + gtk_layer_set_margin (moved, GTK_LAYER_SHELL_EDGE_RIGHT, + window->area.x + window->area.width - x - window->alloc.width); + } + } + else + gtk_window_move (moved, x, y); +} + + + static void panel_window_screen_changed (GtkWidget *widget, GdkScreen *previous_screen) @@ -2668,7 +2686,11 @@ panel_window_screen_layout_changed (GdkScreen *screen, /* the compositor does not manage to display the panel on the right monitor * by himself in general */ if (gtk_layer_is_supported ()) - gtk_layer_set_monitor (GTK_WINDOW (window), monitor); + { + gtk_layer_set_monitor (GTK_WINDOW (window), monitor); + if (window->autohide_behavior != AUTOHIDE_BEHAVIOR_NEVER) + gtk_layer_set_monitor (GTK_WINDOW (window->autohide_window), monitor); + } } /* set the new working area of the panel */ @@ -2928,7 +2950,16 @@ panel_window_autohide_ease_out (gpointer data) if (window->autohide_ease_out_id == 0) return FALSE; - gtk_window_get_position (GTK_WINDOW (window), &x, &y); + if (gtk_layer_is_supported ()) + { + x = window->area.x + gtk_layer_get_margin (GTK_WINDOW (window), + GTK_LAYER_SHELL_EDGE_LEFT); + y = window->area.y + gtk_layer_get_margin (GTK_WINDOW (window), + GTK_LAYER_SHELL_EDGE_TOP); + } + else + gtk_window_get_position (GTK_WINDOW (window), &x, &y); + w = panel_screen_get_width (window->screen); h = panel_screen_get_height (window->screen); @@ -2974,7 +3005,7 @@ panel_window_autohide_ease_out (gpointer data) } window->popdown_progress--; - gtk_window_move (GTK_WINDOW (window), x, y); + panel_window_move (window, GTK_WINDOW (window), x, y); return ret; } @@ -2984,7 +3015,10 @@ panel_window_autohide_ease_out (gpointer data) static void panel_window_autohide_ease_out_timeout_destroy (gpointer user_data) { - PANEL_WINDOW (user_data)->autohide_ease_out_id = 0; + PanelWindow *window = user_data; + + window->autohide_ease_out_id = 0; + window->popdown_progress = - G_MAXINT; } @@ -3167,7 +3201,28 @@ panel_window_set_autohide_behavior (PanelWindow *window, NULL); /* move the window offscreen */ - gtk_window_move (GTK_WINDOW (popup), -9999, -9999); + if (gtk_layer_is_supported ()) + { + gtk_layer_init_for_window (GTK_WINDOW (popup)); + gtk_layer_set_anchor (GTK_WINDOW (popup), GTK_LAYER_SHELL_EDGE_TOP, TRUE); + gtk_layer_set_anchor (GTK_WINDOW (popup), GTK_LAYER_SHELL_EDGE_LEFT, TRUE); + if (gtk_widget_get_realized (GTK_WIDGET (window))) + { + GdkWindow *gdkwindow; + GdkMonitor *monitor; + + gdkwindow = gtk_widget_get_window (GTK_WIDGET (window)); + monitor = gdk_display_get_monitor_at_window (window->display, gdkwindow); + gtk_layer_set_monitor (GTK_WINDOW (popup), monitor); + } + + /* must be done once here before the window is mapped so that subsequent + * resizing is taken into account */ + gtk_widget_set_size_request (popup, window->autohide_size, window->autohide_size); + } + + window->autohide_window = popup; + panel_window_move (window, GTK_WINDOW (window->autohide_window), -9999, -9999); /* bind some properties to sync the two windows */ for (i = 0; i < G_N_ELEMENTS (properties); i++) @@ -3195,7 +3250,6 @@ panel_window_set_autohide_behavior (PanelWindow *window, G_CALLBACK (panel_window_autohide_drag_leave), window); /* show the window */ - window->autohide_window = popup; gtk_widget_show (popup); } -- GitLab From 1763f9021cf24ede9f13281f34cc52fa2078c5a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Bonithon?= Date: Fri, 26 Aug 2022 10:20:25 +0200 Subject: [PATCH 11/22] wayland: Add wlr-foreign-toplevel-management-unstable-v1 protocol --- .gitignore | 2 + Makefile.am | 1 + configure.ac.in | 4 + protocols/Makefile.am | 40 +++ ...oreign-toplevel-management-unstable-v1.xml | 270 ++++++++++++++++++ 5 files changed, 317 insertions(+) create mode 100644 protocols/Makefile.am create mode 100644 protocols/wlr-foreign-toplevel-management-unstable-v1.xml diff --git a/.gitignore b/.gitignore index d16dd8a0d..22a668d6a 100644 --- a/.gitignore +++ b/.gitignore @@ -47,6 +47,8 @@ po/stamp-it po/Makefile.in.in po/.intltool-merge-cache* po/*.gmo +protocols/*.c +protocols/*.h docs/reference/html docs/reference/xml docs/reference/*.stamp diff --git a/Makefile.am b/Makefile.am index 921e9e79c..3c676208e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,7 @@ ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} SUBDIRS = \ + protocols \ libxfce4panel \ common \ panel \ diff --git a/configure.ac.in b/configure.ac.in index 5affd5b2a..f47a3f0c0 100644 --- a/configure.ac.in +++ b/configure.ac.in @@ -155,6 +155,9 @@ XDT_CHECK_PACKAGE([GMODULE], [gmodule-2.0], [2.66.0]) XDT_CHECK_PACKAGE([CAIRO], [cairo], [1.0.0]) XDT_CHECK_PACKAGE([LIBWNCK], [libwnck-3.0], [3.0]) +dnl wayland-scanner >= 1.15 is for private-code option to be present +XDT_CHECK_PACKAGE([WAYLAND_SCANNER], [wayland-scanner], [1.15]) +XDT_CHECK_PACKAGE([WAYLAND_CLIENT], [wayland-client], [1.15]) XDT_CHECK_PACKAGE([GTK_LAYER_SHELL], [gtk-layer-shell-0], [0.7]) dnl ********************************************* @@ -290,6 +293,7 @@ plugins/tasklist/tasklist.desktop.in plugins/windowmenu/Makefile plugins/windowmenu/windowmenu.desktop.in po/Makefile.in +protocols/Makefile ]) AC_OUTPUT diff --git a/protocols/Makefile.am b/protocols/Makefile.am new file mode 100644 index 000000000..2c19c4a5c --- /dev/null +++ b/protocols/Makefile.am @@ -0,0 +1,40 @@ +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + -DG_LOG_DOMAIN=\"libpanel-protocols\" \ + $(PLATFORM_CPPFLAGS) + +noinst_LTLIBRARIES = \ + libpanel-protocols.la + +libpanel_protocols_built_sources = \ + wlr-foreign-toplevel-management-unstable-v1.c \ + wlr-foreign-toplevel-management-unstable-v1-client.h + +libpanel_protocols_la_SOURCES = \ + wlr-foreign-toplevel-management-unstable-v1.c + +libpanel_protocols_la_CFLAGS = \ + $(WAYLAND_CLIENT_CFLAGS) \ + $(PLATFORM_CFLAGS) + +libpanel_protocols_la_LDFLAGS = \ + -no-undefined \ + $(PLATFORM_LDFLAGS) + +libpanel_protocols_la_LIBADD = \ + $(WAYLAND_CLIENT_LIBS) + +%.c: %.xml + $(AM_V_GEN) wayland-scanner private-code $< $@ + +%-client.h: %.xml + $(AM_V_GEN) wayland-scanner client-header $< $@ + +DISTCLEANFILES = \ + $(libpanel_protocols_built_sources) + +BUILT_SOURCES = \ + $(libpanel_protocols_built_sources) + +EXTRA_DIST = \ + wlr-foreign-toplevel-management-unstable-v1.xml diff --git a/protocols/wlr-foreign-toplevel-management-unstable-v1.xml b/protocols/wlr-foreign-toplevel-management-unstable-v1.xml new file mode 100644 index 000000000..44505bbb6 --- /dev/null +++ b/protocols/wlr-foreign-toplevel-management-unstable-v1.xml @@ -0,0 +1,270 @@ + + + + Copyright © 2018 Ilia Bozhinov + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + + The purpose of this protocol is to enable the creation of taskbars + and docks by providing them with a list of opened applications and + letting them request certain actions on them, like maximizing, etc. + + After a client binds the zwlr_foreign_toplevel_manager_v1, each opened + toplevel window will be sent via the toplevel event + + + + + This event is emitted whenever a new toplevel window is created. It + is emitted for all toplevels, regardless of the app that has created + them. + + All initial details of the toplevel(title, app_id, states, etc.) will + be sent immediately after this event via the corresponding events in + zwlr_foreign_toplevel_handle_v1. + + + + + + + Indicates the client no longer wishes to receive events for new toplevels. + However the compositor may emit further toplevel_created events, until + the finished event is emitted. + + The client must not send any more requests after this one. + + + + + + This event indicates that the compositor is done sending events to the + zwlr_foreign_toplevel_manager_v1. The server will destroy the object + immediately after sending this request, so it will become invalid and + the client should free any resources associated with it. + + + + + + + A zwlr_foreign_toplevel_handle_v1 object represents an opened toplevel + window. Each app may have multiple opened toplevels. + + Each toplevel has a list of outputs it is visible on, conveyed to the + client with the output_enter and output_leave events. + + + + + This event is emitted whenever the title of the toplevel changes. + + + + + + + This event is emitted whenever the app-id of the toplevel changes. + + + + + + + This event is emitted whenever the toplevel becomes visible on + the given output. A toplevel may be visible on multiple outputs. + + + + + + + This event is emitted whenever the toplevel stops being visible on + the given output. It is guaranteed that an entered-output event + with the same output has been emitted before this event. + + + + + + + Requests that the toplevel be maximized. If the maximized state actually + changes, this will be indicated by the state event. + + + + + + Requests that the toplevel be unmaximized. If the maximized state actually + changes, this will be indicated by the state event. + + + + + + Requests that the toplevel be minimized. If the minimized state actually + changes, this will be indicated by the state event. + + + + + + Requests that the toplevel be unminimized. If the minimized state actually + changes, this will be indicated by the state event. + + + + + + Request that this toplevel be activated on the given seat. + There is no guarantee the toplevel will be actually activated. + + + + + + + The different states that a toplevel can have. These have the same meaning + as the states with the same names defined in xdg-toplevel + + + + + + + + + + + This event is emitted immediately after the zlw_foreign_toplevel_handle_v1 + is created and each time the toplevel state changes, either because of a + compositor action or because of a request in this protocol. + + + + + + + + This event is sent after all changes in the toplevel state have been + sent. + + This allows changes to the zwlr_foreign_toplevel_handle_v1 properties + to be seen as atomic, even if they happen via multiple events. + + + + + + Send a request to the toplevel to close itself. The compositor would + typically use a shell-specific method to carry out this request, for + example by sending the xdg_toplevel.close event. However, this gives + no guarantees the toplevel will actually be destroyed. If and when + this happens, the zwlr_foreign_toplevel_handle_v1.closed event will + be emitted. + + + + + + The rectangle of the surface specified in this request corresponds to + the place where the app using this protocol represents the given toplevel. + It can be used by the compositor as a hint for some operations, e.g + minimizing. The client is however not required to set this, in which + case the compositor is free to decide some default value. + + If the client specifies more than one rectangle, only the last one is + considered. + + The dimensions are given in surface-local coordinates. + Setting width=height=0 removes the already-set rectangle. + + + + + + + + + + + + + + + + This event means the toplevel has been destroyed. It is guaranteed there + won't be any more events for this zwlr_foreign_toplevel_handle_v1. The + toplevel itself becomes inert so any requests will be ignored except the + destroy request. + + + + + + Destroys the zwlr_foreign_toplevel_handle_v1 object. + + This request should be called either when the client does not want to + use the toplevel anymore or after the closed event to finalize the + destruction of the object. + + + + + + + + Requests that the toplevel be fullscreened on the given output. If the + fullscreen state and/or the outputs the toplevel is visible on actually + change, this will be indicated by the state and output_enter/leave + events. + + The output parameter is only a hint to the compositor. Also, if output + is NULL, the compositor should decide which output the toplevel will be + fullscreened on, if at all. + + + + + + + Requests that the toplevel be unfullscreened. If the fullscreen state + actually changes, this will be indicated by the state event. + + + + + + + + This event is emitted whenever the parent of the toplevel changes. + + No event is emitted when the parent handle is destroyed by the client. + + + + + -- GitLab From 1bc6904ce32117d555453b60debec87ca9332b14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Bonithon?= Date: Tue, 30 Aug 2022 18:19:11 +0200 Subject: [PATCH 12/22] wayland: libxfce4panel: Add new macro `xfce_panel_wl_registry_bind()` This shares the registry between panel and plugins to save memory, as recommended in the `wl_display::get_registry` documentation. --- docs/reference/libxfce4panel-sections.txt | 3 + libxfce4panel/Makefile.am | 2 + libxfce4panel/libxfce4panel.symbols | 1 + libxfce4panel/xfce-panel-convenience.c | 131 ++++++++++++++++++++++ libxfce4panel/xfce-panel-convenience.h | 23 ++++ 5 files changed, 160 insertions(+) diff --git a/docs/reference/libxfce4panel-sections.txt b/docs/reference/libxfce4panel-sections.txt index fc0eac7fc..7cf7eec4f 100644 --- a/docs/reference/libxfce4panel-sections.txt +++ b/docs/reference/libxfce4panel-sections.txt @@ -35,12 +35,15 @@ xfce_arrow_button_get_type
convenience +xfce_panel_wl_registry_bind xfce_panel_create_button xfce_panel_create_toggle_button xfce_panel_get_channel_name xfce_panel_pixbuf_from_source xfce_panel_pixbuf_from_source_at_size xfce_panel_set_image_from_source + +xfce_panel_wl_registry_bind_real
diff --git a/libxfce4panel/Makefile.am b/libxfce4panel/Makefile.am index 37e5c19eb..2b1dee8a7 100644 --- a/libxfce4panel/Makefile.am +++ b/libxfce4panel/Makefile.am @@ -52,6 +52,7 @@ libxfce4panel_2_0_la_SOURCES = \ libxfce4panel_2_0_la_CFLAGS = \ $(GTK_CFLAGS) \ $(LIBXFCE4UTIL_CFLAGS) \ + $(WAYLAND_CLIENT_CFLAGS) \ $(PLATFORM_CFLAGS) libxfce4panel_2_0_la_LDFLAGS = \ @@ -64,6 +65,7 @@ libxfce4panel_2_0_la_LDFLAGS = \ libxfce4panel_2_0_la_LIBADD = \ $(GTK_LIBS) \ $(LIBXFCE4UTIL_LIBS) \ + $(WAYLAND_CLIENT_LIBS) \ -lm # diff --git a/libxfce4panel/libxfce4panel.symbols b/libxfce4panel/libxfce4panel.symbols index 34f3eeb0d..7eeedf7ac 100644 --- a/libxfce4panel/libxfce4panel.symbols +++ b/libxfce4panel/libxfce4panel.symbols @@ -64,6 +64,7 @@ xfce_arrow_button_set_blinking /* xfce-panel-convenience.h */ #if IN_HEADER(__XFCE_PANEL_CONVENIENCE_H__) #if IN_SOURCE(__XFCE_PANEL_CONVENIENCE_C__) +xfce_panel_wl_registry_bind_real xfce_panel_create_button G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT xfce_panel_create_toggle_button G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT xfce_panel_get_channel_name diff --git a/libxfce4panel/xfce-panel-convenience.c b/libxfce4panel/xfce-panel-convenience.c index b947094b9..bacd8004d 100644 --- a/libxfce4panel/xfce-panel-convenience.c +++ b/libxfce4panel/xfce-panel-convenience.c @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -44,6 +45,136 @@ * to help developers of Xfce Panel plugins. **/ + + +static void xfce_panel_wl_registry_global (void *data, + struct wl_registry *registry, + uint32_t id, + const char *interface, + uint32_t version); +static void xfce_panel_wl_registry_global_remove (void *data, + struct wl_registry *registry, + uint32_t id); + + + +typedef struct +{ + uint32_t id; + uint32_t version; +} WlBindingParam; + + + +static struct wl_registry *wl_registry = NULL; +static GHashTable *wl_binding_params = NULL; +static const struct wl_registry_listener wl_registry_listener = +{ + xfce_panel_wl_registry_global, + xfce_panel_wl_registry_global_remove +}; + + + +static void +xfce_panel_wl_registry_global (void *data, + struct wl_registry *registry, + uint32_t id, + const char *interface, + uint32_t version) +{ + WlBindingParam *param; + + param = g_new (WlBindingParam, 1); + param->id = id; + param->version = version; + + g_hash_table_insert (wl_binding_params, g_strdup (interface), param); +} + + + +static void +xfce_panel_wl_registry_global_remove (void *data, + struct wl_registry *registry, + uint32_t id) +{ + WlBindingParam *param; + GHashTableIter iter; + gpointer value; + + g_hash_table_iter_init (&iter, wl_binding_params); + while (g_hash_table_iter_next (&iter, NULL, &value)) + { + param = value; + if (param->id == id) + { + g_hash_table_iter_remove (&iter); + break; + } + } +} + + + +static void +xfce_panel_wayland_finalize (void) +{ + wl_registry_destroy (wl_registry); + g_hash_table_destroy (wl_binding_params); + wl_registry = NULL; + wl_binding_params = NULL; +} + + + +static gboolean +xfce_panel_wayland_init (void) +{ + struct wl_display *wl_display; + GdkDisplay *display; + + if (wl_registry != NULL) + return TRUE; + + display = gdk_display_get_default (); + if (! PANEL_IS_WAYLAND_DISPLAY (display)) + return FALSE; + + wl_display = gdk_wayland_display_get_wl_display (display); + wl_registry = wl_display_get_registry (wl_display); + if (wl_registry == NULL) + return FALSE; + + wl_binding_params = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + wl_registry_add_listener (wl_registry, &wl_registry_listener, NULL); + wl_display_roundtrip (wl_display); + g_signal_connect (display, "closed", G_CALLBACK (xfce_panel_wayland_finalize), NULL); + + return TRUE; +} + + + +gpointer +xfce_panel_wl_registry_bind_real (const gchar *interface_name, + const struct wl_interface *interface) +{ + WlBindingParam *param; + + if (! xfce_panel_wayland_init ()) + return NULL; + + param = g_hash_table_lookup (wl_binding_params, interface_name); + if (param != NULL) + return wl_registry_bind (wl_registry, param->id, interface, + MIN ((uint32_t) interface->version, param->version)); + + return NULL; +} + + + /** * xfce_panel_create_button: * diff --git a/libxfce4panel/xfce-panel-convenience.h b/libxfce4panel/xfce-panel-convenience.h index d235d5c34..4871804ed 100644 --- a/libxfce4panel/xfce-panel-convenience.h +++ b/libxfce4panel/xfce-panel-convenience.h @@ -23,9 +23,32 @@ #define __XFCE_PANEL_CONVENIENCE_H__ #include +#include G_BEGIN_DECLS +/** + * xfce_panel_wl_registry_bind: + * @interface: interface name as a token (not a string) + * + * A wrapper around + * [wl_registry_bind()](https://wayland.app/protocols/wayland#wl_registry:request:bind) + * that allows to bind global objects available from the Walyand compositor from their + * interface name. Beyond convenience, you should use this instead of getting your own + * `wl_registry` from the compositor, as it centralizes resource management (as the + * [documentation says](https://wayland.app/protocols/wayland#wl_display:request:get_registry), + * resources attached to a `wl_registry` are only released when the `wl_display` is + * disconnected). + * + * Returns: (allow-none) (transfer full): The new, client-created object bound to + * the server, or %NULL if @interface is not supported. + **/ +#define xfce_panel_wl_registry_bind(interface) \ + xfce_panel_wl_registry_bind_real (#interface, &interface##_interface) + +gpointer xfce_panel_wl_registry_bind_real (const gchar *interface_name, + const struct wl_interface *interface); + GtkWidget *xfce_panel_create_button (void) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; GtkWidget *xfce_panel_create_toggle_button (void) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; -- GitLab From 99721434c935631f931afda5fe6110ecc31fc5c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Bonithon?= Date: Wed, 31 Aug 2022 17:35:50 +0200 Subject: [PATCH 13/22] wayland: panel: Autohide adaptations (foreign-toplevel-management part) This partially restores the intellihide feature by hiding the panel if the active window is maximized. For the rest, it would be necessary to have access to the geometry of the foreign toplevel, which the protocol does not allow in its current state, and which does not seem to be possible with a reasonable effort by any other means. --- panel/Makefile.am | 6 +- panel/panel-window.c | 311 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 315 insertions(+), 2 deletions(-) diff --git a/panel/Makefile.am b/panel/Makefile.am index 29523a6d1..c0bd8b206 100644 --- a/panel/Makefile.am +++ b/panel/Makefile.am @@ -64,6 +64,7 @@ xfce4_panel_CFLAGS = \ $(XFCONF_CFLAGS) \ $(LIBX11_CFLAGS) \ $(GTK_LAYER_SHELL_CFLAGS) \ + $(WAYLAND_CLIENT_CFLAGS) \ $(PLATFORM_CFLAGS) xfce4_panel_LDFLAGS = \ @@ -73,6 +74,7 @@ xfce4_panel_LDFLAGS = \ xfce4_panel_LDADD = \ $(top_builddir)/libxfce4panel/libxfce4panel-$(LIBXFCE4PANEL_VERSION_API).la \ $(top_builddir)/common/libpanel-common.la \ + $(top_builddir)/protocols/libpanel-protocols.la \ $(GTK_LIBS) \ $(GMODULE_LIBS) \ $(GIO_UNIX_LIBS) \ @@ -82,11 +84,13 @@ xfce4_panel_LDADD = \ $(XFCONF_LIBS) \ $(LIBX11_LIBS) \ $(GTK_LAYER_SHELL_LIBS) \ + $(WAYLAND_CLIENT_LIBS) \ -lm xfce4_panel_DEPENDENCIES = \ $(top_builddir)/libxfce4panel/libxfce4panel-$(LIBXFCE4PANEL_VERSION_API).la \ - $(top_builddir)/common/libpanel-common.la + $(top_builddir)/common/libpanel-common.la \ + $(top_builddir)/protocols/libpanel-protocols.la if MAINTAINER_MODE diff --git a/panel/panel-window.c b/panel/panel-window.c index 67b80bd0d..7775a62d6 100644 --- a/panel/panel-window.c +++ b/panel/panel-window.c @@ -37,6 +37,7 @@ #include #include +#include #include #include @@ -189,6 +190,27 @@ static void panel_window_plugin_set_nrows (GtkWidget gpointer user_data); static void panel_window_plugin_set_screen_position (GtkWidget *widget, gpointer user_data); +static void panel_window_wl_toplevel_manager_toplevel (void *data, + struct zwlr_foreign_toplevel_manager_v1 *manager, + struct zwlr_foreign_toplevel_handle_v1 *toplevel); +static void panel_window_wl_toplevel_manager_finished (void *data, + struct zwlr_foreign_toplevel_manager_v1 *manager); +static void panel_window_wl_toplevel_output_enter (void *data, + struct zwlr_foreign_toplevel_handle_v1 *toplevel, + struct wl_output *output); +static void panel_window_wl_toplevel_output_leave (void *data, + struct zwlr_foreign_toplevel_handle_v1 *toplevel, + struct wl_output *output); +static void panel_window_wl_toplevel_state (void *data, + struct zwlr_foreign_toplevel_handle_v1 *toplevel, + struct wl_array *state); +static void panel_window_wl_toplevel_done (void *data, + struct zwlr_foreign_toplevel_handle_v1 *toplevel); +static void panel_window_wl_toplevel_closed (void *data, + struct zwlr_foreign_toplevel_handle_v1 *toplevel); +static void panel_window_wl_toggle_notify (gpointer data, + GObject *object, + gboolean is_last_ref); @@ -373,12 +395,23 @@ struct _PanelWindow guint32 grab_time; gint grab_x; gint grab_y; + + /* Wayland */ + struct zwlr_foreign_toplevel_manager_v1 *wl_toplevel_manager; + GHashTable *wl_toplevels; + gboolean wl_active_is_maximized; }; /* used for a full XfcePanelWindow name in the class, but not in the code */ typedef PanelWindow XfcePanelWindow; typedef PanelWindowClass XfcePanelWindowClass; +typedef struct +{ + GSList *outputs; + guint state; +} WlToplevelProps; + static GdkAtom cardinal_atom = 0; @@ -387,6 +420,25 @@ static GdkAtom net_wm_strut_atom = 0; #endif static GdkAtom net_wm_strut_partial_atom = 0; +static const struct zwlr_foreign_toplevel_manager_v1_listener wl_toplevel_manager_listener = +{ + panel_window_wl_toplevel_manager_toplevel, + panel_window_wl_toplevel_manager_finished +}; + +static void wl_unused (void) {}; +static const struct zwlr_foreign_toplevel_handle_v1_listener wl_toplevel_listener = +{ + (void (*) (void *, struct zwlr_foreign_toplevel_handle_v1 *, const char *)) wl_unused, + (void (*) (void *, struct zwlr_foreign_toplevel_handle_v1 *, const char *)) wl_unused, + panel_window_wl_toplevel_output_enter, + panel_window_wl_toplevel_output_leave, + panel_window_wl_toplevel_state, + panel_window_wl_toplevel_done, + panel_window_wl_toplevel_closed, + (void (*) (void *, struct zwlr_foreign_toplevel_handle_v1 *, struct zwlr_foreign_toplevel_handle_v1 *)) wl_unused +}; + G_DEFINE_TYPE (XfcePanelWindow, panel_window, PANEL_TYPE_BASE_WINDOW) @@ -605,6 +657,9 @@ panel_window_init (PanelWindow *window) window->grab_time = 0; window->grab_x = 0; window->grab_y = 0; + window->wl_toplevel_manager = NULL; + window->wl_toplevels = NULL; + window->wl_active_is_maximized = FALSE; /* not resizable, so allocation will follow size request */ gtk_window_set_resizable (GTK_WINDOW (window), FALSE); @@ -2773,7 +2828,18 @@ panel_window_active_window_geometry_changed (WnckWindow *active_window, if (window->autohide_behavior == AUTOHIDE_BEHAVIOR_INTELLIGENTLY && window->autohide_block == 0) { - if (! GDK_IS_X11_DISPLAY (window->display) || active_window == NULL) + /* intellihide on Wayland: reduced to maximized active window */ + if (window->wl_toplevel_manager != NULL) + { + if (window->wl_active_is_maximized + && panel_window_pointer_is_outside (window)) + panel_window_autohide_queue (window, AUTOHIDE_POPDOWN); + else + panel_window_autohide_queue (window, AUTOHIDE_VISIBLE); + + return; + } + else if (! GDK_IS_X11_DISPLAY (window->display) || active_window == NULL) return; if (wnck_window_get_window_type (active_window) != WNCK_WINDOW_DESKTOP) @@ -3161,6 +3227,42 @@ panel_window_autohide_event (GtkWidget *widget, +static void +panel_window_wl_toplevel_props_free (gpointer data) +{ + WlToplevelProps *props = data; + + g_slist_free (props->outputs); + g_free (props); +} + + + +static gboolean +panel_window_wl_toplevel_manager_init (gpointer data) +{ + PanelWindow *window = data; + + window->wl_toplevel_manager = xfce_panel_wl_registry_bind (zwlr_foreign_toplevel_manager_v1); + if (window->wl_toplevel_manager != NULL) + { + window->wl_toplevels = + g_hash_table_new_full (g_direct_hash, g_direct_equal, + (GDestroyNotify) zwlr_foreign_toplevel_handle_v1_destroy, + panel_window_wl_toplevel_props_free); + g_object_add_toggle_ref (G_OBJECT (window), panel_window_wl_toggle_notify, + window->wl_toplevel_manager); + zwlr_foreign_toplevel_manager_v1_add_listener (window->wl_toplevel_manager, + &wl_toplevel_manager_listener, + window); + wl_display_roundtrip (gdk_wayland_display_get_wl_display (window->display)); + } + + return FALSE; +} + + + static void panel_window_set_autohide_behavior (PanelWindow *window, AutohideBehavior behavior) @@ -3181,6 +3283,11 @@ panel_window_set_autohide_behavior (PanelWindow *window, /* remember the new behavior */ window->autohide_behavior = behavior; + /* reset Wayland toplevel manager if needed */ + if (window->autohide_behavior != AUTOHIDE_BEHAVIOR_INTELLIGENTLY + && window->wl_toplevel_manager != NULL) + panel_window_wl_toggle_notify (window->wl_toplevel_manager, G_OBJECT (window), TRUE); + /* create an autohide window only if we are autohiding at all */ if (window->autohide_behavior != AUTOHIDE_BEHAVIOR_NEVER) { @@ -3266,6 +3373,15 @@ panel_window_set_autohide_behavior (PanelWindow *window, { /* start intelligent autohide by making the panel visible initially */ panel_window_autohide_queue (window, AUTOHIDE_VISIBLE); + + if (gtk_layer_is_supported ()) + { + /* wait for panel position to be initialized */ + if (window->base_x == -1 && window->base_y == -1) + g_idle_add (panel_window_wl_toplevel_manager_init, window); + else + panel_window_wl_toplevel_manager_init (window); + } } } else if (window->autohide_window != NULL) @@ -3687,6 +3803,199 @@ panel_window_plugin_set_screen_position (GtkWidget *widget, +static void +panel_window_wl_toplevel_manager_toplevel (void *data, + struct zwlr_foreign_toplevel_manager_v1 *manager, + struct zwlr_foreign_toplevel_handle_v1 *toplevel) +{ + PanelWindow *window = data; + WlToplevelProps *props; + + props = g_new0 (WlToplevelProps, 1); + g_hash_table_insert (window->wl_toplevels, toplevel, props); + zwlr_foreign_toplevel_handle_v1_add_listener (toplevel, &wl_toplevel_listener, window); + wl_display_roundtrip (gdk_wayland_display_get_wl_display (window->display)); +} + + + +static void +panel_window_wl_toplevel_manager_finished (void *data, + struct zwlr_foreign_toplevel_manager_v1 *manager) +{ + PanelWindow *window = data; + + g_hash_table_destroy (window->wl_toplevels); + zwlr_foreign_toplevel_manager_v1_destroy (manager); + g_object_unref (window); +} + + + +static void +panel_window_wl_toplevel_output_enter (void *data, + struct zwlr_foreign_toplevel_handle_v1 *toplevel, + struct wl_output *output) +{ + PanelWindow *window = data; + WlToplevelProps *props; + + props = g_hash_table_lookup (window->wl_toplevels, toplevel); + props->outputs = g_slist_prepend (props->outputs, output); +} + + + +static void +panel_window_wl_toplevel_output_leave (void *data, + struct zwlr_foreign_toplevel_handle_v1 *toplevel, + struct wl_output *output) +{ + PanelWindow *window = data; + WlToplevelProps *props; + + props = g_hash_table_lookup (window->wl_toplevels, toplevel); + props->outputs = g_slist_remove (props->outputs, output); +} + + + +static void +panel_window_wl_toplevel_state (void *data, + struct zwlr_foreign_toplevel_handle_v1 *toplevel, + struct wl_array *states) +{ + PanelWindow *window = data; + enum zwlr_foreign_toplevel_handle_v1_state *state; + WlToplevelProps *props; + + /* store toplevel state as flags */ + props = g_hash_table_lookup (window->wl_toplevels, toplevel); + props->state = 0; + wl_array_for_each (state, states) + props->state |= 1 << *state; +} + + + +static void +panel_window_wl_toplevel_done (void *data, + struct zwlr_foreign_toplevel_handle_v1 *toplevel) +{ + PanelWindow *window = data; + WlToplevelProps *props; + GdkMonitor *monitor = NULL; + gboolean maximized; + + /* check if this toplevel and the panel have a common monitor */ + props = g_hash_table_lookup (window->wl_toplevels, toplevel); + if (window->span_monitors) + { + GdkMonitor *p_monitor; + GtkAllocation alloc; + gint fixed_dim, step; + gboolean found = FALSE; + + gtk_widget_get_allocation (GTK_WIDGET (window), &alloc); + if (IS_HORIZONTAL (window)) + { + fixed_dim = window->area.y + alloc.height / 2 + + gtk_layer_get_margin (GTK_WINDOW (window), GTK_LAYER_SHELL_EDGE_TOP); + step = alloc.width / 10; + for (gint x = 0; x <= alloc.width; x += step) + { + p_monitor = gdk_display_get_monitor_at_point (window->display, x, fixed_dim); + if (p_monitor != monitor) + { + monitor = p_monitor; + if (g_slist_find (props->outputs, gdk_wayland_monitor_get_wl_output (monitor))) + { + found = TRUE; + break; + } + } + } + } + else + { + fixed_dim = window->area.x + alloc.width / 2 + + gtk_layer_get_margin (GTK_WINDOW (window), GTK_LAYER_SHELL_EDGE_LEFT); + step = alloc.height / 10; + for (gint y = 0; y <= alloc.height; y += step) + { + p_monitor = gdk_display_get_monitor_at_point (window->display, fixed_dim, y); + if (p_monitor != monitor) + { + monitor = p_monitor; + if (g_slist_find (props->outputs, gdk_wayland_monitor_get_wl_output (monitor))) + { + found = TRUE; + break; + } + } + } + } + + if (! found) + return; + } + else + { + monitor = gtk_layer_get_monitor (GTK_WINDOW (window)); + if (! g_slist_find (props->outputs, gdk_wayland_monitor_get_wl_output (monitor))) + return; + } + + /* update maximized state if needed */ + if (PANEL_HAS_FLAG (props->state, 1 << ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED)) + { + maximized = + PANEL_HAS_FLAG (props->state, 1 << ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED); + if (maximized != window->wl_active_is_maximized) + { + window->wl_active_is_maximized = maximized; + panel_window_active_window_geometry_changed (NULL, window); + } + } +} + + + +static void +panel_window_wl_toplevel_closed (void *data, + struct zwlr_foreign_toplevel_handle_v1 *toplevel) +{ + PanelWindow *window = data; + + /* this does not cover the case of closing a maximized active window, which would require + * switching window->wl_active_is_maximized to FALSE, but this is fixed later when switching + * to XfwlForeignToplevel */ + g_hash_table_remove (window->wl_toplevels, toplevel); +} + + + +static void +panel_window_wl_toggle_notify (gpointer data, + GObject *object, + gboolean is_last_ref) +{ + PanelWindow *window = PANEL_WINDOW (object); + + if (! is_last_ref) + return; + + /* remove this callback now to be sure not to pass here again when object is disposed */ + g_object_ref (object); + g_object_remove_toggle_ref (object, panel_window_wl_toggle_notify, data); + + /* this will trigger finished() later */ + zwlr_foreign_toplevel_manager_v1_stop (window->wl_toplevel_manager); + window->wl_toplevel_manager = NULL; +} + + + GtkWidget * panel_window_new (GdkScreen *screen, gint id, -- GitLab From 54a3cd3b741146956e6d73056dd7be1cfcc419ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Bonithon?= Date: Sun, 11 Sep 2022 17:57:40 +0200 Subject: [PATCH 14/22] wayland: libxfwl: Move Wayland core utils to dedicated files --- docs/reference/libxfce4panel-docs.xml | 6 + docs/reference/libxfce4panel-sections.txt | 10 +- libxfce4panel/Makefile.am | 7 +- libxfce4panel/libxfce4panel.h | 1 + libxfce4panel/libxfce4panel.symbols | 8 +- libxfce4panel/xfce-panel-convenience.c | 131 ------------------ libxfce4panel/xfce-panel-convenience.h | 23 ---- libxfce4panel/xfwl-core-utils.c | 155 ++++++++++++++++++++++ libxfce4panel/xfwl-core-utils.h | 51 +++++++ panel/panel-window.c | 2 +- 10 files changed, 233 insertions(+), 161 deletions(-) create mode 100644 libxfce4panel/xfwl-core-utils.c create mode 100644 libxfce4panel/xfwl-core-utils.h diff --git a/docs/reference/libxfce4panel-docs.xml b/docs/reference/libxfce4panel-docs.xml index 3ec874747..47a6a2ad9 100644 --- a/docs/reference/libxfce4panel-docs.xml +++ b/docs/reference/libxfce4panel-docs.xml @@ -122,6 +122,12 @@ + + Wayland Interaction + + + + Miscelleanous diff --git a/docs/reference/libxfce4panel-sections.txt b/docs/reference/libxfce4panel-sections.txt index 7cf7eec4f..4213c038e 100644 --- a/docs/reference/libxfce4panel-sections.txt +++ b/docs/reference/libxfce4panel-sections.txt @@ -35,15 +35,12 @@ xfce_arrow_button_get_type
convenience -xfce_panel_wl_registry_bind xfce_panel_create_button xfce_panel_create_toggle_button xfce_panel_get_channel_name xfce_panel_pixbuf_from_source xfce_panel_pixbuf_from_source_at_size xfce_panel_set_image_from_source - -xfce_panel_wl_registry_bind_real
@@ -140,6 +137,13 @@ XFCE_PANEL_PLUGIN_GET_CLASS xfce_panel_plugin_get_type
+
+xfwl-core-utils +xfwl_registry_bind + +xfwl_registry_bind_real +
+
macros XFCE_PANEL_CHANNEL_NAME diff --git a/libxfce4panel/Makefile.am b/libxfce4panel/Makefile.am index 2b1dee8a7..dff5b5333 100644 --- a/libxfce4panel/Makefile.am +++ b/libxfce4panel/Makefile.am @@ -30,7 +30,8 @@ libxfce4panel_headers = \ xfce-panel-macros.h \ xfce-panel-plugin.h \ xfce-panel-plugin-provider.h \ - xfce-panel-image.h + xfce-panel-image.h \ + xfwl-core-utils.h libxfce4panel_includedir = \ $(includedir)/xfce4/libxfce4panel-$(LIBXFCE4PANEL_VERSION_API)/libxfce4panel @@ -47,7 +48,8 @@ libxfce4panel_2_0_la_SOURCES = \ xfce-panel-convenience.c \ xfce-panel-plugin.c \ xfce-panel-plugin-provider.c \ - xfce-panel-image.c + xfce-panel-image.c \ + xfwl-core-utils.c libxfce4panel_2_0_la_CFLAGS = \ $(GTK_CFLAGS) \ @@ -137,6 +139,7 @@ INTROSPECTION_SCANNER_ARGS = \ --identifier-prefix=LIBXFCE4PANEL \ --identifier-prefix=libxfce4panel \ --identifier-prefix=Plugin \ + --identifier-prefix=Xfwl \ --c-include=libxfce4panel/libxfce4panel.h INTROSPECTION_COMPILER_ARGS = \ --includedir=$(srcdir) \ diff --git a/libxfce4panel/libxfce4panel.h b/libxfce4panel/libxfce4panel.h index 95ea98555..33e6a719b 100644 --- a/libxfce4panel/libxfce4panel.h +++ b/libxfce4panel/libxfce4panel.h @@ -31,6 +31,7 @@ #include #include #include +#include #undef _LIBXFCE4PANEL_INSIDE_LIBXFCE4PANEL_H diff --git a/libxfce4panel/libxfce4panel.symbols b/libxfce4panel/libxfce4panel.symbols index 7eeedf7ac..717bcf160 100644 --- a/libxfce4panel/libxfce4panel.symbols +++ b/libxfce4panel/libxfce4panel.symbols @@ -64,7 +64,6 @@ xfce_arrow_button_set_blinking /* xfce-panel-convenience.h */ #if IN_HEADER(__XFCE_PANEL_CONVENIENCE_H__) #if IN_SOURCE(__XFCE_PANEL_CONVENIENCE_C__) -xfce_panel_wl_registry_bind_real xfce_panel_create_button G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT xfce_panel_create_toggle_button G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT xfce_panel_get_channel_name @@ -157,3 +156,10 @@ xfce_panel_plugin_provider_remote_event xfce_panel_plugin_provider_set_locked #endif #endif + +/* xfwl-core-utils.h */ +#if IN_HEADER(__XFWL_CORE_UTILS_H__) +#if IN_SOURCE(__XFWL_CORE_UTILS_C__) +xfwl_registry_bind_real +#endif +#endif diff --git a/libxfce4panel/xfce-panel-convenience.c b/libxfce4panel/xfce-panel-convenience.c index bacd8004d..b947094b9 100644 --- a/libxfce4panel/xfce-panel-convenience.c +++ b/libxfce4panel/xfce-panel-convenience.c @@ -28,7 +28,6 @@ #include #include -#include #include #include #include @@ -45,136 +44,6 @@ * to help developers of Xfce Panel plugins. **/ - - -static void xfce_panel_wl_registry_global (void *data, - struct wl_registry *registry, - uint32_t id, - const char *interface, - uint32_t version); -static void xfce_panel_wl_registry_global_remove (void *data, - struct wl_registry *registry, - uint32_t id); - - - -typedef struct -{ - uint32_t id; - uint32_t version; -} WlBindingParam; - - - -static struct wl_registry *wl_registry = NULL; -static GHashTable *wl_binding_params = NULL; -static const struct wl_registry_listener wl_registry_listener = -{ - xfce_panel_wl_registry_global, - xfce_panel_wl_registry_global_remove -}; - - - -static void -xfce_panel_wl_registry_global (void *data, - struct wl_registry *registry, - uint32_t id, - const char *interface, - uint32_t version) -{ - WlBindingParam *param; - - param = g_new (WlBindingParam, 1); - param->id = id; - param->version = version; - - g_hash_table_insert (wl_binding_params, g_strdup (interface), param); -} - - - -static void -xfce_panel_wl_registry_global_remove (void *data, - struct wl_registry *registry, - uint32_t id) -{ - WlBindingParam *param; - GHashTableIter iter; - gpointer value; - - g_hash_table_iter_init (&iter, wl_binding_params); - while (g_hash_table_iter_next (&iter, NULL, &value)) - { - param = value; - if (param->id == id) - { - g_hash_table_iter_remove (&iter); - break; - } - } -} - - - -static void -xfce_panel_wayland_finalize (void) -{ - wl_registry_destroy (wl_registry); - g_hash_table_destroy (wl_binding_params); - wl_registry = NULL; - wl_binding_params = NULL; -} - - - -static gboolean -xfce_panel_wayland_init (void) -{ - struct wl_display *wl_display; - GdkDisplay *display; - - if (wl_registry != NULL) - return TRUE; - - display = gdk_display_get_default (); - if (! PANEL_IS_WAYLAND_DISPLAY (display)) - return FALSE; - - wl_display = gdk_wayland_display_get_wl_display (display); - wl_registry = wl_display_get_registry (wl_display); - if (wl_registry == NULL) - return FALSE; - - wl_binding_params = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - wl_registry_add_listener (wl_registry, &wl_registry_listener, NULL); - wl_display_roundtrip (wl_display); - g_signal_connect (display, "closed", G_CALLBACK (xfce_panel_wayland_finalize), NULL); - - return TRUE; -} - - - -gpointer -xfce_panel_wl_registry_bind_real (const gchar *interface_name, - const struct wl_interface *interface) -{ - WlBindingParam *param; - - if (! xfce_panel_wayland_init ()) - return NULL; - - param = g_hash_table_lookup (wl_binding_params, interface_name); - if (param != NULL) - return wl_registry_bind (wl_registry, param->id, interface, - MIN ((uint32_t) interface->version, param->version)); - - return NULL; -} - - - /** * xfce_panel_create_button: * diff --git a/libxfce4panel/xfce-panel-convenience.h b/libxfce4panel/xfce-panel-convenience.h index 4871804ed..d235d5c34 100644 --- a/libxfce4panel/xfce-panel-convenience.h +++ b/libxfce4panel/xfce-panel-convenience.h @@ -23,32 +23,9 @@ #define __XFCE_PANEL_CONVENIENCE_H__ #include -#include G_BEGIN_DECLS -/** - * xfce_panel_wl_registry_bind: - * @interface: interface name as a token (not a string) - * - * A wrapper around - * [wl_registry_bind()](https://wayland.app/protocols/wayland#wl_registry:request:bind) - * that allows to bind global objects available from the Walyand compositor from their - * interface name. Beyond convenience, you should use this instead of getting your own - * `wl_registry` from the compositor, as it centralizes resource management (as the - * [documentation says](https://wayland.app/protocols/wayland#wl_display:request:get_registry), - * resources attached to a `wl_registry` are only released when the `wl_display` is - * disconnected). - * - * Returns: (allow-none) (transfer full): The new, client-created object bound to - * the server, or %NULL if @interface is not supported. - **/ -#define xfce_panel_wl_registry_bind(interface) \ - xfce_panel_wl_registry_bind_real (#interface, &interface##_interface) - -gpointer xfce_panel_wl_registry_bind_real (const gchar *interface_name, - const struct wl_interface *interface); - GtkWidget *xfce_panel_create_button (void) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; GtkWidget *xfce_panel_create_toggle_button (void) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; diff --git a/libxfce4panel/xfwl-core-utils.c b/libxfce4panel/xfwl-core-utils.c new file mode 100644 index 000000000..c92526e8a --- /dev/null +++ b/libxfce4panel/xfwl-core-utils.c @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2022 Gaël Bonithon + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include "xfwl-core-utils.h" + +/** + * SECTION: xfwl-core-utils + * @title: Xfwl core utilities + * @short_description: Utilities related to the core Wayland protocol + * @include: libxfce4panel/libxfce4panel.h + **/ + +static void xfwl_registry_global (void *data, + struct wl_registry *registry, + uint32_t id, + const char *interface, + uint32_t version); +static void xfwl_registry_global_remove (void *data, + struct wl_registry *registry, + uint32_t id); + + + +typedef struct +{ + uint32_t id; + uint32_t version; +} WlBindingParam; + +static struct wl_registry *wl_registry = NULL; +static GHashTable *wl_binding_params = NULL; + +static const struct wl_registry_listener wl_registry_listener = +{ + xfwl_registry_global, + xfwl_registry_global_remove +}; + + + +static void +xfwl_registry_global (void *data, + struct wl_registry *registry, + uint32_t id, + const char *interface, + uint32_t version) +{ + WlBindingParam *param; + + param = g_new (WlBindingParam, 1); + param->id = id; + param->version = version; + + g_hash_table_insert (wl_binding_params, g_strdup (interface), param); +} + + + +static void +xfwl_registry_global_remove (void *data, + struct wl_registry *registry, + uint32_t id) +{ + WlBindingParam *param; + GHashTableIter iter; + gpointer value; + + g_hash_table_iter_init (&iter, wl_binding_params); + while (g_hash_table_iter_next (&iter, NULL, &value)) + { + param = value; + if (param->id == id) + { + g_hash_table_iter_remove (&iter); + break; + } + } +} + + + +static void +xfwl_finalize (void) +{ + wl_registry_destroy (wl_registry); + g_hash_table_destroy (wl_binding_params); + wl_registry = NULL; + wl_binding_params = NULL; +} + + + +static gboolean +xfwl_init (void) +{ + struct wl_display *wl_display; + GdkDisplay *display; + + if (wl_registry != NULL) + return TRUE; + + display = gdk_display_get_default (); + if (! GDK_IS_WAYLAND_DISPLAY (display)) + return FALSE; + + wl_display = gdk_wayland_display_get_wl_display (display); + wl_registry = wl_display_get_registry (wl_display); + if (wl_registry == NULL) + return FALSE; + + wl_binding_params = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + wl_registry_add_listener (wl_registry, &wl_registry_listener, NULL); + wl_display_roundtrip (wl_display); + g_signal_connect (display, "closed", G_CALLBACK (xfwl_finalize), NULL); + + return TRUE; +} + + + +gpointer +xfwl_registry_bind_real (const gchar *interface_name, + const struct wl_interface *interface) +{ + WlBindingParam *param; + + if (! xfwl_init ()) + return NULL; + + param = g_hash_table_lookup (wl_binding_params, interface_name); + if (param != NULL) + return wl_registry_bind (wl_registry, param->id, interface, + MIN ((uint32_t) interface->version, param->version)); + + return NULL; +} diff --git a/libxfce4panel/xfwl-core-utils.h b/libxfce4panel/xfwl-core-utils.h new file mode 100644 index 000000000..120afcd8f --- /dev/null +++ b/libxfce4panel/xfwl-core-utils.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2022 Gaël Bonithon + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __XFWL_CORE_UTILS_H__ +#define __XFWL_CORE_UTILS_H__ + +#include +#include + +G_BEGIN_DECLS + +/** + * xfwl_registry_bind: + * @interface: interface name as a token (not a string) + * + * A wrapper around + * [wl_registry_bind()](https://wayland.app/protocols/wayland#wl_registry:request:bind) + * that allows to bind global objects available from the Walyand compositor from their + * interface name. Beyond convenience, you should use this instead of getting your own + * `wl_registry` from the compositor, as it centralizes resource management (as the + * [documentation says](https://wayland.app/protocols/wayland#wl_display:request:get_registry), + * resources attached to a `wl_registry` are only released when the `wl_display` is + * disconnected). + * + * Returns: (allow-none) (transfer full): The new, client-created object bound to + * the server, or %NULL if @interface is not supported. + **/ +#define xfwl_registry_bind(interface) \ + xfwl_registry_bind_real (#interface, &interface##_interface) + +gpointer xfwl_registry_bind_real (const gchar *interface_name, + const struct wl_interface *interface); + +G_END_DECLS + +#endif /* !__XFWL_CORE_UTILS_H__ */ diff --git a/panel/panel-window.c b/panel/panel-window.c index 7775a62d6..a906fa28a 100644 --- a/panel/panel-window.c +++ b/panel/panel-window.c @@ -3243,7 +3243,7 @@ panel_window_wl_toplevel_manager_init (gpointer data) { PanelWindow *window = data; - window->wl_toplevel_manager = xfce_panel_wl_registry_bind (zwlr_foreign_toplevel_manager_v1); + window->wl_toplevel_manager = xfwl_registry_bind (zwlr_foreign_toplevel_manager_v1); if (window->wl_toplevel_manager != NULL) { window->wl_toplevels = -- GitLab From e6bca09e304505452539e058ff4f0b663f1b4bc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Bonithon?= Date: Sun, 11 Sep 2022 18:15:13 +0200 Subject: [PATCH 15/22] wayland: libxfwl: Add GObject wrappers for foreign-toplevel-management --- docs/reference/libxfce4panel-docs.xml | 2 + docs/reference/libxfce4panel-sections.txt | 25 + docs/reference/libxfce4panel.types | 2 + libxfce4panel/Makefile.am | 16 +- libxfce4panel/libxfce4panel.h | 2 + libxfce4panel/libxfce4panel.symbols | 25 + libxfce4panel/xfwl-foreign-toplevel-manager.c | 427 +++++++++++++++ libxfce4panel/xfwl-foreign-toplevel-manager.h | 41 ++ libxfce4panel/xfwl-foreign-toplevel.c | 517 ++++++++++++++++++ libxfce4panel/xfwl-foreign-toplevel.h | 64 +++ protocols/Makefile.am | 3 + 11 files changed, 1120 insertions(+), 4 deletions(-) create mode 100644 libxfce4panel/xfwl-foreign-toplevel-manager.c create mode 100644 libxfce4panel/xfwl-foreign-toplevel-manager.h create mode 100644 libxfce4panel/xfwl-foreign-toplevel.c create mode 100644 libxfce4panel/xfwl-foreign-toplevel.h diff --git a/docs/reference/libxfce4panel-docs.xml b/docs/reference/libxfce4panel-docs.xml index 47a6a2ad9..3e7351a9a 100644 --- a/docs/reference/libxfce4panel-docs.xml +++ b/docs/reference/libxfce4panel-docs.xml @@ -126,6 +126,8 @@ Wayland Interaction + + diff --git a/docs/reference/libxfce4panel-sections.txt b/docs/reference/libxfce4panel-sections.txt index 4213c038e..6d8b4086a 100644 --- a/docs/reference/libxfce4panel-sections.txt +++ b/docs/reference/libxfce4panel-sections.txt @@ -144,6 +144,31 @@ xfwl_registry_bind xfwl_registry_bind_real
+
+xfwl-foreign-toplevel +XfwlForeignToplevel +XfwlForeignToplevelState +xfwl_foreign_toplevel_get_wl_toplevel +xfwl_foreign_toplevel_get_title +xfwl_foreign_toplevel_get_app_id +xfwl_foreign_toplevel_get_monitors +xfwl_foreign_toplevel_get_state +xfwl_foreign_toplevel_get_parent + +XFWL_TYPE_FOREIGN_TOPLEVEL +
+ +
+xfwl-foreign-toplevel-manager +XfwlForeignToplevelManager +xfwl_foreign_toplevel_manager_get +xfwl_foreign_toplevel_manager_get_wl_manager +xfwl_foreign_toplevel_manager_get_toplevels +xfwl_foreign_toplevel_manager_get_active + +XFWL_TYPE_FOREIGN_TOPLEVEL_MANAGER +
+
macros XFCE_PANEL_CHANNEL_NAME diff --git a/docs/reference/libxfce4panel.types b/docs/reference/libxfce4panel.types index 0ffc5e003..0c2ab8a82 100644 --- a/docs/reference/libxfce4panel.types +++ b/docs/reference/libxfce4panel.types @@ -1,3 +1,5 @@ xfce_arrow_button_get_type xfce_panel_image_get_type xfce_panel_plugin_get_type +xfwl_foreign_toplevel_get_type +xfwl_foreign_toplevel_manager_get_type diff --git a/libxfce4panel/Makefile.am b/libxfce4panel/Makefile.am index dff5b5333..5fc015235 100644 --- a/libxfce4panel/Makefile.am +++ b/libxfce4panel/Makefile.am @@ -31,7 +31,9 @@ libxfce4panel_headers = \ xfce-panel-plugin.h \ xfce-panel-plugin-provider.h \ xfce-panel-image.h \ - xfwl-core-utils.h + xfwl-core-utils.h \ + xfwl-foreign-toplevel.h \ + xfwl-foreign-toplevel-manager.h libxfce4panel_includedir = \ $(includedir)/xfce4/libxfce4panel-$(LIBXFCE4PANEL_VERSION_API)/libxfce4panel @@ -49,7 +51,9 @@ libxfce4panel_2_0_la_SOURCES = \ xfce-panel-plugin.c \ xfce-panel-plugin-provider.c \ xfce-panel-image.c \ - xfwl-core-utils.c + xfwl-core-utils.c \ + xfwl-foreign-toplevel.c \ + xfwl-foreign-toplevel-manager.c libxfce4panel_2_0_la_CFLAGS = \ $(GTK_CFLAGS) \ @@ -65,11 +69,15 @@ libxfce4panel_2_0_la_LDFLAGS = \ $(PLATFORM_LDFLAGS) libxfce4panel_2_0_la_LIBADD = \ + $(top_builddir)/protocols/libpanel-protocols.la \ $(GTK_LIBS) \ $(LIBXFCE4UTIL_LIBS) \ $(WAYLAND_CLIENT_LIBS) \ -lm +libxfce4panel_2_0_la_DEPENDENCIES = \ + $(top_builddir)/protocols/libpanel-protocols.la + # # Pkg-config file # @@ -98,13 +106,13 @@ libxfce4panel-enum-types.h: $(libxfce4panel_headers) Makefile $(AM_V_GEN) ( cd $(srcdir) && glib-mkenums \ --fhead "#ifndef __LIBXFCE4PANEL_ENUM_TYPES_H__\n#define __LIBXFCE4PANEL_ENUM_TYPES_H__\n#include \nG_BEGIN_DECLS\n" \ --fprod "/* enumerations from \"@filename@\" */\n" \ - --vhead "GType @enum_name@_get_type (void) G_GNUC_CONST;\n#define XFCE_TYPE_@ENUMSHORT@ (@enum_name@_get_type())\n" \ + --vhead "GType @enum_name@_get_type (void) G_GNUC_CONST;\n#define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type())\n" \ --ftail "G_END_DECLS\n\n#endif /* !__LIBXFCE4PANEL_ENUM_TYPES_H__ */" \ $(libxfce4panel_headers) ) > $@ libxfce4panel-enum-types.c: $(libxfce4panel_headers) Makefile $(AM_V_GEN) ( cd $(srcdir) && glib-mkenums \ - --fhead "#include \n#include \n#include " \ + --fhead "#include \n#include \n#include \n#include " \ --fprod "\n/* enumerations from \"@filename@\" */" \ --vhead "GType\n@enum_name@_get_type (void)\n{\n\tstatic GType type = 0;\n\tif (type == 0) {\n\tstatic const G@Type@Value values[] = {"\ --vprod "\t{ @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \ diff --git a/libxfce4panel/libxfce4panel.h b/libxfce4panel/libxfce4panel.h index 33e6a719b..9ab9c6e32 100644 --- a/libxfce4panel/libxfce4panel.h +++ b/libxfce4panel/libxfce4panel.h @@ -32,6 +32,8 @@ #include #include #include +#include +#include #undef _LIBXFCE4PANEL_INSIDE_LIBXFCE4PANEL_H diff --git a/libxfce4panel/libxfce4panel.symbols b/libxfce4panel/libxfce4panel.symbols index 717bcf160..eec4f567a 100644 --- a/libxfce4panel/libxfce4panel.symbols +++ b/libxfce4panel/libxfce4panel.symbols @@ -163,3 +163,28 @@ xfce_panel_plugin_provider_set_locked xfwl_registry_bind_real #endif #endif + +/* xfwl-foreign-toplevel.h */ +#if IN_HEADER(__XFWL_FOREIGN_TOPLEVEL_H__) +#if IN_SOURCE(__XFWL_FOREIGN_TOPLEVEL_C__) +xfwl_foreign_toplevel_get_type +xfwl_foreign_toplevel_state_get_type G_GNUC_CONST +xfwl_foreign_toplevel_get_wl_toplevel +xfwl_foreign_toplevel_get_title +xfwl_foreign_toplevel_get_app_id +xfwl_foreign_toplevel_get_monitors +xfwl_foreign_toplevel_get_state +xfwl_foreign_toplevel_get_parent +#endif +#endif + +/* xfwl-foreign-toplevel-manager.h */ +#if IN_HEADER(__XFWL_FOREIGN_TOPLEVEL_MANAGER_H__) +#if IN_SOURCE(__XFWL_FOREIGN_TOPLEVEL_MANAGER_C__) +xfwl_foreign_toplevel_manager_get_type +xfwl_foreign_toplevel_manager_get +xfwl_foreign_toplevel_manager_get_wl_manager +xfwl_foreign_toplevel_manager_get_toplevels +xfwl_foreign_toplevel_manager_get_active +#endif +#endif diff --git a/libxfce4panel/xfwl-foreign-toplevel-manager.c b/libxfce4panel/xfwl-foreign-toplevel-manager.c new file mode 100644 index 000000000..61d0dd474 --- /dev/null +++ b/libxfce4panel/xfwl-foreign-toplevel-manager.c @@ -0,0 +1,427 @@ +/* + * Copyright (C) 2022 Gaël Bonithon + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include "xfwl-foreign-toplevel-manager.h" +#include "xfwl-core-utils.h" +#include "protocols/wlr-foreign-toplevel-management-unstable-v1-client.h" + +/** + * SECTION: xfwl-foreign-toplevel-manager + * @title: XfwlForeignToplevelManager + * @short_description: GObject wrapper around `struct zwlr_foreign_toplevel_manager_v1` + * @include: libxfce4panel/libxfce4panel.h + * + * Wrapping a + * [`struct zwlr_foreign_toplevel_manager_v1`](https://wayland.app/protocols/wlr-foreign-toplevel-management-unstable-v1#zwlr_foreign_toplevel_manager_v1) + * in a #GObject allows to allocate only one toplevel manager and to cache its resources + * only once, offering at least the usual getters to access them. + **/ + +static void xfwl_foreign_toplevel_manager_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static void xfwl_foreign_toplevel_manager_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void xfwl_foreign_toplevel_manager_constructed (GObject *object); +static void xfwl_foreign_toplevel_manager_finalize (GObject *object); + +static void xfwl_foreign_toplevel_manager_toplevel (void *data, + struct zwlr_foreign_toplevel_manager_v1 *wl_manager, + struct zwlr_foreign_toplevel_handle_v1 *wl_toplevel); +static void xfwl_foreign_toplevel_manager_finished (void *data, + struct zwlr_foreign_toplevel_manager_v1 *wl_manager); +static void xfwl_foreign_toplevel_manager_stop (gpointer data, + GObject *object, + gboolean is_last_ref); + + + +enum +{ + PROP_0, + PROP_WL_MANAGER, + PROP_TOPLEVELS, + PROP_ACTIVE, + N_PROPERTIES +}; + +enum +{ + ADDED, + REMOVED, + N_SIGNALS +}; + +/** + * XfwlForeignToplevelManager: + * + * An opaque structure wrapping a + * [`struct zwlr_foreign_toplevel_manager_v1`](https://wayland.app/protocols/wlr-foreign-toplevel-management-unstable-v1#zwlr_foreign_toplevel_manager_v1). + **/ +struct _XfwlForeignToplevelManager +{ + GObject __parent__; + + struct zwlr_foreign_toplevel_manager_v1 *wl_manager; + GHashTable *toplevels; + XfwlForeignToplevel *active; +}; + +static guint manager_signals[N_SIGNALS]; +static GParamSpec *manager_props[N_PROPERTIES] = { NULL, }; +static XfwlForeignToplevelManager *global_manager = NULL; + +static const struct zwlr_foreign_toplevel_manager_v1_listener wl_manager_listener = +{ + xfwl_foreign_toplevel_manager_toplevel, + xfwl_foreign_toplevel_manager_finished +}; + + + +G_DEFINE_TYPE (XfwlForeignToplevelManager, xfwl_foreign_toplevel_manager, G_TYPE_OBJECT) + + + +static void +xfwl_foreign_toplevel_manager_class_init (XfwlForeignToplevelManagerClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->get_property = xfwl_foreign_toplevel_manager_get_property; + gobject_class->set_property = xfwl_foreign_toplevel_manager_set_property; + gobject_class->constructed = xfwl_foreign_toplevel_manager_constructed; + gobject_class->finalize = xfwl_foreign_toplevel_manager_finalize; + + manager_props[PROP_WL_MANAGER] = + g_param_spec_pointer ("wl-manager", "Wayland manager", "The underlying Wayland object", + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY); + + manager_props[PROP_TOPLEVELS] = + g_param_spec_pointer ("toplevels", "Toplevels", "The toplevel list", + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + manager_props[PROP_ACTIVE] = + g_param_spec_object ("active", "Active", "The active toplevel", XFWL_TYPE_FOREIGN_TOPLEVEL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (gobject_class, N_PROPERTIES, manager_props); + + /** + * XfwlForeignToplevelManager::added: + * @manager: an #XfwlForeignToplevelManager + * @toplevel: the added toplevel + * + * The signal is emitted after the toplevel has been added to the list. + **/ + manager_signals[ADDED] = + g_signal_new (g_intern_static_string ("added"), G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, + G_TYPE_NONE, 1, XFWL_TYPE_FOREIGN_TOPLEVEL); + + /** + * XfwlForeignToplevelManager::removed: + * @manager: an #XfwlForeignToplevelManager + * @toplevel: the removed toplevel + * + * The signal is emitted before the toplevel is removed from the list. + **/ + manager_signals[REMOVED] = + g_signal_new (g_intern_static_string ("removed"), G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, + G_TYPE_NONE, 1, XFWL_TYPE_FOREIGN_TOPLEVEL); +} + + + +static void +xfwl_foreign_toplevel_manager_init (XfwlForeignToplevelManager *manager) +{ +} + + + +static void +xfwl_foreign_toplevel_manager_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + XfwlForeignToplevelManager *manager = XFWL_FOREIGN_TOPLEVEL_MANAGER (object); + + switch (prop_id) + { + case PROP_WL_MANAGER: + g_value_set_pointer (value, manager->wl_manager); + break; + + case PROP_TOPLEVELS: + g_value_set_pointer (value, g_hash_table_get_values (manager->toplevels)); + break; + + case PROP_ACTIVE: + g_value_set_object (value, manager->active); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + + +static void +xfwl_foreign_toplevel_manager_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + XfwlForeignToplevelManager *manager = XFWL_FOREIGN_TOPLEVEL_MANAGER (object); + + switch (prop_id) + { + case PROP_WL_MANAGER: + manager->wl_manager = g_value_get_pointer (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + + +static void +xfwl_foreign_toplevel_manager_constructed (GObject *object) +{ + XfwlForeignToplevelManager *manager = XFWL_FOREIGN_TOPLEVEL_MANAGER (object); + + manager->toplevels = + g_hash_table_new_full (g_direct_hash, g_direct_equal, + (GDestroyNotify) zwlr_foreign_toplevel_handle_v1_destroy, + g_object_unref); + + /* to correctly manage the asynchronous release of the manager */ + g_object_add_toggle_ref (object, xfwl_foreign_toplevel_manager_stop, manager->wl_manager); + + /* fill the hash table */ + zwlr_foreign_toplevel_manager_v1_add_listener (manager->wl_manager, &wl_manager_listener, manager); + wl_display_roundtrip (gdk_wayland_display_get_wl_display (gdk_display_get_default ())); + + G_OBJECT_CLASS (xfwl_foreign_toplevel_manager_parent_class)->constructed (object); +} + + + +static void +xfwl_foreign_toplevel_manager_finalize (GObject *object) +{ + XfwlForeignToplevelManager *manager = XFWL_FOREIGN_TOPLEVEL_MANAGER (object); + + g_hash_table_destroy (manager->toplevels); + zwlr_foreign_toplevel_manager_v1_destroy (manager->wl_manager); + + G_OBJECT_CLASS (xfwl_foreign_toplevel_manager_parent_class)->finalize (object); +} + + + +static gboolean +xfwl_foreign_toplevel_manager_toplevel_closed_idle (gpointer data) +{ + if (global_manager == NULL) + return FALSE; + + g_hash_table_remove (global_manager->toplevels, xfwl_foreign_toplevel_get_wl_toplevel (data)); + g_object_notify_by_pspec (G_OBJECT (global_manager), manager_props[PROP_TOPLEVELS]); + + return FALSE; +} + + + +static void +xfwl_foreign_toplevel_manager_toplevel_closed (XfwlForeignToplevel *toplevel, + XfwlForeignToplevelManager *manager) +{ + if (toplevel == manager->active) + { + manager->active = NULL; + g_object_notify_by_pspec (G_OBJECT (manager), manager_props[PROP_ACTIVE]); + } + + g_signal_emit (manager, manager_signals[REMOVED], 0, toplevel); + + /* let other handlers connected to this signal be called before releasing toplevel */ + g_idle_add (xfwl_foreign_toplevel_manager_toplevel_closed_idle, toplevel); +} + + + +static void +xfwl_foreign_toplevel_manager_toplevel_state (XfwlForeignToplevel *toplevel, + GParamSpec *pspec, + XfwlForeignToplevelManager *manager) +{ + if (toplevel != manager->active + && xfwl_foreign_toplevel_get_state (toplevel) & XFWL_FOREIGN_TOPLEVEL_STATE_ACTIVATED) + { + manager->active = toplevel; + g_object_notify_by_pspec (G_OBJECT (manager), manager_props[PROP_ACTIVE]); + } +} + + + +static void +xfwl_foreign_toplevel_manager_toplevel (void *data, + struct zwlr_foreign_toplevel_manager_v1 *wl_manager, + struct zwlr_foreign_toplevel_handle_v1 *wl_toplevel) +{ + XfwlForeignToplevelManager *manager = data; + XfwlForeignToplevel *toplevel; + + toplevel = g_object_new (XFWL_TYPE_FOREIGN_TOPLEVEL, "wl-toplevel", wl_toplevel, NULL); + g_signal_connect (toplevel, "closed", + G_CALLBACK (xfwl_foreign_toplevel_manager_toplevel_closed), manager); + xfwl_foreign_toplevel_manager_toplevel_state (toplevel, NULL, manager); + g_signal_connect (toplevel, "notify::state", + G_CALLBACK (xfwl_foreign_toplevel_manager_toplevel_state), manager); + + g_hash_table_insert (manager->toplevels, wl_toplevel, toplevel); + g_object_notify_by_pspec (G_OBJECT (manager), manager_props[PROP_TOPLEVELS]); + g_signal_emit (manager, manager_signals[ADDED], 0, toplevel); +} + + + +static void +xfwl_foreign_toplevel_manager_finished (void *data, + struct zwlr_foreign_toplevel_manager_v1 *wl_manager) +{ + /* triggers finalize() */ + g_object_unref (data); +} + + + +static void +xfwl_foreign_toplevel_manager_stop (gpointer data, + GObject *object, + gboolean is_last_ref) +{ + if (! is_last_ref) + return; + + /* remove this callback now to be sure not to pass here again when object is disposed */ + g_object_ref (object); + g_object_remove_toggle_ref (object, xfwl_foreign_toplevel_manager_stop, data); + + /* this will trigger finished() later */ + zwlr_foreign_toplevel_manager_v1_stop (data); + global_manager = NULL; +} + + + +/** + * xfwl_foreign_toplevel_manager_get: + * + * Returns: (transfer full) (nullable): A new toplevel manager, or a reference to + * the existing one, if the + * [`wlr_foreign_toplevel_management_unstable_v1`](https://wayland.app/protocols/wlr-foreign-toplevel-management-unstable-v1) + * protocol is supported. %NULL otherwise. To be released with g_object_unref() when + * no longer used. + **/ +XfwlForeignToplevelManager * +xfwl_foreign_toplevel_manager_get (void) +{ + struct zwlr_foreign_toplevel_manager_v1 *wl_manager; + + if (global_manager != NULL) + return g_object_ref (global_manager); + + wl_manager = xfwl_registry_bind (zwlr_foreign_toplevel_manager_v1); + if (wl_manager == NULL) + return NULL; + + global_manager = g_object_new (XFWL_TYPE_FOREIGN_TOPLEVEL_MANAGER, + "wl-manager", wl_manager, NULL); + + return global_manager; +} + + + +/** + * xfwl_foreign_toplevel_manager_get_wl_manager: + * @manager: an #XfwlForeignToplevelManager + * + * Returns: (transfer none): The underlying + * [`struct zwlr_foreign_toplevel_manager_v1`](https://wayland.app/protocols/wlr-foreign-toplevel-management-unstable-v1#zwlr_foreign_toplevel_manager_v1) + * Wayland object. + **/ +struct zwlr_foreign_toplevel_manager_v1 * +xfwl_foreign_toplevel_manager_get_wl_manager (XfwlForeignToplevelManager *manager) +{ + g_return_val_if_fail (XFWL_IS_FOREIGN_TOPLEVEL_MANAGER (manager), NULL); + + return manager->wl_manager; +} + + + +/** + * xfwl_foreign_toplevel_manager_get_toplevels: + * @manager: an #XfwlForeignToplevelManager + * + * Returns: (element-type XfwlForeignToplevel) (transfer container): The toplevel list, + * to be freed with g_list_free() when no longer used. + **/ +GList * +xfwl_foreign_toplevel_manager_get_toplevels (XfwlForeignToplevelManager *manager) +{ + g_return_val_if_fail (XFWL_IS_FOREIGN_TOPLEVEL_MANAGER (manager), NULL); + + return g_hash_table_get_values (manager->toplevels); +} + + + +/** + * xfwl_foreign_toplevel_manager_get_active: + * @manager: an #XfwlForeignToplevelManager + * + * Returns: (transfer none) (nullable): The active toplevel or %NULL if none is active. + **/ +XfwlForeignToplevel * +xfwl_foreign_toplevel_manager_get_active (XfwlForeignToplevelManager *manager) +{ + g_return_val_if_fail (XFWL_IS_FOREIGN_TOPLEVEL_MANAGER (manager), NULL); + + return manager->active; +} diff --git a/libxfce4panel/xfwl-foreign-toplevel-manager.h b/libxfce4panel/xfwl-foreign-toplevel-manager.h new file mode 100644 index 000000000..cba24a3a6 --- /dev/null +++ b/libxfce4panel/xfwl-foreign-toplevel-manager.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 Gaël Bonithon + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __XFWL_FOREIGN_TOPLEVEL_MANAGER_H__ +#define __XFWL_FOREIGN_TOPLEVEL_MANAGER_H__ + +#include +#include +#include "xfwl-foreign-toplevel.h" + +G_BEGIN_DECLS + +#define XFWL_TYPE_FOREIGN_TOPLEVEL_MANAGER (xfwl_foreign_toplevel_manager_get_type ()) +G_DECLARE_FINAL_TYPE (XfwlForeignToplevelManager, xfwl_foreign_toplevel_manager, XFWL, FOREIGN_TOPLEVEL_MANAGER, GObject) + +XfwlForeignToplevelManager *xfwl_foreign_toplevel_manager_get (void); + +struct zwlr_foreign_toplevel_manager_v1 *xfwl_foreign_toplevel_manager_get_wl_manager (XfwlForeignToplevelManager *manager); + +GList *xfwl_foreign_toplevel_manager_get_toplevels (XfwlForeignToplevelManager *manager); + +XfwlForeignToplevel *xfwl_foreign_toplevel_manager_get_active (XfwlForeignToplevelManager *manager); + +G_END_DECLS + +#endif /* !__XFWL_FOREIGN_TOPLEVEL_MANAGER_H__ */ diff --git a/libxfce4panel/xfwl-foreign-toplevel.c b/libxfce4panel/xfwl-foreign-toplevel.c new file mode 100644 index 000000000..c581783bd --- /dev/null +++ b/libxfce4panel/xfwl-foreign-toplevel.c @@ -0,0 +1,517 @@ +/* + * Copyright (C) 2022 Gaël Bonithon + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include "xfwl-foreign-toplevel.h" +#include "protocols/wlr-foreign-toplevel-management-unstable-v1-client.h" +#include "libxfce4panel-enum-types.h" + +/** + * SECTION: xfwl-foreign-toplevel + * @title: XfwlForeignToplevel + * @short_description: GObject wrapper around `struct zwlr_foreign_toplevel_handle_v1` + * @include: libxfce4panel/libxfce4panel.h + * + * See #XfwlForeignToplevelManager to know how to obtain a #XfwlForeignToplevel. + **/ + +static void xfwl_foreign_toplevel_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static void xfwl_foreign_toplevel_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void xfwl_foreign_toplevel_constructed (GObject *object); +static void xfwl_foreign_toplevel_finalize (GObject *object); + +static void xfwl_foreign_toplevel_title (void *data, + struct zwlr_foreign_toplevel_handle_v1 *wl_toplevel, + const char *title); +static void xfwl_foreign_toplevel_app_id (void *data, + struct zwlr_foreign_toplevel_handle_v1 *wl_toplevel, + const char *app_id); +static void xfwl_foreign_toplevel_output_enter (void *data, + struct zwlr_foreign_toplevel_handle_v1 *wl_toplevel, + struct wl_output *output); +static void xfwl_foreign_toplevel_output_leave (void *data, + struct zwlr_foreign_toplevel_handle_v1 *wl_toplevel, + struct wl_output *output); +static void xfwl_foreign_toplevel_state (void *data, + struct zwlr_foreign_toplevel_handle_v1 *wl_toplevel, + struct wl_array *states); +static void xfwl_foreign_toplevel_done (void *data, + struct zwlr_foreign_toplevel_handle_v1 *wl_toplevel); +static void xfwl_foreign_toplevel_closed (void *data, + struct zwlr_foreign_toplevel_handle_v1 *wl_toplevel); +static void xfwl_foreign_toplevel_parent (void *data, + struct zwlr_foreign_toplevel_handle_v1 *wl_toplevel, + struct zwlr_foreign_toplevel_handle_v1 *wl_parent); + + + +enum +{ + PROP_0, + PROP_WL_TOPLEVEL, + PROP_TITLE, + PROP_APP_ID, + PROP_MONITORS, + PROP_STATE, + PROP_PARENT, + N_PROPERTIES +}; + +enum +{ + CLOSED, + N_SIGNALS +}; + +/** + * XfwlForeignToplevel: + * + * An opaque structure wrapping a + * [`struct zwlr_foreign_toplevel_handle_v1`](https://wayland.app/protocols/wlr-foreign-toplevel-management-unstable-v1#zwlr_foreign_toplevel_handle_v1). + **/ +struct _XfwlForeignToplevel +{ + GObject __parent__; + + struct zwlr_foreign_toplevel_handle_v1 *wl_toplevel; + gchar *title, *app_id; + GList *monitors; + XfwlForeignToplevelState state; + XfwlForeignToplevel *parent; +}; + +static guint toplevel_signals[N_SIGNALS]; +static GParamSpec *toplevel_props[N_PROPERTIES] = { NULL, }; + +static const struct zwlr_foreign_toplevel_handle_v1_listener wl_toplevel_listener = +{ + xfwl_foreign_toplevel_title, + xfwl_foreign_toplevel_app_id, + xfwl_foreign_toplevel_output_enter, + xfwl_foreign_toplevel_output_leave, + xfwl_foreign_toplevel_state, + xfwl_foreign_toplevel_done , + xfwl_foreign_toplevel_closed, + xfwl_foreign_toplevel_parent, +}; + + + +G_DEFINE_TYPE (XfwlForeignToplevel, xfwl_foreign_toplevel, G_TYPE_OBJECT) + + + +static void +xfwl_foreign_toplevel_class_init (XfwlForeignToplevelClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->get_property = xfwl_foreign_toplevel_get_property; + gobject_class->set_property = xfwl_foreign_toplevel_set_property; + gobject_class->constructed = xfwl_foreign_toplevel_constructed; + gobject_class->finalize = xfwl_foreign_toplevel_finalize; + + toplevel_props[PROP_WL_TOPLEVEL] = + g_param_spec_pointer ("wl-toplevel", "Wayland toplevel", "The underlying Wayland object", + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY); + + toplevel_props[PROP_TITLE] = + g_param_spec_string ("title", "Title", "The toplevel title", NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + toplevel_props[PROP_APP_ID] = + g_param_spec_string ("app-id", "Application ID", "The toplevel application ID", NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + toplevel_props[PROP_MONITORS] = + g_param_spec_pointer ("monitors", "Monitors", + "The list of monitors on which the toplevel appears", + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + toplevel_props[PROP_STATE] = + g_param_spec_flags ("state", "State", "The toplevel state", + XFWL_TYPE_FOREIGN_TOPLEVEL_STATE, XFWL_FOREIGN_TOPLEVEL_STATE_NONE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + toplevel_props[PROP_PARENT] = + g_param_spec_object ("parent", "Parent", "The parent toplevel", XFWL_TYPE_FOREIGN_TOPLEVEL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (gobject_class, N_PROPERTIES, toplevel_props); + + /** + * XfwlForeignToplevel::closed: + * @toplevel: an #XfwlForeignToplevel + * + * This signal means the actual toplevel has been destroyed. It is guaranteed there + * won't be any more signals from @toplevel, and any requests will be ignored. When + * all handlers connected to this signal have been called, @toplevel is released. + **/ + toplevel_signals[CLOSED] = + g_signal_new (g_intern_static_string ("closed"), G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); +} + + + +static void +xfwl_foreign_toplevel_init (XfwlForeignToplevel *toplevel) +{ +} + + + +static void +xfwl_foreign_toplevel_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + XfwlForeignToplevel *toplevel = XFWL_FOREIGN_TOPLEVEL (object); + + switch (prop_id) + { + case PROP_WL_TOPLEVEL: + g_value_set_pointer (value, toplevel->wl_toplevel); + break; + + case PROP_TITLE: + g_value_set_string (value, toplevel->title); + break; + + case PROP_APP_ID: + g_value_set_string (value, toplevel->app_id); + break; + + case PROP_MONITORS: + g_value_set_pointer (value, toplevel->monitors); + break; + + case PROP_STATE: + g_value_set_flags (value, toplevel->state); + break; + + case PROP_PARENT: + g_value_set_object (value, toplevel->parent); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + + +static void +xfwl_foreign_toplevel_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + XfwlForeignToplevel *toplevel = XFWL_FOREIGN_TOPLEVEL (object); + + switch (prop_id) + { + case PROP_WL_TOPLEVEL: + toplevel->wl_toplevel = g_value_get_pointer (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + + +static void +xfwl_foreign_toplevel_constructed (GObject *object) +{ + XfwlForeignToplevel *toplevel = XFWL_FOREIGN_TOPLEVEL (object); + + /* set properties */ + zwlr_foreign_toplevel_handle_v1_add_listener (toplevel->wl_toplevel, &wl_toplevel_listener, toplevel); + wl_display_roundtrip (gdk_wayland_display_get_wl_display (gdk_display_get_default ())); + + G_OBJECT_CLASS (xfwl_foreign_toplevel_parent_class)->constructed (object); +} + + + +static void +xfwl_foreign_toplevel_finalize (GObject *object) +{ + XfwlForeignToplevel *toplevel = XFWL_FOREIGN_TOPLEVEL (object); + + g_free (toplevel->title); + g_free (toplevel->app_id); + g_list_free (toplevel->monitors); + if (toplevel->parent != NULL) + g_object_unref (toplevel->parent); + + G_OBJECT_CLASS (xfwl_foreign_toplevel_parent_class)->finalize (object); +} + + + +static void +xfwl_foreign_toplevel_title (void *data, + struct zwlr_foreign_toplevel_handle_v1 *wl_toplevel, + const char *title) +{ + XfwlForeignToplevel *toplevel = data; + + g_free (toplevel->title); + toplevel->title = g_strdup (title); + + g_object_notify_by_pspec (G_OBJECT (toplevel), toplevel_props[PROP_TITLE]); +} + + + +static void +xfwl_foreign_toplevel_app_id (void *data, + struct zwlr_foreign_toplevel_handle_v1 *wl_toplevel, + const char *app_id) +{ + XfwlForeignToplevel *toplevel = data; + + g_free (toplevel->app_id); + toplevel->app_id = g_strdup (app_id); + + g_object_notify_by_pspec (G_OBJECT (toplevel), toplevel_props[PROP_APP_ID]); +} + + + +static void +xfwl_foreign_toplevel_output_enter (void *data, + struct zwlr_foreign_toplevel_handle_v1 *wl_toplevel, + struct wl_output *output) +{ + XfwlForeignToplevel *toplevel = data; + GdkDisplay *display; + GdkMonitor *monitor; + gint n_monitors; + + display = gdk_display_get_default (); + n_monitors = gdk_display_get_n_monitors (display); + for (gint n = 0; n < n_monitors; n++) + { + monitor = gdk_display_get_monitor (display, n); + if (output == gdk_wayland_monitor_get_wl_output (monitor)) + { + toplevel->monitors = g_list_prepend (toplevel->monitors, monitor); + break; + } + } + + g_object_notify_by_pspec (G_OBJECT (toplevel), toplevel_props[PROP_MONITORS]); +} + + + +static void +xfwl_foreign_toplevel_output_leave (void *data, + struct zwlr_foreign_toplevel_handle_v1 *wl_toplevel, + struct wl_output *output) +{ + XfwlForeignToplevel *toplevel = data; + GdkDisplay *display; + GdkMonitor *monitor; + gint n_monitors; + + display = gdk_display_get_default (); + n_monitors = gdk_display_get_n_monitors (display); + for (gint n = 0; n < n_monitors; n++) + { + monitor = gdk_display_get_monitor (display, n); + if (output == gdk_wayland_monitor_get_wl_output (monitor)) + { + toplevel->monitors = g_list_remove (toplevel->monitors, monitor); + break; + } + } + + g_object_notify_by_pspec (G_OBJECT (toplevel), toplevel_props[PROP_MONITORS]); +} + + + +static void +xfwl_foreign_toplevel_state (void *data, + struct zwlr_foreign_toplevel_handle_v1 *wl_toplevel, + struct wl_array *states) +{ + XfwlForeignToplevel *toplevel = data; + enum zwlr_foreign_toplevel_handle_v1_state *state; + + toplevel->state = XFWL_FOREIGN_TOPLEVEL_STATE_NONE; + wl_array_for_each (state, states) + toplevel->state |= 1 << *state; + + g_object_notify_by_pspec (G_OBJECT (toplevel), toplevel_props[PROP_STATE]); +} + + + +static void +xfwl_foreign_toplevel_done (void *data, + struct zwlr_foreign_toplevel_handle_v1 *wl_toplevel) +{ +} + + + +static void +xfwl_foreign_toplevel_closed (void *data, + struct zwlr_foreign_toplevel_handle_v1 *wl_toplevel) +{ + g_signal_emit (data, toplevel_signals[CLOSED], 0); +} + + + +static void +xfwl_foreign_toplevel_parent (void *data, + struct zwlr_foreign_toplevel_handle_v1 *wl_toplevel, + struct zwlr_foreign_toplevel_handle_v1 *wl_parent) +{ + XfwlForeignToplevel *toplevel = data; + + if (toplevel->parent != NULL) + { + g_object_unref (toplevel->parent); + toplevel->parent = NULL; + } + + if (wl_parent != NULL) + toplevel->parent = g_object_new (XFWL_TYPE_FOREIGN_TOPLEVEL, "wl-toplevel", wl_parent, NULL); + + g_object_notify_by_pspec (G_OBJECT (toplevel), toplevel_props[PROP_PARENT]); +} + + + +/** + * xfwl_foreign_toplevel_get_wl_toplevel: + * @toplevel: an #XfwlForeignToplevel + * + * Returns: (transfer none): The underlying + * [`struct zwlr_foreign_toplevel_handle_v1`](https://wayland.app/protocols/wlr-foreign-toplevel-management-unstable-v1#zwlr_foreign_toplevel_handle_v1) + * Wayland object. + **/ +struct zwlr_foreign_toplevel_handle_v1 * +xfwl_foreign_toplevel_get_wl_toplevel (XfwlForeignToplevel *toplevel) +{ + g_return_val_if_fail (XFWL_IS_FOREIGN_TOPLEVEL (toplevel), NULL); + + return toplevel->wl_toplevel; +} + + + +/** + * xfwl_foreign_toplevel_get_title: + * @toplevel: an #XfwlForeignToplevel + * + * Returns: (transfer none) (nullable): The toplevel title or %NULL if there is none. + **/ +const gchar * +xfwl_foreign_toplevel_get_title (XfwlForeignToplevel *toplevel) +{ + g_return_val_if_fail (XFWL_IS_FOREIGN_TOPLEVEL (toplevel), NULL); + + return toplevel->title; +} + + + +/** + * xfwl_foreign_toplevel_get_app_id: + * @toplevel: an #XfwlForeignToplevel + * + * Returns: (transfer none) (nullable): The toplevel application ID or %NULL if there + * is none. + **/ +const gchar * +xfwl_foreign_toplevel_get_app_id (XfwlForeignToplevel *toplevel) +{ + g_return_val_if_fail (XFWL_IS_FOREIGN_TOPLEVEL (toplevel), NULL); + + return toplevel->app_id; +} + + + +/** + * xfwl_foreign_toplevel_get_monitors: + * @toplevel: an #XfwlForeignToplevel + * + * Returns: (element-type GdkMonitor) (transfer none): The list of monitors on which + * the toplevel appears. + **/ +GList * +xfwl_foreign_toplevel_get_monitors (XfwlForeignToplevel *toplevel) +{ + g_return_val_if_fail (XFWL_IS_FOREIGN_TOPLEVEL (toplevel), NULL); + + return toplevel->monitors; +} + + + +/** + * xfwl_foreign_toplevel_get_state: + * @toplevel: an #XfwlForeignToplevel + * + * Returns: The toplevel state. + **/ +XfwlForeignToplevelState +xfwl_foreign_toplevel_get_state (XfwlForeignToplevel *toplevel) +{ + g_return_val_if_fail (XFWL_IS_FOREIGN_TOPLEVEL (toplevel), XFWL_FOREIGN_TOPLEVEL_STATE_NONE); + + return toplevel->state; +} + + + +/** + * xfwl_foreign_toplevel_get_parent: + * @toplevel: an #XfwlForeignToplevel + * + * Returns: (transfer none) (nullable): The parent toplevel or %NULL if there is none. + **/ +XfwlForeignToplevel * +xfwl_foreign_toplevel_get_parent (XfwlForeignToplevel *toplevel) +{ + g_return_val_if_fail (XFWL_IS_FOREIGN_TOPLEVEL (toplevel), NULL); + + return toplevel->parent; +} diff --git a/libxfce4panel/xfwl-foreign-toplevel.h b/libxfce4panel/xfwl-foreign-toplevel.h new file mode 100644 index 000000000..77c2f6753 --- /dev/null +++ b/libxfce4panel/xfwl-foreign-toplevel.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2022 Gaël Bonithon + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __XFWL_FOREIGN_TOPLEVEL_H__ +#define __XFWL_FOREIGN_TOPLEVEL_H__ + +#include +#include + +G_BEGIN_DECLS + +#define XFWL_TYPE_FOREIGN_TOPLEVEL (xfwl_foreign_toplevel_get_type ()) +G_DECLARE_FINAL_TYPE (XfwlForeignToplevel, xfwl_foreign_toplevel, XFWL, FOREIGN_TOPLEVEL, GObject) + +/** + * XfwlForeignToplevelState: + * @XFWL_FOREIGN_TOPLEVEL_STATE_NONE: no particular state + * @XFWL_FOREIGN_TOPLEVEL_STATE_MAXIMIZED: the toplevel is maximized + * @XFWL_FOREIGN_TOPLEVEL_STATE_MINIMIZED: the toplevel is minimized + * @XFWL_FOREIGN_TOPLEVEL_STATE_ACTIVATED: the toplevel is active + * @XFWL_FOREIGN_TOPLEVEL_STATE_FULLSCREEN: the toplevel is fullscreen + * + * The different states that a toplevel can have, some of them mutually exclusive. + **/ +typedef enum /*< flags,prefix=XFWL >*/ +{ + XFWL_FOREIGN_TOPLEVEL_STATE_NONE = 0, + XFWL_FOREIGN_TOPLEVEL_STATE_MAXIMIZED = 1 << 0, + XFWL_FOREIGN_TOPLEVEL_STATE_MINIMIZED = 1 << 1, + XFWL_FOREIGN_TOPLEVEL_STATE_ACTIVATED = 1 << 2, + XFWL_FOREIGN_TOPLEVEL_STATE_FULLSCREEN = 1 << 3 +} +XfwlForeignToplevelState; + +struct zwlr_foreign_toplevel_handle_v1 *xfwl_foreign_toplevel_get_wl_toplevel (XfwlForeignToplevel *toplevel); + +const gchar *xfwl_foreign_toplevel_get_title (XfwlForeignToplevel *toplevel); + +const gchar *xfwl_foreign_toplevel_get_app_id (XfwlForeignToplevel *toplevel); + +GList *xfwl_foreign_toplevel_get_monitors (XfwlForeignToplevel *toplevel); + +XfwlForeignToplevelState xfwl_foreign_toplevel_get_state (XfwlForeignToplevel *toplevel); + +XfwlForeignToplevel *xfwl_foreign_toplevel_get_parent (XfwlForeignToplevel *toplevel); + +G_END_DECLS + +#endif /* !__XFWL_FOREIGN_TOPLEVEL_H__ */ diff --git a/protocols/Makefile.am b/protocols/Makefile.am index 2c19c4a5c..3980c184a 100644 --- a/protocols/Makefile.am +++ b/protocols/Makefile.am @@ -38,3 +38,6 @@ BUILT_SOURCES = \ EXTRA_DIST = \ wlr-foreign-toplevel-management-unstable-v1.xml + +# required for make distcheck +dist-hook: all -- GitLab From db46b019f4fd45481fa79d956c9a92b247c1ea13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Bonithon?= Date: Sun, 11 Sep 2022 19:38:55 +0200 Subject: [PATCH 16/22] wayland: panel: Use Xfwl objects instead of Wayland code --- panel/Makefile.am | 6 +- panel/panel-window.c | 338 ++++++++++++++++--------------------------- 2 files changed, 126 insertions(+), 218 deletions(-) diff --git a/panel/Makefile.am b/panel/Makefile.am index c0bd8b206..29523a6d1 100644 --- a/panel/Makefile.am +++ b/panel/Makefile.am @@ -64,7 +64,6 @@ xfce4_panel_CFLAGS = \ $(XFCONF_CFLAGS) \ $(LIBX11_CFLAGS) \ $(GTK_LAYER_SHELL_CFLAGS) \ - $(WAYLAND_CLIENT_CFLAGS) \ $(PLATFORM_CFLAGS) xfce4_panel_LDFLAGS = \ @@ -74,7 +73,6 @@ xfce4_panel_LDFLAGS = \ xfce4_panel_LDADD = \ $(top_builddir)/libxfce4panel/libxfce4panel-$(LIBXFCE4PANEL_VERSION_API).la \ $(top_builddir)/common/libpanel-common.la \ - $(top_builddir)/protocols/libpanel-protocols.la \ $(GTK_LIBS) \ $(GMODULE_LIBS) \ $(GIO_UNIX_LIBS) \ @@ -84,13 +82,11 @@ xfce4_panel_LDADD = \ $(XFCONF_LIBS) \ $(LIBX11_LIBS) \ $(GTK_LAYER_SHELL_LIBS) \ - $(WAYLAND_CLIENT_LIBS) \ -lm xfce4_panel_DEPENDENCIES = \ $(top_builddir)/libxfce4panel/libxfce4panel-$(LIBXFCE4PANEL_VERSION_API).la \ - $(top_builddir)/common/libpanel-common.la \ - $(top_builddir)/protocols/libpanel-protocols.la + $(top_builddir)/common/libpanel-common.la if MAINTAINER_MODE diff --git a/panel/panel-window.c b/panel/panel-window.c index a906fa28a..ad4401146 100644 --- a/panel/panel-window.c +++ b/panel/panel-window.c @@ -37,7 +37,6 @@ #include #include -#include #include #include @@ -190,27 +189,7 @@ static void panel_window_plugin_set_nrows (GtkWidget gpointer user_data); static void panel_window_plugin_set_screen_position (GtkWidget *widget, gpointer user_data); -static void panel_window_wl_toplevel_manager_toplevel (void *data, - struct zwlr_foreign_toplevel_manager_v1 *manager, - struct zwlr_foreign_toplevel_handle_v1 *toplevel); -static void panel_window_wl_toplevel_manager_finished (void *data, - struct zwlr_foreign_toplevel_manager_v1 *manager); -static void panel_window_wl_toplevel_output_enter (void *data, - struct zwlr_foreign_toplevel_handle_v1 *toplevel, - struct wl_output *output); -static void panel_window_wl_toplevel_output_leave (void *data, - struct zwlr_foreign_toplevel_handle_v1 *toplevel, - struct wl_output *output); -static void panel_window_wl_toplevel_state (void *data, - struct zwlr_foreign_toplevel_handle_v1 *toplevel, - struct wl_array *state); -static void panel_window_wl_toplevel_done (void *data, - struct zwlr_foreign_toplevel_handle_v1 *toplevel); -static void panel_window_wl_toplevel_closed (void *data, - struct zwlr_foreign_toplevel_handle_v1 *toplevel); -static void panel_window_wl_toggle_notify (gpointer data, - GObject *object, - gboolean is_last_ref); +static void panel_window_toplevel_manager_active (PanelWindow *window); @@ -397,8 +376,8 @@ struct _PanelWindow gint grab_y; /* Wayland */ - struct zwlr_foreign_toplevel_manager_v1 *wl_toplevel_manager; - GHashTable *wl_toplevels; + XfwlForeignToplevelManager *wl_toplevel_manager; + XfwlForeignToplevel *wl_active; gboolean wl_active_is_maximized; }; @@ -406,12 +385,6 @@ struct _PanelWindow typedef PanelWindow XfcePanelWindow; typedef PanelWindowClass XfcePanelWindowClass; -typedef struct -{ - GSList *outputs; - guint state; -} WlToplevelProps; - static GdkAtom cardinal_atom = 0; @@ -420,25 +393,6 @@ static GdkAtom net_wm_strut_atom = 0; #endif static GdkAtom net_wm_strut_partial_atom = 0; -static const struct zwlr_foreign_toplevel_manager_v1_listener wl_toplevel_manager_listener = -{ - panel_window_wl_toplevel_manager_toplevel, - panel_window_wl_toplevel_manager_finished -}; - -static void wl_unused (void) {}; -static const struct zwlr_foreign_toplevel_handle_v1_listener wl_toplevel_listener = -{ - (void (*) (void *, struct zwlr_foreign_toplevel_handle_v1 *, const char *)) wl_unused, - (void (*) (void *, struct zwlr_foreign_toplevel_handle_v1 *, const char *)) wl_unused, - panel_window_wl_toplevel_output_enter, - panel_window_wl_toplevel_output_leave, - panel_window_wl_toplevel_state, - panel_window_wl_toplevel_done, - panel_window_wl_toplevel_closed, - (void (*) (void *, struct zwlr_foreign_toplevel_handle_v1 *, struct zwlr_foreign_toplevel_handle_v1 *)) wl_unused -}; - G_DEFINE_TYPE (XfcePanelWindow, panel_window, PANEL_TYPE_BASE_WINDOW) @@ -658,7 +612,7 @@ panel_window_init (PanelWindow *window) window->grab_x = 0; window->grab_y = 0; window->wl_toplevel_manager = NULL; - window->wl_toplevels = NULL; + window->wl_active = NULL; window->wl_active_is_maximized = FALSE; /* not resizable, so allocation will follow size request */ @@ -1011,6 +965,9 @@ panel_window_finalize (GObject *object) if (window->autohide_window != NULL) gtk_widget_destroy (window->autohide_window); + if (window->wl_toplevel_manager != NULL) + g_object_unref (window->wl_toplevel_manager); + g_free (window->output_name); (*G_OBJECT_CLASS (panel_window_parent_class)->finalize) (object); @@ -3227,36 +3184,15 @@ panel_window_autohide_event (GtkWidget *widget, -static void -panel_window_wl_toplevel_props_free (gpointer data) -{ - WlToplevelProps *props = data; - - g_slist_free (props->outputs); - g_free (props); -} - - - static gboolean -panel_window_wl_toplevel_manager_init (gpointer data) +panel_window_toplevel_manager_connect (gpointer data) { PanelWindow *window = data; - window->wl_toplevel_manager = xfwl_registry_bind (zwlr_foreign_toplevel_manager_v1); - if (window->wl_toplevel_manager != NULL) - { - window->wl_toplevels = - g_hash_table_new_full (g_direct_hash, g_direct_equal, - (GDestroyNotify) zwlr_foreign_toplevel_handle_v1_destroy, - panel_window_wl_toplevel_props_free); - g_object_add_toggle_ref (G_OBJECT (window), panel_window_wl_toggle_notify, - window->wl_toplevel_manager); - zwlr_foreign_toplevel_manager_v1_add_listener (window->wl_toplevel_manager, - &wl_toplevel_manager_listener, - window); - wl_display_roundtrip (gdk_wayland_display_get_wl_display (window->display)); - } + panel_window_toplevel_manager_active (window); + g_signal_connect_object (window->wl_toplevel_manager, "notify::active", + G_CALLBACK (panel_window_toplevel_manager_active), + window, G_CONNECT_SWAPPED); return FALSE; } @@ -3286,7 +3222,18 @@ panel_window_set_autohide_behavior (PanelWindow *window, /* reset Wayland toplevel manager if needed */ if (window->autohide_behavior != AUTOHIDE_BEHAVIOR_INTELLIGENTLY && window->wl_toplevel_manager != NULL) - panel_window_wl_toggle_notify (window->wl_toplevel_manager, G_OBJECT (window), TRUE); + { + if (window->wl_active != NULL) + { + g_signal_handlers_disconnect_by_data (window->wl_active, window); + window->wl_active = NULL; + } + + g_signal_handlers_disconnect_by_func (window->wl_toplevel_manager, + panel_window_toplevel_manager_active, window); + g_object_unref (window->wl_toplevel_manager); + window->wl_toplevel_manager = NULL; + } /* create an autohide window only if we are autohiding at all */ if (window->autohide_behavior != AUTOHIDE_BEHAVIOR_NEVER) @@ -3376,11 +3323,15 @@ panel_window_set_autohide_behavior (PanelWindow *window, if (gtk_layer_is_supported ()) { - /* wait for panel position to be initialized */ - if (window->base_x == -1 && window->base_y == -1) - g_idle_add (panel_window_wl_toplevel_manager_init, window); - else - panel_window_wl_toplevel_manager_init (window); + window->wl_toplevel_manager = xfwl_foreign_toplevel_manager_get (); + if (window->wl_toplevel_manager != NULL) + { + /* wait for panel position to be initialized */ + if (window->base_x == -1 && window->base_y == -1) + g_idle_add (panel_window_toplevel_manager_connect, window); + else + panel_window_toplevel_manager_connect (window); + } } } } @@ -3803,98 +3754,18 @@ panel_window_plugin_set_screen_position (GtkWidget *widget, -static void -panel_window_wl_toplevel_manager_toplevel (void *data, - struct zwlr_foreign_toplevel_manager_v1 *manager, - struct zwlr_foreign_toplevel_handle_v1 *toplevel) -{ - PanelWindow *window = data; - WlToplevelProps *props; - - props = g_new0 (WlToplevelProps, 1); - g_hash_table_insert (window->wl_toplevels, toplevel, props); - zwlr_foreign_toplevel_handle_v1_add_listener (toplevel, &wl_toplevel_listener, window); - wl_display_roundtrip (gdk_wayland_display_get_wl_display (window->display)); -} - - - -static void -panel_window_wl_toplevel_manager_finished (void *data, - struct zwlr_foreign_toplevel_manager_v1 *manager) -{ - PanelWindow *window = data; - - g_hash_table_destroy (window->wl_toplevels); - zwlr_foreign_toplevel_manager_v1_destroy (manager); - g_object_unref (window); -} - - - -static void -panel_window_wl_toplevel_output_enter (void *data, - struct zwlr_foreign_toplevel_handle_v1 *toplevel, - struct wl_output *output) -{ - PanelWindow *window = data; - WlToplevelProps *props; - - props = g_hash_table_lookup (window->wl_toplevels, toplevel); - props->outputs = g_slist_prepend (props->outputs, output); -} - - - -static void -panel_window_wl_toplevel_output_leave (void *data, - struct zwlr_foreign_toplevel_handle_v1 *toplevel, - struct wl_output *output) -{ - PanelWindow *window = data; - WlToplevelProps *props; - - props = g_hash_table_lookup (window->wl_toplevels, toplevel); - props->outputs = g_slist_remove (props->outputs, output); -} - - - -static void -panel_window_wl_toplevel_state (void *data, - struct zwlr_foreign_toplevel_handle_v1 *toplevel, - struct wl_array *states) -{ - PanelWindow *window = data; - enum zwlr_foreign_toplevel_handle_v1_state *state; - WlToplevelProps *props; - - /* store toplevel state as flags */ - props = g_hash_table_lookup (window->wl_toplevels, toplevel); - props->state = 0; - wl_array_for_each (state, states) - props->state |= 1 << *state; -} - - - -static void -panel_window_wl_toplevel_done (void *data, - struct zwlr_foreign_toplevel_handle_v1 *toplevel) +static gboolean +panel_window_toplevel_on_panel_monitor (PanelWindow *window, + XfwlForeignToplevel *toplevel) { - PanelWindow *window = data; - WlToplevelProps *props; - GdkMonitor *monitor = NULL; - gboolean maximized; + GList *monitors; - /* check if this toplevel and the panel have a common monitor */ - props = g_hash_table_lookup (window->wl_toplevels, toplevel); + monitors = xfwl_foreign_toplevel_get_monitors (toplevel); if (window->span_monitors) { - GdkMonitor *p_monitor; + GdkMonitor *monitor = NULL, *p_monitor; GtkAllocation alloc; gint fixed_dim, step; - gboolean found = FALSE; gtk_widget_get_allocation (GTK_WIDGET (window), &alloc); if (IS_HORIZONTAL (window)) @@ -3908,11 +3779,8 @@ panel_window_wl_toplevel_done (void *data, if (p_monitor != monitor) { monitor = p_monitor; - if (g_slist_find (props->outputs, gdk_wayland_monitor_get_wl_output (monitor))) - { - found = TRUE; - break; - } + if (g_list_find (monitors, monitor)) + return TRUE; } } } @@ -3927,71 +3795,115 @@ panel_window_wl_toplevel_done (void *data, if (p_monitor != monitor) { monitor = p_monitor; - if (g_slist_find (props->outputs, gdk_wayland_monitor_get_wl_output (monitor))) - { - found = TRUE; - break; - } + if (g_list_find (monitors, monitor)) + return TRUE; } } } - - if (! found) - return; } - else + else if (g_list_find (monitors, gtk_layer_get_monitor (GTK_WINDOW (window)))) + return TRUE; + + return FALSE; +} + + + +static void +panel_window_toplevel_active_closed (XfwlForeignToplevel *toplevel, + PanelWindow *window) +{ + GList *toplevels, *lp; + + if (! window->wl_active_is_maximized) + return; + + /* see if there is a maximized window left on a monitor of interest */ + toplevels = xfwl_foreign_toplevel_manager_get_toplevels (window->wl_toplevel_manager); + for (lp = toplevels; lp != NULL; lp = lp->next) + if (lp->data != toplevel + && xfwl_foreign_toplevel_get_state (lp->data) & XFWL_FOREIGN_TOPLEVEL_STATE_MAXIMIZED + && panel_window_toplevel_on_panel_monitor (window, lp->data)) + { + g_signal_handlers_disconnect_by_func (lp->data, panel_window_toplevel_active_closed, window); + g_signal_connect_object (lp->data, "closed", + G_CALLBACK (panel_window_toplevel_active_closed), window, 0); + break; + } + + g_list_free (toplevels); + + if (lp == NULL) { - monitor = gtk_layer_get_monitor (GTK_WINDOW (window)); - if (! g_slist_find (props->outputs, gdk_wayland_monitor_get_wl_output (monitor))) - return; + window->wl_active_is_maximized = FALSE; + panel_window_active_window_geometry_changed (NULL, window); } +} + + + +static void +panel_window_toplevel_active_state (XfwlForeignToplevel *toplevel, + GParamSpec *pspec, + PanelWindow *window) +{ + gboolean maximized; - /* update maximized state if needed */ - if (PANEL_HAS_FLAG (props->state, 1 << ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED)) + maximized = PANEL_HAS_FLAG (xfwl_foreign_toplevel_get_state (toplevel), + XFWL_FOREIGN_TOPLEVEL_STATE_MAXIMIZED); + if (maximized != window->wl_active_is_maximized) { - maximized = - PANEL_HAS_FLAG (props->state, 1 << ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED); - if (maximized != window->wl_active_is_maximized) - { - window->wl_active_is_maximized = maximized; - panel_window_active_window_geometry_changed (NULL, window); - } + window->wl_active_is_maximized = maximized; + panel_window_active_window_geometry_changed (NULL, window); } } static void -panel_window_wl_toplevel_closed (void *data, - struct zwlr_foreign_toplevel_handle_v1 *toplevel) +panel_window_toplevel_active_monitors (XfwlForeignToplevel *toplevel, + GParamSpec *pspec, + PanelWindow *window) { - PanelWindow *window = data; + /* disconnect from any maximized state update signal */ + g_signal_handlers_disconnect_by_func (toplevel, panel_window_toplevel_active_state, window); + g_signal_handlers_disconnect_by_func (toplevel, panel_window_toplevel_active_closed, window); - /* this does not cover the case of closing a maximized active window, which would require - * switching window->wl_active_is_maximized to FALSE, but this is fixed later when switching - * to XfwlForeignToplevel */ - g_hash_table_remove (window->wl_toplevels, toplevel); + /* check if the active toplevel and the panel have a common monitor */ + if (! panel_window_toplevel_on_panel_monitor (window, toplevel)) + return; + + /* connect to relevant signals to update maximized state */ + panel_window_toplevel_active_state (toplevel, NULL, window); + g_signal_connect_object (toplevel, "notify::state", + G_CALLBACK (panel_window_toplevel_active_state), window, 0); + g_signal_connect_object (toplevel, "closed", + G_CALLBACK (panel_window_toplevel_active_closed), window, 0); } static void -panel_window_wl_toggle_notify (gpointer data, - GObject *object, - gboolean is_last_ref) +panel_window_toplevel_manager_active (PanelWindow *window) { - PanelWindow *window = PANEL_WINDOW (object); + /* disconnect from previous active toplevel signals, except "closed" */ + if (window->wl_active != NULL) + { + g_signal_handlers_disconnect_by_func (window->wl_active, + panel_window_toplevel_active_monitors, window); + g_signal_handlers_disconnect_by_func (window->wl_active, + panel_window_toplevel_active_state, window); + } - if (! is_last_ref) + /* update active toplevel */ + window->wl_active = xfwl_foreign_toplevel_manager_get_active (window->wl_toplevel_manager); + if (window->wl_active == NULL) return; - /* remove this callback now to be sure not to pass here again when object is disposed */ - g_object_ref (object); - g_object_remove_toggle_ref (object, panel_window_wl_toggle_notify, data); - - /* this will trigger finished() later */ - zwlr_foreign_toplevel_manager_v1_stop (window->wl_toplevel_manager); - window->wl_toplevel_manager = NULL; + /* first check if active toplevel is on a monitor of interest */ + panel_window_toplevel_active_monitors (window->wl_active, NULL, window); + g_signal_connect_object (window->wl_active, "notify::monitors", + G_CALLBACK (panel_window_toplevel_active_monitors), window, 0); } -- GitLab From f4b3c1946ef039f9e023b77381b136be5d41c9e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Bonithon?= Date: Thu, 15 Sep 2022 19:03:14 +0200 Subject: [PATCH 17/22] wayland: libxfwl: Add protocol setters for XfwlForeignToplevel --- docs/reference/libxfce4panel-sections.txt | 11 ++ libxfce4panel/libxfce4panel.symbols | 11 ++ libxfce4panel/xfwl-foreign-toplevel.c | 214 ++++++++++++++++++++++ libxfce4panel/xfwl-foreign-toplevel.h | 29 ++- 4 files changed, 263 insertions(+), 2 deletions(-) diff --git a/docs/reference/libxfce4panel-sections.txt b/docs/reference/libxfce4panel-sections.txt index 6d8b4086a..c369617df 100644 --- a/docs/reference/libxfce4panel-sections.txt +++ b/docs/reference/libxfce4panel-sections.txt @@ -154,6 +154,17 @@ xfwl_foreign_toplevel_get_app_id xfwl_foreign_toplevel_get_monitors xfwl_foreign_toplevel_get_state xfwl_foreign_toplevel_get_parent +xfwl_foreign_toplevel_maximize +xfwl_foreign_toplevel_unmaximize +xfwl_foreign_toplevel_minimize +xfwl_foreign_toplevel_unminimize +xfwl_foreign_toplevel_activate +xfwl_foreign_toplevel_activate_on_seat +xfwl_foreign_toplevel_close +xfwl_foreign_toplevel_set_rectangle +xfwl_foreign_toplevel_fullscreen +xfwl_foreign_toplevel_fullscreen_on_monitor +xfwl_foreign_toplevel_unfullscreen XFWL_TYPE_FOREIGN_TOPLEVEL
diff --git a/libxfce4panel/libxfce4panel.symbols b/libxfce4panel/libxfce4panel.symbols index eec4f567a..f62c628b6 100644 --- a/libxfce4panel/libxfce4panel.symbols +++ b/libxfce4panel/libxfce4panel.symbols @@ -175,6 +175,17 @@ xfwl_foreign_toplevel_get_app_id xfwl_foreign_toplevel_get_monitors xfwl_foreign_toplevel_get_state xfwl_foreign_toplevel_get_parent +xfwl_foreign_toplevel_maximize +xfwl_foreign_toplevel_unmaximize +xfwl_foreign_toplevel_minimize +xfwl_foreign_toplevel_unminimize +xfwl_foreign_toplevel_activate +xfwl_foreign_toplevel_activate_on_seat +xfwl_foreign_toplevel_close +xfwl_foreign_toplevel_set_rectangle +xfwl_foreign_toplevel_fullscreen +xfwl_foreign_toplevel_fullscreen_on_monitor +xfwl_foreign_toplevel_unfullscreen #endif #endif diff --git a/libxfce4panel/xfwl-foreign-toplevel.c b/libxfce4panel/xfwl-foreign-toplevel.c index c581783bd..844cf4c5a 100644 --- a/libxfce4panel/xfwl-foreign-toplevel.c +++ b/libxfce4panel/xfwl-foreign-toplevel.c @@ -515,3 +515,217 @@ xfwl_foreign_toplevel_get_parent (XfwlForeignToplevel *toplevel) return toplevel->parent; } + + + +/** + * xfwl_foreign_toplevel_maximize: + * @toplevel: an #XfwlForeignToplevel + * + * Requests that the toplevel be maximized. If the maximized state actually changes, + * this will be indicated by #XfwlForeignToplevel:state. + **/ +void +xfwl_foreign_toplevel_maximize (XfwlForeignToplevel *toplevel) +{ + g_return_if_fail (XFWL_IS_FOREIGN_TOPLEVEL (toplevel)); + + zwlr_foreign_toplevel_handle_v1_set_maximized (toplevel->wl_toplevel); +} + + + +/** + * xfwl_foreign_toplevel_unmaximize: + * @toplevel: an #XfwlForeignToplevel + * + * Requests that the toplevel be unmaximized. If the maximized state actually changes, + * this will be indicated by #XfwlForeignToplevel:state. + **/ +void +xfwl_foreign_toplevel_unmaximize (XfwlForeignToplevel *toplevel) +{ + g_return_if_fail (XFWL_IS_FOREIGN_TOPLEVEL (toplevel)); + + zwlr_foreign_toplevel_handle_v1_unset_maximized (toplevel->wl_toplevel); +} + + + +/** + * xfwl_foreign_toplevel_minimize: + * @toplevel: an #XfwlForeignToplevel + * + * Requests that the toplevel be minimized. If the minimized state actually changes, + * this will be indicated by #XfwlForeignToplevel:state. + **/ +void +xfwl_foreign_toplevel_minimize (XfwlForeignToplevel *toplevel) +{ + g_return_if_fail (XFWL_IS_FOREIGN_TOPLEVEL (toplevel)); + + zwlr_foreign_toplevel_handle_v1_set_minimized (toplevel->wl_toplevel); +} + + + +/** + * xfwl_foreign_toplevel_unminimize: + * @toplevel: an #XfwlForeignToplevel + * + * Requests that the toplevel be unminimized. If the minimized state actually changes, + * this will be indicated by #XfwlForeignToplevel:state. + **/ +void +xfwl_foreign_toplevel_unminimize (XfwlForeignToplevel *toplevel) +{ + g_return_if_fail (XFWL_IS_FOREIGN_TOPLEVEL (toplevel)); + + zwlr_foreign_toplevel_handle_v1_unset_minimized (toplevel->wl_toplevel); +} + + + +/** + * xfwl_foreign_toplevel_activate: + * @toplevel: an #XfwlForeignToplevel + * + * See xfwl_foreign_toplevel_activate_on_seat() for a %NULL seat. + **/ +void +xfwl_foreign_toplevel_activate (XfwlForeignToplevel *toplevel) +{ + g_return_if_fail (XFWL_IS_FOREIGN_TOPLEVEL (toplevel)); + + xfwl_foreign_toplevel_activate_on_seat (toplevel, NULL); +} + + + +/** + * xfwl_foreign_toplevel_activate_on_seat: + * @toplevel: an #XfwlForeignToplevel + * @seat: (nullable): the seat on which @toplevel should be activated or %NULL for + * the default seat on the default display + * + * Requests that the toplevel be activated on the given seat. There is no guarantee + * the toplevel will actually be activated. If so, this will be indicated by + * #XfwlForeignToplevel:state. + **/ +void +xfwl_foreign_toplevel_activate_on_seat (XfwlForeignToplevel *toplevel, + GdkSeat *seat) +{ + g_return_if_fail (XFWL_IS_FOREIGN_TOPLEVEL (toplevel)); + g_return_if_fail (seat == NULL || GDK_IS_SEAT (seat)); + + if (seat == NULL) + seat = gdk_display_get_default_seat (gdk_display_get_default ()); + + zwlr_foreign_toplevel_handle_v1_activate (toplevel->wl_toplevel, + gdk_wayland_seat_get_wl_seat (seat)); +} + + + +/** + * xfwl_foreign_toplevel_close: + * @toplevel: an #XfwlForeignToplevel + * + * Requests the toplevel to close itself. There is no guarantee the toplevel will + * actually be destroyed. If so, this will be indicated by #XfwlForeignToplevel::closed. + **/ +void +xfwl_foreign_toplevel_close (XfwlForeignToplevel *toplevel) +{ + g_return_if_fail (XFWL_IS_FOREIGN_TOPLEVEL (toplevel)); + + zwlr_foreign_toplevel_handle_v1_close (toplevel->wl_toplevel); +} + + + +/** + * xfwl_foreign_toplevel_set_rectangle: + * @toplevel: an #XfwlForeignToplevel + * @window: the window to which the coordinates in @rectangle are relative + * @rectangle: the place where the calling app represents @toplevel, e.g. a window + * button in a taskbar + * + * @rectangle corresponds to the place where the calling app represents @toplevel, + * e.g. a window button in a taskbar. It can be used by the compositor as a hint for + * some operations, e.g minimizing. It is however not required to set this, in which + * case the compositor is free to decide some default value. + * + * If more than one rectangle is specified, only the last one is considered. Setting + * `rectangle->width = rectangle->height = 0` removes the already-set rectangle. + **/ +void +xfwl_foreign_toplevel_set_rectangle (XfwlForeignToplevel *toplevel, + GdkWindow *window, + const GdkRectangle *rectangle) +{ + g_return_if_fail (XFWL_IS_FOREIGN_TOPLEVEL (toplevel)); + g_return_if_fail (GDK_IS_WINDOW (window)); + + zwlr_foreign_toplevel_handle_v1_set_rectangle (toplevel->wl_toplevel, + gdk_wayland_window_get_wl_surface (window), + rectangle->x, rectangle->y, + rectangle->width, rectangle->height); +} + + + +/** + * xfwl_foreign_toplevel_fullscreen: + * @toplevel: an #XfwlForeignToplevel + * + * See xfwl_foreign_toplevel_fullscreen_on_monitor() for a %NULL monitor. + **/ +void +xfwl_foreign_toplevel_fullscreen (XfwlForeignToplevel *toplevel) +{ + g_return_if_fail (XFWL_IS_FOREIGN_TOPLEVEL (toplevel)); + + xfwl_foreign_toplevel_fullscreen_on_monitor (toplevel, NULL); +} + + + +/** + * xfwl_foreign_toplevel_fullscreen_on_monitor: + * @toplevel: an #XfwlForeignToplevel + * @monitor: (nullable): the monitor on which @toplevel should be fullscreened or %NULL + * to let the compositor decide + * + * Requests that the toplevel be fullscreened on the given monitor. If the fullscreen + * state and/or the monitors the toplevel is visible on actually change, this will be + * indicated by the #XfwlForeignToplevel:state and #XfwlForeignToplevel:monitors. + **/ +void +xfwl_foreign_toplevel_fullscreen_on_monitor (XfwlForeignToplevel *toplevel, + GdkMonitor *monitor) +{ + g_return_if_fail (XFWL_IS_FOREIGN_TOPLEVEL (toplevel)); + g_return_if_fail (monitor == NULL || GDK_IS_MONITOR (monitor)); + + zwlr_foreign_toplevel_handle_v1_set_fullscreen (toplevel->wl_toplevel, + monitor == NULL ? NULL : gdk_wayland_monitor_get_wl_output (monitor)); +} + + + +/** + * xfwl_foreign_toplevel_unfullscreen: + * @toplevel: an #XfwlForeignToplevel + * + * Requests that the toplevel be unfullscreened. If the fullscreen state actually changes, + * this will be indicated by #XfwlForeignToplevel:state. + **/ +void +xfwl_foreign_toplevel_unfullscreen (XfwlForeignToplevel *toplevel) +{ + g_return_if_fail (XFWL_IS_FOREIGN_TOPLEVEL (toplevel)); + + zwlr_foreign_toplevel_handle_v1_unset_fullscreen (toplevel->wl_toplevel); +} diff --git a/libxfce4panel/xfwl-foreign-toplevel.h b/libxfce4panel/xfwl-foreign-toplevel.h index 77c2f6753..5d7385e6e 100644 --- a/libxfce4panel/xfwl-foreign-toplevel.h +++ b/libxfce4panel/xfwl-foreign-toplevel.h @@ -19,8 +19,7 @@ #ifndef __XFWL_FOREIGN_TOPLEVEL_H__ #define __XFWL_FOREIGN_TOPLEVEL_H__ -#include -#include +#include G_BEGIN_DECLS @@ -59,6 +58,32 @@ XfwlForeignToplevelState xfwl_foreign_toplevel_get_state XfwlForeignToplevel *xfwl_foreign_toplevel_get_parent (XfwlForeignToplevel *toplevel); +void xfwl_foreign_toplevel_maximize (XfwlForeignToplevel *toplevel); + +void xfwl_foreign_toplevel_unmaximize (XfwlForeignToplevel *toplevel); + +void xfwl_foreign_toplevel_minimize (XfwlForeignToplevel *toplevel); + +void xfwl_foreign_toplevel_unminimize (XfwlForeignToplevel *toplevel); + +void xfwl_foreign_toplevel_activate (XfwlForeignToplevel *toplevel); + +void xfwl_foreign_toplevel_activate_on_seat (XfwlForeignToplevel *toplevel, + GdkSeat *seat); + +void xfwl_foreign_toplevel_close (XfwlForeignToplevel *toplevel); + +void xfwl_foreign_toplevel_set_rectangle (XfwlForeignToplevel *toplevel, + GdkWindow *window, + const GdkRectangle *rectangle); + +void xfwl_foreign_toplevel_fullscreen (XfwlForeignToplevel *toplevel); + +void xfwl_foreign_toplevel_fullscreen_on_monitor (XfwlForeignToplevel *toplevel, + GdkMonitor *monitor); + +void xfwl_foreign_toplevel_unfullscreen (XfwlForeignToplevel *toplevel); + G_END_DECLS #endif /* !__XFWL_FOREIGN_TOPLEVEL_H__ */ -- GitLab From ebcd8c08f7a642e91c6ec4f0f4f98213b0f3a90b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Bonithon?= Date: Mon, 19 Sep 2022 12:05:46 +0200 Subject: [PATCH 18/22] wayland: libxfwl: Add show desktop APIs --- docs/reference/libxfce4panel-sections.txt | 2 + libxfce4panel/libxfce4panel.symbols | 2 + libxfce4panel/xfwl-foreign-toplevel-manager.c | 176 ++++++++++++++++++ libxfce4panel/xfwl-foreign-toplevel-manager.h | 5 + 4 files changed, 185 insertions(+) diff --git a/docs/reference/libxfce4panel-sections.txt b/docs/reference/libxfce4panel-sections.txt index c369617df..80fac3416 100644 --- a/docs/reference/libxfce4panel-sections.txt +++ b/docs/reference/libxfce4panel-sections.txt @@ -176,6 +176,8 @@ xfwl_foreign_toplevel_manager_get xfwl_foreign_toplevel_manager_get_wl_manager xfwl_foreign_toplevel_manager_get_toplevels xfwl_foreign_toplevel_manager_get_active +xfwl_foreign_toplevel_manager_get_show_desktop +xfwl_foreign_toplevel_manager_set_show_desktop XFWL_TYPE_FOREIGN_TOPLEVEL_MANAGER
diff --git a/libxfce4panel/libxfce4panel.symbols b/libxfce4panel/libxfce4panel.symbols index f62c628b6..db60a790f 100644 --- a/libxfce4panel/libxfce4panel.symbols +++ b/libxfce4panel/libxfce4panel.symbols @@ -197,5 +197,7 @@ xfwl_foreign_toplevel_manager_get xfwl_foreign_toplevel_manager_get_wl_manager xfwl_foreign_toplevel_manager_get_toplevels xfwl_foreign_toplevel_manager_get_active +xfwl_foreign_toplevel_manager_get_show_desktop +xfwl_foreign_toplevel_manager_set_show_desktop #endif #endif diff --git a/libxfce4panel/xfwl-foreign-toplevel-manager.c b/libxfce4panel/xfwl-foreign-toplevel-manager.c index 61d0dd474..47d4b0daf 100644 --- a/libxfce4panel/xfwl-foreign-toplevel-manager.c +++ b/libxfce4panel/xfwl-foreign-toplevel-manager.c @@ -55,6 +55,8 @@ static void xfwl_foreign_toplevel_manager_finished (void static void xfwl_foreign_toplevel_manager_stop (gpointer data, GObject *object, gboolean is_last_ref); +static void xfwl_foreign_toplevel_manager_desktop_disconnect (gpointer object, + gpointer data); @@ -64,6 +66,7 @@ enum PROP_WL_MANAGER, PROP_TOPLEVELS, PROP_ACTIVE, + PROP_SHOW_DESKTOP, N_PROPERTIES }; @@ -87,6 +90,11 @@ struct _XfwlForeignToplevelManager struct zwlr_foreign_toplevel_manager_v1 *wl_manager; GHashTable *toplevels; XfwlForeignToplevel *active; + + /* show desktop */ + guint show_desktop : 1; + XfwlForeignToplevel *was_active; + GList *minimized; }; static guint manager_signals[N_SIGNALS]; @@ -127,6 +135,10 @@ xfwl_foreign_toplevel_manager_class_init (XfwlForeignToplevelManagerClass *klass g_param_spec_object ("active", "Active", "The active toplevel", XFWL_TYPE_FOREIGN_TOPLEVEL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + manager_props[PROP_SHOW_DESKTOP] = + g_param_spec_boolean ("show-desktop", "Show desktop", "Whether or not to show the desktop", FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + g_object_class_install_properties (gobject_class, N_PROPERTIES, manager_props); /** @@ -185,6 +197,10 @@ xfwl_foreign_toplevel_manager_get_property (GObject *object, g_value_set_object (value, manager->active); break; + case PROP_SHOW_DESKTOP: + g_value_set_boolean (value, manager->show_desktop); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -207,6 +223,10 @@ xfwl_foreign_toplevel_manager_set_property (GObject *object, manager->wl_manager = g_value_get_pointer (value); break; + case PROP_SHOW_DESKTOP: + xfwl_foreign_toplevel_manager_set_show_desktop (manager, g_value_get_boolean (value)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -245,6 +265,8 @@ xfwl_foreign_toplevel_manager_finalize (GObject *object) g_hash_table_destroy (manager->toplevels); zwlr_foreign_toplevel_manager_v1_destroy (manager->wl_manager); + g_list_free (manager->minimized); + G_OBJECT_CLASS (xfwl_foreign_toplevel_manager_parent_class)->finalize (object); } @@ -425,3 +447,157 @@ xfwl_foreign_toplevel_manager_get_active (XfwlForeignToplevelManager *manager) return manager->active; } + + + +/** + * xfwl_foreign_toplevel_manager_get_show_desktop: + * @manager: an #XfwlForeignToplevelManager + * + * Returns: %TRUE if the desktop is shown, %FALSE otherwise. + **/ +gboolean +xfwl_foreign_toplevel_manager_get_show_desktop (XfwlForeignToplevelManager *manager) +{ + g_return_val_if_fail (XFWL_IS_FOREIGN_TOPLEVEL_MANAGER (manager), FALSE); + + return manager->show_desktop; +} + + + +static void +xfwl_foreign_toplevel_manager_desktop_state (XfwlForeignToplevel *toplevel, + GParamSpec *pspec, + XfwlForeignToplevelManager *manager) +{ + XfwlForeignToplevelState state; + gboolean minimized; + + state = xfwl_foreign_toplevel_get_state (toplevel); + minimized = (g_list_find (manager->minimized, toplevel) != NULL); + + /* toplevel has been minimized */ + if (state & XFWL_FOREIGN_TOPLEVEL_STATE_MINIMIZED && ! minimized) + manager->minimized = g_list_prepend (manager->minimized, toplevel); + /* toplevel has been unminimized */ + else if (! (state & XFWL_FOREIGN_TOPLEVEL_STATE_MINIMIZED) && minimized) + { + xfwl_foreign_toplevel_manager_desktop_disconnect (toplevel, manager); + manager->minimized = g_list_remove (manager->minimized, toplevel); + if (manager->minimized == NULL) + { + if (manager->show_desktop) + { + manager->show_desktop = FALSE; + g_object_notify_by_pspec (G_OBJECT (manager), manager_props[PROP_SHOW_DESKTOP]); + } + + if (manager->was_active != NULL) + xfwl_foreign_toplevel_activate (manager->was_active); + } + } +} + + + +static void +xfwl_foreign_toplevel_manager_desktop_closed (XfwlForeignToplevel *toplevel, + XfwlForeignToplevelManager *manager) +{ + manager->minimized = g_list_remove (manager->minimized, toplevel); + if (manager->minimized == NULL && manager->show_desktop) + { + manager->show_desktop = FALSE; + g_object_notify_by_pspec (G_OBJECT (manager), manager_props[PROP_SHOW_DESKTOP]); + } +} + + + +static void +xfwl_foreign_toplevel_manager_desktop_disconnect (gpointer object, + gpointer data) +{ + g_signal_handlers_disconnect_by_func (object, xfwl_foreign_toplevel_manager_desktop_state, data); + g_signal_handlers_disconnect_by_func (object, xfwl_foreign_toplevel_manager_desktop_closed, data); +} + + + +/** + * xfwl_foreign_toplevel_manager_set_show_desktop: + * @manager: an #XfwlForeignToplevelManager + * @show: %TRUE to show the desktop, %FALSE to restore the previous state + * + * Showing the desktop minimizes the toplevels not minimized at the time of the query. + * The reverse process unminimizes those same toplevels, if they have not already been + * unminimized or destroyed. The desktop show state can be tracked via + * #XfwlForeignToplevelManager:show-desktop. + * + * The state of the previously active window is restored upon unminimization, but there + * is no guarantee for the rest of the window stacking order. + * + * A request to switch to the current state is silently ignored. + **/ +void +xfwl_foreign_toplevel_manager_set_show_desktop (XfwlForeignToplevelManager *manager, + gboolean show) +{ + GList *toplevels; + gboolean revert = TRUE; + + g_return_if_fail (XFWL_IS_FOREIGN_TOPLEVEL_MANAGER (manager)); + + if (!!show == manager->show_desktop) + return; + + manager->show_desktop = !!show; + g_object_notify_by_pspec (G_OBJECT (manager), manager_props[PROP_SHOW_DESKTOP]); + + /* unminimize previously minimized toplevels */ + if (! show) + { + for (GList *lp = manager->minimized; lp != NULL; lp = lp->next) + xfwl_foreign_toplevel_unminimize (lp->data); + + return; + } + + /* remove and disconnect from any previously minimized toplevel: probably there is none, + * but it is asynchronous and the compositor might have failed to unminimize some of them */ + g_list_foreach (manager->minimized, xfwl_foreign_toplevel_manager_desktop_disconnect, manager); + g_list_free (manager->minimized); + manager->minimized = NULL; + manager->was_active = NULL; + + /* request for showing the desktop and prepare reverse process */ + toplevels = xfwl_foreign_toplevel_manager_get_toplevels (manager); + for (GList *lp = toplevels; lp != NULL; lp = lp->next) + { + XfwlForeignToplevelState state; + + state = xfwl_foreign_toplevel_get_state (lp->data); + if (! (state & XFWL_FOREIGN_TOPLEVEL_STATE_MINIMIZED)) + { + revert = FALSE; + g_signal_connect (lp->data, "notify::state", + G_CALLBACK (xfwl_foreign_toplevel_manager_desktop_state), manager); + g_signal_connect (lp->data, "closed", + G_CALLBACK (xfwl_foreign_toplevel_manager_desktop_closed), manager); + if (state & XFWL_FOREIGN_TOPLEVEL_STATE_ACTIVATED) + manager->was_active = lp->data; + + xfwl_foreign_toplevel_minimize (lp->data); + } + } + + /* there was no toplevel to minimize, revert state */ + if (revert) + { + manager->show_desktop = FALSE; + g_object_notify_by_pspec (G_OBJECT (manager), manager_props[PROP_SHOW_DESKTOP]); + } + + g_list_free (toplevels); +} diff --git a/libxfce4panel/xfwl-foreign-toplevel-manager.h b/libxfce4panel/xfwl-foreign-toplevel-manager.h index cba24a3a6..c76e68c75 100644 --- a/libxfce4panel/xfwl-foreign-toplevel-manager.h +++ b/libxfce4panel/xfwl-foreign-toplevel-manager.h @@ -36,6 +36,11 @@ GList *xfwl_foreign_toplevel_manager_get_tople XfwlForeignToplevel *xfwl_foreign_toplevel_manager_get_active (XfwlForeignToplevelManager *manager); +gboolean xfwl_foreign_toplevel_manager_get_show_desktop (XfwlForeignToplevelManager *manager); + +void xfwl_foreign_toplevel_manager_set_show_desktop (XfwlForeignToplevelManager *manager, + gboolean show); + G_END_DECLS #endif /* !__XFWL_FOREIGN_TOPLEVEL_MANAGER_H__ */ -- GitLab From da4cb56c95cf2266373cd01b1c8c093cd3d03649 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Bonithon?= Date: Mon, 19 Sep 2022 12:06:58 +0200 Subject: [PATCH 19/22] wayland: showdesktop: Port to Wayland Middle-click window shading is disabled on Wayland, since there seems to be no way to make this request to the compositor. --- panel/panel-module.c | 2 +- plugins/showdesktop/showdesktop.c | 78 ++++++++++++++++++++++++++----- 2 files changed, 68 insertions(+), 12 deletions(-) diff --git a/panel/panel-module.c b/panel/panel-module.c index 692fdbcc7..7574682ac 100644 --- a/panel/panel-module.c +++ b/panel/panel-module.c @@ -340,7 +340,7 @@ panel_module_new_from_desktop_file (const gchar *filename, if (G_LIKELY (module_name != NULL) && ( GDK_IS_X11_DISPLAY (gdk_display_get_default ()) || ( /* Wayland-incompatible embedded plugins */ - g_strstr_len ("pager showdesktop systray tasklist windowmenu", -1, module_name) == NULL + g_strstr_len ("pager systray tasklist windowmenu", -1, module_name) == NULL /* Wayland-incompatible non-embedded plugins */ && g_strstr_len ("clipman", -1, module_name) == NULL ) diff --git a/plugins/showdesktop/showdesktop.c b/plugins/showdesktop/showdesktop.c index 29db159e0..fc1abcc9c 100644 --- a/plugins/showdesktop/showdesktop.c +++ b/plugins/showdesktop/showdesktop.c @@ -57,6 +57,12 @@ static gboolean show_desktop_plugin_drag_motion (GtkWidget gint y, guint time, ShowDesktopPlugin *plugin); +static void show_desktop_plugin_wl_show_desktop (ShowDesktopPlugin *plugin); +static gboolean show_desktop_plugin_get_showing_desktop (ShowDesktopPlugin *plugin, + WnckScreen *screen); +static void show_desktop_plugin_toggle_showing_desktop (ShowDesktopPlugin *plugin, + WnckScreen *screen, + gboolean show); @@ -78,6 +84,9 @@ struct _ShowDesktopPlugin /* the wnck screen */ WnckScreen *wnck_screen; + + /* Wayland */ + XfwlForeignToplevelManager *wl_toplevel_manager; }; @@ -106,10 +115,7 @@ show_desktop_plugin_init (ShowDesktopPlugin *plugin) GtkWidget *button; plugin->wnck_screen = NULL; - - /* monitor screen changes */ - g_signal_connect (G_OBJECT (plugin), "screen-changed", - G_CALLBACK (show_desktop_plugin_screen_changed), NULL); + plugin->wl_toplevel_manager = NULL; /* create the toggle button */ button = plugin->button = xfce_panel_create_toggle_button (); @@ -118,11 +124,24 @@ show_desktop_plugin_init (ShowDesktopPlugin *plugin) gtk_widget_set_name (button, "showdesktop-button"); g_signal_connect (G_OBJECT (button), "toggled", G_CALLBACK (show_desktop_plugin_toggled), plugin); - g_signal_connect (G_OBJECT (button), "button-release-event", - G_CALLBACK (show_desktop_plugin_button_release_event), plugin); xfce_panel_plugin_add_action_widget (XFCE_PANEL_PLUGIN (plugin), button); gtk_widget_show (button); + if (GDK_IS_X11_DISPLAY (gdk_display_get_default ())) + { + g_signal_connect (G_OBJECT (plugin), "screen-changed", + G_CALLBACK (show_desktop_plugin_screen_changed), NULL); + g_signal_connect (G_OBJECT (button), "button-release-event", + G_CALLBACK (show_desktop_plugin_button_release_event), plugin); + } + else + { + plugin->wl_toplevel_manager = xfwl_foreign_toplevel_manager_get (); + if (plugin->wl_toplevel_manager != NULL) + g_signal_connect_object (plugin->wl_toplevel_manager, "notify::show-desktop", + G_CALLBACK (show_desktop_plugin_wl_show_desktop), plugin, G_CONNECT_SWAPPED); + } + /* allow toggle the button when drag something.*/ gtk_drag_dest_set (GTK_WIDGET (plugin->button), 0, NULL, 0, 0); g_signal_connect (G_OBJECT (plugin->button), "drag_motion", @@ -197,6 +216,9 @@ show_desktop_plugin_free_data (XfcePanelPlugin *panel_plugin) if (plugin->wnck_screen != NULL) g_signal_handlers_disconnect_by_func (G_OBJECT (plugin->wnck_screen), show_desktop_plugin_showing_desktop_changed, plugin); + + if (plugin->wl_toplevel_manager != NULL) + g_object_unref (plugin->wl_toplevel_manager); } @@ -230,12 +252,11 @@ show_desktop_plugin_toggled (GtkToggleButton *button, panel_return_if_fail (XFCE_IS_SHOW_DESKTOP_PLUGIN (plugin)); panel_return_if_fail (GTK_IS_TOGGLE_BUTTON (button)); - panel_return_if_fail (WNCK_IS_SCREEN (plugin->wnck_screen)); /* toggle the desktop */ active = gtk_toggle_button_get_active (button); - if (active != wnck_screen_get_showing_desktop (plugin->wnck_screen)) - wnck_screen_toggle_showing_desktop (plugin->wnck_screen, active); + if (active != show_desktop_plugin_get_showing_desktop (plugin, plugin->wnck_screen)) + show_desktop_plugin_toggle_showing_desktop (plugin, plugin->wnck_screen, active); if (active) text = _("Restore the minimized windows"); @@ -290,12 +311,11 @@ show_desktop_plugin_showing_desktop_changed (WnckScreen *wnck_screen, ShowDesktopPlugin *plugin) { panel_return_if_fail (XFCE_IS_SHOW_DESKTOP_PLUGIN (plugin)); - panel_return_if_fail (WNCK_IS_SCREEN (wnck_screen)); panel_return_if_fail (plugin->wnck_screen == wnck_screen); /* update button to user action */ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (plugin->button), - wnck_screen_get_showing_desktop (wnck_screen)); + show_desktop_plugin_get_showing_desktop (plugin, wnck_screen)); } @@ -351,3 +371,39 @@ show_desktop_plugin_drag_motion (GtkWidget *widget, return TRUE; } + + + +static void +show_desktop_plugin_wl_show_desktop (ShowDesktopPlugin *plugin) +{ + show_desktop_plugin_showing_desktop_changed (NULL, plugin); +} + + + +static gboolean +show_desktop_plugin_get_showing_desktop (ShowDesktopPlugin *plugin, + WnckScreen *screen) +{ + if (screen != NULL) + return wnck_screen_get_showing_desktop (screen); + + if (plugin->wl_toplevel_manager != NULL) + return xfwl_foreign_toplevel_manager_get_show_desktop (plugin->wl_toplevel_manager); + + return FALSE; +} + + + +static void +show_desktop_plugin_toggle_showing_desktop (ShowDesktopPlugin *plugin, + WnckScreen *screen, + gboolean show) +{ + if (screen != NULL) + wnck_screen_toggle_showing_desktop (screen, show); + else if (plugin->wl_toplevel_manager != NULL) + xfwl_foreign_toplevel_manager_set_show_desktop (plugin->wl_toplevel_manager, show); +} -- GitLab From 3566d82807098bb7b36e925bb9ad6bac9d784e36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Bonithon?= Date: Fri, 23 Sep 2022 20:25:40 +0200 Subject: [PATCH 20/22] wayland: systray: Disable legacy systray icons --- plugins/systray/sn-plugin.c | 72 +++++++++++++++++-------------- plugins/systray/systray-manager.c | 33 ++++++++++++-- plugins/systray/systray-socket.c | 23 +++++++++- plugins/systray/systray-socket.h | 8 ++++ plugins/systray/systray.c | 1 - 5 files changed, 99 insertions(+), 38 deletions(-) diff --git a/plugins/systray/sn-plugin.c b/plugins/systray/sn-plugin.c index dbe3d8d1e..19bac246e 100644 --- a/plugins/systray/sn-plugin.c +++ b/plugins/systray/sn-plugin.c @@ -119,10 +119,6 @@ sn_plugin_free (XfcePanelPlugin *panel_plugin) if (plugin->idle_startup != 0) g_source_remove (plugin->idle_startup); - /* disconnect screen changed signal */ - g_signal_handlers_disconnect_by_func (G_OBJECT (plugin), - systray_plugin_screen_changed, NULL); - g_slist_free_full (plugin->names_ordered, g_free); g_hash_table_destroy (plugin->names_hidden); @@ -132,7 +128,12 @@ sn_plugin_free (XfcePanelPlugin *panel_plugin) g_object_unref (G_OBJECT (plugin->manager)); } - gtk_container_remove (GTK_CONTAINER (plugin->box), plugin->systray_box); + if (plugin->systray_box != NULL) + { + gtk_container_remove (GTK_CONTAINER (plugin->box), plugin->systray_box); + g_signal_handlers_disconnect_by_func (G_OBJECT (plugin), + systray_plugin_screen_changed, NULL); + } /* Statusnotifier */ /* remove children so they won't use unrefed SnItems and SnConfig */ @@ -157,8 +158,9 @@ sn_plugin_size_changed (XfcePanelPlugin *panel_plugin, size, xfce_panel_plugin_get_nrows (panel_plugin), xfce_panel_plugin_get_icon_size (panel_plugin)); - systray_plugin_size_changed (panel_plugin, - xfce_panel_plugin_get_size (panel_plugin)); + if (plugin->systray_box != NULL) + systray_plugin_size_changed (panel_plugin, + xfce_panel_plugin_get_size (panel_plugin)); return TRUE; } @@ -178,7 +180,8 @@ sn_plugin_mode_changed (XfcePanelPlugin *panel_plugin, ? GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL; sn_config_set_orientation (plugin->config, panel_orientation, orientation); - systray_plugin_orientation_changed (panel_plugin, panel_orientation); + if (plugin->systray_box != NULL) + systray_plugin_orientation_changed (panel_plugin, panel_orientation); sn_plugin_size_changed (panel_plugin, xfce_panel_plugin_get_size (panel_plugin)); } @@ -292,7 +295,8 @@ sn_plugin_button_toggled (GtkWidget *button, show_hidden = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)); sn_box_set_show_hidden (XFCE_SN_BOX (plugin->sn_box), show_hidden); - systray_box_set_show_hidden (XFCE_SYSTRAY_BOX (plugin->systray_box), show_hidden); + if (plugin->systray_box != NULL) + systray_box_set_show_hidden (XFCE_SYSTRAY_BOX (plugin->systray_box), show_hidden); orientation = xfce_panel_plugin_get_orientation (XFCE_PANEL_PLUGIN (plugin)); if (orientation == GTK_ORIENTATION_HORIZONTAL) @@ -325,35 +329,41 @@ sn_plugin_construct (XfcePanelPlugin *panel_plugin) gtk_widget_show (plugin->box); /* Add systray box */ - plugin->systray_box = systray_box_new (); - gtk_box_pack_start (GTK_BOX (plugin->box), plugin->systray_box, TRUE, TRUE, 0); - g_signal_connect (G_OBJECT (plugin->systray_box), "draw", - G_CALLBACK (systray_plugin_box_draw), plugin); - gtk_container_set_border_width (GTK_CONTAINER (plugin->systray_box), 0); - gtk_widget_show (plugin->systray_box); - - /* monitor screen changes */ - g_signal_connect (G_OBJECT (plugin), "screen-changed", - G_CALLBACK (systray_plugin_screen_changed), NULL); - systray_plugin_screen_changed (GTK_WIDGET (plugin), NULL); - - /* restart internally if compositing changed */ - g_signal_connect_swapped (gdk_screen_get_default (), "composited-changed", - G_CALLBACK (systray_plugin_composited_changed), plugin); + if (GDK_IS_X11_DISPLAY (gdk_display_get_default ())) + { + plugin->systray_box = systray_box_new (); + gtk_box_pack_start (GTK_BOX (plugin->box), plugin->systray_box, TRUE, TRUE, 0); + g_signal_connect (G_OBJECT (plugin->systray_box), "draw", + G_CALLBACK (systray_plugin_box_draw), plugin); + gtk_container_set_border_width (GTK_CONTAINER (plugin->systray_box), 0); + gtk_widget_show (plugin->systray_box); + + /* monitor screen changes */ + g_signal_connect (G_OBJECT (plugin), "screen-changed", + G_CALLBACK (systray_plugin_screen_changed), NULL); + systray_plugin_screen_changed (GTK_WIDGET (plugin), NULL); + + /* restart internally if compositing changed */ + g_signal_connect_swapped (gdk_screen_get_default (), "composited-changed", + G_CALLBACK (systray_plugin_composited_changed), plugin); + + g_signal_connect_swapped (plugin->config, "configuration-changed", + G_CALLBACK (gtk_widget_queue_resize), plugin->systray_box); + g_signal_connect (plugin->config, "configuration-changed", + G_CALLBACK (systray_plugin_configuration_changed), plugin); + g_signal_connect (plugin->config, "legacy-items-list-changed", + G_CALLBACK (systray_plugin_configuration_changed), plugin); + g_signal_connect (G_OBJECT(plugin->systray_box), "notify::has-hidden", + G_CALLBACK(systray_has_hidden_cb), plugin); + } /* Add statusnotifier box */ plugin->sn_box = sn_box_new (plugin->config); gtk_box_pack_start (GTK_BOX (plugin->box), plugin->sn_box, TRUE, TRUE, 0); gtk_widget_show (GTK_WIDGET (plugin->sn_box)); - g_signal_connect_swapped (plugin->config, "configuration-changed", - G_CALLBACK (gtk_widget_queue_resize), plugin->systray_box); g_signal_connect_swapped (plugin->config, "configuration-changed", G_CALLBACK (gtk_widget_queue_resize), plugin->sn_box); - g_signal_connect (plugin->config, "configuration-changed", - G_CALLBACK (systray_plugin_configuration_changed), plugin); - g_signal_connect (plugin->config, "legacy-items-list-changed", - G_CALLBACK (systray_plugin_configuration_changed), plugin); #ifdef HAVE_DBUSMENU plugin->backend = sn_backend_new (); @@ -370,8 +380,6 @@ sn_plugin_construct (XfcePanelPlugin *panel_plugin) g_signal_connect(G_OBJECT(plugin->button), "toggled", G_CALLBACK(sn_plugin_button_toggled), plugin); gtk_button_set_relief(GTK_BUTTON(plugin->button), GTK_RELIEF_NONE); - g_signal_connect (G_OBJECT(plugin->systray_box), "notify::has-hidden", - G_CALLBACK(systray_has_hidden_cb), plugin); g_signal_connect (G_OBJECT(plugin->sn_box), "notify::has-hidden", G_CALLBACK(snbox_has_hidden_cb), plugin); xfce_panel_plugin_add_action_widget(XFCE_PANEL_PLUGIN(plugin), plugin->button); diff --git a/plugins/systray/systray-manager.c b/plugins/systray/systray-manager.c index da740c0b6..00986820f 100644 --- a/plugins/systray/systray-manager.c +++ b/plugins/systray/systray-manager.c @@ -28,13 +28,14 @@ #include #endif -#include -#include - #include -#include #include +#ifdef GDK_WINDOWING_X11 +#include +#include +#endif + #include #include @@ -56,6 +57,7 @@ +#ifdef GDK_WINDOWING_X11 static void systray_manager_finalize (GObject *object); static void systray_manager_remove_socket (gpointer key, gpointer value, @@ -82,6 +84,7 @@ static void systray_manager_set_colors_property (Systr static void systray_manager_message_free (SystrayMessage *message); static void systray_manager_message_remove_from_list (SystrayManager *manager, XClientMessageEvent *xevent); +#endif @@ -162,10 +165,12 @@ XFCE_PANEL_DEFINE_TYPE (SystrayManager, systray_manager, G_TYPE_OBJECT) static void systray_manager_class_init (SystrayManagerClass *klass) { +#ifdef GDK_WINDOWING_X11 GObjectClass *gobject_class; gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = systray_manager_finalize; +#endif systray_manager_signals[ICON_ADDED] = g_signal_new (g_intern_static_string ("icon-added"), @@ -246,6 +251,8 @@ systray_manager_init (SystrayManager *manager) +#ifdef GDK_WINDOWING_X11 + GQuark systray_manager_error_quark (void) { @@ -928,3 +935,21 @@ systray_manager_message_remove_from_list (SystrayManager *manager, } } } + +#else /* ! GDK_WINDOWING_X11 */ + +GQuark systray_manager_error_quark (void) { return 0; } +SystrayManager *systray_manager_new (void) { return NULL; } +gboolean systray_manager_register (SystrayManager *manager, + GdkScreen *screen, + GError **error) { return FALSE; } +void systray_manager_unregister (SystrayManager *manager) {} +void systray_manager_set_colors (SystrayManager *manager, + GdkRGBA *fg, + GdkRGBA *error, + GdkRGBA *warning, + GdkRGBA *success) {} +void systray_manager_set_orientation (SystrayManager *manager, + GtkOrientation orientation) {} + +#endif diff --git a/plugins/systray/systray-socket.c b/plugins/systray/systray-socket.c index a4d564f46..2780fd6a1 100644 --- a/plugins/systray/systray-socket.c +++ b/plugins/systray/systray-socket.c @@ -27,11 +27,12 @@ #include #endif +#ifdef GDK_WINDOWING_X11 #include #include +#endif #include -#include #include #include @@ -64,12 +65,14 @@ struct _SystraySocket +#ifdef GDK_WINDOWING_X11 static void systray_socket_finalize (GObject *object); static void systray_socket_realize (GtkWidget *widget); static void systray_socket_size_allocate (GtkWidget *widget, GtkAllocation *allocation); static gboolean systray_socket_draw (GtkWidget *widget, cairo_t *cr); +#endif @@ -80,6 +83,7 @@ XFCE_PANEL_DEFINE_TYPE (SystraySocket, systray_socket, GTK_TYPE_SOCKET) static void systray_socket_class_init (SystraySocketClass *klass) { +#ifdef GDK_WINDOWING_X11 GtkWidgetClass *gtkwidget_class; GObjectClass *gobject_class; @@ -90,6 +94,7 @@ systray_socket_class_init (SystraySocketClass *klass) gtkwidget_class->realize = systray_socket_realize; gtkwidget_class->size_allocate = systray_socket_size_allocate; gtkwidget_class->draw = systray_socket_draw; +#endif } @@ -103,6 +108,8 @@ systray_socket_init (SystraySocket *socket) +#ifdef GDK_WINDOWING_X11 + static void systray_socket_finalize (GObject *object) { @@ -436,3 +443,17 @@ systray_socket_set_hidden (SystraySocket *socket, socket->hidden = hidden; } + +#else /* ! GDK_WINDOWING_X11 */ + +GtkWidget *systray_socket_new (GdkScreen *screen, + Window window) { return NULL; } +void systray_socket_force_redraw (SystraySocket *socket) {} +gboolean systray_socket_is_composited (SystraySocket *socket) { return FALSE; } +const gchar *systray_socket_get_name (SystraySocket *socket) { return NULL; } +Window *systray_socket_get_window (SystraySocket *socket) { return NULL; } +gboolean systray_socket_get_hidden (SystraySocket *socket) { return FALSE; } +void systray_socket_set_hidden (SystraySocket *socket, + gboolean hidden) {} + +#endif diff --git a/plugins/systray/systray-socket.h b/plugins/systray/systray-socket.h index 8d9ccbdca..ebecccb91 100644 --- a/plugins/systray/systray-socket.h +++ b/plugins/systray/systray-socket.h @@ -23,7 +23,15 @@ #define __SYSTRAY_SOCKET_H__ #include +#ifdef GDK_WINDOWING_X11 #include +#else +typedef GtkWidget GtkSocket; +typedef GtkWidgetClass GtkSocketClass; +typedef gulong Window; +typedef gulong Atom; +#define GTK_TYPE_SOCKET GTK_TYPE_WIDGET +#endif typedef struct _SystraySocketClass SystraySocketClass; typedef struct _SystraySocket SystraySocket; diff --git a/plugins/systray/systray.c b/plugins/systray/systray.c index c7926cf83..bf9e89ad6 100644 --- a/plugins/systray/systray.c +++ b/plugins/systray/systray.c @@ -22,7 +22,6 @@ #endif #include -#include #include #include -- GitLab From a6267ee1f7e60a70dd5b0365fd80681e7726f300 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Bonithon?= Date: Fri, 23 Sep 2022 20:51:14 +0200 Subject: [PATCH 21/22] wayland: systray: Guard X11 code --- panel/panel-module.c | 2 +- plugins/systray/sn-button.c | 16 ++++++++++------ plugins/systray/sn-plugin.c | 1 - 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/panel/panel-module.c b/panel/panel-module.c index 7574682ac..2148306d9 100644 --- a/panel/panel-module.c +++ b/panel/panel-module.c @@ -340,7 +340,7 @@ panel_module_new_from_desktop_file (const gchar *filename, if (G_LIKELY (module_name != NULL) && ( GDK_IS_X11_DISPLAY (gdk_display_get_default ()) || ( /* Wayland-incompatible embedded plugins */ - g_strstr_len ("pager systray tasklist windowmenu", -1, module_name) == NULL + g_strstr_len ("pager tasklist windowmenu", -1, module_name) == NULL /* Wayland-incompatible non-embedded plugins */ && g_strstr_len ("clipman", -1, module_name) == NULL ) diff --git a/plugins/systray/sn-button.c b/plugins/systray/sn-button.c index a32981819..9f6804d83 100644 --- a/plugins/systray/sn-button.c +++ b/plugins/systray/sn-button.c @@ -26,10 +26,9 @@ #include #endif -#include - #include +#include #include "sn-button.h" #include "sn-icon-box.h" #include "sn-util.h" @@ -111,7 +110,6 @@ sn_button_init (SnButton *button) { GtkCssProvider *css_provider; GdkEventMask event_mask = GDK_SCROLL_MASK; - const gchar *wm_name; gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE); @@ -127,10 +125,16 @@ sn_button_init (SnButton *button) GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); g_object_unref (css_provider); + event_mask |= GDK_SMOOTH_SCROLL_MASK; +#ifdef GDK_WINDOWING_X11 /* see https://gitlab.xfce.org/xfce/xfwm4/-/issues/641 */ - wm_name = gdk_x11_screen_get_window_manager_name (gtk_widget_get_screen (GTK_WIDGET (button))); - if (g_strcmp0 (wm_name, "Xfwm4") != 0 && g_strcmp0 (wm_name, "unknown") != 0) - event_mask |= GDK_SMOOTH_SCROLL_MASK; + if (GDK_IS_X11_DISPLAY (gdk_display_get_default ())) + { + const gchar *wm_name = gdk_x11_screen_get_window_manager_name (gdk_screen_get_default ()); + if (g_strcmp0 (wm_name, "Xfwm4") == 0 || g_strcmp0 (wm_name, "unknown") == 0) + event_mask &= ~GDK_SMOOTH_SCROLL_MASK; + } +#endif gtk_widget_add_events (GTK_WIDGET (button), event_mask); diff --git a/plugins/systray/sn-plugin.c b/plugins/systray/sn-plugin.c index 19bac246e..399b463bb 100644 --- a/plugins/systray/sn-plugin.c +++ b/plugins/systray/sn-plugin.c @@ -26,7 +26,6 @@ #endif #include -#include #include #include -- GitLab From 1c424532a4d839933b24b8772f4b7e0e2873badb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Bonithon?= Date: Sun, 25 Sep 2022 16:51:12 +0200 Subject: [PATCH 22/22] wayland: tasklist: Guard X11 code --- plugins/tasklist/tasklist-widget.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/tasklist/tasklist-widget.c b/plugins/tasklist/tasklist-widget.c index 4675d9893..992933136 100644 --- a/plugins/tasklist/tasklist-widget.c +++ b/plugins/tasklist/tasklist-widget.c @@ -36,7 +36,6 @@ #ifdef GDK_WINDOWING_X11 #include -#include #include #endif @@ -3246,11 +3245,13 @@ xfce_tasklist_button_enter_notify_event_disconnected (gpointer data, panel_return_if_fail (WNCK_IS_WINDOW (child->window)); +#ifdef GDK_WINDOWING_X11 /* we need to detach the geometry watch because that is connected * to the window we proxy and thus not disconnected when the * proxy dies */ g_signal_handlers_disconnect_by_func (child->window, xfce_tasklist_button_geometry_changed, child); +#endif g_object_unref (G_OBJECT (child->window)); } -- GitLab