/* Metacity X managed windows */ /* * Copyright (C) 2001 Havoc Pennington, Anders Carlsson * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include #include "window.h" #include "util.h" #include "frame.h" #include "errors.h" #include "workspace.h" #include "stack.h" #include "keybindings.h" #include "ui.h" #include "place.h" #include "session.h" #include "effects.h" #include "prefs.h" #include "xprops.h" #include typedef enum { META_IS_CONFIGURE_REQUEST = 1 << 0, META_DO_GRAVITY_ADJUST = 1 << 1, META_USER_MOVE_RESIZE = 1 << 2 } MetaMoveResizeFlags; typedef enum { WIN_HINTS_SKIP_FOCUS = (1<<0), /* "alt-tab" skips this win */ WIN_HINTS_SKIP_WINLIST = (1<<1), /* not in win list */ WIN_HINTS_SKIP_TASKBAR = (1<<2), /* not on taskbar */ WIN_HINTS_GROUP_TRANSIENT = (1<<3), /* ??????? */ WIN_HINTS_FOCUS_ON_CLICK = (1<<4), /* app only accepts focus when clicked */ WIN_HINTS_DO_NOT_COVER = (1<<5) /* attempt to not cover this window */ } GnomeWinHints; static int destroying_windows_disallowed = 0; static void constrain_size (MetaWindow *window, MetaFrameGeometry *fgeom, int width, int height, int *new_width, int *new_height); static void constrain_position (MetaWindow *window, MetaFrameGeometry *fgeom, int x, int y, int *new_x, int *new_y); static int update_size_hints (MetaWindow *window); static int update_title (MetaWindow *window); static int update_protocols (MetaWindow *window); static int update_wm_hints (MetaWindow *window); static void update_net_wm_state (MetaWindow *window); static void update_mwm_hints (MetaWindow *window); static int update_wm_class (MetaWindow *window); static int update_transient_for (MetaWindow *window); static void update_sm_hints (MetaWindow *window); static void update_role (MetaWindow *window); static void update_net_wm_type (MetaWindow *window); static int update_initial_workspace (MetaWindow *window); static int update_icon_name (MetaWindow *window); static int update_icon (MetaWindow *window, gboolean reread_rgb_icon); static int update_kwm_icon (MetaWindow *window); static void update_struts (MetaWindow *window); static void recalc_window_type (MetaWindow *window); static void recalc_window_features (MetaWindow *window); static void recalc_do_not_cover_struts(MetaWindow *window); static void invalidate_work_areas (MetaWindow *window); static int set_wm_state (MetaWindow *window, int state); static int set_net_wm_state (MetaWindow *window); static void send_configure_notify (MetaWindow *window); static gboolean process_property_notify (MetaWindow *window, XPropertyEvent *event); static void meta_window_show (MetaWindow *window); static void meta_window_hide (MetaWindow *window); static GList* meta_window_get_workspaces (MetaWindow *window); static gboolean meta_window_get_icon_geometry (MetaWindow *window, MetaRectangle *rect); static void adjust_for_gravity (MetaWindow *window, MetaFrameGeometry *fgeom, gboolean coords_assume_border, int x, int y, int *xp, int *yp); static void meta_window_move_resize_internal (MetaWindow *window, MetaMoveResizeFlags flags, int resize_gravity, int root_x_nw, int root_y_nw, int w, int h); void meta_window_move_resize_now (MetaWindow *window); static char* get_text_property (MetaDisplay *display, Window xwindow, Atom atom); void meta_window_unqueue_calc_showing (MetaWindow *window); void meta_window_flush_calc_showing (MetaWindow *window); void meta_window_unqueue_move_resize (MetaWindow *window); void meta_window_flush_move_resize (MetaWindow *window); static void meta_window_apply_session_info (MetaWindow *window, const MetaWindowSessionInfo *info); MetaWindow* meta_window_new (MetaDisplay *display, Window xwindow, gboolean must_be_viewable) { MetaWindow *window; XWindowAttributes attrs; GSList *tmp; MetaWorkspace *space; gulong existing_wm_state; meta_verbose ("Attempting to manage 0x%lx\n", xwindow); if (xwindow == display->no_focus_window) { meta_verbose ("Not managing no_focus_window 0x%lx\n", xwindow); return NULL; } /* Grab server */ meta_display_grab (display); meta_error_trap_push (display); XGetWindowAttributes (display->xdisplay, xwindow, &attrs); if (meta_error_trap_pop (display)) { meta_verbose ("Failed to get attributes for window 0x%lx\n", xwindow); meta_display_ungrab (display); return NULL; } if (attrs.override_redirect) { meta_verbose ("Deciding not to manage override_redirect window 0x%lx\n", xwindow); meta_display_ungrab (display); return NULL; } meta_verbose ("attrs.map_state = %d (%s)\n", attrs.map_state, (attrs.map_state == IsUnmapped) ? "IsUnmapped" : (attrs.map_state == IsViewable) ? "IsViewable" : (attrs.map_state == IsUnviewable) ? "IsUnviewable" : "(unknown)"); existing_wm_state = WithdrawnState; if (must_be_viewable && attrs.map_state != IsViewable) { /* Only manage if WM_STATE is IconicState or NormalState */ gulong state; /* WM_STATE isn't a cardinal, it's type WM_STATE, but is an int */ if (!(meta_prop_get_cardinal_with_atom_type (display, xwindow, display->atom_wm_state, display->atom_wm_state, &state) && (state == IconicState || state == NormalState))) { meta_verbose ("Deciding not to manage unmapped or unviewable window 0x%lx\n", xwindow); meta_display_ungrab (display); return NULL; } existing_wm_state = state; } meta_error_trap_push (display); XAddToSaveSet (display->xdisplay, xwindow); XSelectInput (display->xdisplay, xwindow, PropertyChangeMask | EnterWindowMask | LeaveWindowMask | FocusChangeMask); /* Get rid of any borders */ if (attrs.border_width != 0) XSetWindowBorderWidth (display->xdisplay, xwindow, 0); /* Get rid of weird gravities */ if (attrs.win_gravity != NorthWestGravity) { XSetWindowAttributes set_attrs; set_attrs.win_gravity = NorthWestGravity; XChangeWindowAttributes (display->xdisplay, xwindow, CWWinGravity, &set_attrs); } if (meta_error_trap_pop (display) != Success) { meta_verbose ("Window 0x%lx disappeared just as we tried to manage it\n", xwindow); meta_display_ungrab (display); return NULL; } g_assert (!attrs.override_redirect); window = g_new (MetaWindow, 1); window->xwindow = xwindow; /* this is in window->screen->display, but that's too annoying to * type */ window->display = display; window->workspaces = NULL; window->screen = NULL; tmp = display->screens; while (tmp != NULL) { if (((MetaScreen *)tmp->data)->xscreen == attrs.screen) { window->screen = tmp->data; break; } tmp = tmp->next; } g_assert (window->screen); /* avoid tons of stack updates */ meta_stack_freeze (window->screen->stack); /* Remember this rect is the actual window size */ window->rect.x = attrs.x; window->rect.y = attrs.y; window->rect.width = attrs.width; window->rect.height = attrs.height; window->size_hints.flags = 0; /* And border width, size_hints are the "request" */ window->border_width = attrs.border_width; window->size_hints.x = attrs.x; window->size_hints.y = attrs.y; window->size_hints.width = attrs.width; window->size_hints.height = attrs.height; /* And this is our unmaximized size */ window->saved_rect = window->rect; window->user_rect = window->rect; window->depth = attrs.depth; window->xvisual = attrs.visual; window->title = NULL; window->icon_name = NULL; window->icon = NULL; window->mini_icon = NULL; window->desc = g_strdup_printf ("0x%lx", window->xwindow); window->frame = NULL; window->has_focus = FALSE; window->user_has_move_resized = FALSE; window->maximized = FALSE; window->on_all_workspaces = FALSE; window->shaded = FALSE; window->initially_iconic = FALSE; window->minimized = FALSE; window->iconic = FALSE; window->mapped = attrs.map_state != IsUnmapped; /* if already mapped we don't want to do the placement thing */ window->placed = window->mapped; if (window->placed) meta_verbose ("Not placing window 0x%lx since it's already mapped\n", xwindow); window->unmanaging = FALSE; window->calc_showing_queued = FALSE; window->move_resize_queued = FALSE; window->keys_grabbed = FALSE; window->grab_on_frame = FALSE; window->all_keys_grabbed = FALSE; window->withdrawn = FALSE; window->initial_workspace_set = FALSE; window->calc_placement = FALSE; window->unmaps_pending = 0; window->mwm_decorated = TRUE; window->mwm_has_close_func = TRUE; window->mwm_has_minimize_func = TRUE; window->mwm_has_maximize_func = TRUE; window->mwm_has_move_func = TRUE; window->mwm_has_resize_func = TRUE; window->decorated = TRUE; window->has_close_func = TRUE; window->has_minimize_func = TRUE; window->has_maximize_func = TRUE; window->has_move_func = TRUE; window->has_resize_func = TRUE; window->has_shade_func = TRUE; window->wm_state_modal = FALSE; window->wm_state_skip_taskbar = FALSE; window->wm_state_skip_pager = FALSE; window->res_class = NULL; window->res_name = NULL; window->role = NULL; window->sm_client_id = NULL; window->xtransient_for = None; window->xgroup_leader = None; window->xclient_leader = None; window->icon_pixmap = None; window->icon_mask = None; window->kwm_pixmap = None; window->kwm_mask = None; window->using_rgb_icon = FALSE; window->type = META_WINDOW_NORMAL; window->type_atom = None; window->has_struts = FALSE; window->left_strut = 0; window->right_strut = 0; window->top_strut = 0; window->bottom_strut = 0; window->layer = META_LAYER_NORMAL; window->stack_op = NULL; window->initial_workspace = 0; /* not used */ meta_display_register_x_window (display, &window->xwindow, window); update_size_hints (window); update_title (window); update_protocols (window); update_wm_hints (window); update_struts (window); update_net_wm_state (window); /* Initially maximize if window is fullscreen; FIXME * assume fullscreen state instead once we have that state... */ if (!window->maximized && attrs.x == 0 && attrs.y == 0 && attrs.width == window->screen->width && attrs.height == window->screen->height) window->maximized = TRUE; update_mwm_hints (window); update_wm_class (window); update_transient_for (window); update_sm_hints (window); /* must come after transient_for */ update_role (window); update_net_wm_type (window); update_initial_workspace (window); update_icon_name (window); update_kwm_icon (window); /* should come after wm_hints and kwm_icon updates */ update_icon (window, TRUE); if (!window->mapped && (window->size_hints.flags & PPosition) == 0 && (window->size_hints.flags & USPosition) == 0) { /* ignore current window position */ window->size_hints.x = 0; window->size_hints.y = 0; } if (window->initially_iconic) { /* WM_HINTS said minimized */ window->minimized = TRUE; meta_verbose ("Window %s asked to start out minimized\n", window->desc); } if (existing_wm_state == IconicState) { /* WM_STATE said minimized */ window->minimized = TRUE; meta_verbose ("Window %s had preexisting WM_STATE = IconicState, minimizing\n", window->desc); /* Assume window was previously placed, though perhaps it's * been iconic its whole life, we have no way of knowing. */ window->placed = TRUE; } /* FIXME we have a tendency to set this then immediately * change it again. */ set_wm_state (window, window->iconic ? IconicState : NormalState); set_net_wm_state (window); if (window->decorated) meta_window_ensure_frame (window); meta_window_grab_keys (window); meta_display_grab_window_buttons (window->display, window->xwindow); meta_display_grab_focus_window_button (window->display, window->xwindow); /* For the workspace, first honor hints, * if that fails put transients with parents, * otherwise put window on active space */ if (window->initial_workspace_set) { if (window->initial_workspace == 0xFFFFFFFF) { meta_workspace_add_window (window->screen->active_workspace, window); window->on_all_workspaces = TRUE; } else { space = meta_display_get_workspace_by_screen_index (window->display, window->screen, window->initial_workspace); if (space) meta_workspace_add_window (space, window); } } if (window->workspaces == NULL && window->xtransient_for != None) { /* Try putting dialog on parent's workspace */ MetaWindow *parent; parent = meta_display_lookup_x_window (window->display, window->xtransient_for); if (parent) { GList *tmp; if (parent->on_all_workspaces) window->on_all_workspaces = TRUE; tmp = parent->workspaces; while (tmp != NULL) { meta_workspace_add_window (tmp->data, window); tmp = tmp->next; } } } if (window->workspaces == NULL) { space = window->screen->active_workspace; meta_workspace_add_window (space, window); } /* Only accept USPosition on normal windows because the app is full * of shit claiming the user set -geometry for a dialog or dock */ if (window->type == META_WINDOW_NORMAL && (window->size_hints.flags & USPosition)) { /* don't constrain with placement algorithm */ window->placed = TRUE; meta_verbose ("Honoring USPosition for %s instead of using placement algorithm\n", window->desc); } /* Assume the app knows best how to place these. */ if (window->type == META_WINDOW_DESKTOP || window->type == META_WINDOW_DOCK || window->type == META_WINDOW_TOOLBAR || window->type == META_WINDOW_MENU) { if (window->size_hints.flags & PPosition) { window->placed = TRUE; meta_verbose ("Not placing non-normal non-dialog window with PPosition set\n"); } } if (window->type == META_WINDOW_DESKTOP || window->type == META_WINDOW_DOCK) { /* Change the default, but don't enforce this if * the user focuses the dock/desktop and unsticks it * using key shortcuts */ window->on_all_workspaces = TRUE; } /* for the various on_all_workspaces = TRUE possible above */ meta_window_set_current_workspace_hint (window); /* Put our state back where it should be, * passing TRUE for is_configure_request, ICCCM says * initial map is handled same as configure request */ meta_window_move_resize_internal (window, META_IS_CONFIGURE_REQUEST, NorthWestGravity, window->size_hints.x, window->size_hints.y, window->size_hints.width, window->size_hints.height); meta_stack_add (window->screen->stack, window); /* Now try applying saved stuff from the session */ { const MetaWindowSessionInfo *info; info = meta_window_lookup_saved_state (window); if (info) { meta_window_apply_session_info (window, info); meta_window_release_saved_state (info); } } /* Sync stack changes */ meta_stack_thaw (window->screen->stack); meta_window_queue_calc_showing (window); meta_display_ungrab (display); return window; } /* This function should only be called from the end of meta_window_new () */ static void meta_window_apply_session_info (MetaWindow *window, const MetaWindowSessionInfo *info) { if (info->on_all_workspaces_set) { window->on_all_workspaces = info->on_all_workspaces; meta_verbose ("Restoring sticky state %d for window %s\n", window->on_all_workspaces, window->desc); } if (info->workspace_indices) { GSList *tmp; GSList *spaces; spaces = NULL; tmp = info->workspace_indices; while (tmp != NULL) { MetaWorkspace *space; space = meta_display_get_workspace_by_screen_index (window->display, window->screen, GPOINTER_TO_INT (tmp->data)); if (space) spaces = g_slist_prepend (spaces, space); tmp = tmp->next; } if (spaces) { /* This briefly breaks the invariant that we are supposed * to always be on some workspace. But we paranoically * ensured that one of the workspaces from the session was * indeed valid, so we know we'll go right back to one. */ while (window->workspaces) meta_workspace_remove_window (window->workspaces->data, window); tmp = spaces; while (tmp != NULL) { MetaWorkspace *space; space = tmp->data; meta_workspace_add_window (space, window); meta_verbose ("Restoring saved window %s to workspace %d\n", window->desc, meta_workspace_screen_index (space)); tmp = tmp->next; } g_slist_free (spaces); } } if (info->geometry_set) { int x, y, w, h; window->placed = TRUE; /* don't do placement algorithms later */ x = info->rect.x; y = info->rect.y; w = window->size_hints.base_width + info->rect.width * window->size_hints.width_inc; h = window->size_hints.base_height + info->rect.height * window->size_hints.height_inc; /* Force old gravity, ignoring anything now set */ window->size_hints.win_gravity = info->gravity; meta_verbose ("Restoring pos %d,%d size %d x %d for %s\n", x, y, w, h, window->desc); meta_window_move_resize_internal (window, META_DO_GRAVITY_ADJUST, NorthWestGravity, x, y, w, h); } } void meta_window_free (MetaWindow *window) { GList *tmp; meta_verbose ("Unmanaging 0x%lx\n", window->xwindow); if (destroying_windows_disallowed > 0) meta_bug ("Tried to destroy window %s while destruction was not allowed\n", window->desc); window->unmanaging = TRUE; /* If we have the focus, focus some other window. * This is done first, so that if the unmap causes * an EnterNotify the EnterNotify will have final say * on what gets focused, maintaining sloppy focus * invariants. */ if (window->has_focus) { meta_topic (META_DEBUG_FOCUS, "Focusing top window since we're unmanaging %s\n", window->desc); meta_screen_focus_top_window (window->screen, window); } else { meta_topic (META_DEBUG_FOCUS, "Unmanaging window %s which doesn't currently have focus\n", window->desc); } if (window->has_struts) invalidate_work_areas (window); if (window->display->grab_window == window) meta_display_end_grab_op (window->display, meta_display_get_current_time (window->display)); if (window->display->focus_window == window) window->display->focus_window = NULL; if (window->display->prev_focus_window == window) window->display->prev_focus_window = NULL; meta_window_unqueue_calc_showing (window); meta_window_unqueue_move_resize (window); tmp = window->workspaces; while (tmp != NULL) { GList *next; next = tmp->next; /* pops front of list */ meta_workspace_remove_window (tmp->data, window); tmp = next; } g_assert (window->workspaces == NULL); meta_stack_remove (window->screen->stack, window); /* FIXME restore original size if window has maximized */ if (window->withdrawn) set_wm_state (window, WithdrawnState); if (window->frame) meta_window_destroy_frame (window); meta_window_ungrab_keys (window); meta_display_ungrab_window_buttons (window->display, window->xwindow); meta_display_ungrab_focus_window_button (window->display, window->xwindow); meta_display_unregister_x_window (window->display, window->xwindow); /* Put back anything we messed up */ meta_error_trap_push (window->display); if (window->border_width != 0) XSetWindowBorderWidth (window->display->xdisplay, window->xwindow, window->border_width); meta_error_trap_pop (window->display); if (window->icon) g_object_unref (G_OBJECT (window->icon)); if (window->mini_icon) g_object_unref (G_OBJECT (window->mini_icon)); g_free (window->sm_client_id); g_free (window->role); g_free (window->res_class); g_free (window->res_name); g_free (window->title); g_free (window->icon_name); g_free (window->desc); g_free (window); } static int set_wm_state (MetaWindow *window, int state) { unsigned long data[2]; /* twm sets the icon window as data[1], I couldn't find that in * ICCCM. */ data[0] = state; data[1] = None; meta_error_trap_push (window->display); XChangeProperty (window->display->xdisplay, window->xwindow, window->display->atom_wm_state, window->display->atom_wm_state, 32, PropModeReplace, (guchar*) data, 2); return meta_error_trap_pop (window->display); } static int set_net_wm_state (MetaWindow *window) { int i; unsigned long data[10]; gboolean skip_pager; gboolean skip_taskbar; if (window->type == META_WINDOW_DESKTOP || window->type == META_WINDOW_DOCK || window->type == META_WINDOW_TOOLBAR || window->type == META_WINDOW_MENU) skip_pager = TRUE; else skip_pager = FALSE; if (window->type == META_WINDOW_DESKTOP || window->type == META_WINDOW_DOCK || window->type == META_WINDOW_MENU) skip_taskbar = TRUE; else skip_taskbar = FALSE; i = 0; if (window->shaded) { data[i] = window->display->atom_net_wm_state_shaded; ++i; } if (window->wm_state_modal) { data[i] = window->display->atom_net_wm_state_modal; ++i; } if (window->wm_state_skip_pager || skip_pager) { data[i] = window->display->atom_net_wm_state_skip_pager; ++i; } if (window->wm_state_skip_taskbar || skip_pager) { data[i] = window->display->atom_net_wm_state_skip_taskbar; ++i; } if (window->maximized) { data[i] = window->display->atom_net_wm_state_maximized_horz; ++i; data[i] = window->display->atom_net_wm_state_maximized_vert; ++i; } meta_verbose ("Setting _NET_WM_STATE with %d atoms\n", i); meta_error_trap_push (window->display); XChangeProperty (window->display->xdisplay, window->xwindow, window->display->atom_net_wm_state, XA_ATOM, 32, PropModeReplace, (guchar*) data, i); return meta_error_trap_pop (window->display); } gboolean meta_window_visible_on_workspace (MetaWindow *window, MetaWorkspace *workspace) { return window->on_all_workspaces || meta_workspace_contains_window (workspace, window); } void meta_window_calc_showing (MetaWindow *window) { gboolean on_workspace; meta_verbose ("Calc showing for window %s\n", window->desc); on_workspace = meta_window_visible_on_workspace (window, window->screen->active_workspace); if (!on_workspace) meta_verbose ("Window %s is not on workspace %d\n", window->desc, meta_workspace_index (window->screen->active_workspace)); else meta_verbose ("Window %s is on the active workspace %d\n", window->desc, meta_workspace_index (window->screen->active_workspace)); if (window->on_all_workspaces) meta_verbose ("Window %s is on all workspaces\n", window->desc); if (on_workspace && window->display->showing_desktop && window->type != META_WINDOW_DESKTOP && window->type != META_WINDOW_DOCK) { meta_verbose ("Window %s is on current workspace, but we're showing the desktop\n", window->desc); on_workspace = FALSE; } if (window->minimized || !on_workspace) { /* Really this effects code should probably * be in meta_window_hide so the window->mapped * test isn't duplicated here. Anyhow, we animate * if we are mapped now, we are supposed to * be minimized, and we are on the current workspace. */ if (on_workspace && window->minimized && window->mapped) { MetaRectangle icon_rect, window_rect; gboolean result; /* Check if the window has an icon geometry */ result = meta_window_get_icon_geometry (window, &icon_rect); if (!result) { /* just animate into the corner somehow - maybe * not a good idea... */ icon_rect.x = window->screen->width; icon_rect.y = window->screen->height; icon_rect.width = 1; icon_rect.height = 1; } meta_window_get_outer_rect (window, &window_rect); /* Draw a nice cool animation */ meta_effects_draw_box_animation (window->screen, &window_rect, &icon_rect, META_MINIMIZE_ANIMATION_LENGTH); } meta_window_hide (window); } else { meta_window_show (window); } } static guint calc_showing_idle = 0; static GSList *calc_showing_pending = NULL; static int stackcmp (gconstpointer a, gconstpointer b) { MetaWindow *aw = (gpointer) a; MetaWindow *bw = (gpointer) b; if (aw->screen != bw->screen) return 0; /* don't care how they sort with respect to each other */ else return meta_stack_windows_cmp (aw->screen->stack, aw, bw); } static gboolean idle_calc_showing (gpointer data) { GSList *tmp; GSList *copy; meta_verbose ("Clearing the calc_showing queue\n"); /* Work with a copy, for reentrancy. The allowed reentrancy isn't * complete; destroying a window while we're in here would result in * badness. But it's OK to queue/unqueue calc_showings. */ copy = g_slist_copy (calc_showing_pending); g_slist_free (calc_showing_pending); calc_showing_pending = NULL; calc_showing_idle = 0; destroying_windows_disallowed += 1; /* sort them from bottom to top, so we map the * bottom windows first, so that placement (e.g. cascading) * works properly */ copy = g_slist_sort (copy, stackcmp); tmp = copy; while (tmp != NULL) { MetaWindow *window; window = tmp->data; meta_window_calc_showing (window); /* important to set this here for reentrancy - * if we queue a window again while it's in "copy", * then queue_calc_showing will just return since * calc_showing_queued = TRUE still */ window->calc_showing_queued = FALSE; tmp = tmp->next; } g_slist_free (copy); destroying_windows_disallowed -= 1; return FALSE; } void meta_window_unqueue_calc_showing (MetaWindow *window) { if (!window->calc_showing_queued) return; meta_verbose ("Removing %s from the calc_showing queue\n", window->desc); /* Note that window may not actually be in move_resize_pending * because it may have been in "copy" inside the idle handler */ calc_showing_pending = g_slist_remove (calc_showing_pending, window); window->calc_showing_queued = FALSE; if (calc_showing_pending == NULL && calc_showing_idle != 0) { g_source_remove (calc_showing_idle); calc_showing_idle = 0; } } void meta_window_flush_calc_showing (MetaWindow *window) { if (window->calc_showing_queued) { meta_window_unqueue_calc_showing (window); meta_window_calc_showing (window); } } void meta_window_queue_calc_showing (MetaWindow *window) { if (window->unmanaging) return; if (window->calc_showing_queued) return; meta_verbose ("Putting %s in the calc_showing queue\n", window->desc); window->calc_showing_queued = TRUE; if (calc_showing_idle == 0) calc_showing_idle = g_idle_add (idle_calc_showing, NULL); calc_showing_pending = g_slist_prepend (calc_showing_pending, window); } void meta_window_show (MetaWindow *window) { gboolean did_placement; meta_verbose ("Showing window %s, shaded: %d iconic: %d placed: %d\n", window->desc, window->shaded, window->iconic, window->placed); did_placement = FALSE; if (!window->placed) { /* We have to recalc the placement here since other windows may * have been mapped/placed since we last did constrain_position */ /* calc_placement is an efficiency hack to avoid * multiple placement calculations before we finally * show the window. */ window->calc_placement = TRUE; meta_window_move_resize_now (window); window->calc_placement = FALSE; /* don't ever do the initial position constraint thing again. * This is toggled here so that initially-iconified windows * still get placed when they are ultimately shown. */ window->placed = TRUE; did_placement = TRUE; } /* Shaded means the frame is mapped but the window is not */ if (window->frame && !window->frame->mapped) { meta_verbose ("Frame actually needs map\n"); window->frame->mapped = TRUE; meta_ui_map_frame (window->screen->ui, window->frame->xwindow); } if (window->shaded) { if (window->mapped) { meta_verbose ("%s actually needs unmap (shaded)\n", window->desc); meta_verbose ("Incrementing unmaps_pending on %s for shade\n", window->desc); window->mapped = FALSE; window->unmaps_pending += 1; meta_error_trap_push (window->display); XUnmapWindow (window->display->xdisplay, window->xwindow); meta_error_trap_pop (window->display); } if (!window->iconic) { window->iconic = TRUE; set_wm_state (window, IconicState); } } else { if (!window->mapped) { meta_verbose ("%s actually needs map\n", window->desc); window->mapped = TRUE; meta_error_trap_push (window->display); XMapWindow (window->display->xdisplay, window->xwindow); meta_error_trap_pop (window->display); } if (window->iconic) { window->iconic = FALSE; set_wm_state (window, NormalState); } } if (did_placement) { if (window->xtransient_for != None) { MetaWindow *parent; parent = meta_display_lookup_x_window (window->display, window->xtransient_for); if (parent && parent->has_focus) { meta_topic (META_DEBUG_FOCUS, "Focusing transient window '%s' since parent had focus\n", window->desc); meta_window_focus (window, meta_display_get_current_time (window->display)); } } /* Always focus new windows in click-to-focus */ if (meta_prefs_get_focus_mode () == META_FOCUS_MODE_CLICK) meta_window_focus (window, meta_display_get_current_time (window->display)); } } void meta_window_hide (MetaWindow *window) { gboolean did_hide; meta_verbose ("Hiding window %s\n", window->desc); did_hide = FALSE; if (window->frame && window->frame->mapped) { meta_verbose ("Frame actually needs unmap\n"); window->frame->mapped = FALSE; meta_ui_unmap_frame (window->screen->ui, window->frame->xwindow); did_hide = TRUE; } if (window->mapped) { meta_verbose ("%s actually needs unmap\n", window->desc); meta_verbose ("Incrementing unmaps_pending on %s for hide\n", window->desc); window->mapped = FALSE; window->unmaps_pending += 1; meta_error_trap_push (window->display); XUnmapWindow (window->display->xdisplay, window->xwindow); meta_error_trap_pop (window->display); did_hide = TRUE; } if (!window->iconic) { window->iconic = TRUE; set_wm_state (window, IconicState); } if (did_hide) { if (window->has_struts) invalidate_work_areas (window); } } void meta_window_minimize (MetaWindow *window) { if (!window->minimized) { window->minimized = TRUE; meta_window_queue_calc_showing (window); if (window->has_focus) { meta_topic (META_DEBUG_FOCUS, "Focusing top window due to minimization of focus window %s\n", window->desc); meta_screen_focus_top_window (window->screen, window); } else { meta_topic (META_DEBUG_FOCUS, "Minimizing window %s which doesn't have the focus\n", window->desc); } } } void meta_window_unminimize (MetaWindow *window) { if (window->display->showing_desktop) meta_display_unshow_desktop (window->display); if (window->minimized) { window->minimized = FALSE; meta_window_queue_calc_showing (window); } } void meta_window_maximize (MetaWindow *window) { if (!window->maximized) { meta_verbose ("Maximizing %s\n", window->desc); window->maximized = TRUE; meta_window_raise (window); /* save size/pos as appropriate args for move_resize */ window->saved_rect = window->rect; if (window->frame) { window->saved_rect.x += window->frame->rect.x; window->saved_rect.y += window->frame->rect.y; } /* move_resize with new maximization constraints */ meta_window_queue_move_resize (window); set_net_wm_state (window); } } void meta_window_unmaximize (MetaWindow *window) { if (window->maximized) { meta_verbose ("Unmaximizing %s\n", window->desc); window->maximized = FALSE; meta_window_move_resize (window, TRUE, window->saved_rect.x, window->saved_rect.y, window->saved_rect.width, window->saved_rect.height); set_net_wm_state (window); } } void meta_window_shade (MetaWindow *window) { meta_verbose ("Shading %s\n", window->desc); if (!window->shaded) { if (window->mapped) { /* Animation */ MetaRectangle starting_size; MetaRectangle titlebar_size; meta_window_get_outer_rect (window, &starting_size); if (window->frame) { starting_size.y += window->frame->child_y; starting_size.height -= window->frame->child_y; } titlebar_size = starting_size; titlebar_size.height = 0; meta_effects_draw_box_animation (window->screen, &starting_size, &titlebar_size, META_SHADE_ANIMATION_LENGTH); } window->shaded = TRUE; meta_window_queue_move_resize (window); meta_window_queue_calc_showing (window); /* After queuing the calc showing, since _focus flushes it, * and we need to focus the frame */ meta_topic (META_DEBUG_FOCUS, "Re-focusing window %s after shading it\n", window->desc); meta_window_focus (window, meta_display_get_current_time (window->display)); set_net_wm_state (window); } } void meta_window_unshade (MetaWindow *window) { meta_verbose ("Unshading %s\n", window->desc); if (window->shaded) { window->shaded = FALSE; meta_window_queue_move_resize (window); meta_window_queue_calc_showing (window); /* focus the window */ meta_topic (META_DEBUG_FOCUS, "Focusing window %s after unshading it\n", window->desc); meta_window_focus (window, meta_display_get_current_time (window->display)); set_net_wm_state (window); } } void meta_window_activate (MetaWindow *window, guint32 timestamp) { /* Get window on current workspace */ if (!meta_window_visible_on_workspace (window, window->screen->active_workspace)) meta_window_change_workspace (window, window->screen->active_workspace); if (window->minimized) meta_window_unminimize (window); meta_window_raise (window); meta_topic (META_DEBUG_FOCUS, "Focusing window %s due to activation\n", window->desc); meta_window_focus (window, timestamp); } /* returns values suitable for meta_window_move */ static void adjust_for_gravity (MetaWindow *window, MetaFrameGeometry *fgeom, gboolean coords_assume_border, int x, int y, int *xp, int *yp) { int ref_x, ref_y; int bw; int child_x, child_y; int frame_width, frame_height; if (coords_assume_border) bw = window->border_width; else bw = 0; if (fgeom) { child_x = fgeom->left_width; child_y = fgeom->top_height; frame_width = child_x + window->rect.width + fgeom->right_width; frame_height = child_y + window->rect.height + fgeom->bottom_height; } else { child_x = 0; child_y = 0; frame_width = window->rect.width; frame_height = window->rect.height; } /* We're computing position to pass to window_move, which is * the position of the client window (StaticGravity basically) * * (see WM spec description of gravity computation, but note that * their formulas assume we're honoring the border wi