Skip to content

Commit

Permalink
Merge pull request Cairo-Dock#50 from dkondor/cosmic_workspaces
Browse files Browse the repository at this point in the history
Desktop and WM improvements
  • Loading branch information
dkondor authored Nov 17, 2024
2 parents 9f9421e + 6cccab0 commit 3ffa15b
Show file tree
Hide file tree
Showing 22 changed files with 1,093 additions and 88 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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 ($<$<COMPILE_LANGUAGE:C>:-std=c99> -Wall -Wextra $<$<COMPILE_LANGUAGE:C>:-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)
Expand Down
11 changes: 9 additions & 2 deletions src/cairo-dock-user-menu.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
}
Expand Down
7 changes: 5 additions & 2 deletions src/gldit/cairo-dock-applications-manager.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);

Expand Down Expand Up @@ -746,7 +747,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);

Expand Down
13 changes: 9 additions & 4 deletions src/gldit/cairo-dock-desktop-manager.c
Original file line number Diff line number Diff line change
Expand Up @@ -215,13 +215,18 @@ gboolean gldi_desktop_set_current (int iDesktopNumber, int iViewportNumberX, int
return FALSE;
}

gboolean gldi_desktop_set_nb_desktops (int iNbDesktops, int iNbViewportX, int iNbViewportY)
void gldi_desktop_add_workspace (void)
{
if (s_backend.set_nb_desktops)
return s_backend.set_nb_desktops (iNbDesktops, iNbViewportX, iNbViewportY);
return FALSE;
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)
Expand Down
33 changes: 26 additions & 7 deletions src/gldit/cairo-dock-desktop-manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -155,10 +155,11 @@ 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);
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.
Expand Down Expand Up @@ -221,7 +222,25 @@ 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.
* 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);

Expand Down
3 changes: 3 additions & 0 deletions src/gldit/cairo-dock-dock-visibility.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
2 changes: 1 addition & 1 deletion src/gldit/cairo-dock-module-manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
45 changes: 37 additions & 8 deletions src/gldit/cairo-dock-windows-manager.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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)
Expand Down
34 changes: 33 additions & 1 deletion src/gldit/cairo-dock-windows-manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
*
Expand Down
1 change: 1 addition & 0 deletions src/implementations/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
26 changes: 19 additions & 7 deletions src/implementations/cairo-dock-X-manager.c
Original file line number Diff line number Diff line change
Expand Up @@ -789,13 +789,22 @@ static gboolean _set_current_desktop (int iDesktopNumber, int iViewportNumberX,
return TRUE;
}

static gboolean _set_nb_desktops (int iNbDesktops, int iNbViewportX, int iNbViewportY)
static void _add_workspace (void)
{
if (iNbDesktops > 0)
cairo_dock_set_nb_desktops (iNbDesktops);
if (iNbViewportX > 0 && iNbViewportY > 0)
cairo_dock_set_nb_viewports (iNbViewportX, iNbViewportY);
return TRUE;
if (g_desktopGeometry.iNbViewportX == 1 && g_desktopGeometry.iNbViewportY == 1)
cairo_dock_set_nb_desktops (g_desktopGeometry.iNbDesktops + 1);
else cairo_dock_change_nb_viewports (1, cairo_dock_set_nb_viewports);
}

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 cairo_dock_change_nb_viewports (-1, cairo_dock_set_nb_viewports);
}

static cairo_surface_t *_get_desktop_bg_surface (void) // attention : fonction lourde.
Expand Down Expand Up @@ -1642,10 +1651,11 @@ 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;
dmb.add_workspace = _add_workspace;
dmb.remove_last_workspace = _remove_workspace;
gldi_desktop_manager_register_backend (&dmb, "X11");

GldiWindowManagerBackend wmb;
Expand All @@ -1672,6 +1682,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);

Expand Down
Loading

0 comments on commit 3ffa15b

Please sign in to comment.