summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/50-marco-desktop-key.xml.in2
-rw-r--r--src/core/frame-private.h4
-rw-r--r--src/core/frame.c46
-rw-r--r--src/core/keybindings.c59
-rw-r--r--src/core/place.c2
-rw-r--r--src/core/place.h3
-rw-r--r--src/core/prefs.c27
-rw-r--r--src/core/screen-private.h3
-rw-r--r--src/core/screen.c294
-rw-r--r--src/core/window-private.h2
-rw-r--r--src/core/window.c36
-rw-r--r--src/core/workspace.c7
-rw-r--r--src/include/all-keybindings.h1
-rw-r--r--src/include/common.h14
-rw-r--r--src/include/frame.h1
-rw-r--r--src/include/prefs.h8
-rw-r--r--src/org.mate.marco.gschema.xml15
-rw-r--r--src/ui/theme.c50
-rw-r--r--src/ui/theme.h4
19 files changed, 497 insertions, 81 deletions
diff --git a/src/50-marco-desktop-key.xml.in b/src/50-marco-desktop-key.xml.in
index 61f2ff4f..661476cc 100644
--- a/src/50-marco-desktop-key.xml.in
+++ b/src/50-marco-desktop-key.xml.in
@@ -9,6 +9,8 @@
<KeyListEntry name="run-command-window-screenshot" description="Take a screenshot of a window" />
+ <KeyListEntry name="run-command-area-screenshot" description="Take a screenshot of an area" />
+
<KeyListEntry name="run-command-terminal" description="Run a terminal" />
<KeyListEntry name="rename-workspace" description="Rename current workspace" />
diff --git a/src/core/frame-private.h b/src/core/frame-private.h
index 8c2a2eeb..77216420 100644
--- a/src/core/frame-private.h
+++ b/src/core/frame-private.h
@@ -48,9 +48,13 @@ struct _MetaFrame
int right_width;
int bottom_height;
+ /* valid if borders_cached is set */
+ MetaFrameBorders cached_borders;
+
guint mapped : 1;
guint need_reapply_frame_shape : 1;
guint is_flashing : 1; /* used by the visual bell flash */
+ guint borders_cached : 1;
};
void meta_window_ensure_frame (MetaWindow *window);
diff --git a/src/core/frame.c b/src/core/frame.c
index e1eec2a4..a8ba65c0 100644
--- a/src/core/frame.c
+++ b/src/core/frame.c
@@ -85,6 +85,7 @@ meta_window_ensure_frame (MetaWindow *window)
frame->mapped = FALSE;
frame->need_reapply_frame_shape = TRUE;
frame->is_flashing = FALSE;
+ frame->borders_cached = FALSE;
meta_verbose ("Frame geometry %d,%d %dx%d\n",
frame->rect.x, frame->rect.y,
@@ -302,22 +303,51 @@ meta_frame_get_flags (MetaFrame *frame)
return flags;
}
+static void
+clear_border (GtkBorder *border)
+{
+ border->left = 0;
+ border->right = 0;
+ border->top = 0;
+ border->bottom = 0;
+}
+
void
-meta_frame_borders_clear (MetaFrameBorders *self)
+meta_frame_borders_clear (MetaFrameBorders *borders)
{
- self->visible.top = self->invisible.top = self->total.top = 0;
- self->visible.bottom = self->invisible.bottom = self->total.bottom = 0;
- self->visible.left = self->invisible.left = self->total.left = 0;
- self->visible.right = self->invisible.right = self->total.right = 0;
+ clear_border (&borders->visible);
+ clear_border (&borders->shadow);
+ clear_border (&borders->resize);
+ clear_border (&borders->invisible);
+ clear_border (&borders->total);
}
void
meta_frame_calc_borders (MetaFrame *frame,
MetaFrameBorders *borders)
{
- meta_ui_get_frame_borders (frame->window->screen->ui,
- frame->xwindow,
- borders);
+ if (frame == NULL)
+ {
+ meta_frame_borders_clear (borders);
+ }
+ else
+ {
+ if (!frame->borders_cached)
+ {
+ meta_ui_get_frame_borders (frame->window->screen->ui,
+ frame->xwindow,
+ &frame->cached_borders);
+ frame->borders_cached = TRUE;
+ }
+
+ *borders = frame->cached_borders;
+ }
+}
+
+void
+meta_frame_clear_cached_borders (MetaFrame *frame)
+{
+ frame->borders_cached = FALSE;
}
static gboolean
diff --git a/src/core/keybindings.c b/src/core/keybindings.c
index fb3131c2..45be2353 100644
--- a/src/core/keybindings.c
+++ b/src/core/keybindings.c
@@ -2447,19 +2447,6 @@ error_on_command (int command_index,
}
}
-static void
-set_display_setup_func (void *data)
-{
- const char *screen_name = data;
- char *full;
-
- full = g_strdup_printf ("DISPLAY=%s", screen_name);
-
- putenv (full);
-
- /* do not free full, because putenv is lame */
-}
-
static gboolean
meta_spawn_command_line_async_on_screen (const gchar *command_line,
MetaScreen *screen,
@@ -2467,6 +2454,7 @@ meta_spawn_command_line_async_on_screen (const gchar *command_line,
{
gboolean retval;
gchar **argv = NULL;
+ gchar **envp = NULL;
g_return_val_if_fail (command_line != NULL, FALSE);
@@ -2475,15 +2463,20 @@ meta_spawn_command_line_async_on_screen (const gchar *command_line,
error))
return FALSE;
+ envp = g_get_environ();
+ envp = g_environ_setenv(envp, "DISPLAY", screen->screen_name, TRUE);
+
retval = g_spawn_async (NULL,
argv,
- NULL,
+ envp,
G_SPAWN_SEARCH_PATH,
- set_display_setup_func,
- screen->screen_name,
+ NULL,
+ NULL,
NULL,
error);
+
g_strfreev (argv);
+ g_strfreev (envp);
return retval;
}
@@ -2517,6 +2510,15 @@ handle_run_command (MetaDisplay *display,
return;
}
+ /* Release keyboard grabs immediately for screenshot commands that may need
+ * input grabbing. This prevents race conditions with applications like
+ * mate-screenshot that need to grab input for area selection.
+ */
+ if (which >= SCREENSHOT_COMMAND_IDX && which <= AREA_SCREENSHOT_COMMAND_IDX)
+ {
+ ungrab_keyboard (display, event->xkey.time);
+ }
+
err = NULL;
if (!meta_spawn_command_line_async_on_screen (command, screen, &err))
{
@@ -2703,23 +2705,34 @@ handle_move_to_center (MetaDisplay *display,
const MetaXineramaScreenInfo* current;
MetaRectangle work_area;
MetaRectangle outer;
- int orig_x, orig_y;
- int frame_width, frame_height;
+ MetaFrameBorders borders;
+ int client_x, client_y;
current = meta_screen_get_xinerama_for_window(screen, window);
meta_window_get_work_area_for_xinerama (window,
current->number,
&work_area);
meta_window_get_outer_rect (window, &outer);
- meta_window_get_position (window, &orig_x, &orig_y);
- frame_width = (window->frame ? window->frame->child_x : 0);
- frame_height = (window->frame ? window->frame->child_y : 0);
+ center_rect_in_area (&outer, &work_area);
+
+ /* Convert frame position to client position */
+ if (window->frame)
+ {
+ meta_frame_calc_borders (window->frame, &borders);
+ client_x = outer.x + borders.visible.left;
+ client_y = outer.y + borders.visible.top;
+ }
+ else
+ {
+ client_x = outer.x;
+ client_y = outer.y;
+ }
meta_window_move_resize (window,
TRUE,
- work_area.x + (work_area.width +frame_width -outer.width )/2,
- work_area.y + (work_area.height+frame_height-outer.height)/2,
+ client_x,
+ client_y,
window->rect.width,
window->rect.height);
}
diff --git a/src/core/place.c b/src/core/place.c
index 1aafff79..854469a9 100644
--- a/src/core/place.c
+++ b/src/core/place.c
@@ -528,7 +528,7 @@ center_tile_rect_in_area (MetaRectangle *rect,
rect->y = work_area->y + fluff;
}
-static void
+void
center_rect_in_area (MetaRectangle *rect,
MetaRectangle *work_area)
{
diff --git a/src/core/place.h b/src/core/place.h
index 468e3a39..6a2d0ff4 100644
--- a/src/core/place.h
+++ b/src/core/place.h
@@ -34,4 +34,7 @@ void meta_window_place (MetaWindow *window,
int *new_x,
int *new_y);
+void center_rect_in_area (MetaRectangle *rect,
+ MetaRectangle *work_area);
+
#endif
diff --git a/src/core/prefs.c b/src/core/prefs.c
index 844b1a06..e1e09153 100644
--- a/src/core/prefs.c
+++ b/src/core/prefs.c
@@ -36,10 +36,6 @@
#define MAX_REASONABLE_WORKSPACES 36
-#define MAX_COMMANDS (32 + NUM_EXTRA_COMMANDS)
-#define NUM_EXTRA_COMMANDS 2
-#define SCREENSHOT_COMMAND_IDX (MAX_COMMANDS - 2)
-#define WIN_SCREENSHOT_COMMAND_IDX (MAX_COMMANDS - 1)
/* If you add a key, it needs updating in init() and in the GSettings
* notify listener and of course in the .gschema file.
@@ -105,6 +101,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;
@@ -376,6 +373,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,
@@ -1583,6 +1586,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)
{
@@ -1636,6 +1645,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";
@@ -2053,6 +2065,10 @@ update_command (const char *name,
{
i = WIN_SCREENSHOT_COMMAND_IDX;
}
+ else if (strcmp (name, "command-area-screenshot") == 0)
+ {
+ i = AREA_SCREENSHOT_COMMAND_IDX;
+ }
else
{
meta_topic (META_DEBUG_KEYBINDINGS,
@@ -2107,6 +2123,9 @@ meta_prefs_get_settings_key_for_command (int i)
case WIN_SCREENSHOT_COMMAND_IDX:
key = g_strdup (KEY_COMMAND_PREFIX "window-screenshot");
break;
+ case AREA_SCREENSHOT_COMMAND_IDX:
+ key = g_strdup (KEY_COMMAND_PREFIX "area-screenshot");
+ break;
default:
key = g_strdup_printf (KEY_COMMAND_PREFIX"%d", i + 1);
break;
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 6c61585e..f6f1c14a 100644
--- a/src/core/screen.c
+++ b/src/core/screen.c
@@ -55,11 +55,17 @@
#include <string.h>
#include <stdio.h>
+#define MAX_REASONABLE_WORKSPACES 36
+
static char* get_screen_name (MetaDisplay *display,
int number);
static void update_num_workspaces (MetaScreen *screen,
guint32 timestamp);
+static void safely_remove_workspaces (MetaScreen *screen,
+ GList *extras,
+ MetaWorkspace *last_remaining,
+ guint32 timestamp);
static void update_focus_mode (MetaScreen *screen);
static void set_workspace_names (MetaScreen *screen);
static void prefs_changed_callback (MetaPreference pref,
@@ -509,6 +515,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;
@@ -645,6 +652,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)
@@ -882,6 +895,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);
@@ -1113,6 +1130,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)
{
@@ -1158,39 +1209,200 @@ 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_count = MAX (0, trailing_empty_count - 1);
+ if (workspaces_to_remove_count > 0)
+ {
+ GList *workspaces_to_remove = NULL;
+ MetaWorkspace *safe_target = NULL;
+
+ /* Build list of workspaces to remove */
+ int current_count = meta_screen_get_n_workspaces (screen);
+ for (int i = 0; i < workspaces_to_remove_count; i++)
+ {
+ MetaWorkspace *last_ws = meta_screen_get_workspace_by_index (screen, current_count - 1 - i);
+ if (last_ws && !workspace_has_non_sticky_windows (last_ws))
+ {
+ workspaces_to_remove = g_list_prepend (workspaces_to_remove, last_ws);
+ }
+ }
+
+ /* Find a safe target workspace - prefer non-empty workspaces, but accept
+ * any workspace that will remain if all are empty */
+ for (int i = 0; i < meta_screen_get_n_workspaces (screen); i++)
+ {
+ MetaWorkspace *ws = meta_screen_get_workspace_by_index (screen, i);
+ if (ws && !g_list_find (workspaces_to_remove, ws))
+ {
+ /* Found a workspace that will remain */
+ if (!safe_target)
+ safe_target = ws; /* Use this as fallback */
+
+ /* Prefer non-empty workspaces */
+ if (workspace_has_non_sticky_windows (ws))
+ {
+ safe_target = ws;
+ break;
+ }
+ }
+ }
+
+ /* If we found workspaces to remove and a safe target, do the removal */
+ if (workspaces_to_remove && safe_target)
+ {
+ guint32 timestamp = meta_display_get_current_time_roundtrip (screen->display);
+ int expected_count = current_count - g_list_length (workspaces_to_remove);
+ safely_remove_workspaces (screen, workspaces_to_remove, safe_target, timestamp);
+
+ /* Verify workspace count is as expected after removal */
+ int actual_count = meta_screen_get_n_workspaces (screen);
+ if (actual_count != expected_count)
+ {
+ meta_warning ("Dynamic workspaces: expected %d workspaces after removal, got %d\n",
+ expected_count, actual_count);
+ }
+
+ request_workspace_count_change (screen, actual_count);
+ }
+ else if (workspaces_to_remove && !safe_target)
+ {
+ meta_warning ("Dynamic workspaces: could not find safe target for workspace removal\n");
+ }
+
+ g_list_free (workspaces_to_remove);
+ }
+
+ 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)
+safely_remove_workspaces (MetaScreen *screen,
+ GList *extras,
+ MetaWorkspace *last_remaining,
+ guint32 timestamp)
{
- int new_num;
GList *tmp;
- int i;
- GList *extras;
- MetaWorkspace *last_remaining;
gboolean need_change_space;
- new_num = meta_prefs_get_num_workspaces ();
+ g_return_if_fail (screen != NULL);
+ g_return_if_fail (last_remaining != NULL);
- g_assert (new_num > 0);
+ if (extras == NULL)
+ return;
- last_remaining = NULL;
- extras = NULL;
- i = 0;
- tmp = screen->workspaces;
- while (tmp != NULL)
+ /* Validate that we're not trying to remove all workspaces */
+ int total_workspaces = meta_screen_get_n_workspaces (screen);
+ int removal_count = g_list_length (extras);
+ if (removal_count >= total_workspaces)
{
- MetaWorkspace *w = tmp->data;
-
- if (i >= new_num)
- extras = g_list_prepend (extras, w);
- else
- last_remaining = w;
+ meta_warning ("Attempted to remove all workspaces (%d >= %d), aborting\n",
+ removal_count, total_workspaces);
+ return;
+ }
- ++i;
- tmp = tmp->next;
+ /* Validate that last_remaining is not in the removal list */
+ if (g_list_find (extras, last_remaining))
+ {
+ meta_warning ("Last remaining workspace is in removal list, aborting workspace removal\n");
+ return;
}
- g_assert (last_remaining);
+ meta_verbose ("Safely removing %d workspaces\n", g_list_length (extras));
/* Get rid of the extra workspaces by moving all their windows
* to last_remaining, then activating last_remaining if
@@ -1227,6 +1439,44 @@ update_num_workspaces (MetaScreen *screen,
tmp = tmp->next;
}
+ meta_verbose ("Workspace removal completed successfully\n");
+}
+
+static void
+update_num_workspaces (MetaScreen *screen,
+ guint32 timestamp)
+{
+ int new_num;
+ GList *tmp;
+ int i;
+ GList *extras;
+ MetaWorkspace *last_remaining;
+
+ new_num = meta_prefs_get_num_workspaces ();
+
+ g_assert (new_num > 0);
+
+ last_remaining = NULL;
+ extras = NULL;
+ i = 0;
+ tmp = screen->workspaces;
+ while (tmp != NULL)
+ {
+ MetaWorkspace *w = tmp->data;
+
+ if (i >= new_num)
+ extras = g_list_prepend (extras, w);
+ else
+ last_remaining = w;
+
+ ++i;
+ tmp = tmp->next;
+ }
+
+ g_assert (last_remaining);
+
+ safely_remove_workspaces (screen, extras, last_remaining, timestamp);
+
g_list_free (extras);
while (i < new_num)
diff --git a/src/core/window-private.h b/src/core/window-private.h
index 52864a86..06d4fd65 100644
--- a/src/core/window-private.h
+++ b/src/core/window-private.h
@@ -687,6 +687,8 @@ const char* meta_window_get_startup_id (MetaWindow *window);
void meta_window_recalc_features (MetaWindow *window);
void meta_window_recalc_window_type (MetaWindow *window);
+void meta_window_frame_size_changed (MetaWindow *window);
+
void meta_window_stack_just_below (MetaWindow *window,
MetaWindow *below_this_one);
diff --git a/src/core/window.c b/src/core/window.c
index 050c89f8..ae0fa791 100644
--- a/src/core/window.c
+++ b/src/core/window.c
@@ -2593,6 +2593,8 @@ meta_window_maximize_internal (MetaWindow *window,
window->maximized_vertically =
window->maximized_vertically || maximize_vertically;
+ meta_window_frame_size_changed (window);
+
/* Fix for #336850: If the frame shape isn't reapplied, it is
* possible that the frame will retains its rounded corners. That
* happens if the client's size when maximized equals the unmaximized
@@ -2725,6 +2727,13 @@ meta_window_tile (MetaWindow *window)
*/
meta_window_queue (window, META_QUEUE_MOVE_RESIZE);
+ /* Clear cached frame bounds that depend on invisible border calculations */
+ if (window->frame_bounds)
+ {
+ cairo_region_destroy (window->frame_bounds);
+ window->frame_bounds = NULL;
+ }
+
set_allowed_actions_hint (window);
}
@@ -2808,6 +2817,8 @@ meta_window_unmaximize (MetaWindow *window,
window->maximized_vertically =
window->maximized_vertically && !unmaximize_vertically;
+ meta_window_frame_size_changed (window);
+
/* Unmaximize to the saved_rect position in the direction(s)
* being unmaximized.
*/
@@ -2932,6 +2943,8 @@ meta_window_make_above (MetaWindow *window)
meta_window_update_layer (window);
meta_window_raise (window);
set_net_wm_state (window);
+
+ meta_window_frame_size_changed (window);
}
void
@@ -2941,6 +2954,8 @@ meta_window_unmake_above (MetaWindow *window)
meta_window_raise (window);
meta_window_update_layer (window);
set_net_wm_state (window);
+
+ meta_window_frame_size_changed (window);
}
void
@@ -3071,7 +3086,8 @@ meta_window_shade (MetaWindow *window,
{
window->shaded = TRUE;
- meta_window_queue(window, META_QUEUE_MOVE_RESIZE | META_QUEUE_CALC_SHOWING);
+ meta_window_queue (window, META_QUEUE_MOVE_RESIZE | META_QUEUE_CALC_SHOWING);
+ meta_window_frame_size_changed (window);
set_allowed_actions_hint (window);
@@ -3096,7 +3112,9 @@ meta_window_unshade (MetaWindow *window,
if (window->shaded)
{
window->shaded = FALSE;
- meta_window_queue(window, META_QUEUE_MOVE_RESIZE | META_QUEUE_CALC_SHOWING);
+
+ meta_window_queue (window, META_QUEUE_MOVE_RESIZE | META_QUEUE_CALC_SHOWING);
+ meta_window_frame_size_changed (window);
set_allowed_actions_hint (window);
@@ -4531,6 +4549,8 @@ window_stick_impl (MetaWindow *window)
*/
window->on_all_workspaces = TRUE;
+ meta_window_frame_size_changed (window);
+
/* We do, however, change the MRU lists of all the workspaces
*/
tmp = window->screen->workspaces;
@@ -4561,6 +4581,8 @@ window_unstick_impl (MetaWindow *window)
window->on_all_workspaces = FALSE;
+ meta_window_frame_size_changed (window);
+
/* Remove window from MRU lists that it doesn't belong in */
tmp = window->screen->workspaces;
while (tmp)
@@ -5587,6 +5609,7 @@ static void
meta_window_appears_focused_changed (MetaWindow *window)
{
set_net_wm_state (window);
+ meta_window_frame_size_changed (window);
if (window->frame)
meta_frame_queue_draw (window->frame);
@@ -6583,6 +6606,13 @@ recalc_window_type (MetaWindow *window)
}
}
+void
+meta_window_frame_size_changed (MetaWindow *window)
+{
+ if (window->frame)
+ meta_frame_clear_cached_borders (window->frame);
+}
+
static void
set_allowed_actions_hint (MetaWindow *window)
{
@@ -6877,6 +6907,8 @@ recalc_window_features (MetaWindow *window)
old_always_sticky != window->always_sticky)
set_allowed_actions_hint (window);
+ meta_window_frame_size_changed (window);
+
/* FIXME perhaps should ensure if we don't have a shade func,
* we aren't shaded, etc.
*/
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/all-keybindings.h b/src/include/all-keybindings.h
index 992d077f..02452878 100644
--- a/src/include/all-keybindings.h
+++ b/src/include/all-keybindings.h
@@ -194,6 +194,7 @@ keybind (run-command-32, handle_run_command, 31, 0)
keybind (run-command-screenshot, handle_run_command, 32, 0)
keybind (run-command-window-screenshot, handle_run_command, 33, 0)
+keybind (run-command-area-screenshot, handle_run_command, 34, 0)
keybind (run-command-terminal, handle_run_terminal, 0, 0)
keybind (rename-workspace, handle_rename_workspace, 0, 0)
diff --git a/src/include/common.h b/src/include/common.h
index 031274f6..a0dfb69c 100644
--- a/src/include/common.h
+++ b/src/include/common.h
@@ -310,15 +310,11 @@ struct _MetaButtonLayout
typedef struct _MetaFrameBorders MetaFrameBorders;
struct _MetaFrameBorders
{
- /* The frame border is made up of two pieces - an inner visible portion
- * and an outer portion that is invisible but responds to events.
- */
- GtkBorder visible;
- GtkBorder invisible;
-
- /* For convenience, we have a "total" border which is equal to the sum
- * of the two borders above. */
- GtkBorder total;
+ GtkBorder visible; /* Visible window frame decoration */
+ GtkBorder shadow; /* Extra size needed for shadow */
+ GtkBorder resize; /* Extra size used for resize cursor area */
+ GtkBorder invisible; /* Max of shadow and resize borders */
+ GtkBorder total; /* Sum of visible and invisible borders */
};
/* sets all dimensions to zero */
diff --git a/src/include/frame.h b/src/include/frame.h
index 97314d4b..2deba88b 100644
--- a/src/include/frame.h
+++ b/src/include/frame.h
@@ -30,5 +30,6 @@
Window meta_frame_get_xwindow (MetaFrame *frame);
void meta_frame_calc_borders (MetaFrame *frame,
MetaFrameBorders *borders);
+void meta_frame_clear_cached_borders (MetaFrame *frame);
#endif
diff --git a/src/include/prefs.h b/src/include/prefs.h
index e34f1b14..1170e8e8 100644
--- a/src/include/prefs.h
+++ b/src/include/prefs.h
@@ -29,6 +29,12 @@
#include "common.h"
#include <pango/pango-font.h>
+#define MAX_COMMANDS (32 + NUM_EXTRA_COMMANDS)
+#define NUM_EXTRA_COMMANDS 3
+#define SCREENSHOT_COMMAND_IDX (MAX_COMMANDS - 3)
+#define WIN_SCREENSHOT_COMMAND_IDX (MAX_COMMANDS - 2)
+#define AREA_SCREENSHOT_COMMAND_IDX (MAX_COMMANDS - 1)
+
typedef enum
{
META_PREF_MOUSE_BUTTON_MODS,
@@ -46,6 +52,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,
@@ -101,6 +108,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 731ffc11..ec80cf40 100644
--- a/src/org.mate.marco.gschema.xml
+++ b/src/org.mate.marco.gschema.xml
@@ -142,6 +142,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>
@@ -665,6 +670,11 @@
<summary>Take a screenshot of a window</summary>
<description>The format looks like "&lt;Control&gt;a" or "&lt;Shift&gt;&lt;Alt&gt;F1". The parser is fairly liberal and allows lower or upper case, and also abbreviations such as "&lt;Ctl&gt;" and "&lt;Ctrl&gt;". If you set the option to the special string "disabled", then there will be no keybinding for this action.</description>
</key>
+ <key name="run-command-area-screenshot" type="s">
+ <default>'&lt;Shift&gt;Print'</default>
+ <summary>Take a screenshot of an area</summary>
+ <description>The format looks like "&lt;Control&gt;a" or "&lt;Shift&gt;&lt;Alt&gt;F1". The parser is fairly liberal and allows lower or upper case, and also abbreviations such as "&lt;Ctl&gt;" and "&lt;Ctrl&gt;". If you set the option to the special string "disabled", then there will be no keybinding for this action.</description>
+ </key>
<key name="run-command-terminal" type="s">
<default>'disabled'</default>
<summary>Run a terminal</summary>
@@ -748,6 +758,11 @@
<summary>The window screenshot command</summary>
<description>The /apps/marco/global_keybindings/run_command_window_screenshot key defines a keybinding which causes the command specified by this setting to be invoked.</description>
</key>
+ <key name="command-area-screenshot" type="s">
+ <default>'mate-screenshot --area'</default>
+ <summary>The area screenshot command</summary>
+ <description>The /apps/marco/global_keybindings/run_command_area_screenshot key defines a keybinding which causes the command specified by this setting to be invoked.</description>
+ </key>
</schema>
<schema id="org.mate.Marco.window-keybindings" path="/org/mate/marco/window-keybindings/">
diff --git a/src/ui/theme.c b/src/ui/theme.c
index dadc9ab8..8bd144f2 100644
--- a/src/ui/theme.c
+++ b/src/ui/theme.c
@@ -328,10 +328,10 @@ meta_frame_layout_new (void)
layout->right_width = -1;
layout->bottom_height = -1;
- layout->invisible_border.left = 10;
- layout->invisible_border.right = 10;
- layout->invisible_border.bottom = 10;
- layout->invisible_border.top = 10;
+ layout->invisible_resize_border.left = 10;
+ layout->invisible_resize_border.right = 10;
+ layout->invisible_resize_border.bottom = 10;
+ layout->invisible_resize_border.top = 10;
init_border (&layout->title_border);
@@ -514,6 +514,7 @@ void
meta_frame_layout_get_borders (const MetaFrameLayout *layout,
int text_height,
MetaFrameFlags flags,
+ MetaFrameType type,
MetaFrameBorders *borders)
{
int buttons_height, title_height;
@@ -540,25 +541,52 @@ meta_frame_layout_get_borders (const MetaFrameLayout *layout,
borders->visible.right = layout->right_width;
borders->visible.bottom = layout->bottom_height;
+ borders->shadow.top = 0;
+ borders->shadow.left = 0;
+ borders->shadow.right = 0;
+ borders->shadow.bottom = 0;
+
if (flags & META_FRAME_ALLOWS_HORIZONTAL_RESIZE)
{
- borders->invisible.left = layout->invisible_border.left;
- borders->invisible.right = layout->invisible_border.right;
+ borders->resize.left = layout->invisible_resize_border.left;
+ borders->resize.right = layout->invisible_resize_border.right;
}
if (flags & META_FRAME_ALLOWS_VERTICAL_RESIZE)
{
- borders->invisible.bottom = layout->invisible_border.bottom;
- borders->invisible.top = layout->invisible_border.top;
+ borders->resize.bottom = layout->invisible_resize_border.bottom;
+
+ if (type != META_FRAME_TYPE_ATTACHED)
+ borders->resize.top = layout->invisible_resize_border.top;
}
- if (flags & META_FRAME_SHADED)
- borders->visible.bottom = borders->invisible.bottom = 0;
+ borders->invisible.left = MAX (borders->shadow.left, borders->resize.left);
+ borders->invisible.right = MAX (borders->shadow.right, borders->resize.right);
+ borders->invisible.bottom = MAX (borders->shadow.bottom, borders->resize.bottom);
+ borders->invisible.top = MAX (borders->shadow.top, borders->resize.top);
+
+ /* Maximized and tiled windows should not have invisible borders on the sides
+ * that touch the screen edges */
+ if (flags & (META_FRAME_MAXIMIZED | META_FRAME_TILED_LEFT | META_FRAME_TILED_RIGHT))
+ {
+ borders->invisible.top = 0;
+ borders->invisible.bottom = 0;
+ }
+ if (flags & (META_FRAME_MAXIMIZED | META_FRAME_TILED_LEFT))
+ borders->invisible.left = 0;
+ if (flags & (META_FRAME_MAXIMIZED | META_FRAME_TILED_RIGHT))
+ borders->invisible.right = 0;
+
+ if (type == META_FRAME_TYPE_ATTACHED)
+ borders->invisible.top = 0;
borders->total.left = borders->invisible.left + borders->visible.left;
borders->total.right = borders->invisible.right + borders->visible.right;
borders->total.bottom = borders->invisible.bottom + borders->visible.bottom;
borders->total.top = borders->invisible.top + borders->visible.top;
+
+ if (flags & META_FRAME_SHADED)
+ borders->visible.bottom = borders->invisible.bottom = 0;
}
static MetaButtonType
@@ -758,6 +786,7 @@ meta_frame_layout_calc_geometry (const MetaFrameLayout *layout,
meta_frame_layout_get_borders (layout, text_height,
flags,
+ META_FRAME_TYPE_NORMAL, /* Will be updated when type parameter is passed down */
&borders);
fgeom->borders = borders;
@@ -5947,6 +5976,7 @@ meta_theme_get_frame_borders (MetaTheme *theme,
meta_frame_layout_get_borders (style->layout,
text_height,
flags,
+ type,
borders);
}
diff --git a/src/ui/theme.h b/src/ui/theme.h
index b32690f1..c3c10810 100644
--- a/src/ui/theme.h
+++ b/src/ui/theme.h
@@ -92,8 +92,7 @@ struct _MetaFrameLayout
/** Size of bottom side */
int bottom_height;
- /** Invisible border */
- GtkBorder invisible_border;
+ GtkBorder invisible_resize_border;
/** Border of blue title region
* \bug (blue?!)
@@ -874,6 +873,7 @@ void meta_frame_layout_unref (MetaFrameLayout *layout)
void meta_frame_layout_get_borders (const MetaFrameLayout *layout,
int text_height,
MetaFrameFlags flags,
+ MetaFrameType type,
MetaFrameBorders *borders);
void meta_frame_layout_calc_geometry (const MetaFrameLayout *layout,
int text_height,