diff options
-rw-r--r-- | src/core/prefs.c | 16 | ||||
-rw-r--r-- | src/core/screen-private.h | 3 | ||||
-rw-r--r-- | src/core/screen.c | 160 | ||||
-rw-r--r-- | src/core/workspace.c | 7 | ||||
-rw-r--r-- | src/include/prefs.h | 2 | ||||
-rw-r--r-- | src/org.mate.marco.gschema.xml | 5 |
6 files changed, 193 insertions, 0 deletions
diff --git a/src/core/prefs.c b/src/core/prefs.c index 032bba8f..ca832c8b 100644 --- a/src/core/prefs.c +++ b/src/core/prefs.c @@ -105,6 +105,7 @@ static gboolean raise_on_click = TRUE; static gboolean attach_modal_dialogs = FALSE; static char* current_theme = NULL; static int num_workspaces = 4; +static gboolean dynamic_workspaces = FALSE; static MetaWrapStyle wrap_style = META_WRAP_NONE; static MetaActionTitlebar action_double_click_titlebar = META_ACTION_TITLEBAR_TOGGLE_MAXIMIZE; static MetaActionTitlebar action_middle_click_titlebar = META_ACTION_TITLEBAR_LOWER; @@ -364,6 +365,12 @@ static MetaBoolPreference preferences_bool[] = NULL, /* feature is known but disabled */ FALSE, }, + { "dynamic-workspaces", + KEY_GENERAL_SCHEMA, + META_PREF_DYNAMIC_WORKSPACES, + &dynamic_workspaces, + FALSE, + }, { "disable-workarounds", KEY_GENERAL_SCHEMA, META_PREF_DISABLE_WORKAROUNDS, @@ -1571,6 +1578,12 @@ meta_prefs_get_num_workspaces (void) return num_workspaces; } +gboolean +meta_prefs_get_dynamic_workspaces (void) +{ + return dynamic_workspaces; +} + MetaWrapStyle meta_prefs_get_wrap_style (void) { @@ -1624,6 +1637,9 @@ meta_preference_to_string (MetaPreference pref) case META_PREF_NUM_WORKSPACES: return "NUM_WORKSPACES"; + case META_PREF_DYNAMIC_WORKSPACES: + return "DYNAMIC_WORKSPACES"; + case META_PREF_WRAP_STYLE: return "WRAP_STYLE"; diff --git a/src/core/screen-private.h b/src/core/screen-private.h index 56755f9a..fb9f68f4 100644 --- a/src/core/screen-private.h +++ b/src/core/screen-private.h @@ -140,6 +140,8 @@ struct _MetaScreen /* Managed by compositor.c */ gpointer compositor_data; + + guint dynamic_workspace_idle_id; }; MetaScreen* meta_screen_new (MetaDisplay *display, @@ -191,6 +193,7 @@ void meta_screen_get_natural_xinerama_list (MetaScreen *screen, void meta_screen_update_workspace_layout (MetaScreen *screen); void meta_screen_update_workspace_names (MetaScreen *screen); void meta_screen_queue_workarea_recalc (MetaScreen *screen); +void meta_screen_update_dynamic_workspaces (MetaScreen *screen); Window meta_create_offscreen_window (Display *xdisplay, Window parent, diff --git a/src/core/screen.c b/src/core/screen.c index 99e3fc39..b71cff2b 100644 --- a/src/core/screen.c +++ b/src/core/screen.c @@ -55,6 +55,8 @@ #include <string.h> #include <stdio.h> +#define MAX_REASONABLE_WORKSPACES 36 + static char* get_screen_name (MetaDisplay *display, int number); @@ -506,6 +508,7 @@ meta_screen_new (MetaDisplay *display, screen->vertical_workspaces = FALSE; screen->starting_corner = META_SCREEN_TOPLEFT; screen->compositor_data = NULL; + screen->dynamic_workspace_idle_id = 0; { XFontStruct *font_info; @@ -638,6 +641,12 @@ meta_screen_free (MetaScreen *screen, screen->closing += 1; + if (screen->dynamic_workspace_idle_id != 0) + { + g_source_remove (screen->dynamic_workspace_idle_id); + screen->dynamic_workspace_idle_id = 0; + } + meta_display_grab (display); if (screen->display->compositor) @@ -872,6 +881,10 @@ prefs_changed_callback (MetaPreference pref, meta_display_get_current_time_roundtrip (screen->display); update_num_workspaces (screen, timestamp); } + else if (pref == META_PREF_DYNAMIC_WORKSPACES) + { + meta_screen_update_dynamic_workspaces (screen); + } else if (pref == META_PREF_FOCUS_MODE) { update_focus_mode (screen); @@ -1051,6 +1064,40 @@ set_number_of_spaces_hint (MetaScreen *screen, meta_error_trap_pop (screen->display, FALSE); } +/* + * Asks the window manager to change the number of workspaces on screen. + * This function is copied from libwnck. + */ +static void +request_workspace_count_change (MetaScreen *screen, + int count) +{ + XEvent xev; + + if (screen->closing > 0) + return; + + meta_verbose ("Requesting workspace count change to %d via client message\n", count); + + /* Send _NET_NUMBER_OF_DESKTOPS client message to trigger proper GSettings update */ + xev.xclient.type = ClientMessage; + xev.xclient.serial = 0; + xev.xclient.window = screen->xroot; + xev.xclient.send_event = True; + xev.xclient.display = screen->display->xdisplay; + xev.xclient.message_type = screen->display->atom__NET_NUMBER_OF_DESKTOPS; + xev.xclient.format = 32; + xev.xclient.data.l[0] = count; + + meta_error_trap_push (screen->display); + XSendEvent (screen->display->xdisplay, + screen->xroot, + False, + SubstructureRedirectMask | SubstructureNotifyMask, + &xev); + meta_error_trap_pop (screen->display, FALSE); +} + static void set_desktop_geometry_hint (MetaScreen *screen) { @@ -1096,6 +1143,119 @@ set_desktop_viewport_hint (MetaScreen *screen) meta_error_trap_pop (screen->display, FALSE); } + +static gboolean +workspace_has_non_sticky_windows (MetaWorkspace *workspace) +{ + GList *window_list; + + for (window_list = workspace->windows; window_list != NULL; window_list = window_list->next) + { + MetaWindow *window = window_list->data; + if (!window->on_all_workspaces && !window->always_sticky) + return TRUE; + } + return FALSE; +} + +static gboolean +dynamic_workspaces_idle_callback (gpointer user_data) +{ + MetaScreen *screen = user_data; + int workspace_count = meta_screen_get_n_workspaces (screen); + GList *workspace_list; + int empty_workspaces = 0; + + screen->dynamic_workspace_idle_id = 0; + + if (!meta_prefs_get_dynamic_workspaces ()) + return G_SOURCE_REMOVE; + + /* Count empty workspaces */ + for (workspace_list = screen->workspaces; workspace_list != NULL; workspace_list = workspace_list->next) + { + MetaWorkspace *workspace = workspace_list->data; + if (!workspace_has_non_sticky_windows (workspace)) + empty_workspaces++; + } + + /* Make sure there's at least one empty workspace */ + if (empty_workspaces == 0 && workspace_count < MAX_REASONABLE_WORKSPACES) + { + meta_workspace_new (screen); + workspace_count++; + empty_workspaces++; + request_workspace_count_change (screen, workspace_count); + } + + /* If there are empty workspaces in the middle, consolidate windows from the + * next workspace into it. Do this sequentially to make sure all empty + * workspaces are at the end. */ + for (int i = 0; i < workspace_count - 1; i++) + { + MetaWorkspace *current_ws = meta_screen_get_workspace_by_index (screen, i); + if (current_ws && !workspace_has_non_sticky_windows (current_ws)) + { + /* Find next workspace with windows to move into this empty slot */ + for (int j = i + 1; j < workspace_count; j++) + { + MetaWorkspace *source_ws = meta_screen_get_workspace_by_index (screen, j); + if (source_ws && workspace_has_non_sticky_windows (source_ws)) + { + meta_workspace_relocate_windows (source_ws, current_ws); + break; + } + } + } + } + + /* Count how many trailing empty workspaces there are */ + int trailing_empty_count = 0; + for (int i = workspace_count - 1; i >= 0; i--) + { + MetaWorkspace *ws = meta_screen_get_workspace_by_index (screen, i); + if (ws && !workspace_has_non_sticky_windows (ws)) + { + trailing_empty_count++; + } + else + { + break; + } + } + + /* Remove all but one trailing empty workspace */ + int workspaces_to_remove = MAX (0, trailing_empty_count - 1); + for (int i = 0; i < workspaces_to_remove; i++) + { + MetaWorkspace *last_ws = meta_screen_get_workspace_by_index (screen, workspace_count - 1); + if (last_ws) + { + meta_workspace_free (last_ws); + workspace_count--; + } + } + + if (workspaces_to_remove > 0) + request_workspace_count_change (screen, workspace_count); + + return G_SOURCE_REMOVE; +} + +void +meta_screen_update_dynamic_workspaces (MetaScreen *screen) +{ + if (!meta_prefs_get_dynamic_workspaces ()) + return; + + /* Don't queue multiple idle callbacks */ + if (screen->dynamic_workspace_idle_id != 0) + return; + + /* Queue idle callback to run after current operations complete */ + screen->dynamic_workspace_idle_id = g_idle_add (dynamic_workspaces_idle_callback, screen); +} + static void update_num_workspaces (MetaScreen *screen, guint32 timestamp) diff --git a/src/core/workspace.c b/src/core/workspace.c index 3b640cd1..556899e8 100644 --- a/src/core/workspace.c +++ b/src/core/workspace.c @@ -27,6 +27,7 @@ #include "workspace.h" #include "errors.h" #include "prefs.h" +#include "screen-private.h" #include <X11/Xatom.h> #include <string.h> #include <canberra-gtk.h> @@ -217,6 +218,9 @@ meta_workspace_add_window (MetaWorkspace *workspace, * the relevant struts */ meta_window_queue (window, META_QUEUE_CALC_SHOWING|META_QUEUE_MOVE_RESIZE); + + /* Update dynamic workspaces (this will add a new workspace if necessary) */ + meta_screen_update_dynamic_workspaces (workspace->screen); } void @@ -263,6 +267,9 @@ meta_workspace_remove_window (MetaWorkspace *workspace, * the relevant struts */ meta_window_queue (window, META_QUEUE_CALC_SHOWING|META_QUEUE_MOVE_RESIZE); + + /* Update dynamic workspaces (this will remove an empty workspace if necessary) */ + meta_screen_update_dynamic_workspaces (workspace->screen); } void diff --git a/src/include/prefs.h b/src/include/prefs.h index efb2242b..4c7dd606 100644 --- a/src/include/prefs.h +++ b/src/include/prefs.h @@ -44,6 +44,7 @@ typedef enum META_PREF_THEME, META_PREF_TITLEBAR_FONT, META_PREF_NUM_WORKSPACES, + META_PREF_DYNAMIC_WORKSPACES, META_PREF_WRAP_STYLE, META_PREF_APPLICATION_BASED, META_PREF_KEYBINDINGS, @@ -99,6 +100,7 @@ const char* meta_prefs_get_theme (void); /* returns NULL if GTK default should be used */ const PangoFontDescription* meta_prefs_get_titlebar_font (void); int meta_prefs_get_num_workspaces (void); +gboolean meta_prefs_get_dynamic_workspaces (void); gboolean meta_prefs_get_application_based (void); gboolean meta_prefs_get_disable_workarounds (void); gboolean meta_prefs_get_auto_raise (void); diff --git a/src/org.mate.marco.gschema.xml b/src/org.mate.marco.gschema.xml index c8766b44..c3810ccb 100644 --- a/src/org.mate.marco.gschema.xml +++ b/src/org.mate.marco.gschema.xml @@ -126,6 +126,11 @@ <summary>Number of workspaces</summary> <description>Number of workspaces. Must be more than zero, and has a fixed maximum to prevent making the desktop unusable by accidentally asking for too many workspaces.</description> </key> + <key name="dynamic-workspaces" type="b"> + <default>false</default> + <summary>Enable dynamic workspaces</summary> + <description>If true, workspaces are automatically created and deleted as needed. A new workspace is created when there are no empty ones, and empty workspaces are deleted when there is more than one empty workspace.</description> + </key> <key name="wrap-style" enum="org.mate.Marco.WrapStyle"> <default>'no wrap'</default> <summary>Workspace wrap style</summary> |