diff --git a/.gitignore b/.gitignore index d16dd8a0dae7a4f2c21884848390228a449c5972..22a668d6aad974823fe416adb5b2f1c46a1a3ace 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 921e9e79ceb19aa74b88e1429faf182f2b47dadb..3c676208e3eab78721320aa07de9b8266fbfa1e9 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/common/panel-private.h b/common/panel-private.h index 6908a1b019a40ff29f1947998110ef2ce2723794..b7e260ea09c144b6a79de30f445ce2aa3012c98b 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/configure.ac.in b/configure.ac.in index 5763d988e4c4a363cb54cb81c47578a69592a49d..f47a3f0c0e74046aa5e0802a083f564f5286261e 100644 --- a/configure.ac.in +++ b/configure.ac.in @@ -155,6 +155,11 @@ 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 ********************************************* dnl *** Optional DBUSMENU for StatusNotifiers *** dnl ********************************************* @@ -288,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/docs/reference/Makefile.am b/docs/reference/Makefile.am index 28334fa90d2561987442f5ca76be9f272c25e0dc..b132ef918682f8a68460ff17d66fbdd497b5763f 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/docs/reference/libxfce4panel-docs.xml b/docs/reference/libxfce4panel-docs.xml index 3ec8747478c5c0e4279b33f7736d2ae9fd34aff8..3e7351a9aca7c90cddc8e2e1956fbc9b1024e7d3 100644 --- a/docs/reference/libxfce4panel-docs.xml +++ b/docs/reference/libxfce4panel-docs.xml @@ -122,6 +122,14 @@ + + Wayland Interaction + + + + + + Miscelleanous diff --git a/docs/reference/libxfce4panel-sections.txt b/docs/reference/libxfce4panel-sections.txt index fc0eac7fca54d88ea6af2d6a3dafbcb931bd72f3..80fac34169e4d9cd7007e846b730ef41af69ac3c 100644 --- a/docs/reference/libxfce4panel-sections.txt +++ b/docs/reference/libxfce4panel-sections.txt @@ -137,6 +137,51 @@ XFCE_PANEL_PLUGIN_GET_CLASS xfce_panel_plugin_get_type +
+xfwl-core-utils +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_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 +
+ +
+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_foreign_toplevel_manager_get_show_desktop +xfwl_foreign_toplevel_manager_set_show_desktop + +XFWL_TYPE_FOREIGN_TOPLEVEL_MANAGER +
+
macros XFCE_PANEL_CHANNEL_NAME diff --git a/docs/reference/libxfce4panel.types b/docs/reference/libxfce4panel.types index 0ffc5e00372e05ed76bfbd4dbae3a49d0168929f..0c2ab8a820167621536d72bf1833f57dd7693a55 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 37e5c19eb95f701f50e0e3b005f811a013498cd5..5fc0152356f7162474a6be41059a78bc0ca61192 100644 --- a/libxfce4panel/Makefile.am +++ b/libxfce4panel/Makefile.am @@ -30,7 +30,10 @@ 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 \ + xfwl-foreign-toplevel.h \ + xfwl-foreign-toplevel-manager.h libxfce4panel_includedir = \ $(includedir)/xfce4/libxfce4panel-$(LIBXFCE4PANEL_VERSION_API)/libxfce4panel @@ -47,11 +50,15 @@ 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 \ + xfwl-foreign-toplevel.c \ + xfwl-foreign-toplevel-manager.c libxfce4panel_2_0_la_CFLAGS = \ $(GTK_CFLAGS) \ $(LIBXFCE4UTIL_CFLAGS) \ + $(WAYLAND_CLIENT_CFLAGS) \ $(PLATFORM_CFLAGS) libxfce4panel_2_0_la_LDFLAGS = \ @@ -62,10 +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 # @@ -94,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@\" }," \ @@ -135,6 +147,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 95ea985550c2b121a8a9d2b549bc02c91b916169..9ab9c6e320fd3b384fac21f19587d11d70af44e3 100644 --- a/libxfce4panel/libxfce4panel.h +++ b/libxfce4panel/libxfce4panel.h @@ -31,6 +31,9 @@ #include #include #include +#include +#include +#include #undef _LIBXFCE4PANEL_INSIDE_LIBXFCE4PANEL_H diff --git a/libxfce4panel/libxfce4panel.symbols b/libxfce4panel/libxfce4panel.symbols index 34f3eeb0dcf19d830c82a56e837c5b6885e923b0..db60a790f1582f150806d7bda371c738d97ef648 100644 --- a/libxfce4panel/libxfce4panel.symbols +++ b/libxfce4panel/libxfce4panel.symbols @@ -156,3 +156,48 @@ 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 + +/* 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 +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 + +/* 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 +xfwl_foreign_toplevel_manager_get_show_desktop +xfwl_foreign_toplevel_manager_set_show_desktop +#endif +#endif diff --git a/libxfce4panel/xfce-arrow-button.c b/libxfce4panel/xfce-arrow-button.c index bd4ef3c379d8f6eaffc84904e9f6737321cd1c8e..93d353982fb69a84d608940e0fe13ad2f3e72d9b 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/libxfce4panel/xfce-panel-plugin.c b/libxfce4panel/xfce-panel-plugin.c index 0927e42e457a8846b775cee9782e5b1819fb573e..bad592d65e477eded79813422903f0ce54c2347a 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/libxfce4panel/xfwl-core-utils.c b/libxfce4panel/xfwl-core-utils.c new file mode 100644 index 0000000000000000000000000000000000000000..c92526e8ad257d770be359a1baa46311168877c3 --- /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 0000000000000000000000000000000000000000..120afcd8f38966a7fc9d1a701fe77a377846d0da --- /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/libxfce4panel/xfwl-foreign-toplevel-manager.c b/libxfce4panel/xfwl-foreign-toplevel-manager.c new file mode 100644 index 0000000000000000000000000000000000000000..47d4b0daf44d65cab5ec7ad1bb51916ae0b8d375 --- /dev/null +++ b/libxfce4panel/xfwl-foreign-toplevel-manager.c @@ -0,0 +1,603 @@ +/* + * 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); +static void xfwl_foreign_toplevel_manager_desktop_disconnect (gpointer object, + gpointer data); + + + +enum +{ + PROP_0, + PROP_WL_MANAGER, + PROP_TOPLEVELS, + PROP_ACTIVE, + PROP_SHOW_DESKTOP, + 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; + + /* show desktop */ + guint show_desktop : 1; + XfwlForeignToplevel *was_active; + GList *minimized; +}; + +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); + + 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); + + /** + * 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; + + 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; + } +} + + + +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; + + 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; + } +} + + + +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_list_free (manager->minimized); + + 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; +} + + + +/** + * 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 new file mode 100644 index 0000000000000000000000000000000000000000..c76e68c75f9b63092e5b48ab642d50c6a1e9283a --- /dev/null +++ b/libxfce4panel/xfwl-foreign-toplevel-manager.h @@ -0,0 +1,46 @@ +/* + * 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); + +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__ */ diff --git a/libxfce4panel/xfwl-foreign-toplevel.c b/libxfce4panel/xfwl-foreign-toplevel.c new file mode 100644 index 0000000000000000000000000000000000000000..844cf4c5aa0fe0d7d669b6956d7da73e67f09712 --- /dev/null +++ b/libxfce4panel/xfwl-foreign-toplevel.c @@ -0,0 +1,731 @@ +/* + * 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; +} + + + +/** + * 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 new file mode 100644 index 0000000000000000000000000000000000000000..5d7385e6e0f0234c1311316e6dad3a30dc99f302 --- /dev/null +++ b/libxfce4panel/xfwl-foreign-toplevel.h @@ -0,0 +1,89 @@ +/* + * 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 + +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); + +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__ */ diff --git a/panel/Makefile.am b/panel/Makefile.am index 26724f3c1d9cc6b28941cc54401be593e89b18b0..29523a6d1134ffe87a5334f3ecd177b376697bf6 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,7 @@ xfce4_panel_CFLAGS = \ $(LIBWNCK_CFLAGS) \ $(XFCONF_CFLAGS) \ $(LIBX11_CFLAGS) \ - $(LIBWNCK_CFLAGS) \ + $(GTK_LAYER_SHELL_CFLAGS) \ $(PLATFORM_CFLAGS) xfce4_panel_LDFLAGS = \ @@ -82,7 +81,7 @@ xfce4_panel_LDADD = \ $(LIBWNCK_LIBS) \ $(XFCONF_LIBS) \ $(LIBX11_LIBS) \ - $(LIBWNCK_LIBS) \ + $(GTK_LAYER_SHELL_LIBS) \ -lm xfce4_panel_DEPENDENCIES = \ diff --git a/panel/panel-application.c b/panel/panel-application.c index 5f1f94243c0aaafda85ce8ad5a9920021781044a..f15720d42539ed8beced46be0a726b2bf5593854 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-base-window.c b/panel/panel-base-window.c index b30b78dfbe0097bb9e1cd64fc4b9303d6423d49e..7917b2729f3ab9704b8f2826f417f55c8448d706 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 2cb9a7b27cb6bcd32a2240b75daabf9e330021cb..33badf6d1cf855a68b431ef8f0571a5b1a8c57bc 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-module.c b/panel/panel-module.c index 02a7b253829ec9d364cc0d38982b8017dfc3f24a..2148306d9c798037affbf75b053e622ee2ac03ce 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 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 d2509afc703fecd5df58fe4a03ccdccec69289e8..ecbfb6da048c20908143604b5b6b7a3802c2de50 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 b6904576a608df18440ef8b9083bff96e78d564d..de681c48bca1d325f1cb778f77ef51cfc2fcad57 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 1e48c97a11367907fa262383b68fa4b232cc0236..3ccaef8327c2ba9f40172558f1a60dc2534225d4 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 0618ff248c4df0d8d429ab682390c01aa24e3726..c940943501e46f3ba066a29b4b82f1caafd14c56 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 f370381fe4c3143fa2852c36872e26a001e2d7e0..62d4676b3515e8e9370c671824af8ee11afef477 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 eff6560844e74aed7e394072ebff92f3faaa3519..ad4401146a353d0e757be2539848acb060769e84 100644 --- a/panel/panel-window.c +++ b/panel/panel-window.c @@ -28,7 +28,6 @@ #endif #ifdef GDK_WINDOWING_X11 -#include #include #include #endif @@ -37,6 +36,8 @@ #include +#include + #include #include #include @@ -133,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); @@ -142,6 +147,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); @@ -183,6 +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_toplevel_manager_active (PanelWindow *window); @@ -367,6 +374,11 @@ struct _PanelWindow guint32 grab_time; gint grab_x; gint grab_y; + + /* Wayland */ + XfwlForeignToplevelManager *wl_toplevel_manager; + XfwlForeignToplevel *wl_active; + gboolean wl_active_is_maximized; }; /* used for a full XfcePanelWindow name in the class, but not in the code */ @@ -593,12 +605,15 @@ 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; window->grab_x = 0; window->grab_y = 0; + window->wl_toplevel_manager = NULL; + window->wl_active = NULL; + window->wl_active_is_maximized = FALSE; /* not resizable, so allocation will follow size request */ gtk_window_set_resizable (GTK_WINDOW (window), FALSE); @@ -609,6 +624,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); } @@ -861,6 +880,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) { @@ -890,6 +912,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 */ @@ -940,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); @@ -1127,6 +1155,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; @@ -1138,12 +1167,19 @@ 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 */ 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 */ @@ -1159,6 +1195,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 */ @@ -1184,8 +1222,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; @@ -1196,7 +1234,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); @@ -1233,13 +1277,27 @@ 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; + } - 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)) @@ -1282,6 +1340,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"); @@ -1409,6 +1469,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); + } } @@ -1448,11 +1523,15 @@ 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)) { /* 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 */ @@ -1489,9 +1568,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); - panel_base_window_move_resize (PANEL_BASE_WINDOW (window->autohide_window), - x, y, w, h); + 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 @@ -1521,7 +1604,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 @@ -1541,10 +1624,9 @@ 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); + panel_window_move (window, GTK_WINDOW (window->autohide_window), -9999, -9999); - 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)); @@ -1676,13 +1758,37 @@ 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) { 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 +1817,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 +1872,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 +1921,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 } @@ -1912,6 +2026,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. @@ -1962,6 +2098,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 +2120,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)) { @@ -2111,14 +2249,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) @@ -2139,6 +2296,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) { @@ -2407,6 +2694,15 @@ 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); + 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 */ @@ -2452,15 +2748,32 @@ 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) { 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 */ @@ -2472,10 +2785,26 @@ panel_window_active_window_geometry_changed (WnckWindow *active_window, if (window->autohide_behavior == AUTOHIDE_BEHAVIOR_INTELLIGENTLY && window->autohide_block == 0) { + /* 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) { +#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, @@ -2525,6 +2854,7 @@ panel_window_active_window_geometry_changed (WnckWindow *active_window, XFree (data); } } +#endif /* apply scale factor */ window_area.x /= window->scale_factor; @@ -2546,23 +2876,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 { @@ -2657,7 +2973,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); @@ -2703,7 +3028,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; } @@ -2713,7 +3038,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; } @@ -2856,6 +3184,21 @@ panel_window_autohide_event (GtkWidget *widget, +static gboolean +panel_window_toplevel_manager_connect (gpointer data) +{ + PanelWindow *window = data; + + 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; +} + + + static void panel_window_set_autohide_behavior (PanelWindow *window, AutohideBehavior behavior) @@ -2876,6 +3219,22 @@ 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) + { + 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) { @@ -2896,8 +3255,28 @@ 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); + 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++) @@ -2925,7 +3304,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); } @@ -2942,6 +3320,19 @@ 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 ()) + { + 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); + } + } } } else if (window->autohide_window != NULL) @@ -3363,6 +3754,160 @@ panel_window_plugin_set_screen_position (GtkWidget *widget, +static gboolean +panel_window_toplevel_on_panel_monitor (PanelWindow *window, + XfwlForeignToplevel *toplevel) +{ + GList *monitors; + + monitors = xfwl_foreign_toplevel_get_monitors (toplevel); + if (window->span_monitors) + { + GdkMonitor *monitor = NULL, *p_monitor; + GtkAllocation alloc; + gint fixed_dim, step; + + 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_list_find (monitors, monitor)) + return TRUE; + } + } + } + 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_list_find (monitors, monitor)) + return TRUE; + } + } + } + } + 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) + { + 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; + + maximized = PANEL_HAS_FLAG (xfwl_foreign_toplevel_get_state (toplevel), + XFWL_FOREIGN_TOPLEVEL_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_toplevel_active_monitors (XfwlForeignToplevel *toplevel, + GParamSpec *pspec, + PanelWindow *window) +{ + /* 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); + + /* 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_toplevel_manager_active (PanelWindow *window) +{ + /* 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); + } + + /* update active toplevel */ + window->wl_active = xfwl_foreign_toplevel_manager_get_active (window->wl_toplevel_manager); + if (window->wl_active == NULL) + return; + + /* 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); +} + + + GtkWidget * panel_window_new (GdkScreen *screen, gint id, @@ -3478,9 +4023,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); @@ -3491,15 +4034,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); @@ -3537,8 +4072,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; @@ -3564,14 +4099,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 f792e99c230b29e4ecca3e24862292adc2557cc8..aa8b5600ac9c88c72632f1f268762775d0f3400b 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/plugins/showdesktop/showdesktop.c b/plugins/showdesktop/showdesktop.c index 29db159e0f1e824ed346f19a98f20266691b9020..fc1abcc9c89eeabfc647a9839a522c3047232773 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); +} diff --git a/plugins/systray/sn-button.c b/plugins/systray/sn-button.c index a32981819b4c13027dffe07cd45c9052c6c556eb..9f6804d83a11c63c7254d9f446358d434183f13c 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 8c040e432d97bdd90ac54cb5d2292bc30158d71c..399b463bb6e20b7252f03afdf0504dbb38c66b3f 100644 --- a/plugins/systray/sn-plugin.c +++ b/plugins/systray/sn-plugin.c @@ -26,7 +26,6 @@ #endif #include -#include #include #include @@ -119,10 +118,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 +127,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 +157,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 +179,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)); } @@ -279,39 +281,29 @@ 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 = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)); + sn_box_set_show_hidden (XFCE_SN_BOX (plugin->sn_box), show_hidden); + if (plugin->systray_box != NULL) + systray_box_set_show_hidden (XFCE_SYSTRAY_BOX (plugin->systray_box), show_hidden); - show_hidden = systray_box_get_show_hidden(XFCE_SYSTRAY_BOX(plugin->systray_box)); - orientation = xfce_panel_plugin_get_orientation(XFCE_PANEL_PLUGIN(plugin)); + 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); } @@ -336,35 +328,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 (); @@ -381,8 +379,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 da740c0b698d877e9412b6f86107f931276e03a8..00986820fd7a883bb6249c50529259aa72d3535a 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 a4d564f46054433ae21dc9e1224a859e7a012f04..2780fd6a141a26f039f655ad578820b05b1938bc 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 8d9ccbdcadb6fee02d2c9442a9d233dbc1558397..ebecccb91970e34b8f7fc1dc86c48dd3951d7edb 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 c7926cf83c8a1bf5da4235cd5d7d8b6a5e383e75..bf9e89ad6686d459c2c95e61bd5df43022032614 100644 --- a/plugins/systray/systray.c +++ b/plugins/systray/systray.c @@ -22,7 +22,6 @@ #endif #include -#include #include #include diff --git a/plugins/tasklist/tasklist-widget.c b/plugins/tasklist/tasklist-widget.c index 4675d9893f11226ad6b904e6b013568daf620895..99293313677ca749f277e7def95a4fe498580a55 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)); } diff --git a/protocols/Makefile.am b/protocols/Makefile.am new file mode 100644 index 0000000000000000000000000000000000000000..3980c184abd51fbaaa891551b5534d9ad20096a0 --- /dev/null +++ b/protocols/Makefile.am @@ -0,0 +1,43 @@ +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 + +# required for make distcheck +dist-hook: all 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 0000000000000000000000000000000000000000..44505bbb698d4b90cc0ae5fbe52be193d47b9e24 --- /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. + + + + + diff --git a/wrapper/wrapper-plug.h b/wrapper/wrapper-plug.h index 42975b477dd6f3fab8d964c1a941dd9194616f16..e8e448d0edfe3a85501e1ef190be3f3a562b66fd 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