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    */
04328   return TRUE;
04329 }
04330 
04331 MetaWindow *
04332 meta_window_find_root_ancestor (MetaWindow *window)
04333 {
04334   MetaWindow *ancestor;
04335   ancestor = window;
04336   meta_window_foreach_ancestor (window, find_root_ancestor, &ancestor);
04337   return ancestor;
04338 }
04339 
04340 void
04341 meta_window_raise (MetaWindow  *window)
04342 {
04343   MetaWindow *ancestor;
04344   ancestor = meta_window_find_root_ancestor (window);
04345 
04346   meta_topic (META_DEBUG_WINDOW_OPS,
04347               "Raising window %s, ancestor of %s\n", 
04348               ancestor->desc, window->desc);
04349 
04350   /* Raise the ancestor of the window (if the window has no ancestor,
04351    * then ancestor will be set to the window itself); do this because
04352    * it's weird to see windows from other apps stacked between a child
04353    * and parent window of the currently active app.  The stacking
04354    * constraints in stack.c then magically take care of raising all
04355    * the child windows appropriately.
04356    */
04357   if (window->screen->stack == ancestor->screen->stack)
04358     meta_stack_raise (window->screen->stack, ancestor);
04359   else
04360     {
04361       meta_warning (
04362                     "Either stacks aren't per screen or some window has a weird "
04363                     "transient_for hint; window->screen->stack != "
04364                     "ancestor->screen->stack.  window = %s, ancestor = %s.\n",
04365                     window->desc, ancestor->desc);
04366       /* We could raise the window here, but don't want to do that twice and
04367        * so we let the case below handle that.
04368        */
04369     }
04370 
04371   /* Okay, so stacking constraints misses one case: If a window has
04372    * two children and we want to raise one of those children, then
04373    * raising the ancestor isn't enough; we need to also raise the
04374    * correct child.  See bug 307875.
04375    */
04376   if (window != ancestor)
04377     meta_stack_raise (window->screen->stack, window);
04378 }
04379 
04380 void
04381 meta_window_lower (MetaWindow  *window)
04382 {
04383   meta_topic (META_DEBUG_WINDOW_OPS,
04384               "Lowering window %s\n", window->desc);
04385 
04386   meta_stack_lower (window->screen->stack, window);
04387 }
04388 
04389 void
04390 meta_window_send_icccm_message (MetaWindow *window,
04391                                 Atom        atom,
04392                                 guint32     timestamp)
04393 {
04394   /* This comment and code are from twm, copyright
04395    * Open Group, Evans & Sutherland, etc.
04396    */
04397   
04398   /*
04399    * ICCCM Client Messages - Section 4.2.8 of the ICCCM dictates that all
04400    * client messages will have the following form:
04401    *
04402    *     event type     ClientMessage
04403    *     message type   _XA_WM_PROTOCOLS
04404    *     window         tmp->w
04405    *     format         32
04406    *     data[0]                message atom
04407    *     data[1]                time stamp
04408    */
04409   
04410     XClientMessageEvent ev;
04411     
04412     ev.type = ClientMessage;
04413     ev.window = window->xwindow;
04414     ev.message_type = window->display->atom_WM_PROTOCOLS;
04415     ev.format = 32;
04416     ev.data.l[0] = atom;
04417     ev.data.l[1] = timestamp;
04418 
04419     meta_error_trap_push (window->display);
04420     XSendEvent (window->display->xdisplay,
04421                 window->xwindow, False, 0, (XEvent*) &ev);
04422     meta_error_trap_pop (window->display, FALSE);
04423 }
04424 
04425 void
04426 meta_window_move_resize_request (MetaWindow *window,
04427                                  guint       value_mask,
04428                                  int         gravity,
04429                                  int         new_x,
04430                                  int         new_y,
04431                                  int         new_width,
04432                                  int         new_height)
04433 {
04434   int x, y, width, height;
04435   gboolean allow_position_change;
04436   gboolean in_grab_op;
04437   MetaMoveResizeFlags flags;
04438 
04439   /* We ignore configure requests while the user is moving/resizing
04440    * the window, since these represent the app sucking and fighting
04441    * the user, most likely due to a bug in the app (e.g. pfaedit
04442    * seemed to do this)
04443    *
04444    * Still have to do the ConfigureNotify and all, but pretend the
04445    * app asked for the current size/position instead of the new one.
04446    */  
04447   in_grab_op = FALSE;
04448   if (window->display->grab_op != META_GRAB_OP_NONE &&
04449       window == window->display->grab_window)
04450     {
04451       switch (window->display->grab_op)
04452         {
04453         case META_GRAB_OP_MOVING:
04454         case META_GRAB_OP_RESIZING_SE:
04455         case META_GRAB_OP_RESIZING_S:
04456         case META_GRAB_OP_RESIZING_SW:
04457         case META_GRAB_OP_RESIZING_N:
04458         case META_GRAB_OP_RESIZING_NE:
04459         case META_GRAB_OP_RESIZING_NW:
04460         case META_GRAB_OP_RESIZING_W:
04461         case META_GRAB_OP_RESIZING_E:
04462           in_grab_op = TRUE;
04463           break;
04464         default:
04465           break;
04466         }
04467     }
04468   
04469   /* it's essential to use only the explicitly-set fields,
04470    * and otherwise use our current up-to-date position.
04471    *
04472    * Otherwise you get spurious position changes when the app changes
04473    * size, for example, if window->rect is not in sync with the
04474    * server-side position in effect when the configure request was
04475    * generated.
04476    */
04477   meta_window_get_gravity_position (window,
04478                                     gravity,
04479                                     &x, &y);
04480 
04481   allow_position_change = FALSE;
04482   
04483   if (meta_prefs_get_disable_workarounds ())
04484     {
04485       if (window->type == META_WINDOW_DIALOG ||
04486           window->type == META_WINDOW_MODAL_DIALOG ||
04487           window->type == META_WINDOW_SPLASHSCREEN)
04488         ; /* No position change for these */
04489       else if ((window->size_hints.flags & PPosition) ||
04490                /* USPosition is just stale if window is placed;
04491                 * no --geometry involved here.
04492                 */
04493                ((window->size_hints.flags & USPosition) &&
04494                 !window->placed))
04495         allow_position_change = TRUE;
04496     }
04497   else
04498     {
04499       allow_position_change = TRUE;
04500     }
04501 
04502   if (in_grab_op)
04503     allow_position_change = FALSE;
04504   
04505   if (allow_position_change)
04506     {
04507       if (value_mask & CWX)
04508         x = new_x;
04509       if (value_mask & CWY)
04510         y = new_y;
04511       if (value_mask & (CWX | CWY))
04512         {
04513           /* Once manually positioned, windows shouldn't be placed
04514            * by the window manager.
04515            */
04516           window->placed = TRUE;
04517         }
04518     }
04519   else
04520     {
04521       meta_topic (META_DEBUG_GEOMETRY,
04522                   "Not allowing position change for window %s PPosition 0x%lx USPosition 0x%lx type %u\n", 
04523                   window->desc, window->size_hints.flags & PPosition, 
04524                   window->size_hints.flags & USPosition,
04525                   window->type);     
04526     }
04527 
04528   width = window->rect.width;
04529   height = window->rect.height;
04530   if (!in_grab_op)
04531     {
04532       if (value_mask & CWWidth)
04533         width = new_width;
04534       
04535       if (value_mask & CWHeight)
04536         height = new_height;
04537     }
04538 
04539   /* ICCCM 4.1.5 */
04540   
04541   /* We're ignoring the value_mask here, since sizes
04542    * not in the mask will be the current window geometry.
04543    */
04544   window->size_hints.x = x;
04545   window->size_hints.y = y;
04546   window->size_hints.width = width;
04547   window->size_hints.height = height;
04548 
04549   /* NOTE: We consider ConfigureRequests to be "user" actions in one
04550    * way, but not in another.  Explanation of the two cases are in the
04551    * next two big comments.
04552    */
04553 
04554   /* The constraints code allows user actions to move windows
04555    * offscreen, etc., and configure request actions would often send
04556    * windows offscreen when users don't want it if not constrained
04557    * (e.g. hitting a dropdown triangle in a fileselector to show more
04558    * options, which makes the window bigger).  Thus we do not set
04559    * META_IS_USER_ACTION in flags to the
04560    * meta_window_move_resize_internal() call.
04561    */
04562   flags = META_IS_CONFIGURE_REQUEST;
04563   if (value_mask & (CWX | CWY))
04564     flags |= META_IS_MOVE_ACTION;
04565   if (value_mask & (CWWidth | CWHeight))
04566     flags |= META_IS_RESIZE_ACTION;
04567 
04568   if (flags & (META_IS_MOVE_ACTION | META_IS_RESIZE_ACTION))
04569     meta_window_move_resize_internal (window, 
04570                                       flags,
04571                                       gravity,
04572                                       x,
04573                                       y,
04574                                       width,
04575                                       height);
04576 
04577   /* window->user_rect exists to allow "snapping-back" the window if a
04578    * new strut is set (causing the window to move) and then the strut
04579    * is later removed without the user moving the window in the
04580    * interim.  We'd like to "snap-back" to the position specified by
04581    * ConfigureRequest events (at least the constrained version of the
04582    * ConfigureRequest, since that is guaranteed to be onscreen) so we
04583    * set user_rect here.
04584    *
04585    * See also bug 426519.
04586    */
04587   save_user_window_placement (window);
04588 }
04589 
04590 gboolean
04591 meta_window_configure_request (MetaWindow *window,
04592                                XEvent     *event)
04593 {
04594   /* Note that x, y is the corner of the window border,
04595    * and width, height is the size of the window inside
04596    * its border, but that we always deny border requests
04597    * and give windows a border of 0. But we save the
04598    * requested border here.
04599    */
04600   if (event->xconfigurerequest.value_mask & CWBorderWidth)
04601     window->border_width = event->xconfigurerequest.border_width;
04602 
04603   meta_window_move_resize_request(window,
04604                                   event->xconfigurerequest.value_mask,
04605                                   window->size_hints.win_gravity,
04606                                   event->xconfigurerequest.x,
04607                                   event->xconfigurerequest.y,
04608                                   event->xconfigurerequest.width,
04609                                   event->xconfigurerequest.height);
04610       
04611   /* Handle stacking. We only handle raises/lowers, mostly because
04612    * stack.c really can't deal with anything else.  I guess we'll fix
04613    * that if a client turns up that really requires it. Only a very
04614    * few clients even require the raise/lower (and in fact all client
04615    * attempts to deal with stacking order are essentially broken,
04616    * since they have no idea what other clients are involved or how
04617    * the stack looks).
04618    *
04619    * I'm pretty sure no interesting client uses TopIf, BottomIf, or
04620    * Opposite anyway, so the only possible missing thing is
04621    * Above/Below with a sibling set. For now we just pretend there's
04622    * never a sibling set and always do the full raise/lower instead of
04623    * the raise-just-above/below-sibling.
04624    */
04625   if (event->xconfigurerequest.value_mask & CWStackMode)
04626     {
04627       MetaWindow *active_window;
04628       active_window = window->display->expected_focus_window;
04629       if (meta_prefs_get_disable_workarounds () ||
04630           !meta_prefs_get_raise_on_click ())
04631         {
04632           meta_topic (META_DEBUG_STACK,
04633                       "%s sent an xconfigure stacking request; this is "
04634                       "broken behavior and the request is being ignored.\n",
04635                       window->desc);
04636         }
04637       else if (active_window &&
04638                !meta_window_same_application (window, active_window) &&
04639                XSERVER_TIME_IS_BEFORE (window->net_wm_user_time,
04640                                        active_window->net_wm_user_time))
04641         {
04642           meta_topic (META_DEBUG_STACK,
04643                       "Ignoring xconfigure stacking request from %s (with "
04644                       "user_time %u); currently active application is %s (with "
04645                       "user_time %u).\n",
04646                       window->desc,
04647                       window->net_wm_user_time,
04648                       active_window->desc,
04649                       active_window->net_wm_user_time);
04650           if (event->xconfigurerequest.detail == Above)
04651             meta_window_set_demands_attention(window);
04652         }
04653       else
04654         {
04655           switch (event->xconfigurerequest.detail)
04656             {
04657             case Above:
04658               meta_window_raise (window);
04659               break;
04660             case Below:
04661               meta_window_lower (window);
04662               break;
04663             case TopIf:
04664             case BottomIf:
04665             case Opposite:
04666               break;
04667             }
04668         }
04669     }      
04670   
04671   return TRUE;
04672 }
04673 
04674 gboolean
04675 meta_window_property_notify (MetaWindow *window,
04676                              XEvent     *event)
04677 {
04678   return process_property_notify (window, &event->xproperty);  
04679 }
04680 
04681 #define _NET_WM_MOVERESIZE_SIZE_TOPLEFT      0
04682 #define _NET_WM_MOVERESIZE_SIZE_TOP          1
04683 #define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT     2
04684 #define _NET_WM_MOVERESIZE_SIZE_RIGHT        3
04685 #define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT  4
04686 #define _NET_WM_MOVERESIZE_SIZE_BOTTOM       5
04687 #define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT   6
04688 #define _NET_WM_MOVERESIZE_SIZE_LEFT         7
04689 #define _NET_WM_MOVERESIZE_MOVE              8
04690 #define _NET_WM_MOVERESIZE_SIZE_KEYBOARD     9
04691 #define _NET_WM_MOVERESIZE_MOVE_KEYBOARD    10
04692 
04693 gboolean
04694 meta_window_client_message (MetaWindow *window,
04695                             XEvent     *event)
04696 {
04697   MetaDisplay *display;
04698 
04699   display = window->display;
04700 
04701   if (event->xclient.message_type ==
04702       display->atom__NET_CLOSE_WINDOW)
04703     {
04704       guint32 timestamp;
04705 
04706       if (event->xclient.data.l[0] != 0)
04707         timestamp = event->xclient.data.l[0];
04708       else
04709         {
04710           meta_warning ("Receiving a NET_CLOSE_WINDOW message for %s without "
04711                         "a timestamp!  This means some buggy (outdated) "
04712                         "application is on the loose!\n",
04713                         window->desc);
04714           timestamp = meta_display_get_current_time (window->display);
04715         }
04716       
04717       meta_window_delete (window, timestamp);
04718 
04719       return TRUE;
04720     }
04721   else if (event->xclient.message_type ==
04722            display->atom__NET_WM_DESKTOP)
04723     {
04724       int space;
04725       MetaWorkspace *workspace;
04726               
04727       space = event->xclient.data.l[0];
04728               
04729       meta_verbose ("Request to move %s to workspace %d\n",
04730                     window->desc, space);
04731 
04732       workspace =
04733         meta_screen_get_workspace_by_index (window->screen,
04734                                             space);
04735 
04736       if (workspace)
04737         {
04738           if (window->on_all_workspaces)
04739             meta_window_unstick (window);
04740           meta_window_change_workspace (window, workspace);
04741         }
04742       else if (space == (int) 0xFFFFFFFF)
04743         {
04744           meta_window_stick (window);
04745         }
04746       else
04747         {
04748           meta_verbose ("No such workspace %d for screen\n", space);
04749         }
04750 
04751       meta_verbose ("Window %s now on_all_workspaces = %d\n",
04752                     window->desc, window->on_all_workspaces);
04753       
04754       return TRUE;
04755     }
04756   else if (event->xclient.message_type ==
04757            display->atom__NET_WM_STATE)
04758     {
04759       gulong action;
04760       Atom first;
04761       Atom second;
04762 
04763       action = event->xclient.data.l[0];
04764       first = event->xclient.data.l[1];
04765       second = event->xclient.data.l[2];
04766       
04767       if (meta_is_verbose ())
04768         {
04769           char *str1;
04770           char *str2;
04771 
04772           meta_error_trap_push_with_return (display);
04773           str1 = XGetAtomName (display->xdisplay, first);
04774           if (meta_error_trap_pop_with_return (display, TRUE) != Success)
04775             str1 = NULL;
04776 
04777           meta_error_trap_push_with_return (display);
04778           str2 = XGetAtomName (display->xdisplay, second); 
04779           if (meta_error_trap_pop_with_return (display, TRUE) != Success)
04780             str2 = NULL;
04781           
04782           meta_verbose ("Request to change _NET_WM_STATE action %lu atom1: %s atom2: %s\n",
04783                         action,
04784                         str1 ? str1 : "(unknown)",
04785                         str2 ? str2 : "(unknown)");
04786 
04787           meta_XFree (str1);
04788           meta_XFree (str2);
04789         }
04790 
04791       if (first == display->atom__NET_WM_STATE_SHADED ||
04792           second == display->atom__NET_WM_STATE_SHADED)
04793         {
04794           gboolean shade;
04795           guint32 timestamp;
04796 
04797           /* Stupid protocol has no timestamp; of course, shading
04798            * sucks anyway so who really cares that we're forced to do
04799            * a roundtrip here? 
04800            */
04801           timestamp = meta_display_get_current_time_roundtrip (window->display);
04802 
04803           shade = (action == _NET_WM_STATE_ADD ||
04804                    (action == _NET_WM_STATE_TOGGLE && !window->shaded));
04805           if (shade && window->has_shade_func)
04806             meta_window_shade (window, timestamp);
04807           else
04808             meta_window_unshade (window, timestamp);
04809         }
04810 
04811       if (first == display->atom__NET_WM_STATE_FULLSCREEN ||
04812           second == display->atom__NET_WM_STATE_FULLSCREEN)
04813         {
04814           gboolean make_fullscreen;
04815 
04816           make_fullscreen = (action == _NET_WM_STATE_ADD ||
04817                              (action == _NET_WM_STATE_TOGGLE && !window->fullscreen));
04818           if (make_fullscreen && window->has_fullscreen_func)
04819             meta_window_make_fullscreen (window);
04820           else
04821             meta_window_unmake_fullscreen (window);
04822         }
04823       
04824       if (first == display->atom__NET_WM_STATE_MAXIMIZED_HORZ ||
04825           second == display->atom__NET_WM_STATE_MAXIMIZED_HORZ)
04826         {
04827           gboolean max;
04828 
04829           max = (action == _NET_WM_STATE_ADD ||
04830                  (action == _NET_WM_STATE_TOGGLE && 
04831                   !window->maximized_horizontally));
04832           if (max && window->has_maximize_func)
04833             {
04834               if (meta_prefs_get_raise_on_click ())
04835                 meta_window_raise (window);
04836               meta_window_maximize (window, META_MAXIMIZE_HORIZONTAL);
04837             }
04838           else
04839             {
04840               if (meta_prefs_get_raise_on_click ())
04841                 meta_window_raise (window);
04842               meta_window_unmaximize (window, META_MAXIMIZE_HORIZONTAL);
04843             }
04844         }
04845 
04846       if (first == display->atom__NET_WM_STATE_MAXIMIZED_VERT ||
04847           second == display->atom__NET_WM_STATE_MAXIMIZED_VERT)
04848         {
04849           gboolean max;
04850 
04851           max = (action == _NET_WM_STATE_ADD ||
04852                  (action == _NET_WM_STATE_TOGGLE && 
04853                   !window->maximized_vertically));
04854           if (max && window->has_maximize_func)
04855             {
04856               if (meta_prefs_get_raise_on_click ())
04857                 meta_window_raise (window);
04858               meta_window_maximize (window, META_MAXIMIZE_VERTICAL);
04859             }
04860           else
04861             {
04862               if (meta_prefs_get_raise_on_click ())
04863                 meta_window_raise (window);
04864               meta_window_unmaximize (window, META_MAXIMIZE_VERTICAL);
04865             }
04866         }
04867 
04868       if (first == display->atom__NET_WM_STATE_MODAL ||
04869           second == display->atom__NET_WM_STATE_MODAL)
04870         {
04871           window->wm_state_modal =
04872             (action == _NET_WM_STATE_ADD) ||
04873             (action == _NET_WM_STATE_TOGGLE && !window->wm_state_modal);
04874           
04875           recalc_window_type (window);
04876           meta_window_queue(window, META_QUEUE_MOVE_RESIZE);
04877         }
04878 
04879       if (first == display->atom__NET_WM_STATE_SKIP_PAGER ||
04880           second == display->atom__NET_WM_STATE_SKIP_PAGER)
04881         {
04882           window->wm_state_skip_pager = 
04883             (action == _NET_WM_STATE_ADD) ||
04884             (action == _NET_WM_STATE_TOGGLE && !window->skip_pager);
04885 
04886           recalc_window_features (window);
04887           set_net_wm_state (window);
04888         }
04889 
04890       if (first == display->atom__NET_WM_STATE_SKIP_TASKBAR ||
04891           second == display->atom__NET_WM_STATE_SKIP_TASKBAR)
04892         {
04893           window->wm_state_skip_taskbar =
04894             (action == _NET_WM_STATE_ADD) ||
04895             (action == _NET_WM_STATE_TOGGLE && !window->skip_taskbar);
04896 
04897           recalc_window_features (window);
04898           set_net_wm_state (window);
04899         }
04900 
04901       if (first == display->atom__NET_WM_STATE_ABOVE ||
04902           second == display->atom__NET_WM_STATE_ABOVE)
04903         {
04904           window->wm_state_above = 
04905             (action == _NET_WM_STATE_ADD) ||
04906             (action == _NET_WM_STATE_TOGGLE && !window->wm_state_above);
04907 
04908           meta_window_update_layer (window);
04909           set_net_wm_state (window);
04910         }
04911 
04912       if (first == display->atom__NET_WM_STATE_BELOW ||
04913           second == display->atom__NET_WM_STATE_BELOW)
04914         {
04915           window->wm_state_below = 
04916             (action == _NET_WM_STATE_ADD) ||
04917             (action == _NET_WM_STATE_TOGGLE && !window->wm_state_below);
04918 
04919           meta_window_update_layer (window);
04920           set_net_wm_state (window);
04921         }
04922 
04923       if (first == display->atom__NET_WM_STATE_DEMANDS_ATTENTION ||
04924           second == display->atom__NET_WM_STATE_DEMANDS_ATTENTION)
04925         {
04926           if ((action == _NET_WM_STATE_ADD) ||
04927               (action == _NET_WM_STATE_TOGGLE && !window->wm_state_demands_attention))
04928             meta_window_set_demands_attention(window);
04929           else
04930             meta_window_unset_demands_attention(window);
04931         }
04932       
04933       return TRUE;
04934     }
04935   else if (event->xclient.message_type ==
04936            display->atom_WM_CHANGE_STATE)
04937     {
04938       meta_verbose ("WM_CHANGE_STATE client message, state: %ld\n",
04939                     event->xclient.data.l[0]);
04940       if (event->xclient.data.l[0] == IconicState &&
04941           window->has_minimize_func)
04942         meta_window_minimize (window);
04943 
04944       return TRUE;
04945     }
04946   else if (event->xclient.message_type ==
04947            display->atom__NET_WM_MOVERESIZE)
04948     {
04949       int x_root;
04950       int y_root;
04951       int action;
04952       MetaGrabOp op;
04953       int button;
04954       guint32 timestamp;
04955 
04956       /* _NET_WM_MOVERESIZE messages are almost certainly going to come from
04957        * clients when users click on the fake "frame" that the client has,
04958        * thus we should also treat such messages as though it were a
04959        * "frame action".
04960        */
04961       gboolean const frame_action = TRUE;
04962       
04963       x_root = event->xclient.data.l[0];
04964       y_root = event->xclient.data.l[1];
04965       action = event->xclient.data.l[2];
04966       button = event->xclient.data.l[3];
04967 
04968       /* FIXME: What a braindead protocol; no timestamp?!? */
04969       timestamp = meta_display_get_current_time_roundtrip (display);
04970       meta_warning ("Received a _NET_WM_MOVERESIZE message for %s; these "
04971                     "messages lack timestamps and therefore suck.\n",
04972                     window->desc);
04973       meta_topic (META_DEBUG_WINDOW_OPS,
04974                   "Received _NET_WM_MOVERESIZE message on %s, %d,%d action = %d, button %d\n",
04975                   window->desc,
04976                   x_root, y_root, action, button);
04977       
04978       op = META_GRAB_OP_NONE;
04979       switch (action)
04980         {
04981         case _NET_WM_MOVERESIZE_SIZE_TOPLEFT:
04982           op = META_GRAB_OP_RESIZING_NW;
04983           break;
04984         case _NET_WM_MOVERESIZE_SIZE_TOP:
04985           op = META_GRAB_OP_RESIZING_N;
04986           break;
04987         case _NET_WM_MOVERESIZE_SIZE_TOPRIGHT:
04988           op = META_GRAB_OP_RESIZING_NE;
04989           break;
04990         case _NET_WM_MOVERESIZE_SIZE_RIGHT:
04991           op = META_GRAB_OP_RESIZING_E;
04992           break;
04993         case _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT:
04994           op = META_GRAB_OP_RESIZING_SE;
04995           break;
04996         case _NET_WM_MOVERESIZE_SIZE_BOTTOM:          
04997           op = META_GRAB_OP_RESIZING_S;
04998           break;
04999         case _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT:
05000           op = META_GRAB_OP_RESIZING_SW;
05001           break;
05002         case _NET_WM_MOVERESIZE_SIZE_LEFT:
05003           op = META_GRAB_OP_RESIZING_W;
05004           break;
05005         case _NET_WM_MOVERESIZE_MOVE:
05006           op = META_GRAB_OP_MOVING;
05007           break;
05008         case _NET_WM_MOVERESIZE_SIZE_KEYBOARD:
05009           op = META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN;
05010           break;
05011         case _NET_WM_MOVERESIZE_MOVE_KEYBOARD:
05012           op = META_GRAB_OP_KEYBOARD_MOVING;
05013           break;
05014         default:
05015           break;
05016         }
05017 
05018       if (op != META_GRAB_OP_NONE &&
05019           ((window->has_move_func && op == META_GRAB_OP_KEYBOARD_MOVING) ||
05020            (window->has_resize_func && op == META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN)))
05021         {
05022           meta_window_begin_grab_op (window, op, frame_action, timestamp);
05023         }
05024       else if (op != META_GRAB_OP_NONE &&
05025                ((window->has_move_func && op == META_GRAB_OP_MOVING) ||
05026                (window->has_resize_func && 
05027                 (op != META_GRAB_OP_MOVING && 
05028                  op != META_GRAB_OP_KEYBOARD_MOVING))))
05029         {
05030           /*
05031            * the button SHOULD already be included in the message
05032            */
05033           if (button == 0)
05034             {
05035               int x, y, query_root_x, query_root_y;
05036               Window root, child;
05037               guint mask;
05038 
05039               /* The race conditions in this _NET_WM_MOVERESIZE thing
05040                * are mind-boggling
05041                */
05042               mask = 0;
05043               meta_error_trap_push (window->display);
05044               XQueryPointer (window->display->xdisplay,
05045                              window->xwindow,
05046                              &root, &child,
05047                              &query_root_x, &query_root_y,
05048                              &x, &y,
05049                              &mask);
05050               meta_error_trap_pop (window->display, TRUE);
05051 
05052               if (mask & Button1Mask)
05053                 button = 1;
05054               else if (mask & Button2Mask)
05055                 button = 2;
05056               else if (mask & Button3Mask)
05057                 button = 3;
05058               else
05059                 button = 0;
05060             }
05061 
05062           if (button != 0)
05063             {
05064               meta_topic (META_DEBUG_WINDOW_OPS,
05065                           "Beginning move/resize with button = %d\n", button);
05066               meta_display_begin_grab_op (window->display,
05067                                           window->screen,
05068                                           window,
05069                                           op,
05070                                           FALSE,
05071                                           frame_action,
05072                                           button, 0,
05073                                           timestamp,
05074                                           x_root,
05075                                           y_root);
05076             }
05077         }
05078 
05079       return TRUE;
05080     }
05081   else if (event->xclient.message_type ==
05082            display->atom__NET_MOVERESIZE_WINDOW)
05083     {
05084       int gravity, source;
05085       guint value_mask;
05086 
05087       gravity = (event->xclient.data.l[0] & 0xff);
05088       value_mask = (event->xclient.data.l[0] & 0xf00) >> 8;
05089       source = (event->xclient.data.l[0] & 0xf000) >> 12;
05090 
05091       if (gravity == 0)
05092         gravity = window->size_hints.win_gravity;
05093 
05094       meta_window_move_resize_request(window,
05095                                       value_mask,
05096                                       gravity,
05097                                       event->xclient.data.l[1],  /* x */
05098                                       event->xclient.data.l[2],  /* y */
05099                                       event->xclient.data.l[3],  /* width */
05100                                       event->xclient.data.l[4]); /* height */
05101     }
05102   else if (event->xclient.message_type ==
05103            display->atom__NET_ACTIVE_WINDOW)
05104     {
05105       MetaClientType source_indication;
05106       guint32        timestamp;
05107 
05108       meta_verbose ("_NET_ACTIVE_WINDOW request for window '%s', activating\n",
05109                     window->desc);
05110 
05111       source_indication = event->xclient.data.l[0];
05112       timestamp = event->xclient.data.l[1];
05113 
05114       if (source_indication > META_CLIENT_TYPE_MAX_RECOGNIZED)
05115         source_indication = META_CLIENT_TYPE_UNKNOWN;
05116 
05117       if (timestamp == 0)
05118         {
05119           /* Client using older EWMH _NET_ACTIVE_WINDOW without a timestamp */
05120           meta_warning ("Buggy client sent a _NET_ACTIVE_WINDOW message with a "
05121                         "timestamp of 0 for %s\n",
05122                         window->desc);
05123           timestamp = meta_display_get_current_time (display);
05124         }
05125 
05126       window_activate (window, timestamp, source_indication, NULL);
05127       return TRUE;
05128     }
05129   
05130   return FALSE;
05131 }
05132 
05133 gboolean
05134 meta_window_notify_focus (MetaWindow *window,
05135                           XEvent     *event)
05136 {
05137   /* note the event can be on either the window or the frame,
05138    * we focus the frame for shaded windows
05139    */
05140   
05141   /* The event can be FocusIn, FocusOut, or UnmapNotify.
05142    * On UnmapNotify we have to pretend it's focus out,
05143    * because we won't get a focus out if it occurs, apparently.
05144    */
05145 
05146   /* We ignore grabs, though this is questionable.
05147    * It may be better to increase the intelligence of
05148    * the focus window tracking.
05149    *
05150    * The problem is that keybindings for windows are done with
05151    * XGrabKey, which means focus_window disappears and the front of
05152    * the MRU list gets confused from what the user expects once a
05153    * keybinding is used.
05154    */  
05155   meta_topic (META_DEBUG_FOCUS,
05156               "Focus %s event received on %s 0x%lx (%s) "
05157               "mode %s detail %s\n",
05158               event->type == FocusIn ? "in" :
05159               event->type == FocusOut ? "out" :
05160               event->type == UnmapNotify ? "unmap" :
05161               "???",
05162               window->desc, event->xany.window,
05163               event->xany.window == window->xwindow ?
05164               "client window" :
05165               (window->frame && event->xany.window == window->frame->xwindow) ?
05166               "frame window" :
05167               "unknown window",
05168               event->type != UnmapNotify ?
05169               meta_event_mode_to_string (event->xfocus.mode) : "n/a",
05170               event->type != UnmapNotify ?
05171               meta_event_detail_to_string (event->xfocus.detail) : "n/a");
05172 
05173   /* FIXME our pointer tracking is broken; see how
05174    * gtk+/gdk/x11/gdkevents-x11.c or XFree86/xc/programs/xterm/misc.c
05175    * handle it for the correct way.  In brief you need to track
05176    * pointer focus and regular focus, and handle EnterNotify in
05177    * PointerRoot mode with no window manager.  However as noted above,
05178    * accurate focus tracking will break things because we want to keep
05179    * windows "focused" when using keybindings on them, and also we
05180    * sometimes "focus" a window by focusing its frame or
05181    * no_focus_window; so this all needs rethinking massively.
05182    *
05183    * My suggestion is to change it so that we clearly separate
05184    * actual keyboard focus tracking using the xterm algorithm,
05185    * and metacity's "pretend" focus window, and go through all
05186    * the code and decide which one should be used in each place;
05187    * a hard bit is deciding on a policy for that.
05188    *
05189    * http://bugzilla.gnome.org/show_bug.cgi?id=90382
05190    */
05191   
05192   if ((event->type == FocusIn ||
05193        event->type == FocusOut) &&
05194       (event->xfocus.mode == NotifyGrab ||
05195        event->xfocus.mode == NotifyUngrab ||
05196        /* From WindowMaker, ignore all funky pointer root events */
05197        event->xfocus.detail > NotifyNonlinearVirtual))
05198     {
05199       meta_topic (META_DEBUG_FOCUS,
05200                   "Ignoring focus event generated by a grab or other weirdness\n");
05201       return TRUE;
05202     }
05203     
05204   if (event->type == FocusIn)
05205     {
05206       if (window != window->display->focus_window)
05207         {
05208           meta_topic (META_DEBUG_FOCUS,
05209                       "* Focus --> %s\n", window->desc);
05210           window->display->focus_window = window;
05211           window->has_focus = TRUE;
05212           meta_compositor_set_active_window (window->display->compositor,
05213                                              window->screen, window);
05214 
05215           /* Move to the front of the focusing workspace's MRU list.
05216            * We should only be "removing" it from the MRU list if it's
05217            * not already there.  Note that it's possible that we might
05218            * be processing this FocusIn after we've changed to a
05219            * different workspace; we should therefore update the MRU
05220            * list only if the window is actually on the active
05221            * workspace.
05222            */
05223           if (window->screen->active_workspace &&
05224               meta_window_located_on_workspace (window, 
05225                                                 window->screen->active_workspace))
05226             {
05227               GList* link;
05228               link = g_list_find (window->screen->active_workspace->mru_list, 
05229                                   window);
05230               g_assert (link);
05231 
05232               window->screen->active_workspace->mru_list = 
05233                 g_list_remove_link (window->screen->active_workspace->mru_list,
05234                                     link);
05235               g_list_free (link);
05236 
05237               window->screen->active_workspace->mru_list = 
05238                 g_list_prepend (window->screen->active_workspace->mru_list, 
05239                                 window);
05240             }
05241 
05242           if (window->frame)
05243             meta_frame_queue_draw (window->frame);
05244           
05245           meta_error_trap_push (window->display);
05246           XInstallColormap (window->display->xdisplay,
05247                             window->colormap);
05248           meta_error_trap_pop (window->display, FALSE);
05249 
05250           /* move into FOCUSED_WINDOW layer */
05251           meta_window_update_layer (window);
05252 
05253           /* Ungrab click to focus button since the sync grab can interfere
05254            * with some things you might do inside the focused window, by
05255            * causing the client to get funky enter/leave events.
05256            *
05257            * The reason we usually have a passive grab on the window is
05258            * so that we can intercept clicks and raise the window in
05259            * response. For click-to-focus we don't need that since the
05260            * focused window is already raised. When raise_on_click is
05261            * FALSE we also don't need that since we don't do anything
05262            * when the window is clicked.
05263            *
05264            * There is dicussion in bugs 102209, 115072, and 461577
05265            */
05266           if (meta_prefs_get_focus_mode () == META_FOCUS_MODE_CLICK ||
05267               !meta_prefs_get_raise_on_click())
05268             meta_display_ungrab_focus_window_button (window->display, window);
05269         }
05270     }
05271   else if (event->type == FocusOut ||
05272            event->type == UnmapNotify)
05273     {
05274       if (event->type == FocusOut &&
05275           event->xfocus.detail == NotifyInferior)
05276         {
05277           /* This event means the client moved focus to a subwindow */
05278           meta_topic (META_DEBUG_FOCUS,
05279                       "Ignoring focus out on %s with NotifyInferior\n",
05280                       window->desc);
05281           return TRUE;
05282         }
05283       
05284       if (window == window->display->focus_window)
05285         {
05286           meta_topic (META_DEBUG_FOCUS,
05287                       "%s is now the previous focus window due to being focused out or unmapped\n",
05288                       window->desc);
05289 
05290           meta_topic (META_DEBUG_FOCUS,
05291                       "* Focus --> NULL (was %s)\n", window->desc);
05292           
05293           window->display->focus_window = NULL;
05294           window->has_focus = FALSE;
05295           if (window->frame)
05296             meta_frame_queue_draw (window->frame);
05297 
05298           meta_compositor_set_active_window (window->display->compositor, 
05299                                              window->screen, NULL);
05300 
05301           meta_error_trap_push (window->display);
05302           XUninstallColormap (window->display->xdisplay,
05303                               window->colormap);
05304           meta_error_trap_pop (window->display, FALSE);
05305 
05306           /* move out of FOCUSED_WINDOW layer */
05307           meta_window_update_layer (window);
05308 
05309           /* Re-grab for click to focus and raise-on-click, if necessary */
05310           if (meta_prefs_get_focus_mode () == META_FOCUS_MODE_CLICK ||
05311               !meta_prefs_get_raise_on_click ())
05312             meta_display_grab_focus_window_button (window->display, window);
05313        }
05314     }
05315 
05316   /* Now set _NET_ACTIVE_WINDOW hint */
05317   meta_display_update_active_window_hint (window->display);
05318   
05319   return FALSE;
05320 }
05321 
05322 static gboolean
05323 process_property_notify (MetaWindow     *window,
05324                          XPropertyEvent *event)
05325 {
05326   /* First, property notifies to ignore because we shouldn't honor
05327    * new values
05328    */
05329   if (event->atom == window->display->atom__NET_WM_STATE)
05330     {
05331       meta_verbose ("Property notify on %s for _NET_WM_STATE, ignoring (we should be the one who set the property in the first place)\n",
05332                     window->desc);
05333       return TRUE;
05334     }
05335 
05336   /* Second, property notifies we want to use.
05337    * FIXME once we move entirely to the window-props.h framework, we
05338    * can just call reload on the property in the event and get rid of
05339    * this if-else chain.
05340    */
05341   
05342   if (event->atom == XA_WM_NAME)
05343     {
05344       meta_verbose ("Property notify on %s for WM_NAME\n", window->desc);
05345 
05346       /* don't bother reloading WM_NAME if using _NET_WM_NAME already */
05347       if (!window->using_net_wm_name)
05348         meta_window_reload_property (window, XA_WM_NAME);
05349     }
05350   else if (event->atom == window->display->atom__NET_WM_NAME)
05351     {
05352       meta_verbose ("Property notify on %s for NET_WM_NAME\n", window->desc);
05353       meta_window_reload_property (window, window->display->atom__NET_WM_NAME);
05354       
05355       /* if _NET_WM_NAME was unset, reload WM_NAME */
05356       if (!window->using_net_wm_name)
05357         meta_window_reload_property (window, XA_WM_NAME);      
05358     }
05359   else if (event->atom == XA_WM_ICON_NAME)
05360     {
05361       meta_verbose ("Property notify on %s for WM_ICON_NAME\n", window->desc);
05362 
05363       /* don't bother reloading WM_ICON_NAME if using _NET_WM_ICON_NAME already */
05364       if (!window->using_net_wm_icon_name)
05365         meta_window_reload_property (window, XA_WM_ICON_NAME);
05366     }
05367   else if (event->atom == window->display->atom__NET_WM_ICON_NAME)
05368     {
05369       meta_verbose ("Property notify on %s for NET_WM_ICON_NAME\n", window->desc);
05370       meta_window_reload_property (window, window->display->atom__NET_WM_ICON_NAME);
05371       
05372       /* if _NET_WM_ICON_NAME was unset, reload WM_ICON_NAME */
05373       if (!window->using_net_wm_icon_name)
05374         meta_window_reload_property (window, XA_WM_ICON_NAME);
05375     }  
05376   else if (event->atom == XA_WM_NORMAL_HINTS)
05377     {
05378       meta_verbose ("Property notify on %s for WM_NORMAL_HINTS\n", window->desc);
05379       
05380       meta_window_reload_property (window, XA_WM_NORMAL_HINTS);
05381       
05382       /* See if we need to constrain current size */
05383       meta_window_queue(window, META_QUEUE_MOVE_RESIZE);
05384     }
05385   else if (event->atom == window->display->atom_WM_PROTOCOLS)
05386     {
05387       meta_verbose ("Property notify on %s for WM_PROTOCOLS\n", window->desc);
05388       
05389       meta_window_reload_property (window, window->display->atom_WM_PROTOCOLS);
05390     }
05391   else if (event->atom == XA_WM_HINTS)
05392     {
05393       meta_verbose ("Property notify on %s for WM_HINTS\n", window->desc);
05394 
05395       meta_window_reload_property (window, XA_WM_HINTS);
05396     }
05397   else if (event->atom == window->display->atom__MOTIF_WM_HINTS)
05398     {
05399       meta_verbose ("Property notify on %s for MOTIF_WM_HINTS\n", window->desc);
05400       
05401       meta_window_reload_property (window,
05402                                    window->display->atom__MOTIF_WM_HINTS);
05403     }
05404   else if (event->atom == XA_WM_CLASS)
05405     {
05406       meta_verbose ("Property notify on %s for WM_CLASS\n", window->desc);
05407       
05408       meta_window_reload_property (window, XA_WM_CLASS);
05409     }
05410   else if (event->atom == XA_WM_TRANSIENT_FOR)
05411     {
05412       meta_verbose ("Property notify on %s for WM_TRANSIENT_FOR\n", window->desc);
05413       
05414       meta_window_reload_property (window, XA_WM_TRANSIENT_FOR);
05415     }
05416   else if (event->atom ==
05417            window->display->atom_WM_WINDOW_ROLE)
05418     {
05419       meta_verbose ("Property notify on %s for WM_WINDOW_ROLE\n", window->desc);
05420       
05421       update_role (window);
05422     }
05423   else if (event->atom ==
05424            window->display->atom_WM_CLIENT_LEADER ||
05425            event->atom ==
05426            window->display->atom_SM_CLIENT_ID)
05427     {
05428       meta_warning ("Broken client! Window %s changed client leader window or SM client ID\n", window->desc);
05429     }
05430   else if (event->atom ==
05431            window->display->atom__NET_WM_WINDOW_TYPE)
05432     {
05433       meta_verbose ("Property notify on %s for NET_WM_WINDOW_TYPE\n", window->desc);
05434       update_net_wm_type (window);
05435     }
05436   else if (event->atom == window->display->atom__NET_WM_ICON)
05437     {
05438       meta_verbose ("Property notify on %s for NET_WM_ICON\n", window->desc);
05439       meta_icon_cache_property_changed (&window->icon_cache,
05440                                         window->display,
05441                                         event->atom);
05442       meta_window_queue(window, META_QUEUE_UPDATE_ICON);
05443     }
05444   else if (event->atom == window->display->atom__KWM_WIN_ICON)
05445     {
05446       meta_verbose ("Property notify on %s for KWM_WIN_ICON\n", window->desc);
05447 
05448       meta_icon_cache_property_changed (&window->icon_cache,
05449                                         window->display,
05450                                         event->atom);
05451       meta_window_queue(window, META_QUEUE_UPDATE_ICON);
05452     }
05453   else if ((event->atom == window->display->atom__NET_WM_STRUT) ||
05454            (event->atom == window->display->atom__NET_WM_STRUT_PARTIAL))
05455     {
05456       meta_verbose ("Property notify on %s for _NET_WM_STRUT\n", window->desc);
05457       meta_window_update_struts (window);
05458     }
05459   else if (event->atom == window->display->atom__NET_STARTUP_ID)
05460     {
05461       meta_verbose ("Property notify on %s for _NET_STARTUP_ID\n", window->desc);
05462       
05463       meta_window_reload_property (window,
05464                                    window->display->atom__NET_STARTUP_ID);
05465     }
05466   else if (event->atom == window->display->atom__NET_WM_SYNC_REQUEST_COUNTER)
05467     {
05468       meta_verbose ("Property notify on %s for _NET_WM_SYNC_REQUEST_COUNTER\n", window->desc);
05469       
05470       meta_window_reload_property (window,
05471                                    window->display->atom__NET_WM_SYNC_REQUEST_COUNTER);
05472     }
05473   else if (event->atom == window->display->atom__NET_WM_USER_TIME)
05474     {
05475       Window xid;
05476       Atom atom__NET_WM_USER_TIME;
05477 
05478       meta_verbose ("Property notify on %s for _NET_WM_USER_TIME\n", window->desc);
05479 
05480       atom__NET_WM_USER_TIME = window->display->atom__NET_WM_USER_TIME;
05481       if (window->user_time_window)
05482         xid = window->user_time_window;
05483       else
05484         xid = window->xwindow;
05485       meta_window_reload_property_from_xwindow (window,
05486                                                 xid,
05487                                                 atom__NET_WM_USER_TIME);
05488     }
05489 
05490   return TRUE;
05491 }
05492 
05493 static void
05494 send_configure_notify (MetaWindow *window)
05495 {
05496   XEvent event;
05497 
05498   /* from twm */
05499   
05500   event.type = ConfigureNotify;
05501   event.xconfigure.display = window->display->xdisplay;
05502   event.xconfigure.event = window->xwindow;
05503   event.xconfigure.window = window->xwindow;
05504   event.xconfigure.x = window->rect.x - window->border_width;
05505   event.xconfigure.y = window->rect.y - window->border_width;
05506   if (window->frame)
05507     {
05508       if (window->withdrawn)
05509         {
05510           /* WARNING: x & y need to be set to whatever the XReparentWindow
05511            * call in meta_window_destroy_frame will use so that the window
05512            * has the right coordinates.  Currently, that means no change to
05513            * x & y.
05514            */
05515         }
05516       else
05517         {
05518           /* Need to be in root window coordinates */
05519           event.xconfigure.x += window->frame->rect.x;
05520           event.xconfigure.y += window->frame->rect.y;
05521         }
05522     }
05523   event.xconfigure.width = window->rect.width;
05524   event.xconfigure.height = window->rect.height;
05525   event.xconfigure.border_width = window->border_width; /* requested not actual */
05526   event.xconfigure.above = None; /* FIXME */
05527   event.xconfigure.override_redirect = False;
05528 
05529   meta_topic (META_DEBUG_GEOMETRY,
05530               "Sending synthetic configure notify to %s with x: %d y: %d w: %d h: %d\n",
05531               window->desc,
05532               event.xconfigure.x, event.xconfigure.y,
05533               event.xconfigure.width, event.xconfigure.height);
05534   
05535   meta_error_trap_push (window->display);
05536   XSendEvent (window->display->xdisplay,
05537               window->xwindow,
05538               False, StructureNotifyMask, &event);
05539   meta_error_trap_pop (window->display, FALSE);
05540 }
05541 
05542 gboolean
05543 meta_window_get_icon_geometry (MetaWindow    *window,
05544                                MetaRectangle *rect)
05545 {
05546   gulong *geometry = NULL;
05547   int nitems;
05548 
05549   if (meta_prop_get_cardinal_list (window->display,
05550                                    window->xwindow,
05551                                    window->display->atom__NET_WM_ICON_GEOMETRY,
05552                                    &geometry, &nitems))
05553     {
05554       if (nitems != 4)
05555         {
05556           meta_verbose ("_NET_WM_ICON_GEOMETRY on %s has %d values instead of 4\n",
05557                         window->desc, nitems);
05558           meta_XFree (geometry);
05559           return FALSE;
05560         }
05561   
05562       if (rect)
05563         {
05564           rect->x = geometry[0];
05565           rect->y = geometry[1];
05566           rect->width = geometry[2];
05567           rect->height = geometry[3];
05568         }
05569 
05570       meta_XFree (geometry);
05571 
05572       return TRUE;
05573     }
05574 
05575   return FALSE;
05576 }
05577 
05578 static Window
05579 read_client_leader (MetaDisplay *display,
05580                     Window       xwindow)
05581 {
05582   Window retval = None;
05583   
05584   meta_prop_get_window (display, xwindow,
05585                         display->atom_WM_CLIENT_LEADER,
05586                         &retval);
05587 
05588   return retval;
05589 }
05590 
05591 typedef struct
05592 {
05593   Window leader;  
05594 } ClientLeaderData;
05595 
05596 static gboolean
05597 find_client_leader_func (MetaWindow *ancestor,
05598                          void       *data)
05599 {
05600   ClientLeaderData *d;
05601 
05602   d = data;
05603 
05604   d->leader = read_client_leader (ancestor->display,
05605                                   ancestor->xwindow);
05606 
05607   /* keep going if no client leader found */
05608   return d->leader == None;
05609 }
05610 
05611 static void
05612 update_sm_hints (MetaWindow *window)
05613 {
05614   Window leader;
05615   
05616   window->xclient_leader = None;
05617   window->sm_client_id = NULL;
05618 
05619   /* If not on the current window, we can get the client
05620    * leader from transient parents. If we find a client
05621    * leader, we read the SM_CLIENT_ID from it.
05622    */
05623   leader = read_client_leader (window->display, window->xwindow);
05624   if (leader == None)
05625     {
05626       ClientLeaderData d;
05627       d.leader = None;
05628       meta_window_foreach_ancestor (window, find_client_leader_func,
05629                                     &d);
05630       leader = d.leader;
05631     }
05632       
05633   if (leader != None)
05634     {
05635       char *str;
05636       
05637       window->xclient_leader = leader;
05638 
05639       if (meta_prop_get_latin1_string (window->display, leader,
05640                                        window->display->atom_SM_CLIENT_ID,
05641                                        &str))
05642         {
05643           window->sm_client_id = g_strdup (str);
05644           meta_XFree (str);
05645         }
05646     }
05647   else
05648     {
05649       meta_verbose ("Didn't find a client leader for %s\n", window->desc);
05650 
05651       if (!meta_prefs_get_disable_workarounds ())
05652         {
05653           /* Some broken apps (kdelibs fault?) set SM_CLIENT_ID on the app
05654            * instead of the client leader
05655            */
05656           char *str;
05657 
05658           str = NULL;
05659           if (meta_prop_get_latin1_string (window->display, window->xwindow,
05660                                            window->display->atom_SM_CLIENT_ID,
05661                                            &str))
05662             {
05663               if (window->sm_client_id == NULL) /* first time through */
05664                 meta_warning (_("Window %s sets SM_CLIENT_ID on itself, instead of on the WM_CLIENT_LEADER window as specified in the ICCCM.\n"),
05665                               window->desc);
05666               
05667               window->sm_client_id = g_strdup (str);
05668               meta_XFree (str);
05669             }
05670         }
05671     }
05672 
05673   meta_verbose ("Window %s client leader: 0x%lx SM_CLIENT_ID: '%s'\n",
05674                 window->desc, window->xclient_leader,
05675                 window->sm_client_id ? window->sm_client_id : "none");
05676 }
05677 
05678 static void
05679 update_role (MetaWindow *window)
05680 {
05681   char *str;
05682   
05683   if (window->role)
05684     g_free (window->role);
05685   window->role = NULL;
05686 
05687   if (meta_prop_get_latin1_string (window->display, window->xwindow,
05688                                    window->display->atom_WM_WINDOW_ROLE,
05689                                    &str))
05690     {
05691       window->role = g_strdup (str);
05692       meta_XFree (str);
05693     }
05694 
05695   meta_verbose ("Updated role of %s to '%s'\n",
05696                 window->desc, window->role ? window->role : "null");
05697 }
05698 
05699 static void
05700 update_net_wm_type (MetaWindow *window)
05701 {
05702   int n_atoms;
05703   Atom *atoms;
05704   int i;
05705 
05706   window->type_atom = None;
05707   n_atoms = 0;
05708   atoms = NULL;
05709   
05710   meta_prop_get_atom_list (window->display, window->xwindow, 
05711                            window->display->atom__NET_WM_WINDOW_TYPE,
05712                            &atoms, &n_atoms);
05713 
05714   i = 0;
05715   while (i < n_atoms)
05716     {
05717       /* We break as soon as we find one we recognize,
05718        * supposed to prefer those near the front of the list
05719        */
05720       if (atoms[i] == window->display->atom__NET_WM_WINDOW_TYPE_DESKTOP ||
05721           atoms[i] == window->display->atom__NET_WM_WINDOW_TYPE_DOCK ||
05722           atoms[i] == window->display->atom__NET_WM_WINDOW_TYPE_TOOLBAR ||
05723           atoms[i] == window->display->atom__NET_WM_WINDOW_TYPE_MENU ||
05724           atoms[i] == window->display->atom__NET_WM_WINDOW_TYPE_DIALOG ||
05725           atoms[i] == window->display->atom__NET_WM_WINDOW_TYPE_NORMAL ||
05726           atoms[i] == window->display->atom__NET_WM_WINDOW_TYPE_UTILITY ||
05727           atoms[i] == window->display->atom__NET_WM_WINDOW_TYPE_SPLASH)
05728         {
05729           window->type_atom = atoms[i];
05730           break;
05731         }
05732       
05733       ++i;
05734     }
05735   
05736   meta_XFree (atoms);
05737 
05738   if (meta_is_verbose ())
05739     {
05740       char *str;
05741 
05742       str = NULL;
05743       if (window->type_atom != None)
05744         {
05745           meta_error_trap_push (window->display);
05746           str = XGetAtomName (window->display->xdisplay, window->type_atom);
05747           meta_error_trap_pop (window->display, TRUE);
05748         }
05749 
05750       meta_verbose ("Window %s type atom %s\n", window->desc,
05751                     str ? str : "(none)");
05752 
05753       if (str)
05754         meta_XFree (str);
05755     }
05756   
05757   recalc_window_type (window);
05758 }
05759 
05760 static void
05761 redraw_icon (MetaWindow *window)
05762 {
05763   /* We could probably be smart and just redraw the icon here,
05764    * instead of the whole frame.
05765    */
05766   if (window->frame && (window->mapped || window->frame->mapped))
05767     meta_ui_queue_frame_draw (window->screen->ui, window->frame->xwindow);
05768 }
05769 
05770 void
05771 meta_window_update_icon_now (MetaWindow *window)
05772 {
05773   GdkPixbuf *icon;
05774   GdkPixbuf *mini_icon;
05775 
05776   icon = NULL;
05777   mini_icon = NULL;
05778   
05779   if (meta_read_icons (window->screen,
05780                        window->xwindow,
05781                        &window->icon_cache,
05782                        window->wm_hints_pixmap,
05783                        window->wm_hints_mask,
05784                        &icon,
05785                        META_ICON_WIDTH, META_ICON_HEIGHT,
05786                        &mini_icon,
05787                        META_MINI_ICON_WIDTH,
05788                        META_MINI_ICON_HEIGHT))
05789     {
05790       if (window->icon)
05791         g_object_unref (G_OBJECT (window->icon));
05792       
05793       if (window->mini_icon)
05794         g_object_unref (G_OBJECT (window->mini_icon));
05795       
05796       window->icon = icon;
05797       window->mini_icon = mini_icon;
05798 
05799       redraw_icon (window);
05800     }
05801   
05802   g_assert (window->icon);
05803   g_assert (window->mini_icon);
05804 }
05805 
05806 static gboolean
05807 idle_update_icon (gpointer data)
05808 {
05809   GSList *tmp;
05810   GSList *copy;
05811   guint queue_index = GPOINTER_TO_INT (data);
05812 
05813   meta_topic (META_DEBUG_GEOMETRY, "Clearing the update_icon queue\n");
05814 
05815   /* Work with a copy, for reentrancy. The allowed reentrancy isn't
05816    * complete; destroying a window while we're in here would result in
05817    * badness. But it's OK to queue/unqueue update_icons.
05818    */
05819   copy = g_slist_copy (queue_pending[queue_index]);
05820   g_slist_free (queue_pending[queue_index]);
05821   queue_pending[queue_index] = NULL;
05822   queue_idle[queue_index] = 0;
05823 
05824   destroying_windows_disallowed += 1;
05825   
05826   tmp = copy;
05827   while (tmp != NULL)
05828     {
05829       MetaWindow *window;
05830 
05831       window = tmp->data;
05832 
05833       meta_window_update_icon_now (window); 
05834       window->is_in_queues &= ~META_QUEUE_UPDATE_ICON;
05835       
05836       tmp = tmp->next;
05837     }
05838 
05839   g_slist_free (copy);
05840 
05841   destroying_windows_disallowed -= 1;
05842   
05843   return FALSE;
05844 }
05845 
05846 GList*
05847 meta_window_get_workspaces (MetaWindow *window)
05848 {
05849   if (window->on_all_workspaces)
05850     return window->screen->workspaces;
05851   else
05852     return window->workspace->list_containing_self;
05853 }
05854 
05855 static void
05856 invalidate_work_areas (MetaWindow *window)
05857 {
05858   GList *tmp;
05859 
05860   tmp = meta_window_get_workspaces (window);
05861   
05862   while (tmp != NULL)
05863     {
05864       meta_workspace_invalidate_work_area (tmp->data);
05865       tmp = tmp->next;
05866     }
05867 }
05868 
05869 void
05870 meta_window_update_struts (MetaWindow *window)
05871 {
05872   GSList *old_struts;
05873   GSList *new_struts;
05874   GSList *old_iter, *new_iter;
05875   gulong *struts = NULL;
05876   int nitems;
05877   gboolean changed;
05878 
05879   meta_verbose ("Updating struts for %s\n", window->desc);
05880 
05881   old_struts = window->struts;
05882   new_struts = NULL; 
05883 
05884   if (meta_prop_get_cardinal_list (window->display,
05885                                    window->xwindow,
05886                                    window->display->atom__NET_WM_STRUT_PARTIAL,
05887                                    &struts, &nitems))
05888     {
05889       if (nitems != 12)
05890         meta_verbose ("_NET_WM_STRUT_PARTIAL on %s has %d values instead "
05891                       "of 12\n",
05892                       window->desc, nitems);
05893       else
05894         {
05895           /* Pull out the strut info for each side in the hint */
05896           int i;
05897           for (i=0; i<4; i++)
05898             {
05899               MetaStrut *temp;
05900               int thickness, strut_begin, strut_end;
05901 
05902               thickness = struts[i];
05903               if (thickness == 0)
05904                 continue;
05905               strut_begin = struts[4+(i*2)];
05906               strut_end   = struts[4+(i*2)+1];
05907 
05908               temp = g_new (MetaStrut, 1);
05909               temp->side = 1 << i; /* See MetaDirection def.  Matches nicely, eh? */
05910               temp->rect = window->screen->rect;
05911               switch (temp->side)
05912                 {
05913                 case META_SIDE_RIGHT:
05914                   temp->rect.x = BOX_RIGHT(temp->rect) - thickness;
05915                   /* Intentionally fall through without breaking */
05916                 case META_SIDE_LEFT:
05917                   temp->rect.width  = thickness;
05918                   temp->rect.y      = strut_begin;
05919                   temp->rect.height = strut_end - strut_begin + 1;
05920                   break;
05921                 case META_SIDE_BOTTOM:
05922                   temp->rect.y = BOX_BOTTOM(temp->rect) - thickness;
05923                   /* Intentionally fall through without breaking */
05924                 case META_SIDE_TOP:
05925                   temp->rect.height = thickness;
05926                   temp->rect.x      = strut_begin;
05927                   temp->rect.width  = strut_end - strut_begin + 1;
05928                   break;
05929                 default:
05930                   g_assert_not_reached ();
05931                 }
05932 
05933               new_struts = g_slist_prepend (new_struts, temp);
05934             }
05935 
05936           meta_verbose ("_NET_WM_STRUT_PARTIAL struts %lu %lu %lu %lu for "
05937                         "window %s\n",
05938                         struts[0], struts[1], struts[2], struts[3], 
05939                         window->desc);
05940         }
05941       meta_XFree (struts);
05942     }
05943   else
05944     {
05945       meta_verbose ("No _NET_WM_STRUT property for %s\n",
05946                     window->desc);
05947     }
05948 
05949   if (!new_struts &&
05950       meta_prop_get_cardinal_list (window->display,
05951                                    window->xwindow,
05952                                    window->display->atom__NET_WM_STRUT,
05953                                    &struts, &nitems))
05954     {
05955       if (nitems != 4)
05956         meta_verbose ("_NET_WM_STRUT on %s has %d values instead of 4\n",
05957                       window->desc, nitems);
05958       else
05959         {
05960           /* Pull out the strut info for each side in the hint */
05961           int i;
05962           for (i=0; i<4; i++)
05963             {
05964               MetaStrut *temp;
05965               int thickness;
05966 
05967               thickness = struts[i];
05968               if (thickness == 0)
05969                 continue;
05970 
05971               temp = g_new (MetaStrut, 1);
05972               temp->side = 1 << i;
05973               temp->rect = window->screen->rect;
05974               switch (temp->side)
05975                 {
05976                 case META_SIDE_RIGHT:
05977                   temp->rect.x = BOX_RIGHT(temp->rect) - thickness;
05978                   /* Intentionally fall through without breaking */
05979                 case META_SIDE_LEFT:
05980                   temp->rect.width  = thickness;
05981                   break;
05982                 case META_SIDE_BOTTOM:
05983                   temp->rect.y = BOX_BOTTOM(temp->rect) - thickness;
05984                   /* Intentionally fall through without breaking */
05985                 case META_SIDE_TOP:
05986                   temp->rect.height = thickness;
05987                   break;
05988                 default:
05989                   g_assert_not_reached ();
05990                 }
05991 
05992               new_struts = g_slist_prepend (new_struts, temp);
05993             }
05994               
05995           meta_verbose ("_NET_WM_STRUT struts %lu %lu %lu %lu for window %s\n",
05996                         struts[0], struts[1], struts[2], struts[3], 
05997                         window->desc);
05998         }
05999       meta_XFree (struts);
06000     }
06001   else if (!new_struts)
06002     {
06003       meta_verbose ("No _NET_WM_STRUT property for %s\n",
06004                     window->desc);
06005     }
06006  
06007   /* Determine whether old_struts and new_struts are the same */
06008   old_iter = old_struts;
06009   new_iter = new_struts;
06010   while (old_iter && new_iter)
06011     {
06012       MetaStrut *old_strut = (MetaStrut*) old_iter->data;
06013       MetaStrut *new_strut = (MetaStrut*) new_iter->data;
06014 
06015       if (old_strut->side != new_strut->side ||
06016           !meta_rectangle_equal (&old_strut->rect, &new_strut->rect))
06017         break;
06018 
06019       old_iter = old_iter->next;
06020       new_iter = new_iter->next;
06021     }
06022   changed = (old_iter != NULL || new_iter != NULL);
06023 
06024   /* Update appropriately */
06025   meta_free_gslist_and_elements (old_struts);
06026   window->struts = new_struts;
06027   if (changed)
06028     {
06029       meta_topic (META_DEBUG_WORKAREA,
06030                   "Invalidating work areas of window %s due to struts update\n",
06031                   window->desc);
06032       invalidate_work_areas (window);
06033     }
06034   else
06035     {
06036       meta_topic (META_DEBUG_WORKAREA,
06037                   "Struts on %s were unchanged\n", window->desc);
06038     }
06039 }
06040 
06041 void
06042 meta_window_recalc_window_type (MetaWindow *window)
06043 {
06044   recalc_window_type (window);
06045 }
06046 
06047 static void
06048 recalc_window_type (MetaWindow *window)
06049 {
06050   MetaWindowType old_type;
06051 
06052   old_type = window->type;
06053   
06054   if (window->type_atom != None)
06055     {
06056       if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_DESKTOP)
06057         window->type = META_WINDOW_DESKTOP;
06058       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_DOCK)
06059         window->type = META_WINDOW_DOCK;
06060       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_TOOLBAR)
06061         window->type = META_WINDOW_TOOLBAR;
06062       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_MENU)
06063         window->type = META_WINDOW_MENU;
06064       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_DIALOG)
06065         window->type = META_WINDOW_DIALOG;
06066       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_NORMAL)
06067         window->type = META_WINDOW_NORMAL;
06068       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_UTILITY)
06069         window->type = META_WINDOW_UTILITY;
06070       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_SPLASH)
06071         window->type = META_WINDOW_SPLASHSCREEN;
06072       else
06073         meta_bug ("Set a type atom for %s that wasn't handled in recalc_window_type\n",
06074                   window->desc);
06075     }
06076   else if (window->xtransient_for != None)
06077     {
06078       window->type = META_WINDOW_DIALOG;
06079     }
06080   else
06081     {
06082       window->type = META_WINDOW_NORMAL;
06083     }
06084 
06085   if (window->type == META_WINDOW_DIALOG &&
06086       window->wm_state_modal)
06087     window->type = META_WINDOW_MODAL_DIALOG;
06088   
06089   meta_verbose ("Calculated type %u for %s, old type %u\n",
06090                 window->type, window->desc, old_type);
06091 
06092   if (old_type != window->type)
06093     {
06094       recalc_window_features (window);
06095       
06096       set_net_wm_state (window);
06097       
06098       /* Update frame */
06099       if (window->decorated)
06100         meta_window_ensure_frame (window);
06101       else
06102         meta_window_destroy_frame (window);
06103       
06104       /* update stacking constraints */
06105       meta_window_update_layer (window);
06106 
06107       meta_window_grab_keys (window);
06108     }
06109 }
06110 
06111 static void
06112 set_allowed_actions_hint (MetaWindow *window)
06113 {
06114 #define MAX_N_ACTIONS 12
06115   unsigned long data[MAX_N_ACTIONS];
06116   int i;
06117 
06118   i = 0;
06119   if (window->has_move_func)
06120     {
06121       data[i] = window->display->atom__NET_WM_ACTION_MOVE;
06122       ++i;
06123     }
06124   if (window->has_resize_func)
06125     {
06126       data[i] = window->display->atom__NET_WM_ACTION_RESIZE;
06127       ++i;
06128     }
06129   if (window->has_fullscreen_func)
06130     {
06131       data[i] = window->display->atom__NET_WM_ACTION_FULLSCREEN;
06132       ++i;
06133     }
06134   if (window->has_minimize_func)
06135     {
06136       data[i] = window->display->atom__NET_WM_ACTION_MINIMIZE;
06137       ++i;
06138     }
06139   if (window->has_shade_func)
06140     {
06141       data[i] = window->display->atom__NET_WM_ACTION_SHADE;
06142       ++i;
06143     }
06144   /* sticky according to EWMH is different from metacity's sticky;
06145    * metacity doesn't support EWMH sticky
06146    */
06147   if (window->has_maximize_func)
06148     {
06149       data[i] = window->display->atom__NET_WM_ACTION_MAXIMIZE_HORZ;
06150       ++i;
06151       data[i] = window->display->atom__NET_WM_ACTION_MAXIMIZE_VERT;
06152       ++i;
06153     }
06154   /* We always allow this */
06155   data[i] = window->display->atom__NET_WM_ACTION_CHANGE_DESKTOP;
06156   ++i;
06157   if (window->has_close_func)
06158     {
06159       data[i] = window->display->atom__NET_WM_ACTION_CLOSE;
06160       ++i;
06161     }
06162 
06163   /* I guess we always allow above/below operations */
06164   data[i] = window->display->atom__NET_WM_ACTION_ABOVE;
06165   ++i;
06166   data[i] = window->display->atom__NET_WM_ACTION_BELOW;
06167   ++i;
06168 
06169   g_assert (i <= MAX_N_ACTIONS);
06170 
06171   meta_verbose ("Setting _NET_WM_ALLOWED_ACTIONS with %d atoms\n", i);
06172   
06173   meta_error_trap_push (window->display);
06174   XChangeProperty (window->display->xdisplay, window->xwindow,
06175                    window->display->atom__NET_WM_ALLOWED_ACTIONS,
06176                    XA_ATOM,
06177                    32, PropModeReplace, (guchar*) data, i);
06178   meta_error_trap_pop (window->display, FALSE);
06179 #undef MAX_N_ACTIONS
06180 }
06181 
06182 void
06183 meta_window_recalc_features (MetaWindow *window)
06184 {
06185   recalc_window_features (window);
06186 }
06187 
06188 static void
06189 recalc_window_features (MetaWindow *window)
06190 {
06191   gboolean old_has_close_func;
06192   gboolean old_has_minimize_func;
06193   gboolean old_has_move_func;
06194   gboolean old_has_resize_func;
06195   gboolean old_has_shade_func;
06196   gboolean old_always_sticky;
06197 
06198   old_has_close_func = window->has_close_func;
06199   old_has_minimize_func = window->has_minimize_func;
06200   old_has_move_func = window->has_move_func;
06201   old_has_resize_func = window->has_resize_func;
06202   old_has_shade_func = window->has_shade_func;
06203   old_always_sticky = window->always_sticky;
06204 
06205   /* Use MWM hints initially */
06206   window->decorated = window->mwm_decorated;
06207   window->border_only = window->mwm_border_only;
06208   window->has_close_func = window->mwm_has_close_func;
06209   window->has_minimize_func = window->mwm_has_minimize_func;
06210   window->has_maximize_func = window->mwm_has_maximize_func;
06211   window->has_move_func = window->mwm_has_move_func;
06212     
06213   window->has_resize_func = TRUE;  
06214 
06215   /* If min_size == max_size, then don't allow resize */
06216   if (window->size_hints.min_width == window->size_hints.max_width &&
06217       window->size_hints.min_height == window->size_hints.max_height)
06218     window->has_resize_func = FALSE;
06219   else if (!window->mwm_has_resize_func)
06220     {
06221       /* We ignore mwm_has_resize_func because WM_NORMAL_HINTS is the
06222        * authoritative source for that info. Some apps such as mplayer or
06223        * xine disable resize via MWM but not WM_NORMAL_HINTS, but that
06224        * leads to e.g. us not fullscreening their windows.  Apps that set
06225        * MWM but not WM_NORMAL_HINTS are basically broken. We complain
06226        * about these apps but make them work.
06227        */
06228       
06229       meta_warning (_("Window %s sets an MWM hint indicating it isn't resizable, but sets min size %d x %d and max size %d x %d; this doesn't make much sense.\n"),
06230                     window->desc,
06231                     window->size_hints.min_width,
06232                     window->size_hints.min_height,
06233                     window->size_hints.max_width,
06234                     window->size_hints.max_height);
06235     }
06236 
06237   window->has_shade_func = TRUE;
06238   window->has_fullscreen_func = TRUE;
06239 
06240   window->always_sticky = FALSE;
06241   
06242   /* Semantic category overrides the MWM hints */
06243   if (window->type == META_WINDOW_TOOLBAR)
06244     window->decorated = FALSE;
06245 
06246   if (window->type == META_WINDOW_DESKTOP ||
06247       window->type == META_WINDOW_DOCK)
06248     window->always_sticky = TRUE;
06249   
06250   if (window->type == META_WINDOW_DESKTOP ||
06251       window->type == META_WINDOW_DOCK ||
06252       window->type == META_WINDOW_SPLASHSCREEN)
06253     {
06254       window->decorated = FALSE;
06255       window->has_close_func = FALSE;
06256       window->has_shade_func = FALSE;
06257 
06258       /* FIXME this keeps panels and things from using
06259        * NET_WM_MOVERESIZE; the problem is that some
06260        * panels (edge panels) have fixed possible locations,
06261        * and others ("floating panels") do not.
06262        *
06263        * Perhaps we should require edge panels to explicitly
06264        * disable movement?
06265        */
06266       window->has_move_func = FALSE;
06267       window->has_resize_func = FALSE;
06268     }
06269   
06270   if (window->type != META_WINDOW_NORMAL)
06271     {
06272       window->has_minimize_func = FALSE;
06273       window->has_maximize_func = FALSE;
06274       window->has_fullscreen_func = FALSE;
06275     }
06276 
06277   if (!window->has_resize_func)
06278     {      
06279       window->has_maximize_func = FALSE;
06280       
06281       /* don't allow fullscreen if we can't resize, unless the size
06282        * is entire screen size (kind of broken, because we
06283        * actually fullscreen to xinerama head size not screen size)
06284        */
06285       if (window->size_hints.min_width == window->screen->rect.width &&
06286           window->size_hints.min_height == window->screen->rect.height)
06287         ; /* leave fullscreen available */
06288       else
06289         window->has_fullscreen_func = FALSE;
06290     }
06291 
06292   /* We leave fullscreen windows decorated, just push the frame outside
06293    * the screen. This avoids flickering to unparent them.
06294    *
06295    * Note that setting has_resize_func = FALSE here must come after the
06296    * above code that may disable fullscreen, because if the window
06297    * is not resizable purely due to fullscreen, we don't want to
06298    * disable fullscreen mode.
06299    */
06300   if (window->fullscreen)
06301     {
06302       window->has_shade_func = FALSE;
06303       window->has_move_func = FALSE;
06304       window->has_resize_func = FALSE;
06305       window->has_maximize_func = FALSE;
06306     }
06307 
06308   meta_topic (META_DEBUG_WINDOW_OPS,
06309               "Window %s fullscreen = %d not resizable, maximizable = %d fullscreenable = %d min size %dx%d max size %dx%d\n",
06310               window->desc,
06311               window->fullscreen,
06312               window->has_maximize_func, window->has_fullscreen_func,
06313               window->size_hints.min_width,
06314               window->size_hints.min_height,
06315               window->size_hints.max_width,
06316               window->size_hints.max_height);
06317   
06318   /* no shading if not decorated */
06319   if (!window->decorated || window->border_only)
06320     window->has_shade_func = FALSE;
06321 
06322   window->skip_taskbar = FALSE;
06323   window->skip_pager = FALSE;
06324 
06325   if (window->wm_state_skip_taskbar)
06326     window->skip_taskbar = TRUE;
06327   
06328   if (window->wm_state_skip_pager)
06329     window->skip_pager = TRUE;
06330   
06331   switch (window->type)
06332     {
06333       /* Force skip taskbar/pager on these window types */
06334     case META_WINDOW_DESKTOP:
06335     case META_WINDOW_DOCK:
06336     case META_WINDOW_TOOLBAR:
06337     case META_WINDOW_MENU:
06338     case META_WINDOW_UTILITY:
06339     case META_WINDOW_SPLASHSCREEN:
06340       window->skip_taskbar = TRUE;
06341       window->skip_pager = TRUE;
06342       break;
06343 
06344     case META_WINDOW_DIALOG:
06345     case META_WINDOW_MODAL_DIALOG:
06346       /* only skip taskbar if we have a real transient parent */
06347       if (window->xtransient_for != None &&
06348           window->xtransient_for != window->screen->xroot)
06349         window->skip_taskbar = TRUE;
06350       break;
06351       
06352     case META_WINDOW_NORMAL:
06353       break;
06354     }
06355 
06356   meta_topic (META_DEBUG_WINDOW_OPS,
06357               "Window %s decorated = %d border_only = %d has_close = %d has_minimize = %d has_maximize = %d has_move = %d has_shade = %d skip_taskbar = %d skip_pager = %d\n",
06358               window->desc,
06359               window->decorated,
06360               window->border_only,
06361               window->has_close_func,
06362               window->has_minimize_func,
06363               window->has_maximize_func,
06364               window->has_move_func,
06365               window->has_shade_func,
06366               window->skip_taskbar,
06367               window->skip_pager);
06368   
06369   /* FIXME:
06370    * Lame workaround for recalc_window_features
06371    * being used overzealously. The fix is to
06372    * only recalc_window_features when something
06373    * has actually changed.
06374    */
06375   if (window->constructing                               ||
06376       old_has_close_func != window->has_close_func       ||
06377       old_has_minimize_func != window->has_minimize_func ||
06378       old_has_move_func != window->has_move_func         ||
06379       old_has_resize_func != window->has_resize_func     ||
06380       old_has_shade_func != window->has_shade_func       ||
06381       old_always_sticky != window->always_sticky)
06382     set_allowed_actions_hint (window);
06383     
06384   /* FIXME perhaps should ensure if we don't have a shade func,
06385    * we aren't shaded, etc.
06386    */
06387 }
06388 
06389 static void
06390 menu_callback (MetaWindowMenu *menu,
06391                Display        *xdisplay,
06392                Window          client_xwindow,
06393                guint32         timestamp,
06394                MetaMenuOp      op,
06395                int             workspace_index,
06396                gpointer        data)
06397 {
06398   MetaDisplay *display;
06399   MetaWindow *window;
06400   MetaWorkspace *workspace;
06401   
06402   display = meta_display_for_x_display (xdisplay);
06403   window = meta_display_lookup_x_window (display, client_xwindow);
06404   workspace = NULL;
06405   
06406   if (window != NULL) /* window can be NULL */
06407     {
06408       meta_verbose ("Menu op %u on %s\n", op, window->desc);
06409       
06410       /* op can be 0 for none */
06411       switch (op)
06412         {
06413         case META_MENU_OP_DELETE:
06414           meta_window_delete (window, timestamp);
06415           break;
06416 
06417         case META_MENU_OP_MINIMIZE:
06418           meta_window_minimize (window);
06419           break;
06420 
06421         case META_MENU_OP_UNMAXIMIZE:
06422           meta_window_unmaximize (window,
06423                                   META_MAXIMIZE_HORIZONTAL |
06424                                   META_MAXIMIZE_VERTICAL);
06425           break;
06426       
06427         case META_MENU_OP_MAXIMIZE:
06428           meta_window_maximize (window,
06429                                 META_MAXIMIZE_HORIZONTAL |
06430                                 META_MAXIMIZE_VERTICAL);
06431           break;
06432 
06433         case META_MENU_OP_UNSHADE:
06434           meta_window_unshade (window, timestamp);
06435           break;
06436       
06437         case META_MENU_OP_SHADE:
06438           meta_window_shade (window, timestamp);
06439           break;
06440       
06441         case META_MENU_OP_MOVE_LEFT:
06442           workspace = meta_workspace_get_neighbor (window->screen->active_workspace,
06443                                                    META_MOTION_LEFT);
06444           break;
06445 
06446         case META_MENU_OP_MOVE_RIGHT:
06447           workspace = meta_workspace_get_neighbor (window->screen->active_workspace,
06448                                                    META_MOTION_RIGHT);
06449           break;
06450 
06451         case META_MENU_OP_MOVE_UP:
06452           workspace = meta_workspace_get_neighbor (window->screen->active_workspace,
06453                                                    META_MOTION_UP);
06454           break;
06455 
06456         case META_MENU_OP_MOVE_DOWN:
06457           workspace = meta_workspace_get_neighbor (window->screen->active_workspace,
06458                                                    META_MOTION_DOWN);
06459           break;
06460 
06461         case META_MENU_OP_WORKSPACES:
06462           workspace = meta_screen_get_workspace_by_index (window->screen,
06463                                                           workspace_index);
06464           break;
06465 
06466         case META_MENU_OP_STICK:
06467           meta_window_stick (window);
06468           break;
06469 
06470         case META_MENU_OP_UNSTICK:
06471           meta_window_unstick (window);
06472           break;
06473 
06474         case META_MENU_OP_ABOVE:
06475         case META_MENU_OP_UNABOVE:
06476           if (window->wm_state_above == FALSE)
06477             meta_window_make_above (window);
06478           else
06479             meta_window_unmake_above (window);
06480           break;
06481 
06482         case META_MENU_OP_MOVE:
06483           meta_window_begin_grab_op (window,
06484                                      META_GRAB_OP_KEYBOARD_MOVING,
06485                                      TRUE,
06486                                      timestamp);
06487           break;
06488 
06489         case META_MENU_OP_RESIZE:
06490           meta_window_begin_grab_op (window,
06491                                      META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN,
06492                                      TRUE,
06493                                      timestamp);
06494           break;
06495 
06496         case META_MENU_OP_RECOVER:
06497           meta_window_shove_titlebar_onscreen (window);
06498           break;
06499           
06500         case 0:
06501           /* nothing */
06502           break;
06503           
06504         default:
06505           meta_warning (G_STRLOC": Unknown window op\n");
06506           break;
06507         }
06508 
06509       if (workspace)
06510         {
06511           meta_window_change_workspace (window,
06512                                         workspace);
06513 #if 0
06514           meta_workspace_activate (workspace);
06515           meta_window_raise (window);
06516 #endif
06517         }
06518     }
06519   else
06520     {
06521       meta_verbose ("Menu callback on nonexistent window\n");
06522     }
06523 
06524   if (display->window_menu == menu)
06525     {
06526       display->window_menu = NULL;
06527       display->window_with_menu = NULL;
06528     }
06529   
06530   meta_ui_window_menu_free (menu);
06531 }
06532 
06533 void
06534 meta_window_show_menu (MetaWindow *window,
06535                        int         root_x,
06536                        int         root_y,
06537                        int         button,
06538                        guint32     timestamp)
06539 {
06540   MetaMenuOp ops;
06541   MetaMenuOp insensitive;
06542   MetaWindowMenu *menu;
06543   MetaWorkspaceLayout layout;
06544   int n_workspaces;
06545   gboolean ltr;
06546   
06547   if (window->display->window_menu)
06548     {
06549       meta_ui_window_menu_free (window->display->window_menu);
06550       window->display->window_menu = NULL;
06551       window->display->window_with_menu = NULL;
06552     }
06553 
06554   ops = 0;
06555   insensitive = 0;
06556 
06557   ops |= (META_MENU_OP_DELETE | META_MENU_OP_MINIMIZE | META_MENU_OP_MOVE | META_MENU_OP_RESIZE);
06558 
06559   if (!meta_window_titlebar_is_onscreen (window) &&
06560       window->type != META_WINDOW_DOCK &&
06561       window->type != META_WINDOW_DESKTOP)
06562     ops |= META_MENU_OP_RECOVER;
06563 
06564   n_workspaces = meta_screen_get_n_workspaces (window->screen);
06565 
06566   if (n_workspaces > 1)
06567     ops |= META_MENU_OP_WORKSPACES;
06568 
06569   meta_screen_calc_workspace_layout (window->screen,
06570                                      n_workspaces,
06571                                      meta_workspace_index ( window->screen->active_workspace),
06572                                      &layout);
06573 
06574   if (!window->on_all_workspaces)
06575     {
06576       ltr = meta_ui_get_direction() == META_UI_DIRECTION_LTR;
06577 
06578       if (layout.current_col > 0)
06579         ops |= ltr ? META_MENU_OP_MOVE_LEFT : META_MENU_OP_MOVE_RIGHT;
06580       if ((layout.current_col < layout.cols - 1) &&
06581           (layout.current_row * layout.cols + (layout.current_col + 1) < n_workspaces))
06582         ops |= ltr ? META_MENU_OP_MOVE_RIGHT : META_MENU_OP_MOVE_LEFT;
06583       if (layout.current_row > 0)
06584         ops |= META_MENU_OP_MOVE_UP;
06585       if ((layout.current_row < layout.rows - 1) &&
06586           ((layout.current_row + 1) * layout.cols + layout.current_col < n_workspaces))
06587         ops |= META_MENU_OP_MOVE_DOWN;
06588     }
06589 
06590   meta_screen_free_workspace_layout (&layout);
06591 
06592   if (META_WINDOW_MAXIMIZED (window))
06593     ops |= META_MENU_OP_UNMAXIMIZE;
06594   else
06595     ops |= META_MENU_OP_MAXIMIZE;
06596   
06597 #if 0
06598   if (window->shaded)
06599     ops |= META_MENU_OP_UNSHADE;
06600   else
06601     ops |= META_MENU_OP_SHADE;
06602 #endif
06603 
06604   ops |= META_MENU_OP_UNSTICK;
06605   ops |= META_MENU_OP_STICK;
06606 
06607   if (window->wm_state_above)
06608     ops |= META_MENU_OP_UNABOVE;
06609   else
06610     ops |= META_MENU_OP_ABOVE;
06611   
06612   if (!window->has_maximize_func)
06613     insensitive |= META_MENU_OP_UNMAXIMIZE | META_MENU_OP_MAXIMIZE;
06614   
06615   if (!window->has_minimize_func)
06616     insensitive |= META_MENU_OP_MINIMIZE;
06617   
06618   if (!window->has_close_func)
06619     insensitive |= META_MENU_OP_DELETE;
06620 
06621   if (!window->has_shade_func)
06622     insensitive |= META_MENU_OP_SHADE | META_MENU_OP_UNSHADE;
06623 
06624   if (!META_WINDOW_ALLOWS_MOVE (window))
06625     insensitive |= META_MENU_OP_MOVE;
06626 
06627   if (!META_WINDOW_ALLOWS_RESIZE (window))
06628     insensitive |= META_MENU_OP_RESIZE;
06629 
06630    if (window->always_sticky)
06631      insensitive |= META_MENU_OP_STICK | META_MENU_OP_UNSTICK | META_MENU_OP_WORKSPACES;
06632 
06633   if ((window->type == META_WINDOW_DESKTOP) ||
06634       (window->type == META_WINDOW_DOCK) ||
06635       (window->type == META_WINDOW_SPLASHSCREEN))
06636     insensitive |= META_MENU_OP_ABOVE | META_MENU_OP_UNABOVE;
06637 
06638   /* If all operations are disabled, just quit without showing the menu.
06639    * This is the case, for example, with META_WINDOW_DESKTOP windows.
06640    */
06641   if ((ops & ~insensitive) == 0)
06642     return;
06643   
06644   menu =
06645     meta_ui_window_menu_new (window->screen->ui,
06646                              window->xwindow,
06647                              ops,
06648                              insensitive,
06649                              meta_window_get_net_wm_desktop (window),
06650                              meta_screen_get_n_workspaces (window->screen),
06651                              menu_callback,
06652                              NULL); 
06653 
06654   window->display->window_menu = menu;
06655   window->display->window_with_menu = window;
06656   
06657   meta_verbose ("Popping up window menu for %s\n", window->desc);
06658   
06659   meta_ui_window_menu_popup (menu, root_x, root_y, button, timestamp);
06660 }
06661 
06662 void
06663 meta_window_shove_titlebar_onscreen (MetaWindow *window)
06664 {
06665   MetaRectangle  outer_rect;
06666   GList         *onscreen_region;
06667   int            horiz_amount, vert_amount;
06668   int            newx, newy;
06669 
06670   /* If there's no titlebar, don't bother */
06671   if (!window->frame)
06672     return;
06673 
06674   /* Get the basic info we need */
06675   meta_window_get_outer_rect (window, &outer_rect);
06676   onscreen_region = window->screen->active_workspace->screen_region;
06677 
06678   /* Extend the region (just in case the window is too big to fit on the
06679    * screen), then shove the window on screen, then return the region to
06680    * normal.
06681    */
06682   horiz_amount = outer_rect.width;
06683   vert_amount  = outer_rect.height;
06684   meta_rectangle_expand_region (onscreen_region,
06685                                 horiz_amount,
06686                                 horiz_amount, 
06687                                 0,
06688                                 vert_amount);
06689   meta_rectangle_shove_into_region(onscreen_region,
06690                                    FIXED_DIRECTION_X,
06691                                    &outer_rect);
06692   meta_rectangle_expand_region (onscreen_region,
06693                                 -horiz_amount,
06694                                 -horiz_amount, 
06695                                 0,
06696                                 -vert_amount);
06697 
06698   newx = outer_rect.x + window->frame->child_x;
06699   newy = outer_rect.y + window->frame->child_y;
06700   meta_window_move_resize (window,
06701                            FALSE,
06702                            newx,
06703                            newy,
06704                            window->rect.width, 
06705                            window->rect.height);
06706 }
06707 
06708 gboolean
06709 meta_window_titlebar_is_onscreen (MetaWindow *window)
06710 {
06711   MetaRectangle  titlebar_rect;
06712   GList         *onscreen_region;
06713   int            titlebar_size;
06714   gboolean       is_onscreen;
06715 
06716   const int min_height_needed  = 8;
06717   const int min_width_percent  = 0.5;
06718   const int min_width_absolute = 50;
06719 
06720   /* Titlebar can't be offscreen if there is no titlebar... */
06721   if (!window->frame)
06722     return FALSE;
06723   
06724   /* Get the rectangle corresponding to the titlebar */
06725   meta_window_get_outer_rect (window, &titlebar_rect);
06726   titlebar_rect.height = window->frame->child_y;
06727   titlebar_size = meta_rectangle_area (&titlebar_rect);
06728 
06729   /* Run through the spanning rectangles for the screen and see if one of
06730    * them overlaps with the titlebar sufficiently to consider it onscreen.
06731    */
06732   is_onscreen = FALSE;
06733   onscreen_region = window->screen->active_workspace->screen_region;
06734   while (onscreen_region)
06735     {
06736       MetaRectangle *spanning_rect = onscreen_region->data;
06737       MetaRectangle overlap;
06738       
06739       meta_rectangle_intersect (&titlebar_rect, spanning_rect, &overlap);
06740       if (overlap.height > MIN (titlebar_rect.height, min_height_needed) &&
06741           overlap.width  > MIN (titlebar_rect.width * min_width_percent, 
06742                                 min_width_absolute))
06743         {
06744           is_onscreen = TRUE;
06745           break;
06746         }
06747         
06748       onscreen_region = onscreen_region->next;
06749     }
06750 
06751   return is_onscreen;
06752 }
06753 
06754 static double
06755 timeval_to_ms (const GTimeVal *timeval)
06756 {
06757   return (timeval->tv_sec * G_USEC_PER_SEC + timeval->tv_usec) / 1000.0;
06758 }
06759 
06760 static double
06761 time_diff (const GTimeVal *first,
06762            const GTimeVal *second)
06763 {
06764   double first_ms = timeval_to_ms (first);
06765   double second_ms = timeval_to_ms (second);
06766 
06767   return first_ms - second_ms;
06768 }
06769 
06770 static gboolean
06771 check_moveresize_frequency (MetaWindow *window, 
06772                             gdouble    *remaining)
06773 {
06774   GTimeVal current_time;
06775   
06776   g_get_current_time (&current_time);
06777 
06778 #ifdef HAVE_XSYNC
06779   if (!window->disable_sync &&
06780       window->display->grab_sync_request_alarm != None)
06781     {
06782       if (window->sync_request_time.tv_sec != 0 ||
06783           window->sync_request_time.tv_usec != 0)
06784         {
06785           double elapsed =
06786             time_diff (&current_time, &window->sync_request_time);
06787 
06788           if (elapsed < 1000.0)
06789             {
06790               /* We want to be sure that the timeout happens at
06791                * a time where elapsed will definitely be
06792                * greater than 1000, so we can disable sync
06793                */
06794               if (remaining)
06795                 *remaining = 1000.0 - elapsed + 100;
06796               
06797               return FALSE;
06798             }
06799           else
06800             {
06801               /* We have now waited for more than a second for the
06802                * application to respond to the sync request
06803                */
06804               window->disable_sync = TRUE;
06805               return TRUE;
06806             }
06807         }
06808       else
06809         {
06810           /* No outstanding sync requests. Go ahead and resize
06811            */
06812           return TRUE;
06813         }
06814     }
06815   else
06816 #endif /* HAVE_XSYNC */
06817     {
06818       const double max_resizes_per_second = 25.0;
06819       const double ms_between_resizes = 1000.0 / max_resizes_per_second;
06820       double elapsed;
06821 
06822       elapsed = time_diff (&current_time, &window->display->grab_last_moveresize_time);
06823 
06824       if (elapsed >= 0.0 && elapsed < ms_between_resizes)
06825         {
06826           meta_topic (META_DEBUG_RESIZING,
06827                       "Delaying move/resize as only %g of %g ms elapsed\n",
06828                       elapsed, ms_between_resizes);
06829           
06830           if (remaining)
06831             *remaining = (ms_between_resizes - elapsed);
06832 
06833           return FALSE;
06834         }
06835       
06836       meta_topic (META_DEBUG_RESIZING,
06837                   " Checked moveresize freq, allowing move/resize now (%g of %g seconds elapsed)\n",
06838                   elapsed / 1000.0, 1.0 / max_resizes_per_second);
06839       
06840       return TRUE;
06841     }
06842 }
06843 
06844 static gboolean
06845 update_move_timeout (gpointer data)
06846 {
06847   MetaWindow *window = data;
06848 
06849   update_move (window, 
06850                window->display->grab_last_user_action_was_snap,
06851                window->display->grab_latest_motion_x,
06852                window->display->grab_latest_motion_y);
06853 
06854   return FALSE;
06855 }
06856 
06857 static void
06858 update_move (MetaWindow  *window,
06859              gboolean     snap,
06860              int          x,
06861              int          y)
06862 {
06863   int dx, dy;
06864   int new_x, new_y;
06865   MetaRectangle old;
06866   int shake_threshold;
06867   MetaDisplay *display = window->display;
06868   
06869   display->grab_latest_motion_x = x;
06870   display->grab_latest_motion_y = y;
06871   
06872   dx = x - display->grab_anchor_root_x;
06873   dy = y - display->grab_anchor_root_y;
06874 
06875   new_x = display->grab_anchor_window_pos.x + dx;
06876   new_y = display->grab_anchor_window_pos.y + dy;
06877 
06878   meta_verbose ("x,y = %d,%d anchor ptr %d,%d anchor pos %d,%d dx,dy %d,%d\n",
06879                 x, y,
06880                 display->grab_anchor_root_x,
06881                 display->grab_anchor_root_y,
06882                 display->grab_anchor_window_pos.x,
06883                 display->grab_anchor_window_pos.y,
06884                 dx, dy);
06885 
06886   /* Don't bother doing anything if no move has been specified.  (This
06887    * happens often, even in keyboard moving, due to the warping of the
06888    * pointer.
06889    */
06890   if (dx == 0 && dy == 0)
06891     return;
06892 
06893   /* shake loose (unmaximize) maximized window if dragged beyond the threshold
06894    * in the Y direction. You can't pull a window loose via X motion.
06895    */
06896 
06897 #define DRAG_THRESHOLD_TO_SHAKE_THRESHOLD_FACTOR 6
06898   shake_threshold = meta_ui_get_drag_threshold (window->screen->ui) *
06899     DRAG_THRESHOLD_TO_SHAKE_THRESHOLD_FACTOR;
06900     
06901   if (META_WINDOW_MAXIMIZED (window) && ABS (dy) >= shake_threshold)
06902     {
06903       double prop;
06904 
06905       /* Shake loose */
06906       window->shaken_loose = TRUE;
06907                   
06908       /* move the unmaximized window to the cursor */
06909       prop = 
06910         ((double)(x - display->grab_initial_window_pos.x)) / 
06911         ((double)display->grab_initial_window_pos.width);
06912 
06913       display->grab_initial_window_pos.x = 
06914         x - window->saved_rect.width * prop;
06915       display->grab_initial_window_pos.y = y;
06916 
06917       if (window->frame)
06918         {
06919           display->grab_initial_window_pos.y += window->frame->child_y / 2;
06920         }
06921 
06922       window->saved_rect.x = display->grab_initial_window_pos.x;
06923       window->saved_rect.y = display->grab_initial_window_pos.y;
06924       display->grab_anchor_root_x = x;
06925       display->grab_anchor_root_y = y;
06926 
06927       meta_window_unmaximize (window,
06928                               META_MAXIMIZE_HORIZONTAL |
06929                               META_MAXIMIZE_VERTICAL);
06930 
06931       return;
06932     }
06933   /* remaximize window on an other xinerama monitor if window has
06934    * been shaken loose or it is still maximized (then move straight)
06935    */
06936   else if (window->shaken_loose || META_WINDOW_MAXIMIZED (window))
06937     {
06938       const MetaXineramaScreenInfo *wxinerama;
06939       MetaRectangle work_area;
06940       int monitor;
06941 
06942       wxinerama = meta_screen_get_xinerama_for_window (window->screen, window);
06943 
06944       for (monitor = 0; monitor < window->screen->n_xinerama_infos; monitor++)
06945         {
06946           meta_window_get_work_area_for_xinerama (window, monitor, &work_area);
06947 
06948           /* check if cursor is near the top of a xinerama work area */ 
06949           if (x >= work_area.x &&
06950               x < (work_area.x + work_area.width) &&
06951               y >= work_area.y &&
06952               y < (work_area.y + shake_threshold))
06953             {
06954               /* move the saved rect if window will become maximized on an
06955                * other monitor so user isn't surprised on a later unmaximize
06956                */
06957               if (wxinerama->number != monitor)
06958                 {
06959                   window->saved_rect.x = work_area.x;
06960                   window->saved_rect.y = work_area.y;
06961                   
06962                   if (window->frame) 
06963                     {
06964                       window->saved_rect.x += window->frame->child_x;
06965                       window->saved_rect.y += window->frame->child_y;
06966                     }
06967 
06968                   window->user_rect.x = window->saved_rect.x;
06969                   window->user_rect.y = window->saved_rect.y;
06970 
06971                   meta_window_unmaximize (window,
06972                                           META_MAXIMIZE_HORIZONTAL |
06973                                           META_MAXIMIZE_VERTICAL);
06974                 }
06975 
06976               display->grab_initial_window_pos = work_area;
06977               display->grab_anchor_root_x = x;
06978               display->grab_anchor_root_y = y;
06979               window->shaken_loose = FALSE;
06980               
06981               meta_window_maximize (window,
06982                                     META_MAXIMIZE_HORIZONTAL |
06983                                     META_MAXIMIZE_VERTICAL);
06984 
06985               return;
06986             }
06987         }
06988     }
06989 
06990   if (display->grab_wireframe_active)
06991     old = display->grab_wireframe_rect;
06992   else
06993     meta_window_get_client_root_coords (window, &old);
06994 
06995   /* Don't allow movement in the maximized directions */
06996   if (window->maximized_horizontally)
06997     new_x = old.x;
06998   if (window->maximized_vertically)
06999     new_y = old.y;
07000 
07001   /* Do any edge resistance/snapping */
07002   meta_window_edge_resistance_for_move (window, 
07003                                         old.x,
07004                                         old.y,
07005                                         &new_x,
07006                                         &new_y,
07007                                         update_move_timeout,
07008                                         snap,
07009                                         FALSE);
07010 
07011   if (display->compositor)
07012     {
07013       int root_x = new_x - display->grab_anchor_window_pos.x + display->grab_anchor_root_x;
07014       int root_y = new_y - display->grab_anchor_window_pos.y + display->grab_anchor_root_y;
07015       
07016       meta_compositor_update_move (display->compositor,
07017                                    window, root_x, root_y);
07018     }
07019   
07020   if (display->grab_wireframe_active)
07021     meta_window_update_wireframe (window, new_x, new_y, 
07022                                   display->grab_wireframe_rect.width,
07023                                   display->grab_wireframe_rect.height);
07024   else
07025     meta_window_move (window, TRUE, new_x, new_y);
07026 }
07027 
07028 static gboolean
07029 update_resize_timeout (gpointer data)
07030 {
07031   MetaWindow *window = data;
07032 
07033   update_resize (window, 
07034                  window->display->grab_last_user_action_was_snap,
07035                  window->display->grab_latest_motion_x,
07036                  window->display->grab_latest_motion_y,
07037                  TRUE);
07038   return FALSE;
07039 }
07040 
07041 static void
07042 update_resize (MetaWindow *window,
07043                gboolean    snap,
07044                int x, int y,
07045                gboolean force)
07046 {
07047   int dx, dy;
07048   int new_w, new_h;
07049   int gravity;
07050   MetaRectangle old;
07051   int new_x, new_y;
07052   double remaining;
07053   
07054   window->display->grab_latest_motion_x = x;
07055   window->display->grab_latest_motion_y = y;
07056   
07057   dx = x - window->display->grab_anchor_root_x;
07058   dy = y - window->display->grab_anchor_root_y;
07059 
07060   new_w = window->display->grab_anchor_window_pos.width;
07061   new_h = window->display->grab_anchor_window_pos.height;
07062 
07063   /* Don't bother doing anything if no move has been specified.  (This
07064    * happens often, even in keyboard resizing, due to the warping of the
07065    * pointer.
07066    */
07067   if (dx == 0 && dy == 0)
07068     return;
07069   
07070   /* FIXME this is only used in wireframe mode */
07071   new_x = window->display->grab_anchor_window_pos.x;
07072   new_y = window->display->grab_anchor_window_pos.y;
07073 
07074   if (window->display->grab_op == META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN)
07075     {
07076       if ((dx > 0) && (dy > 0))
07077         {
07078           window->display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_SE;
07079           meta_window_update_keyboard_resize (window, TRUE);
07080         }
07081       else if ((dx < 0) && (dy > 0))
07082         {
07083           window->display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_SW;
07084           meta_window_update_keyboard_resize (window, TRUE);
07085         }
07086       else if ((dx > 0) && (dy < 0))
07087         {
07088           window->display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_NE;
07089           meta_window_update_keyboard_resize (window, TRUE);
07090         }
07091       else if ((dx < 0) && (dy < 0))
07092         {
07093           window->display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_NW;
07094           meta_window_update_keyboard_resize (window, TRUE);
07095         }
07096       else if (dx < 0)
07097         {
07098           window->display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_W;
07099           meta_window_update_keyboard_resize (window, TRUE);
07100         }
07101       else if (dx > 0)
07102         {
07103           window->display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_E;
07104           meta_window_update_keyboard_resize (window, TRUE);
07105         }
07106       else if (dy > 0)
07107         {
07108           window->display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_S;
07109           meta_window_update_keyboard_resize (window, TRUE);
07110         }
07111       else if (dy < 0)
07112         {
07113           window->display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_N;
07114           meta_window_update_keyboard_resize (window, TRUE);
07115         }
07116     }
07117   
07118   /* FIXME: This stupidity only needed because of wireframe mode and
07119    * the fact that wireframe isn't making use of
07120    * meta_rectangle_resize_with_gravity().  If we were to use that, we
07121    * could just increment new_w and new_h by dx and dy in all cases.
07122    */
07123   switch (window->display->grab_op)
07124     {
07125     case META_GRAB_OP_RESIZING_SE:
07126     case META_GRAB_OP_RESIZING_NE:
07127     case META_GRAB_OP_RESIZING_E:
07128     case META_GRAB_OP_KEYBOARD_RESIZING_SE:
07129     case META_GRAB_OP_KEYBOARD_RESIZING_NE:
07130     case META_GRAB_OP_KEYBOARD_RESIZING_E:
07131       new_w += dx;
07132       break;
07133 
07134     case META_GRAB_OP_RESIZING_NW:
07135     case META_GRAB_OP_RESIZING_SW:
07136     case META_GRAB_OP_RESIZING_W:
07137     case META_GRAB_OP_KEYBOARD_RESIZING_NW:
07138     case META_GRAB_OP_KEYBOARD_RESIZING_SW:
07139     case META_GRAB_OP_KEYBOARD_RESIZING_W:
07140       new_w -= dx;
07141       new_x += dx;
07142       break;
07143       
07144     default:
07145       break;
07146     }
07147   
07148   switch (window->display->grab_op)
07149     {
07150     case META_GRAB_OP_RESIZING_SE:
07151     case META_GRAB_OP_RESIZING_S:
07152     case META_GRAB_OP_RESIZING_SW:
07153     case META_GRAB_OP_KEYBOARD_RESIZING_SE:
07154     case META_GRAB_OP_KEYBOARD_RESIZING_S:
07155     case META_GRAB_OP_KEYBOARD_RESIZING_SW:
07156       new_h += dy;
07157       break;
07158       
07159     case META_GRAB_OP_RESIZING_N:
07160     case META_GRAB_OP_RESIZING_NE:
07161     case META_GRAB_OP_RESIZING_NW:
07162     case META_GRAB_OP_KEYBOARD_RESIZING_N:
07163     case META_GRAB_OP_KEYBOARD_RESIZING_NE:
07164     case META_GRAB_OP_KEYBOARD_RESIZING_NW:
07165       new_h -= dy;
07166       new_y += dy;
07167       break;
07168     default:
07169       break;
07170     }
07171 
07172   if (!check_moveresize_frequency (window, &remaining) && !force)
07173     {
07174       /* we are ignoring an event here, so we schedule a
07175        * compensation event when we would otherwise not ignore
07176        * an event. Otherwise we can become stuck if the user never
07177        * generates another event.
07178        */
07179       if (!window->display->grab_resize_timeout_id)
07180         {
07181           window->display->grab_resize_timeout_id = 
07182             g_timeout_add ((int)remaining, update_resize_timeout, window);
07183         }
07184 
07185       return;
07186     }
07187 
07188   /* If we get here, it means the client should have redrawn itself */
07189   if (window->display->compositor)
07190     meta_compositor_set_updates (window->display->compositor, window, TRUE);
07191 
07192   /* Remove any scheduled compensation events */
07193   if (window->display->grab_resize_timeout_id)
07194     {
07195       g_source_remove (window->display->grab_resize_timeout_id);
07196       window->display->grab_resize_timeout_id = 0;
07197     }
07198   
07199   if (window->display->grab_wireframe_active)
07200     old = window->display->grab_wireframe_rect;
07201   else
07202     old = window->rect;  /* Don't actually care about x,y */
07203 
07204   /* One sided resizing ought to actually be one-sided, despite the fact that
07205    * aspect ratio windows don't interact nicely with the above stuff.  So,
07206    * to avoid some nasty flicker, we enforce that.
07207    */
07208   switch (window->display->grab_op)
07209     {
07210     case META_GRAB_OP_RESIZING_S:
07211     case META_GRAB_OP_RESIZING_N:
07212       new_w = old.width;
07213       break;
07214       
07215     case META_GRAB_OP_RESIZING_E:
07216     case META_GRAB_OP_RESIZING_W:
07217       new_h = old.height;
07218       break;
07219 
07220     default:
07221       break;
07222     }
07223 
07224   /* compute gravity of client during operation */
07225   gravity = meta_resize_gravity_from_grab_op (window->display->grab_op);
07226   g_assert (gravity >= 0);
07227   
07228   /* Do any edge resistance/snapping */
07229   meta_window_edge_resistance_for_resize (window,
07230                                           old.width,
07231                                           old.height,
07232                                           &new_w,
07233                                           &new_h,
07234                                           gravity,
07235                                           update_resize_timeout,
07236                                           snap,
07237                                           FALSE);
07238 
07239   if (window->display->grab_wireframe_active)
07240     {
07241       if ((new_x + new_w <= new_x) || (new_y + new_h <= new_y))
07242         return;
07243 
07244       /* FIXME This is crap. For example, the wireframe isn't
07245        * constrained in the way that a real resize would be. An
07246        * obvious elegant solution is to unmap the window during
07247        * wireframe, but still resize it; however, that probably
07248        * confuses broken clients that have problems with opaque
07249        * resize, they probably don't track their visibility.
07250        */
07251       meta_window_update_wireframe (window, new_x, new_y, new_w, new_h);
07252     }
07253   else
07254     {
07255       /* We don't need to update unless the specified width and height
07256        * are actually different from what we had before.
07257        */
07258       if (old.width != new_w || old.height != new_h)
07259         meta_window_resize_with_gravity (window, TRUE, new_w, new_h, gravity);
07260     }
07261 
07262   /* Store the latest resize time, if we actually resized. */
07263   if (window->rect.width != old.width || window->rect.height != old.height)
07264     g_get_current_time (&window->display->grab_last_moveresize_time);
07265 }
07266 
07267 typedef struct
07268 {
07269   const XEvent *current_event;
07270   int           count;
07271   guint32       last_time;
07272 } EventScannerData;
07273 
07274 static Bool
07275 find_last_time_predicate (Display  *display,
07276                           XEvent   *xevent,
07277                           XPointer  arg)
07278 {
07279   EventScannerData *esd = (void*) arg;
07280 
07281   if (esd->current_event->type == xevent->type &&
07282       esd->current_event->xany.window == xevent->xany.window)
07283     {
07284       esd->count += 1;
07285       esd->last_time = xevent->xmotion.time;
07286     }
07287 
07288   return False;
07289 }
07290 
07291 static gboolean
07292 check_use_this_motion_notify (MetaWindow *window,
07293                               XEvent     *event)
07294 {
07295   EventScannerData esd;
07296   XEvent useless;
07297 
07298   /* This code is copied from Owen's GDK code. */
07299   
07300   if (window->display->grab_motion_notify_time != 0)
07301     {
07302       /* == is really the right test, but I'm all for paranoia */
07303       if (window->display->grab_motion_notify_time <=
07304           event->xmotion.time)
07305         {
07306           meta_topic (META_DEBUG_RESIZING,
07307                       "Arrived at event with time %u (waiting for %u), using it\n",
07308                       (unsigned int)event->xmotion.time,
07309                       window->display->grab_motion_notify_time);
07310           window->display->grab_motion_notify_time = 0;
07311           return TRUE;
07312         }
07313       else
07314         return FALSE; /* haven't reached the saved timestamp yet */
07315     }
07316   
07317   esd.current_event = event;
07318   esd.count = 0;
07319   esd.last_time = 0;
07320 
07321   /* "useless" isn't filled in because the predicate never returns True */
07322   XCheckIfEvent (window->display->xdisplay,
07323                  &useless,
07324                  find_last_time_predicate,
07325                  (XPointer) &esd);
07326 
07327   if (esd.count > 0)
07328     meta_topic (META_DEBUG_RESIZING,
07329                 "Will skip %d motion events and use the event with time %u\n",
07330                 esd.count, (unsigned int) esd.last_time);
07331   
07332   if (esd.last_time == 0)
07333     return TRUE;
07334   else
07335     {
07336       /* Save this timestamp, and ignore all motion notify
07337        * until we get to the one with this stamp.
07338        */
07339       window->display->grab_motion_notify_time = esd.last_time;
07340       return FALSE;
07341     }
07342 }
07343 
07344 void
07345 meta_window_handle_mouse_grab_op_event (MetaWindow *window,
07346                                         XEvent     *event)
07347 {
07348 #ifdef HAVE_XSYNC
07349   if (event->type == (window->display->xsync_event_base + XSyncAlarmNotify))
07350     {
07351       meta_topic (META_DEBUG_RESIZING,
07352                   "Alarm event received last motion x = %d y = %d\n",
07353                   window->display->grab_latest_motion_x,
07354                   window->display->grab_latest_motion_y);
07355 
07356       /* If sync was previously disabled, turn it back on and hope
07357        * the application has come to its senses (maybe it was just
07358        * busy with a pagefault or a long computation).
07359        */
07360       window->disable_sync = FALSE;
07361       window->sync_request_time.tv_sec = 0;
07362       window->sync_request_time.tv_usec = 0;
07363       
07364       /* This means we are ready for another configure. */
07365       switch (window->display->grab_op)
07366         {
07367         case META_GRAB_OP_RESIZING_E:
07368         case META_GRAB_OP_RESIZING_W:
07369         case META_GRAB_OP_RESIZING_S:
07370         case META_GRAB_OP_RESIZING_N:
07371         case META_GRAB_OP_RESIZING_SE:
07372         case META_GRAB_OP_RESIZING_SW:
07373         case META_GRAB_OP_RESIZING_NE:
07374         case META_GRAB_OP_RESIZING_NW:
07375         case META_GRAB_OP_KEYBOARD_RESIZING_S:
07376         case META_GRAB_OP_KEYBOARD_RESIZING_N:
07377         case META_GRAB_OP_KEYBOARD_RESIZING_W:
07378         case META_GRAB_OP_KEYBOARD_RESIZING_E:
07379         case META_GRAB_OP_KEYBOARD_RESIZING_SE:
07380         case META_GRAB_OP_KEYBOARD_RESIZING_NE:
07381         case META_GRAB_OP_KEYBOARD_RESIZING_SW:
07382         case META_GRAB_OP_KEYBOARD_RESIZING_NW:
07383           /* no pointer round trip here, to keep in sync */
07384           update_resize (window,
07385                          window->display->grab_last_user_action_was_snap,
07386                          window->display->grab_latest_motion_x,
07387                          window->display->grab_latest_motion_y,
07388                          TRUE);
07389           break;
07390           
07391         default:
07392           break;
07393         }
07394     }
07395 #endif /* HAVE_XSYNC */
07396   
07397   switch (event->type)
07398     {
07399     case ButtonRelease:
07400       meta_display_check_threshold_reached (window->display,
07401                                             event->xbutton.x_root,
07402                                             event->xbutton.y_root);
07403       /* If the user was snap moving then ignore the button release
07404        * because they may have let go of shift before releasing the
07405        * mouse button and they almost certainly do not want a
07406        * non-snapped movement to occur from the button release.
07407        */
07408       if (!window->display->grab_last_user_action_was_snap)
07409         {
07410           if (meta_grab_op_is_moving (window->display->grab_op))
07411             {
07412               if (event->xbutton.root == window->screen->xroot)
07413                 update_move (window, event->xbutton.state & ShiftMask,
07414                              event->xbutton.x_root, event->xbutton.y_root);
07415             }
07416           else if (meta_grab_op_is_resizing (window->display->grab_op))
07417             {
07418               if (event->xbutton.root == window->screen->xroot)
07419                 update_resize (window,
07420                                event->xbutton.state & ShiftMask,
07421                                event->xbutton.x_root,
07422                                event->xbutton.y_root,
07423                                TRUE);
07424               if (window->display->compositor)
07425                 meta_compositor_set_updates (window->display->compositor, window, TRUE);
07426             }
07427         }
07428 
07429       meta_display_end_grab_op (window->display, event->xbutton.time);
07430       break;    
07431 
07432     case MotionNotify:
07433       meta_display_check_threshold_reached (window->display,
07434                                             event->xmotion.x_root,
07435                                             event->xmotion.y_root);
07436       if (meta_grab_op_is_moving (window->display->grab_op))
07437         {
07438           if (event->xmotion.root == window->screen->xroot)
07439             {
07440               if (check_use_this_motion_notify (window,
07441                                                 event))
07442                 update_move (window,
07443                              event->xmotion.state & ShiftMask,
07444                              event->xmotion.x_root,
07445                              event->xmotion.y_root);
07446             }
07447         }
07448       else if (meta_grab_op_is_resizing (window->display->grab_op))
07449         {
07450           if (event->xmotion.root == window->screen->xroot)
07451             {
07452               if (check_use_this_motion_notify (window,
07453                                                 event))
07454                 update_resize (window,
07455                                event->xmotion.state & ShiftMask,
07456                                event->xmotion.x_root,
07457                                event->xmotion.y_root,
07458                                FALSE);
07459             }
07460         }
07461       break;
07462 
07463     default:
07464       break;
07465     }
07466 }
07467 
07468 void
07469 meta_window_set_gravity (MetaWindow *window,
07470                          int         gravity)
07471 {
07472   XSetWindowAttributes attrs;
07473 
07474   meta_verbose ("Setting gravity of %s to %d\n", window->desc, gravity);
07475 
07476   attrs.win_gravity = gravity;
07477   
07478   meta_error_trap_push (window->display);
07479 
07480   XChangeWindowAttributes (window->display->xdisplay,
07481                            window->xwindow,
07482                            CWWinGravity,
07483                            &attrs);
07484   
07485   meta_error_trap_pop (window->display, FALSE);
07486 }
07487 
07488 static void
07489 get_work_area_xinerama (MetaWindow    *window,
07490                         MetaRectangle *area,
07491                         int            which_xinerama)
07492 {
07493   GList *tmp;  
07494   
07495   g_assert (which_xinerama >= 0);
07496 
07497   /* Initialize to the whole xinerama */
07498   *area = window->screen->xinerama_infos[which_xinerama].rect;
07499   
07500   tmp = meta_window_get_workspaces (window);  
07501   while (tmp != NULL)
07502     {
07503       MetaRectangle workspace_work_area;
07504       meta_workspace_get_work_area_for_xinerama (tmp->data,
07505                                                  which_xinerama,
07506                                                  &workspace_work_area);
07507       meta_rectangle_intersect (area,
07508                                 &workspace_work_area,
07509                                 area);
07510       tmp = tmp->next;
07511     }
07512   
07513   meta_topic (META_DEBUG_WORKAREA,
07514               "Window %s xinerama %d has work area %d,%d %d x %d\n",
07515               window->desc, which_xinerama,
07516               area->x, area->y, area->width, area->height);
07517 }
07518 
07519 void
07520 meta_window_get_work_area_current_xinerama (MetaWindow    *window,
07521                                             MetaRectangle *area)
07522 {
07523   const MetaXineramaScreenInfo *xinerama = NULL;
07524   xinerama = meta_screen_get_xinerama_for_window (window->screen,
07525                                                   window);
07526 
07527   meta_window_get_work_area_for_xinerama (window,
07528                                           xinerama->number,
07529                                           area);
07530 }
07531 
07532 void
07533 meta_window_get_work_area_for_xinerama (MetaWindow    *window,
07534                                         int            which_xinerama,
07535                                         MetaRectangle *area)
07536 {
07537   g_return_if_fail (which_xinerama >= 0);
07538   
07539   get_work_area_xinerama (window,
07540                           area,
07541                           which_xinerama);
07542 }
07543 
07544 void
07545 meta_window_get_work_area_all_xineramas (MetaWindow    *window,
07546                                          MetaRectangle *area)
07547 {
07548   GList *tmp;  
07549 
07550   /* Initialize to the whole screen */
07551   *area = window->screen->rect;
07552   
07553   tmp = meta_window_get_workspaces (window);  
07554   while (tmp != NULL)
07555     {
07556       MetaRectangle workspace_work_area;
07557       meta_workspace_get_work_area_all_xineramas (tmp->data,
07558                                                   &workspace_work_area);
07559       meta_rectangle_intersect (area,
07560                                 &workspace_work_area,
07561                                 area);
07562       tmp = tmp->next;
07563     }
07564 
07565   meta_topic (META_DEBUG_WORKAREA,
07566               "Window %s has whole-screen work area %d,%d %d x %d\n",
07567               window->desc, area->x, area->y, area->width, area->height);
07568 }
07569 
07570 
07571 gboolean
07572 meta_window_same_application (MetaWindow *window,
07573                               MetaWindow *other_window)
07574 {
07575   MetaGroup *group       = meta_window_get_group (window);
07576   MetaGroup *other_group = meta_window_get_group (other_window);
07577 
07578   return
07579     group!=NULL &&
07580     other_group!=NULL &&
07581     group==other_group;
07582 }
07583 
07584 void
07585 meta_window_refresh_resize_popup (MetaWindow *window)
07586 {
07587   if (window->display->grab_op == META_GRAB_OP_NONE)
07588     return;
07589 
07590   if (window->display->grab_window != window)
07591     return;
07592 
07593   /* We shouldn't ever get called when the wireframe is active
07594    * because that's handled by a different code path in effects.c
07595    */
07596   if (window->display->grab_wireframe_active)
07597     {
07598       meta_topic (META_DEBUG_WINDOW_OPS,
07599                   "refresh_resize_popup called when wireframe active\n");
07600       return;
07601     }
07602   
07603   switch (window->display->grab_op)
07604     {
07605     case META_GRAB_OP_RESIZING_SE:
07606     case META_GRAB_OP_RESIZING_S:
07607     case META_GRAB_OP_RESIZING_SW:
07608     case META_GRAB_OP_RESIZING_N:
07609     case META_GRAB_OP_RESIZING_NE:
07610     case META_GRAB_OP_RESIZING_NW:
07611     case META_GRAB_OP_RESIZING_W:
07612     case META_GRAB_OP_RESIZING_E:
07613     case META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN:
07614     case META_GRAB_OP_KEYBOARD_RESIZING_S:
07615     case META_GRAB_OP_KEYBOARD_RESIZING_N:
07616     case META_GRAB_OP_KEYBOARD_RESIZING_W:
07617     case META_GRAB_OP_KEYBOARD_RESIZING_E:
07618     case META_GRAB_OP_KEYBOARD_RESIZING_SE:
07619     case META_GRAB_OP_KEYBOARD_RESIZING_NE:
07620     case META_GRAB_OP_KEYBOARD_RESIZING_SW:
07621     case META_GRAB_OP_KEYBOARD_RESIZING_NW:
07622       break;
07623 
07624     default:
07625       /* Not resizing */
07626       return;
07627     }
07628       
07629   if (window->display->grab_resize_popup == NULL)
07630     {
07631       if (window->size_hints.width_inc > 1 ||
07632           window->size_hints.height_inc > 1)
07633         window->display->grab_resize_popup =
07634           meta_ui_resize_popup_new (window->display->xdisplay,
07635                                     window->screen->number);
07636     }
07637   
07638   if (window->display->grab_resize_popup != NULL)
07639     {
07640       MetaRectangle rect;
07641       
07642       if (window->display->grab_wireframe_active)
07643         rect = window->display->grab_wireframe_rect;
07644       else
07645         meta_window_get_client_root_coords (window, &rect);
07646       
07647       meta_ui_resize_popup_set (window->display->grab_resize_popup,
07648                                 rect,
07649                                 window->size_hints.base_width,
07650                                 window->size_hints.base_height,
07651                                 window->size_hints.width_inc,
07652                                 window->size_hints.height_inc);
07653 
07654       meta_ui_resize_popup_set_showing (window->display->grab_resize_popup,
07655                                         TRUE);
07656     }
07657 }
07658 
07659 void
07660 meta_window_foreach_transient (MetaWindow            *window,
07661                                MetaWindowForeachFunc  func,
07662                                void                  *data)
07663 {
07664   GSList *windows;
07665   GSList *tmp;
07666 
07667   windows = meta_display_list_windows (window->display);
07668 
07669   tmp = windows;
07670   while (tmp != NULL)
07671     {
07672       MetaWindow *transient = tmp->data;
07673       
07674       if (meta_window_is_ancestor_of_transient (window, transient))
07675         {
07676           if (!(* func) (transient, data))
07677             break;
07678         }
07679       
07680       tmp = tmp->next;
07681     }
07682 
07683   g_slist_free (windows);
07684 }
07685 
07686 void
07687 meta_window_foreach_ancestor (MetaWindow            *window,
07688                               MetaWindowForeachFunc  func,
07689                               void                  *data)
07690 {
07691   MetaWindow *w;
07692   MetaWindow *tortoise;
07693   
07694   w = window;
07695   tortoise = window;
07696   while (TRUE)
07697     {
07698       if (w->xtransient_for == None ||
07699           w->transient_parent_is_root_window)
07700         break;
07701       
07702       w = meta_display_lookup_x_window (w->display, w->xtransient_for);
07703       
07704       if (w == NULL || w == tortoise)
07705         break;
07706 
07707       if (!(* func) (w, data))
07708         break;      
07709       
07710       if (w->xtransient_for == None ||
07711           w->transient_parent_is_root_window)
07712         break;
07713 
07714       w = meta_display_lookup_x_window (w->display, w->xtransient_for);
07715       
07716       if (w == NULL || w == tortoise)
07717         break;
07718 
07719       if (!(* func) (w, data))
07720         break;
07721       
07722       tortoise = meta_display_lookup_x_window (tortoise->display,
07723                                                tortoise->xtransient_for);
07724 
07725       /* "w" should have already covered all ground covered by the
07726        * tortoise, so the following must hold.
07727        */
07728       g_assert (tortoise != NULL);
07729       g_assert (tortoise->xtransient_for != None);
07730       g_assert (!tortoise->transient_parent_is_root_window);
07731     }
07732 }
07733 
07734 typedef struct
07735 {
07736   MetaWindow *ancestor;
07737   gboolean found;
07738 } FindAncestorData;
07739 
07740 static gboolean
07741 find_ancestor_func (MetaWindow *window,
07742                     void       *data)
07743 {
07744   FindAncestorData *d = data;
07745 
07746   if (window == d->ancestor)
07747     {
07748       d->found = TRUE;
07749       return FALSE;
07750     }
07751 
07752   return TRUE;
07753 }
07754 
07755 gboolean
07756 meta_window_is_ancestor_of_transient (MetaWindow *window,
07757                                       MetaWindow *transient)
07758 {
07759   FindAncestorData d;
07760 
07761   d.ancestor = window;
07762   d.found = FALSE;
07763 
07764   meta_window_foreach_ancestor (transient, find_ancestor_func, &d);
07765 
07766   return d.found;
07767 }
07768 
07769 /* Warp pointer to location appropriate for grab,
07770  * return root coordinates where pointer ended up.
07771  */
07772 static gboolean
07773 warp_grab_pointer (MetaWindow          *window,
07774                    MetaGrabOp           grab_op,
07775                    int                 *x,
07776                    int                 *y)
07777 {
07778   MetaRectangle  rect;
07779   MetaDisplay   *display;
07780 
07781   display = window->display;
07782 
07783   /* We may not have done begin_grab_op yet, i.e. may not be in a grab
07784    */
07785   
07786   if (window == display->grab_window && display->grab_wireframe_active)
07787     {
07788       meta_window_get_xor_rect (window, &display->grab_wireframe_rect, &rect);
07789     }
07790   else
07791     {
07792       meta_window_get_outer_rect (window, &rect);
07793     }
07794   
07795   switch (grab_op)
07796     {
07797       case META_GRAB_OP_KEYBOARD_MOVING:
07798       case META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN:
07799         *x = rect.width / 2;
07800         *y = rect.height / 2;
07801         break;
07802 
07803       case META_GRAB_OP_KEYBOARD_RESIZING_S:
07804         *x = rect.width / 2;
07805         *y = rect.height - 1;
07806         break;
07807 
07808       case META_GRAB_OP_KEYBOARD_RESIZING_N:
07809         *x = rect.width / 2;
07810         *y = 0;
07811         break;
07812 
07813       case META_GRAB_OP_KEYBOARD_RESIZING_W:
07814         *x = 0;
07815         *y = rect.height / 2;
07816         break;
07817 
07818       case META_GRAB_OP_KEYBOARD_RESIZING_E:
07819         *x = rect.width - 1;
07820         *y = rect.height / 2;
07821         break;
07822 
07823       case META_GRAB_OP_KEYBOARD_RESIZING_SE:
07824         *x = rect.width - 1;
07825         *y = rect.height - 1;
07826         break;
07827 
07828       case META_GRAB_OP_KEYBOARD_RESIZING_NE:
07829         *x = rect.width - 1;
07830         *y = 0;
07831         break;
07832 
07833       case META_GRAB_OP_KEYBOARD_RESIZING_SW:
07834         *x = 0;
07835         *y = rect.height - 1;
07836         break;
07837 
07838       case META_GRAB_OP_KEYBOARD_RESIZING_NW:
07839         *x = 0;
07840         *y = 0;
07841         break;
07842 
07843       default:
07844         return FALSE;
07845     }
07846 
07847   *x += rect.x;
07848   *y += rect.y;
07849 
07850   /* Avoid weird bouncing at the screen edge; see bug 154706 */
07851   *x = CLAMP (*x, 0, window->screen->rect.width-1);
07852   *y = CLAMP (*y, 0, window->screen->rect.height-1);
07853   
07854   meta_error_trap_push_with_return (display);
07855 
07856   meta_topic (META_DEBUG_WINDOW_OPS,
07857               "Warping pointer to %d,%d with window at %d,%d\n",
07858               *x, *y, rect.x, rect.y);
07859 
07860   /* Need to update the grab positions so that the MotionNotify and other
07861    * events generated by the XWarpPointer() call below don't cause complete
07862    * funkiness.  See bug 124582 and bug 122670.
07863    */
07864   display->grab_anchor_root_x = *x;
07865   display->grab_anchor_root_y = *y;
07866   display->grab_latest_motion_x = *x;
07867   display->grab_latest_motion_y = *y;
07868   if (display->grab_wireframe_active)
07869     display->grab_anchor_window_pos = display->grab_wireframe_rect;
07870   else
07871     meta_window_get_client_root_coords (window,
07872                                         &display->grab_anchor_window_pos);
07873   
07874   XWarpPointer (display->xdisplay,
07875                 None,
07876                 window->screen->xroot,
07877                 0, 0, 0, 0, 
07878                 *x, *y);
07879 
07880   if (meta_error_trap_pop_with_return (display, FALSE) != Success)
07881     {
07882       meta_verbose ("Failed to warp pointer for window %s\n",
07883                     window->desc);
07884       return FALSE;
07885     }
07886   
07887   return TRUE;
07888 }
07889 
07890 void
07891 meta_window_begin_grab_op (MetaWindow *window,
07892                            MetaGrabOp  op,
07893                            gboolean    frame_action,
07894                            guint32     timestamp)
07895 {
07896   int x, y;
07897 
07898   warp_grab_pointer (window,
07899                      op, &x, &y);
07900 
07901   meta_display_begin_grab_op (window->display,
07902                               window->screen,
07903                               window,
07904                               op,
07905                               FALSE,
07906                               frame_action,
07907                               0 /* button */,
07908                               0,
07909                               timestamp,
07910                               x, y);
07911 }
07912 
07913 void
07914 meta_window_update_keyboard_resize (MetaWindow *window,
07915                                     gboolean    update_cursor)
07916 {
07917   int x, y;
07918   
07919   warp_grab_pointer (window,
07920                      window->display->grab_op,
07921                      &x, &y);
07922 
07923   if (update_cursor)
07924     {
07925       guint32 timestamp;
07926       /* FIXME: Using CurrentTime is really bad mojo */
07927       timestamp = CurrentTime;
07928       meta_display_set_grab_op_cursor (window->display,
07929                                        NULL,
07930                                        window->display->grab_op,
07931                                        TRUE,
07932                                        window->display->grab_xwindow,
07933                                        timestamp);
07934     }
07935 }
07936 
07937 void
07938 meta_window_update_keyboard_move (MetaWindow *window)
07939 {
07940   int x, y;
07941   
07942   warp_grab_pointer (window,
07943                      window->display->grab_op,
07944                      &x, &y);
07945 }
07946 
07947 void
07948 meta_window_update_layer (MetaWindow *window)
07949 {
07950   MetaGroup *group;
07951   
07952   meta_stack_freeze (window->screen->stack);
07953   group = meta_window_get_group (window);
07954   if (group)
07955     meta_group_update_layers (group);
07956   else
07957     meta_stack_update_layer (window->screen->stack, window);
07958   meta_stack_thaw (window->screen->stack);
07959 }
07960 
07961 /* ensure_mru_position_after ensures that window appears after
07962  * below_this_one in the active_workspace's mru_list (i.e. it treats
07963  * window as having been less recently used than below_this_one)
07964  */
07965 static void
07966 ensure_mru_position_after (MetaWindow *window,
07967                            MetaWindow *after_this_one)
07968 {
07969   /* This is sort of slow since it runs through the entire list more
07970    * than once (especially considering the fact that we expect the
07971    * windows of interest to be the first two elements in the list),
07972    * but it doesn't matter while we're only using it on new window
07973    * map.
07974    */
07975 
07976   GList* active_mru_list;
07977   GList* window_position;
07978   GList* after_this_one_position;
07979 
07980   active_mru_list         = window->screen->active_workspace->mru_list;
07981   window_position         = g_list_find (active_mru_list, window);
07982   after_this_one_position = g_list_find (active_mru_list, after_this_one);
07983 
07984   /* after_this_one_position is NULL when we switch workspaces, but in
07985    * that case we don't need to do any MRU shuffling so we can simply
07986    * return.
07987    */
07988   if (after_this_one_position == NULL)
07989     return;
07990 
07991   if (g_list_length (window_position) > g_list_length (after_this_one_position))
07992     {
07993       window->screen->active_workspace->mru_list =
07994         g_list_delete_link (window->screen->active_workspace->mru_list,
07995                             window_position);
07996 
07997       window->screen->active_workspace->mru_list =
07998         g_list_insert_before (window->screen->active_workspace->mru_list,
07999                               after_this_one_position->next,
08000                               window);
08001     }
08002 }
08003 
08004 void
08005 meta_window_stack_just_below (MetaWindow *window,
08006                               MetaWindow *below_this_one)
08007 {
08008   g_return_if_fail (window         != NULL);
08009   g_return_if_fail (below_this_one != NULL);
08010 
08011   if (window->stack_position > below_this_one->stack_position)
08012     {
08013       meta_topic (META_DEBUG_STACK,
08014                   "Setting stack position of window %s to %d (making it below window %s).\n",
08015                   window->desc, 
08016                   below_this_one->stack_position, 
08017                   below_this_one->desc);
08018       meta_window_set_stack_position (window, below_this_one->stack_position);
08019     }
08020   else
08021     {
08022       meta_topic (META_DEBUG_STACK,
08023                   "Window %s  was already below window %s.\n",
08024                   window->desc, below_this_one->desc);
08025     }
08026 }
08027 
08028 void
08029 meta_window_set_user_time (MetaWindow *window,
08030                            guint32     timestamp)
08031 {
08032   /* FIXME: If Soeren's suggestion in bug 151984 is implemented, it will allow
08033    * us to sanity check the timestamp here and ensure it doesn't correspond to
08034    * a future time.
08035    */
08036 
08037   /* Only update the time if this timestamp is newer... */
08038   if (window->net_wm_user_time_set &&
08039       XSERVER_TIME_IS_BEFORE (timestamp, window->net_wm_user_time))
08040     {
08041       meta_topic (META_DEBUG_STARTUP,
08042                   "Window %s _NET_WM_USER_TIME not updated to %u, because it "
08043                   "is less than %u\n",
08044                   window->desc, timestamp, window->net_wm_user_time);
08045     }
08046   else
08047     {
08048       meta_topic (META_DEBUG_STARTUP,
08049                   "Window %s has _NET_WM_USER_TIME of %u\n",
08050                   window->desc, timestamp);
08051       window->net_wm_user_time_set = TRUE;
08052       window->net_wm_user_time = timestamp;
08053       if (XSERVER_TIME_IS_BEFORE (window->display->last_user_time, timestamp))
08054         window->display->last_user_time = timestamp;
08055 
08056       /* If this is a terminal, user interaction with it means the user likely
08057        * doesn't want to have focus transferred for now due to new windows.
08058        */
08059       if (meta_prefs_get_focus_new_windows () ==
08060                META_FOCUS_NEW_WINDOWS_STRICT &&
08061           __window_is_terminal (window))
08062         window->display->allow_terminal_deactivation = FALSE;
08063     }
08064 }
08065 
08066 /* Sets the demands_attention hint on a window, but only
08067  * if it's at least partially obscured (see #305882).
08068  */
08069 void
08070 meta_window_set_demands_attention (MetaWindow *window)
08071 {
08072   MetaRectangle candidate_rect, other_rect;
08073   GList *stack = window->screen->stack->sorted;
08074   MetaWindow *other_window;
08075   gboolean obscured = FALSE;
08076   
08077   MetaWorkspace *workspace = window->screen->active_workspace;
08078   if (workspace!=window->workspace)
08079     {
08080       /* windows on other workspaces are necessarily obscured */
08081       obscured = TRUE;
08082     }
08083   else
08084     {
08085       meta_window_get_outer_rect (window, &candidate_rect);
08086 
08087       /* The stack is sorted with the top windows first. */
08088       
08089       while (stack != NULL && stack->data != window)
08090         {
08091           other_window = stack->data;
08092           stack = stack->next;
08093          
08094           if (other_window->on_all_workspaces ||
08095               window->on_all_workspaces ||
08096               other_window->workspace == window->workspace)
08097             {
08098               meta_window_get_outer_rect (other_window, &other_rect);
08099 
08100               if (meta_rectangle_overlap (&candidate_rect, &other_rect))
08101                 {
08102                   obscured = TRUE;
08103                   break;
08104                 }
08105             }
08106         }
08107       /* If the window's in full view, there's no point setting the flag. */
08108   
08109       meta_topic (META_DEBUG_WINDOW_OPS,
08110           "Not marking %s as needing attention because it's in full view\n",
08111           window->desc);
08112       return;
08113     }
08114     
08115   /* Otherwise, go ahead and set the flag. */
08116   
08117   meta_topic (META_DEBUG_WINDOW_OPS,
08118       "Marking %s as needing attention\n", window->desc);
08119 
08120   window->wm_state_demands_attention = TRUE;
08121   set_net_wm_state (window);
08122 }
08123 
08124 void
08125 meta_window_unset_demands_attention (MetaWindow *window)
08126 {
08127   meta_topic (META_DEBUG_WINDOW_OPS,
08128       "Marking %s as not needing attention\n", window->desc);
08129   
08130   window->wm_state_demands_attention = FALSE;
08131   set_net_wm_state (window);
08132 }
08133 
08134 MetaFrame *
08135 meta_window_get_frame (MetaWindow *window)
08136 {
08137   return window->frame;
08138 }
08139 
08140 gboolean
08141 meta_window_has_focus (MetaWindow *window)
08142 {
08143   return window->has_focus;
08144 }
08145 
08146 gboolean
08147 meta_window_is_shaded (MetaWindow *window)
08148 {
08149   return window->shaded;
08150 }
08151 
08152 MetaRectangle *
08153 meta_window_get_rect (MetaWindow *window)
08154 {
08155   return &window->rect;
08156 }
08157 
08158 MetaScreen *
08159 meta_window_get_screen (MetaWindow *window)
08160 {
08161   return window->screen;
08162 }
08163 
08164 MetaDisplay *
08165 meta_window_get_display (MetaWindow *window)
08166 {
08167   return window->display;
08168 }
08169 
08170 Window
08171 meta_window_get_xwindow (MetaWindow *window)
08172 {
08173   return window->xwindow;
08174 }

Generated on Sat Aug 23 22:04:18 2008 for metacity by  doxygen 1.5.5