diff options
| -rw-r--r-- | src/50-marco-desktop-key.xml.in | 2 | ||||
| -rw-r--r-- | src/core/frame-private.h | 4 | ||||
| -rw-r--r-- | src/core/frame.c | 46 | ||||
| -rw-r--r-- | src/core/keybindings.c | 59 | ||||
| -rw-r--r-- | src/core/place.c | 2 | ||||
| -rw-r--r-- | src/core/place.h | 3 | ||||
| -rw-r--r-- | src/core/prefs.c | 27 | ||||
| -rw-r--r-- | src/core/screen-private.h | 3 | ||||
| -rw-r--r-- | src/core/screen.c | 294 | ||||
| -rw-r--r-- | src/core/window-private.h | 2 | ||||
| -rw-r--r-- | src/core/window.c | 36 | ||||
| -rw-r--r-- | src/core/workspace.c | 7 | ||||
| -rw-r--r-- | src/include/all-keybindings.h | 1 | ||||
| -rw-r--r-- | src/include/common.h | 14 | ||||
| -rw-r--r-- | src/include/frame.h | 1 | ||||
| -rw-r--r-- | src/include/prefs.h | 8 | ||||
| -rw-r--r-- | src/org.mate.marco.gschema.xml | 15 | ||||
| -rw-r--r-- | src/ui/theme.c | 50 | ||||
| -rw-r--r-- | src/ui/theme.h | 4 |
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 "<Control>a" or "<Shift><Alt>F1". The parser is fairly liberal and allows lower or upper case, and also abbreviations such as "<Ctl>" and "<Ctrl>". 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>'<Shift>Print'</default> + <summary>Take a screenshot of an area</summary> + <description>The format looks like "<Control>a" or "<Shift><Alt>F1". The parser is fairly liberal and allows lower or upper case, and also abbreviations such as "<Ctl>" and "<Ctrl>". 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, |
