window.c

Go to the documentation of this file.
00001 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
00002 
00003 /* Metacity X managed windows */
00004 
00005 /* 
00006  * Copyright (C) 2001 Havoc Pennington, Anders Carlsson
00007  * Copyright (C) 2002, 2003 Red Hat, Inc.
00008  * Copyright (C) 2003 Rob Adams
00009  * Copyright (C) 2004-2006 Elijah Newren
00010  * 
00011  * This program is free software; you can redistribute it and/or
00012  * modify it under the terms of the GNU General Public License as
00013  * published by the Free Software Foundation; either version 2 of the
00014  * License, or (at your option) any later version.
00015  *
00016  * This program is distributed in the hope that it will be useful, but
00017  * WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00019  * General Public License for more details.
00020  * 
00021  * You should have received a copy of the GNU General Public License
00022  * along with this program; if not, write to the Free Software
00023  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
00024  * 02111-1307, USA.
00025  */
00026 
00027 #include <config.h>
00028 #include "window-private.h"
00029 #include "edge-resistance.h"
00030 #include "util.h"
00031 #include "frame-private.h"
00032 #include "errors.h"
00033 #include "workspace.h"
00034 #include "stack.h"
00035 #include "keybindings.h"
00036 #include "ui.h"
00037 #include "place.h"
00038 #include "session.h"
00039 #include "effects.h"
00040 #include "prefs.h"
00041 #include "resizepopup.h"
00042 #include "xprops.h"
00043 #include "group.h"
00044 #include "window-props.h"
00045 #include "constraints.h"
00046 #include "compositor.h"
00047 #include "effects.h"
00048 
00049 #include <X11/Xatom.h>
00050 #include <string.h>
00051 
00052 #ifdef HAVE_SHAPE
00053 #include <X11/extensions/shape.h>
00054 #endif
00055 
00056 static int destroying_windows_disallowed = 0;
00057 
00058 
00059 static void     update_sm_hints           (MetaWindow     *window);
00060 static void     update_role               (MetaWindow     *window);
00061 static void     update_net_wm_type        (MetaWindow     *window);
00062 static void     update_net_frame_extents  (MetaWindow     *window);
00063 static void     recalc_window_type        (MetaWindow     *window);
00064 static void     recalc_window_features    (MetaWindow     *window);
00065 static void     invalidate_work_areas     (MetaWindow     *window);
00066 static void     recalc_window_type        (MetaWindow     *window);
00067 static void     set_wm_state              (MetaWindow     *window,
00068                                            int             state);
00069 static void     set_net_wm_state          (MetaWindow     *window);
00070 
00071 static void     send_configure_notify     (MetaWindow     *window);
00072 static gboolean process_property_notify   (MetaWindow     *window,
00073                                            XPropertyEvent *event);
00074 static void     meta_window_show          (MetaWindow     *window);
00075 static void     meta_window_hide          (MetaWindow     *window);
00076 
00077 static void     meta_window_save_rect         (MetaWindow    *window);
00078 static void     save_user_window_placement    (MetaWindow    *window);
00079 static void     force_save_user_window_placement (MetaWindow    *window);
00080 
00081 static void meta_window_move_resize_internal (MetaWindow         *window,
00082                                               MetaMoveResizeFlags flags,
00083                                               int                 resize_gravity,
00084                                               int                 root_x_nw,
00085                                               int                 root_y_nw,
00086                                               int                 w,
00087                                               int                 h);
00088 
00089 static void     ensure_mru_position_after (MetaWindow *window,
00090                                            MetaWindow *after_this_one);
00091 
00092 
00093 static void meta_window_move_resize_now (MetaWindow  *window);
00094 
00095 static void meta_window_unqueue (MetaWindow *window, guint queuebits);
00096 
00097 static void     update_move           (MetaWindow   *window,
00098                                        gboolean      snap,
00099                                        int           x,
00100                                        int           y);
00101 static gboolean update_move_timeout   (gpointer data);
00102 static void     update_resize         (MetaWindow   *window,
00103                                        gboolean      snap,
00104                                        int           x,
00105                                        int           y,
00106                                        gboolean      force);
00107 static gboolean update_resize_timeout (gpointer data);
00108 
00109 
00110 static void meta_window_flush_calc_showing   (MetaWindow *window);
00111 
00112 static gboolean queue_calc_showing_func (MetaWindow *window,
00113                                          void       *data);
00114 
00115 static void meta_window_apply_session_info (MetaWindow                  *window,
00116                                             const MetaWindowSessionInfo *info);
00117 
00118 static void unmaximize_window_before_freeing (MetaWindow        *window);
00119 static void unminimize_window_and_all_transient_parents (MetaWindow *window);
00120 
00121 /* Idle handlers for the three queues. The "data" parameter in each case
00122  * will be a GINT_TO_POINTER of the index into the queue arrays to use.
00123  *
00124  * TODO: Possibly there is still some code duplication among these, which we
00125  * need to sort out at some point.
00126  */
00127 static gboolean idle_calc_showing (gpointer data);
00128 static gboolean idle_move_resize (gpointer data);
00129 static gboolean idle_update_icon (gpointer data);
00130 
00131 #ifdef WITH_VERBOSE_MODE
00132 static const char*
00133 wm_state_to_string (int state)
00134 {
00135   switch (state)
00136     {
00137     case NormalState:
00138       return "NormalState";      
00139     case IconicState:
00140       return "IconicState";
00141     case WithdrawnState:
00142       return "WithdrawnState";
00143     }
00144 
00145   return "Unknown";
00146 }
00147 #endif
00148 
00149 static gboolean
00150 is_desktop_or_dock_foreach (MetaWindow *window,
00151                             void       *data)
00152 {
00153   gboolean *result = data;
00154 
00155   *result =
00156     window->type == META_WINDOW_DESKTOP ||
00157     window->type == META_WINDOW_DOCK;
00158   if (*result)
00159     return FALSE; /* stop as soon as we find one */
00160   else
00161     return TRUE;
00162 }
00163 
00164 /* window is the window that's newly mapped provoking
00165  * the possible change
00166  */
00167 static void
00168 maybe_leave_show_desktop_mode (MetaWindow *window)
00169 {
00170   gboolean is_desktop_or_dock;
00171 
00172   if (!window->screen->active_workspace->showing_desktop)
00173     return;
00174 
00175   /* If the window is a transient for the dock or desktop, don't
00176    * leave show desktop mode when the window opens. That's
00177    * so you can e.g. hide all windows, manipulate a file on
00178    * the desktop via a dialog, then unshow windows again.
00179    */
00180   is_desktop_or_dock = FALSE;
00181   is_desktop_or_dock_foreach (window,
00182                               &is_desktop_or_dock);
00183 
00184   meta_window_foreach_ancestor (window, is_desktop_or_dock_foreach,
00185                                 &is_desktop_or_dock);
00186 
00187   if (!is_desktop_or_dock)
00188     {
00189       meta_screen_minimize_all_on_active_workspace_except (window->screen,
00190                                                            window);
00191       meta_screen_unshow_desktop (window->screen);      
00192     }
00193 }
00194 
00195 MetaWindow*
00196 meta_window_new (MetaDisplay *display,
00197                  Window       xwindow,
00198                  gboolean     must_be_viewable)
00199 {
00200   XWindowAttributes attrs;
00201   MetaWindow *window;
00202   
00203   meta_display_grab (display);
00204   meta_error_trap_push (display); /* Push a trap over all of window
00205                                    * creation, to reduce XSync() calls
00206                                    */
00207   
00208   meta_error_trap_push_with_return (display);
00209   
00210   if (XGetWindowAttributes (display->xdisplay,xwindow, &attrs)) 
00211    {
00212       if(meta_error_trap_pop_with_return (display, TRUE) != Success)
00213        {
00214           meta_verbose ("Failed to get attributes for window 0x%lx\n",
00215                         xwindow);
00216           meta_error_trap_pop (display, TRUE);
00217           meta_display_ungrab (display);
00218           return NULL;
00219        }
00220       window = meta_window_new_with_attrs (display, xwindow,
00221                                        must_be_viewable, &attrs);
00222    }
00223   else
00224    {
00225          meta_error_trap_pop_with_return (display, TRUE); 
00226          meta_verbose ("Failed to get attributes for window 0x%lx\n",
00227                         xwindow);
00228          meta_error_trap_pop (display, TRUE);
00229          meta_display_ungrab (display);
00230          return NULL;
00231    }
00232    
00233   
00234   meta_error_trap_pop (display, FALSE);
00235   meta_display_ungrab (display);
00236 
00237   return window;
00238 }
00239 
00240 MetaWindow*
00241 meta_window_new_with_attrs (MetaDisplay       *display,
00242                             Window             xwindow,
00243                             gboolean           must_be_viewable,
00244                             XWindowAttributes *attrs)
00245 {
00246   MetaWindow *window;
00247   GSList *tmp;
00248   MetaWorkspace *space;
00249   gulong existing_wm_state;
00250   gulong event_mask;
00251   MetaMoveResizeFlags flags;
00252 #define N_INITIAL_PROPS 18
00253   Atom initial_props[N_INITIAL_PROPS];
00254   int i;
00255   gboolean has_shape;
00256 
00257   g_assert (attrs != NULL);
00258   g_assert (N_INITIAL_PROPS == (int) G_N_ELEMENTS (initial_props));
00259   
00260   meta_verbose ("Attempting to manage 0x%lx\n", xwindow);
00261 
00262   if (meta_display_xwindow_is_a_no_focus_window (display, xwindow))
00263     {
00264       meta_verbose ("Not managing no_focus_window 0x%lx\n",
00265                     xwindow);
00266       return NULL;
00267     }
00268 
00269   if (attrs->override_redirect)
00270     {
00271       meta_verbose ("Deciding not to manage override_redirect window 0x%lx\n", xwindow);
00272       return NULL;
00273     }
00274   
00275   /* Grab server */
00276   meta_display_grab (display);
00277   meta_error_trap_push (display); /* Push a trap over all of window
00278                                    * creation, to reduce XSync() calls
00279                                    */
00280 
00281   meta_verbose ("must_be_viewable = %d attrs->map_state = %d (%s)\n",
00282                 must_be_viewable,
00283                 attrs->map_state,
00284                 (attrs->map_state == IsUnmapped) ?
00285                 "IsUnmapped" :
00286                 (attrs->map_state == IsViewable) ?
00287                 "IsViewable" :
00288                 (attrs->map_state == IsUnviewable) ?
00289                 "IsUnviewable" :
00290                 "(unknown)");
00291   
00292   existing_wm_state = WithdrawnState;
00293   if (must_be_viewable && attrs->map_state != IsViewable)
00294     {
00295       /* Only manage if WM_STATE is IconicState or NormalState */
00296       gulong state;
00297 
00298       /* WM_STATE isn't a cardinal, it's type WM_STATE, but is an int */
00299       if (!(meta_prop_get_cardinal_with_atom_type (display, xwindow,
00300                                                    display->atom_WM_STATE,
00301                                                    display->atom_WM_STATE,
00302                                                    &state) &&
00303             (state == IconicState || state == NormalState)))
00304         {
00305           meta_verbose ("Deciding not to manage unmapped or unviewable window 0x%lx\n", xwindow);
00306           meta_error_trap_pop (display, TRUE);
00307           meta_display_ungrab (display);
00308           return NULL;
00309         }
00310 
00311       existing_wm_state = state;
00312       meta_verbose ("WM_STATE of %lx = %s\n", xwindow,
00313                     wm_state_to_string (existing_wm_state));
00314     }
00315   
00316   meta_error_trap_push_with_return (display);
00317   
00318   XAddToSaveSet (display->xdisplay, xwindow);
00319 
00320   event_mask =
00321     PropertyChangeMask | EnterWindowMask | LeaveWindowMask |
00322     FocusChangeMask | ColormapChangeMask;
00323 
00324   XSelectInput (display->xdisplay, xwindow, event_mask);
00325 
00326   has_shape = FALSE;
00327 #ifdef HAVE_SHAPE
00328   if (META_DISPLAY_HAS_SHAPE (display))
00329     {
00330       int x_bounding, y_bounding, x_clip, y_clip;
00331       unsigned w_bounding, h_bounding, w_clip, h_clip;
00332       int bounding_shaped, clip_shaped;
00333 
00334       XShapeSelectInput (display->xdisplay, xwindow, ShapeNotifyMask);
00335 
00336       XShapeQueryExtents (display->xdisplay, xwindow,
00337                           &bounding_shaped, &x_bounding, &y_bounding,
00338                           &w_bounding, &h_bounding,
00339                           &clip_shaped, &x_clip, &y_clip,
00340                           &w_clip, &h_clip);
00341 
00342       has_shape = bounding_shaped != FALSE;
00343 
00344       meta_topic (META_DEBUG_SHAPES,
00345                   "Window has_shape = %d extents %d,%d %u x %u\n",
00346                   has_shape, x_bounding, y_bounding,
00347                   w_bounding, h_bounding);
00348     }
00349 #endif
00350 
00351   /* Get rid of any borders */
00352   if (attrs->border_width != 0)
00353     XSetWindowBorderWidth (display->xdisplay, xwindow, 0);
00354 
00355   /* Get rid of weird gravities */
00356   if (attrs->win_gravity != NorthWestGravity)
00357     {
00358       XSetWindowAttributes set_attrs;
00359       
00360       set_attrs.win_gravity = NorthWestGravity;
00361       
00362       XChangeWindowAttributes (display->xdisplay,
00363                                xwindow,
00364                                CWWinGravity,
00365                                &set_attrs);
00366     }
00367   
00368   if (meta_error_trap_pop_with_return (display, FALSE) != Success)
00369     {
00370       meta_verbose ("Window 0x%lx disappeared just as we tried to manage it\n",
00371                     xwindow);
00372       meta_error_trap_pop (display, FALSE);
00373       meta_display_ungrab (display);
00374       return NULL;
00375     }
00376 
00377   g_assert (!attrs->override_redirect);
00378   
00379   window = g_new (MetaWindow, 1);
00380 
00381   window->constructing = TRUE;
00382   
00383   window->dialog_pid = -1;
00384   window->dialog_pipe = -1;
00385   
00386   window->xwindow = xwindow;
00387   
00388   /* this is in window->screen->display, but that's too annoying to
00389    * type
00390    */
00391   window->display = display;
00392   window->workspace = NULL;
00393 
00394 #ifdef HAVE_XSYNC
00395   window->sync_request_counter = None;
00396   window->sync_request_serial = 0;
00397   window->sync_request_time.tv_sec = 0;
00398   window->sync_request_time.tv_usec = 0;
00399 #endif
00400   
00401   window->screen = NULL;
00402   tmp = display->screens;
00403   while (tmp != NULL)
00404     {
00405       MetaScreen *scr = tmp->data;
00406 
00407       if (scr->xroot == attrs->root)
00408         {
00409           window->screen = tmp->data;
00410           break;
00411         }
00412       
00413       tmp = tmp->next;
00414     }
00415   
00416   g_assert (window->screen);
00417 
00418   window->desc = g_strdup_printf ("0x%lx", window->xwindow);
00419 
00420   /* avoid tons of stack updates */
00421   meta_stack_freeze (window->screen->stack);
00422 
00423   window->has_shape = has_shape;
00424   
00425   window->rect.x = attrs->x;
00426   window->rect.y = attrs->y;
00427   window->rect.width = attrs->width;
00428   window->rect.height = attrs->height;
00429 
00430   /* And border width, size_hints are the "request" */
00431   window->border_width = attrs->border_width;
00432   window->size_hints.x = attrs->x;
00433   window->size_hints.y = attrs->y;
00434   window->size_hints.width = attrs->width;
00435   window->size_hints.height = attrs->height;
00436   /* initialize the remaining size_hints as if size_hints.flags were zero */
00437   meta_set_normal_hints (window, NULL);
00438 
00439   /* And this is our unmaximized size */
00440   window->saved_rect = window->rect;
00441   window->user_rect = window->rect;
00442   
00443   window->depth = attrs->depth;
00444   window->xvisual = attrs->visual;
00445   window->colormap = attrs->colormap;
00446   
00447   window->title = NULL;
00448   window->icon_name = NULL;
00449   window->icon = NULL;
00450   window->mini_icon = NULL;
00451   meta_icon_cache_init (&window->icon_cache);
00452   window->wm_hints_pixmap = None;
00453   window->wm_hints_mask = None;
00454 
00455   window->frame = NULL;
00456   window->has_focus = FALSE;
00457 
00458   window->maximized_horizontally = FALSE;
00459   window->maximized_vertically = FALSE;
00460   window->maximize_horizontally_after_placement = FALSE;
00461   window->maximize_vertically_after_placement = FALSE;
00462   window->minimize_after_placement = FALSE;
00463   window->fullscreen = FALSE;
00464   window->require_fully_onscreen = TRUE;
00465   window->require_on_single_xinerama = TRUE;
00466   window->require_titlebar_visible = TRUE;
00467   window->on_all_workspaces = FALSE;
00468   window->shaded = FALSE;
00469   window->initially_iconic = FALSE;
00470   window->minimized = FALSE;
00471   window->was_minimized = FALSE;
00472   window->tab_unminimized = FALSE;
00473   window->iconic = FALSE;
00474   window->mapped = attrs->map_state != IsUnmapped;
00475   /* if already mapped, no need to worry about focus-on-first-time-showing */
00476   window->showing_for_first_time = !window->mapped;
00477   /* if already mapped we don't want to do the placement thing */
00478   window->placed = window->mapped;
00479   if (window->placed)
00480     meta_topic (META_DEBUG_PLACEMENT,
00481                 "Not placing window 0x%lx since it's already mapped\n",
00482                 xwindow);
00483   window->denied_focus_and_not_transient = FALSE;
00484   window->unmanaging = FALSE;
00485   window->is_in_queues = 0;
00486   window->keys_grabbed = FALSE;
00487   window->grab_on_frame = FALSE;
00488   window->all_keys_grabbed = FALSE;
00489   window->withdrawn = FALSE;
00490   window->initial_workspace_set = FALSE;
00491   window->initial_timestamp_set = FALSE;
00492   window->net_wm_user_time_set = FALSE;
00493   window->user_time_window = None;
00494   window->calc_placement = FALSE;
00495   window->shaken_loose = FALSE;
00496   window->have_focus_click_grab = FALSE;
00497   window->disable_sync = FALSE;
00498   
00499   window->unmaps_pending = 0;
00500   
00501   window->mwm_decorated = TRUE;
00502   window->mwm_border_only = FALSE;
00503   window->mwm_has_close_func = TRUE;
00504   window->mwm_has_minimize_func = TRUE;
00505   window->mwm_has_maximize_func = TRUE;
00506   window->mwm_has_move_func = TRUE;
00507   window->mwm_has_resize_func = TRUE;
00508   
00509   window->decorated = TRUE;
00510   window->has_close_func = TRUE;
00511   window->has_minimize_func = TRUE;
00512   window->has_maximize_func = TRUE;
00513   window->has_move_func = TRUE;
00514   window->has_resize_func = TRUE;
00515 
00516   window->has_shade_func = TRUE;
00517 
00518   window->has_fullscreen_func = TRUE;
00519 
00520   window->always_sticky = FALSE;
00521   
00522   window->wm_state_modal = FALSE;
00523   window->skip_taskbar = FALSE;
00524   window->skip_pager = FALSE;
00525   window->wm_state_skip_taskbar = FALSE;
00526   window->wm_state_skip_pager = FALSE;
00527   window->wm_state_above = FALSE;
00528   window->wm_state_below = FALSE;
00529   window->wm_state_demands_attention = FALSE;
00530   
00531   window->res_class = NULL;
00532   window->res_name = NULL;
00533   window->role = NULL;
00534   window->sm_client_id = NULL;
00535   window->wm_client_machine = NULL;
00536   window->startup_id = NULL;
00537   
00538   window->net_wm_pid = -1;
00539   
00540   window->xtransient_for = None;
00541   window->xclient_leader = None;
00542   window->transient_parent_is_root_window = FALSE;
00543   
00544   window->type = META_WINDOW_NORMAL;
00545   window->type_atom = None;
00546 
00547   window->struts = NULL;
00548 
00549   window->using_net_wm_name              = FALSE;
00550   window->using_net_wm_visible_name      = FALSE;
00551   window->using_net_wm_icon_name         = FALSE;
00552   window->using_net_wm_visible_icon_name = FALSE;
00553 
00554   window->need_reread_icon = TRUE;
00555   
00556   window->layer = META_LAYER_LAST; /* invalid value */
00557   window->stack_position = -1;
00558   window->initial_workspace = 0; /* not used */
00559   window->initial_timestamp = 0; /* not used */
00560   
00561   meta_display_register_x_window (display, &window->xwindow, window);
00562 
00563 
00564   /* assign the window to its group, or create a new group if needed
00565    */
00566   window->group = NULL;
00567   window->xgroup_leader = None;
00568   meta_window_compute_group (window);
00569   
00570   /* Fill these in the order we want them to be gotten.  we want to
00571    * get window name and class first so we can use them in error
00572    * messages and such.  However, name is modified depending on
00573    * wm_client_machine, so push it slightly sooner.
00574    */
00575   i = 0;
00576   initial_props[i++] = display->atom_WM_CLIENT_MACHINE;
00577   initial_props[i++] = display->atom__NET_WM_NAME;
00578   initial_props[i++] = XA_WM_CLASS;
00579   initial_props[i++] = display->atom__NET_WM_PID;
00580   initial_props[i++] = XA_WM_NAME;
00581   initial_props[i++] = display->atom__NET_WM_ICON_NAME;
00582   initial_props[i++] = XA_WM_ICON_NAME;
00583   initial_props[i++] = display->atom__NET_WM_DESKTOP;
00584   initial_props[i++] = display->atom__NET_STARTUP_ID;
00585   initial_props[i++] = display->atom__NET_WM_SYNC_REQUEST_COUNTER;
00586   initial_props[i++] = XA_WM_NORMAL_HINTS;
00587   initial_props[i++] = display->atom_WM_PROTOCOLS;
00588   initial_props[i++] = XA_WM_HINTS;
00589   initial_props[i++] = display->atom__NET_WM_USER_TIME;
00590   initial_props[i++] = display->atom__NET_WM_STATE;
00591   initial_props[i++] = display->atom__MOTIF_WM_HINTS;
00592   initial_props[i++] = XA_WM_TRANSIENT_FOR;
00593   initial_props[i++] = display->atom__NET_WM_USER_TIME_WINDOW;
00594   g_assert (N_INITIAL_PROPS == i);
00595   
00596   meta_window_reload_properties (window, initial_props, N_INITIAL_PROPS);
00597   
00598   update_sm_hints (window); /* must come after transient_for */
00599   update_role (window);
00600   update_net_wm_type (window);
00601   meta_window_update_icon_now (window);
00602 
00603   if (window->initially_iconic)
00604     {
00605       /* WM_HINTS said minimized */
00606       window->minimized = TRUE;
00607       meta_verbose ("Window %s asked to start out minimized\n", window->desc);
00608     }
00609 
00610   if (existing_wm_state == IconicState)
00611     {
00612       /* WM_STATE said minimized */
00613       window->minimized = TRUE;
00614       meta_verbose ("Window %s had preexisting WM_STATE = IconicState, minimizing\n",
00615                     window->desc);
00616 
00617       /* Assume window was previously placed, though perhaps it's
00618        * been iconic its whole life, we have no way of knowing.
00619        */
00620       window->placed = TRUE;
00621     }
00622 
00623   /* Apply any window attributes such as initial workspace
00624    * based on startup notification
00625    */
00626   meta_screen_apply_startup_properties (window->screen, window);
00627 
00628   /* Try to get a "launch timestamp" for the window.  If the window is
00629    * a transient, we'd like to be able to get a last-usage timestamp
00630    * from the parent window.  If the window has no parent, there isn't
00631    * much we can do...except record the current time so that any children
00632    * can use this time as a fallback.
00633    */
00634   if (!window->net_wm_user_time_set) {
00635     MetaWindow *parent = NULL;
00636     if (window->xtransient_for)
00637       parent = meta_display_lookup_x_window (window->display,
00638                                              window->xtransient_for);
00639 
00640     /* First, maybe the app was launched with startup notification using an
00641      * obsolete version of the spec; use that timestamp if it exists.
00642      */
00643     if (window->initial_timestamp_set)
00644       /* NOTE: Do NOT toggle net_wm_user_time_set to true; this is just
00645        * being recorded as a fallback for potential transients
00646        */
00647       window->net_wm_user_time = window->initial_timestamp;
00648     else if (parent != NULL)
00649       meta_window_set_user_time(window, parent->net_wm_user_time);
00650     else
00651       /* NOTE: Do NOT toggle net_wm_user_time_set to true; this is just
00652        * being recorded as a fallback for potential transients
00653        */
00654       window->net_wm_user_time =
00655         meta_display_get_current_time_roundtrip (window->display);
00656   }
00657   
00658   if (window->decorated)
00659     meta_window_ensure_frame (window);
00660 
00661   meta_window_grab_keys (window);
00662   meta_display_grab_window_buttons (window->display, window->xwindow);
00663   meta_display_grab_focus_window_button (window->display, window);
00664 
00665   if (window->type == META_WINDOW_DESKTOP ||
00666       window->type == META_WINDOW_DOCK)
00667     {
00668       /* Change the default, but don't enforce this if the user
00669        * focuses the dock/desktop and unsticks it using key shortcuts.
00670        * Need to set this before adding to the workspaces so the MRU
00671        * lists will be updated.
00672        */
00673       window->on_all_workspaces = TRUE;
00674     }
00675   
00676   /* For the workspace, first honor hints,
00677    * if that fails put transients with parents,
00678    * otherwise put window on active space
00679    */
00680   
00681   if (window->initial_workspace_set)
00682     {
00683       if (window->initial_workspace == (int) 0xFFFFFFFF)
00684         {
00685           meta_topic (META_DEBUG_PLACEMENT,
00686                       "Window %s is initially on all spaces\n",
00687                       window->desc);
00688           
00689           /* need to set on_all_workspaces first so that it will be
00690            * added to all the MRU lists
00691            */
00692           window->on_all_workspaces = TRUE;
00693           meta_workspace_add_window (window->screen->active_workspace, window);
00694         }
00695       else
00696         {
00697           meta_topic (META_DEBUG_PLACEMENT,
00698                       "Window %s is initially on space %d\n",
00699                       window->desc, window->initial_workspace);
00700 
00701           space =
00702             meta_screen_get_workspace_by_index (window->screen,
00703                                                 window->initial_workspace);
00704           
00705           if (space)
00706             meta_workspace_add_window (space, window);
00707         }
00708     }
00709   
00710   if (window->workspace == NULL && 
00711       window->xtransient_for != None)
00712     {
00713       /* Try putting dialog on parent's workspace */
00714       MetaWindow *parent;
00715 
00716       parent = meta_display_lookup_x_window (window->display,
00717                                              window->xtransient_for);
00718 
00719       if (parent && parent->workspace)
00720         {
00721           meta_topic (META_DEBUG_PLACEMENT,
00722                       "Putting window %s on same workspace as parent %s\n",
00723                       window->desc, parent->desc);
00724           
00725           if (parent->on_all_workspaces)
00726             window->on_all_workspaces = TRUE;
00727           
00728           /* this will implicitly add to the appropriate MRU lists
00729            */
00730           meta_workspace_add_window (parent->workspace, window);
00731         }
00732     }
00733   
00734   if (window->workspace == NULL)
00735     {
00736       meta_topic (META_DEBUG_PLACEMENT,
00737                   "Putting window %s on active workspace\n",
00738                   window->desc);
00739       
00740       space = window->screen->active_workspace;
00741 
00742       meta_workspace_add_window (space, window);
00743     }
00744   
00745   /* for the various on_all_workspaces = TRUE possible above */
00746   meta_window_set_current_workspace_hint (window);
00747   
00748   meta_window_update_struts (window);
00749 
00750   /* Must add window to stack before doing move/resize, since the
00751    * window might have fullscreen size (i.e. should have been
00752    * fullscreen'd; acrobat is one such braindead case; it withdraws
00753    * and remaps its window whenever trying to become fullscreen...)
00754    * and thus constraints may try to auto-fullscreen it which also
00755    * means restacking it.
00756    */
00757   meta_stack_add (window->screen->stack, 
00758                   window);
00759 
00760   /* Put our state back where it should be,
00761    * passing TRUE for is_configure_request, ICCCM says
00762    * initial map is handled same as configure request
00763    */
00764   flags =
00765     META_IS_CONFIGURE_REQUEST | META_IS_MOVE_ACTION | META_IS_RESIZE_ACTION;
00766   meta_window_move_resize_internal (window,
00767                                     flags,
00768                                     window->size_hints.win_gravity,
00769                                     window->size_hints.x,
00770                                     window->size_hints.y,
00771                                     window->size_hints.width,
00772                                     window->size_hints.height);
00773 
00774   /* Now try applying saved stuff from the session */
00775   {
00776     const MetaWindowSessionInfo *info;
00777 
00778     info = meta_window_lookup_saved_state (window);
00779 
00780     if (info)
00781       {
00782         meta_window_apply_session_info (window, info);
00783         meta_window_release_saved_state (info);
00784       }
00785   }
00786   
00787   /* FIXME we have a tendency to set this then immediately
00788    * change it again.
00789    */
00790   set_wm_state (window, window->iconic ? IconicState : NormalState);
00791   set_net_wm_state (window);
00792 
00793   /* Sync stack changes */
00794   meta_stack_thaw (window->screen->stack);
00795 
00796   /* disable show desktop mode unless we're a desktop component */
00797   maybe_leave_show_desktop_mode (window);
00798   
00799   meta_window_queue (window, META_QUEUE_CALC_SHOWING);
00800   /* See bug 303284; a transient of the given window can already exist, in which
00801    * case we think it should probably be shown.
00802    */
00803   meta_window_foreach_transient (window,
00804                                  queue_calc_showing_func,
00805                                  NULL);
00806   /* See bug 334899; the window may have minimized ancestors
00807    * which need to be shown.
00808    *
00809    * However, we shouldn't unminimize windows here when opening
00810    * a new display because that breaks passing _NET_WM_STATE_HIDDEN
00811    * between window managers when replacing them; see bug 358042.
00812    *
00813    * And we shouldn't unminimize windows if they were initially
00814    * iconic.
00815    */
00816   if (!display->display_opening && !window->initially_iconic)
00817     unminimize_window_and_all_transient_parents (window);
00818 
00819   meta_error_trap_pop (display, FALSE); /* pop the XSync()-reducing trap */
00820   meta_display_ungrab (display);
00821  
00822   window->constructing = FALSE;
00823 
00824   return window;
00825 }
00826 
00827 /* This function should only be called from the end of meta_window_new_with_attrs () */
00828 static void
00829 meta_window_apply_session_info (MetaWindow *window,
00830                                 const MetaWindowSessionInfo *info)
00831 {
00832   if (info->stack_position_set)
00833     {
00834       meta_topic (META_DEBUG_SM,
00835                   "Restoring stack position %d for window %s\n",
00836                   info->stack_position, window->desc);
00837 
00838       /* FIXME well, I'm not sure how to do this. */
00839     }
00840 
00841   if (info->minimized_set)
00842     {
00843       meta_topic (META_DEBUG_SM,
00844                   "Restoring minimized state %d for window %s\n",
00845                   info->minimized, window->desc);
00846 
00847       if (window->has_minimize_func && info->minimized)
00848         meta_window_minimize (window);
00849     }
00850 
00851   if (info->maximized_set)
00852     {
00853       meta_topic (META_DEBUG_SM,
00854                   "Restoring maximized state %d for window %s\n",
00855                   info->maximized, window->desc);
00856       
00857       if (window->has_maximize_func && info->maximized)
00858         {
00859           meta_window_maximize (window, 
00860                                 META_MAXIMIZE_HORIZONTAL |
00861                                 META_MAXIMIZE_VERTICAL);
00862 
00863           if (info->saved_rect_set)
00864             {
00865               meta_topic (META_DEBUG_SM,
00866                           "Restoring saved rect %d,%d %dx%d for window %s\n",
00867                           info->saved_rect.x,
00868                           info->saved_rect.y,
00869                           info->saved_rect.width,
00870                           info->saved_rect.height,
00871                           window->desc);
00872               
00873               window->saved_rect.x = info->saved_rect.x;
00874               window->saved_rect.y = info->saved_rect.y;
00875               window->saved_rect.width = info->saved_rect.width;
00876               window->saved_rect.height = info->saved_rect.height;
00877             }
00878         }
00879     }
00880   
00881   if (info->on_all_workspaces_set)
00882     {
00883       window->on_all_workspaces = info->on_all_workspaces;
00884       meta_topic (META_DEBUG_SM,
00885                   "Restoring sticky state %d for window %s\n",
00886                   window->on_all_workspaces, window->desc);
00887     }
00888   
00889   if (info->workspace_indices)
00890     {
00891       GSList *tmp;
00892       GSList *spaces;      
00893 
00894       spaces = NULL;
00895       
00896       tmp = info->workspace_indices;
00897       while (tmp != NULL)
00898         {
00899           MetaWorkspace *space;          
00900 
00901           space =
00902             meta_screen_get_workspace_by_index (window->screen,
00903                                                 GPOINTER_TO_INT (tmp->data));
00904           
00905           if (space)
00906             spaces = g_slist_prepend (spaces, space);
00907           
00908           tmp = tmp->next;
00909         }
00910 
00911       if (spaces)
00912         {
00913           /* This briefly breaks the invariant that we are supposed
00914            * to always be on some workspace. But we paranoically
00915            * ensured that one of the workspaces from the session was
00916            * indeed valid, so we know we'll go right back to one.
00917            */
00918           if (window->workspace)
00919             meta_workspace_remove_window (window->workspace, window);
00920 
00921           /* Only restore to the first workspace if the window
00922            * happened to be on more than one, since we have replaces
00923            * window->workspaces with window->workspace
00924            */
00925           meta_workspace_add_window (spaces->data, window);              
00926 
00927           meta_topic (META_DEBUG_SM,
00928                       "Restoring saved window %s to workspace %d\n",
00929                       window->desc,
00930                       meta_workspace_index (spaces->data));
00931               
00932           g_slist_free (spaces);
00933         }
00934     }
00935 
00936   if (info->geometry_set)
00937     {
00938       int x, y, w, h;
00939       MetaMoveResizeFlags flags;
00940       
00941       window->placed = TRUE; /* don't do placement algorithms later */
00942 
00943       x = info->rect.x;
00944       y = info->rect.y;
00945 
00946       w = window->size_hints.base_width +
00947         info->rect.width * window->size_hints.width_inc;
00948       h = window->size_hints.base_height +
00949         info->rect.height * window->size_hints.height_inc;
00950 
00951       /* Force old gravity, ignoring anything now set */
00952       window->size_hints.win_gravity = info->gravity;
00953       
00954       meta_topic (META_DEBUG_SM,
00955                   "Restoring pos %d,%d size %d x %d for %s\n",
00956                   x, y, w, h, window->desc);
00957       
00958       flags = META_DO_GRAVITY_ADJUST | META_IS_MOVE_ACTION | META_IS_RESIZE_ACTION;
00959       meta_window_move_resize_internal (window,
00960                                         flags,
00961                                         window->size_hints.win_gravity,
00962                                         x, y, w, h);
00963     }
00964 }
00965 
00966 void
00967 meta_window_free (MetaWindow  *window,
00968                   guint32      timestamp)
00969 {
00970   GList *tmp;
00971   
00972   meta_verbose ("Unmanaging 0x%lx\n", window->xwindow);
00973 
00974   if (window->display->compositor)
00975     meta_compositor_free_window (window->display->compositor, window);
00976   
00977   if (window->display->window_with_menu == window)
00978     {
00979       meta_ui_window_menu_free (window->display->window_menu);
00980       window->display->window_menu = NULL;
00981       window->display->window_with_menu = NULL;
00982     }
00983   
00984   if (destroying_windows_disallowed > 0)
00985     meta_bug ("Tried to destroy window %s while destruction was not allowed\n",
00986               window->desc);
00987   
00988   window->unmanaging = TRUE;
00989 
00990   if (window->fullscreen)
00991     {
00992       MetaGroup *group;
00993 
00994       /* If the window is fullscreen, it may be forcing
00995        * other windows in its group to a higher layer
00996        */
00997 
00998       meta_stack_freeze (window->screen->stack);
00999       group = meta_window_get_group (window);
01000       if (group)
01001         meta_group_update_layers (group);
01002       meta_stack_thaw (window->screen->stack);
01003     }
01004 
01005   meta_window_shutdown_group (window); /* safe to do this early as
01006                                         * group.c won't re-add to the
01007                                         * group if window->unmanaging
01008                                         */
01009   
01010   /* If we have the focus, focus some other window.
01011    * This is done first, so that if the unmap causes
01012    * an EnterNotify the EnterNotify will have final say
01013    * on what gets focused, maintaining sloppy focus
01014    * invariants.
01015    */
01016   if (window->has_focus)
01017     {
01018       meta_topic (META_DEBUG_FOCUS,
01019                   "Focusing default window since we're unmanaging %s\n",
01020                   window->desc);
01021       meta_workspace_focus_default_window (window->screen->active_workspace,
01022                                            window,
01023                                            timestamp);
01024     }
01025   else if (window->display->expected_focus_window == window)
01026     {
01027       meta_topic (META_DEBUG_FOCUS,
01028                   "Focusing default window since expected focus window freed %s\n",
01029                   window->desc);
01030       window->display->expected_focus_window = NULL;
01031       meta_workspace_focus_default_window (window->screen->active_workspace,
01032                                            window,
01033                                            timestamp);
01034     }
01035   else
01036     {
01037       meta_topic (META_DEBUG_FOCUS,
01038                   "Unmanaging window %s which doesn't currently have focus\n",
01039                   window->desc);
01040     }
01041 
01042   if (window->struts)
01043     {
01044       meta_free_gslist_and_elements (window->struts);
01045       window->struts = NULL;
01046 
01047       meta_topic (META_DEBUG_WORKAREA,
01048                   "Unmanaging window %s which has struts, so invalidating work areas\n",
01049                   window->desc);
01050       invalidate_work_areas (window);
01051     }
01052   
01053   if (window->display->grab_window == window)
01054     meta_display_end_grab_op (window->display, timestamp);
01055 
01056   g_assert (window->display->grab_window != window);
01057   
01058   if (window->display->focus_window == window)
01059     {
01060       window->display->focus_window = NULL;
01061       meta_compositor_set_active_window (window->display->compositor,
01062                                          window->screen, NULL);
01063     }
01064 
01065   if (window->maximized_horizontally || window->maximized_vertically)
01066     unmaximize_window_before_freeing (window);
01067 
01068   /* The XReparentWindow call in meta_window_destroy_frame() moves the
01069    * window so we need to send a configure notify; see bug 399552.  (We
01070    * also do this just in case a window got unmaximized.)
01071    */
01072   send_configure_notify (window);
01073   
01074   meta_window_unqueue (window, META_QUEUE_CALC_SHOWING |
01075                                META_QUEUE_MOVE_RESIZE |
01076                                META_QUEUE_UPDATE_ICON);
01077   meta_window_free_delete_dialog (window);
01078   
01079   if (window->workspace)
01080     meta_workspace_remove_window (window->workspace, window);
01081 
01082   g_assert (window->workspace == NULL);
01083 
01084 #ifndef G_DISABLE_CHECKS
01085   tmp = window->screen->workspaces;
01086   while (tmp != NULL)
01087     {
01088       MetaWorkspace *workspace = tmp->data;
01089 
01090       g_assert (g_list_find (workspace->windows, window) == NULL);
01091       g_assert (g_list_find (workspace->mru_list, window) == NULL);
01092 
01093       tmp = tmp->next;
01094     }
01095 #endif
01096   
01097   meta_stack_remove (window->screen->stack, window);
01098   
01099   if (window->frame)
01100     meta_window_destroy_frame (window);
01101   
01102   if (window->withdrawn)
01103     {
01104       /* We need to clean off the window's state so it
01105        * won't be restored if the app maps it again.
01106        */
01107       meta_error_trap_push (window->display);
01108       meta_verbose ("Cleaning state from window %s\n", window->desc);
01109       XDeleteProperty (window->display->xdisplay,
01110                        window->xwindow,
01111                        window->display->atom__NET_WM_DESKTOP);
01112       XDeleteProperty (window->display->xdisplay,
01113                        window->xwindow,
01114                        window->display->atom__NET_WM_STATE);
01115       set_wm_state (window, WithdrawnState);
01116       meta_error_trap_pop (window->display, FALSE);
01117     }
01118   else
01119     {
01120       /* We need to put WM_STATE so that others will understand it on
01121        * restart.
01122        */
01123       if (!window->minimized)
01124         {
01125           meta_error_trap_push (window->display);
01126           set_wm_state (window, NormalState);
01127           meta_error_trap_pop (window->display, FALSE);
01128         }
01129 
01130       /* And we need to be sure the window is mapped so other WMs
01131        * know that it isn't Withdrawn
01132        */
01133       meta_error_trap_push (window->display);
01134       XMapWindow (window->display->xdisplay,
01135                   window->xwindow);
01136       meta_error_trap_pop (window->display, FALSE);
01137     }
01138 
01139   meta_window_ungrab_keys (window);
01140   meta_display_ungrab_window_buttons (window->display, window->xwindow);
01141   meta_display_ungrab_focus_window_button (window->display, window);
01142   
01143   meta_display_unregister_x_window (window->display, window->xwindow);
01144   
01145 
01146   meta_error_trap_push (window->display);
01147 
01148   /* Put back anything we messed up */
01149   if (window->border_width != 0)
01150     XSetWindowBorderWidth (window->display->xdisplay,
01151                            window->xwindow,
01152                            window->border_width);
01153 
01154   /* No save set */
01155   XRemoveFromSaveSet (window->display->xdisplay,
01156                       window->xwindow);
01157 
01158   /* Don't get events on not-managed windows */
01159   XSelectInput (window->display->xdisplay,
01160                 window->xwindow,
01161                 NoEventMask);
01162 
01163   /* Stop getting events for the window's _NET_WM_USER_TIME_WINDOW too */
01164   if (window->user_time_window != None)
01165     {
01166       meta_display_unregister_x_window (window->display,
01167                                         window->user_time_window);
01168       XSelectInput (window->display->xdisplay,
01169                     window->user_time_window,
01170                     NoEventMask);
01171       window->user_time_window = None;
01172     }
01173 
01174 #ifdef HAVE_SHAPE
01175   if (META_DISPLAY_HAS_SHAPE (window->display))
01176     XShapeSelectInput (window->display->xdisplay, window->xwindow, NoEventMask);
01177 #endif
01178   
01179   meta_error_trap_pop (window->display, FALSE);
01180 
01181   if (window->icon)
01182     g_object_unref (G_OBJECT (window->icon));
01183 
01184   if (window->mini_icon)
01185     g_object_unref (G_OBJECT (window->mini_icon));
01186 
01187   meta_icon_cache_free (&window->icon_cache);
01188   
01189   g_free (window->sm_client_id);
01190   g_free (window->wm_client_machine);
01191   g_free (window->startup_id);
01192   g_free (window->role);
01193   g_free (window->res_class);
01194   g_free (window->res_name);
01195   g_free (window->title);
01196   g_free (window->icon_name);
01197   g_free (window->desc);
01198   g_free (window);
01199 }
01200 
01201 static void
01202 set_wm_state (MetaWindow *window,
01203               int         state)
01204 {
01205   unsigned long data[2];
01206   
01207   meta_verbose ("Setting wm state %s on %s\n",
01208                 wm_state_to_string (state), window->desc);
01209   
01210   /* Metacity doesn't use icon windows, so data[1] should be None
01211    * according to the ICCCM 2.0 Section 4.1.3.1.
01212    */
01213   data[0] = state;
01214   data[1] = None;
01215 
01216   meta_error_trap_push (window->display);
01217   XChangeProperty (window->display->xdisplay, window->xwindow,
01218                    window->display->atom_WM_STATE,
01219                    window->display->atom_WM_STATE,
01220                    32, PropModeReplace, (guchar*) data, 2);
01221   meta_error_trap_pop (window->display, FALSE);
01222 }
01223 
01224 static void
01225 set_net_wm_state (MetaWindow *window)
01226 {
01227   int i;
01228   unsigned long data[11];
01229   
01230   i = 0;
01231   if (window->shaded)
01232     {
01233       data[i] = window->display->atom__NET_WM_STATE_SHADED;
01234       ++i;
01235     }
01236   if (window->wm_state_modal)
01237     {
01238       data[i] = window->display->atom__NET_WM_STATE_MODAL;
01239       ++i;
01240     }
01241   if (window->skip_pager)
01242     {
01243       data[i] = window->display->atom__NET_WM_STATE_SKIP_PAGER;
01244       ++i;
01245     }
01246   if (window->skip_taskbar)
01247     {
01248       data[i] = window->display->atom__NET_WM_STATE_SKIP_TASKBAR;
01249       ++i;
01250     }
01251   if (window->maximized_horizontally)
01252     {
01253       data[i] = window->display->atom__NET_WM_STATE_MAXIMIZED_HORZ;
01254       ++i;
01255     }
01256   if (window->maximized_vertically)
01257     {
01258       data[i] = window->display->atom__NET_WM_STATE_MAXIMIZED_VERT;
01259       ++i;
01260     }
01261   if (window->fullscreen)
01262     {
01263       data[i] = window->display->atom__NET_WM_STATE_FULLSCREEN;
01264       ++i;
01265     }
01266   if (!meta_window_showing_on_its_workspace (window) || window->shaded)
01267     {
01268       data[i] = window->display->atom__NET_WM_STATE_HIDDEN;
01269       ++i;
01270     }
01271   if (window->wm_state_above)
01272     {
01273       data[i] = window->display->atom__NET_WM_STATE_ABOVE;
01274       ++i;
01275     }
01276   if (window->wm_state_below)
01277     {
01278       data[i] = window->display->atom__NET_WM_STATE_BELOW;
01279       ++i;
01280     }
01281   if (window->wm_state_demands_attention)
01282     {
01283       data[i] = window->display->atom__NET_WM_STATE_DEMANDS_ATTENTION;
01284       ++i;
01285     }
01286 
01287   meta_verbose ("Setting _NET_WM_STATE with %d atoms\n", i);
01288   
01289   meta_error_trap_push (window->display);
01290   XChangeProperty (window->display->xdisplay, window->xwindow,
01291                    window->display->atom__NET_WM_STATE,
01292                    XA_ATOM,
01293                    32, PropModeReplace, (guchar*) data, i);
01294   meta_error_trap_pop (window->display, FALSE);
01295 }
01296 
01297 gboolean
01298 meta_window_located_on_workspace (MetaWindow    *window,
01299                                   MetaWorkspace *workspace)
01300 {
01301   return (window->on_all_workspaces && window->screen == workspace->screen) ||
01302     (window->workspace == workspace);
01303 }
01304 
01305 static gboolean
01306 is_minimized_foreach (MetaWindow *window,
01307                       void       *data)
01308 {
01309   gboolean *result = data;
01310 
01311   *result = window->minimized;
01312   if (*result)
01313     return FALSE; /* stop as soon as we find one */
01314   else
01315     return TRUE;
01316 }
01317 
01318 static gboolean
01319 ancestor_is_minimized (MetaWindow *window)
01320 {
01321   gboolean is_minimized;
01322 
01323   is_minimized = FALSE;
01324 
01325   meta_window_foreach_ancestor (window, is_minimized_foreach, &is_minimized);
01326 
01327   return is_minimized;
01328 }
01329 
01330 gboolean
01331 meta_window_showing_on_its_workspace (MetaWindow *window)
01332 {
01333   gboolean showing;
01334   gboolean is_desktop_or_dock;
01335   MetaWorkspace* workspace_of_window;
01336 
01337   showing = TRUE;
01338 
01339   /* 1. See if we're minimized */
01340   if (window->minimized)
01341     showing = FALSE;
01342   
01343   /* 2. See if we're in "show desktop" mode */
01344   is_desktop_or_dock = FALSE;
01345   is_desktop_or_dock_foreach (window,
01346                               &is_desktop_or_dock);
01347 
01348   meta_window_foreach_ancestor (window, is_desktop_or_dock_foreach,
01349                                 &is_desktop_or_dock);
01350 
01351   if (window->on_all_workspaces)
01352     workspace_of_window = window->screen->active_workspace;
01353   else if (window->workspace)
01354     workspace_of_window = window->workspace;
01355   else /* This only seems to be needed for startup */
01356     workspace_of_window = NULL;
01357 
01358   if (showing &&      
01359       workspace_of_window && workspace_of_window->showing_desktop &&
01360       !is_desktop_or_dock)
01361     {
01362       meta_verbose ("We're showing the desktop on the workspace(s) that window %s is on\n",
01363                     window->desc);
01364       showing = FALSE;
01365     }
01366 
01367   /* 3. See if an ancestor is minimized (note that
01368    *    ancestor's "mapped" field may not be up to date
01369    *    since it's being computed in this same idle queue)
01370    */
01371   
01372   if (showing)
01373     {
01374       if (ancestor_is_minimized (window))
01375         showing = FALSE;
01376     }
01377 
01378 #if 0
01379   /* 4. See if we're drawing wireframe
01380    */
01381   if (window->display->grab_window == window &&
01382       window->display->grab_wireframe_active)    
01383     showing = FALSE;
01384 #endif
01385 
01386   return showing;
01387 }
01388 
01389 gboolean
01390 meta_window_should_be_showing (MetaWindow  *window)
01391 {
01392   gboolean on_workspace;
01393 
01394   meta_verbose ("Should be showing for window %s\n", window->desc);
01395 
01396   /* See if we're on the workspace */
01397   on_workspace = meta_window_located_on_workspace (window, 
01398                                                    window->screen->active_workspace);
01399 
01400   if (!on_workspace)
01401     meta_verbose ("Window %s is not on workspace %d\n",
01402                   window->desc,
01403                   meta_workspace_index (window->screen->active_workspace));
01404   else
01405     meta_verbose ("Window %s is on the active workspace %d\n",
01406                   window->desc,
01407                   meta_workspace_index (window->screen->active_workspace));
01408 
01409   if (window->on_all_workspaces)
01410     meta_verbose ("Window %s is on all workspaces\n", window->desc);
01411 
01412   return on_workspace && meta_window_showing_on_its_workspace (window);
01413 }
01414 
01415 static void
01416 finish_minimize (gpointer data)
01417 {
01418   MetaWindow *window = data;
01419   /* FIXME: It really sucks to put timestamp pinging here; it'd
01420    * probably make more sense in implement_showing() so that it's at
01421    * least not duplicated in meta_window_show; but since
01422    * finish_minimize is a callback making things just slightly icky, I
01423    * haven't done that yet.
01424    */
01425   guint32 timestamp = meta_display_get_current_time_roundtrip (window->display);
01426   
01427   meta_window_hide (window);
01428   if (window->has_focus)
01429     {
01430       meta_workspace_focus_default_window (window->screen->active_workspace,
01431                                            window, 
01432                                            timestamp);
01433     }
01434 }
01435 
01436 static void
01437 implement_showing (MetaWindow *window,
01438                    gboolean    showing)
01439 {
01440   /* Actually show/hide the window */
01441   meta_verbose ("Implement showing = %d for window %s\n",
01442                 showing, window->desc);
01443     
01444   if (!showing)
01445     {
01446       gboolean on_workspace;
01447 
01448       on_workspace = meta_window_located_on_workspace (window, 
01449                                                        window->screen->active_workspace);
01450       
01451       /* Really this effects code should probably
01452        * be in meta_window_hide so the window->mapped
01453        * test isn't duplicated here. Anyhow, we animate
01454        * if we are mapped now, we are supposed to
01455        * be minimized, and we are on the current workspace.
01456        */
01457       if (on_workspace && window->minimized && window->mapped &&
01458           !meta_prefs_get_reduced_resources ())
01459         {
01460           MetaRectangle icon_rect, window_rect;
01461           gboolean result;
01462           
01463           /* Check if the window has an icon geometry */
01464           result = meta_window_get_icon_geometry (window, &icon_rect);
01465           
01466           if (!result)
01467             {
01468               /* just animate into the corner somehow - maybe
01469                * not a good idea...
01470                */              
01471               icon_rect.x = window->screen->rect.width;
01472               icon_rect.y = window->screen->rect.height;
01473               icon_rect.width = 1;
01474               icon_rect.height = 1;
01475             }
01476 
01477           meta_window_get_outer_rect (window, &window_rect);
01478 
01479           meta_effect_run_minimize (window,
01480                                     &window_rect,
01481                                     &icon_rect,
01482                                     finish_minimize,
01483                                     window);
01484         }
01485       else
01486         {
01487           finish_minimize (window);
01488         }
01489     }
01490   else
01491     {
01492       meta_window_show (window);
01493     }
01494 }
01495 
01496 void
01497 meta_window_calc_showing (MetaWindow  *window)
01498 {
01499   implement_showing (window, meta_window_should_be_showing (window));
01500 }
01501 
01502 static guint queue_idle[NUMBER_OF_QUEUES] = {0, 0, 0};
01503 static GSList *queue_pending[NUMBER_OF_QUEUES] = {NULL, NULL, NULL};
01504 
01505 static int
01506 stackcmp (gconstpointer a, gconstpointer b)
01507 {
01508   MetaWindow *aw = (gpointer) a;
01509   MetaWindow *bw = (gpointer) b;
01510 
01511   if (aw->screen != bw->screen)
01512     return 0; /* don't care how they sort with respect to each other */
01513   else
01514     return meta_stack_windows_cmp (aw->screen->stack,
01515                                    aw, bw);
01516 }
01517 
01518 static gboolean
01519 idle_calc_showing (gpointer data)
01520 {
01521   GSList *tmp;
01522   GSList *copy;
01523   GSList *should_show;
01524   GSList *should_hide;
01525   GSList *unplaced;
01526   GSList *displays;
01527   MetaWindow *first_window;
01528   guint queue_index = GPOINTER_TO_INT (data);
01529 
01530   meta_topic (META_DEBUG_WINDOW_STATE,
01531               "Clearing the calc_showing queue\n");
01532 
01533   /* Work with a copy, for reentrancy. The allowed reentrancy isn't
01534    * complete; destroying a window while we're in here would result in
01535    * badness. But it's OK to queue/unqueue calc_showings.
01536    */
01537   copy = g_slist_copy (queue_pending[queue_index]);
01538   g_slist_free (queue_pending[queue_index]);
01539   queue_pending[queue_index] = NULL;
01540   queue_idle[queue_index] = 0;
01541 
01542   destroying_windows_disallowed += 1;
01543   
01544   /* We map windows from top to bottom and unmap from bottom to
01545    * top, to avoid extra expose events. The exception is
01546    * for unplaced windows, which have to be mapped from bottom to
01547    * top so placement works.
01548    */
01549   should_show = NULL;
01550   should_hide = NULL;
01551   unplaced = NULL;
01552   displays = NULL;
01553 
01554   tmp = copy;
01555   while (tmp != NULL)
01556     {
01557       MetaWindow *window;
01558 
01559       window = tmp->data;
01560 
01561       if (!window->placed)
01562         unplaced = g_slist_prepend (unplaced, window);
01563       else if (meta_window_should_be_showing (window))
01564         should_show = g_slist_prepend (should_show, window);
01565       else
01566         should_hide = g_slist_prepend (should_hide, window);
01567       
01568       tmp = tmp->next;
01569     }
01570 
01571   /* bottom to top */
01572   unplaced = g_slist_sort (unplaced, stackcmp);
01573   should_hide = g_slist_sort (should_hide, stackcmp);
01574   /* top to bottom */
01575   should_show = g_slist_sort (should_show, stackcmp);
01576   should_show = g_slist_reverse (should_show);
01577 
01578   first_window = copy->data;
01579 
01580   meta_display_grab (first_window->display);
01581   
01582   tmp = unplaced;
01583   while (tmp != NULL)
01584     {
01585       MetaWindow *window;
01586 
01587       window = tmp->data;
01588 
01589       meta_window_calc_showing (window);
01590       
01591       tmp = tmp->next;
01592     }
01593 
01594   tmp = should_show;
01595   while (tmp != NULL)
01596     {
01597       MetaWindow *window;
01598 
01599       window = tmp->data;
01600 
01601       implement_showing (window, TRUE);
01602       
01603       tmp = tmp->next;
01604     }
01605   
01606   tmp = should_hide;
01607   while (tmp != NULL)
01608     {
01609       MetaWindow *window;
01610 
01611       window = tmp->data;
01612 
01613       implement_showing (window, FALSE);
01614       
01615       tmp = tmp->next;
01616     }
01617   
01618   tmp = copy;
01619   while (tmp != NULL)
01620     {
01621       MetaWindow *window;
01622 
01623       window = tmp->data;
01624 
01625       /* important to set this here for reentrancy -
01626        * if we queue a window again while it's in "copy",
01627        * then queue_calc_showing will just return since
01628        * we are still in the calc_showing queue
01629        */
01630       window->is_in_queues &= ~META_QUEUE_CALC_SHOWING;
01631       
01632       tmp = tmp->next;
01633     }
01634 
01635   if (meta_prefs_get_focus_mode () != META_FOCUS_MODE_CLICK)
01636     {
01637       /* When display->mouse_mode is false, we want to ignore
01638        * EnterNotify events unless they come from mouse motion.  To do
01639        * that, we set a sentinel property on the root window if we're
01640        * not in mouse_mode.
01641        */
01642       tmp = should_show;
01643       while (tmp != NULL)
01644         {
01645           MetaWindow *window = tmp->data;
01646           
01647           if (!window->display->mouse_mode)
01648             meta_display_increment_focus_sentinel (window->display);
01649 
01650           tmp = tmp->next;
01651         }
01652     }
01653 
01654   meta_display_ungrab (first_window->display);
01655   
01656   g_slist_free (copy);
01657 
01658   g_slist_free (unplaced);
01659   g_slist_free (should_show);
01660   g_slist_free (should_hide);
01661   g_slist_free (displays);
01662   
01663   destroying_windows_disallowed -= 1;
01664   
01665   return FALSE;
01666 }
01667 
01668 static const gchar* meta_window_queue_names[NUMBER_OF_QUEUES] =
01669   {"calc_showing", "move_resize", "update_icon"};
01670 
01671 static void
01672 meta_window_unqueue (MetaWindow *window, guint queuebits)
01673 {
01674   gint queuenum;
01675 
01676   for (queuenum=0; queuenum<NUMBER_OF_QUEUES; queuenum++)
01677     {
01678       if ((queuebits & 1<<queuenum) /* they have asked to unqueue */
01679           &&
01680           (window->is_in_queues & 1<<queuenum)) /* it's in the queue */
01681         {
01682 
01683           meta_topic (META_DEBUG_WINDOW_STATE,
01684               "Removing %s from the %s queue\n",
01685               window->desc,
01686               meta_window_queue_names[queuenum]);
01687 
01688           /* Note that window may not actually be in the queue
01689            * because it may have been in "copy" inside the idle handler
01690            */  
01691           queue_pending[queuenum] = g_slist_remove (queue_pending[queuenum], window);
01692           window->is_in_queues &= ~(1<<queuenum);
01693 
01694           /* Okay, so maybe we've used up all the entries in the queue.
01695            * In that case, we should kill the function that deals with
01696            * the queue, because there's nothing left for it to do.
01697            */
01698           if (queue_pending[queuenum] == NULL && queue_idle[queuenum] != 0)
01699             {
01700               g_source_remove (queue_idle[queuenum]);
01701               queue_idle[queuenum] = 0;
01702             }
01703         }
01704     }
01705 }
01706 
01707 static void
01708 meta_window_flush_calc_showing (MetaWindow *window)
01709 {
01710   if (window->is_in_queues & META_QUEUE_CALC_SHOWING)
01711     {
01712       meta_window_unqueue (window, META_QUEUE_CALC_SHOWING);
01713       meta_window_calc_showing (window);
01714     }
01715 }
01716 
01717 void
01718 meta_window_queue (MetaWindow *window, guint queuebits)
01719 {
01720   guint queuenum;
01721 
01722   for (queuenum=0; queuenum<NUMBER_OF_QUEUES; queuenum++)
01723     {
01724       if (queuebits & 1<<queuenum)
01725         {
01726           /* Data which varies between queues.
01727            * Yes, these do look a lot like associative arrays:
01728            * I seem to be turning into a Perl programmer.
01729            */
01730 
01731           const gint window_queue_idle_priority[NUMBER_OF_QUEUES] =
01732             {
01733               G_PRIORITY_DEFAULT_IDLE,  /* CALC_SHOWING */
01734               META_PRIORITY_RESIZE,     /* MOVE_RESIZE */
01735               G_PRIORITY_DEFAULT_IDLE   /* UPDATE_ICON */
01736             };
01737 
01738           const GSourceFunc window_queue_idle_handler[NUMBER_OF_QUEUES] =
01739             {
01740               idle_calc_showing,
01741               idle_move_resize,
01742               idle_update_icon,
01743             };
01744 
01745           /* If we're about to drop the window, there's no point in putting
01746            * it on a queue.
01747            */
01748           if (window->unmanaging)
01749             break;
01750 
01751           /* If the window already claims to be in that queue, there's no
01752            * point putting it in the queue.
01753            */
01754           if (window->is_in_queues & 1<<queuenum)
01755             break;
01756 
01757           meta_topic (META_DEBUG_WINDOW_STATE,
01758               "Putting %s in the %s queue\n",
01759               window->desc,
01760               meta_window_queue_names[queuenum]);
01761 
01762           /* So, mark it as being in this queue. */
01763           window->is_in_queues |= 1<<queuenum;
01764 
01765           /* There's not a lot of point putting things into a queue if
01766            * nobody's on the other end pulling them out. Therefore,
01767            * let's check to see whether an idle handler exists to do
01768            * that. If not, we'll create one.
01769            */
01770 
01771           if (queue_idle[queuenum] == 0)
01772             queue_idle[queuenum] = g_idle_add_full
01773               (
01774                 window_queue_idle_priority[queuenum],
01775                 window_queue_idle_handler[queuenum],
01776                 GUINT_TO_POINTER(queuenum),
01777                 NULL
01778               );
01779 
01780           /* And now we actually put it on the queue. */
01781           queue_pending[queuenum] = g_slist_prepend (queue_pending[queuenum],
01782                                                      window);
01783       }
01784   }
01785 }
01786 
01787 static gboolean
01788 intervening_user_event_occurred (MetaWindow *window)
01789 {
01790   guint32 compare;
01791   MetaWindow *focus_window;
01792 
01793   focus_window = window->display->focus_window;
01794 
01795   meta_topic (META_DEBUG_STARTUP,
01796               "COMPARISON:\n"
01797               "  net_wm_user_time_set : %d\n"
01798               "  net_wm_user_time     : %u\n"
01799               "  initial_timestamp_set: %d\n"
01800               "  initial_timestamp    : %u\n",
01801               window->net_wm_user_time_set,
01802               window->net_wm_user_time,
01803               window->initial_timestamp_set,
01804               window->initial_timestamp);
01805   if (focus_window != NULL)
01806     {
01807       meta_topic (META_DEBUG_STARTUP,
01808                   "COMPARISON (continued):\n"
01809                   "  focus_window             : %s\n"
01810                   "  fw->net_wm_user_time_set : %d\n"
01811                   "  fw->net_wm_user_time     : %u\n",
01812                   focus_window->desc,
01813                   focus_window->net_wm_user_time_set,
01814                   focus_window->net_wm_user_time);
01815     }
01816 
01817   /* We expect the most common case for not focusing a new window
01818    * to be when a hint to not focus it has been set.  Since we can
01819    * deal with that case rapidly, we use special case it--this is
01820    * merely a preliminary optimization.  :)
01821    */
01822   if ( ((window->net_wm_user_time_set == TRUE) &&
01823        (window->net_wm_user_time == 0))
01824       ||
01825        ((window->initial_timestamp_set == TRUE) &&
01826        (window->initial_timestamp == 0)))
01827     {
01828       meta_topic (META_DEBUG_STARTUP,
01829                   "window %s explicitly requested no focus\n",
01830                   window->desc);
01831       return TRUE;
01832     }
01833 
01834   if (!(window->net_wm_user_time_set) && !(window->initial_timestamp_set))
01835     {
01836       meta_topic (META_DEBUG_STARTUP,
01837                   "no information about window %s found\n",
01838                   window->desc);
01839       return FALSE;
01840     }
01841 
01842   if (focus_window != NULL &&
01843       !focus_window->net_wm_user_time_set)
01844     {
01845       meta_topic (META_DEBUG_STARTUP,
01846                   "focus window, %s, doesn't have a user time set yet!\n",
01847                   window->desc);
01848       return FALSE;
01849     }
01850 
01851   /* To determine the "launch" time of an application,
01852    * startup-notification can set the TIMESTAMP and the
01853    * application (usually via its toolkit such as gtk or qt) can
01854    * set the _NET_WM_USER_TIME.  If both are set, then it means
01855    * the user has interacted with the application since it
01856    * launched, and _NET_WM_USER_TIME is the value that should be
01857    * used in the comparison.
01858    */
01859   compare = window->initial_timestamp_set ? window->initial_timestamp : 0;
01860   compare = window->net_wm_user_time_set  ? window->net_wm_user_time  : compare;
01861 
01862   if ((focus_window != NULL) &&
01863       XSERVER_TIME_IS_BEFORE (compare, focus_window->net_wm_user_time))
01864     {
01865       meta_topic (META_DEBUG_STARTUP,
01866                   "window %s focus prevented by other activity; %u < %u\n",
01867                   window->desc,
01868                   compare, 
01869                   focus_window->net_wm_user_time);
01870       return TRUE;
01871     }
01872   else
01873     {
01874       meta_topic (META_DEBUG_STARTUP,
01875                   "new window %s with no intervening events\n",
01876                   window->desc);
01877       return FALSE;
01878     }
01879 }
01880 
01881 /* This function is an ugly hack.  It's experimental in nature and ought to be
01882  * replaced by a real hint from the app to the WM if we decide the experimental
01883  * behavior is worthwhile.  The basic idea is to get more feedback about how
01884  * usage scenarios of "strict" focus users and what they expect.  See #326159.
01885  */
01886 gboolean
01887 __window_is_terminal (MetaWindow *window)
01888 {
01889   if (window == NULL || window->res_class == NULL)
01890     return FALSE;
01891 
01892   /*
01893    * Compare res_class, which is not user-settable, and thus theoretically
01894    * a more-reliable indication of term-ness.
01895    */
01896 
01897   /* gnome-terminal -- if you couldn't guess */
01898   if (strcmp (window->res_class, "Gnome-terminal") == 0)
01899     return TRUE;
01900   /* xterm, rxvt, aterm */
01901   else if (strcmp (window->res_class, "XTerm") == 0)
01902     return TRUE;
01903   /* konsole, KDE's terminal program */
01904   else if (strcmp (window->res_class, "Konsole") == 0)
01905     return TRUE;
01906   /* rxvt-unicode */
01907   else if (strcmp (window->res_class, "URxvt") == 0)
01908     return TRUE;
01909   /* eterm */
01910   else if (strcmp (window->res_class, "Eterm") == 0)
01911     return TRUE;
01912   /* KTerm -- some terminal not KDE based; so not like Konsole */
01913   else if (strcmp (window->res_class, "KTerm") == 0)
01914     return TRUE;
01915   /* Multi-gnome-terminal */
01916   else if (strcmp (window->res_class, "Multi-gnome-terminal") == 0)
01917     return TRUE;
01918   /* mlterm ("multi lingual terminal emulator on X") */
01919   else if (strcmp (window->res_class, "mlterm") == 0)
01920     return TRUE;
01921 
01922   return FALSE;
01923 }
01924 
01925 /* This function determines what state the window should have assuming that it
01926  * and the focus_window have no relation
01927  */
01928 static void
01929 window_state_on_map (MetaWindow *window, 
01930                      gboolean *takes_focus,
01931                      gboolean *places_on_top)
01932 {
01933   gboolean intervening_events;
01934 
01935   intervening_events = intervening_user_event_occurred (window);
01936 
01937   *takes_focus = !intervening_events;
01938   *places_on_top = *takes_focus;
01939 
01940   /* don't initially focus windows that are intended to not accept
01941    * focus
01942    */
01943   if (!(window->input || window->take_focus))
01944     {
01945       *takes_focus = FALSE;
01946       return;
01947     }
01948 
01949   /* Terminal usage may be different; some users intend to launch
01950    * many apps in quick succession or to just view things in the new
01951    * window while still interacting with the terminal.  In that case,
01952    * apps launched from the terminal should not take focus.  This
01953    * isn't quite the same as not allowing focus to transfer from
01954    * terminals due to new window map, but the latter is a much easier
01955    * approximation to enforce so we do that.
01956    */
01957   if (*takes_focus &&
01958       meta_prefs_get_focus_new_windows () == META_FOCUS_NEW_WINDOWS_STRICT &&
01959       !window->display->allow_terminal_deactivation &&
01960       __window_is_terminal (window->display->focus_window) &&
01961       !meta_window_is_ancestor_of_transient (window->display->focus_window,
01962                                              window))
01963     {
01964       meta_topic (META_DEBUG_FOCUS,
01965                   "focus_window is terminal; not focusing new window.\n");
01966       *takes_focus = FALSE;
01967       *places_on_top = FALSE;
01968     }
01969 
01970   switch (window->type)
01971     {
01972     case META_WINDOW_UTILITY:
01973     case META_WINDOW_TOOLBAR:
01974       *takes_focus = FALSE;
01975       *places_on_top = FALSE;
01976       break;
01977     case META_WINDOW_DOCK:
01978     case META_WINDOW_DESKTOP:
01979     case META_WINDOW_SPLASHSCREEN:
01980     case META_WINDOW_MENU:
01981       /* don't focus any of these; places_on_top may be irrelevant for some of
01982        * these (e.g. dock)--but you never know--the focus window might also be
01983        * of the same type in some weird situation...
01984        */
01985       *takes_focus = FALSE;
01986       break;
01987     case META_WINDOW_NORMAL:
01988     case META_WINDOW_DIALOG:
01989     case META_WINDOW_MODAL_DIALOG:
01990       /* The default is correct for these */
01991       break;
01992     }
01993 }
01994 
01995 static gboolean
01996 windows_overlap (const MetaWindow *w1, const MetaWindow *w2)
01997 {
01998   MetaRectangle w1rect, w2rect;
01999   meta_window_get_outer_rect (w1, &w1rect);
02000   meta_window_get_outer_rect (w2, &w2rect);
02001   return meta_rectangle_overlap (&w1rect, &w2rect);
02002 }
02003 
02004 /* Returns whether a new window would be covered by any
02005  * existing window on the same workspace that is set
02006  * to be "above" ("always on top").  A window that is not
02007  * set "above" would be underneath the new window anyway.
02008  *
02009  * We take "covered" to mean even partially covered, but
02010  * some people might prefer entirely covered.  I think it
02011  * is more useful to behave this way if any part of the
02012  * window is covered, because a partial coverage could be
02013  * (say) ninety per cent and almost indistinguishable from total.
02014  */
02015 static gboolean
02016 window_would_be_covered (const MetaWindow *newbie)
02017 {
02018   MetaWorkspace *workspace = newbie->workspace;
02019   GList *tmp, *windows;
02020  
02021   windows = meta_workspace_list_windows (workspace);
02022 
02023   tmp = windows;
02024   while (tmp != NULL)
02025     { 
02026       MetaWindow *w = tmp->data;
02027 
02028       if (w->wm_state_above && w != newbie)
02029         {
02030           /* We have found a window that is "above". Perhaps it overlaps. */
02031           if (windows_overlap (w, newbie))
02032             {
02033               g_list_free (windows); /* clean up... */
02034               return TRUE; /* yes, it does */
02035             }
02036         }
02037 
02038       tmp = tmp->next;
02039     }
02040 
02041   g_list_free (windows);
02042   return FALSE; /* none found */
02043 }
02044 
02045 /* XXX META_EFFECT_*_MAP */
02046 void
02047 meta_window_show (MetaWindow *window)
02048 {
02049   gboolean did_show;
02050   gboolean takes_focus_on_map;
02051   gboolean place_on_top_on_map;
02052   gboolean needs_stacking_adjustment;
02053   MetaWindow *focus_window;
02054   guint32     timestamp;
02055 
02056   /* FIXME: It really sucks to put timestamp pinging here; it'd
02057    * probably make more sense in implement_showing() so that it's at
02058    * least not duplicated in finish_minimize.  *shrug*
02059    */
02060   timestamp = meta_display_get_current_time_roundtrip (window->display);
02061 
02062   meta_topic (META_DEBUG_WINDOW_STATE,
02063               "Showing window %s, shaded: %d iconic: %d placed: %d\n",
02064               window->desc, window->shaded, window->iconic, window->placed);
02065 
02066   focus_window = window->display->focus_window;  /* May be NULL! */
02067   did_show = FALSE;
02068   window_state_on_map (window, &takes_focus_on_map, &place_on_top_on_map);
02069   needs_stacking_adjustment = FALSE;
02070 
02071   meta_topic (META_DEBUG_WINDOW_STATE,
02072               "Window %s %s focus on map, and %s place on top on map.\n",
02073               window->desc,
02074               takes_focus_on_map ? "does" : "does not",
02075               place_on_top_on_map ? "does" : "does not");
02076 
02077   /* Now, in some rare cases we should *not* put a new window on top.
02078    * These cases include certain types of windows showing for the first
02079    * time, and any window which would be covered because of another window
02080    * being set "above" ("always on top").
02081    *
02082    * FIXME: Although "place_on_top_on_map" and "takes_focus_on_map" are
02083    * generally based on the window type, there is a special case when the
02084    * focus window is a terminal for them both to be false; this should
02085    * probably rather be a term in the "if" condition below.
02086    */
02087 
02088   if ( focus_window != NULL && window->showing_for_first_time &&
02089       ( (!place_on_top_on_map && !takes_focus_on_map) ||
02090       window_would_be_covered (window) )
02091     ) {
02092       if (meta_window_is_ancestor_of_transient (focus_window, window))
02093         {
02094           /* This happens for error dialogs or alerts; these need to remain on
02095            * top, but it would be confusing to have its ancestor remain
02096            * focused.
02097            */
02098           meta_topic (META_DEBUG_STARTUP,
02099                       "The focus window %s is an ancestor of the newly mapped "
02100                       "window %s which isn't being focused.  Unfocusing the "
02101                       "ancestor.\n",
02102                       focus_window->desc, window->desc);
02103 
02104           meta_display_focus_the_no_focus_window (window->display,
02105                                                   window->screen,
02106                                                   timestamp);
02107         }
02108       else
02109         {
02110           needs_stacking_adjustment = TRUE;
02111           if (!window->placed)
02112             window->denied_focus_and_not_transient = TRUE;
02113         }
02114     }
02115 
02116   if (!window->placed)
02117     {
02118       /* We have to recalc the placement here since other windows may
02119        * have been mapped/placed since we last did constrain_position
02120        */
02121 
02122       /* calc_placement is an efficiency hack to avoid
02123        * multiple placement calculations before we finally
02124        * show the window.
02125        */
02126       window->calc_placement = TRUE;
02127       meta_window_move_resize_now (window);
02128       window->calc_placement = FALSE;
02129 
02130       /* don't ever do the initial position constraint thing again.
02131        * This is toggled here so that initially-iconified windows
02132        * still get placed when they are ultimately shown.
02133        */
02134       window->placed = TRUE;
02135 
02136       /* Don't want to accidentally reuse the fact that we had been denied
02137        * focus in any future constraints unless we're denied focus again.
02138        */
02139       window->denied_focus_and_not_transient = FALSE;
02140     }
02141   
02142   if (needs_stacking_adjustment)
02143     {
02144       gboolean overlap;
02145 
02146       /* This window isn't getting focus on map.  We may need to do some
02147        * special handing with it in regards to
02148        *   - the stacking of the window
02149        *   - the MRU position of the window
02150        *   - the demands attention setting of the window
02151        *
02152        * Firstly, set the flag so we don't give the window focus anyway
02153        * and confuse people.
02154        */
02155           
02156       takes_focus_on_map = FALSE;
02157 
02158       overlap = windows_overlap (window, focus_window);
02159 
02160       /* We want alt tab to go to the denied-focus window */
02161       ensure_mru_position_after (window, focus_window);
02162 
02163       /* We don't want the denied-focus window to obscure the focus
02164        * window, and if we're in both click-to-focus mode and
02165        * raise-on-click mode then we want to maintain the invariant
02166        * that MRU order == stacking order.  The need for this if
02167        * comes from the fact that in sloppy/mouse focus the focus
02168        * window may not overlap other windows and also can be
02169        * considered "below" them; this combination means that
02170        * placing the denied-focus window "below" the focus window
02171        * in the stack when it doesn't overlap it confusingly places
02172        * that new window below a lot of other windows.
02173        */
02174       if (overlap || 
02175           (meta_prefs_get_focus_mode () == META_FOCUS_MODE_CLICK &&
02176            meta_prefs_get_raise_on_click ()))
02177         meta_window_stack_just_below (window, focus_window);
02178 
02179       /* If the window will be obscured by the focus window, then the
02180        * user might not notice the window appearing so set the
02181        * demands attention hint.
02182        *
02183        * We set the hint ourselves rather than calling 
02184        * meta_window_set_demands_attention() because that would cause
02185        * a recalculation of overlap, and a call to set_net_wm_state()
02186        * which we are going to call ourselves here a few lines down.
02187        */
02188       if (overlap)
02189         window->wm_state_demands_attention = TRUE;
02190     } 
02191 
02192   /* Shaded means the frame is mapped but the window is not */
02193   
02194   if (window->frame && !window->frame->mapped)
02195     {
02196       meta_topic (META_DEBUG_WINDOW_STATE,
02197                   "Frame actually needs map\n");
02198       window->frame->mapped = TRUE;
02199       meta_ui_map_frame (window->screen->ui, window->frame->xwindow);
02200       did_show = TRUE;
02201     }
02202 
02203   if (window->shaded)
02204     {
02205       if (window->mapped)
02206         {          
02207           meta_topic (META_DEBUG_WINDOW_STATE,
02208                       "%s actually needs unmap (shaded)\n", window->desc);
02209           meta_topic (META_DEBUG_WINDOW_STATE,
02210                       "Incrementing unmaps_pending on %s for shade\n",
02211                       window->desc);
02212           window->mapped = FALSE;
02213           window->unmaps_pending += 1;
02214           meta_error_trap_push (window->display);
02215           XUnmapWindow (window->display->xdisplay, window->xwindow);
02216           meta_error_trap_pop (window->display, FALSE);
02217         }
02218 
02219       if (!window->iconic)
02220         {
02221           window->iconic = TRUE;
02222           set_wm_state (window, IconicState);
02223         }
02224     }
02225   else
02226     {
02227       if (!window->mapped)
02228         {
02229           meta_topic (META_DEBUG_WINDOW_STATE,
02230                       "%s actually needs map\n", window->desc);
02231           window->mapped = TRUE;
02232           meta_error_trap_push (window->display);
02233           XMapWindow (window->display->xdisplay, window->xwindow);
02234           meta_error_trap_pop (window->display, FALSE);
02235           did_show = TRUE;
02236           
02237           if (window->was_minimized)
02238             {
02239               MetaRectangle window_rect;
02240               MetaRectangle icon_rect;
02241               
02242               window->was_minimized = FALSE;
02243               
02244               if (meta_window_get_icon_geometry (window, &icon_rect))
02245                 {
02246                   meta_window_get_outer_rect (window, &window_rect);
02247                   
02248                   meta_effect_run_unminimize (window,
02249                                               &window_rect,
02250                                               &icon_rect,
02251                                               NULL, NULL);
02252                 }
02253             }
02254         }      
02255       
02256       if (window->iconic)
02257         {
02258           window->iconic = FALSE;
02259           set_wm_state (window, NormalState);
02260         }
02261     }
02262 
02263   /* We don't want to worry about all cases from inside
02264    * implement_showing(); we only want to worry about focus if this
02265    * window has not been shown before.
02266    */
02267   if (window->showing_for_first_time)
02268     {
02269       window->showing_for_first_time = FALSE;
02270       if (takes_focus_on_map)
02271         {                
02272           meta_window_focus (window, timestamp);
02273         }
02274       else
02275         {
02276           /* Prevent EnterNotify events in sloppy/mouse focus from
02277            * erroneously focusing the window that had been denied
02278            * focus.  FIXME: This introduces a race; I have a couple
02279            * ideas for a better way to accomplish the same thing, but
02280            * they're more involved so do it this way for now.
02281            */
02282           meta_display_increment_focus_sentinel (window->display);
02283         }
02284     }
02285 
02286   set_net_wm_state (window);
02287 
02288   if (did_show && window->struts)
02289     {
02290       meta_topic (META_DEBUG_WORKAREA,
02291                   "Mapped window %s with struts, so invalidating work areas\n",
02292                   window->desc);
02293       invalidate_work_areas (window);
02294     }
02295 }
02296 
02297 /* XXX META_EFFECT_*_UNMAP */
02298 static void
02299 meta_window_hide (MetaWindow *window)
02300 {
02301   gboolean did_hide;
02302   
02303   meta_topic (META_DEBUG_WINDOW_STATE,
02304               "Hiding window %s\n", window->desc);
02305 
02306   did_hide = FALSE;
02307   
02308   if (window->frame && window->frame->mapped)
02309     {
02310       meta_topic (META_DEBUG_WINDOW_STATE, "Frame actually needs unmap\n");
02311       window->frame->mapped = FALSE;
02312       meta_ui_unmap_frame (window->screen->ui, window->frame->xwindow);
02313       did_hide = TRUE;
02314     }
02315 
02316   if (window->mapped)
02317     {
02318       meta_topic (META_DEBUG_WINDOW_STATE,
02319                   "%s actually needs unmap\n", window->desc);
02320       meta_topic (META_DEBUG_WINDOW_STATE,
02321                   "Incrementing unmaps_pending on %s for hide\n",
02322                   window->desc);
02323       window->mapped = FALSE;
02324       window->unmaps_pending += 1;
02325       meta_error_trap_push (window->display);      
02326       XUnmapWindow (window->display->xdisplay, window->xwindow);
02327       meta_error_trap_pop (window->display, FALSE);
02328       did_hide = TRUE;
02329     }
02330 
02331   if (!window->iconic)
02332     {
02333       window->iconic = TRUE;
02334       set_wm_state (window, IconicState);
02335     }
02336   
02337   set_net_wm_state (window);
02338       
02339   if (did_hide && window->struts)
02340     {
02341       meta_topic (META_DEBUG_WORKAREA,
02342                   "Unmapped window %s with struts, so invalidating work areas\n",
02343                   window->desc);
02344       invalidate_work_areas (window);
02345     }
02346 }
02347 
02348 static gboolean
02349 queue_calc_showing_func (MetaWindow *window,
02350                          void       *data)
02351 {
02352   meta_window_queue(window, META_QUEUE_CALC_SHOWING);
02353   return TRUE;
02354 }
02355 
02356 void
02357 meta_window_minimize (MetaWindow  *window)
02358 {
02359   if (!window->minimized)
02360     {
02361       window->minimized = TRUE;
02362       meta_window_queue(window, META_QUEUE_CALC_SHOWING);
02363 
02364       meta_window_foreach_transient (window,
02365                                      queue_calc_showing_func,
02366                                      NULL);
02367       
02368       if (window->has_focus)
02369         {
02370           meta_topic (META_DEBUG_FOCUS,
02371                       "Focusing default window due to minimization of focus window %s\n",
02372                       window->desc);
02373         }
02374       else
02375         {
02376           meta_topic (META_DEBUG_FOCUS,
02377                       "Minimizing window %s which doesn't have the focus\n",
02378                       window->desc);
02379         }
02380     }
02381 }
02382 
02383 void
02384 meta_window_unminimize (MetaWindow  *window)
02385 {
02386   if (window->minimized)
02387     {
02388       window->minimized = FALSE;
02389       window->was_minimized = TRUE;
02390       meta_window_queue(window, META_QUEUE_CALC_SHOWING);
02391 
02392       meta_window_foreach_transient (window,
02393                                      queue_calc_showing_func,
02394                                      NULL);
02395     }
02396 }
02397 
02398 static void
02399 ensure_size_hints_satisfied (MetaRectangle    *rect,
02400                              const XSizeHints *size_hints)
02401 {
02402   int minw, minh, maxw, maxh;   /* min/max width/height                      */
02403   int basew, baseh, winc, hinc; /* base width/height, width/height increment */
02404   int extra_width, extra_height;
02405 
02406   minw  = size_hints->min_width;  minh  = size_hints->min_height;
02407   maxw  = size_hints->max_width;  maxh  = size_hints->max_height;
02408   basew = size_hints->base_width; baseh = size_hints->base_height;
02409   winc  = size_hints->width_inc;  hinc  = size_hints->height_inc;
02410   
02411   /* First, enforce min/max size constraints */
02412   rect->width  = CLAMP (rect->width,  minw, maxw);
02413   rect->height = CLAMP (rect->height, minh, maxh);
02414 
02415   /* Now, verify size increment constraints are satisfied, or make them be */
02416   extra_width  = (rect->width  - basew) % winc;
02417   extra_height = (rect->height - baseh) % hinc;
02418 
02419   rect->width  -= extra_width;
02420   rect->height -= extra_height;
02421 
02422   /* Adjusting width/height down, as done above, may violate minimum size
02423    * constraints, so one last fix.
02424    */
02425   if (rect->width  < minw)
02426     rect->width  += ((minw - rect->width)/winc  + 1)*winc;
02427   if (rect->height < minh)
02428     rect->height += ((minh - rect->height)/hinc + 1)*hinc;
02429 }
02430 
02431 static void
02432 meta_window_save_rect (MetaWindow *window)
02433 {
02434   if (!(META_WINDOW_MAXIMIZED (window) || window->fullscreen))
02435     {
02436       /* save size/pos as appropriate args for move_resize */
02437       if (!window->maximized_horizontally)
02438         {
02439           window->saved_rect.x      = window->rect.x;
02440           window->saved_rect.width  = window->rect.width;
02441           if (window->frame)
02442             window->saved_rect.x   += window->frame->rect.x;
02443         }
02444       if (!window->maximized_vertically)
02445         {
02446           window->saved_rect.y      = window->rect.y;
02447           window->saved_rect.height = window->rect.height;
02448           if (window->frame)
02449             window->saved_rect.y   += window->frame->rect.y;
02450         }
02451     }
02452 }
02453 
02460 static void
02461 force_save_user_window_placement (MetaWindow *window)
02462 {
02463   meta_window_get_client_root_coords (window, &window->user_rect);
02464 }
02465 
02473 static void
02474 save_user_window_placement (MetaWindow *window)
02475 {
02476   if (!(META_WINDOW_MAXIMIZED (window) || window->fullscreen))
02477     {
02478       MetaRectangle user_rect;
02479 
02480       meta_window_get_client_root_coords (window, &user_rect);
02481 
02482       if (!window->maximized_horizontally)
02483         {
02484           window->user_rect.x     = user_rect.x;
02485           window->user_rect.width = user_rect.width;
02486         }
02487       if (!window->maximized_vertically)
02488         {
02489           window->user_rect.y      = user_rect.y;
02490           window->user_rect.height = user_rect.height;
02491         }
02492     }
02493 }
02494 
02495 void
02496 meta_window_maximize_internal (MetaWindow        *window,
02497                                MetaMaximizeFlags  directions,
02498                                MetaRectangle     *saved_rect)
02499 {
02500   /* At least one of the two directions ought to be set */
02501   gboolean maximize_horizontally, maximize_vertically;
02502   maximize_horizontally = directions & META_MAXIMIZE_HORIZONTAL;
02503   maximize_vertically   = directions & META_MAXIMIZE_VERTICAL;
02504   g_assert (maximize_horizontally || maximize_vertically);
02505 
02506   meta_topic (META_DEBUG_WINDOW_OPS,
02507               "Maximizing %s%s\n",
02508               window->desc,
02509               maximize_horizontally && maximize_vertically ? "" :
02510                 maximize_horizontally ? " horizontally" :
02511                   maximize_vertically ? " vertically" : "BUGGGGG");
02512   
02513   if (saved_rect != NULL)
02514     window->saved_rect = *saved_rect;
02515   else
02516     meta_window_save_rect (window);
02517   
02518   window->maximized_horizontally = 
02519     window->maximized_horizontally || maximize_horizontally;
02520   window->maximized_vertically = 
02521     window->maximized_vertically   || maximize_vertically;
02522 
02523   /* Fix for #336850: If the frame shape isn't reapplied, it is
02524    * possible that the frame will retains its rounded corners. That
02525    * happens if the client's size when maximized equals the unmaximized
02526    * size.
02527    */
02528   if (window->frame)
02529     window->frame->need_reapply_frame_shape = TRUE;
02530   
02531   recalc_window_features (window);
02532   set_net_wm_state (window);
02533 }
02534 
02535 void
02536 meta_window_maximize (MetaWindow        *window,
02537                       MetaMaximizeFlags  directions)
02538 {
02539   /* At least one of the two directions ought to be set */
02540   gboolean maximize_horizontally, maximize_vertically;
02541   maximize_horizontally = directions & META_MAXIMIZE_HORIZONTAL;
02542   maximize_vertically   = directions & META_MAXIMIZE_VERTICAL;
02543   g_assert (maximize_horizontally || maximize_vertically);
02544 
02545   /* Only do something if the window isn't already maximized in the
02546    * given direction(s).
02547    */
02548   if ((maximize_horizontally && !window->maximized_horizontally) ||
02549       (maximize_vertically   && !window->maximized_vertically))
02550     {
02551       if (window->shaded && maximize_vertically)
02552         {
02553           /* Shading sucks anyway; I'm not adding a timestamp argument
02554            * to this function just for this niche usage & corner case.
02555            */
02556           guint32 timestamp = 
02557             meta_display_get_current_time_roundtrip (window->display);
02558           meta_window_unshade (window, timestamp);
02559         }
02560       
02561       /* if the window hasn't been placed yet, we'll maximize it then
02562        */
02563       if (!window->placed)
02564         {
02565           window->maximize_horizontally_after_placement = 
02566             window->maximize_horizontally_after_placement || 
02567             maximize_horizontally;
02568           window->maximize_vertically_after_placement = 
02569             window->maximize_vertically_after_placement || 
02570             maximize_vertically;
02571           return;
02572         }
02573 
02574       meta_window_maximize_internal (window, 
02575                                      directions,
02576                                      NULL);
02577 
02578       /* move_resize with new maximization constraints
02579        */
02580       meta_window_queue(window, META_QUEUE_MOVE_RESIZE);
02581     }
02582 }
02583 
02584 static void
02585 unmaximize_window_before_freeing (MetaWindow        *window)
02586 {
02587   meta_topic (META_DEBUG_WINDOW_OPS,
02588               "Unmaximizing %s just before freeing\n",
02589               window->desc);
02590 
02591   window->maximized_horizontally = FALSE;
02592   window->maximized_vertically = FALSE;
02593 
02594   if (window->withdrawn)                /* See bug #137185 */
02595     {
02596       window->rect = window->saved_rect;
02597       set_net_wm_state (window);
02598     }
02599   else if (window->screen->closing)     /* See bug #358042 */
02600     {
02601       /* Do NOT update net_wm_state: this screen is closing,
02602        * it likely will be managed by another window manager
02603        * that will need the current _NET_WM_STATE atoms.
02604        * Moreover, it will need to know the unmaximized geometry,
02605        * therefore move_resize the window to saved_rect here
02606        * before closing it. */
02607       meta_window_move_resize (window,
02608                                FALSE,
02609                                window->saved_rect.x,
02610                                window->saved_rect.y,
02611                                window->saved_rect.width,
02612                                window->saved_rect.height);
02613     }
02614 }
02615 
02616 void
02617 meta_window_unmaximize (MetaWindow        *window,
02618                         MetaMaximizeFlags  directions)
02619 {
02620   /* At least one of the two directions ought to be set */
02621   gboolean unmaximize_horizontally, unmaximize_vertically;
02622   unmaximize_horizontally = directions & META_MAXIMIZE_HORIZONTAL;
02623   unmaximize_vertically   = directions & META_MAXIMIZE_VERTICAL;
02624   g_assert (unmaximize_horizontally || unmaximize_vertically);
02625 
02626   /* Only do something if the window isn't already maximized in the
02627    * given direction(s).
02628    */
02629   if ((unmaximize_horizontally && window->maximized_horizontally) ||
02630       (unmaximize_vertically   && window->maximized_vertically))
02631     {
02632       MetaRectangle target_rect;
02633 
02634       meta_topic (META_DEBUG_WINDOW_OPS,
02635                   "Unmaximizing %s%s\n",
02636                   window->desc,
02637                   unmaximize_horizontally && unmaximize_vertically ? "" :
02638                     unmaximize_horizontally ? " horizontally" :
02639                       unmaximize_vertically ? " vertically" : "BUGGGGG");
02640       
02641       window->maximized_horizontally = 
02642         window->maximized_horizontally && !unmaximize_horizontally;
02643       window->maximized_vertically = 
02644         window->maximized_vertically   && !unmaximize_vertically;
02645 
02646       /* Unmaximize to the saved_rect position in the direction(s)
02647        * being unmaximized.
02648        */
02649       meta_window_get_client_root_coords (window, &target_rect);
02650       if (unmaximize_horizontally)
02651         {
02652           target_rect.x     = window->saved_rect.x;
02653           target_rect.width = window->saved_rect.width;
02654         }
02655       if (unmaximize_vertically)
02656         {
02657           target_rect.y      = window->saved_rect.y;
02658           target_rect.height = window->saved_rect.height;
02659         }
02660 
02661       /* Window's size hints may have changed while maximized, making
02662        * saved_rect invalid.  #329152
02663        */
02664       ensure_size_hints_satisfied (&target_rect, &window->size_hints);
02665 
02666       /* When we unmaximize, if we're doing a mouse move also we could
02667        * get the window suddenly jumping to the upper left corner of
02668        * the workspace, since that's where it was when the grab op
02669        * started.  So we need to update the grab state.
02670        */
02671       if (meta_grab_op_is_moving (window->display->grab_op) &&
02672           window->display->grab_window == window)
02673         {
02674           window->display->grab_anchor_window_pos = target_rect;
02675         }
02676 
02677       meta_window_move_resize (window,
02678                                FALSE,
02679                                target_rect.x,
02680                                target_rect.y,
02681                                target_rect.width,
02682                                target_rect.height);
02683 
02684       if (window->display->grab_wireframe_active)
02685         {
02686           window->display->grab_wireframe_rect = target_rect;
02687         }
02688 
02689       recalc_window_features (window);
02690       set_net_wm_state (window);
02691     }
02692 }
02693 
02694 void
02695 meta_window_make_above (MetaWindow  *window)
02696 {
02697   window->wm_state_above = TRUE;
02698   meta_window_update_layer (window);
02699   meta_window_raise (window);
02700   set_net_wm_state (window);
02701 }
02702 
02703 void
02704 meta_window_unmake_above (MetaWindow  *window)
02705 {
02706   window->wm_state_above = FALSE;
02707   meta_window_raise (window);
02708   meta_window_update_layer (window);
02709   set_net_wm_state (window);
02710 }
02711 
02712 void
02713 meta_window_make_fullscreen_internal (MetaWindow  *window)
02714 {
02715   if (!window->fullscreen)
02716     {
02717       meta_topic (META_DEBUG_WINDOW_OPS,
02718                   "Fullscreening %s\n", window->desc);
02719 
02720       if (window->shaded)
02721         {
02722           /* Shading sucks anyway; I'm not adding a timestamp argument
02723            * to this function just for this niche usage & corner case.
02724            */
02725           guint32 timestamp = 
02726             meta_display_get_current_time_roundtrip (window->display);
02727           meta_window_unshade (window, timestamp);
02728         }
02729 
02730       meta_window_save_rect (window);
02731       
02732       window->fullscreen = TRUE;
02733 
02734       meta_stack_freeze (window->screen->stack);
02735       meta_window_update_layer (window);
02736       
02737       meta_window_raise (window);
02738       meta_stack_thaw (window->screen->stack);
02739       
02740       recalc_window_features (window);
02741       set_net_wm_state (window);
02742     }
02743 }
02744 
02745 void
02746 meta_window_make_fullscreen (MetaWindow  *window)
02747 {
02748   if (!window->fullscreen)
02749     {
02750       meta_window_make_fullscreen_internal (window);
02751       /* move_resize with new constraints
02752        */
02753       meta_window_queue(window, META_QUEUE_MOVE_RESIZE);
02754     }
02755 }
02756 
02757 void
02758 meta_window_unmake_fullscreen (MetaWindow  *window)
02759 {
02760   if (window->fullscreen)
02761     {
02762       MetaRectangle target_rect;
02763 
02764       meta_topic (META_DEBUG_WINDOW_OPS,
02765                   "Unfullscreening %s\n", window->desc);
02766       
02767       window->fullscreen = FALSE;
02768       target_rect = window->saved_rect;
02769 
02770       /* Window's size hints may have changed while maximized, making
02771        * saved_rect invalid.  #329152
02772        */
02773       ensure_size_hints_satisfied (&target_rect, &window->size_hints);
02774 
02775       meta_window_move_resize (window,
02776                                FALSE,
02777                                target_rect.x,
02778                                target_rect.y,
02779                                target_rect.width,
02780                                target_rect.height);
02781 
02782       meta_window_update_layer (window);
02783       
02784       recalc_window_features (window);
02785       set_net_wm_state (window);
02786     }
02787 }
02788 
02789 void
02790 meta_window_shade (MetaWindow  *window,
02791                    guint32      timestamp)
02792 {
02793   meta_topic (META_DEBUG_WINDOW_OPS,
02794               "Shading %s\n", window->desc);
02795   if (!window->shaded)
02796     {      
02797       window->shaded = TRUE;
02798 
02799       meta_window_queue(window, META_QUEUE_MOVE_RESIZE | META_QUEUE_CALC_SHOWING);
02800 
02801       /* After queuing the calc showing, since _focus flushes it,
02802        * and we need to focus the frame
02803        */
02804       meta_topic (META_DEBUG_FOCUS,
02805                   "Re-focusing window %s after shading it\n",
02806                   window->desc);
02807       meta_window_focus (window, timestamp);
02808       
02809       set_net_wm_state (window);
02810     }
02811 }
02812 
02813 void
02814 meta_window_unshade (MetaWindow  *window,
02815                      guint32      timestamp)
02816 {
02817   meta_topic (META_DEBUG_WINDOW_OPS,
02818               "Unshading %s\n", window->desc);
02819   if (window->shaded)
02820     {
02821       window->shaded = FALSE;
02822       meta_window_queue(window, META_QUEUE_MOVE_RESIZE | META_QUEUE_CALC_SHOWING);
02823 
02824       /* focus the window */
02825       meta_topic (META_DEBUG_FOCUS,
02826                   "Focusing window %s after unshading it\n",
02827                   window->desc);
02828       meta_window_focus (window, timestamp);
02829 
02830       set_net_wm_state (window);
02831     }
02832 }
02833 
02834 static gboolean
02835 unminimize_func (MetaWindow *window,
02836                  void       *data)
02837 {
02838   meta_window_unminimize (window);
02839   return TRUE;
02840 }
02841 
02842 static void
02843 unminimize_window_and_all_transient_parents (MetaWindow *window)
02844 {
02845   meta_window_unminimize (window);
02846   meta_window_foreach_ancestor (window, unminimize_func, NULL);
02847 }
02848 
02849 static void
02850 window_activate (MetaWindow     *window,
02851                  guint32         timestamp,
02852                  MetaClientType  source_indication,
02853                  MetaWorkspace  *workspace)
02854 {
02855   gboolean can_ignore_outdated_timestamps;
02856   meta_topic (META_DEBUG_FOCUS,
02857               "_NET_ACTIVE_WINDOW message sent for %s at time %u "
02858               "by client type %u.\n",
02859               window->desc, timestamp, source_indication);
02860 
02861   /* Older EWMH spec didn't specify a timestamp; we decide to honor these only
02862    * if the app specifies that it is a pager.
02863    *
02864    * Update: Unconditionally honor 0 timestamps for now; we'll fight
02865    * that battle later.  Just remove the "FALSE &&" in order to only
02866    * honor 0 timestamps for pagers.
02867    */
02868   can_ignore_outdated_timestamps =
02869     (timestamp != 0 || (FALSE && source_indication != META_CLIENT_TYPE_PAGER));
02870   if (XSERVER_TIME_IS_BEFORE (timestamp, window->display->last_user_time) &&
02871       can_ignore_outdated_timestamps)
02872     {
02873       meta_topic (META_DEBUG_FOCUS,
02874                   "last_user_time (%u) is more recent; ignoring "
02875                   " _NET_ACTIVE_WINDOW message.\n",
02876                   window->display->last_user_time);
02877       meta_window_set_demands_attention(window);
02878       return;
02879     }
02880 
02881   /* For those stupid pagers, get a valid timestamp and show a warning */  
02882   if (timestamp == 0)
02883     {
02884       meta_warning ("meta_window_activate called by a pager with a 0 timestamp; "
02885                     "the pager needs to be fixed.\n");
02886       timestamp = meta_display_get_current_time_roundtrip (window->display);
02887     }
02888 
02889   meta_window_set_user_time (window, timestamp);
02890 
02891   /* disable show desktop mode unless we're a desktop component */
02892   maybe_leave_show_desktop_mode (window);
02893  
02894   /* Get window on current or given workspace */
02895   if (workspace == NULL)
02896     workspace = window->screen->active_workspace;
02897   if (!meta_window_located_on_workspace (window, workspace))
02898     meta_window_change_workspace (window, workspace);
02899   
02900   if (window->shaded)
02901     meta_window_unshade (window, timestamp);
02902 
02903   unminimize_window_and_all_transient_parents (window);
02904 
02905   if (meta_prefs_get_raise_on_click () ||
02906       source_indication == META_CLIENT_TYPE_PAGER)
02907     meta_window_raise (window);
02908 
02909   meta_topic (META_DEBUG_FOCUS,
02910               "Focusing window %s due to activation\n",
02911               window->desc);
02912   meta_window_focus (window, timestamp);
02913 }
02914 
02915 /* This function exists since most of the functionality in window_activate
02916  * is useful for Metacity, but Metacity shouldn't need to specify a client
02917  * type for itself.  ;-)
02918  */
02919 void
02920 meta_window_activate (MetaWindow     *window,
02921                       guint32         timestamp)
02922 {
02923   /* We're not really a pager, but the behavior we want is the same as if
02924    * we were such.  If we change the pager behavior later, we could revisit
02925    * this and just add extra flags to window_activate.
02926    */
02927   window_activate (window, timestamp, META_CLIENT_TYPE_PAGER, NULL);
02928 }
02929 
02930 void
02931 meta_window_activate_with_workspace (MetaWindow     *window,
02932                                      guint32         timestamp,
02933                                      MetaWorkspace  *workspace)
02934 {
02935   /* We're not really a pager, but the behavior we want is the same as if
02936    * we were such.  If we change the pager behavior later, we could revisit
02937    * this and just add extra flags to window_activate.
02938    */
02939   window_activate (window, timestamp, META_CLIENT_TYPE_APPLICATION, workspace);
02940 }
02941 
02942 /* Manually fix all the weirdness explained in the big comment at the
02943  * beginning of meta_window_move_resize_internal() giving positions
02944  * expected by meta_window_constrain (i.e. positions & sizes of the
02945  * internal or client window).
02946  */
02947 static void
02948 adjust_for_gravity (MetaWindow        *window,
02949                     MetaFrameGeometry *fgeom,
02950                     gboolean           coords_assume_border,
02951                     int                gravity,
02952                     MetaRectangle     *rect)
02953 {
02954   int ref_x, ref_y;
02955   int bw;
02956   int child_x, child_y;
02957   int frame_width, frame_height;
02958   
02959   if (coords_assume_border)
02960     bw = window->border_width;
02961   else
02962     bw = 0;
02963 
02964   if (fgeom)
02965     {
02966       child_x = fgeom->left_width;
02967       child_y = fgeom->top_height;
02968       frame_width = child_x + rect->width + fgeom->right_width;
02969       frame_height = child_y + rect->height + fgeom->bottom_height;
02970     }
02971   else
02972     {
02973       child_x = 0;
02974       child_y = 0;
02975       frame_width = rect->width;
02976       frame_height = rect->height;
02977     }
02978   
02979   /* We're computing position to pass to window_move, which is
02980    * the position of the client window (StaticGravity basically)
02981    *
02982    * (see WM spec description of gravity computation, but note that
02983    * their formulas assume we're honoring the border width, rather
02984    * than compensating for having turned it off)
02985    */
02986   switch (gravity)
02987     {
02988     case NorthWestGravity:
02989       ref_x = rect->x;
02990       ref_y = rect->y;
02991       break;
02992     case NorthGravity:
02993       ref_x = rect->x + rect->width / 2 + bw;
02994       ref_y = rect->y;
02995       break;
02996     case NorthEastGravity:
02997       ref_x = rect->x + rect->width + bw * 2;
02998       ref_y = rect->y;
02999       break;
03000     case WestGravity:
03001       ref_x = rect->x;
03002       ref_y = rect->y + rect->height / 2 + bw;
03003       break;
03004     case CenterGravity:
03005       ref_x = rect->x + rect->width / 2 + bw;
03006       ref_y = rect->y + rect->height / 2 + bw;
03007       break;
03008     case EastGravity:
03009       ref_x = rect->x + rect->width + bw * 2;
03010       ref_y = rect->y + rect->height / 2 + bw;
03011       break;
03012     case SouthWestGravity:
03013       ref_x = rect->x;
03014       ref_y = rect->y + rect->height + bw * 2;
03015       break;
03016     case SouthGravity:
03017       ref_x = rect->x + rect->width / 2 + bw;
03018       ref_y = rect->y + rect->height + bw * 2;
03019       break;
03020     case SouthEastGravity:
03021       ref_x = rect->x + rect->width + bw * 2;
03022       ref_y = rect->y + rect->height + bw * 2;
03023       break;
03024     case StaticGravity:
03025     default:
03026       ref_x = rect->x;
03027       ref_y = rect->y;
03028       break;
03029     }
03030 
03031   switch (gravity)
03032     {
03033     case NorthWestGravity:
03034       rect->x = ref_x + child_x;
03035       rect->y = ref_y + child_y;
03036       break;
03037     case NorthGravity:
03038       rect->x = ref_x - frame_width / 2 + child_x;
03039       rect->y = ref_y + child_y;
03040       break;
03041     case NorthEastGravity:
03042       rect->x = ref_x - frame_width + child_x;
03043       rect->y = ref_y + child_y;
03044       break;
03045     case WestGravity:
03046       rect->x = ref_x + child_x;
03047       rect->y = ref_y - frame_height / 2 + child_y;
03048       break;
03049     case CenterGravity:
03050       rect->x = ref_x - frame_width / 2 + child_x;
03051       rect->y = ref_y - frame_height / 2 + child_y;
03052       break;
03053     case EastGravity:
03054       rect->x = ref_x - frame_width + child_x;
03055       rect->y = ref_y - frame_height / 2 + child_y;
03056       break;
03057     case SouthWestGravity:
03058       rect->x = ref_x + child_x;
03059       rect->y = ref_y - frame_height + child_y;
03060       break;
03061     case SouthGravity:
03062       rect->x = ref_x - frame_width / 2 + child_x;
03063       rect->y = ref_y - frame_height + child_y;
03064       break;
03065     case SouthEastGravity:
03066       rect->x = ref_x - frame_width + child_x;
03067       rect->y = ref_y - frame_height + child_y;
03068       break;
03069     case StaticGravity:
03070     default:
03071       rect->x = ref_x;
03072       rect->y = ref_y;
03073       break;
03074     }
03075 }
03076 
03077 static gboolean
03078 static_gravity_works (MetaDisplay *display)
03079 {
03080   return display->static_gravity_works;
03081 }
03082 
03083 #ifdef HAVE_XSYNC
03084 static void
03085 send_sync_request (MetaWindow *window)
03086 {
03087   XSyncValue value;
03088   XClientMessageEvent ev;
03089 
03090   window->sync_request_serial++;
03091 
03092   XSyncIntToValue (&value, window->sync_request_serial);
03093   
03094   ev.type = ClientMessage;
03095   ev.window = window->xwindow;
03096   ev.message_type = window->display->atom_WM_PROTOCOLS;
03097   ev.format = 32;
03098   ev.data.l[0] = window->display->atom__NET_WM_SYNC_REQUEST;
03099   /* FIXME: meta_display_get_current_time() is bad, but since calls
03100    * come from meta_window_move_resize_internal (which in turn come
03101    * from all over), I'm not sure what we can do to fix it.  Do we
03102    * want to use _roundtrip, though?
03103    */
03104   ev.data.l[1] = meta_display_get_current_time (window->display);
03105   ev.data.l[2] = XSyncValueLow32 (value);
03106   ev.data.l[3] = XSyncValueHigh32 (value);
03107 
03108   /* We don't need to trap errors here as we are already
03109    * inside an error_trap_push()/pop() pair.
03110    */
03111   XSendEvent (window->display->xdisplay,
03112               window->xwindow, False, 0, (XEvent*) &ev);
03113 
03114   g_get_current_time (&window->sync_request_time);
03115 }
03116 #endif
03117 
03118 static void
03119 meta_window_move_resize_internal (MetaWindow          *window,
03120                                   MetaMoveResizeFlags  flags,
03121                                   int                  gravity,
03122                                   int                  root_x_nw,
03123                                   int                  root_y_nw,
03124                                   int                  w,
03125                                   int                  h)
03126 {
03127   /* meta_window_move_resize_internal gets called with very different
03128    * meanings for root_x_nw and root_y_nw.  w & h are always the area
03129    * of the inner or client window (i.e. excluding the frame) and
03130    * gravity is the relevant gravity associated with the request (note
03131    * that gravity is ignored for move-only operations unless its
03132    * e.g. a configure request).  The location is different for
03133    * different cases because of how this function gets called; note
03134    * that in all cases what we want to find out is the upper left
03135    * corner of the position of the inner window:
03136    *
03137    *   Case | Called from (flags; gravity)
03138    *   -----+-----------------------------------------------
03139    *    1   | A resize only ConfigureRequest
03140    *    1   | meta_window_resize
03141    *    1   | meta_window_resize_with_gravity
03142    *    2   | New window
03143    *    2   | Session restore
03144    *    2   | A not-resize-only ConfigureRequest/net_moveresize_window request
03145    *    3   | meta_window_move
03146    *    3   | meta_window_move_resize
03147    *
03148    * For each of the cases, root_x_nw and root_y_nw must be treated as follows:
03149    *
03150    *   (1) They should be entirely ignored; instead the previous position
03151    *       and size of the window should be resized according to the given
03152    *       gravity in order to determine the new position of the window.
03153    *   (2) Needs to be fixed up by adjust_for_gravity() as these
03154    *       coordinates are relative to some corner or side of the outer
03155    *       window (except for the case of StaticGravity) and we want to
03156    *       know the location of the upper left corner of the inner window.
03157    *   (3) These values are already the desired positon of the NW corner
03158    *       of the inner window
03159    */
03160   XWindowChanges values;
03161   unsigned int mask;
03162   gboolean need_configure_notify;
03163   MetaFrameGeometry fgeom;
03164   gboolean need_move_client = FALSE;
03165   gboolean need_move_frame = FALSE;
03166   gboolean need_resize_client = FALSE;
03167   gboolean need_resize_frame = FALSE;
03168   int frame_size_dx;
03169   int frame_size_dy;
03170   int size_dx;
03171   int size_dy;
03172   gboolean is_configure_request;
03173   gboolean do_gravity_adjust;
03174   gboolean is_user_action;
03175   gboolean configure_frame_first;
03176   gboolean use_static_gravity;
03177   /* used for the configure request, but may not be final
03178    * destination due to StaticGravity etc.
03179    */
03180   int client_move_x;
03181   int client_move_y;
03182   MetaRectangle new_rect;
03183   MetaRectangle old_rect;
03184   
03185   is_configure_request = (flags & META_IS_CONFIGURE_REQUEST) != 0;
03186   do_gravity_adjust = (flags & META_DO_GRAVITY_ADJUST) != 0;
03187   is_user_action = (flags & META_IS_USER_ACTION) != 0;
03188 
03189   /* The action has to be a move or a resize or both... */
03190   g_assert (flags & (META_IS_MOVE_ACTION | META_IS_RESIZE_ACTION));
03191   
03192   /* We don't need it in the idle queue anymore. */
03193   meta_window_unqueue (window, META_QUEUE_MOVE_RESIZE);
03194 
03195   meta_window_get_client_root_coords (window, &old_rect);
03196   
03197   meta_topic (META_DEBUG_GEOMETRY,
03198               "Move/resize %s to %d,%d %dx%d%s%s from %d,%d %dx%d\n",
03199               window->desc, root_x_nw, root_y_nw, w, h,
03200               is_configure_request ? " (configure request)" : "",
03201               is_user_action ? " (user move/resize)" : "",
03202               old_rect.x, old_rect.y, old_rect.width, old_rect.height);
03203   
03204   if (window->frame)
03205     meta_frame_calc_geometry (window->frame,
03206                               &fgeom);
03207 
03208   new_rect.x = root_x_nw;
03209   new_rect.y = root_y_nw;
03210   new_rect.width  = w;
03211   new_rect.height = h;
03212 
03213   /* If this is a resize only, the position should be ignored and
03214    * instead obtained by resizing the old rectangle according to the
03215    * relevant gravity.
03216    */
03217   if ((flags & (META_IS_MOVE_ACTION | META_IS_RESIZE_ACTION)) == 
03218       META_IS_RESIZE_ACTION)
03219     { 
03220       meta_rectangle_resize_with_gravity (&old_rect,
03221                                           &new_rect,
03222                                           gravity,
03223                                           new_rect.width,
03224                                           new_rect.height);
03225 
03226       meta_topic (META_DEBUG_GEOMETRY,
03227                   "Compensated for gravity in resize action; new pos %d,%d\n",
03228                   new_rect.x, new_rect.y);
03229     }
03230   else if (is_configure_request || do_gravity_adjust)
03231     {      
03232       adjust_for_gravity (window,
03233                           window->frame ? &fgeom : NULL,
03234                           /* configure request coords assume
03235                            * the border width existed
03236                            */
03237                           is_configure_request,
03238                           gravity,
03239                           &new_rect);
03240 
03241       meta_topic (META_DEBUG_GEOMETRY,
03242                   "Compensated for configure_request/do_gravity_adjust needing "
03243                   "weird positioning; new pos %d,%d\n",
03244                   new_rect.x, new_rect.y);
03245     }
03246 
03247   meta_window_constrain (window,
03248                          window->frame ? &fgeom : NULL,
03249                          flags,
03250                          gravity,
03251                          &old_rect,
03252                          &new_rect);
03253 
03254   w = new_rect.width;
03255   h = new_rect.height;
03256   root_x_nw = new_rect.x;
03257   root_y_nw = new_rect.y;
03258   
03259   if (w != window->rect.width ||
03260       h != window->rect.height)
03261     need_resize_client = TRUE;  
03262   
03263   window->rect.width = w;
03264   window->rect.height = h;
03265   
03266   if (window->frame)
03267     {
03268       int new_w, new_h;
03269 
03270       new_w = window->rect.width + fgeom.left_width + fgeom.right_width;
03271 
03272       if (window->shaded)
03273         new_h = fgeom.top_height;
03274       else
03275         new_h = window->rect.height + fgeom.top_height + fgeom.bottom_height;
03276 
03277       frame_size_dx = new_w - window->frame->rect.width;
03278       frame_size_dy = new_h - window->frame->rect.height;
03279 
03280       need_resize_frame = (frame_size_dx != 0 || frame_size_dy != 0);
03281       
03282       window->frame->rect.width = new_w;
03283       window->frame->rect.height = new_h;
03284 
03285       meta_topic (META_DEBUG_GEOMETRY,
03286                   "Calculated frame size %dx%d\n",
03287                   window->frame->rect.width,
03288                   window->frame->rect.height);
03289     }
03290   else
03291     {
03292       frame_size_dx = 0;
03293       frame_size_dy = 0;
03294     }
03295 
03296   /* For nice effect, when growing the window we want to move/resize
03297    * the frame first, when shrinking the window we want to move/resize
03298    * the client first. If we grow one way and shrink the other,
03299    * see which way we're moving "more"
03300    *
03301    * Mail from Owen subject "Suggestion: Gravity and resizing from the left"
03302    * http://mail.gnome.org/archives/wm-spec-list/1999-November/msg00088.html
03303    *
03304    * An annoying fact you need to know in this code is that StaticGravity
03305    * does nothing if you _only_ resize or _only_ move the frame;
03306    * it must move _and_ resize, otherwise you get NorthWestGravity
03307    * behavior. The move and resize must actually occur, it is not
03308    * enough to set CWX | CWWidth but pass in the current size/pos.
03309    */
03310       
03311   if (window->frame)
03312     {
03313       int new_x, new_y;
03314       int frame_pos_dx, frame_pos_dy;
03315       
03316       /* Compute new frame coords */
03317       new_x = root_x_nw - fgeom.left_width;
03318       new_y = root_y_nw - fgeom.top_height;
03319 
03320       frame_pos_dx = new_x - window->frame->rect.x;
03321       frame_pos_dy = new_y - window->frame->rect.y;
03322 
03323       need_move_frame = (frame_pos_dx != 0 || frame_pos_dy != 0);
03324       
03325       window->frame->rect.x = new_x;
03326       window->frame->rect.y = new_y;      
03327 
03328       /* If frame will both move and resize, then StaticGravity
03329        * on the child window will kick in and implicitly move
03330        * the child with respect to the frame. The implicit
03331        * move will keep the child in the same place with
03332        * respect to the root window. If frame only moves
03333        * or only resizes, then the child will just move along
03334        * with the frame.
03335        */
03336 
03337       /* window->rect.x, window->rect.y are relative to frame,
03338        * remember they are the server coords
03339        */
03340           
03341       new_x = fgeom.left_width;
03342       new_y = fgeom.top_height;
03343 
03344       if (need_resize_frame && need_move_frame &&
03345           static_gravity_works (window->display))
03346         {
03347           /* static gravity kicks in because frame
03348            * is both moved and resized
03349            */
03350           /* when we move the frame by frame_pos_dx, frame_pos_dy the
03351            * client will implicitly move relative to frame by the
03352            * inverse delta.
03353            * 
03354            * When moving client then frame, we move the client by the
03355            * frame delta, to be canceled out by the implicit move by
03356            * the inverse frame delta, resulting in a client at new_x,
03357            * new_y.
03358            *
03359            * When moving frame then client, we move the client
03360            * by the same delta as the frame, because the client
03361            * was "left behind" by the frame - resulting in a client
03362            * at new_x, new_y.
03363            *
03364            * In both cases we need to move the client window
03365            * in all cases where we had to move the frame window.
03366            */
03367           
03368           client_move_x = new_x + frame_pos_dx;
03369           client_move_y = new_y + frame_pos_dy;
03370 
03371           if (need_move_frame)
03372             need_move_client = TRUE;
03373 
03374           use_static_gravity = TRUE;
03375         }
03376       else
03377         {
03378           client_move_x = new_x;
03379           client_move_y = new_y;
03380 
03381           if (client_move_x != window->rect.x ||
03382               client_move_y != window->rect.y)
03383             need_move_client = TRUE;
03384 
03385           use_static_gravity = FALSE;
03386         }
03387 
03388       /* This is the final target position, but not necessarily what
03389        * we pass to XConfigureWindow, due to StaticGravity implicit
03390        * movement.
03391        */      
03392       window->rect.x = new_x;
03393       window->rect.y = new_y;
03394     }
03395   else
03396     {
03397       if (root_x_nw != window->rect.x ||
03398           root_y_nw != window->rect.y)
03399         need_move_client = TRUE;
03400       
03401       window->rect.x = root_x_nw;
03402       window->rect.y = root_y_nw;
03403 
03404       client_move_x = window->rect.x;
03405       client_move_y = window->rect.y;
03406 
03407       use_static_gravity = FALSE;
03408     }
03409 
03410   /* If frame extents have changed, fill in other frame fields and
03411      change frame's extents property. */
03412   if (window->frame &&
03413       (window->frame->child_x != fgeom.left_width ||
03414        window->frame->child_y != fgeom.top_height ||
03415        window->frame->right_width != fgeom.right_width ||
03416        window->frame->bottom_height != fgeom.bottom_height))
03417     {
03418       window->frame->child_x = fgeom.left_width;
03419       window->frame->child_y = fgeom.top_height;
03420       window->frame->right_width = fgeom.right_width;
03421       window->frame->bottom_height = fgeom.bottom_height;
03422 
03423       update_net_frame_extents (window);
03424     }
03425 
03426   /* See ICCCM 4.1.5 for when to send ConfigureNotify */
03427   
03428   need_configure_notify = FALSE;
03429   
03430   /* If this is a configure request and we change nothing, then we
03431    * must send configure notify.
03432    */
03433   if  (is_configure_request &&
03434        !(need_move_client || need_move_frame ||
03435          need_resize_client || need_resize_frame ||
03436          window->border_width != 0))
03437     need_configure_notify = TRUE;
03438 
03439   /* We must send configure notify if we move but don't resize, since
03440    * the client window may not get a real event
03441    */
03442   if ((need_move_client || need_move_frame) &&
03443       !(need_resize_client || need_resize_frame))
03444     need_configure_notify = TRUE;
03445 
03446   /* MapRequest events with a PPosition or UPosition hint with a frame
03447    * are moved by metacity without resizing; send a configure notify
03448    * in such cases.  See #322840.  (Note that window->constructing is
03449    * only true iff this call is due to a MapRequest, and when
03450    * PPosition/UPosition hints aren't set, metacity seems to send a
03451    * ConfigureNotify anyway due to the above code.)
03452    */
03453   if (window->constructing && window->frame &&
03454       ((window->size_hints.flags & PPosition) ||
03455        (window->size_hints.flags & USPosition)))
03456     need_configure_notify = TRUE;
03457   
03458   /* The rest of this function syncs our new size/pos with X as
03459    * efficiently as possible
03460    */
03461 
03462   /* configure frame first if we grow more than we shrink
03463    */
03464   size_dx = w - window->rect.width;
03465   size_dy = h - window->rect.height;
03466 
03467   configure_frame_first = (size_dx + size_dy >= 0);
03468 
03469   if (use_static_gravity)
03470     meta_window_set_gravity (window, StaticGravity);  
03471   
03472   if (configure_frame_first && window->frame)
03473     meta_frame_sync_to_window (window->frame,
03474                                gravity,
03475                                need_move_frame, need_resize_frame);
03476 
03477   values.border_width = 0;
03478   values.x = client_move_x;
03479   values.y = client_move_y;
03480   values.width = window->rect.width;
03481   values.height = window->rect.height;
03482   
03483   mask = 0;
03484   if (is_configure_request && window->border_width != 0)
03485     mask |= CWBorderWidth; /* must force to 0 */
03486   if (need_move_client)
03487     mask |= (CWX | CWY);
03488   if (need_resize_client)
03489     mask |= (CWWidth | CWHeight);
03490 
03491   if (mask != 0)
03492     {
03493       {
03494         int newx, newy;
03495         meta_window_get_position (window, &newx, &newy);
03496         meta_topic (META_DEBUG_GEOMETRY,
03497                     "Syncing new client geometry %d,%d %dx%d, border: %s pos: %s size: %s\n",
03498                     newx, newy,
03499                     window->rect.width, window->rect.height,
03500                     mask & CWBorderWidth ? "true" : "false",
03501                     need_move_client ? "true" : "false",
03502                     need_resize_client ? "true" : "false");
03503       }
03504       
03505       meta_error_trap_push (window->display);
03506 
03507 #ifdef HAVE_XSYNC
03508       if (window->sync_request_counter != None &&
03509           window->display->grab_sync_request_alarm != None &&
03510           window->sync_request_time.tv_usec == 0 &&
03511           window->sync_request_time.tv_sec == 0)
03512         {
03513           /* turn off updating */       
03514           if (window->display->compositor)
03515             meta_compositor_set_updates (window->display->compositor, window, FALSE);
03516 
03517           send_sync_request (window);
03518         }
03519 #endif
03520 
03521       XConfigureWindow (window->display->xdisplay,
03522                         window->xwindow,
03523                         mask,
03524                         &values);
03525       
03526       meta_error_trap_pop (window->display, FALSE);
03527     }
03528 
03529   if (!configure_frame_first && window->frame)
03530     meta_frame_sync_to_window (window->frame,
03531                                gravity,
03532                                need_move_frame, need_resize_frame);  
03533 
03534   /* Put gravity back to be nice to lesser window managers */
03535   if (use_static_gravity)
03536     meta_window_set_gravity (window, NorthWestGravity);  
03537   
03538   if (need_configure_notify)
03539     send_configure_notify (window);
03540 
03541   if (!window->placed)
03542     force_save_user_window_placement (window);
03543   else if (is_user_action)
03544     save_user_window_placement (window);
03545 
03546   if (need_move_frame || need_resize_frame ||
03547       need_move_client || need_resize_client)
03548     {
03549       int newx, newy;
03550       meta_window_get_position (window, &newx, &newy);
03551       meta_topic (META_DEBUG_GEOMETRY,
03552                   "New size/position %d,%d %dx%d (user %d,%d %dx%d)\n",
03553                   newx, newy, window->rect.width, window->rect.height,
03554                   window->user_rect.x, window->user_rect.y,
03555                   window->user_rect.width, window->user_rect.height);
03556     }
03557   else
03558     {
03559       meta_topic (META_DEBUG_GEOMETRY, "Size/position not modified\n");
03560     }
03561   
03562   if (window->display->grab_wireframe_active)
03563     meta_window_update_wireframe (window, root_x_nw, root_y_nw, w, h);
03564   else
03565     meta_window_refresh_resize_popup (window);
03566   
03567   /* Invariants leaving this function are:
03568    *   a) window->rect and frame->rect reflect the actual
03569    *      server-side size/pos of window->xwindow and frame->xwindow
03570    *   b) all constraints are obeyed by window->rect and frame->rect
03571    */
03572 }
03573 
03574 void
03575 meta_window_resize (MetaWindow  *window,
03576                     gboolean     user_op,
03577                     int          w,
03578                     int          h)
03579 {
03580   int x, y;
03581   MetaMoveResizeFlags flags;
03582 
03583   meta_window_get_position (window, &x, &y);
03584   
03585   flags = (user_op ? META_IS_USER_ACTION : 0) | META_IS_RESIZE_ACTION;
03586   meta_window_move_resize_internal (window,
03587                                     flags,
03588                                     NorthWestGravity,
03589                                     x, y, w, h);
03590 }
03591 
03592 void
03593 meta_window_move (MetaWindow  *window,
03594                   gboolean     user_op,
03595                   int          root_x_nw,
03596                   int          root_y_nw)
03597 {
03598   MetaMoveResizeFlags flags = 
03599     (user_op ? META_IS_USER_ACTION : 0) | META_IS_MOVE_ACTION;
03600   meta_window_move_resize_internal (window,
03601                                     flags,
03602                                     NorthWestGravity,
03603                                     root_x_nw, root_y_nw,
03604                                     window->rect.width,
03605                                     window->rect.height);
03606 }
03607 
03608 void
03609 meta_window_move_resize (MetaWindow  *window,
03610                          gboolean     user_op,
03611                          int          root_x_nw,
03612                          int          root_y_nw,
03613                          int          w,
03614                          int          h)
03615 {
03616   MetaMoveResizeFlags flags = 
03617     (user_op ? META_IS_USER_ACTION : 0) | 
03618     META_IS_MOVE_ACTION | META_IS_RESIZE_ACTION;
03619   meta_window_move_resize_internal (window,
03620                                     flags,
03621                                     NorthWestGravity,
03622                                     root_x_nw, root_y_nw,
03623                                     w, h);
03624 }
03625 
03626 void
03627 meta_window_resize_with_gravity (MetaWindow *window,
03628                                  gboolean     user_op,
03629                                  int          w,
03630                                  int          h,
03631                                  int          gravity)
03632 {
03633   int x, y;
03634   MetaMoveResizeFlags flags;
03635 
03636   meta_window_get_position (window, &x, &y);
03637   
03638   flags = (user_op ? META_IS_USER_ACTION : 0) | META_IS_RESIZE_ACTION;
03639   meta_window_move_resize_internal (window,
03640                                     flags,
03641                                     gravity,
03642                                     x, y, w, h);
03643 }
03644 
03645 static void
03646 meta_window_move_resize_now (MetaWindow  *window)
03647 {
03648   /* If constraints have changed then we want to snap back to wherever
03649    * the user had the window.  We use user_rect for this reason.  See
03650    * also bug 426519 comment 3.
03651    */
03652   meta_window_move_resize (window, FALSE,
03653                            window->user_rect.x,
03654                            window->user_rect.y,
03655                            window->user_rect.width,
03656                            window->user_rect.height);
03657 }
03658 
03659 static gboolean
03660 idle_move_resize (gpointer data)
03661 {
03662   GSList *tmp;
03663   GSList *copy;
03664   guint queue_index = GPOINTER_TO_INT (data);
03665 
03666   meta_topic (META_DEBUG_GEOMETRY, "Clearing the move_resize queue\n");
03667 
03668   /* Work with a copy, for reentrancy. The allowed reentrancy isn't
03669    * complete; destroying a window while we're in here would result in
03670    * badness. But it's OK to queue/unqueue move_resizes.
03671    */
03672   copy = g_slist_copy (queue_pending[queue_index]);
03673   g_slist_free (queue_pending[queue_index]);
03674   queue_pending[queue_index] = NULL;
03675   queue_idle[queue_index] = 0;
03676 
03677   destroying_windows_disallowed += 1;
03678   
03679   tmp = copy;
03680   while (tmp != NULL)
03681     {
03682       MetaWindow *window;
03683 
03684       window = tmp->data;
03685 
03686       /* As a side effect, sets window->move_resize_queued = FALSE */
03687       meta_window_move_resize_now (window); 
03688       
03689       tmp = tmp->next;
03690     }
03691 
03692   g_slist_free (copy);
03693 
03694   destroying_windows_disallowed -= 1;
03695   
03696   return FALSE;
03697 }
03698 
03699 void
03700 meta_window_get_position (MetaWindow  *window,
03701                           int         *x,
03702                           int         *y)
03703 {
03704   if (window->frame)
03705     {
03706       if (x)
03707         *x = window->frame->rect.x + window->frame->child_x;
03708       if (y)
03709         *y = window->frame->rect.y + window->frame->child_y;
03710     }
03711   else
03712     {
03713       if (x)
03714         *x = window->rect.x;
03715       if (y)
03716         *y = window->rect.y;
03717     }
03718 }
03719 
03720 void
03721 meta_window_get_client_root_coords (MetaWindow    *window,
03722                                     MetaRectangle *rect)
03723 {
03724   meta_window_get_position (window, &rect->x, &rect->y);
03725   rect->width  = window->rect.width;
03726   rect->height = window->rect.height;
03727 }
03728 
03729 void
03730 meta_window_get_gravity_position (MetaWindow  *window,
03731                                   int          gravity,
03732                                   int         *root_x,
03733                                   int         *root_y)
03734 {
03735   MetaRectangle frame_extents;
03736   int w, h;
03737   int x, y;
03738   
03739   w = window->rect.width;
03740   h = window->rect.height;
03741 
03742   if (gravity == StaticGravity)
03743     {
03744       frame_extents = window->rect;
03745       if (window->frame)
03746         {
03747           frame_extents.x = window->frame->rect.x + window->frame->child_x;
03748           frame_extents.y = window->frame->rect.y + window->frame->child_y;
03749         }
03750     }
03751   else
03752     {
03753       if (window->frame == NULL)
03754         frame_extents = window->rect;
03755       else
03756         frame_extents = window->frame->rect;
03757     }
03758 
03759   x = frame_extents.x;
03760   y = frame_extents.y;
03761   
03762   switch (gravity)
03763     {
03764     case NorthGravity:
03765     case CenterGravity:
03766     case SouthGravity:
03767       /* Find center of frame. */
03768       x += frame_extents.width / 2;
03769       /* Center client window on that point. */
03770       x -= w / 2;
03771       break;
03772       
03773     case SouthEastGravity:
03774     case EastGravity:
03775     case NorthEastGravity:
03776       /* Find right edge of frame */
03777       x += frame_extents.width;
03778       /* Align left edge of client at that point. */
03779       x -= w;
03780       break;
03781     default:
03782       break;
03783     }
03784   
03785   switch (gravity)
03786     {
03787     case WestGravity:
03788     case CenterGravity:
03789     case EastGravity:
03790       /* Find center of frame. */
03791       y += frame_extents.height / 2;
03792       /* Center client window there. */
03793       y -= h / 2;
03794       break;
03795     case SouthWestGravity:
03796     case SouthGravity:
03797     case SouthEastGravity:
03798       /* Find south edge of frame */
03799       y += frame_extents.height;
03800       /* Place bottom edge of client there */
03801       y -= h;
03802       break;
03803     default:
03804       break;
03805     }
03806   
03807   if (root_x)
03808     *root_x = x;
03809   if (root_y)
03810     *root_y = y;
03811 }
03812 
03813 void
03814 meta_window_get_geometry (MetaWindow  *window,
03815                           int         *x,
03816                           int         *y,
03817                           int         *width,
03818                           int         *height)
03819 {
03820   meta_window_get_gravity_position (window,
03821                                     window->size_hints.win_gravity,
03822                                     x, y);
03823 
03824   *width = (window->rect.width - window->size_hints.base_width) /
03825     window->size_hints.width_inc;
03826   *height = (window->rect.height - window->size_hints.base_height) /
03827     window->size_hints.height_inc;
03828 }
03829 
03830 void
03831 meta_window_get_outer_rect (const MetaWindow *window,
03832                             MetaRectangle    *rect)
03833 {
03834   if (window->frame)
03835     *rect = window->frame->rect;
03836   else
03837     *rect = window->rect;
03838 }
03839 
03840 void
03841 meta_window_get_xor_rect (MetaWindow          *window,
03842                           const MetaRectangle *grab_wireframe_rect,
03843                           MetaRectangle       *xor_rect)
03844 {
03845   if (window->frame)
03846     {
03847       xor_rect->x = grab_wireframe_rect->x - window->frame->child_x;
03848       xor_rect->y = grab_wireframe_rect->y - window->frame->child_y;
03849       xor_rect->width = grab_wireframe_rect->width + window->frame->child_x + window->frame->right_width;
03850 
03851       if (window->shaded)
03852         xor_rect->height = window->frame->child_y;
03853       else
03854         xor_rect->height = grab_wireframe_rect->height + window->frame->child_y + window->frame->bottom_height;
03855     }
03856   else
03857     *xor_rect = *grab_wireframe_rect;
03858 }
03859 
03860 /* Figure out the numbers that show up in the
03861  * resize popup when in reduced resources mode.
03862  */
03863 static void
03864 meta_window_get_wireframe_geometry (MetaWindow    *window,
03865                                     int           *width,
03866                                     int           *height)
03867 {
03868   if (!window->display->grab_wireframe_active)
03869     return;
03870 
03871   if ((width == NULL) || (height == NULL))
03872     return;
03873 
03874   if ((window->display->grab_window->size_hints.width_inc <= 1) ||
03875       (window->display->grab_window->size_hints.height_inc <= 1))
03876     {
03877       *width = -1;
03878       *height = -1;
03879       return;
03880     }
03881 
03882   *width = window->display->grab_wireframe_rect.width - 
03883       window->display->grab_window->size_hints.base_width;
03884   *width /= window->display->grab_window->size_hints.width_inc;
03885 
03886   *height = window->display->grab_wireframe_rect.height -
03887       window->display->grab_window->size_hints.base_height;
03888   *height /= window->display->grab_window->size_hints.height_inc;
03889 }
03890 
03891 /* XXX META_EFFECT_ALT_TAB, well, this and others */
03892 void
03893 meta_window_begin_wireframe (MetaWindow *window)
03894 {
03895 
03896   MetaRectangle new_xor;
03897   int display_width, display_height;
03898 
03899   meta_window_get_client_root_coords (window,
03900                                       &window->display->grab_wireframe_rect);
03901 
03902   meta_window_get_xor_rect (window, &window->display->grab_wireframe_rect,
03903                             &new_xor);
03904   meta_window_get_wireframe_geometry (window, &display_width, &display_height);
03905 
03906   meta_effects_begin_wireframe (window->screen,
03907                                 &new_xor, display_width, display_height);
03908 
03909   window->display->grab_wireframe_last_xor_rect = new_xor;
03910   window->display->grab_wireframe_last_display_width = display_width;
03911   window->display->grab_wireframe_last_display_height = display_height;
03912 }
03913 
03914 void
03915 meta_window_update_wireframe (MetaWindow *window,
03916                               int         x,
03917                               int         y,
03918                               int         width,
03919                               int         height)
03920 {
03921 
03922   MetaRectangle new_xor;
03923   int display_width, display_height;
03924 
03925   window->display->grab_wireframe_rect.x = x;
03926   window->display->grab_wireframe_rect.y = y;
03927   window->display->grab_wireframe_rect.width = width;
03928   window->display->grab_wireframe_rect.height = height;
03929 
03930   meta_window_get_xor_rect (window, &window->display->grab_wireframe_rect,
03931                             &new_xor);
03932   meta_window_get_wireframe_geometry (window, &display_width, &display_height);
03933 
03934   meta_effects_update_wireframe (window->screen,
03935                                  &window->display->grab_wireframe_last_xor_rect,
03936                                  window->display->grab_wireframe_last_display_width,
03937                                  window->display->grab_wireframe_last_display_height,
03938                                  &new_xor, display_width, display_height);
03939 
03940   window->display->grab_wireframe_last_xor_rect = new_xor;
03941   window->display->grab_wireframe_last_display_width = display_width;
03942   window->display->grab_wireframe_last_display_height = display_height;
03943 }
03944 
03945 void
03946 meta_window_end_wireframe (MetaWindow *window)
03947 {
03948   meta_effects_end_wireframe (window->display->grab_window->screen,
03949                               &window->display->grab_wireframe_last_xor_rect,
03950                               window->display->grab_wireframe_last_display_width,
03951                               window->display->grab_wireframe_last_display_height);
03952 }
03953 
03954 const char*
03955 meta_window_get_startup_id (MetaWindow *window)
03956 {
03957   if (window->startup_id == NULL)
03958     {
03959       MetaGroup *group;
03960 
03961       group = meta_window_get_group (window);
03962 
03963       if (group != NULL)
03964         return meta_group_get_startup_id (group);
03965     }
03966 
03967   return window->startup_id;
03968 }
03969 
03970 static MetaWindow*
03971 get_modal_transient (MetaWindow *window)
03972 {
03973   GSList *windows;
03974   GSList *tmp;
03975   MetaWindow *modal_transient;
03976 
03977   /* A window can't be the transient of itself, but this is just for
03978    * convenience in the loop below; we manually fix things up at the
03979    * end if no real modal transient was found.
03980    */
03981   modal_transient = window;
03982 
03983   windows = meta_display_list_windows (window->display);
03984   tmp = windows;
03985   while (tmp != NULL)
03986     {
03987       MetaWindow *transient = tmp->data;
03988       
03989       if (transient->xtransient_for == modal_transient->xwindow &&
03990           transient->wm_state_modal)
03991         {
03992           modal_transient = transient;
03993           tmp = windows;
03994           continue;
03995         }
03996       
03997       tmp = tmp->next;
03998     }
03999 
04000   g_slist_free (windows);
04001 
04002   if (window == modal_transient)
04003     modal_transient = NULL;
04004 
04005   return modal_transient;
04006 }
04007 
04008 /* XXX META_EFFECT_FOCUS */
04009 void
04010 meta_window_focus (MetaWindow  *window,
04011                    guint32      timestamp)
04012 {  
04013   MetaWindow *modal_transient;
04014 
04015   meta_topic (META_DEBUG_FOCUS,
04016               "Setting input focus to window %s, input: %d take_focus: %d\n",
04017               window->desc, window->input, window->take_focus);
04018   
04019   if (window->display->grab_window &&
04020       window->display->grab_window->all_keys_grabbed)
04021     {
04022       meta_topic (META_DEBUG_FOCUS,
04023                   "Current focus window %s has global keygrab, not focusing window %s after all\n",
04024                   window->display->grab_window->desc, window->desc);
04025       return;
04026     }
04027 
04028   modal_transient = get_modal_transient (window);
04029   if (modal_transient != NULL &&
04030       !modal_transient->unmanaging)
04031     {
04032       meta_topic (META_DEBUG_FOCUS,
04033                   "%s has %s as a modal transient, so focusing it instead.\n",
04034                   window->desc, modal_transient->desc);
04035       if (!modal_transient->on_all_workspaces &&
04036           modal_transient->workspace != window->screen->active_workspace)
04037         meta_window_change_workspace (modal_transient, 
04038                                       window->screen->active_workspace);
04039       window = modal_transient;
04040     }
04041 
04042   meta_window_flush_calc_showing (window);
04043 
04044   if (!window->mapped && !window->shaded)
04045     {
04046       meta_topic (META_DEBUG_FOCUS,
04047                   "Window %s is not showing, not focusing after all\n",
04048                   window->desc);
04049       return;
04050     }
04051 
04052   /* For output-only or shaded windows, focus the frame.
04053    * This seems to result in the client window getting key events
04054    * though, so I don't know if it's icccm-compliant.
04055    *
04056    * Still, we have to do this or keynav breaks for these windows.
04057    */
04058   if (window->frame &&
04059       (window->shaded ||
04060        !(window->input || window->take_focus)))
04061     {
04062       if (window->frame)
04063         {
04064           meta_topic (META_DEBUG_FOCUS,
04065                       "Focusing frame of %s\n", window->desc);
04066           meta_display_set_input_focus_window (window->display,
04067                                                window,
04068                                                TRUE,
04069                                                timestamp);
04070         }
04071     }
04072   else
04073     {
04074       if (window->input)
04075         {
04076           meta_topic (META_DEBUG_FOCUS,
04077                       "Setting input focus on %s since input = true\n",
04078                       window->desc);
04079           meta_display_set_input_focus_window (window->display,
04080                                                window,
04081                                                FALSE,
04082                                                timestamp);
04083         }
04084       
04085       if (window->take_focus)
04086         {
04087           meta_topic (META_DEBUG_FOCUS,
04088                       "Sending WM_TAKE_FOCUS to %s since take_focus = true\n",
04089                       window->desc);
04090           meta_window_send_icccm_message (window,
04091                                           window->display->atom_WM_TAKE_FOCUS,
04092                                           timestamp);
04093           window->display->expected_focus_window = window;
04094         }
04095     }
04096 
04097   if (window->wm_state_demands_attention)
04098     meta_window_unset_demands_attention(window);
04099 
04100   meta_effect_run_focus(window, NULL, NULL);
04101 }
04102 
04103 static void
04104 meta_window_change_workspace_without_transients (MetaWindow    *window,
04105                                                  MetaWorkspace *workspace)
04106 {
04107   meta_verbose ("Changing window %s to workspace %d\n",
04108                 window->desc, meta_workspace_index (workspace));
04109   
04110   /* unstick if stuck. meta_window_unstick would call 
04111    * meta_window_change_workspace recursively if the window
04112    * is not in the active workspace.
04113    */
04114   if (window->on_all_workspaces)
04115     meta_window_unstick (window);
04116 
04117   /* See if we're already on this space. If not, make sure we are */
04118   if (window->workspace != workspace)
04119     {
04120       meta_workspace_remove_window (window->workspace, window);
04121       meta_workspace_add_window (workspace, window);
04122     }
04123 }
04124 
04125 static gboolean
04126 change_workspace_foreach (MetaWindow *window,
04127                           void       *data)
04128 {
04129   meta_window_change_workspace_without_transients (window, data);
04130   return TRUE;
04131 }
04132 
04133 void
04134 meta_window_change_workspace (MetaWindow    *window,
04135                               MetaWorkspace *workspace)
04136 {
04137   meta_window_change_workspace_without_transients (window, workspace);
04138 
04139   meta_window_foreach_transient (window, change_workspace_foreach,
04140                                  workspace);
04141   meta_window_foreach_ancestor (window, change_workspace_foreach,
04142                                 workspace);
04143 }
04144 
04145 static void
04146 window_stick_impl (MetaWindow  *window)
04147 {
04148   GList *tmp; 
04149   MetaWorkspace *workspace;
04150 
04151   meta_verbose ("Sticking window %s current on_all_workspaces = %d\n",
04152                 window->desc, window->on_all_workspaces);
04153   
04154   if (window->on_all_workspaces)
04155     return;
04156 
04157   /* We don't change window->workspaces, because we revert
04158    * to that original workspace list if on_all_workspaces is
04159    * toggled back off.
04160    */
04161   window->on_all_workspaces = TRUE;
04162 
04163   /* We do, however, change the MRU lists of all the workspaces
04164    */
04165   tmp = window->screen->workspaces;
04166   while (tmp)
04167     {
04168       workspace = (MetaWorkspace *) tmp->data;
04169       if (!g_list_find (workspace->mru_list, window))
04170         workspace->mru_list = g_list_prepend (workspace->mru_list, window);
04171 
04172       tmp = tmp->next;
04173     }
04174 
04175   meta_window_set_current_workspace_hint (window);
04176   
04177   meta_window_queue(window, META_QUEUE_CALC_SHOWING);
04178 }
04179 
04180 static void
04181 window_unstick_impl (MetaWindow  *window)
04182 {
04183   GList *tmp;
04184   MetaWorkspace *workspace;
04185 
04186   if (!window->on_all_workspaces)
04187     return;
04188 
04189   /* Revert to window->workspaces */
04190 
04191   window->on_all_workspaces = FALSE;
04192 
04193   /* Remove window from MRU lists that it doesn't belong in */
04194   tmp = window->screen->workspaces;
04195   while (tmp)
04196     {
04197       workspace = (MetaWorkspace *) tmp->data;
04198       if (window->workspace != workspace)
04199         workspace->mru_list = g_list_remove (workspace->mru_list, window);
04200       tmp = tmp->next;
04201     }
04202 
04203   /* We change ourselves to the active workspace, since otherwise you'd get
04204    * a weird window-vaporization effect. Once we have UI for being
04205    * on more than one workspace this should probably be add_workspace
04206    * not change_workspace.
04207    */
04208   if (window->screen->active_workspace != window->workspace)
04209     meta_window_change_workspace (window, window->screen->active_workspace);
04210   
04211   meta_window_set_current_workspace_hint (window);
04212   
04213   meta_window_queue(window, META_QUEUE_CALC_SHOWING);
04214 }
04215 
04216 static gboolean
04217 stick_foreach_func (MetaWindow *window,
04218                     void       *data)
04219 {
04220   gboolean stick;
04221 
04222   stick = *(gboolean*)data;
04223   if (stick)
04224     window_stick_impl (window);
04225   else
04226     window_unstick_impl (window);
04227   return TRUE;
04228 }
04229 
04230 void
04231 meta_window_stick (MetaWindow  *window)
04232 {
04233   gboolean stick = TRUE;
04234   window_stick_impl (window);
04235   meta_window_foreach_transient (window,
04236                                  stick_foreach_func,
04237                                  &stick);
04238 }
04239 
04240 void
04241 meta_window_unstick (MetaWindow  *window)
04242 {
04243   gboolean stick = FALSE;
04244   window_unstick_impl (window);
04245   meta_window_foreach_transient (window,
04246                                  stick_foreach_func,
04247                                  &stick);
04248 }
04249 
04250 unsigned long
04251 meta_window_get_net_wm_desktop (MetaWindow *window)
04252 {
04253   if (window->on_all_workspaces)
04254     return 0xFFFFFFFF;
04255   else
04256     return meta_workspace_index (window->workspace);
04257 }
04258 
04259 static void
04260 update_net_frame_extents (MetaWindow *window)
04261 {
04262   unsigned long data[4] = { 0, 0, 0, 0 };
04263 
04264   if (window->frame)
04265     {
04266       /* Left */
04267       data[0] = window->frame->child_x;
04268       /* Right */
04269       data[1] = window->frame->right_width;
04270       /* Top */
04271       data[2] = window->frame->child_y;
04272       /* Bottom */
04273       data[3] = window->frame->bottom_height;
04274     }
04275 
04276   meta_topic (META_DEBUG_GEOMETRY,
04277               "Setting _NET_FRAME_EXTENTS on managed window 0x%lx "
04278               "to left = %lu, right = %lu, top = %lu, bottom = %lu\n",
04279               window->xwindow, data[0], data[1], data[2], data[3]);
04280 
04281   meta_error_trap_push (window->display);
04282   XChangeProperty (window->display->xdisplay, window->xwindow,
04283                    window->display->atom__NET_FRAME_EXTENTS,
04284                    XA_CARDINAL,
04285                    32, PropModeReplace, (guchar*) data, 4);
04286   meta_error_trap_pop (window->display, FALSE);
04287 }
04288 
04289 void
04290 meta_window_set_current_workspace_hint (MetaWindow *window)
04291 {
04292   /* FIXME if on more than one workspace, we claim to be "sticky",
04293    * the WM spec doesn't say what to do here.
04294    */
04295   unsigned long data[1];
04296 
04297   if (window->workspace == NULL)
04298     {
04299       /* this happens when unmanaging windows */      
04300       return;
04301     }
04302   
04303   data[0] = meta_window_get_net_wm_desktop (window);
04304 
04305   meta_verbose ("Setting _NET_WM_DESKTOP of %s to %lu\n",
04306                 window->desc, data[0]);
04307   
04308   meta_error_trap_push (window->display);
04309   XChangeProperty (window->display->xdisplay, window->xwindow,
04310                    window->display->atom__NET_WM_DESKTOP,
04311                    XA_CARDINAL,
04312                    32, PropModeReplace, (guchar*) data, 1);
04313   meta_error_trap_pop (window->display, FALSE);
04314 }
04315 
04316 static gboolean
04317 find_root_ancestor (MetaWindow *window,
04318                     void       *data)
04319 {
04320   MetaWindow **ancestor = data;
04321 
04322   /* Overwrite the previously "most-root" ancestor with the new one found */
04323   *ancestor = window;
04324 
04325   /* We want this to continue until meta_window_foreach_ancestor quits because
04326    * there are no more valid ancestors.
04327