From 1b526131e4028fae7d73ad0e1bc7ef969f6a68c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kondor=20D=C3=A1niel?= Date: Sat, 26 Oct 2024 14:27:47 +0200 Subject: [PATCH 01/24] windows-manager: allow querying some more capabilities --- src/gldit/cairo-dock-windows-manager.c | 43 +++++++++++++++++++++----- src/gldit/cairo-dock-windows-manager.h | 34 +++++++++++++++++++- 2 files changed, 69 insertions(+), 8 deletions(-) diff --git a/src/gldit/cairo-dock-windows-manager.c b/src/gldit/cairo-dock-windows-manager.c index 014dbe88..bd562cd7 100644 --- a/src/gldit/cairo-dock-windows-manager.c +++ b/src/gldit/cairo-dock-windows-manager.c @@ -322,35 +322,48 @@ static inline gboolean _window_is_on_current_desktop (GtkAllocation *pWindowGeom } gboolean gldi_window_is_on_current_desktop (GldiWindowActor *actor) { - if (GPOINTER_TO_INT (s_backend.flags) & GLDI_WM_GEOM_REL_TO_VIEWPORT - || GPOINTER_TO_INT (s_backend.flags) & GLDI_WM_NO_VIEWPORT_OVERLAP) + const int flags = GPOINTER_TO_INT (s_backend.flags); + if (! (flags & GLDI_WM_HAVE_WORKSPACES)) + return FALSE; // if we don't track workspaces, there is no point + + if (actor->bIsSticky || actor->iNumDesktop == -1) // a sticky window is by definition on all desktops/viewports + return TRUE; + + if ( !(flags & GLDI_WM_HAVE_WINDOW_GEOMETRY) || (flags & GLDI_WM_NO_VIEWPORT_OVERLAP)) + return (actor->iNumDesktop == g_desktopGeometry.iCurrentDesktop + && actor->iViewPortX == g_desktopGeometry.iCurrentViewportX + && actor->iViewPortY == g_desktopGeometry.iCurrentViewportY); + + if (flags & GLDI_WM_GEOM_REL_TO_VIEWPORT) return gldi_window_is_on_desktop (actor, g_desktopGeometry.iCurrentDesktop, g_desktopGeometry.iCurrentViewportX, g_desktopGeometry.iCurrentViewportY); - if (actor->bIsSticky || actor->iNumDesktop == -1) return TRUE; - return _window_is_on_current_desktop (&actor->windowGeometry, actor->iNumDesktop); } gboolean gldi_window_is_on_desktop (GldiWindowActor *pAppli, int iNumDesktop, int iNumViewportX, int iNumViewportY) { + const int flags = GPOINTER_TO_INT (s_backend.flags); + if (! (flags & GLDI_WM_HAVE_WORKSPACES)) + return FALSE; // if we don't track workspaces, there is no point + if (pAppli->bIsSticky || pAppli->iNumDesktop == -1) // a sticky window is by definition on all desktops/viewports return TRUE; - if (GPOINTER_TO_INT (s_backend.flags) & GLDI_WM_NO_VIEWPORT_OVERLAP) + if ( !(flags & GLDI_WM_HAVE_WINDOW_GEOMETRY) || (flags & GLDI_WM_NO_VIEWPORT_OVERLAP)) return (pAppli->iNumDesktop == iNumDesktop && pAppli->iViewPortX == iNumViewportX && pAppli->iViewPortY == iNumViewportY); // On calcule les coordonnees en repere absolu. int x = pAppli->windowGeometry.x; // par rapport au viewport courant (or self, depending on the backend) - if (GPOINTER_TO_INT (s_backend.flags) & GLDI_WM_GEOM_REL_TO_VIEWPORT) x += pAppli->iViewPortX * gldi_desktop_get_width(); // repere absolu + if (flags & GLDI_WM_GEOM_REL_TO_VIEWPORT) x += pAppli->iViewPortX * gldi_desktop_get_width(); // repere absolu else x += g_desktopGeometry.iCurrentViewportX * gldi_desktop_get_width(); if (x < 0) x += g_desktopGeometry.iNbViewportX * gldi_desktop_get_width(); int y = pAppli->windowGeometry.y; - if (GPOINTER_TO_INT (s_backend.flags) & GLDI_WM_GEOM_REL_TO_VIEWPORT) y += pAppli->iViewPortY * gldi_desktop_get_height(); + if (flags & GLDI_WM_GEOM_REL_TO_VIEWPORT) y += pAppli->iViewPortY * gldi_desktop_get_height(); else y += g_desktopGeometry.iCurrentViewportY * gldi_desktop_get_height(); if (y < 0) y += g_desktopGeometry.iNbViewportY * gldi_desktop_get_height(); @@ -378,6 +391,22 @@ gboolean gldi_window_manager_is_position_relative_to_current_viewport (void) return !(GPOINTER_TO_INT (s_backend.flags) & GLDI_WM_GEOM_REL_TO_VIEWPORT); } +gboolean gldi_window_manager_have_coordinates (void) +{ + return (GPOINTER_TO_INT (s_backend.flags) & GLDI_WM_HAVE_WINDOW_GEOMETRY); +} + +gboolean gldi_window_manager_can_track_workspaces (void) +{ + return (GPOINTER_TO_INT (s_backend.flags) & GLDI_WM_HAVE_WORKSPACES); +} + +gboolean gldi_window_manager_can_move_to_desktop (void) +{ + return (s_backend.move_to_nth_desktop || s_backend.move_to_viewport_abs); +} + + gchar* gldi_window_parse_class(const gchar* res_class, const gchar* res_name) { gchar *cClass = NULL; if (res_class) diff --git a/src/gldit/cairo-dock-windows-manager.h b/src/gldit/cairo-dock-windows-manager.h index 703c1226..9c6118c3 100644 --- a/src/gldit/cairo-dock-windows-manager.h +++ b/src/gldit/cairo-dock-windows-manager.h @@ -55,7 +55,9 @@ typedef enum { typedef enum { GLDI_WM_GEOM_REL_TO_VIEWPORT = 1, // if present, windows' geometry is relative to the viewport they are present on (and not to the current viewport) - GLDI_WM_NO_VIEWPORT_OVERLAP = 2 // if present, windows cannot span multiple viewports + GLDI_WM_NO_VIEWPORT_OVERLAP = 2, // if present, windows cannot span multiple viewports + GLDI_WM_HAVE_WINDOW_GEOMETRY = 4, // the WM provides valid window geometry information (in _GldiWindowActor::windowGeometry) + GLDI_WM_HAVE_WORKSPACES = 8 // the WM tracks which workspace (desktop, viewport) a window is on (in iNumDesktop, iViewPortX, iViewPortY) } GldiWMBackendFlags; /// Definition of the Windows Manager backend. @@ -185,8 +187,38 @@ guint gldi_window_get_id (GldiWindowActor *pAppli); GldiWindowActor *gldi_window_pick (GtkWindow *pParentWindow); + +/* WM capabilities -- use cases outside of cairo-dock-windows-manager.c (especially plugins) + * should check these before using the corresponding fields */ + +/** Check whether we can track windows' position. + *@return whether GldiWindowActor::windowGeometry contains the actual + * window position; if FALSE, this should not be used + */ +gboolean gldi_window_manager_have_coordinates (void); + +/** Check whether we can track which workspace / viewport / desktop + * windows are present on. + *@return whether GldiWindowActor::iNumDesktop, iViewPortX and iViewPortY + * contains valid information; if FALSE, these should not be used + */ +gboolean gldi_window_manager_can_track_workspaces (void); + +/** Check how window position coordinates should be interpreted. Result is only + * valud if gldi_window_manager_have_coordinates () == TRUE as well. + *@return whether window coordinates should be interpreted relative to the + * currently active workspace / viewport; if false, coordinates are relative + * to the workspace / viewport that the window is currently on + */ gboolean gldi_window_manager_is_position_relative_to_current_viewport (void); +/** Check whether it is possible to move a window to another desktop / viewport. + *@return TRUE if it is possible to move a window; if FALSE, + * gldi_window_move_to_current_desktop () and gldi_window_move_to_desktop () + * will do nothing + */ +gboolean gldi_window_manager_can_move_to_desktop (void); + /* utility for parsing special cases in the window class / app ID; * used by both the X and Wayland backends * From 91ae9a0f56ebca28f0e274deabaeca13c1ce9f41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kondor=20D=C3=A1niel?= Date: Sat, 26 Oct 2024 14:28:12 +0200 Subject: [PATCH 02/24] dock-visibility: only start if WM explicitly supports window positions --- src/gldit/cairo-dock-dock-visibility.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/gldit/cairo-dock-dock-visibility.c b/src/gldit/cairo-dock-dock-visibility.c index a04fae1b..2966094e 100644 --- a/src/gldit/cairo-dock-dock-visibility.c +++ b/src/gldit/cairo-dock-dock-visibility.c @@ -327,6 +327,9 @@ void gldi_docks_visibility_start (void) { static gboolean first = TRUE; + if (! (gldi_window_manager_have_coordinates () && gldi_window_manager_can_track_workspaces ()) ) + return; + // register to events if (first) { From ebed19f4f9c349b684bfa36c5ec798744b2d8686 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kondor=20D=C3=A1niel?= Date: Sat, 26 Oct 2024 14:28:43 +0200 Subject: [PATCH 03/24] user-menu: check if it possible to move windows between workspaces --- src/cairo-dock-user-menu.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/cairo-dock-user-menu.c b/src/cairo-dock-user-menu.c index 04173df6..af241ad0 100644 --- a/src/cairo-dock-user-menu.c +++ b/src/cairo-dock-user-menu.c @@ -1405,6 +1405,8 @@ static void _add_desktops_entry (GtkWidget *pMenu, gboolean bAll, gpointer *data static gpointer *s_pDesktopData = NULL; GtkWidget *pMenuItem; + if (!gldi_window_manager_can_move_to_desktop ()) return; + if (g_desktopGeometry.iNbDesktops > 1 || g_desktopGeometry.iNbViewportX > 1 || g_desktopGeometry.iNbViewportY > 1) { // add separator @@ -1900,7 +1902,8 @@ gboolean cairo_dock_notification_build_icon_menu (G_GNUC_UNUSED gpointer *pUserD // Move pMenuItem = _add_entry_in_menu (_("Move to this desktop"), GLDI_ICON_NAME_JUMP_TO, _cairo_dock_move_appli_to_current_desktop, pSubMenuOtherActions); - if (gldi_window_is_on_current_desktop (pAppli)) + // if it is not possible to move the window, we still keep this menu item, but make it inactive + if (!gldi_window_manager_can_move_to_desktop () || gldi_window_is_on_current_desktop (pAppli)) gtk_widget_set_sensitive (pMenuItem, FALSE); // Fullscreen @@ -1978,7 +1981,11 @@ gboolean cairo_dock_notification_build_icon_menu (G_GNUC_UNUSED gpointer *pUserD //\_________________________ Other actions GtkWidget *pSubMenuOtherActions = cairo_dock_create_sub_menu (_("Other actions"), menu, NULL); - _add_entry_in_menu (_("Move all to this desktop"), GLDI_ICON_NAME_JUMP_TO, _cairo_dock_move_class_to_current_desktop, pSubMenuOtherActions); + pMenuItem = _add_entry_in_menu (_("Move all to this desktop"), GLDI_ICON_NAME_JUMP_TO, _cairo_dock_move_class_to_current_desktop, pSubMenuOtherActions); + // if it is not possible to move the windows, we still keep this menu item, but make it inactive + if (!gldi_window_manager_can_move_to_desktop ()) + gtk_widget_set_sensitive (pMenuItem, FALSE); + _add_desktops_entry (pSubMenuOtherActions, TRUE, data); } From a94a0fb2962fd8bb290648e0381bbcd9ccb942b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kondor=20D=C3=A1niel?= Date: Sat, 26 Oct 2024 14:33:53 +0200 Subject: [PATCH 04/24] implementations: advertise WM capabilities --- src/implementations/cairo-dock-X-manager.c | 2 ++ src/implementations/cairo-dock-plasma-window-manager.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/implementations/cairo-dock-X-manager.c b/src/implementations/cairo-dock-X-manager.c index 3b560544..4fa04719 100644 --- a/src/implementations/cairo-dock-X-manager.c +++ b/src/implementations/cairo-dock-X-manager.c @@ -1672,6 +1672,8 @@ static void init (void) wmb.can_minimize_maximize_close = _can_minimize_maximize_close; wmb.get_id = _get_id; wmb.pick_window = _pick_window; + //!! TODO: figure out GLDI_WM_NO_VIEWPORT_OVERLAP flag (depends on the WM, needs to be done in *-integration.c) !! + wmb.flags = GINT_TO_POINTER (GLDI_WM_HAVE_WINDOW_GEOMETRY | GLDI_WM_HAVE_WORKSPACES); wmb.name = "X11"; gldi_windows_manager_register_backend (&wmb); diff --git a/src/implementations/cairo-dock-plasma-window-manager.c b/src/implementations/cairo-dock-plasma-window-manager.c index 11aed43d..85f13f4b 100644 --- a/src/implementations/cairo-dock-plasma-window-manager.c +++ b/src/implementations/cairo-dock-plasma-window-manager.c @@ -359,7 +359,7 @@ static void gldi_plasma_window_manager_init () wmb.can_minimize_maximize_close = _can_minimize_maximize_close; // wmb.get_id = _get_id; wmb.pick_window = gldi_wayland_wm_pick_window; - wmb.flags = GINT_TO_POINTER (GLDI_WM_NO_VIEWPORT_OVERLAP | GLDI_WM_GEOM_REL_TO_VIEWPORT); + wmb.flags = GINT_TO_POINTER (GLDI_WM_NO_VIEWPORT_OVERLAP | GLDI_WM_GEOM_REL_TO_VIEWPORT | GLDI_WM_HAVE_WINDOW_GEOMETRY | GLDI_WM_HAVE_WORKSPACES); wmb.name = "plasma"; gldi_windows_manager_register_backend (&wmb); From 4f634ae780f5cd7552bd1db244a445ca7547bbfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kondor=20D=C3=A1niel?= Date: Sat, 26 Oct 2024 15:18:23 +0200 Subject: [PATCH 05/24] applications-manager: consider whether we are able to track workspaces --- src/gldit/cairo-dock-applications-manager.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/gldit/cairo-dock-applications-manager.c b/src/gldit/cairo-dock-applications-manager.c index b7992a6a..f2c2277a 100644 --- a/src/gldit/cairo-dock-applications-manager.c +++ b/src/gldit/cairo-dock-applications-manager.c @@ -746,7 +746,9 @@ static gboolean get_config (GKeyFile *pKeyFile, CairoTaskbarParam *pTaskBar) if (pTaskBar->bShowAppli) { - pTaskBar->bAppliOnCurrentDesktopOnly = cairo_dock_get_boolean_key_value (pKeyFile, "TaskBar", "current desktop only", &bFlushConfFileNeeded, FALSE, "Applications", NULL); + // current desktop only option only makes sense if we are actually able to track which desktop a window is on + pTaskBar->bAppliOnCurrentDesktopOnly = gldi_window_manager_can_track_workspaces () + && cairo_dock_get_boolean_key_value (pKeyFile, "TaskBar", "current desktop only", &bFlushConfFileNeeded, FALSE, "Applications", NULL); pTaskBar->bMixLauncherAppli = cairo_dock_get_boolean_key_value (pKeyFile, "TaskBar", "mix launcher appli", &bFlushConfFileNeeded, TRUE, NULL, NULL); From e06faacb9497943d57376bc58b23775720530df8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kondor=20D=C3=A1niel?= Date: Sat, 26 Oct 2024 16:33:08 +0200 Subject: [PATCH 06/24] desktop-manager: new API to create or remove workspaces --- src/gldit/cairo-dock-desktop-manager.c | 12 ++++++++++++ src/gldit/cairo-dock-desktop-manager.h | 21 +++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/src/gldit/cairo-dock-desktop-manager.c b/src/gldit/cairo-dock-desktop-manager.c index 92644043..e6634091 100644 --- a/src/gldit/cairo-dock-desktop-manager.c +++ b/src/gldit/cairo-dock-desktop-manager.c @@ -222,6 +222,18 @@ gboolean gldi_desktop_set_nb_desktops (int iNbDesktops, int iNbViewportX, int iN return FALSE; } +void gldi_desktop_add_workspace (void) +{ + if (s_backend.add_workspace) s_backend.add_workspace (); +} + +void gldi_desktop_remove_last_workspace (void) +{ + if (s_backend.remove_last_workspace) s_backend.remove_last_workspace (); +} + + + void gldi_desktop_refresh (void) { if (s_backend.refresh) diff --git a/src/gldit/cairo-dock-desktop-manager.h b/src/gldit/cairo-dock-desktop-manager.h index f2021799..62f90431 100644 --- a/src/gldit/cairo-dock-desktop-manager.h +++ b/src/gldit/cairo-dock-desktop-manager.h @@ -159,6 +159,8 @@ struct _GldiDesktopManagerBackend { void (*refresh) (void); void (*notify_startup) (const gchar *cClass); gboolean (*grab_shortkey) (guint keycode, guint modifiers, gboolean grab); + void (*add_workspace) (void); // gldi_desktop_add_workspace () + void (*remove_last_workspace) (void); // gldi_desktop_remove_last_workspace () }; /// Definition of a Desktop Background Buffer. It has a reference count so that it can be shared across all the lib. @@ -223,6 +225,25 @@ gboolean gldi_desktop_set_names (gchar **cNames); gboolean gldi_desktop_set_current (int iDesktopNumber, int iViewportNumberX, int iViewportNumberY); gboolean gldi_desktop_set_nb_desktops (int iNbDesktops, int iNbViewportX, int iNbViewportY); +/** Adds a new workspace, desktop or viewport in an implementation-defined manner. + * Typically this can mean adding one more workspace / desktop as the "last" one. + * On X11, this will resize the desktop geometry, and could result in adding + * multiple viewports. + * Might not suceed, depending on the capabilities of the backend + * (NOTIFICATION_DESKTOP_GEOMETRY_CHANGED will be emitted if successful). + */ +void gldi_desktop_add_workspace (void); + +/** Remove the "last" workspace desktop or viewport, according to the + * internal ordering of workspaces. The actual number of workspaces can + * be > 1, depending on the backend (on X11, if viewports are arranged + * in a square). + * Might not suceed, depending on the capabilities of the backend + * (NOTIFICATION_DESKTOP_GEOMETRY_CHANGED will be emitted if successful). + */ +void gldi_desktop_remove_last_workspace (void); + + void gldi_desktop_refresh (void); void gldi_desktop_notify_startup (const gchar *cClass); From 479cf228f4bae89ecbc8201f2ae663b3c1b3b156 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kondor=20D=C3=A1niel?= Date: Sat, 26 Oct 2024 16:33:46 +0200 Subject: [PATCH 07/24] X-manager: provide the new API for adding and removing desktops / viewports --- src/implementations/cairo-dock-X-manager.c | 40 ++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/implementations/cairo-dock-X-manager.c b/src/implementations/cairo-dock-X-manager.c index 4fa04719..cb2951fb 100644 --- a/src/implementations/cairo-dock-X-manager.c +++ b/src/implementations/cairo-dock-X-manager.c @@ -798,6 +798,44 @@ static gboolean _set_nb_desktops (int iNbDesktops, int iNbViewportX, int iNbView return TRUE; } +static void _change_viewports (int iDeltaNbDesktops) +{ + // taken from the switcher applet + int iNewX, iNewY; + // Try to keep a square: (delta > 0 && X <= Y) || (delta < 0 && X > Y) + if ((iDeltaNbDesktops > 0) == (g_desktopGeometry.iNbViewportX <= g_desktopGeometry.iNbViewportY)) + { + iNewX = g_desktopGeometry.iNbViewportX + iDeltaNbDesktops; + if (iNewX <= 0) return; // cannot remove the last viewport + iNewY = g_desktopGeometry.iNbViewportY; + } + else + { + iNewX = g_desktopGeometry.iNbViewportX; + iNewY = g_desktopGeometry.iNbViewportY + iDeltaNbDesktops; + if (iNewY <= 0) return; // cannot remove the last viewport + } + cairo_dock_set_nb_viewports (iNewX, iNewY); +} + +static void _add_workspace (void) +{ + if (g_desktopGeometry.iNbViewportX == 1 && g_desktopGeometry.iNbViewportY == 1) + cairo_dock_set_nb_desktops (g_desktopGeometry.iNbDesktops + 1); + else _change_viewports (1); +} + +static void _remove_workspace (void) +{ + if (g_desktopGeometry.iNbViewportX == 1 && g_desktopGeometry.iNbViewportY == 1) + { + // note: do not attempt to remove the last desktop + if (g_desktopGeometry.iNbDesktops > 1) + cairo_dock_set_nb_desktops (g_desktopGeometry.iNbDesktops - 1); + } + else _change_viewports (-1); +} + static cairo_surface_t *_get_desktop_bg_surface (void) // attention : fonction lourde. { //g_print ("+++ %s ()\n", __func__); @@ -1646,6 +1684,8 @@ static void init (void) dmb.refresh = _refresh; dmb.notify_startup = _notify_startup; dmb.grab_shortkey = _grab_shortkey; + dmb.add_workspace = _add_workspace; + dmb.remove_last_workspace = _remove_workspace; gldi_desktop_manager_register_backend (&dmb, "X11"); GldiWindowManagerBackend wmb; From 7da7a3dcbb4e3ab00666e967a7e467a8a2679224 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kondor=20D=C3=A1niel?= Date: Sat, 26 Oct 2024 16:34:10 +0200 Subject: [PATCH 08/24] plasma-virtual-desktop: support adding and removing workspaces --- .../cairo-dock-plasma-virtual-desktop.c | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/implementations/cairo-dock-plasma-virtual-desktop.c b/src/implementations/cairo-dock-plasma-virtual-desktop.c index bed9209e..c520cad3 100644 --- a/src/implementations/cairo-dock-plasma-virtual-desktop.c +++ b/src/implementations/cairo-dock-plasma-virtual-desktop.c @@ -271,10 +271,25 @@ static gboolean _set_current_desktop (G_GNUC_UNUSED int iDesktopNumber, int iVie return FALSE; } +static struct org_kde_plasma_virtual_desktop_management* s_pmanager = NULL; + +static void _add_workspace (void) +{ + if (!s_pmanager) return; + char *name = g_strdup_printf ("Workspace %u", s_iNumDesktops + 1); + org_kde_plasma_virtual_desktop_management_request_create_virtual_desktop (s_pmanager, name, s_iNumDesktops); + g_free (name); +} + +static void _remove_workspace (void) +{ + if (s_iNumDesktops <= 1 || !s_pmanager) return; + org_kde_plasma_virtual_desktop_management_request_remove_virtual_desktop (s_pmanager, desktops[s_iNumDesktops - 1]->id); +} + static uint32_t protocol_id, protocol_version; static gboolean protocol_found = FALSE; -static struct org_kde_plasma_virtual_desktop_management* s_pmanager = NULL; gboolean gldi_plasma_virtual_desktop_match_protocol (uint32_t id, const char *interface, uint32_t version) { @@ -298,8 +313,10 @@ gboolean gldi_plasma_virtual_desktop_try_init (struct wl_registry *registry) GldiDesktopManagerBackend dmb; memset (&dmb, 0, sizeof (GldiDesktopManagerBackend)); - dmb.set_current_desktop = _set_current_desktop; - dmb.get_desktops_names = _get_desktops_names; + dmb.set_current_desktop = _set_current_desktop; + dmb.get_desktops_names = _get_desktops_names; + dmb.add_workspace = _add_workspace; + dmb.remove_last_workspace = _remove_workspace; gldi_desktop_manager_register_backend (&dmb, "plasma-virtual-desktop"); org_kde_plasma_virtual_desktop_management_add_listener (s_pmanager, &manager_listener, NULL); From 7ce1b0722582200d826100185f15ebad65e8e21b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kondor=20D=C3=A1niel?= Date: Sat, 26 Oct 2024 16:35:16 +0200 Subject: [PATCH 09/24] cosmic-workspaces: support protocol Tested with basic functions on Cosmic and Labwc. Limitations: - only one workspace group is supported (did not test any configuration with > 1 group) - adding and removing workspaces is not supported by either compositor - tracking outputs is very limited, we only store the last received wl_output for the sole workspace group (this is needed to move windows between workspaces on Cosmic; no use on Labwc) - Cosmic: only tested with one output --- src/implementations/CMakeLists.txt | 1 + .../cairo-dock-cosmic-workspaces.c | 567 ++++++++++++++++++ .../cairo-dock-cosmic-workspaces.h | 36 ++ .../cairo-dock-wayland-manager.c | 25 +- 4 files changed, 625 insertions(+), 4 deletions(-) create mode 100644 src/implementations/cairo-dock-cosmic-workspaces.c create mode 100644 src/implementations/cairo-dock-cosmic-workspaces.h diff --git a/src/implementations/CMakeLists.txt b/src/implementations/CMakeLists.txt index ba6b0d19..303d410f 100644 --- a/src/implementations/CMakeLists.txt +++ b/src/implementations/CMakeLists.txt @@ -34,6 +34,7 @@ if(WaylandScanner_FOUND) cairo-dock-cosmic-toplevel.c cairo-dock-cosmic-toplevel.h cairo-dock-plasma-window-manager.c cairo-dock-plasma-window-manager.h cairo-dock-plasma-virtual-desktop.c cairo-dock-plasma-virtual-desktop.h + cairo-dock-cosmic-workspaces.c cairo-dock-cosmic-workspaces.h ) ecm_add_wayland_client_protocol( diff --git a/src/implementations/cairo-dock-cosmic-workspaces.c b/src/implementations/cairo-dock-cosmic-workspaces.c new file mode 100644 index 00000000..189aa7ab --- /dev/null +++ b/src/implementations/cairo-dock-cosmic-workspaces.c @@ -0,0 +1,567 @@ +/* + * cairo-dock-cosmic-workspaces.c -- desktop / workspace management + * facilities for Cosmic and compatible + * + * Copyright 2024 Daniel Kondor + * + * This program 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 3 + * of the License, or (at your option) any later version. + * + * This program 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 program. If not, see . + * + */ + +#include "cairo-dock-log.h" +#include "cairo-dock-desktop-manager.h" +#include "cairo-dock-windows-manager.h" +#include "cairo-dock-cosmic-workspaces.h" + +struct wl_output *s_ws_output = NULL; + +/* + * notes: + * Labwc: does not send coordinates (but seems to send the workspaces in the correct order), + * non-active workspaces have the "hidden" flag, workspaces can be activated, but no other + * action is supported; multiple outputs share workspaces, always only one workspace group + * Cosmic: coordinates: x starts from 1, y all zero (default config / layout), workspaces + * can be activated, no other action + */ + + +typedef struct _CosmicWS { + struct zcosmic_workspace_handle_v1 *handle; // protocol object representing this desktop + char *name; // can be NULL if no name was delivered + guint x, y; // x and y coordinates; (guint)-1 means invalid + guint pending_x, pending_y; + char *pending_name; + gboolean bRemoved; // set to TRUE when receiving the removed event +} CosmicWS; + +#define INVALID_COORD (guint)-1 + +/// private variables -- track the current state of workspaces +static unsigned int s_iNumDesktops = 0; +static CosmicWS **desktops = NULL; // allocated when the first workspace is added +static unsigned int s_iDesktopCap = 0; // capacity of the above array +static unsigned int s_iCurrent = 0; // index into desktops array with the currently active desktop +static unsigned int s_iPending = 0; // workspace with pending activation + +static gboolean s_bPendingAdded = FALSE; // workspace was added or removed, need to recalculate layout + +static struct zcosmic_workspace_manager_v1 *s_pWSManager = NULL; +static struct zcosmic_workspace_group_handle_v1 *s_pWSGroup = NULL; // we support having only one workspace group for now + +static gboolean bValidX = FALSE; // if x coordinates are valid +static gboolean bValidY = FALSE; // if y coordinates are valid +static guint s_iXOffset = 0; // offset to add to x coordinates (if they don't start from 0) +static guint s_iYOffset = 0; // offset to add to x coordinates (if they don't start from 0) + +// we don't want arbitrarily large coordinates +#define MAX_DESKTOP_DIM 16 +#define MAX_DESKTOP_NUM 128 + +/* to be called after all workspaces have been delivered, from the workspace manager's done event */ +static void _update_desktop_layout () +{ + guint rows = 0, cols = 0; + g_desktopGeometry.iNbDesktops = 1; + + unsigned int i; + gboolean have_invalid_x = FALSE; + gboolean have_invalid_y = FALSE; + gboolean all_invalid_y = TRUE; + s_iXOffset = G_MAXUINT; + s_iYOffset = G_MAXUINT; + for (i = 0; i < s_iNumDesktops; i++) + { + guint x = desktops[i]->x; + guint y = desktops[i]->y; + + if (x == (guint)-1) have_invalid_x = TRUE; + else + { + if (x > cols) cols = x; + if (x < s_iXOffset) s_iXOffset = x; + } + + if (y == (guint)-1) have_invalid_y = TRUE; + else { + all_invalid_y = FALSE; + if (y > rows) rows = y; + if (y < s_iYOffset) s_iYOffset = y; + } + } + + if (s_iNumDesktops > 0 && !have_invalid_x) + { + if (have_invalid_y && all_invalid_y && cols < MAX_DESKTOP_NUM && cols/8 <= s_iNumDesktops) + { + bValidX = TRUE; + bValidY = FALSE; + g_desktopGeometry.iNbViewportX = cols + 1 - s_iXOffset; + g_desktopGeometry.iNbViewportY = 1; + return; + } + if (!have_invalid_y && cols < MAX_DESKTOP_DIM && rows < MAX_DESKTOP_DIM && rows*cols < MAX_DESKTOP_NUM && (rows*cols) / 8 <= s_iNumDesktops) + { + bValidX = TRUE; + bValidY = TRUE; + g_desktopGeometry.iNbViewportX = cols + 1 - s_iXOffset; + g_desktopGeometry.iNbViewportY = rows + 1 - s_iYOffset; + return; + } + } + + bValidX = FALSE; + bValidY = FALSE; + g_desktopGeometry.iNbViewportX = s_iNumDesktops ? s_iNumDesktops : 1; + g_desktopGeometry.iNbViewportY = 1; +} + +static void _update_current_desktop (void) +{ + g_desktopGeometry.iCurrentDesktop = 0; + if (bValidX && s_iCurrent < s_iNumDesktops) + { + CosmicWS *desktop = desktops[s_iCurrent]; + g_desktopGeometry.iCurrentViewportX = desktop->x - s_iXOffset; + if (bValidY) g_desktopGeometry.iCurrentViewportY = desktop->y - s_iYOffset; + else g_desktopGeometry.iCurrentViewportY = 0; + } + else + { + g_desktopGeometry.iCurrentViewportX = s_iCurrent; + g_desktopGeometry.iCurrentViewportY = 0; + } +} + +static void _name (void *data, struct zcosmic_workspace_handle_v1* handle, const char *name) +{ + cd_warning ("workspace name: %p, %s", handle, name ? name : "(null)"); + CosmicWS *desktop = (CosmicWS*)data; + g_free (desktop->pending_name); + desktop->pending_name = g_strdup ((gchar *)name); +} + +static void _coordinates (void *data, struct zcosmic_workspace_handle_v1* handle, struct wl_array *coords) +{ + CosmicWS *desktop = (CosmicWS*)data; + uint32_t *cdata = (uint32_t*)coords->data; + size_t size = coords->size; + if (size > 2*sizeof(uint32_t) || size < sizeof(uint32_t)) + { + // too many or no coordinates, we cannot use them + desktop->pending_x = (guint)-1; + desktop->pending_y = (guint)-1; + } + else + { + // we have at least one coordinate + desktop->pending_x = cdata[0]; + if (size == 2*sizeof(uint32_t)) desktop->pending_y = cdata[1]; + else desktop->pending_y = (guint)-1; + } + cd_warning ("workspace coordinates: %p, size: %lu, x: %u, y: %u", handle, size, desktop->pending_x, desktop->pending_y); +} + +static void _state (void *data, struct zcosmic_workspace_handle_v1* handle, struct wl_array *state) +{ + gboolean bActivated = FALSE; + gboolean bUrgent = FALSE; + gboolean bHidden = FALSE; + int i; + uint32_t* stdata = (uint32_t*)state->data; + for (i = 0; i*sizeof(uint32_t) < state->size; i++) + { + if (stdata[i] == ZCOSMIC_WORKSPACE_HANDLE_V1_STATE_ACTIVE) + bActivated = TRUE; + else if (stdata[i] == ZCOSMIC_WORKSPACE_HANDLE_V1_STATE_URGENT) + bUrgent = TRUE; + else if (stdata[i] == ZCOSMIC_WORKSPACE_HANDLE_V1_STATE_HIDDEN) + bHidden = TRUE; + } + cd_warning ("workspace state: %p, activated: %d, urgent: %d, hidden: %d", handle, !!bActivated, !!bUrgent, !!bHidden); + + if (bActivated) + { + CosmicWS *desktop = (CosmicWS*)data; + unsigned int j; + for (j = 0; j < s_iNumDesktops; j++) + if (desktops[j] == desktop) + { + s_iPending = j; + return; + } + cd_critical ("cosmic-workspaces: could not find currently activated desktop!"); + } +} + +static void _capabilities (void*, struct zcosmic_workspace_handle_v1* handle, struct wl_array* cap) +{ + uint32_t* capdata = (uint32_t*)cap->data; + cd_warning ("workspace capabilities: %p", handle); + int i; + for (i = 0; i*sizeof(uint32_t) < cap->size; i++) + { + if (capdata[i] == ZCOSMIC_WORKSPACE_HANDLE_V1_ZCOSMIC_WORKSPACE_CAPABILITIES_V1_ACTIVATE) + g_print ("workspace can be activated\n"); + else if (capdata[i] == ZCOSMIC_WORKSPACE_HANDLE_V1_ZCOSMIC_WORKSPACE_CAPABILITIES_V1_DEACTIVATE) + g_print ("workspace can be deactivated\n"); + else if (capdata[i] == ZCOSMIC_WORKSPACE_HANDLE_V1_ZCOSMIC_WORKSPACE_CAPABILITIES_V1_REMOVE) + g_print ("workspace can be removed\n"); + else if (capdata[i] == ZCOSMIC_WORKSPACE_HANDLE_V1_ZCOSMIC_WORKSPACE_CAPABILITIES_V1_RENAME) + g_print ("workspace can be renamed\n"); + else if (capdata[i] == ZCOSMIC_WORKSPACE_HANDLE_V1_ZCOSMIC_WORKSPACE_CAPABILITIES_V1_SET_TILING_STATE) + g_print ("workspace can set tiling state\n"); + } +} + +static void _removed (void *data, struct zcosmic_workspace_handle_v1 *handle) +{ + cd_warning ("workspace removed: %p", handle); + CosmicWS *desktop = (CosmicWS*)data; + desktop->bRemoved = TRUE; +} + + +static void _free_workspace (CosmicWS *desktop) +{ + g_free (desktop->name); + g_free (desktop->pending_name); + zcosmic_workspace_handle_v1_destroy (desktop->handle); + g_free (desktop); +} + + +static void _tiling_state (void*, struct zcosmic_workspace_handle_v1*, uint32_t) +{ + /* don't care */ +} + +static const struct zcosmic_workspace_handle_v1_listener desktop_listener = { + .name = _name, + .coordinates = _coordinates, + .state = _state, + .capabilities = _capabilities, + .remove = _removed, + .tiling_state = _tiling_state +}; + + +static void _group_capabilities (void*, struct zcosmic_workspace_group_handle_v1* handle, struct wl_array* cap) +{ + /* don't care */ + uint32_t* capdata = (uint32_t*)cap->data; + int i; + for (i = 0; i*sizeof(uint32_t) < cap->size; i++) + { + if (capdata[i] == ZCOSMIC_WORKSPACE_GROUP_HANDLE_V1_CREATE_WORKSPACE) + cd_warning ("workspace group can be add workspaces: %p\n", handle); + } +} + +static void _output_enter (void*, struct zcosmic_workspace_group_handle_v1* handle, struct wl_output* output) +{ + if (handle == s_pWSGroup) s_ws_output = output; +} + +static void _output_leave (void*, struct zcosmic_workspace_group_handle_v1* handle, struct wl_output* output) +{ + if (handle == s_pWSGroup && output == s_ws_output) s_ws_output = NULL; +} + +static void _desktop_created (void*, struct zcosmic_workspace_group_handle_v1 *manager, + struct zcosmic_workspace_handle_v1 *new_workspace) +{ + cd_warning ("new workspace: %p, %p", manager, new_workspace); + if (manager != s_pWSGroup) + { + // this is not "our" manager, we don't care + zcosmic_workspace_handle_v1_destroy (new_workspace); + return; + } + + CosmicWS *desktop = g_new0 (CosmicWS, 1); + desktop->x = INVALID_COORD; + desktop->y = INVALID_COORD; + desktop->pending_x = INVALID_COORD; + desktop->pending_y = INVALID_COORD; + if (s_iNumDesktops >= s_iDesktopCap) + { + desktops = g_renew (CosmicWS*, desktops, s_iNumDesktops + 16); + s_iDesktopCap = s_iNumDesktops + 1; + } + desktops[s_iNumDesktops] = desktop; + s_iNumDesktops++; + + desktop->handle = new_workspace; + zcosmic_workspace_handle_v1_add_listener (new_workspace, &desktop_listener, desktop); + s_bPendingAdded = TRUE; +} + +static void _group_removed (void*, struct zcosmic_workspace_group_handle_v1 *handle) +{ + cd_warning ("workspace group removed: %p", handle); + if (handle == s_pWSGroup) + { + + if (s_iNumDesktops) + { + // the compositor should have deleted all desktop handles before + cd_critical ("cosmic-workspaces: non-empty workspace group removed!"); + do { + --s_iNumDesktops; + _free_workspace (desktops[s_iNumDesktops]); + } while(s_iNumDesktops); + } + + g_free (desktops); + desktops = NULL; + s_iDesktopCap = 0; + _update_desktop_layout (); + gldi_object_notify (&myDesktopMgr, NOTIFICATION_DESKTOP_GEOMETRY_CHANGED, FALSE); + s_pWSGroup = NULL; + } + zcosmic_workspace_group_handle_v1_destroy (handle); +} + + +static const struct zcosmic_workspace_group_handle_v1_listener group_listener = { + .capabilities = _group_capabilities, + .output_enter = _output_enter, + .output_leave = _output_leave, + .workspace = _desktop_created, + .remove = _group_removed +}; + + +static void _new_workspace_group (void*, struct zcosmic_workspace_manager_v1*, struct zcosmic_workspace_group_handle_v1 *new_group) +{ + cd_warning ("new workspace group: %p", new_group); + if (s_pWSGroup) + { + cd_warning ("cosmic-workspaces: multiple workspace groups are not supported!\n"); + zcosmic_workspace_group_handle_v1_add_listener (new_group, &group_listener, NULL); + zcosmic_workspace_group_handle_v1_destroy (new_group); + } + else + { + s_pWSGroup = new_group; + zcosmic_workspace_group_handle_v1_add_listener (new_group, &group_listener, NULL); + } +} + +static void _done (void*, struct zcosmic_workspace_manager_v1*) +{ + cd_warning ("workspace manager done"); + gboolean bRemoved = FALSE; // if any workspace was removed + gboolean bCoords = FALSE; // any of the coordinates changed + gboolean bName = FALSE; // any of the names changed + // check all workspaces + unsigned int i, j = 0; + for (i = 0; i < s_iNumDesktops; i++) + { + if (desktops[i]->bRemoved) + { + _free_workspace (desktops[i]); + desktops[i] = NULL; + bRemoved = TRUE; + } + else { + if (desktops[i]->pending_x != desktops[i]->x) + { + desktops[i]->x = desktops[i]->pending_x; + bCoords = TRUE; + } + if (desktops[i]->pending_y != desktops[i]->y) + { + desktops[i]->y = desktops[i]->pending_y; + bCoords = TRUE; + } + if (desktops[i]->pending_name) + { + g_free (desktops[i]->name); + desktops[i]->name = desktops[i]->pending_name; + desktops[i]->pending_name = NULL; + bName = TRUE; + } + + if (i != j) desktops[j] = desktops[i]; + j++; + } + } + + s_iNumDesktops = j; // remaining number of desktops + + if (bRemoved || bCoords || s_bPendingAdded) + { + s_iCurrent = s_iPending; + if (s_iCurrent >= s_iNumDesktops) s_iCurrent = s_iNumDesktops ? (s_iNumDesktops - 1) : 0; + _update_desktop_layout (); + _update_current_desktop (); + gldi_object_notify (&myDesktopMgr, NOTIFICATION_DESKTOP_GEOMETRY_CHANGED, FALSE); + gldi_object_notify (&myDesktopMgr, NOTIFICATION_DESKTOP_CHANGED); + } + else if (s_iCurrent != s_iPending) + { + s_iCurrent = s_iPending; + _update_current_desktop (); + gldi_object_notify (&myDesktopMgr, NOTIFICATION_DESKTOP_CHANGED); + } + + if (bName) gldi_object_notify (&myDesktopMgr, NOTIFICATION_DESKTOP_NAMES_CHANGED); + s_bPendingAdded = FALSE; +} + +static void _finished (void*, struct zcosmic_workspace_manager_v1 *handle) +{ + cd_warning ("workspace manager finished"); + zcosmic_workspace_manager_v1_destroy (handle); +} + +static const struct zcosmic_workspace_manager_v1_listener manager_listener = { + .workspace_group = _new_workspace_group, + .done = _done, + .finished = _finished +}; + + +static gchar** _get_desktops_names (void) +{ + gchar **ret = g_new0 (gchar*, s_iNumDesktops + 1); // + 1, so that it is a null-terminated list, as expected by the switcher plugin + unsigned int i; + for (i = 0; i < s_iNumDesktops; i++) ret[i] = g_strdup (desktops[i]->name); + return ret; +} + +static unsigned int _get_ix (guint x, guint y) +{ + unsigned int i = 0; + if (bValidX) + { + for (; i < s_iNumDesktops; i++) + { + if (!desktops[i]->bRemoved && (desktops[i]->x == x + s_iXOffset) + && (!bValidY || desktops[i]->y == y + s_iYOffset)) + break; + } + } + else i = x; + return i; +} + +static gboolean _set_current_desktop (G_GNUC_UNUSED int iDesktopNumber, int iViewportNumberX, int iViewportNumberY) +{ + // desktop number is ignored (it should be 0) + if (iViewportNumberX >= 0 && iViewportNumberY >= 0) + { + unsigned int iReq = _get_ix ((guint)iViewportNumberX, (guint)iViewportNumberY); + if (iReq < s_iNumDesktops) + { + zcosmic_workspace_handle_v1_activate (desktops[iReq]->handle); + zcosmic_workspace_manager_v1_commit (s_pWSManager); + return TRUE; // we don't know if we succeeded + } + } + cd_warning ("cosmic-workspaces: invalid workspace requested!\n"); + return FALSE; +} + +/* currently not supported on either Cosmic or Labwc, easier to disable +static void _add_workspace (void) +{ + if (s_pWSGroup && s_pWSManager) + { + char *name = g_strdup_printf ("Workspace %u", s_iNumDesktops + 1); + zcosmic_workspace_group_handle_v1_create_workspace (s_pWSGroup, name); + zcosmic_workspace_manager_v1_commit (s_pWSManager); + g_free (name); + } +} + +static void _remove_workspace (void) +{ + if (s_iNumDesktops <= 1 || !s_pWSManager) return; + zcosmic_workspace_handle_v1_remove (desktops[s_iNumDesktops - 1]->handle); + zcosmic_workspace_manager_v1_commit (s_pWSManager); +} +*/ + +static uint32_t protocol_id, protocol_version; +static gboolean protocol_found = FALSE; + +gboolean gldi_cosmic_workspaces_match_protocol (uint32_t id, const char *interface, uint32_t version) +{ + if (!strcmp(interface, zcosmic_workspace_manager_v1_interface.name)) + { + protocol_found = TRUE; + protocol_id = id; + protocol_version = version; + if ((uint32_t)zcosmic_workspace_manager_v1_interface.version < protocol_version) + protocol_version = zcosmic_workspace_manager_v1_interface.version; + return TRUE; + } + return FALSE; +} + +gboolean gldi_cosmic_workspaces_try_init (struct wl_registry *registry) +{ + if (!protocol_found) return FALSE; + s_pWSManager = wl_registry_bind (registry, protocol_id, &zcosmic_workspace_manager_v1_interface, protocol_version); + if (!s_pWSManager) return FALSE; + + GldiDesktopManagerBackend dmb; + memset (&dmb, 0, sizeof (GldiDesktopManagerBackend)); + dmb.set_current_desktop = _set_current_desktop; + dmb.get_desktops_names = _get_desktops_names; +// dmb.add_workspace = _add_workspace; +// dmb.remove_last_workspace = _remove_workspace; + gldi_desktop_manager_register_backend (&dmb, "cosmic-workspaces"); + + zcosmic_workspace_manager_v1_add_listener (s_pWSManager, &manager_listener, NULL); + return TRUE; +} + +struct zcosmic_workspace_handle_v1 *gldi_cosmic_workspaces_get_handle (int x, int y) +{ + unsigned int iReq = _get_ix ((guint)x, (guint)y); + if (iReq < s_iNumDesktops) + return desktops[iReq]->handle; + cd_warning ("cosmic-workspaces: invalid workspace requested!\n"); + return NULL; +} + +void gldi_cosmic_workspaces_update_window (GldiWindowActor *actor, struct zcosmic_workspace_handle_v1 *handle) +{ + unsigned int i; + for (i = 0; i < s_iNumDesktops; i++) + { + if (desktops[i]->handle == handle) + { + actor->iNumDesktop = 0; + if (bValidX) + { + actor->iViewPortX = desktops[i]->x - s_iXOffset; + if (bValidY) actor->iViewPortY = desktops[i]->y - s_iYOffset; + else actor->iViewPortY = 0; + } + else + { + actor->iViewPortX = i; + actor->iViewPortY = 0; + } + gldi_object_notify (&myWindowObjectMgr, NOTIFICATION_WINDOW_DESKTOP_CHANGED, actor); + return; + } + } + cd_warning ("cosmic-workspaces: workspace not found!\n"); +} + + diff --git a/src/implementations/cairo-dock-cosmic-workspaces.h b/src/implementations/cairo-dock-cosmic-workspaces.h new file mode 100644 index 00000000..d207caef --- /dev/null +++ b/src/implementations/cairo-dock-cosmic-workspaces.h @@ -0,0 +1,36 @@ +/* + * cairo-dock-cosmic-workspaces.h -- desktop / workspace management + * facilities for Cosmic and compatible + * + * Copyright 2024 Daniel Kondor + * + * This program 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 3 + * of the License, or (at your option) any later version. + * + * This program 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 program. If not, see . + * + */ + +#ifndef CAIRO_DOCK_COSMIC_WORKSPACES_H +#define CAIRO_DOCK_COSMIC_WORKSPACES_H + +#include +#include +#include "wayland-cosmic-workspace-client-protocol.h" + +extern struct wl_output *s_ws_output; + +gboolean gldi_cosmic_workspaces_match_protocol (uint32_t id, const char *interface, uint32_t version); +gboolean gldi_cosmic_workspaces_try_init (struct wl_registry *registry); +struct zcosmic_workspace_handle_v1 *gldi_cosmic_workspaces_get_handle (int x, int y); +void gldi_cosmic_workspaces_update_window (GldiWindowActor *actor, struct zcosmic_workspace_handle_v1 *handle); + +#endif + diff --git a/src/implementations/cairo-dock-wayland-manager.c b/src/implementations/cairo-dock-wayland-manager.c index f380ba43..9f6aa439 100644 --- a/src/implementations/cairo-dock-wayland-manager.c +++ b/src/implementations/cairo-dock-wayland-manager.c @@ -49,6 +49,7 @@ #include "cairo-dock-plasma-window-manager.h" #include "cairo-dock-cosmic-toplevel.h" #include "cairo-dock-plasma-virtual-desktop.h" +#include "cairo-dock-cosmic-workspaces.h" #endif #include "cairo-dock-wayland-hotspots.h" #include "cairo-dock-egl.h" @@ -532,7 +533,14 @@ static void _registry_global_cb ( G_GNUC_UNUSED void *data, struct wl_registry * { cd_debug("Found plasma-virtual-desktop-manager"); } - else gldi_cosmic_toplevel_match_protocol (id, interface, version); + else if (gldi_cosmic_toplevel_match_protocol (id, interface, version)) + { + cd_debug("Found cosmic-toplevel-manager"); + } + else if (gldi_cosmic_workspaces_match_protocol (id, interface, version)) + { + cd_debug("Found cosmic-workspace-manager"); + } #endif s_bInitializing = TRUE; } @@ -587,10 +595,19 @@ static void init (void) if (gldi_wayland_hotspots_try_init (registry)) cmb.update_polling_screen_edge = gldi_wayland_hotspots_update; #ifdef HAVE_WAYLAND_PROTOCOLS - if (!gldi_cosmic_toplevel_try_init (registry)) - if (!gldi_plasma_window_manager_try_init (registry)) + gboolean bCosmic = gldi_cosmic_toplevel_try_init (registry); + if (!bCosmic) if (!gldi_plasma_window_manager_try_init (registry)) gldi_wlr_foreign_toplevel_try_init (registry); - gldi_plasma_virtual_desktop_try_init (registry); + if (bCosmic) + { + if (!gldi_cosmic_workspaces_try_init (registry)) + gldi_plasma_virtual_desktop_try_init (registry); + } + else + { + if (!gldi_plasma_virtual_desktop_try_init (registry)) + gldi_cosmic_workspaces_try_init (registry); + } #endif cmb.set_input_shape = _set_input_shape; cmb.is_wayland = _is_wayland; From 45d523da56bb4ba0ad01193601b701e4376db1dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kondor=20D=C3=A1niel?= Date: Sat, 26 Oct 2024 16:38:19 +0200 Subject: [PATCH 10/24] cosmic-toplevel: add workspaces - track which workspace each window is on - allow moving windows between workspaces (only tested with one output) - advertise the WM capabilities according to the new WM API --- .../cairo-dock-cosmic-toplevel.c | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/implementations/cairo-dock-cosmic-toplevel.c b/src/implementations/cairo-dock-cosmic-toplevel.c index 731f19b3..c8a11aa5 100644 --- a/src/implementations/cairo-dock-cosmic-toplevel.c +++ b/src/implementations/cairo-dock-cosmic-toplevel.c @@ -32,6 +32,7 @@ #include "cairo-dock-log.h" #include "cairo-dock-cosmic-toplevel.h" #include "cairo-dock-wayland-wm.h" +#include "cairo-dock-cosmic-workspaces.h" #include @@ -59,6 +60,16 @@ static uint32_t manager_id, info_id, manager_version, info_version; /********************************************************************** * window manager interface -- toplevel manager */ +static void _move_to_nth_desktop (GldiWindowActor *actor, G_GNUC_UNUSED int iNumDesktop, + int x, int y) +{ + if (!s_ws_output) return; + GldiWaylandWindowActor *wactor = (GldiWaylandWindowActor *)actor; + struct zcosmic_workspace_handle_v1 *ws = gldi_cosmic_workspaces_get_handle (x, y); + //!! TODO: we need a valid wl_output here !! + if (ws) zcosmic_toplevel_manager_v1_move_to_workspace (s_ptoplevel_manager, wactor->handle, ws, s_ws_output); +} + static void _show (GldiWindowActor *actor) { if (!can_activate) return; @@ -227,11 +238,21 @@ static void _gldi_toplevel_parent_cb (void* data, G_GNUC_UNUSED wfthandle *handl } */ + +static void _workspace_entered (void *data, G_GNUC_UNUSED wfthandle *handle, struct zcosmic_workspace_handle_v1 *wshandle) +{ + cd_warning ("%p -- workspace: %p", handle, wshandle); + gldi_cosmic_workspaces_update_window ((GldiWindowActor*)data, wshandle); + gldi_object_notify (&myWindowObjectMgr, NOTIFICATION_WINDOW_DESKTOP_CHANGED, data); +} + static void _dummy (G_GNUC_UNUSED void *data, G_GNUC_UNUSED wfthandle *handle, G_GNUC_UNUSED void *workspace) { } + + /********************************************************************** * interface and object manager definitions */ @@ -248,7 +269,7 @@ static struct zcosmic_toplevel_handle_v1_listener gldi_toplevel_handle_interface .done = _gldi_toplevel_done_cb, .closed = _gldi_toplevel_closed_cb, // .parent = _gldi_toplevel_parent_cb, - .workspace_enter = (void (*)(void*, struct zcosmic_toplevel_handle_v1*, struct zcosmic_workspace_handle_v1*))_dummy, + .workspace_enter = _workspace_entered, .workspace_leave = (void (*)(void*, struct zcosmic_toplevel_handle_v1*, struct zcosmic_workspace_handle_v1*))_dummy }; @@ -318,6 +339,7 @@ gboolean gldi_cosmic_toplevel_try_init (struct wl_registry *registry) GldiWindowManagerBackend wmb; memset (&wmb, 0, sizeof (GldiWindowManagerBackend)); wmb.get_active_window = gldi_wayland_wm_get_active_window; + wmb.move_to_viewport_abs = _move_to_nth_desktop; // wmb.move_to_nth_desktop = _move_to_nth_desktop; wmb.show = _show; wmb.close = _close; @@ -340,6 +362,7 @@ gboolean gldi_cosmic_toplevel_try_init (struct wl_registry *registry) wmb.can_minimize_maximize_close = _can_minimize_maximize_close; // wmb.get_id = _get_id; wmb.pick_window = gldi_wayland_wm_pick_window; + wmb.flags = GINT_TO_POINTER (GLDI_WM_NO_VIEWPORT_OVERLAP | GLDI_WM_GEOM_REL_TO_VIEWPORT | GLDI_WM_HAVE_WORKSPACES); wmb.name = "Cosmic"; gldi_windows_manager_register_backend (&wmb); From af57a6ddbe67e0d10d5f8e7c3922ba167b0ecfad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kondor=20D=C3=A1niel?= Date: Sat, 26 Oct 2024 17:06:21 +0200 Subject: [PATCH 11/24] applications-manager: avoid duplicating taskbar icons on startup --- src/gldit/cairo-dock-applications-manager.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gldit/cairo-dock-applications-manager.c b/src/gldit/cairo-dock-applications-manager.c index f2c2277a..050f7b69 100644 --- a/src/gldit/cairo-dock-applications-manager.c +++ b/src/gldit/cairo-dock-applications-manager.c @@ -62,7 +62,7 @@ extern gboolean g_bUseOpenGL; // private static GHashTable *s_hAppliIconsTable = NULL; // table des fenetres affichees dans le dock. -static int s_bAppliManagerIsRunning = FALSE; +static gboolean s_bAppliManagerIsRunning = FALSE; static GldiWindowActor *s_pCurrentActiveWindow = NULL; static void cairo_dock_unregister_appli (Icon *icon); @@ -88,6 +88,7 @@ static Icon *_get_appli_icon (GldiWindowActor *actor) static gboolean _on_window_created (G_GNUC_UNUSED gpointer data, GldiWindowActor *actor) { + if (!s_bAppliManagerIsRunning) return GLDI_NOTIFICATION_LET_PASS; Icon *pIcon = _get_appli_icon (actor); g_return_val_if_fail (pIcon == NULL, GLDI_NOTIFICATION_LET_PASS); From 1e7697e0b8943fe41047f883089aa13e66fc98ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kondor=20D=C3=A1niel?= Date: Sat, 26 Oct 2024 20:38:06 +0200 Subject: [PATCH 12/24] wayland-wm: stacking order + subclassing Allow a very basic tracking of windows' stacking order by always putting the most recently activated window on top. Also, allow subclassing GldiWaylandWindowActor (make the manager publicly accessible) --- src/implementations/cairo-dock-wayland-wm.c | 36 ++++++++++++++++++++- src/implementations/cairo-dock-wayland-wm.h | 9 ++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/implementations/cairo-dock-wayland-wm.c b/src/implementations/cairo-dock-wayland-wm.c index bdb33b49..37fec262 100644 --- a/src/implementations/cairo-dock-wayland-wm.c +++ b/src/implementations/cairo-dock-wayland-wm.c @@ -30,7 +30,7 @@ extern CairoDock* g_pMainDock; -static GldiObjectManager myWaylandWMObjectMgr; +GldiObjectManager myWaylandWMObjectMgr; static void (*s_handle_destroy_cb)(gpointer handle) = NULL; @@ -43,6 +43,10 @@ static GldiWindowActor* s_pMaybeActiveWindow = NULL; /* our own config window -- will only work if the docks themselves are not * reported */ static GldiWindowActor* s_pSelf = NULL; +/* internal counter for updating windows' stacking order */ +static int s_iStackCounter = 0; +/* there was a change in windows' stacking order that needs to be signaled */ +static gboolean s_bStackChange = FALSE; // extra callback for when a new app is activated // this is useful for e.g. interactively selecting a window @@ -370,6 +374,36 @@ void gldi_wayland_wm_done (GldiWaylandWindowActor *wactor) s_pCurrent = NULL; wactor = g_queue_pop_head(&s_pending_queue); // note: it is OK to call this on an empty queue } while (wactor); + + if (s_bStackChange) + { + gldi_object_notify (&myWindowObjectMgr, NOTIFICATION_WINDOW_Z_ORDER_CHANGED, NULL); + s_bStackChange = FALSE; + } +} + +static void _restack_windows (void* ptr, void*) +{ + GldiWindowActor *actor = (GldiWindowActor*)ptr; + actor->iStackOrder = s_iStackCounter; + s_iStackCounter++; +} + +void gldi_wayland_wm_stack_on_top (GldiWindowActor *actor) +{ + s_bStackChange = TRUE; + + if (s_iStackCounter < INT_MAX) + { + s_iStackCounter++; + actor->iStackOrder = s_iStackCounter; + return; + } + + // our counter would overflow, reset the order for all windows + s_iStackCounter = 0; + gldi_windows_foreach (TRUE, _restack_windows, NULL); + actor->iStackOrder = s_iStackCounter; // counter was incremented in the callback } GldiWindowActor* gldi_wayland_wm_get_active_window () diff --git a/src/implementations/cairo-dock-wayland-wm.h b/src/implementations/cairo-dock-wayland-wm.h index 4072ceef..e0785fa0 100644 --- a/src/implementations/cairo-dock-wayland-wm.h +++ b/src/implementations/cairo-dock-wayland-wm.h @@ -49,6 +49,9 @@ struct _GldiWaylandWindowActor { }; typedef struct _GldiWaylandWindowActor GldiWaylandWindowActor; +// manager for the above, can be extended by more specific implementations +extern GldiObjectManager myWaylandWMObjectMgr; + // functions to update the state of a toplevel and potentially signal // the taskbar of the changes @@ -73,6 +76,12 @@ GldiWindowActor* gldi_wayland_wm_get_active_window (); GldiWindowActor* gldi_wayland_wm_pick_window (GtkWindow *pParentWindow); +/** Change the stacking order such that actor is on top. Does not send + * a notification; the user should do that manually, or call one of + * the above functions with notify == TRUE. + *@param actor The window to put on top. + */ +void gldi_wayland_wm_stack_on_top (GldiWindowActor *actor); typedef void (*GldiWaylandWMHandleDestroyFunc)(gpointer handle); void gldi_wayland_wm_init (GldiWaylandWMHandleDestroyFunc destroy_cb); From fd1d958a540d5e944e7ddc56f270d52f08918f7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kondor=20D=C3=A1niel?= Date: Sat, 26 Oct 2024 20:41:51 +0200 Subject: [PATCH 13/24] plasma-window-management: track stacking order Subclass GldiWaylandWindowActor to store the uuids required for this. --- .../cairo-dock-plasma-window-manager.c | 106 +++++++++++++++--- 1 file changed, 90 insertions(+), 16 deletions(-) diff --git a/src/implementations/cairo-dock-plasma-window-manager.c b/src/implementations/cairo-dock-plasma-window-manager.c index 85f13f4b..7f51a75a 100644 --- a/src/implementations/cairo-dock-plasma-window-manager.c +++ b/src/implementations/cairo-dock-plasma-window-manager.c @@ -33,6 +33,8 @@ * (this probably needs support for the plasma-virtual-desktop protocol) */ +#define _GNU_SOURCE + #include #include "wayland-plasma-window-management-client-protocol.h" #include "cairo-dock-windows-manager.h" @@ -44,11 +46,31 @@ #include "cairo-dock-plasma-virtual-desktop.h" #include +#include typedef struct org_kde_plasma_window pwhandle; +static GldiObjectManager myPlasmaWindowObjectMgr; + +struct _GldiPlasmaWindowActor { + GldiWaylandWindowActor wactor; + + // uuid, can be used to identify windows, e.g. when updating the stacking order + char *uuid; +// char *themed_icon_name; -- supplied as a separate event, not sure if we need it + unsigned int pid; // pid of the process associated with this window +}; +typedef struct _GldiPlasmaWindowActor GldiPlasmaWindowActor; + +typedef enum { + NB_NOTIFICATIONS_PLASMA_WINDOW_MANAGER = NB_NOTIFICATIONS_WINDOWS +} CairoPlasmaWMNotifications; + +static uint32_t protocol_version = 0; +static GHashTable *s_hIDTable = NULL; + // window manager interface static void _move_to_nth_desktop (GldiWindowActor *actor, G_GNUC_UNUSED int iNumDesktop, @@ -160,7 +182,15 @@ static void _gldi_toplevel_state_cb (void *data, G_GNUC_UNUSED pwhandle *handle, gldi_wayland_wm_fullscreen_changed (wactor, !!(flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREEN), FALSE); gldi_wayland_wm_attention_changed (wactor, !!(flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_DEMANDS_ATTENTION), FALSE); gldi_wayland_wm_skip_changed (wactor, !!(flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPTASKBAR), FALSE); - if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ACTIVE) gldi_wayland_wm_activated (wactor, FALSE); + if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ACTIVE) + { + gldi_wayland_wm_activated (wactor, FALSE); + // versions >= 12 and < 17 have stacking_order_uuid_changed which + // is handled separately below; for versions >= 17, we would need + // to support stacking_order_changed_2 + if (protocol_version < 12 || protocol_version >= 17) + gldi_wayland_wm_stack_on_top ((GldiWindowActor*)wactor); + } if (wactor->init_done) gldi_wayland_wm_done (wactor); } @@ -187,7 +217,6 @@ static void _gldi_toplevel_parent_cb (void* data, G_GNUC_UNUSED pwhandle *handle static void _gldi_toplevel_geometry_cb (void* data, G_GNUC_UNUSED pwhandle *handle, int32_t x, int32_t y, uint32_t w, uint32_t h) { - //!! TODO !! GldiWaylandWindowActor* wactor = (GldiWaylandWindowActor*)data; GldiWindowActor* actor = (GldiWindowActor*)wactor; actor->windowGeometry.width = w; @@ -227,9 +256,10 @@ static void _gldi_toplevel_icon_changed_cb (G_GNUC_UNUSED void* data, G_GNUC_UNU /* don't care */ } -static void _gldi_toplevel_pid_changed_cb (G_GNUC_UNUSED void* data, G_GNUC_UNUSED pwhandle *handle, G_GNUC_UNUSED uint32_t pid) +static void _gldi_toplevel_pid_changed_cb (void* data, G_GNUC_UNUSED pwhandle *handle, uint32_t pid) { - /* don't care */ + GldiPlasmaWindowActor *pactor = (GldiPlasmaWindowActor*)data; + if (pactor) pactor->pid = pid; } static void _gldi_toplevel_application_menu_cb (G_GNUC_UNUSED void* data, G_GNUC_UNUSED pwhandle *handle, @@ -258,19 +288,18 @@ static struct org_kde_plasma_window_listener gldi_toplevel_handle_interface = { }; -static void _destroy (gpointer obj) -{ - org_kde_plasma_window_destroy((pwhandle*)obj); -} - /* register new toplevel */ static void _new_toplevel ( G_GNUC_UNUSED void *data, struct org_kde_plasma_window_management *manager, G_GNUC_UNUSED uint32_t id, const char *uuid) { - // fprintf(stderr,"New toplevel: %p\n", handle); + if (!uuid) return; // sanity check + pwhandle* handle = org_kde_plasma_window_management_get_window_by_uuid (manager, uuid); - GldiWaylandWindowActor* wactor = gldi_wayland_wm_new_toplevel (handle); + GldiPlasmaWindowActor *pactor = (GldiPlasmaWindowActor*)gldi_object_new (&myPlasmaWindowObjectMgr, handle); + pactor->uuid = g_strdup (uuid); + g_hash_table_insert (s_hIDTable, pactor->uuid, pactor); + GldiWaylandWindowActor *wactor = (GldiWaylandWindowActor*)pactor; org_kde_plasma_window_set_user_data (handle, wactor); GldiWindowActor *actor = (GldiWindowActor*)wactor; // set initial position to something sensible in case we don't get it updated @@ -305,9 +334,26 @@ static void _stacking_order_changed_cb( G_GNUC_UNUSED void *data, } static void _stacking_order_uuid_changed_cb ( G_GNUC_UNUSED void *data, - G_GNUC_UNUSED struct org_kde_plasma_window_management *org_kde_plasma_window_management, G_GNUC_UNUSED const char *uuids) -{ - /* don't care */ + G_GNUC_UNUSED struct org_kde_plasma_window_management *org_kde_plasma_window_management, const char *uuids) +{ + int i = 0; + GString *str = NULL; + do { + const char *next = strchrnul (uuids, ';'); + if (!str) str = g_string_sized_new (next - uuids + 1); + g_string_overwrite_len (str, 0, uuids, next - uuids); + void *ptr = g_hash_table_lookup (s_hIDTable, str->str); + if (ptr) + { + GldiWindowActor *actor = (GldiWindowActor*)ptr; + actor->iStackOrder = i; + i++; + } + uuids = next; + if (*uuids) uuids++; // skip the trailing ';' + } while (*uuids); + if (str) g_string_free (str, TRUE); + gldi_object_notify (&myWindowObjectMgr, NOTIFICATION_WINDOW_Z_ORDER_CHANGED, NULL); } static struct org_kde_plasma_window_management_listener gldi_toplevel_manager = { @@ -319,7 +365,7 @@ static struct org_kde_plasma_window_management_listener gldi_toplevel_manager = }; static struct org_kde_plasma_window_management* s_ptoplevel_manager = NULL; -static uint32_t protocol_id, protocol_version; +static uint32_t protocol_id; static gboolean protocol_found = FALSE; /// Desktop management functions @@ -330,8 +376,24 @@ static gboolean _show_hide_desktop (gboolean bShow) return TRUE; } +void _reset_object (GldiObject* obj) +{ + GldiPlasmaWindowActor* pactor = (GldiPlasmaWindowActor*)obj; + if (pactor) + { + g_hash_table_remove (s_hIDTable, pactor->uuid); + g_free (pactor->uuid); + org_kde_plasma_window_destroy((pwhandle*)pactor->wactor.handle); + } +} + static void gldi_plasma_window_manager_init () { + // hash table to map uuids to windows + // note: no free functions, the keys are the uuids stored in (and owned by) + // the GldiPlasmaWindowActor, they are freed together + s_hIDTable = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); + org_kde_plasma_window_management_add_listener(s_ptoplevel_manager, &gldi_toplevel_manager, NULL); // register window manager GldiWindowManagerBackend wmb; @@ -369,7 +431,19 @@ static void gldi_plasma_window_manager_init () dmb.show_hide_desktop = _show_hide_desktop; gldi_desktop_manager_register_backend (&dmb, "plasma-window-management"); - gldi_wayland_wm_init (_destroy); + gldi_wayland_wm_init (NULL); + + // extend the generic Wayland toplevel object manager + memset (&myPlasmaWindowObjectMgr, 0, sizeof (GldiObjectManager)); + myPlasmaWindowObjectMgr.cName = "plasma-window-manager"; + myPlasmaWindowObjectMgr.iObjectSize = sizeof (GldiPlasmaWindowActor); + // interface + myPlasmaWindowObjectMgr.init_object = NULL; + myPlasmaWindowObjectMgr.reset_object = _reset_object; + // signals + gldi_object_install_notifications (&myPlasmaWindowObjectMgr, NB_NOTIFICATIONS_PLASMA_WINDOW_MANAGER); + // parent object + gldi_object_set_manager (GLDI_OBJECT (&myPlasmaWindowObjectMgr), &myWaylandWMObjectMgr); } From 2a10b89a7476bcb7143e05a86e60e2a9eacc4c5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kondor=20D=C3=A1niel?= Date: Sat, 26 Oct 2024 21:10:19 +0200 Subject: [PATCH 14/24] plasma-wm: implement forced quit using the pid supplied --- .../cairo-dock-plasma-window-manager.c | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/implementations/cairo-dock-plasma-window-manager.c b/src/implementations/cairo-dock-plasma-window-manager.c index 7f51a75a..83cc42b0 100644 --- a/src/implementations/cairo-dock-plasma-window-manager.c +++ b/src/implementations/cairo-dock-plasma-window-manager.c @@ -61,6 +61,7 @@ struct _GldiPlasmaWindowActor { char *uuid; // char *themed_icon_name; -- supplied as a separate event, not sure if we need it unsigned int pid; // pid of the process associated with this window + unsigned int sigkill_timeout; // set to an event source if the user requested to kill this process }; typedef struct _GldiPlasmaWindowActor GldiPlasmaWindowActor; @@ -159,6 +160,29 @@ static void _set_minimize_position (GldiWindowActor *actor, GtkWidget* pContaine } +gboolean _send_sigkill (void *data) +{ + GldiPlasmaWindowActor *pactor = (GldiPlasmaWindowActor*)data; + if (pactor->pid) kill (pactor->pid, SIGKILL); + pactor->sigkill_timeout = 0; + return G_SOURCE_REMOVE; +} + +// kill the process corresponding to a window +// note that this depends on the pid provided by KWin and is thus +// inherently racy, if the pid is reused between the process exit and +// KWin sending the unmapped event +static void _kill (GldiWindowActor *actor) +{ + GldiPlasmaWindowActor *pactor = (GldiPlasmaWindowActor*)actor; + if (!pactor->pid) return; + // 1. we try sigterm, so that the app has a chance to shut down gracefully + kill (pactor->pid, SIGTERM); + // 2. after 2s, we send sigkill (if the window is still not closed) + pactor->sigkill_timeout = g_timeout_add (2000, _send_sigkill, pactor); +} + + /** callbacks **/ static void _gldi_toplevel_title_cb (void *data, G_GNUC_UNUSED pwhandle *handle, const char *title) { @@ -205,6 +229,12 @@ static void _gldi_toplevel_done_cb ( void *data, G_GNUC_UNUSED pwhandle *handle) static void _gldi_toplevel_closed_cb (void *data, G_GNUC_UNUSED pwhandle *handle) { + GldiPlasmaWindowActor *pactor = (GldiPlasmaWindowActor*)data; + if (pactor->sigkill_timeout) + { + g_source_remove (pactor->sigkill_timeout); + pactor->sigkill_timeout = 0; + } gldi_wayland_wm_closed (data, TRUE); } @@ -402,7 +432,7 @@ static void gldi_plasma_window_manager_init () wmb.move_to_viewport_abs = _move_to_nth_desktop; wmb.show = _show; wmb.close = _close; - // wmb.kill = _kill; + wmb.kill = _kill; wmb.minimize = _minimize; // wmb.lower = _lower; wmb.maximize = _maximize; From a572baf6c0e8e9974a761dc8583dcd4ceb56628b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kondor=20D=C3=A1niel?= Date: Sat, 26 Oct 2024 22:21:20 +0200 Subject: [PATCH 15/24] plasma-wm: implement additional functionality --- src/gldit/cairo-dock-windows-manager.c | 2 +- .../cairo-dock-plasma-window-manager.c | 57 ++++++++++++++++--- src/implementations/cairo-dock-wayland-wm.c | 22 ++++++- src/implementations/cairo-dock-wayland-wm.h | 2 + 4 files changed, 71 insertions(+), 12 deletions(-) diff --git a/src/gldit/cairo-dock-windows-manager.c b/src/gldit/cairo-dock-windows-manager.c index bd562cd7..690e38a8 100644 --- a/src/gldit/cairo-dock-windows-manager.c +++ b/src/gldit/cairo-dock-windows-manager.c @@ -255,7 +255,7 @@ GldiWindowActor *gldi_window_get_transient_for (GldiWindowActor *actor) void gldi_window_is_above_or_below (GldiWindowActor *actor, gboolean *bIsAbove, gboolean *bIsBelow) { - if (s_backend.set_window_border) + if (s_backend.is_above_or_below) s_backend.is_above_or_below (actor, bIsAbove, bIsBelow); else { diff --git a/src/implementations/cairo-dock-plasma-window-manager.c b/src/implementations/cairo-dock-plasma-window-manager.c index 83cc42b0..5b365720 100644 --- a/src/implementations/cairo-dock-plasma-window-manager.c +++ b/src/implementations/cairo-dock-plasma-window-manager.c @@ -62,6 +62,7 @@ struct _GldiPlasmaWindowActor { // char *themed_icon_name; -- supplied as a separate event, not sure if we need it unsigned int pid; // pid of the process associated with this window unsigned int sigkill_timeout; // set to an event source if the user requested to kill this process + unsigned int cap_and_state; // capabilites and state that is not stored elsewhere }; typedef struct _GldiPlasmaWindowActor GldiPlasmaWindowActor; @@ -107,6 +108,17 @@ static void _minimize (GldiWindowActor *actor) ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZED, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZED); } +/* does not work this way, would need to use timeout +static void _lower (GldiWindowActor *actor) +{ + GldiWaylandWindowActor *wactor = (GldiWaylandWindowActor *)actor; + org_kde_plasma_window_set_state (wactor->handle, + ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_BELOW, + ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_BELOW); + org_kde_plasma_window_set_state (wactor->handle, + ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_BELOW, 0); +} +*/ static void _maximize (GldiWindowActor *actor, gboolean bMaximize) { GldiWaylandWindowActor *wactor = (GldiWaylandWindowActor *)actor; @@ -121,6 +133,13 @@ static void _fullscreen (GldiWindowActor *actor, gboolean bFullScreen) ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREEN, bFullScreen ? ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREEN : 0); } +static void _set_above (GldiWindowActor *actor, gboolean bAbove) +{ + GldiWaylandWindowActor *wactor = (GldiWaylandWindowActor *)actor; + org_kde_plasma_window_set_state (wactor->handle, + ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_ABOVE, + bAbove ? ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_ABOVE : 0); +} static GldiWindowActor* _get_transient_for(GldiWindowActor* actor) { @@ -131,12 +150,28 @@ static GldiWindowActor* _get_transient_for(GldiWindowActor* actor) return (GldiWindowActor*)pactor; } +static void _is_above_or_below (GldiWindowActor *actor, gboolean *bIsAbove, gboolean *bIsBelow) +{ + GldiPlasmaWindowActor *pactor = (GldiPlasmaWindowActor *)actor; + *bIsAbove = !!(pactor->cap_and_state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_ABOVE); + *bIsBelow = !!(pactor->cap_and_state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_BELOW); +} + +static void _set_sticky (GldiWindowActor *actor, gboolean bSticky) +{ + GldiWaylandWindowActor *wactor = (GldiWaylandWindowActor *)actor; + // note: this seems to do nothing + org_kde_plasma_window_set_state (wactor->handle, + ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ON_ALL_DESKTOPS, + bSticky ? ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ON_ALL_DESKTOPS : 0); +} + static void _can_minimize_maximize_close ( G_GNUC_UNUSED GldiWindowActor *actor, gboolean *bCanMinimize, gboolean *bCanMaximize, gboolean *bCanClose) { - // we don't know, set everything to true - *bCanMinimize = TRUE; - *bCanMaximize = TRUE; - *bCanClose = TRUE; + GldiPlasmaWindowActor *pactor = (GldiPlasmaWindowActor *)actor; + *bCanMinimize = !!(pactor->cap_and_state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZABLE); + *bCanMaximize = !!(pactor->cap_and_state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZABLE); + *bCanClose = !!(pactor->cap_and_state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_CLOSEABLE); } /// TODO: which one of these two are really used? In cairo-dock-X-manager.c, @@ -199,13 +234,18 @@ static void _gldi_toplevel_appid_cb (void *data, G_GNUC_UNUSED pwhandle *handle, static void _gldi_toplevel_state_cb (void *data, G_GNUC_UNUSED pwhandle *handle, uint32_t flags) { if (!data) return; - GldiWaylandWindowActor* wactor = (GldiWaylandWindowActor*)data; + GldiPlasmaWindowActor* pactor = (GldiPlasmaWindowActor*)data; + // save the flags that contains other useful info (keep above / below, capabilities) + pactor->cap_and_state = flags; + + GldiWaylandWindowActor* wactor = (GldiWaylandWindowActor*)data; gldi_wayland_wm_maximized_changed (wactor, !!(flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZED), FALSE); gldi_wayland_wm_minimized_changed (wactor, !!(flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZED), FALSE); gldi_wayland_wm_fullscreen_changed (wactor, !!(flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREEN), FALSE); gldi_wayland_wm_attention_changed (wactor, !!(flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_DEMANDS_ATTENTION), FALSE); gldi_wayland_wm_skip_changed (wactor, !!(flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPTASKBAR), FALSE); + gldi_wayland_wm_sticky_changed (wactor, !!(flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ON_ALL_DESKTOPS), FALSE); if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ACTIVE) { gldi_wayland_wm_activated (wactor, FALSE); @@ -437,7 +477,7 @@ static void gldi_plasma_window_manager_init () // wmb.lower = _lower; wmb.maximize = _maximize; wmb.set_fullscreen = _fullscreen; - // wmb.set_above = _set_above; + wmb.set_above = _set_above; wmb.set_minimize_position = _set_minimize_position; wmb.set_thumbnail_area = _set_thumbnail_area; // wmb.set_window_border = _set_window_border; @@ -445,9 +485,8 @@ static void gldi_plasma_window_manager_init () // wmb.get_thumbnail_surface = _get_thumbnail_surface; // wmb.get_texture = _get_texture; wmb.get_transient_for = _get_transient_for; - // wmb.is_above_or_below = _is_above_or_below; - // wmb.is_sticky = _is_sticky; - // wmb.set_sticky = _set_sticky; + wmb.is_above_or_below = _is_above_or_below; + wmb.set_sticky = _set_sticky; wmb.can_minimize_maximize_close = _can_minimize_maximize_close; // wmb.get_id = _get_id; wmb.pick_window = gldi_wayland_wm_pick_window; diff --git a/src/implementations/cairo-dock-wayland-wm.c b/src/implementations/cairo-dock-wayland-wm.c index 37fec262..d766aadf 100644 --- a/src/implementations/cairo-dock-wayland-wm.c +++ b/src/implementations/cairo-dock-wayland-wm.c @@ -151,6 +151,12 @@ void gldi_wayland_wm_skip_changed (GldiWaylandWindowActor *wactor, gboolean skip if (notify) gldi_wayland_wm_done (wactor); } +void gldi_wayland_wm_sticky_changed (GldiWaylandWindowActor *wactor, gboolean sticky, gboolean notify) +{ + wactor->sticky_pending = sticky; + if (notify) gldi_wayland_wm_done (wactor); +} + void gldi_wayland_wm_closed (GldiWaylandWindowActor *wactor, gboolean notify) { wactor->close_pending = TRUE; @@ -211,6 +217,16 @@ static gboolean _update_attention (GldiWaylandWindowActor *wactor, gboolean noti return changed; } +static gboolean _update_sticky (GldiWaylandWindowActor *wactor, gboolean notify) +{ + GldiWindowActor* actor = (GldiWindowActor*)wactor; + gboolean changed = (wactor->sticky_pending != actor->bIsSticky); + if (changed) actor->bIsSticky = wactor->sticky_pending; + // a change in stickyness can be seen as a change in the desktop position + if (notify && changed) gldi_object_notify (&myWindowObjectMgr, NOTIFICATION_WINDOW_DESKTOP_CHANGED, actor); + return changed; +} + static gboolean _idle_done (G_GNUC_UNUSED gpointer data) { GldiWaylandWindowActor *wactor = g_queue_pop_head(&s_pending_queue); @@ -353,8 +369,10 @@ void gldi_wayland_wm_done (GldiWaylandWindowActor *wactor) // check if other properties have changed (and send a notification) if (_update_state (wactor, TRUE)) continue; // update the needs-attention property - if (_update_attention(wactor, TRUE)) continue; - + if (_update_attention (wactor, TRUE)) continue; + // update the sticky property + if (_update_sticky (wactor, TRUE)) continue; + if (actor == s_pMaybeActiveWindow) { s_pActiveWindow = actor; diff --git a/src/implementations/cairo-dock-wayland-wm.h b/src/implementations/cairo-dock-wayland-wm.h index e0785fa0..5fc7673a 100644 --- a/src/implementations/cairo-dock-wayland-wm.h +++ b/src/implementations/cairo-dock-wayland-wm.h @@ -42,6 +42,7 @@ struct _GldiWaylandWindowActor { gboolean fullscreen_pending; // fullscreen state received gboolean attention_pending; // needs-attention state received (only KDE) gboolean skip_taskbar; // should not be shown in taskbar (only KDE) + gboolean sticky_pending; // sticky state received (only KDE) gboolean close_pending; // this window has been closed gboolean init_done; // initial state has been configured @@ -66,6 +67,7 @@ void gldi_wayland_wm_minimized_changed (GldiWaylandWindowActor *wactor, gboolean void gldi_wayland_wm_fullscreen_changed (GldiWaylandWindowActor *wactor, gboolean fullscreen, gboolean notify); void gldi_wayland_wm_attention_changed (GldiWaylandWindowActor *wactor, gboolean attention, gboolean notify); void gldi_wayland_wm_skip_changed (GldiWaylandWindowActor *wactor, gboolean skip, gboolean notify); +void gldi_wayland_wm_sticky_changed (GldiWaylandWindowActor *wactor, gboolean sticky, gboolean notify); void gldi_wayland_wm_activated (GldiWaylandWindowActor *wactor, gboolean notify); void gldi_wayland_wm_closed (GldiWaylandWindowActor *wactor, gboolean notify); From 8329f82762a66acc3616b934668e3107cc8ab24c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kondor=20D=C3=A1niel?= Date: Sat, 26 Oct 2024 22:30:53 +0200 Subject: [PATCH 16/24] cosmic-toplevel: update stacking order --- src/implementations/cairo-dock-cosmic-toplevel.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/implementations/cairo-dock-cosmic-toplevel.c b/src/implementations/cairo-dock-cosmic-toplevel.c index c8a11aa5..d61d7363 100644 --- a/src/implementations/cairo-dock-cosmic-toplevel.c +++ b/src/implementations/cairo-dock-cosmic-toplevel.c @@ -63,7 +63,7 @@ static uint32_t manager_id, info_id, manager_version, info_version; static void _move_to_nth_desktop (GldiWindowActor *actor, G_GNUC_UNUSED int iNumDesktop, int x, int y) { - if (!s_ws_output) return; + if (!(s_ws_output && can_move_workspace)) return; GldiWaylandWindowActor *wactor = (GldiWaylandWindowActor *)actor; struct zcosmic_workspace_handle_v1 *ws = gldi_cosmic_workspaces_get_handle (x, y); //!! TODO: we need a valid wl_output here !! @@ -206,7 +206,10 @@ static void _gldi_toplevel_state_cb (void *data, G_GNUC_UNUSED wfthandle *handle for (i = 0; i*sizeof(uint32_t) < state->size; i++) { if (stdata[i] == ZCOSMIC_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED) + { gldi_wayland_wm_activated (wactor, FALSE); + gldi_wayland_wm_stack_on_top ((GldiWindowActor*)wactor); + } else if (stdata[i] == ZCOSMIC_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED) maximized_pending = TRUE; else if (stdata[i] == ZCOSMIC_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED) From 3924de2f85e2ebb6f0ad6e9273a1ab5d54db382a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kondor=20D=C3=A1niel?= Date: Sat, 26 Oct 2024 23:08:52 +0200 Subject: [PATCH 17/24] X-manager: refactor changing the number of viewports to make the logic reusable --- src/implementations/cairo-dock-X-manager.c | 34 ++------------------ src/implementations/cairo-dock-X-utilities.c | 20 ++++++++++++ src/implementations/cairo-dock-X-utilities.h | 11 +++++++ 3 files changed, 33 insertions(+), 32 deletions(-) diff --git a/src/implementations/cairo-dock-X-manager.c b/src/implementations/cairo-dock-X-manager.c index cb2951fb..ced3c7d4 100644 --- a/src/implementations/cairo-dock-X-manager.c +++ b/src/implementations/cairo-dock-X-manager.c @@ -789,40 +789,11 @@ static gboolean _set_current_desktop (int iDesktopNumber, int iViewportNumberX, return TRUE; } -static gboolean _set_nb_desktops (int iNbDesktops, int iNbViewportX, int iNbViewportY) -{ - if (iNbDesktops > 0) - cairo_dock_set_nb_desktops (iNbDesktops); - if (iNbViewportX > 0 && iNbViewportY > 0) - cairo_dock_set_nb_viewports (iNbViewportX, iNbViewportY); - return TRUE; -} - -static void _change_viewports (int iDeltaNbDesktops) -{ - // taken from the switcher applet - int iNewX, iNewY; - // Try to keep a square: (delta > 0 && X <= Y) || (delta < 0 && X > Y) - if ((iDeltaNbDesktops > 0) == (g_desktopGeometry.iNbViewportX <= g_desktopGeometry.iNbViewportY)) - { - iNewX = g_desktopGeometry.iNbViewportX + iDeltaNbDesktops; - if (iNewX <= 0) return; // cannot remove the last viewport - iNewY = g_desktopGeometry.iNbViewportY; - } - else - { - iNewX = g_desktopGeometry.iNbViewportX; - iNewY = g_desktopGeometry.iNbViewportY + iDeltaNbDesktops; - if (iNewY <= 0) return; // cannot remove the last viewport - } - cairo_dock_set_nb_viewports (iNewX, iNewY); -} - static void _add_workspace (void) { if (g_desktopGeometry.iNbViewportX == 1 && g_desktopGeometry.iNbViewportY == 1) cairo_dock_set_nb_desktops (g_desktopGeometry.iNbDesktops + 1); - else _change_viewports (1); + else cairo_dock_change_nb_viewports (1, cairo_dock_set_nb_viewports); } static void _remove_workspace (void) @@ -833,7 +804,7 @@ static void _remove_workspace (void) if (g_desktopGeometry.iNbDesktops > 1) cairo_dock_set_nb_desktops (g_desktopGeometry.iNbDesktops - 1); } - else _change_viewports (-1); + else cairo_dock_change_nb_viewports (-1, cairo_dock_set_nb_viewports); } static cairo_surface_t *_get_desktop_bg_surface (void) // attention : fonction lourde. @@ -1680,7 +1651,6 @@ static void init (void) dmb.set_desktops_names = _set_desktops_names; dmb.get_desktop_bg_surface = _get_desktop_bg_surface; dmb.set_current_desktop = _set_current_desktop; - dmb.set_nb_desktops = _set_nb_desktops; dmb.refresh = _refresh; dmb.notify_startup = _notify_startup; dmb.grab_shortkey = _grab_shortkey; diff --git a/src/implementations/cairo-dock-X-utilities.c b/src/implementations/cairo-dock-X-utilities.c index 92888d98..07a514e9 100644 --- a/src/implementations/cairo-dock-X-utilities.c +++ b/src/implementations/cairo-dock-X-utilities.c @@ -719,6 +719,26 @@ void cairo_dock_set_nb_desktops (gulong iNbDesktops) XFlush (s_XDisplay); } +void cairo_dock_change_nb_viewports (int iDeltaNbDesktops, GldiChangeViewportFunc cb) +{ + // taken from the switcher applet + int iNewX, iNewY; + // Try to keep a square: (delta > 0 && X <= Y) || (delta < 0 && X > Y) + if ((iDeltaNbDesktops > 0) == (g_desktopGeometry.iNbViewportX <= g_desktopGeometry.iNbViewportY)) + { + iNewX = g_desktopGeometry.iNbViewportX + iDeltaNbDesktops; + if (iNewX <= 0) return; // cannot remove the last viewport + iNewY = g_desktopGeometry.iNbViewportY; + } + else + { + iNewX = g_desktopGeometry.iNbViewportX; + iNewY = g_desktopGeometry.iNbViewportY + iDeltaNbDesktops; + if (iNewY <= 0) return; // cannot remove the last viewport + } + cb (iNewX, iNewY); +} + static gboolean cairo_dock_support_X_extension (void) { diff --git a/src/implementations/cairo-dock-X-utilities.h b/src/implementations/cairo-dock-X-utilities.h index b8a68b07..58e2fc47 100644 --- a/src/implementations/cairo-dock-X-utilities.h +++ b/src/implementations/cairo-dock-X-utilities.h @@ -69,6 +69,17 @@ GdkPixbuf *cairo_dock_get_pixbuf_from_pixmap (int XPixmapID, gboolean bAddAlpha) void cairo_dock_set_nb_viewports (int iNbViewportX, int iNbViewportY); void cairo_dock_set_nb_desktops (gulong iNbDesktops); +typedef void (*GldiChangeViewportFunc) (int iNbViewportX, int iNbViewportY); + +/** Change the number of viewports while keeping the total workarea as a rectangle. + * The new dimensions are calculated to keep it close to a square. The actual change + * is carried out by the callback given as the second argument. + * @param iDeltaNbDesktops should be +1 or -1 to indicate whether to add or remove viewports + * @param cb a function carrying out the actual change, taking two integer parameters + * which are the new X and Y dimensions + */ +void cairo_dock_change_nb_viewports (int iDeltaNbDesktops, GldiChangeViewportFunc cb); + //////////// // WINDOW // From db3168902f0b04c9ac5a55dbb846f930bcc6fbfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kondor=20D=C3=A1niel?= Date: Sat, 26 Oct 2024 23:09:16 +0200 Subject: [PATCH 18/24] compiz-integration: adapt to the new API for adding and removing viewports --- .../cairo-dock-compiz-integration.c | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/implementations/cairo-dock-compiz-integration.c b/src/implementations/cairo-dock-compiz-integration.c index 8389e6b2..b25f0f21 100644 --- a/src/implementations/cairo-dock-compiz-integration.c +++ b/src/implementations/cairo-dock-compiz-integration.c @@ -31,7 +31,7 @@ #include "cairo-dock-container.h" // gldi_container_get_gdk_window #include "cairo-dock-class-manager.h" #include "cairo-dock-utils.h" // cairo_dock_launch_command_sync -#include "cairo-dock-X-utilities.h" // cairo_dock_get_X_display +#include "cairo-dock-X-utilities.h" // cairo_dock_get_X_display, cairo_dock_change_nb_viewports #include "cairo-dock-compiz-integration.h" static DBusGProxy *s_pScaleProxy = NULL; @@ -307,24 +307,11 @@ static gboolean set_on_widget_layer (GldiContainer *pContainer, gboolean bOnWidg /* Only add workspaces with Compiz: We shouldn't add desktops when using Compiz * and with this method, Compiz saves the new state */ -static gboolean set_nb_desktops (int iNbDesktops, int iNbViewportX, int iNbViewportY) +static gboolean _compiz_set_nb_viewports (int X, int Y) { gboolean bSuccess = FALSE; if (s_pHSizeProxy != NULL && s_pVSizeProxy != NULL) { - // We can receive (-1, >0, >0) or (2, -1, -1) - int X, Y; - if (iNbDesktops > 0) - { - X = iNbDesktops; - Y = 1; - } - else - { - X = iNbViewportX > 0 ? iNbViewportX : 1; - Y = iNbViewportY > 0 ? iNbViewportY : 1; - } - GError *error = NULL; bSuccess = dbus_g_proxy_call (s_pHSizeProxy, "set", &error, G_TYPE_INT, X, @@ -350,6 +337,16 @@ static gboolean set_nb_desktops (int iNbDesktops, int iNbViewportX, int iNbViewp return bSuccess; } +static void _add_workspace (void) +{ + cairo_dock_change_nb_viewports (1, _compiz_set_nb_viewports); +} + +static void _remove_workspace (void) +{ + cairo_dock_change_nb_viewports (-1, _compiz_set_nb_viewports); +} + static void _register_compiz_backend (void) { @@ -361,7 +358,9 @@ static void _register_compiz_backend (void) p.present_desktops = present_desktops; p.show_widget_layer = show_widget_layer; p.set_on_widget_layer = set_on_widget_layer; - p.set_nb_desktops = set_nb_desktops; + p.add_workspace = _add_workspace; + p.remove_last_workspace = _remove_workspace; + gldi_desktop_manager_register_backend (&p, "Compiz"); } From a7b9e554e821874faafa572a16aee0277d82068f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kondor=20D=C3=A1niel?= Date: Sat, 26 Oct 2024 23:10:22 +0200 Subject: [PATCH 19/24] desktop-manager: remove the old API for changing the number of desktops --- src/gldit/cairo-dock-desktop-manager.c | 7 ------- src/gldit/cairo-dock-desktop-manager.h | 2 -- 2 files changed, 9 deletions(-) diff --git a/src/gldit/cairo-dock-desktop-manager.c b/src/gldit/cairo-dock-desktop-manager.c index e6634091..5aa9a896 100644 --- a/src/gldit/cairo-dock-desktop-manager.c +++ b/src/gldit/cairo-dock-desktop-manager.c @@ -215,13 +215,6 @@ gboolean gldi_desktop_set_current (int iDesktopNumber, int iViewportNumberX, int return FALSE; } -gboolean gldi_desktop_set_nb_desktops (int iNbDesktops, int iNbViewportX, int iNbViewportY) -{ - if (s_backend.set_nb_desktops) - return s_backend.set_nb_desktops (iNbDesktops, iNbViewportX, iNbViewportY); - return FALSE; -} - void gldi_desktop_add_workspace (void) { if (s_backend.add_workspace) s_backend.add_workspace (); diff --git a/src/gldit/cairo-dock-desktop-manager.h b/src/gldit/cairo-dock-desktop-manager.h index 62f90431..09c507cf 100644 --- a/src/gldit/cairo-dock-desktop-manager.h +++ b/src/gldit/cairo-dock-desktop-manager.h @@ -155,7 +155,6 @@ struct _GldiDesktopManagerBackend { gboolean (*set_desktops_names) (gchar **cNames); cairo_surface_t* (*get_desktop_bg_surface) (void); gboolean (*set_current_desktop) (int iDesktopNumber, int iViewportNumberX, int iViewportNumberY); - gboolean (*set_nb_desktops) (int iNbDesktops, int iNbViewportX, int iNbViewportY); void (*refresh) (void); void (*notify_startup) (const gchar *cClass); gboolean (*grab_shortkey) (guint keycode, guint modifiers, gboolean grab); @@ -223,7 +222,6 @@ gboolean gldi_desktop_is_visible (void); gchar** gldi_desktop_get_names (void); gboolean gldi_desktop_set_names (gchar **cNames); gboolean gldi_desktop_set_current (int iDesktopNumber, int iViewportNumberX, int iViewportNumberY); -gboolean gldi_desktop_set_nb_desktops (int iNbDesktops, int iNbViewportX, int iNbViewportY); /** Adds a new workspace, desktop or viewport in an implementation-defined manner. * Typically this can mean adding one more workspace / desktop as the "last" one. From 2f8d29509f8c11f06df926f7e71a4e0a120a84a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kondor=20D=C3=A1niel?= Date: Sat, 26 Oct 2024 23:11:38 +0200 Subject: [PATCH 20/24] bump ABI and project version --- CMakeLists.txt | 2 +- src/gldit/cairo-dock-module-manager.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index eb43e7e3..c5741284 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ include ("${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules/GNUInstallDirs.cmake") ########### project ############### project ("cairo-dock") -set (VERSION "3.5.99.beta3") # no dash, only numbers, dots and maybe alpha/beta/rc, e.g.: 3.3.1 or 3.3.99.alpha1 +set (VERSION "3.5.99.beta4") # no dash, only numbers, dots and maybe alpha/beta/rc, e.g.: 3.3.1 or 3.3.99.alpha1 add_compile_options ($<$:-std=c99> -Wall -Wextra $<$:-Werror-implicit-function-declaration>) # -Wextra -Wwrite-strings -Wuninitialized -Werror-implicit-function-declaration -Wstrict-prototypes -Wreturn-type -Wparentheses -Warray-bounds) if (NOT DEFINED CMAKE_BUILD_TYPE) diff --git a/src/gldit/cairo-dock-module-manager.h b/src/gldit/cairo-dock-module-manager.h index b174b353..eb121f85 100644 --- a/src/gldit/cairo-dock-module-manager.h +++ b/src/gldit/cairo-dock-module-manager.h @@ -44,7 +44,7 @@ G_BEGIN_DECLS * It is not required the change this when adding a function to the * public API (loading the module will fail if it refers to an * unresolved symbol anyway). */ -#define GLDI_ABI_VERSION 20240921 +#define GLDI_ABI_VERSION 20241026 // manager typedef struct _GldiModulesParam GldiModulesParam; From 7b2a291ab70c8a2d4b48f0b74819105203971495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kondor=20D=C3=A1niel?= Date: Sat, 26 Oct 2024 23:53:53 +0200 Subject: [PATCH 21/24] cosmic-workspaces: remove debug output and clean code --- .../cairo-dock-cosmic-workspaces.c | 49 ++++++++----------- 1 file changed, 20 insertions(+), 29 deletions(-) diff --git a/src/implementations/cairo-dock-cosmic-workspaces.c b/src/implementations/cairo-dock-cosmic-workspaces.c index 189aa7ab..b51390af 100644 --- a/src/implementations/cairo-dock-cosmic-workspaces.c +++ b/src/implementations/cairo-dock-cosmic-workspaces.c @@ -84,14 +84,14 @@ static void _update_desktop_layout () guint x = desktops[i]->x; guint y = desktops[i]->y; - if (x == (guint)-1) have_invalid_x = TRUE; + if (x == INVALID_COORD) have_invalid_x = TRUE; else { if (x > cols) cols = x; if (x < s_iXOffset) s_iXOffset = x; } - if (y == (guint)-1) have_invalid_y = TRUE; + if (y == INVALID_COORD) have_invalid_y = TRUE; else { all_invalid_y = FALSE; if (y > rows) rows = y; @@ -142,15 +142,14 @@ static void _update_current_desktop (void) } } -static void _name (void *data, struct zcosmic_workspace_handle_v1* handle, const char *name) +static void _name (void *data, struct zcosmic_workspace_handle_v1*, const char *name) { - cd_warning ("workspace name: %p, %s", handle, name ? name : "(null)"); CosmicWS *desktop = (CosmicWS*)data; g_free (desktop->pending_name); desktop->pending_name = g_strdup ((gchar *)name); } -static void _coordinates (void *data, struct zcosmic_workspace_handle_v1* handle, struct wl_array *coords) +static void _coordinates (void *data, struct zcosmic_workspace_handle_v1*, struct wl_array *coords) { CosmicWS *desktop = (CosmicWS*)data; uint32_t *cdata = (uint32_t*)coords->data; @@ -158,36 +157,34 @@ static void _coordinates (void *data, struct zcosmic_workspace_handle_v1* handle if (size > 2*sizeof(uint32_t) || size < sizeof(uint32_t)) { // too many or no coordinates, we cannot use them - desktop->pending_x = (guint)-1; - desktop->pending_y = (guint)-1; + desktop->pending_x = INVALID_COORD; + desktop->pending_y = INVALID_COORD; } else { // we have at least one coordinate desktop->pending_x = cdata[0]; if (size == 2*sizeof(uint32_t)) desktop->pending_y = cdata[1]; - else desktop->pending_y = (guint)-1; + else desktop->pending_y = INVALID_COORD; } - cd_warning ("workspace coordinates: %p, size: %lu, x: %u, y: %u", handle, size, desktop->pending_x, desktop->pending_y); } -static void _state (void *data, struct zcosmic_workspace_handle_v1* handle, struct wl_array *state) +static void _state (void *data, struct zcosmic_workspace_handle_v1*, struct wl_array *state) { gboolean bActivated = FALSE; - gboolean bUrgent = FALSE; - gboolean bHidden = FALSE; +/* gboolean bUrgent = FALSE; -- we do not care about these + gboolean bHidden = FALSE; */ int i; uint32_t* stdata = (uint32_t*)state->data; for (i = 0; i*sizeof(uint32_t) < state->size; i++) { if (stdata[i] == ZCOSMIC_WORKSPACE_HANDLE_V1_STATE_ACTIVE) bActivated = TRUE; - else if (stdata[i] == ZCOSMIC_WORKSPACE_HANDLE_V1_STATE_URGENT) +/* else if (stdata[i] == ZCOSMIC_WORKSPACE_HANDLE_V1_STATE_URGENT) bUrgent = TRUE; else if (stdata[i] == ZCOSMIC_WORKSPACE_HANDLE_V1_STATE_HIDDEN) - bHidden = TRUE; + bHidden = TRUE; */ } - cd_warning ("workspace state: %p, activated: %d, urgent: %d, hidden: %d", handle, !!bActivated, !!bUrgent, !!bHidden); if (bActivated) { @@ -203,9 +200,9 @@ static void _state (void *data, struct zcosmic_workspace_handle_v1* handle, stru } } -static void _capabilities (void*, struct zcosmic_workspace_handle_v1* handle, struct wl_array* cap) +static void _capabilities (void*, G_GNUC_UNUSED struct zcosmic_workspace_handle_v1* handle, G_GNUC_UNUSED struct wl_array* cap) { - uint32_t* capdata = (uint32_t*)cap->data; +/* uint32_t* capdata = (uint32_t*)cap->data; cd_warning ("workspace capabilities: %p", handle); int i; for (i = 0; i*sizeof(uint32_t) < cap->size; i++) @@ -220,12 +217,11 @@ static void _capabilities (void*, struct zcosmic_workspace_handle_v1* handle, st g_print ("workspace can be renamed\n"); else if (capdata[i] == ZCOSMIC_WORKSPACE_HANDLE_V1_ZCOSMIC_WORKSPACE_CAPABILITIES_V1_SET_TILING_STATE) g_print ("workspace can set tiling state\n"); - } + } */ } -static void _removed (void *data, struct zcosmic_workspace_handle_v1 *handle) +static void _removed (void *data, struct zcosmic_workspace_handle_v1*) { - cd_warning ("workspace removed: %p", handle); CosmicWS *desktop = (CosmicWS*)data; desktop->bRemoved = TRUE; } @@ -255,16 +251,16 @@ static const struct zcosmic_workspace_handle_v1_listener desktop_listener = { }; -static void _group_capabilities (void*, struct zcosmic_workspace_group_handle_v1* handle, struct wl_array* cap) +static void _group_capabilities (void*, G_GNUC_UNUSED struct zcosmic_workspace_group_handle_v1* handle, G_GNUC_UNUSED struct wl_array* cap) { /* don't care */ - uint32_t* capdata = (uint32_t*)cap->data; +/* uint32_t* capdata = (uint32_t*)cap->data; int i; for (i = 0; i*sizeof(uint32_t) < cap->size; i++) { if (capdata[i] == ZCOSMIC_WORKSPACE_GROUP_HANDLE_V1_CREATE_WORKSPACE) cd_warning ("workspace group can be add workspaces: %p\n", handle); - } + } */ } static void _output_enter (void*, struct zcosmic_workspace_group_handle_v1* handle, struct wl_output* output) @@ -280,7 +276,6 @@ static void _output_leave (void*, struct zcosmic_workspace_group_handle_v1* hand static void _desktop_created (void*, struct zcosmic_workspace_group_handle_v1 *manager, struct zcosmic_workspace_handle_v1 *new_workspace) { - cd_warning ("new workspace: %p, %p", manager, new_workspace); if (manager != s_pWSGroup) { // this is not "our" manager, we don't care @@ -296,7 +291,7 @@ static void _desktop_created (void*, struct zcosmic_workspace_group_handle_v1 *m if (s_iNumDesktops >= s_iDesktopCap) { desktops = g_renew (CosmicWS*, desktops, s_iNumDesktops + 16); - s_iDesktopCap = s_iNumDesktops + 1; + s_iDesktopCap = s_iNumDesktops + 16; } desktops[s_iNumDesktops] = desktop; s_iNumDesktops++; @@ -308,7 +303,6 @@ static void _desktop_created (void*, struct zcosmic_workspace_group_handle_v1 *m static void _group_removed (void*, struct zcosmic_workspace_group_handle_v1 *handle) { - cd_warning ("workspace group removed: %p", handle); if (handle == s_pWSGroup) { @@ -344,7 +338,6 @@ static const struct zcosmic_workspace_group_handle_v1_listener group_listener = static void _new_workspace_group (void*, struct zcosmic_workspace_manager_v1*, struct zcosmic_workspace_group_handle_v1 *new_group) { - cd_warning ("new workspace group: %p", new_group); if (s_pWSGroup) { cd_warning ("cosmic-workspaces: multiple workspace groups are not supported!\n"); @@ -360,7 +353,6 @@ static void _new_workspace_group (void*, struct zcosmic_workspace_manager_v1*, s static void _done (void*, struct zcosmic_workspace_manager_v1*) { - cd_warning ("workspace manager done"); gboolean bRemoved = FALSE; // if any workspace was removed gboolean bCoords = FALSE; // any of the coordinates changed gboolean bName = FALSE; // any of the names changed @@ -422,7 +414,6 @@ static void _done (void*, struct zcosmic_workspace_manager_v1*) static void _finished (void*, struct zcosmic_workspace_manager_v1 *handle) { - cd_warning ("workspace manager finished"); zcosmic_workspace_manager_v1_destroy (handle); } From 0d765245938d9a80190685ecc4ef9a7258f5192c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kondor=20D=C3=A1niel?= Date: Thu, 14 Nov 2024 21:03:30 +0100 Subject: [PATCH 22/24] cosmic-toplevel: remove debug output --- src/implementations/cairo-dock-cosmic-toplevel.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/implementations/cairo-dock-cosmic-toplevel.c b/src/implementations/cairo-dock-cosmic-toplevel.c index d61d7363..d9033ed2 100644 --- a/src/implementations/cairo-dock-cosmic-toplevel.c +++ b/src/implementations/cairo-dock-cosmic-toplevel.c @@ -244,7 +244,6 @@ static void _gldi_toplevel_parent_cb (void* data, G_GNUC_UNUSED wfthandle *handl static void _workspace_entered (void *data, G_GNUC_UNUSED wfthandle *handle, struct zcosmic_workspace_handle_v1 *wshandle) { - cd_warning ("%p -- workspace: %p", handle, wshandle); gldi_cosmic_workspaces_update_window ((GldiWindowActor*)data, wshandle); gldi_object_notify (&myWindowObjectMgr, NOTIFICATION_WINDOW_DESKTOP_CHANGED, data); } From 47eeb643db27031d3ded79ff60a433e1a58a1519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kondor=20D=C3=A1niel?= Date: Thu, 14 Nov 2024 21:03:48 +0100 Subject: [PATCH 23/24] wayland-manager: avoid spurious workspace switch if possible --- src/implementations/cairo-dock-wayland-manager.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/implementations/cairo-dock-wayland-manager.c b/src/implementations/cairo-dock-wayland-manager.c index 9f6aa439..3a211ef5 100644 --- a/src/implementations/cairo-dock-wayland-manager.c +++ b/src/implementations/cairo-dock-wayland-manager.c @@ -444,7 +444,12 @@ void gldi_wayland_grab_keyboard (GldiContainer *pContainer) void gldi_wayland_release_keyboard ( G_GNUC_UNUSED GldiContainer *pContainer) { GldiWindowActor *actor = gldi_windows_get_active (); - if (actor && !actor->bIsHidden) gldi_window_show (actor); + if (actor && !actor->bIsHidden) { + if (gldi_window_manager_can_track_workspaces () && !gldi_window_is_on_current_desktop (actor)) + return; + // TODO: avoid activating a window not on the current workspace in other cases! + gldi_window_show (actor); + } } static gboolean _dock_handle_leave (CairoDock *pDock, GdkEventCrossing *pEvent) From 6cccab0251a72e363f30f6a2d75dd4cbcc1b17fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kondor=20D=C3=A1niel?= Date: Sun, 17 Nov 2024 13:10:00 +0100 Subject: [PATCH 24/24] desktop-manager: update some notes --- src/gldit/cairo-dock-desktop-manager.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/gldit/cairo-dock-desktop-manager.h b/src/gldit/cairo-dock-desktop-manager.h index 09c507cf..01f0fc4a 100644 --- a/src/gldit/cairo-dock-desktop-manager.h +++ b/src/gldit/cairo-dock-desktop-manager.h @@ -80,7 +80,7 @@ Questions / issues: - Unsure if for any WM / DE, we can have both iNbDesktops > 1 and multiple viewports. In theory, on Wayland this will be possible with the ext-workspace protocol if there are multiple workspace groups. On X11, this is explicitly supported by the standards, but likely not implemented by WMs. - - Are there X11 WM that provides multipe desktops and arranges them in 2D? In theory, this is possible using + - Are there X11 WMs that provide multipe desktops and arranges them in 2D? In theory, this is possible using _NET_DESKTOP_LAYOUT, however Cairo-Dock currently does not handle this. Note: according to the specification, this is set by the "pager", which is possibly a separate entity from the WM: https://specifications.freedesktop.org/wm-spec/latest/ar01s03.html @@ -107,8 +107,8 @@ Minimal changes for Wayland: supported, converted to 1D) - update the switcher applet to display desktops / workspace groups more independently - change APIs to add / remove individual workspaces, take care to handle to case of X11 rectangular - viewports though - - move GldiWMBackendFlags and the related capabilities here? + viewports though -> partly done + - move GldiWMBackendFlags and the related capabilities here? -> no, better in window manager - X11: special case WMs where desktops can be arranged in 2D? (but has to be no viewports) -> "desktops" would be interpreted as independent viewports in this case @@ -124,8 +124,8 @@ Plan for a new API: - vieports can be overlapping (a window can span multiple), flag / setting for this that is set by the backend Next steps: - - change API for adding removing "workspaces", these are handled by a backend-specific way (move some of the - logic from the switcher plugin to core) + - change API for adding and removing "workspaces", these are handled by a backend-specific way (move some of the + logic from the switcher plugin to core) -> done - make the number of viewports per desktop independent, use this to implement the ext-workspace / cosmic-workspace protocol - implement support for _NET_DESKTOP_LAYOUT for arranging desktops in a 2D grid on X11