summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/prefs.c16
-rw-r--r--src/core/screen-private.h3
-rw-r--r--src/core/screen.c160
-rw-r--r--src/core/workspace.c7
-rw-r--r--src/include/prefs.h2
-rw-r--r--src/org.mate.marco.gschema.xml5
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>