1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 
3 /* Marco X managed windows */
4 
5 /*
6  * Copyright (C) 2001 Havoc Pennington, Anders Carlsson
7  * Copyright (C) 2002, 2003 Red Hat, Inc.
8  * Copyright (C) 2003 Rob Adams
9  * Copyright (C) 2004-2006 Elijah Newren
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License as
13  * published by the Free Software Foundation; either version 2 of the
14  * License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful, but
17  * WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
24  * 02110-1301, USA.
25  */
26 
27 #include <config.h>
28 #include <glib/gi18n-lib.h>
29 
30 #include "window-private.h"
31 #include "edge-resistance.h"
32 #include "util.h"
33 #include "frame-private.h"
34 #include "errors.h"
35 #include "workspace.h"
36 #include "stack.h"
37 #include "keybindings.h"
38 #include "ui.h"
39 #include "place.h"
40 #include "session.h"
41 #include "effects.h"
42 #include "prefs.h"
43 #include "resizepopup.h"
44 #include "xprops.h"
45 #include "group.h"
46 #include "window-props.h"
47 #include "constraints.h"
48 #include "compositor.h"
49 #include "effects.h"
50 
51 #include <X11/Xatom.h>
52 #include <X11/Xlibint.h> /* For display->resource_mask */
53 #include <string.h>
54 
55 #ifdef HAVE_SHAPE
56 #include <X11/extensions/shape.h>
57 #endif
58 
59 static int destroying_windows_disallowed = 0;
60 
61 static void     update_sm_hints           (MetaWindow     *window);
62 static void     update_net_frame_extents  (MetaWindow     *window);
63 static void     recalc_window_type        (MetaWindow     *window);
64 static void     recalc_window_features    (MetaWindow     *window);
65 static void     invalidate_work_areas     (MetaWindow     *window);
66 static void     recalc_window_type        (MetaWindow     *window);
67 static void     set_wm_state              (MetaWindow     *window,
68                                            int             state);
69 static void     set_net_wm_state          (MetaWindow     *window);
70 static void     set_allowed_actions_hint  (MetaWindow     *window);
71 static void     send_configure_notify     (MetaWindow     *window);
72 static gboolean process_property_notify   (MetaWindow     *window,
73                                            XPropertyEvent *event);
74 static void     meta_window_show          (MetaWindow     *window);
75 static void     meta_window_hide          (MetaWindow     *window);
76 
77 static gboolean meta_window_same_client (MetaWindow *window,
78                                          MetaWindow *other_window);
79 
80 static void     meta_window_save_rect         (MetaWindow    *window);
81 static void     save_user_window_placement    (MetaWindow    *window);
82 static void     force_save_user_window_placement (MetaWindow    *window);
83 
84 static void meta_window_move_resize_internal (MetaWindow         *window,
85                                               MetaMoveResizeFlags flags,
86                                               int                 resize_gravity,
87                                               int                 root_x_nw,
88                                               int                 root_y_nw,
89                                               int                 w,
90                                               int                 h);
91 
92 static void     ensure_mru_position_after (MetaWindow *window,
93                                            MetaWindow *after_this_one);
94 
95 static void meta_window_move_resize_now (MetaWindow  *window);
96 
97 static void meta_window_unqueue (MetaWindow *window, guint queuebits);
98 
99 static void     update_move           (MetaWindow   *window,
100                                        gboolean      snap,
101                                        int           x,
102                                        int           y);
103 static gboolean update_move_timeout   (gpointer data);
104 static void     update_resize         (MetaWindow   *window,
105                                        gboolean      snap,
106                                        int           x,
107                                        int           y,
108                                        gboolean      force);
109 
110 static MetaTileMode calculate_tiling_mode(int x,
111                                           int y,
112                                           MetaWindow *window,
113                                           const MetaXineramaScreenInfo *monitor,
114                                           MetaRectangle work_area,
115                                           int shake_threshold);
116 
117 static void         meta_window_transform_to_monitor (MetaRectangle *target_rect,
118                                                     const MetaRectangle *from_monitor,
119                                                     const MetaRectangle *to_monitor);
120 
121 static gboolean update_resize_timeout (gpointer data);
122 
123 static void meta_window_flush_calc_showing   (MetaWindow *window);
124 
125 static gboolean queue_calc_showing_func (MetaWindow *window,
126                                          void       *data);
127 
128 static void meta_window_apply_session_info (MetaWindow                  *window,
129                                             const MetaWindowSessionInfo *info);
130 
131 static void unmaximize_window_before_freeing (MetaWindow        *window);
132 static void unminimize_window_and_all_transient_parents (MetaWindow *window);
133 
134 /* Idle handlers for the three queues. The "data" parameter in each case
135  * will be a GINT_TO_POINTER of the index into the queue arrays to use.
136  *
137  * TODO: Possibly there is still some code duplication among these, which we
138  * need to sort out at some point.
139  */
140 static gboolean idle_calc_showing (gpointer data);
141 static gboolean idle_move_resize (gpointer data);
142 static gboolean idle_update_icon (gpointer data);
143 
G_DEFINE_TYPE(MetaWindow,meta_window,G_TYPE_OBJECT)144 G_DEFINE_TYPE (MetaWindow, meta_window, G_TYPE_OBJECT)
145 
146 #ifdef WITH_VERBOSE_MODE
147 static const char*
148 wm_state_to_string (int state)
149 {
150   switch (state)
151     {
152     case NormalState:
153       return "NormalState";
154     case IconicState:
155       return "IconicState";
156     case WithdrawnState:
157       return "WithdrawnState";
158     }
159 
160   return "Unknown";
161 }
162 #endif
163 
164 static gboolean
is_desktop_or_dock_foreach(MetaWindow * window,void * data)165 is_desktop_or_dock_foreach (MetaWindow *window,
166                             void       *data)
167 {
168   gboolean *result = data;
169 
170   *result =
171     window->type == META_WINDOW_DESKTOP ||
172     window->type == META_WINDOW_DOCK;
173   if (*result)
174     return FALSE; /* stop as soon as we find one */
175   else
176     return TRUE;
177 }
178 
179 /* window is the window that's newly mapped provoking
180  * the possible change
181  */
182 static void
maybe_leave_show_desktop_mode(MetaWindow * window)183 maybe_leave_show_desktop_mode (MetaWindow *window)
184 {
185   gboolean is_desktop_or_dock;
186 
187   if (!window->screen->active_workspace->showing_desktop)
188     return;
189 
190   /* If the window is a transient for the dock or desktop, don't
191    * leave show desktop mode when the window opens. That's
192    * so you can e.g. hide all windows, manipulate a file on
193    * the desktop via a dialog, then unshow windows again.
194    */
195   is_desktop_or_dock = FALSE;
196   is_desktop_or_dock_foreach (window,
197                               &is_desktop_or_dock);
198 
199   meta_window_foreach_ancestor (window, is_desktop_or_dock_foreach,
200                                 &is_desktop_or_dock);
201 
202   if (!is_desktop_or_dock)
203     {
204       meta_screen_minimize_all_on_active_workspace_except (window->screen,
205                                                            window);
206       meta_screen_unshow_desktop (window->screen);
207     }
208 }
209 
210 MetaWindow*
meta_window_new(MetaDisplay * display,Window xwindow,gboolean must_be_viewable)211 meta_window_new (MetaDisplay *display,
212                  Window       xwindow,
213                  gboolean     must_be_viewable)
214 {
215   XWindowAttributes attrs;
216   MetaWindow *window;
217 
218   meta_display_grab (display);
219   meta_error_trap_push (display); /* Push a trap over all of window
220                                    * creation, to reduce XSync() calls
221                                    */
222 
223   meta_error_trap_push (display);
224 
225   if (XGetWindowAttributes (display->xdisplay,xwindow, &attrs))
226    {
227       if(meta_error_trap_pop_with_return (display, TRUE) != Success)
228        {
229           meta_verbose ("Failed to get attributes for window 0x%lx\n",
230                         xwindow);
231           meta_error_trap_pop (display, TRUE);
232           meta_display_ungrab (display);
233           return NULL;
234        }
235       window = meta_window_new_with_attrs (display, xwindow,
236                                        must_be_viewable, &attrs);
237    }
238   else
239    {
240          meta_error_trap_pop_with_return (display, TRUE);
241          meta_verbose ("Failed to get attributes for window 0x%lx\n",
242                         xwindow);
243          meta_error_trap_pop (display, TRUE);
244          meta_display_ungrab (display);
245          return NULL;
246    }
247 
248   meta_error_trap_pop (display, FALSE);
249   meta_display_ungrab (display);
250 
251   return window;
252 }
253 
254 MetaWindow*
meta_window_new_with_attrs(MetaDisplay * display,Window xwindow,gboolean must_be_viewable,XWindowAttributes * attrs)255 meta_window_new_with_attrs (MetaDisplay       *display,
256                             Window             xwindow,
257                             gboolean           must_be_viewable,
258                             XWindowAttributes *attrs)
259 {
260   MetaWindow *window;
261   GSList *tmp;
262   MetaWorkspace *space;
263   gulong existing_wm_state;
264   gulong event_mask;
265   MetaMoveResizeFlags flags;
266 #define N_INITIAL_PROPS 20
267   Atom initial_props[N_INITIAL_PROPS];
268   int i;
269   gboolean has_shape;
270 
271   g_assert (attrs != NULL);
272   g_assert (N_INITIAL_PROPS == (int) G_N_ELEMENTS (initial_props));
273 
274   meta_verbose ("Attempting to manage 0x%lx\n", xwindow);
275 
276   if (meta_display_xwindow_is_a_no_focus_window (display, xwindow))
277     {
278       meta_verbose ("Not managing no_focus_window 0x%lx\n",
279                     xwindow);
280       return NULL;
281     }
282 
283   if (attrs->override_redirect)
284     {
285       meta_verbose ("Deciding not to manage override_redirect window 0x%lx\n", xwindow);
286       return NULL;
287     }
288 
289   /* Grab server */
290   meta_display_grab (display);
291   meta_error_trap_push (display); /* Push a trap over all of window
292                                    * creation, to reduce XSync() calls
293                                    */
294 
295   meta_verbose ("must_be_viewable = %d attrs->map_state = %d (%s)\n",
296                 must_be_viewable,
297                 attrs->map_state,
298                 (attrs->map_state == IsUnmapped) ?
299                 "IsUnmapped" :
300                 (attrs->map_state == IsViewable) ?
301                 "IsViewable" :
302                 (attrs->map_state == IsUnviewable) ?
303                 "IsUnviewable" :
304                 "(unknown)");
305 
306   existing_wm_state = WithdrawnState;
307   if (must_be_viewable && attrs->map_state != IsViewable)
308     {
309       /* Only manage if WM_STATE is IconicState or NormalState */
310       gulong state;
311 
312       /* WM_STATE isn't a cardinal, it's type WM_STATE, but is an int */
313       if (!(meta_prop_get_cardinal_with_atom_type (display, xwindow,
314                                                    display->atom_WM_STATE,
315                                                    display->atom_WM_STATE,
316                                                    &state) &&
317             (state == IconicState || state == NormalState)))
318         {
319           meta_verbose ("Deciding not to manage unmapped or unviewable window 0x%lx\n", xwindow);
320           meta_error_trap_pop (display, TRUE);
321           meta_display_ungrab (display);
322           return NULL;
323         }
324 
325       existing_wm_state = state;
326       meta_verbose ("WM_STATE of %lx = %s\n", xwindow,
327                     wm_state_to_string (existing_wm_state));
328     }
329 
330   meta_error_trap_push (display);
331 
332   XAddToSaveSet (display->xdisplay, xwindow);
333 
334   event_mask =
335     PropertyChangeMask | EnterWindowMask | LeaveWindowMask |
336     FocusChangeMask | ColormapChangeMask;
337 
338   XSelectInput (display->xdisplay, xwindow, event_mask);
339 
340   has_shape = FALSE;
341 #ifdef HAVE_SHAPE
342   if (META_DISPLAY_HAS_SHAPE (display))
343     {
344       int x_bounding, y_bounding, x_clip, y_clip;
345       unsigned w_bounding, h_bounding, w_clip, h_clip;
346       int bounding_shaped, clip_shaped;
347 
348       XShapeSelectInput (display->xdisplay, xwindow, ShapeNotifyMask);
349 
350       XShapeQueryExtents (display->xdisplay, xwindow,
351                           &bounding_shaped, &x_bounding, &y_bounding,
352                           &w_bounding, &h_bounding,
353                           &clip_shaped, &x_clip, &y_clip,
354                           &w_clip, &h_clip);
355 
356       has_shape = bounding_shaped != FALSE;
357 
358       meta_topic (META_DEBUG_SHAPES,
359                   "Window has_shape = %d extents %d,%d %u x %u\n",
360                   has_shape, x_bounding, y_bounding,
361                   w_bounding, h_bounding);
362     }
363 #endif
364 
365   /* Get rid of any borders */
366   if (attrs->border_width != 0)
367     XSetWindowBorderWidth (display->xdisplay, xwindow, 0);
368 
369   /* Get rid of weird gravities */
370   if (attrs->win_gravity != NorthWestGravity)
371     {
372       XSetWindowAttributes set_attrs;
373 
374       set_attrs.win_gravity = NorthWestGravity;
375 
376       XChangeWindowAttributes (display->xdisplay,
377                                xwindow,
378                                CWWinGravity,
379                                &set_attrs);
380     }
381 
382   if (meta_error_trap_pop_with_return (display, FALSE) != Success)
383     {
384       meta_verbose ("Window 0x%lx disappeared just as we tried to manage it\n",
385                     xwindow);
386       meta_error_trap_pop (display, FALSE);
387       meta_display_ungrab (display);
388       return NULL;
389     }
390 
391   g_assert (!attrs->override_redirect);
392 
393   window = g_object_new (META_TYPE_WINDOW, NULL);
394 
395   window->constructing = TRUE;
396 
397   window->dialog_pid = -1;
398 
399   window->xwindow = xwindow;
400 
401   /* this is in window->screen->display, but that's too annoying to
402    * type
403    */
404   window->display = display;
405   window->workspace = NULL;
406 
407 #ifdef HAVE_XSYNC
408   window->sync_request_counter = None;
409   window->sync_request_serial = 0;
410   window->sync_request_time = 0;
411 #endif
412 
413   window->screen = NULL;
414   tmp = display->screens;
415   while (tmp != NULL)
416     {
417       MetaScreen *scr = tmp->data;
418 
419       if (scr->xroot == attrs->root)
420         {
421           window->screen = tmp->data;
422           break;
423         }
424 
425       tmp = tmp->next;
426     }
427 
428   g_assert (window->screen);
429 
430   window->desc = g_strdup_printf ("0x%lx", window->xwindow);
431 
432   /* avoid tons of stack updates */
433   meta_stack_freeze (window->screen->stack);
434 
435   window->has_shape = has_shape;
436 
437   window->rect.x = attrs->x;
438   window->rect.y = attrs->y;
439   window->rect.width = attrs->width;
440   window->rect.height = attrs->height;
441 
442   /* And border width, size_hints are the "request" */
443   window->border_width = attrs->border_width;
444   window->size_hints.x = attrs->x;
445   window->size_hints.y = attrs->y;
446   window->size_hints.width = attrs->width;
447   window->size_hints.height = attrs->height;
448   /* initialize the remaining size_hints as if size_hints.flags were zero */
449   meta_set_normal_hints (window, NULL);
450 
451   window->has_custom_frame_extents = FALSE;
452   window->custom_frame_extents.left = 0;
453   window->custom_frame_extents.right = 0;
454   window->custom_frame_extents.top = 0;
455   window->custom_frame_extents.bottom = 0;
456 
457   /* And this is our unmaximized size */
458   window->saved_rect = window->rect;
459   window->user_rect = window->rect;
460 
461   window->depth = attrs->depth;
462   window->xvisual = attrs->visual;
463   window->colormap = attrs->colormap;
464 
465   window->title = NULL;
466   window->icon_name = NULL;
467   window->icon = NULL;
468   window->mini_icon = NULL;
469   meta_icon_cache_init (&window->icon_cache);
470   window->wm_hints_pixmap = None;
471   window->wm_hints_mask = None;
472 
473   window->frame = NULL;
474   window->has_focus = FALSE;
475 
476   window->maximized_horizontally = FALSE;
477   window->maximized_vertically = FALSE;
478   window->maximize_horizontally_after_placement = FALSE;
479   window->maximize_vertically_after_placement = FALSE;
480   window->minimize_after_placement = FALSE;
481   window->move_after_placement = FALSE;
482   window->fullscreen = FALSE;
483   window->fullscreen_after_placement = FALSE;
484   window->fullscreen_monitors[0] = -1;
485   window->require_fully_onscreen = TRUE;
486   window->require_on_single_xinerama = TRUE;
487   window->require_titlebar_visible = TRUE;
488   window->on_all_workspaces = FALSE;
489   window->tile_mode = META_TILE_NONE;
490   window->tile_resized = FALSE;
491   window->tile_monitor_number = -1;
492   window->shaded = FALSE;
493   window->initially_iconic = FALSE;
494   window->minimized = FALSE;
495   window->was_minimized = FALSE;
496   window->tab_unminimized = FALSE;
497   window->iconic = FALSE;
498   window->mapped = attrs->map_state != IsUnmapped;
499   /* if already mapped, no need to worry about focus-on-first-time-showing */
500   window->showing_for_first_time = !window->mapped;
501   /* if already mapped we don't want to do the placement thing */
502   window->placed = window->mapped;
503   if (window->placed)
504     meta_topic (META_DEBUG_PLACEMENT,
505                 "Not placing window 0x%lx since it's already mapped\n",
506                 xwindow);
507   window->force_save_user_rect = TRUE;
508   window->denied_focus_and_not_transient = FALSE;
509   window->unmanaging = FALSE;
510   window->is_in_queues = 0;
511   window->keys_grabbed = FALSE;
512   window->grab_on_frame = FALSE;
513   window->all_keys_grabbed = FALSE;
514   window->withdrawn = FALSE;
515   window->initial_workspace_set = FALSE;
516   window->initial_timestamp_set = FALSE;
517   window->net_wm_user_time_set = FALSE;
518   window->user_time_window = None;
519   window->take_focus = FALSE;
520   window->input = TRUE;
521   window->calc_placement = FALSE;
522   window->shaken_loose = FALSE;
523   window->have_focus_click_grab = FALSE;
524   window->disable_sync = FALSE;
525   window->frame_bounds = NULL;
526 
527   window->unmaps_pending = 0;
528 
529   window->mwm_decorated = TRUE;
530   window->mwm_border_only = FALSE;
531   window->mwm_has_close_func = TRUE;
532   window->mwm_has_minimize_func = TRUE;
533   window->mwm_has_maximize_func = TRUE;
534   window->mwm_has_move_func = TRUE;
535   window->mwm_has_resize_func = TRUE;
536 
537   window->decorated = TRUE;
538   window->has_close_func = TRUE;
539   window->has_minimize_func = TRUE;
540   window->has_maximize_func = TRUE;
541   window->has_move_func = TRUE;
542   window->has_resize_func = TRUE;
543 
544   window->has_shade_func = TRUE;
545 
546   window->has_fullscreen_func = TRUE;
547 
548   window->always_sticky = FALSE;
549 
550   window->wm_state_modal = FALSE;
551   window->skip_taskbar = FALSE;
552   window->skip_pager = FALSE;
553   window->wm_state_skip_taskbar = FALSE;
554   window->wm_state_skip_pager = FALSE;
555   window->wm_state_above = FALSE;
556   window->wm_state_below = FALSE;
557   window->wm_state_demands_attention = FALSE;
558 
559   window->res_class = NULL;
560   window->res_name = NULL;
561   window->role = NULL;
562   window->sm_client_id = NULL;
563   window->wm_client_machine = NULL;
564   window->startup_id = NULL;
565   window->gtk_theme_variant = NULL;
566 
567   window->net_wm_pid = -1;
568 
569   window->xtransient_for = None;
570   window->xclient_leader = None;
571   window->transient_parent_is_root_window = FALSE;
572 
573   window->type = META_WINDOW_NORMAL;
574   window->type_atom = None;
575 
576   window->struts = NULL;
577 
578   window->using_net_wm_name              = FALSE;
579   window->using_net_wm_visible_name      = FALSE;
580   window->using_net_wm_icon_name         = FALSE;
581   window->using_net_wm_visible_icon_name = FALSE;
582 
583   window->need_reread_icon = TRUE;
584 
585   window->layer = META_LAYER_LAST; /* invalid value */
586   window->stack_position = -1;
587   window->initial_workspace = 0; /* not used */
588   window->initial_timestamp = 0; /* not used */
589 
590   meta_display_register_x_window (display, &window->xwindow, window);
591 
592   /* assign the window to its group, or create a new group if needed
593    */
594   window->group = NULL;
595   window->xgroup_leader = None;
596   meta_window_compute_group (window);
597 
598   /* Fill these in the order we want them to be gotten.  we want to
599    * get window name and class first so we can use them in error
600    * messages and such.  However, name is modified depending on
601    * wm_client_machine, so push it slightly sooner.
602    */
603   i = 0;
604   initial_props[i++] = display->atom_WM_CLIENT_MACHINE;
605   initial_props[i++] = display->atom__NET_WM_PID;
606   initial_props[i++] = display->atom__NET_WM_NAME;
607   initial_props[i++] = XA_WM_CLASS;
608   initial_props[i++] = XA_WM_NAME;
609   initial_props[i++] = display->atom__NET_WM_ICON_NAME;
610   initial_props[i++] = XA_WM_ICON_NAME;
611   initial_props[i++] = display->atom__NET_WM_DESKTOP;
612   initial_props[i++] = display->atom__NET_STARTUP_ID;
613   initial_props[i++] = display->atom__NET_WM_SYNC_REQUEST_COUNTER;
614   initial_props[i++] = XA_WM_NORMAL_HINTS;
615   initial_props[i++] = display->atom_WM_PROTOCOLS;
616   initial_props[i++] = XA_WM_HINTS;
617   initial_props[i++] = display->atom__NET_WM_USER_TIME;
618   initial_props[i++] = display->atom__NET_WM_STATE;
619   initial_props[i++] = display->atom__MOTIF_WM_HINTS;
620   initial_props[i++] = XA_WM_TRANSIENT_FOR;
621   initial_props[i++] = display->atom__NET_WM_USER_TIME_WINDOW;
622   initial_props[i++] = display->atom__NET_WM_FULLSCREEN_MONITORS;
623   initial_props[i++] = display->atom__GTK_THEME_VARIANT;
624   g_assert (N_INITIAL_PROPS == i);
625 
626   meta_window_reload_properties (window, initial_props, N_INITIAL_PROPS, TRUE);
627 
628   update_sm_hints (window); /* must come after transient_for */
629   meta_window_update_role (window);
630   meta_window_update_net_wm_type (window);
631   meta_window_update_icon_now (window);
632 
633   if (window->initially_iconic)
634     {
635       /* WM_HINTS said minimized */
636       window->minimized = TRUE;
637       meta_verbose ("Window %s asked to start out minimized\n", window->desc);
638     }
639 
640   if (existing_wm_state == IconicState)
641     {
642       /* WM_STATE said minimized */
643       window->minimized = TRUE;
644       meta_verbose ("Window %s had preexisting WM_STATE = IconicState, minimizing\n",
645                     window->desc);
646 
647       /* Assume window was previously placed, though perhaps it's
648        * been iconic its whole life, we have no way of knowing.
649        */
650       window->placed = TRUE;
651     }
652 
653   /* Apply any window attributes such as initial workspace
654    * based on startup notification
655    */
656   meta_screen_apply_startup_properties (window->screen, window);
657 
658   /* Try to get a "launch timestamp" for the window.  If the window is
659    * a transient, we'd like to be able to get a last-usage timestamp
660    * from the parent window.  If the window has no parent, there isn't
661    * much we can do...except record the current time so that any children
662    * can use this time as a fallback.
663    */
664   if (!window->net_wm_user_time_set) {
665     MetaWindow *parent = NULL;
666     if (window->xtransient_for)
667       parent = meta_display_lookup_x_window (window->display,
668                                              window->xtransient_for);
669 
670     /* First, maybe the app was launched with startup notification using an
671      * obsolete version of the spec; use that timestamp if it exists.
672      */
673     if (window->initial_timestamp_set)
674       /* NOTE: Do NOT toggle net_wm_user_time_set to true; this is just
675        * being recorded as a fallback for potential transients
676        */
677       window->net_wm_user_time = window->initial_timestamp;
678     else if (parent != NULL)
679       meta_window_set_user_time(window, parent->net_wm_user_time);
680     else
681       /* NOTE: Do NOT toggle net_wm_user_time_set to true; this is just
682        * being recorded as a fallback for potential transients
683        */
684       window->net_wm_user_time =
685         meta_display_get_current_time_roundtrip (window->display);
686   }
687 
688   if (window->decorated)
689     meta_window_ensure_frame (window);
690 
691   meta_window_grab_keys (window);
692   if (window->type != META_WINDOW_DOCK)
693     {
694       meta_display_grab_window_buttons (window->display, window->xwindow);
695       meta_display_grab_focus_window_button (window->display, window);
696     }
697 
698   if (window->type == META_WINDOW_DESKTOP ||
699       window->type == META_WINDOW_DOCK)
700     {
701       /* Change the default, but don't enforce this if the user
702        * focuses the dock/desktop and unsticks it using key shortcuts.
703        * Need to set this before adding to the workspaces so the MRU
704        * lists will be updated.
705        */
706       window->on_all_workspaces = TRUE;
707     }
708 
709   /* For the workspace, first honor hints,
710    * if that fails put transients with parents,
711    * otherwise put window on active space
712    */
713 
714   if (window->initial_workspace_set)
715     {
716       if (window->initial_workspace == (int) 0xFFFFFFFF)
717         {
718           meta_topic (META_DEBUG_PLACEMENT,
719                       "Window %s is initially on all spaces\n",
720                       window->desc);
721 
722 	  /* need to set on_all_workspaces first so that it will be
723 	   * added to all the MRU lists
724 	   */
725           window->on_all_workspaces = TRUE;
726           meta_workspace_add_window (window->screen->active_workspace, window);
727         }
728       else
729         {
730           meta_topic (META_DEBUG_PLACEMENT,
731                       "Window %s is initially on space %d\n",
732                       window->desc, window->initial_workspace);
733 
734           space =
735             meta_screen_get_workspace_by_index (window->screen,
736                                                 window->initial_workspace);
737 
738           if (space)
739             meta_workspace_add_window (space, window);
740         }
741     }
742 
743   if (window->workspace == NULL &&
744       window->xtransient_for != None)
745     {
746       /* Try putting dialog on parent's workspace */
747       MetaWindow *parent;
748 
749       parent = meta_display_lookup_x_window (window->display,
750                                              window->xtransient_for);
751 
752       if (parent && parent->workspace)
753         {
754           meta_topic (META_DEBUG_PLACEMENT,
755                       "Putting window %s on same workspace as parent %s\n",
756                       window->desc, parent->desc);
757 
758           if (parent->on_all_workspaces)
759             window->on_all_workspaces = TRUE;
760 
761 	  /* this will implicitly add to the appropriate MRU lists
762 	   */
763           meta_workspace_add_window (parent->workspace, window);
764         }
765     }
766 
767   if (window->workspace == NULL)
768     {
769       meta_topic (META_DEBUG_PLACEMENT,
770                   "Putting window %s on active workspace\n",
771                   window->desc);
772 
773       space = window->screen->active_workspace;
774 
775       meta_workspace_add_window (space, window);
776     }
777 
778   /* for the various on_all_workspaces = TRUE possible above */
779   meta_window_set_current_workspace_hint (window);
780 
781   meta_window_update_struts (window);
782 
783   /* Must add window to stack before doing move/resize, since the
784    * window might have fullscreen size (i.e. should have been
785    * fullscreen'd; acrobat is one such braindead case; it withdraws
786    * and remaps its window whenever trying to become fullscreen...)
787    * and thus constraints may try to auto-fullscreen it which also
788    * means restacking it.
789    */
790   meta_stack_add (window->screen->stack,
791                   window);
792 
793   /* Put our state back where it should be,
794    * passing TRUE for is_configure_request, ICCCM says
795    * initial map is handled same as configure request
796    */
797   flags =
798     META_IS_CONFIGURE_REQUEST | META_IS_MOVE_ACTION | META_IS_RESIZE_ACTION;
799   meta_window_move_resize_internal (window,
800                                     flags,
801                                     window->size_hints.win_gravity,
802                                     window->size_hints.x,
803                                     window->size_hints.y,
804                                     window->size_hints.width,
805                                     window->size_hints.height);
806 
807   /* Now try applying saved stuff from the session */
808   {
809     const MetaWindowSessionInfo *info;
810 
811     info = meta_window_lookup_saved_state (window);
812 
813     if (info)
814       {
815         meta_window_apply_session_info (window, info);
816         meta_window_release_saved_state (info);
817       }
818   }
819 
820   /* FIXME we have a tendency to set this then immediately
821    * change it again.
822    */
823   set_wm_state (window, window->iconic ? IconicState : NormalState);
824   set_net_wm_state (window);
825 
826   /* Sync stack changes */
827   meta_stack_thaw (window->screen->stack);
828 
829   /* disable show desktop mode unless we're a desktop component */
830   maybe_leave_show_desktop_mode (window);
831 
832   meta_window_queue (window, META_QUEUE_CALC_SHOWING);
833   /* See bug 303284; a transient of the given window can already exist, in which
834    * case we think it should probably be shown.
835    */
836   meta_window_foreach_transient (window,
837                                  queue_calc_showing_func,
838                                  NULL);
839   /* See bug 334899; the window may have minimized ancestors
840    * which need to be shown.
841    *
842    * However, we shouldn't unminimize windows here when opening
843    * a new display because that breaks passing _NET_WM_STATE_HIDDEN
844    * between window managers when replacing them; see bug 358042.
845    *
846    * And we shouldn't unminimize windows if they were initially
847    * iconic.
848    */
849   if (!display->display_opening && !window->initially_iconic)
850     unminimize_window_and_all_transient_parents (window);
851 
852   meta_error_trap_pop (display, FALSE); /* pop the XSync()-reducing trap */
853   meta_display_ungrab (display);
854 
855   window->constructing = FALSE;
856 
857   return window;
858 }
859 
860 /* This function should only be called from the end of meta_window_new_with_attrs () */
861 static void
meta_window_apply_session_info(MetaWindow * window,const MetaWindowSessionInfo * info)862 meta_window_apply_session_info (MetaWindow *window,
863                                 const MetaWindowSessionInfo *info)
864 {
865   if (info->stack_position_set)
866     {
867       meta_topic (META_DEBUG_SM,
868                   "Restoring stack position %d for window %s\n",
869                   info->stack_position, window->desc);
870 
871       /* FIXME well, I'm not sure how to do this. */
872     }
873 
874   if (info->minimized_set)
875     {
876       meta_topic (META_DEBUG_SM,
877                   "Restoring minimized state %d for window %s\n",
878                   info->minimized, window->desc);
879 
880       if (window->has_minimize_func && info->minimized)
881         meta_window_minimize (window);
882     }
883 
884   if (info->maximized_set)
885     {
886       meta_topic (META_DEBUG_SM,
887                   "Restoring maximized state %d for window %s\n",
888                   info->maximized, window->desc);
889 
890       if (window->has_maximize_func && info->maximized)
891         {
892           meta_window_maximize (window,
893                                 META_MAXIMIZE_HORIZONTAL |
894                                 META_MAXIMIZE_VERTICAL);
895 
896           if (info->saved_rect_set)
897             {
898               meta_topic (META_DEBUG_SM,
899                           "Restoring saved rect %d,%d %dx%d for window %s\n",
900                           info->saved_rect.x,
901                           info->saved_rect.y,
902                           info->saved_rect.width,
903                           info->saved_rect.height,
904                           window->desc);
905 
906               window->saved_rect.x = info->saved_rect.x;
907               window->saved_rect.y = info->saved_rect.y;
908               window->saved_rect.width = info->saved_rect.width;
909               window->saved_rect.height = info->saved_rect.height;
910             }
911 	}
912     }
913 
914   if (info->on_all_workspaces_set)
915     {
916       window->on_all_workspaces = info->on_all_workspaces;
917       meta_topic (META_DEBUG_SM,
918                   "Restoring sticky state %d for window %s\n",
919                   window->on_all_workspaces, window->desc);
920     }
921 
922   if (info->workspace_indices)
923     {
924       GSList *tmp;
925       GSList *spaces;
926 
927       spaces = NULL;
928 
929       tmp = info->workspace_indices;
930       while (tmp != NULL)
931         {
932           MetaWorkspace *space;
933 
934           space =
935             meta_screen_get_workspace_by_index (window->screen,
936                                                 GPOINTER_TO_INT (tmp->data));
937 
938           if (space)
939             spaces = g_slist_prepend (spaces, space);
940 
941           tmp = tmp->next;
942         }
943 
944       if (spaces)
945         {
946           /* This briefly breaks the invariant that we are supposed
947            * to always be on some workspace. But we paranoically
948            * ensured that one of the workspaces from the session was
949            * indeed valid, so we know we'll go right back to one.
950            */
951           if (window->workspace)
952             meta_workspace_remove_window (window->workspace, window);
953 
954           /* Only restore to the first workspace if the window
955            * happened to be on more than one, since we have replaces
956            * window->workspaces with window->workspace
957            */
958           meta_workspace_add_window (spaces->data, window);
959 
960           meta_topic (META_DEBUG_SM,
961                       "Restoring saved window %s to workspace %d\n",
962                       window->desc,
963                       meta_workspace_index (spaces->data));
964 
965           g_slist_free (spaces);
966         }
967     }
968 
969   if (info->geometry_set)
970     {
971       int x, y, w, h;
972       MetaMoveResizeFlags flags;
973 
974       window->placed = TRUE; /* don't do placement algorithms later */
975 
976       x = info->rect.x;
977       y = info->rect.y;
978 
979       w = window->size_hints.base_width +
980         info->rect.width * window->size_hints.width_inc;
981       h = window->size_hints.base_height +
982         info->rect.height * window->size_hints.height_inc;
983 
984       /* Force old gravity, ignoring anything now set */
985       window->size_hints.win_gravity = info->gravity;
986 
987       meta_topic (META_DEBUG_SM,
988                   "Restoring pos %d,%d size %d x %d for %s\n",
989                   x, y, w, h, window->desc);
990 
991       flags = META_DO_GRAVITY_ADJUST | META_IS_MOVE_ACTION | META_IS_RESIZE_ACTION;
992       meta_window_move_resize_internal (window,
993                                         flags,
994                                         window->size_hints.win_gravity,
995                                         x, y, w, h);
996     }
997 }
998 
999 void
meta_window_free(MetaWindow * window,guint32 timestamp)1000 meta_window_free (MetaWindow  *window,
1001                   guint32      timestamp)
1002 {
1003   GList *tmp;
1004 
1005   meta_verbose ("Unmanaging 0x%lx\n", window->xwindow);
1006 
1007   if (window->display->compositor)
1008     meta_compositor_free_window (window->display->compositor, window);
1009 
1010   if (window->display->window_with_menu == window)
1011     {
1012       meta_ui_window_menu_free (window->display->window_menu);
1013       window->display->window_menu = NULL;
1014       window->display->window_with_menu = NULL;
1015     }
1016 
1017   if (destroying_windows_disallowed > 0)
1018     meta_bug ("Tried to destroy window %s while destruction was not allowed\n",
1019               window->desc);
1020 
1021   window->unmanaging = TRUE;
1022 
1023   if (window->fullscreen)
1024     {
1025       MetaGroup *group;
1026 
1027       /* If the window is fullscreen, it may be forcing
1028        * other windows in its group to a higher layer
1029        */
1030 
1031       meta_stack_freeze (window->screen->stack);
1032       group = meta_window_get_group (window);
1033       if (group)
1034         meta_group_update_layers (group);
1035       meta_stack_thaw (window->screen->stack);
1036     }
1037 
1038   meta_window_shutdown_group (window); /* safe to do this early as
1039                                         * group.c won't re-add to the
1040                                         * group if window->unmanaging
1041                                         */
1042 
1043   /* If we have the focus, focus some other window.
1044    * This is done first, so that if the unmap causes
1045    * an EnterNotify the EnterNotify will have final say
1046    * on what gets focused, maintaining sloppy focus
1047    * invariants.
1048    */
1049   if (window->has_focus)
1050     {
1051       meta_topic (META_DEBUG_FOCUS,
1052                   "Focusing default window since we're unmanaging %s\n",
1053                   window->desc);
1054       meta_workspace_focus_default_window (window->screen->active_workspace,
1055                                            window,
1056                                            timestamp);
1057     }
1058   else if (window->display->expected_focus_window == window)
1059     {
1060       meta_topic (META_DEBUG_FOCUS,
1061                   "Focusing default window since expected focus window freed %s\n",
1062                   window->desc);
1063       window->display->expected_focus_window = NULL;
1064       meta_workspace_focus_default_window (window->screen->active_workspace,
1065                                            window,
1066                                            timestamp);
1067     }
1068   else
1069     {
1070       meta_topic (META_DEBUG_FOCUS,
1071                   "Unmanaging window %s which doesn't currently have focus\n",
1072                   window->desc);
1073     }
1074 
1075   if (window->struts)
1076     {
1077       g_slist_free_full (window->struts, g_free);
1078       window->struts = NULL;
1079 
1080       meta_topic (META_DEBUG_WORKAREA,
1081                   "Unmanaging window %s which has struts, so invalidating work areas\n",
1082                   window->desc);
1083       invalidate_work_areas (window);
1084     }
1085 
1086   if (window->display->grab_window == window)
1087     meta_display_end_grab_op (window->display, timestamp);
1088 
1089   g_assert (window->display->grab_window != window);
1090 
1091   if (window->display->focus_window == window)
1092     {
1093       window->display->focus_window = NULL;
1094       meta_compositor_set_active_window (window->display->compositor,
1095                                          window->screen, NULL);
1096     }
1097 
1098   if (window->maximized_horizontally || window->maximized_vertically)
1099     unmaximize_window_before_freeing (window);
1100 
1101   /* The XReparentWindow call in meta_window_destroy_frame() moves the
1102    * window so we need to send a configure notify; see bug 399552.  (We
1103    * also do this just in case a window got unmaximized.)
1104    */
1105   send_configure_notify (window);
1106 
1107   meta_window_unqueue (window, META_QUEUE_CALC_SHOWING |
1108                                META_QUEUE_MOVE_RESIZE |
1109                                META_QUEUE_UPDATE_ICON);
1110   meta_window_free_delete_dialog (window);
1111 
1112   if (window->workspace)
1113     meta_workspace_remove_window (window->workspace, window);
1114 
1115   g_assert (window->workspace == NULL);
1116 
1117 #ifndef G_DISABLE_CHECKS
1118   tmp = window->screen->workspaces;
1119   while (tmp != NULL)
1120     {
1121       MetaWorkspace *workspace = tmp->data;
1122 
1123       g_assert (g_list_find (workspace->windows, window) == NULL);
1124       g_assert (g_list_find (workspace->mru_list, window) == NULL);
1125 
1126       tmp = tmp->next;
1127     }
1128 #endif
1129 
1130   meta_stack_remove (window->screen->stack, window);
1131 
1132   if (window->frame)
1133     meta_window_destroy_frame (window);
1134 
1135   if (window->withdrawn)
1136     {
1137       /* We need to clean off the window's state so it
1138        * won't be restored if the app maps it again.
1139        */
1140       meta_error_trap_push (window->display);
1141       meta_verbose ("Cleaning state from window %s\n", window->desc);
1142       XDeleteProperty (window->display->xdisplay,
1143                        window->xwindow,
1144                        window->display->atom__NET_WM_DESKTOP);
1145       XDeleteProperty (window->display->xdisplay,
1146                        window->xwindow,
1147                        window->display->atom__NET_WM_STATE);
1148       XDeleteProperty (window->display->xdisplay,
1149                        window->xwindow,
1150                        window->display->atom__NET_WM_FULLSCREEN_MONITORS);
1151       set_wm_state (window, WithdrawnState);
1152       meta_error_trap_pop (window->display, FALSE);
1153     }
1154   else
1155     {
1156       /* We need to put WM_STATE so that others will understand it on
1157        * restart.
1158        */
1159       if (!window->minimized)
1160         {
1161           meta_error_trap_push (window->display);
1162           set_wm_state (window, NormalState);
1163           meta_error_trap_pop (window->display, FALSE);
1164         }
1165 
1166       /* And we need to be sure the window is mapped so other WMs
1167        * know that it isn't Withdrawn
1168        */
1169       meta_error_trap_push (window->display);
1170       XMapWindow (window->display->xdisplay,
1171                   window->xwindow);
1172       meta_error_trap_pop (window->display, FALSE);
1173     }
1174 
1175   meta_window_ungrab_keys (window);
1176   meta_display_ungrab_window_buttons (window->display, window->xwindow);
1177   meta_display_ungrab_focus_window_button (window->display, window);
1178 
1179   meta_display_unregister_x_window (window->display, window->xwindow);
1180 
1181   meta_error_trap_push (window->display);
1182 
1183   /* Put back anything we messed up */
1184   if (window->border_width != 0)
1185     XSetWindowBorderWidth (window->display->xdisplay,
1186                            window->xwindow,
1187                            window->border_width);
1188 
1189   /* No save set */
1190   XRemoveFromSaveSet (window->display->xdisplay,
1191                       window->xwindow);
1192 
1193   /* Don't get events on not-managed windows */
1194   XSelectInput (window->display->xdisplay,
1195                 window->xwindow,
1196                 NoEventMask);
1197 
1198   /* Stop getting events for the window's _NET_WM_USER_TIME_WINDOW too */
1199   if (window->user_time_window != None)
1200     {
1201       meta_display_unregister_x_window (window->display,
1202                                         window->user_time_window);
1203       XSelectInput (window->display->xdisplay,
1204                     window->user_time_window,
1205                     NoEventMask);
1206       window->user_time_window = None;
1207     }
1208 
1209 #ifdef HAVE_SHAPE
1210   if (META_DISPLAY_HAS_SHAPE (window->display))
1211     XShapeSelectInput (window->display->xdisplay, window->xwindow, NoEventMask);
1212 #endif
1213 
1214   meta_error_trap_pop (window->display, FALSE);
1215 
1216   g_object_unref (window);
1217 }
1218 
1219 static void
set_wm_state(MetaWindow * window,int state)1220 set_wm_state (MetaWindow *window,
1221               int         state)
1222 {
1223   unsigned long data[2];
1224 
1225   meta_verbose ("Setting wm state %s on %s\n",
1226                 wm_state_to_string (state), window->desc);
1227 
1228   /* Marco doesn't use icon windows, so data[1] should be None
1229    * according to the ICCCM 2.0 Section 4.1.3.1.
1230    */
1231   data[0] = state;
1232   data[1] = None;
1233 
1234   meta_error_trap_push (window->display);
1235   XChangeProperty (window->display->xdisplay, window->xwindow,
1236                    window->display->atom_WM_STATE,
1237                    window->display->atom_WM_STATE,
1238                    32, PropModeReplace, (guchar*) data, 2);
1239   meta_error_trap_pop (window->display, FALSE);
1240 }
1241 
1242 static void
set_net_wm_state(MetaWindow * window)1243 set_net_wm_state (MetaWindow *window)
1244 {
1245   int i;
1246   unsigned long data[13];
1247 
1248   i = 0;
1249   if (window->shaded)
1250     {
1251       data[i] = window->display->atom__NET_WM_STATE_SHADED;
1252       ++i;
1253     }
1254   if (window->wm_state_modal)
1255     {
1256       data[i] = window->display->atom__NET_WM_STATE_MODAL;
1257       ++i;
1258     }
1259   if (window->skip_pager)
1260     {
1261       data[i] = window->display->atom__NET_WM_STATE_SKIP_PAGER;
1262       ++i;
1263     }
1264   if (window->skip_taskbar)
1265     {
1266       data[i] = window->display->atom__NET_WM_STATE_SKIP_TASKBAR;
1267       ++i;
1268     }
1269   if (window->maximized_horizontally)
1270     {
1271       data[i] = window->display->atom__NET_WM_STATE_MAXIMIZED_HORZ;
1272       ++i;
1273     }
1274   if (window->maximized_vertically)
1275     {
1276       data[i] = window->display->atom__NET_WM_STATE_MAXIMIZED_VERT;
1277       ++i;
1278     }
1279   if (window->fullscreen)
1280     {
1281       data[i] = window->display->atom__NET_WM_STATE_FULLSCREEN;
1282       ++i;
1283     }
1284   if (!meta_window_showing_on_its_workspace (window) || window->shaded)
1285     {
1286       data[i] = window->display->atom__NET_WM_STATE_HIDDEN;
1287       ++i;
1288     }
1289   if (window->wm_state_above)
1290     {
1291       data[i] = window->display->atom__NET_WM_STATE_ABOVE;
1292       ++i;
1293     }
1294   if (window->wm_state_below)
1295     {
1296       data[i] = window->display->atom__NET_WM_STATE_BELOW;
1297       ++i;
1298     }
1299   if (window->wm_state_demands_attention)
1300     {
1301       data[i] = window->display->atom__NET_WM_STATE_DEMANDS_ATTENTION;
1302       ++i;
1303     }
1304   if (window->on_all_workspaces)
1305     {
1306       data[i] = window->display->atom__NET_WM_STATE_STICKY;
1307       ++i;
1308     }
1309   if (meta_window_appears_focused (window))
1310     {
1311       data[i] = window->display->atom__NET_WM_STATE_FOCUSED;
1312       ++i;
1313     }
1314 
1315   meta_verbose ("Setting _NET_WM_STATE with %d atoms\n", i);
1316 
1317   meta_error_trap_push (window->display);
1318   XChangeProperty (window->display->xdisplay, window->xwindow,
1319                    window->display->atom__NET_WM_STATE,
1320                    XA_ATOM,
1321                    32, PropModeReplace, (guchar*) data, i);
1322   meta_error_trap_pop (window->display, FALSE);
1323 
1324   if (window->fullscreen)
1325     {
1326       data[0] = window->fullscreen_monitors[0];
1327       data[1] = window->fullscreen_monitors[1];
1328       data[2] = window->fullscreen_monitors[2];
1329       data[3] = window->fullscreen_monitors[3];
1330 
1331       meta_verbose ("Setting _NET_WM_FULLSCREEN_MONITORS\n");
1332       meta_error_trap_push (window->display);
1333       XChangeProperty (window->display->xdisplay,
1334                        window->xwindow,
1335                        window->display->atom__NET_WM_FULLSCREEN_MONITORS,
1336                        XA_CARDINAL, 32, PropModeReplace,
1337                        (guchar*) data, 4);
1338       meta_error_trap_pop (window->display, FALSE);
1339     }
1340 }
1341 
1342 gboolean
meta_window_located_on_workspace(MetaWindow * window,MetaWorkspace * workspace)1343 meta_window_located_on_workspace (MetaWindow    *window,
1344                                   MetaWorkspace *workspace)
1345 {
1346   return (window->on_all_workspaces && window->screen == workspace->screen) ||
1347     (window->workspace == workspace);
1348 }
1349 
1350 static gboolean
is_minimized_foreach(MetaWindow * window,void * data)1351 is_minimized_foreach (MetaWindow *window,
1352                       void       *data)
1353 {
1354   gboolean *result = data;
1355 
1356   *result = window->minimized;
1357   if (*result)
1358     return FALSE; /* stop as soon as we find one */
1359   else
1360     return TRUE;
1361 }
1362 
1363 static gboolean
ancestor_is_minimized(MetaWindow * window)1364 ancestor_is_minimized (MetaWindow *window)
1365 {
1366   gboolean is_minimized;
1367 
1368   is_minimized = FALSE;
1369 
1370   meta_window_foreach_ancestor (window, is_minimized_foreach, &is_minimized);
1371 
1372   return is_minimized;
1373 }
1374 
1375 gboolean
meta_window_showing_on_its_workspace(MetaWindow * window)1376 meta_window_showing_on_its_workspace (MetaWindow *window)
1377 {
1378   gboolean showing;
1379   gboolean is_desktop_or_dock;
1380   MetaWorkspace* workspace_of_window;
1381 
1382   showing = TRUE;
1383 
1384   /* 1. See if we're minimized */
1385   if (window->minimized)
1386     showing = FALSE;
1387 
1388   /* 2. See if we're in "show desktop" mode */
1389   is_desktop_or_dock = FALSE;
1390   is_desktop_or_dock_foreach (window,
1391                               &is_desktop_or_dock);
1392 
1393   meta_window_foreach_ancestor (window, is_desktop_or_dock_foreach,
1394                                 &is_desktop_or_dock);
1395 
1396   if (window->on_all_workspaces)
1397     workspace_of_window = window->screen->active_workspace;
1398   else if (window->workspace)
1399     workspace_of_window = window->workspace;
1400   else /* This only seems to be needed for startup */
1401     workspace_of_window = NULL;
1402 
1403   if (showing &&
1404       workspace_of_window && workspace_of_window->showing_desktop &&
1405       !is_desktop_or_dock)
1406     {
1407       meta_verbose ("We're showing the desktop on the workspace(s) that window %s is on\n",
1408                     window->desc);
1409       showing = FALSE;
1410     }
1411 
1412   /* 3. See if an ancestor is minimized (note that
1413    *    ancestor's "mapped" field may not be up to date
1414    *    since it's being computed in this same idle queue)
1415    */
1416 
1417   if (showing)
1418     {
1419       if (ancestor_is_minimized (window))
1420         showing = FALSE;
1421     }
1422 
1423 #if 0
1424   /* 4. See if we're drawing wireframe
1425    */
1426   if (window->display->grab_window == window &&
1427       window->display->grab_wireframe_active)
1428     showing = FALSE;
1429 #endif
1430 
1431   return showing;
1432 }
1433 
1434 gboolean
meta_window_should_be_showing(MetaWindow * window)1435 meta_window_should_be_showing (MetaWindow  *window)
1436 {
1437   gboolean on_workspace;
1438 
1439   meta_verbose ("Should be showing for window %s\n", window->desc);
1440 
1441   /* See if we're on the workspace */
1442   on_workspace = meta_window_located_on_workspace (window,
1443                                                    window->screen->active_workspace);
1444 
1445   if (!on_workspace)
1446     meta_verbose ("Window %s is not on workspace %d\n",
1447                   window->desc,
1448                   meta_workspace_index (window->screen->active_workspace));
1449   else
1450     meta_verbose ("Window %s is on the active workspace %d\n",
1451                   window->desc,
1452                   meta_workspace_index (window->screen->active_workspace));
1453 
1454   if (window->on_all_workspaces)
1455     meta_verbose ("Window %s is on all workspaces\n", window->desc);
1456 
1457   return on_workspace && meta_window_showing_on_its_workspace (window);
1458 }
1459 
1460 static void
finish_minimize(gpointer data)1461 finish_minimize (gpointer data)
1462 {
1463   MetaWindow *window = data;
1464   /* FIXME: It really sucks to put timestamp pinging here; it'd
1465    * probably make more sense in implement_showing() so that it's at
1466    * least not duplicated in meta_window_show; but since
1467    * finish_minimize is a callback making things just slightly icky, I
1468    * haven't done that yet.
1469    */
1470   guint32 timestamp = meta_display_get_current_time_roundtrip (window->display);
1471 
1472   meta_window_hide (window);
1473   if (window->has_focus)
1474     {
1475       meta_workspace_focus_default_window (window->screen->active_workspace,
1476                                            window,
1477                                            timestamp);
1478     }
1479 }
1480 
1481 static void
implement_showing(MetaWindow * window,gboolean showing)1482 implement_showing (MetaWindow *window,
1483                    gboolean    showing)
1484 {
1485   /* Actually show/hide the window */
1486   meta_verbose ("Implement showing = %d for window %s\n",
1487                 showing, window->desc);
1488 
1489   if (!showing)
1490     {
1491       gboolean on_workspace;
1492 
1493       on_workspace = meta_window_located_on_workspace (window,
1494                                                        window->screen->active_workspace);
1495 
1496       /* Really this effects code should probably
1497        * be in meta_window_hide so the window->mapped
1498        * test isn't duplicated here. Anyhow, we animate
1499        * if we are mapped now, we are supposed to
1500        * be minimized, and we are on the current workspace.
1501        */
1502       if (on_workspace && window->minimized && window->mapped &&
1503           !meta_prefs_get_reduced_resources ())
1504         {
1505           MetaRectangle icon_rect, window_rect;
1506           gboolean result;
1507 
1508           /* Check if the window has an icon geometry */
1509           result = meta_window_get_icon_geometry (window, &icon_rect);
1510 
1511           if (!result)
1512             {
1513               /* just animate into the corner somehow - maybe
1514                * not a good idea...
1515                */
1516               icon_rect.x = window->screen->rect.width;
1517               icon_rect.y = window->screen->rect.height;
1518               icon_rect.width = 1;
1519               icon_rect.height = 1;
1520             }
1521 
1522           meta_window_get_outer_rect (window, &window_rect);
1523 
1524           meta_effect_run_minimize (window,
1525                                     &window_rect,
1526                                     &icon_rect,
1527                                     finish_minimize,
1528                                     window);
1529         }
1530       else
1531         {
1532           finish_minimize (window);
1533         }
1534     }
1535   else
1536     {
1537       meta_window_show (window);
1538     }
1539 }
1540 
1541 void
meta_window_calc_showing(MetaWindow * window)1542 meta_window_calc_showing (MetaWindow  *window)
1543 {
1544   implement_showing (window, meta_window_should_be_showing (window));
1545 }
1546 
1547 static guint queue_idle[NUMBER_OF_QUEUES] = {0, 0, 0};
1548 static GSList *queue_pending[NUMBER_OF_QUEUES] = {NULL, NULL, NULL};
1549 
1550 static int
stackcmp(gconstpointer a,gconstpointer b)1551 stackcmp (gconstpointer a, gconstpointer b)
1552 {
1553   MetaWindow *aw = (gpointer) a;
1554   MetaWindow *bw = (gpointer) b;
1555 
1556   if (aw->screen != bw->screen)
1557     return 0; /* don't care how they sort with respect to each other */
1558   else
1559     return meta_stack_windows_cmp (aw->screen->stack,
1560                                    aw, bw);
1561 }
1562 
1563 static gboolean
idle_calc_showing(gpointer data)1564 idle_calc_showing (gpointer data)
1565 {
1566   GSList *tmp;
1567   GSList *copy;
1568   GSList *should_show;
1569   GSList *should_hide;
1570   GSList *unplaced;
1571   MetaWindow *first_window;
1572   guint queue_index = GPOINTER_TO_INT (data);
1573 
1574   meta_topic (META_DEBUG_WINDOW_STATE,
1575               "Clearing the calc_showing queue\n");
1576 
1577   /* Work with a copy, for reentrancy. The allowed reentrancy isn't
1578    * complete; destroying a window while we're in here would result in
1579    * badness. But it's OK to queue/unqueue calc_showings.
1580    */
1581   copy = g_slist_copy (queue_pending[queue_index]);
1582   g_slist_free (queue_pending[queue_index]);
1583   queue_pending[queue_index] = NULL;
1584   queue_idle[queue_index] = 0;
1585 
1586   destroying_windows_disallowed += 1;
1587 
1588   /* We map windows from top to bottom and unmap from bottom to
1589    * top, to avoid extra expose events. The exception is
1590    * for unplaced windows, which have to be mapped from bottom to
1591    * top so placement works.
1592    */
1593   should_show = NULL;
1594   should_hide = NULL;
1595   unplaced = NULL;
1596 
1597   tmp = copy;
1598   while (tmp != NULL)
1599     {
1600       MetaWindow *window;
1601 
1602       window = tmp->data;
1603 
1604       if (!window->placed)
1605         unplaced = g_slist_prepend (unplaced, window);
1606       else if (meta_window_should_be_showing (window))
1607         should_show = g_slist_prepend (should_show, window);
1608       else
1609         should_hide = g_slist_prepend (should_hide, window);
1610 
1611       tmp = tmp->next;
1612     }
1613 
1614   /* bottom to top */
1615   unplaced = g_slist_sort (unplaced, stackcmp);
1616   should_hide = g_slist_sort (should_hide, stackcmp);
1617   /* top to bottom */
1618   should_show = g_slist_sort (should_show, stackcmp);
1619   should_show = g_slist_reverse (should_show);
1620 
1621   first_window = copy->data;
1622 
1623   meta_display_grab (first_window->display);
1624 
1625   tmp = unplaced;
1626   while (tmp != NULL)
1627     {
1628       MetaWindow *window;
1629 
1630       window = tmp->data;
1631 
1632       meta_window_calc_showing (window);
1633 
1634       tmp = tmp->next;
1635     }
1636 
1637   tmp = should_show;
1638   while (tmp != NULL)
1639     {
1640       MetaWindow *window;
1641 
1642       window = tmp->data;
1643 
1644       implement_showing (window, TRUE);
1645 
1646       tmp = tmp->next;
1647     }
1648 
1649   tmp = should_hide;
1650   while (tmp != NULL)
1651     {
1652       MetaWindow *window;
1653 
1654       window = tmp->data;
1655 
1656       implement_showing (window, FALSE);
1657 
1658       tmp = tmp->next;
1659     }
1660 
1661   tmp = copy;
1662   while (tmp != NULL)
1663     {
1664       MetaWindow *window;
1665 
1666       window = tmp->data;
1667 
1668       /* important to set this here for reentrancy -
1669        * if we queue a window again while it's in "copy",
1670        * then queue_calc_showing will just return since
1671        * we are still in the calc_showing queue
1672        */
1673       window->is_in_queues &= ~META_QUEUE_CALC_SHOWING;
1674 
1675       tmp = tmp->next;
1676     }
1677 
1678   if (meta_prefs_get_focus_mode () != META_FOCUS_MODE_CLICK)
1679     {
1680       /* When display->mouse_mode is false, we want to ignore
1681        * EnterNotify events unless they come from mouse motion.  To do
1682        * that, we set a sentinel property on the root window if we're
1683        * not in mouse_mode.
1684        */
1685       tmp = should_show;
1686       while (tmp != NULL)
1687         {
1688           MetaWindow *window = tmp->data;
1689 
1690           if (!window->display->mouse_mode)
1691             meta_display_increment_focus_sentinel (window->display);
1692 
1693           tmp = tmp->next;
1694         }
1695     }
1696 
1697   meta_display_ungrab (first_window->display);
1698 
1699   g_slist_free (copy);
1700 
1701   g_slist_free (unplaced);
1702   g_slist_free (should_show);
1703   g_slist_free (should_hide);
1704 
1705   destroying_windows_disallowed -= 1;
1706 
1707   return FALSE;
1708 }
1709 
1710 #ifdef WITH_VERBOSE_MODE
1711 static const gchar* meta_window_queue_names[NUMBER_OF_QUEUES] =
1712   {"calc_showing", "move_resize", "update_icon"};
1713 #endif
1714 
1715 static void
meta_window_unqueue(MetaWindow * window,guint queuebits)1716 meta_window_unqueue (MetaWindow *window, guint queuebits)
1717 {
1718   gint queuenum;
1719 
1720   for (queuenum=0; queuenum<NUMBER_OF_QUEUES; queuenum++)
1721     {
1722       if ((queuebits & 1<<queuenum) /* they have asked to unqueue */
1723           &&
1724           (window->is_in_queues & 1<<queuenum)) /* it's in the queue */
1725         {
1726 
1727           meta_topic (META_DEBUG_WINDOW_STATE,
1728               "Removing %s from the %s queue\n",
1729               window->desc,
1730               meta_window_queue_names[queuenum]);
1731 
1732           /* Note that window may not actually be in the queue
1733            * because it may have been in "copy" inside the idle handler
1734            */
1735           queue_pending[queuenum] = g_slist_remove (queue_pending[queuenum], window);
1736           window->is_in_queues &= ~(1<<queuenum);
1737 
1738           /* Okay, so maybe we've used up all the entries in the queue.
1739            * In that case, we should kill the function that deals with
1740            * the queue, because there's nothing left for it to do.
1741            */
1742           if (queue_pending[queuenum] == NULL && queue_idle[queuenum] != 0)
1743             {
1744               g_source_remove (queue_idle[queuenum]);
1745               queue_idle[queuenum] = 0;
1746             }
1747         }
1748     }
1749 }
1750 
1751 static void
meta_window_flush_calc_showing(MetaWindow * window)1752 meta_window_flush_calc_showing (MetaWindow *window)
1753 {
1754   if (window->is_in_queues & META_QUEUE_CALC_SHOWING)
1755     {
1756       meta_window_unqueue (window, META_QUEUE_CALC_SHOWING);
1757       meta_window_calc_showing (window);
1758     }
1759 }
1760 
1761 void
meta_window_queue(MetaWindow * window,guint queuebits)1762 meta_window_queue (MetaWindow *window, guint queuebits)
1763 {
1764   guint queuenum;
1765 
1766   for (queuenum=0; queuenum<NUMBER_OF_QUEUES; queuenum++)
1767     {
1768       if (queuebits & 1<<queuenum)
1769         {
1770           /* Data which varies between queues.
1771            * Yes, these do look a lot like associative arrays:
1772            * I seem to be turning into a Perl programmer.
1773            */
1774 
1775           const gint window_queue_idle_priority[NUMBER_OF_QUEUES] =
1776             {
1777               G_PRIORITY_DEFAULT_IDLE,  /* CALC_SHOWING */
1778               META_PRIORITY_RESIZE,     /* MOVE_RESIZE */
1779               G_PRIORITY_DEFAULT_IDLE   /* UPDATE_ICON */
1780             };
1781 
1782           const GSourceFunc window_queue_idle_handler[NUMBER_OF_QUEUES] =
1783             {
1784               idle_calc_showing,
1785               idle_move_resize,
1786               idle_update_icon,
1787             };
1788 
1789           /* If we're about to drop the window, there's no point in putting
1790            * it on a queue.
1791            */
1792           if (window->unmanaging)
1793             break;
1794 
1795           /* If the window already claims to be in that queue, there's no
1796            * point putting it in the queue.
1797            */
1798           if (window->is_in_queues & 1<<queuenum)
1799             break;
1800 
1801           meta_topic (META_DEBUG_WINDOW_STATE,
1802               "Putting %s in the %s queue\n",
1803               window->desc,
1804               meta_window_queue_names[queuenum]);
1805 
1806           /* So, mark it as being in this queue. */
1807           window->is_in_queues |= 1<<queuenum;
1808 
1809           /* There's not a lot of point putting things into a queue if
1810            * nobody's on the other end pulling them out. Therefore,
1811            * let's check to see whether an idle handler exists to do
1812            * that. If not, we'll create one.
1813            */
1814 
1815           if (queue_idle[queuenum] == 0)
1816             queue_idle[queuenum] = g_idle_add_full
1817               (
1818                 window_queue_idle_priority[queuenum],
1819                 window_queue_idle_handler[queuenum],
1820                 GUINT_TO_POINTER(queuenum),
1821                 NULL
1822               );
1823 
1824           /* And now we actually put it on the queue. */
1825           queue_pending[queuenum] = g_slist_prepend (queue_pending[queuenum],
1826                                                      window);
1827       }
1828   }
1829 }
1830 
1831 static gboolean
intervening_user_event_occurred(MetaWindow * window)1832 intervening_user_event_occurred (MetaWindow *window)
1833 {
1834   guint32 compare;
1835   MetaWindow *focus_window;
1836 
1837   focus_window = window->display->focus_window;
1838 
1839   meta_topic (META_DEBUG_STARTUP,
1840               "COMPARISON:\n"
1841               "  net_wm_user_time_set : %d\n"
1842               "  net_wm_user_time     : %u\n"
1843               "  initial_timestamp_set: %d\n"
1844               "  initial_timestamp    : %u\n",
1845               window->net_wm_user_time_set,
1846               window->net_wm_user_time,
1847               window->initial_timestamp_set,
1848               window->initial_timestamp);
1849   if (focus_window != NULL)
1850     {
1851       meta_topic (META_DEBUG_STARTUP,
1852                   "COMPARISON (continued):\n"
1853                   "  focus_window             : %s\n"
1854                   "  fw->net_wm_user_time_set : %d\n"
1855                   "  fw->net_wm_user_time     : %u\n",
1856                   focus_window->desc,
1857                   focus_window->net_wm_user_time_set,
1858                   focus_window->net_wm_user_time);
1859     }
1860 
1861   /* We expect the most common case for not focusing a new window
1862    * to be when a hint to not focus it has been set.  Since we can
1863    * deal with that case rapidly, we use special case it--this is
1864    * merely a preliminary optimization.  :)
1865    */
1866   if ( ((window->net_wm_user_time_set == TRUE) &&
1867        (window->net_wm_user_time == 0))
1868       ||
1869        ((window->initial_timestamp_set == TRUE) &&
1870        (window->initial_timestamp == 0)))
1871     {
1872       meta_topic (META_DEBUG_STARTUP,
1873                   "window %s explicitly requested no focus\n",
1874                   window->desc);
1875       return TRUE;
1876     }
1877 
1878   if (!(window->net_wm_user_time_set) && !(window->initial_timestamp_set))
1879     {
1880       meta_topic (META_DEBUG_STARTUP,
1881                   "no information about window %s found\n",
1882                   window->desc);
1883       return FALSE;
1884     }
1885 
1886   if (focus_window != NULL &&
1887       !focus_window->net_wm_user_time_set)
1888     {
1889       meta_topic (META_DEBUG_STARTUP,
1890                   "focus window, %s, doesn't have a user time set yet!\n",
1891                   window->desc);
1892       return FALSE;
1893     }
1894 
1895   /* To determine the "launch" time of an application,
1896    * startup-notification can set the TIMESTAMP and the
1897    * application (usually via its toolkit such as gtk or qt) can
1898    * set the _NET_WM_USER_TIME.  If both are set, we need to be
1899    * using the newer of the two values.
1900    *
1901    * See http://bugzilla.gnome.org/show_bug.cgi?id=573922
1902    */
1903   compare = 0;
1904   if (window->net_wm_user_time_set &&
1905       window->initial_timestamp_set)
1906     compare =
1907       XSERVER_TIME_IS_BEFORE (window->net_wm_user_time,
1908                               window->initial_timestamp) ?
1909       window->initial_timestamp : window->net_wm_user_time;
1910   else if (window->net_wm_user_time_set)
1911     compare = window->net_wm_user_time;
1912   else if (window->initial_timestamp_set)
1913     compare = window->initial_timestamp;
1914 
1915   if ((focus_window != NULL) &&
1916       XSERVER_TIME_IS_BEFORE (compare, focus_window->net_wm_user_time))
1917     {
1918       meta_topic (META_DEBUG_STARTUP,
1919                   "window %s focus prevented by other activity; %u < %u\n",
1920                   window->desc,
1921                   compare,
1922                   focus_window->net_wm_user_time);
1923       return TRUE;
1924     }
1925   else
1926     {
1927       meta_topic (META_DEBUG_STARTUP,
1928                   "new window %s with no intervening events\n",
1929                   window->desc);
1930       return FALSE;
1931     }
1932 }
1933 
1934 /* This function is an ugly hack.  It's experimental in nature and ought to be
1935  * replaced by a real hint from the app to the WM if we decide the experimental
1936  * behavior is worthwhile.  The basic idea is to get more feedback about how
1937  * usage scenarios of "strict" focus users and what they expect.  See #326159.
1938  */
1939 gboolean
__window_is_terminal(MetaWindow * window)1940 __window_is_terminal (MetaWindow *window)
1941 {
1942   if (window == NULL || window->res_class == NULL)
1943     return FALSE;
1944 
1945   /*
1946    * Compare res_class, which is not user-settable, and thus theoretically
1947    * a more-reliable indication of term-ness.
1948    */
1949 
1950   /* mate-terminal -- if you couldn't guess */
1951   if (strcmp (window->res_class, "Mate-terminal") == 0)
1952     return TRUE;
1953   /* xterm, rxvt, aterm */
1954   else if (strcmp (window->res_class, "XTerm") == 0)
1955     return TRUE;
1956   /* konsole, KDE's terminal program */
1957   else if (strcmp (window->res_class, "Konsole") == 0)
1958     return TRUE;
1959   /* rxvt-unicode */
1960   else if (strcmp (window->res_class, "URxvt") == 0)
1961     return TRUE;
1962   /* eterm */
1963   else if (strcmp (window->res_class, "Eterm") == 0)
1964     return TRUE;
1965   /* KTerm -- some terminal not KDE based; so not like Konsole */
1966   else if (strcmp (window->res_class, "KTerm") == 0)
1967     return TRUE;
1968   /* Multi-mate-terminal */
1969   else if (strcmp (window->res_class, "Multi-mate-terminal") == 0)
1970     return TRUE;
1971   /* mlterm ("multi lingual terminal emulator on X") */
1972   else if (strcmp (window->res_class, "mlterm") == 0)
1973     return TRUE;
1974   /* Terminal -- XFCE Terminal */
1975   else if (strcmp (window->res_class, "Terminal") == 0)
1976     return TRUE;
1977 
1978   return FALSE;
1979 }
1980 
1981 /* This function determines what state the window should have assuming that it
1982  * and the focus_window have no relation
1983  */
1984 static void
window_state_on_map(MetaWindow * window,gboolean * takes_focus,gboolean * places_on_top)1985 window_state_on_map (MetaWindow *window,
1986                      gboolean *takes_focus,
1987                      gboolean *places_on_top)
1988 {
1989   gboolean intervening_events;
1990 
1991   intervening_events = intervening_user_event_occurred (window);
1992 
1993   *takes_focus = !intervening_events;
1994   *places_on_top = *takes_focus;
1995 
1996   /* don't initially focus windows that are intended to not accept
1997    * focus
1998    */
1999   if (!(window->input || window->take_focus))
2000     {
2001       *takes_focus = FALSE;
2002       return;
2003     }
2004 
2005   /* Terminal usage may be different; some users intend to launch
2006    * many apps in quick succession or to just view things in the new
2007    * window while still interacting with the terminal.  In that case,
2008    * apps launched from the terminal should not take focus.  This
2009    * isn't quite the same as not allowing focus to transfer from
2010    * terminals due to new window map, but the latter is a much easier
2011    * approximation to enforce so we do that.
2012    */
2013   if (*takes_focus &&
2014       meta_prefs_get_focus_new_windows () == META_FOCUS_NEW_WINDOWS_STRICT &&
2015       !window->display->allow_terminal_deactivation &&
2016       __window_is_terminal (window->display->focus_window) &&
2017       !meta_window_is_ancestor_of_transient (window->display->focus_window,
2018                                              window))
2019     {
2020       meta_topic (META_DEBUG_FOCUS,
2021                   "focus_window is terminal; not focusing new window.\n");
2022       *takes_focus = FALSE;
2023       *places_on_top = FALSE;
2024     }
2025 
2026   switch (window->type)
2027     {
2028     case META_WINDOW_UTILITY:
2029     case META_WINDOW_TOOLBAR:
2030       *takes_focus = FALSE;
2031       *places_on_top = FALSE;
2032       break;
2033     case META_WINDOW_DOCK:
2034     case META_WINDOW_DESKTOP:
2035     case META_WINDOW_SPLASHSCREEN:
2036     case META_WINDOW_MENU:
2037       /* don't focus any of these; places_on_top may be irrelevant for some of
2038        * these (e.g. dock)--but you never know--the focus window might also be
2039        * of the same type in some weird situation...
2040        */
2041       *takes_focus = FALSE;
2042       break;
2043     case META_WINDOW_NORMAL:
2044     case META_WINDOW_DIALOG:
2045     case META_WINDOW_MODAL_DIALOG:
2046       /* The default is correct for these */
2047       break;
2048     }
2049 }
2050 
2051 static gboolean
windows_overlap(const MetaWindow * w1,const MetaWindow * w2)2052 windows_overlap (const MetaWindow *w1, const MetaWindow *w2)
2053 {
2054   if (w1->minimized || w2->minimized)
2055     return FALSE;
2056 
2057   MetaRectangle w1rect, w2rect;
2058   meta_window_get_outer_rect (w1, &w1rect);
2059   meta_window_get_outer_rect (w2, &w2rect);
2060   return meta_rectangle_overlap (&w1rect, &w2rect);
2061 }
2062 
2063 /* Returns whether a new window would be covered by any
2064  * existing window on the same workspace that is set
2065  * to be "above" ("always on top").  A window that is not
2066  * set "above" would be underneath the new window anyway.
2067  *
2068  * We take "covered" to mean even partially covered, but
2069  * some people might prefer entirely covered.  I think it
2070  * is more useful to behave this way if any part of the
2071  * window is covered, because a partial coverage could be
2072  * (say) ninety per cent and almost indistinguishable from total.
2073  */
2074 static gboolean
window_would_be_covered(const MetaWindow * newbie)2075 window_would_be_covered (const MetaWindow *newbie)
2076 {
2077   MetaWorkspace *workspace;
2078   GList *tmp, *windows;
2079 
2080   workspace = meta_window_get_workspace ((MetaWindow *) newbie);
2081   windows = meta_workspace_list_windows (workspace);
2082 
2083   tmp = windows;
2084   while (tmp != NULL)
2085     {
2086       MetaWindow *w = tmp->data;
2087 
2088       if (w->wm_state_above && w != newbie)
2089         {
2090           /* We have found a window that is "above". Perhaps it overlaps. */
2091           if (windows_overlap (w, newbie))
2092             {
2093               g_list_free (windows); /* clean up... */
2094               return TRUE; /* yes, it does */
2095             }
2096         }
2097 
2098       tmp = tmp->next;
2099     }
2100 
2101   g_list_free (windows);
2102   return FALSE; /* none found */
2103 }
2104 
2105 /* XXX META_EFFECT_*_MAP */
2106 void
meta_window_show(MetaWindow * window)2107 meta_window_show (MetaWindow *window)
2108 {
2109   gboolean did_show;
2110   gboolean takes_focus_on_map;
2111   gboolean place_on_top_on_map;
2112   gboolean needs_stacking_adjustment;
2113   gboolean will_be_covered;
2114   MetaWindow *focus_window;
2115   guint32     timestamp;
2116 
2117   /* FIXME: It really sucks to put timestamp pinging here; it'd
2118    * probably make more sense in implement_showing() so that it's at
2119    * least not duplicated in finish_minimize.  *shrug*
2120    */
2121   timestamp = meta_display_get_current_time_roundtrip (window->display);
2122 
2123   meta_topic (META_DEBUG_WINDOW_STATE,
2124               "Showing window %s, shaded: %d iconic: %d placed: %d\n",
2125               window->desc, window->shaded, window->iconic, window->placed);
2126 
2127   focus_window = window->display->focus_window;  /* May be NULL! */
2128   did_show = FALSE;
2129   window_state_on_map (window, &takes_focus_on_map, &place_on_top_on_map);
2130   needs_stacking_adjustment = FALSE;
2131   will_be_covered = window_would_be_covered (window);
2132 
2133   meta_topic (META_DEBUG_WINDOW_STATE,
2134               "Window %s %s focus on map, and %s place on top on map.\n",
2135               window->desc,
2136               takes_focus_on_map ? "does" : "does not",
2137               place_on_top_on_map ? "does" : "does not");
2138 
2139   /* Now, in some rare cases we should *not* put a new window on top.
2140    * These cases include certain types of windows showing for the first
2141    * time, and any window which would be covered because of another window
2142    * being set "above" ("always on top").
2143    *
2144    * FIXME: Although "place_on_top_on_map" and "takes_focus_on_map" are
2145    * generally based on the window type, there is a special case when the
2146    * focus window is a terminal for them both to be false; this should
2147    * probably rather be a term in the "if" condition below.
2148    */
2149 
2150   if ( focus_window != NULL && window->showing_for_first_time &&
2151       ( (!place_on_top_on_map && !takes_focus_on_map) ||
2152       will_be_covered )
2153     ) {
2154       if (!meta_window_is_ancestor_of_transient (focus_window, window))
2155         {
2156           needs_stacking_adjustment = TRUE;
2157           if (!window->placed)
2158             window->denied_focus_and_not_transient = TRUE;
2159         }
2160     }
2161 
2162   if (!window->placed)
2163     {
2164       /* We have to recalc the placement here since other windows may
2165        * have been mapped/placed since we last did constrain_position
2166        */
2167 
2168       /* calc_placement is an efficiency hack to avoid
2169        * multiple placement calculations before we finally
2170        * show the window.
2171        */
2172       window->calc_placement = TRUE;
2173       meta_window_move_resize_now (window);
2174       window->calc_placement = FALSE;
2175 
2176       /* don't ever do the initial position constraint thing again.
2177        * This is toggled here so that initially-iconified windows
2178        * still get placed when they are ultimately shown.
2179        */
2180       window->placed = TRUE;
2181 
2182       /* Don't want to accidentally reuse the fact that we had been denied
2183        * focus in any future constraints unless we're denied focus again.
2184        */
2185       window->denied_focus_and_not_transient = FALSE;
2186     }
2187 
2188   if (needs_stacking_adjustment)
2189     {
2190       gboolean overlap;
2191 
2192       /* This window isn't getting focus on map.  We may need to do some
2193        * special handing with it in regards to
2194        *   - the stacking of the window
2195        *   - the MRU position of the window
2196        *   - the demands attention setting of the window
2197        *
2198        * Firstly, set the flag so we don't give the window focus anyway
2199        * and confuse people.
2200        */
2201 
2202       takes_focus_on_map = FALSE;
2203 
2204       overlap = windows_overlap (window, focus_window);
2205 
2206       /* We want alt tab to go to the denied-focus window */
2207       ensure_mru_position_after (window, focus_window);
2208 
2209       /* We don't want the denied-focus window to obscure the focus
2210        * window, and if we're in both click-to-focus mode and
2211        * raise-on-click mode then we want to maintain the invariant
2212        * that MRU order == stacking order.  The need for this if
2213        * comes from the fact that in sloppy/mouse focus the focus
2214        * window may not overlap other windows and also can be
2215        * considered "below" them; this combination means that
2216        * placing the denied-focus window "below" the focus window
2217        * in the stack when it doesn't overlap it confusingly places
2218        * that new window below a lot of other windows.
2219        */
2220       if (!will_be_covered && (overlap ||
2221           (meta_prefs_get_focus_mode () == META_FOCUS_MODE_CLICK &&
2222            meta_prefs_get_raise_on_click ())))
2223         meta_window_stack_just_below (window, focus_window);
2224 
2225       /* If the window will be obscured by the focus window or a window set to
2226        * always on top, then the user might not notice the window appearing so
2227        * set the demands attention hint.
2228        *
2229        * We set the hint ourselves rather than calling
2230        * meta_window_set_demands_attention() because that would cause
2231        * a recalculation of overlap, and a call to set_net_wm_state()
2232        * which we are going to call ourselves here a few lines down.
2233        */
2234       if (overlap || will_be_covered)
2235         window->wm_state_demands_attention = TRUE;
2236     }
2237 
2238   /* Shaded means the frame is mapped but the window is not */
2239 
2240   if (window->frame && !window->frame->mapped)
2241     {
2242       meta_topic (META_DEBUG_WINDOW_STATE,
2243                   "Frame actually needs map\n");
2244       window->frame->mapped = TRUE;
2245       meta_ui_map_frame (window->screen->ui, window->frame->xwindow);
2246       did_show = TRUE;
2247     }
2248 
2249   if (window->shaded)
2250     {
2251       if (window->mapped)
2252         {
2253           meta_topic (META_DEBUG_WINDOW_STATE,
2254                       "%s actually needs unmap (shaded)\n", window->desc);
2255           meta_topic (META_DEBUG_WINDOW_STATE,
2256                       "Incrementing unmaps_pending on %s for shade\n",
2257                       window->desc);
2258           window->mapped = FALSE;
2259           window->unmaps_pending += 1;
2260           meta_error_trap_push (window->display);
2261           XUnmapWindow (window->display->xdisplay, window->xwindow);
2262           meta_error_trap_pop (window->display, FALSE);
2263         }
2264 
2265       if (!window->iconic)
2266         {
2267           window->iconic = TRUE;
2268           set_wm_state (window, IconicState);
2269         }
2270     }
2271   else
2272     {
2273       if (!window->mapped)
2274         {
2275           meta_topic (META_DEBUG_WINDOW_STATE,
2276                       "%s actually needs map\n", window->desc);
2277           window->mapped = TRUE;
2278           meta_error_trap_push (window->display);
2279           XMapWindow (window->display->xdisplay, window->xwindow);
2280           meta_error_trap_pop (window->display, FALSE);
2281           did_show = TRUE;
2282 
2283           if (window->was_minimized)
2284             {
2285               MetaRectangle window_rect;
2286               MetaRectangle icon_rect;
2287 
2288               window->was_minimized = FALSE;
2289 
2290               if (meta_window_get_icon_geometry (window, &icon_rect))
2291                 {
2292                   meta_window_get_outer_rect (window, &window_rect);
2293 
2294                   meta_effect_run_unminimize (window,
2295                                               &window_rect,
2296                                               &icon_rect,
2297                                               NULL, NULL);
2298                 }
2299             }
2300         }
2301 
2302       if (window->iconic)
2303         {
2304           window->iconic = FALSE;
2305           set_wm_state (window, NormalState);
2306         }
2307     }
2308 
2309   /* We don't want to worry about all cases from inside
2310    * implement_showing(); we only want to worry about focus if this
2311    * window has not been shown before.
2312    */
2313   if (window->showing_for_first_time)
2314     {
2315       window->showing_for_first_time = FALSE;
2316       if (takes_focus_on_map)
2317         {
2318           meta_window_focus (window, timestamp);
2319 
2320           if (window->move_after_placement)
2321             {
2322               meta_window_begin_grab_op(window, META_GRAB_OP_KEYBOARD_MOVING,
2323                                         FALSE, timestamp);
2324               window->move_after_placement = FALSE;
2325             }
2326         }
2327       else
2328         {
2329           /* Prevent EnterNotify events in sloppy/mouse focus from
2330            * erroneously focusing the window that had been denied
2331            * focus.  FIXME: This introduces a race; I have a couple
2332            * ideas for a better way to accomplish the same thing, but
2333            * they're more involved so do it this way for now.
2334            */
2335           meta_display_increment_focus_sentinel (window->display);
2336         }
2337     }
2338 
2339   set_net_wm_state (window);
2340 
2341   if (did_show && window->struts)
2342     {
2343       meta_topic (META_DEBUG_WORKAREA,
2344                   "Mapped window %s with struts, so invalidating work areas\n",
2345                   window->desc);
2346       invalidate_work_areas (window);
2347     }
2348 
2349   /*
2350    * Now that we have shown the window, we no longer want to consider the
2351    * initial timestamp in any subsequent deliberations whether to focus this
2352    * window or not, so clear the flag.
2353    *
2354    * See http://bugzilla.gnome.org/show_bug.cgi?id=573922
2355    */
2356   window->initial_timestamp_set = FALSE;
2357 }
2358 
2359 /* XXX META_EFFECT_*_UNMAP */
2360 static void
meta_window_hide(MetaWindow * window)2361 meta_window_hide (MetaWindow *window)
2362 {
2363   gboolean did_hide;
2364 
2365   meta_topic (META_DEBUG_WINDOW_STATE,
2366               "Hiding window %s\n", window->desc);
2367 
2368   did_hide = FALSE;
2369 
2370   if (window->frame && window->frame->mapped)
2371     {
2372       meta_topic (META_DEBUG_WINDOW_STATE, "Frame actually needs unmap\n");
2373       window->frame->mapped = FALSE;
2374       meta_ui_unmap_frame (window->screen->ui, window->frame->xwindow);
2375       did_hide = TRUE;
2376     }
2377 
2378   if (window->mapped)
2379     {
2380       meta_topic (META_DEBUG_WINDOW_STATE,
2381                   "%s actually needs unmap\n", window->desc);
2382       meta_topic (META_DEBUG_WINDOW_STATE,
2383                   "Incrementing unmaps_pending on %s for hide\n",
2384                   window->desc);
2385       window->mapped = FALSE;
2386       window->unmaps_pending += 1;
2387       meta_error_trap_push (window->display);
2388       XUnmapWindow (window->display->xdisplay, window->xwindow);
2389       meta_error_trap_pop (window->display, FALSE);
2390       did_hide = TRUE;
2391     }
2392 
2393   if (!window->iconic)
2394     {
2395       window->iconic = TRUE;
2396       set_wm_state (window, IconicState);
2397     }
2398 
2399   set_net_wm_state (window);
2400 
2401   if (did_hide && window->struts)
2402     {
2403       meta_topic (META_DEBUG_WORKAREA,
2404                   "Unmapped window %s with struts, so invalidating work areas\n",
2405                   window->desc);
2406       invalidate_work_areas (window);
2407     }
2408 }
2409 
2410 static gboolean
queue_calc_showing_func(MetaWindow * window,void * data)2411 queue_calc_showing_func (MetaWindow *window,
2412                          void       *data)
2413 {
2414   meta_window_queue(window, META_QUEUE_CALC_SHOWING);
2415   return TRUE;
2416 }
2417 
2418 void
meta_window_minimize(MetaWindow * window)2419 meta_window_minimize (MetaWindow  *window)
2420 {
2421   if (!window->minimized)
2422     {
2423       window->minimized = TRUE;
2424       meta_window_queue(window, META_QUEUE_CALC_SHOWING);
2425 
2426       meta_window_foreach_transient (window,
2427                                      queue_calc_showing_func,
2428                                      NULL);
2429 
2430       set_allowed_actions_hint (window);
2431 
2432       if (window->has_focus)
2433         {
2434           meta_topic (META_DEBUG_FOCUS,
2435                       "Focusing default window due to minimization of focus window %s\n",
2436                       window->desc);
2437         }
2438       else
2439         {
2440           meta_topic (META_DEBUG_FOCUS,
2441                       "Minimizing window %s which doesn't have the focus\n",
2442                       window->desc);
2443         }
2444     }
2445 }
2446 
2447 void
meta_window_unminimize(MetaWindow * window)2448 meta_window_unminimize (MetaWindow  *window)
2449 {
2450   if (window->minimized)
2451     {
2452       window->minimized = FALSE;
2453       window->was_minimized = TRUE;
2454       meta_window_queue(window, META_QUEUE_CALC_SHOWING);
2455 
2456       meta_window_foreach_transient (window,
2457                                      queue_calc_showing_func,
2458                                      NULL);
2459 
2460       set_allowed_actions_hint (window);
2461     }
2462 }
2463 
2464 static void
ensure_size_hints_satisfied(MetaRectangle * rect,const XSizeHints * size_hints)2465 ensure_size_hints_satisfied (MetaRectangle    *rect,
2466                              const XSizeHints *size_hints)
2467 {
2468   int minw, minh, maxw, maxh;   /* min/max width/height                      */
2469   int basew, baseh, winc, hinc; /* base width/height, width/height increment */
2470   int extra_width, extra_height;
2471 
2472   minw  = size_hints->min_width;  minh  = size_hints->min_height;
2473   maxw  = size_hints->max_width;  maxh  = size_hints->max_height;
2474   basew = size_hints->base_width; baseh = size_hints->base_height;
2475   winc  = size_hints->width_inc;  hinc  = size_hints->height_inc;
2476 
2477   /* First, enforce min/max size constraints */
2478   rect->width  = CLAMP (rect->width,  minw, maxw);
2479   rect->height = CLAMP (rect->height, minh, maxh);
2480 
2481   /* Now, verify size increment constraints are satisfied, or make them be */
2482   extra_width  = (rect->width  - basew) % winc;
2483   extra_height = (rect->height - baseh) % hinc;
2484 
2485   rect->width  -= extra_width;
2486   rect->height -= extra_height;
2487 
2488   /* Adjusting width/height down, as done above, may violate minimum size
2489    * constraints, so one last fix.
2490    */
2491   if (rect->width  < minw)
2492     rect->width  += ((minw - rect->width)/winc  + 1)*winc;
2493   if (rect->height < minh)
2494     rect->height += ((minh - rect->height)/hinc + 1)*hinc;
2495 }
2496 
2497 static void
meta_window_save_rect(MetaWindow * window)2498 meta_window_save_rect (MetaWindow *window)
2499 {
2500   if (!(META_WINDOW_MAXIMIZED (window) ||
2501         META_WINDOW_SIDE_TILED (window) ||
2502         META_WINDOW_CORNER_TILED(window) ||
2503         window->fullscreen))
2504     {
2505       /* save size/pos as appropriate args for move_resize */
2506       if (!window->maximized_horizontally)
2507         {
2508           window->saved_rect.x      = window->rect.x;
2509           window->saved_rect.width  = window->rect.width;
2510           if (window->frame)
2511             window->saved_rect.x   += window->frame->rect.x;
2512         }
2513       if (!window->maximized_vertically)
2514         {
2515           window->saved_rect.y      = window->rect.y;
2516           window->saved_rect.height = window->rect.height;
2517           if (window->frame)
2518             window->saved_rect.y   += window->frame->rect.y;
2519         }
2520     }
2521 }
2522 
2523 /**
2524  * Save the user_rect regardless of whether the window is maximized or
2525  * fullscreen. See save_user_window_placement() for most uses.
2526  *
2527  * \param window  Store current position of this window for future reference
2528  */
2529 static void
force_save_user_window_placement(MetaWindow * window)2530 force_save_user_window_placement (MetaWindow *window)
2531 {
2532   meta_window_get_client_root_coords (window, &window->user_rect);
2533 }
2534 
2535 /**
2536  * Save the user_rect, but only if the window is neither maximized nor
2537  * fullscreen, otherwise the window may snap back to those dimensions
2538  * (bug #461927).
2539  *
2540  * \param window  Store current position of this window for future reference
2541  */
2542 static void
save_user_window_placement(MetaWindow * window)2543 save_user_window_placement (MetaWindow *window)
2544 {
2545   if (!(META_WINDOW_MAXIMIZED (window) || META_WINDOW_SIDE_TILED (window) || window->fullscreen))
2546     {
2547       MetaRectangle user_rect;
2548 
2549       meta_window_get_client_root_coords (window, &user_rect);
2550 
2551       if (!window->maximized_horizontally)
2552 	{
2553 	  window->user_rect.x     = user_rect.x;
2554 	  window->user_rect.width = user_rect.width;
2555 	}
2556       if (!window->maximized_vertically)
2557 	{
2558 	  window->user_rect.y      = user_rect.y;
2559 	  window->user_rect.height = user_rect.height;
2560 	}
2561     }
2562 }
2563 
2564 void
meta_window_maximize_internal(MetaWindow * window,MetaMaximizeFlags directions,MetaRectangle * saved_rect)2565 meta_window_maximize_internal (MetaWindow        *window,
2566                                MetaMaximizeFlags  directions,
2567                                MetaRectangle     *saved_rect)
2568 {
2569   /* At least one of the two directions ought to be set */
2570   gboolean maximize_horizontally, maximize_vertically;
2571   maximize_horizontally = directions & META_MAXIMIZE_HORIZONTAL;
2572   maximize_vertically   = directions & META_MAXIMIZE_VERTICAL;
2573   g_assert (maximize_horizontally || maximize_vertically);
2574 
2575   meta_topic (META_DEBUG_WINDOW_OPS,
2576               "Maximizing %s%s\n",
2577               window->desc,
2578               maximize_horizontally && maximize_vertically ? "" :
2579                 maximize_horizontally ? " horizontally" :
2580                   maximize_vertically ? " vertically" : "BUGGGGG");
2581 
2582   if (saved_rect != NULL)
2583     window->saved_rect = *saved_rect;
2584   else
2585     meta_window_save_rect (window);
2586 
2587   if (maximize_horizontally && maximize_vertically)
2588     window->saved_maximize = TRUE;
2589 
2590   window->maximized_horizontally =
2591     window->maximized_horizontally || maximize_horizontally;
2592   window->maximized_vertically =
2593     window->maximized_vertically   || maximize_vertically;
2594 
2595   /* Fix for #336850: If the frame shape isn't reapplied, it is
2596    * possible that the frame will retains its rounded corners. That
2597    * happens if the client's size when maximized equals the unmaximized
2598    * size.
2599    */
2600   if (window->frame)
2601     window->frame->need_reapply_frame_shape = TRUE;
2602 
2603   recalc_window_features (window);
2604   set_allowed_actions_hint (window);
2605   set_net_wm_state (window);
2606 }
2607 
2608 void
meta_window_maximize(MetaWindow * window,MetaMaximizeFlags directions)2609 meta_window_maximize (MetaWindow        *window,
2610                       MetaMaximizeFlags  directions)
2611 {
2612   MetaRectangle *saved_rect = NULL;
2613   /* At least one of the two directions ought to be set */
2614   gboolean maximize_horizontally, maximize_vertically;
2615   maximize_horizontally = directions & META_MAXIMIZE_HORIZONTAL;
2616   maximize_vertically   = directions & META_MAXIMIZE_VERTICAL;
2617   g_assert (maximize_horizontally || maximize_vertically);
2618 
2619   /* Only do something if the window isn't already maximized in the
2620    * given direction(s).
2621    */
2622   if ((maximize_horizontally && !window->maximized_horizontally) ||
2623       (maximize_vertically   && !window->maximized_vertically))
2624     {
2625       if (window->shaded && maximize_vertically)
2626         {
2627           /* Shading sucks anyway; I'm not adding a timestamp argument
2628            * to this function just for this niche usage & corner case.
2629            */
2630           guint32 timestamp =
2631             meta_display_get_current_time_roundtrip (window->display);
2632           meta_window_unshade (window, timestamp);
2633         }
2634 
2635       /* if the window hasn't been placed yet, we'll maximize it then
2636        */
2637       if (!window->placed)
2638 	{
2639           window->maximize_horizontally_after_placement = window->maximize_horizontally_after_placement || maximize_horizontally;
2640           window->maximize_vertically_after_placement = window->maximize_vertically_after_placement || maximize_vertically;
2641           return;
2642         }
2643 
2644       if (window->tile_mode != META_TILE_NONE)
2645         {
2646           saved_rect = &window->saved_rect;
2647 
2648           window->maximized_vertically = FALSE;
2649           window->tile_mode = META_TILE_NONE;
2650         }
2651 
2652       meta_window_maximize_internal (window,
2653                                      directions,
2654                                      saved_rect);
2655 
2656       /* move_resize with new maximization constraints
2657        */
2658       meta_window_queue(window, META_QUEUE_MOVE_RESIZE);
2659 
2660       meta_compositor_maximize_window (window->display->compositor, window);
2661     }
2662 }
2663 
2664 static void
unmaximize_window_before_freeing(MetaWindow * window)2665 unmaximize_window_before_freeing (MetaWindow        *window)
2666 {
2667   meta_topic (META_DEBUG_WINDOW_OPS,
2668               "Unmaximizing %s just before freeing\n",
2669               window->desc);
2670 
2671   window->maximized_horizontally = FALSE;
2672   window->maximized_vertically = FALSE;
2673 
2674   if (window->withdrawn)                /* See bug #137185 */
2675     {
2676       window->rect = window->saved_rect;
2677       set_net_wm_state (window);
2678     }
2679   else if (window->screen->closing)     /* See bug #358042 */
2680     {
2681       /* Do NOT update net_wm_state: this screen is closing,
2682        * it likely will be managed by another window manager
2683        * that will need the current _NET_WM_STATE atoms.
2684        * Moreover, it will need to know the unmaximized geometry,
2685        * therefore move_resize the window to saved_rect here
2686        * before closing it. */
2687       meta_window_move_resize (window,
2688                                FALSE,
2689                                window->saved_rect.x,
2690                                window->saved_rect.y,
2691                                window->saved_rect.width,
2692                                window->saved_rect.height);
2693     }
2694 }
2695 
2696 void
meta_window_tile(MetaWindow * window)2697 meta_window_tile (MetaWindow *window)
2698 {
2699   /* Don't do anything if no tiling is requested */
2700   if (window->tile_mode == META_TILE_NONE)
2701     return;
2702 
2703   if (window->tile_mode == META_TILE_LEFT ||
2704       window->tile_mode == META_TILE_RIGHT)
2705     {
2706       MetaRectangle *saved_rect = NULL;
2707       saved_rect = &window->saved_rect;
2708       meta_window_maximize_internal (window, META_MAXIMIZE_VERTICAL, saved_rect);
2709     }
2710   else if (window->tile_mode == META_TILE_BOTTOM_RIGHT ||
2711            window->tile_mode == META_TILE_BOTTOM_LEFT ||
2712            window->tile_mode == META_TILE_TOP_RIGHT ||
2713            window->tile_mode == META_TILE_TOP_LEFT)
2714     {
2715       MetaRectangle *saved_rect = NULL;
2716       saved_rect = &window->saved_rect;
2717       meta_window_maximize_internal (window, META_MAXIMIZE_HORIZONTAL, saved_rect);
2718     }
2719   else
2720     meta_window_save_rect (window);
2721 
2722   window->tiled = TRUE;
2723   /* move_resize with new tiling constraints
2724    */
2725   meta_window_queue (window, META_QUEUE_MOVE_RESIZE);
2726 
2727   set_allowed_actions_hint (window);
2728 }
2729 
2730 void
meta_window_untile(MetaWindow * window)2731 meta_window_untile (MetaWindow *window)
2732 {
2733   window->tiled = FALSE;
2734 
2735   meta_window_unmaximize (window, META_MAXIMIZE_VERTICAL | META_MAXIMIZE_HORIZONTAL);
2736 }
2737 
2738 static gboolean
meta_window_can_tile_maximized(MetaWindow * window)2739 meta_window_can_tile_maximized (MetaWindow *window)
2740 {
2741   return window->has_maximize_func;
2742 }
2743 
2744 gboolean
meta_window_can_tile(MetaWindow * window)2745 meta_window_can_tile (MetaWindow *window)
2746 {
2747   const MetaXineramaScreenInfo *monitor;
2748   MetaRectangle tile_area;
2749 
2750   /*if (!META_WINDOW_ALLOWS_RESIZE (window))*/
2751   if (!meta_window_can_tile_maximized (window) || window->shaded)
2752     return FALSE;
2753 
2754   monitor = meta_screen_get_current_xinerama (window->screen);
2755   meta_window_get_work_area_for_xinerama (window, monitor->number, &tile_area);
2756 
2757   tile_area.width /= 2;
2758 
2759   if (window->frame)
2760     {
2761       MetaFrameBorders borders;
2762 
2763       meta_frame_calc_borders (window->frame, &borders);
2764 
2765       tile_area.width  -= (borders.visible.left + borders.visible.right);
2766       tile_area.height -= (borders.visible.top + borders.visible.bottom);
2767     }
2768 
2769   return tile_area.width >= window->size_hints.min_width &&
2770          tile_area.height >= window->size_hints.min_height;
2771 }
2772 
2773 void
meta_window_unmaximize(MetaWindow * window,MetaMaximizeFlags directions)2774 meta_window_unmaximize (MetaWindow        *window,
2775                         MetaMaximizeFlags  directions)
2776 {
2777   /* At least one of the two directions ought to be set */
2778   gboolean unmaximize_horizontally, unmaximize_vertically;
2779 
2780   unmaximize_horizontally = directions & META_MAXIMIZE_HORIZONTAL;
2781   unmaximize_vertically   = directions & META_MAXIMIZE_VERTICAL;
2782   g_assert (unmaximize_horizontally || unmaximize_vertically);
2783 
2784   if (unmaximize_horizontally && unmaximize_vertically)
2785     window->saved_maximize = FALSE;
2786 
2787   window->tile_mode = META_TILE_NONE;
2788   window->tiled = FALSE;
2789 
2790   /* Only do something if the window is already maximized in the
2791    * given direction(s).
2792    */
2793   if ((unmaximize_horizontally && window->maximized_horizontally) ||
2794       (unmaximize_vertically   && window->maximized_vertically))
2795     {
2796       MetaRectangle target_rect;
2797 
2798       meta_topic (META_DEBUG_WINDOW_OPS,
2799                   "Unmaximizing %s%s\n",
2800                   window->desc,
2801                   unmaximize_horizontally && unmaximize_vertically ? "" :
2802                   unmaximize_horizontally ? " horizontally" :
2803                   unmaximize_vertically ? " vertically" : "BUGGGGG");
2804 
2805       window->maximized_horizontally =
2806         window->maximized_horizontally && !unmaximize_horizontally;
2807       window->maximized_vertically =
2808         window->maximized_vertically   && !unmaximize_vertically;
2809 
2810       /* Unmaximize to the saved_rect position in the direction(s)
2811        * being unmaximized.
2812        */
2813       meta_window_get_client_root_coords (window, &target_rect);
2814       if (unmaximize_horizontally)
2815         {
2816           target_rect.x     = window->saved_rect.x;
2817           target_rect.width = window->saved_rect.width;
2818         }
2819       if (unmaximize_vertically)
2820         {
2821           target_rect.y      = window->saved_rect.y;
2822           target_rect.height = window->saved_rect.height;
2823         }
2824 
2825       /* Window's size hints may have changed while maximized, making
2826        * saved_rect invalid.  #329152
2827        */
2828       ensure_size_hints_satisfied (&target_rect, &window->size_hints);
2829 
2830       meta_window_move_resize (window,
2831                                FALSE,
2832                                target_rect.x,
2833                                target_rect.y,
2834                                target_rect.width,
2835                                target_rect.height);
2836 
2837       /* Make sure user_rect is current.
2838        */
2839       force_save_user_window_placement (window);
2840 
2841       /* When we unmaximize, if we're doing a mouse move also we could
2842        * get the window suddenly jumping to the upper left corner of
2843        * the workspace, since that's where it was when the grab op
2844        * started.  So we need to update the grab state. We have to do
2845        * it after the actual operation, as the window may have been moved
2846        * by constraints.
2847        */
2848       if (meta_grab_op_is_moving (window->display->grab_op) &&
2849           window->display->grab_window == window)
2850         {
2851           window->display->grab_anchor_window_pos = window->user_rect;
2852         }
2853 
2854       if (window->display->grab_wireframe_active)
2855         {
2856           window->display->grab_wireframe_rect = target_rect;
2857         }
2858 
2859       recalc_window_features (window);
2860       set_allowed_actions_hint (window);
2861       set_net_wm_state (window);
2862 
2863       meta_compositor_unmaximize_window (window->display->compositor, window);
2864   }
2865 }
2866 
2867 void
meta_window_move_to_monitor(MetaWindow * window,const MetaXineramaScreenInfo * from_monitor,const MetaXineramaScreenInfo * to_monitor)2868 meta_window_move_to_monitor(MetaWindow *window,
2869                             const MetaXineramaScreenInfo *from_monitor,
2870                             const MetaXineramaScreenInfo *to_monitor)
2871 {
2872   MetaRectangle target_rect;
2873 
2874   if(META_WINDOW_TILED (window))
2875     {
2876       window->tile_monitor_number = to_monitor->number;
2877 
2878       /* Transform user_rect and saved_rect to the other monitor */
2879       meta_window_transform_to_monitor(&window->saved_rect,
2880                                      &from_monitor->rect,
2881                                      &to_monitor->rect);
2882       meta_window_transform_to_monitor(&window->user_rect,
2883                                      &from_monitor->rect,
2884                                      &to_monitor->rect);
2885 
2886       meta_window_tile(window);
2887       return;
2888     }
2889 
2890   /* Normally, just transform the window itself */
2891   meta_window_get_client_root_coords(window, &target_rect);
2892 
2893   meta_window_transform_to_monitor(&target_rect,
2894                                  &from_monitor->rect,
2895                                  &to_monitor->rect);
2896 
2897   meta_window_move_resize(window, TRUE,
2898                           target_rect.x,
2899                           target_rect.y,
2900                           target_rect.width,
2901                           target_rect.height);
2902 }
2903 
meta_window_transform_to_monitor(MetaRectangle * target_rect,const MetaRectangle * from_monitor,const MetaRectangle * to_monitor)2904 static void meta_window_transform_to_monitor(MetaRectangle *target_rect,
2905                                            const MetaRectangle *from_monitor,
2906                                            const MetaRectangle *to_monitor)
2907 {
2908   double horizontal_ratio;
2909   double vertical_ratio;
2910 
2911   horizontal_ratio = (double)to_monitor->width / from_monitor->width;
2912   vertical_ratio = (double)to_monitor->height / from_monitor->height;
2913 
2914   target_rect->x -= from_monitor->x;
2915   target_rect->y -= from_monitor->y;
2916 
2917   target_rect->width *= horizontal_ratio;
2918   target_rect->height *= vertical_ratio;
2919   target_rect->x *= horizontal_ratio;
2920   target_rect->y *= vertical_ratio;
2921 
2922   target_rect->x += to_monitor->x;
2923   target_rect->y += to_monitor->y;
2924 
2925 }
2926 
2927 void
meta_window_make_above(MetaWindow * window)2928 meta_window_make_above (MetaWindow  *window)
2929 {
2930   window->wm_state_above = TRUE;
2931   meta_window_update_layer (window);
2932   meta_window_raise (window);
2933   set_net_wm_state (window);
2934 }
2935 
2936 void
meta_window_unmake_above(MetaWindow * window)2937 meta_window_unmake_above (MetaWindow  *window)
2938 {
2939   window->wm_state_above = FALSE;
2940   meta_window_raise (window);
2941   meta_window_update_layer (window);
2942   set_net_wm_state (window);
2943 }
2944 
2945 void
meta_window_make_fullscreen_internal(MetaWindow * window)2946 meta_window_make_fullscreen_internal (MetaWindow  *window)
2947 {
2948   if (!window->fullscreen)
2949     {
2950       meta_topic (META_DEBUG_WINDOW_OPS,
2951                   "Fullscreening %s\n", window->desc);
2952 
2953       if (window->shaded)
2954         {
2955           /* Shading sucks anyway; I'm not adding a timestamp argument
2956            * to this function just for this niche usage & corner case.
2957            */
2958           guint32 timestamp =
2959             meta_display_get_current_time_roundtrip (window->display);
2960           meta_window_unshade (window, timestamp);
2961         }
2962 
2963       meta_window_save_rect (window);
2964 
2965       window->fullscreen = TRUE;
2966       window->tile_resized = FALSE;
2967       window->force_save_user_rect = FALSE;
2968 
2969       meta_stack_freeze (window->screen->stack);
2970       meta_window_update_layer (window);
2971 
2972       meta_window_raise (window);
2973       meta_stack_thaw (window->screen->stack);
2974 
2975       recalc_window_features (window);
2976       set_allowed_actions_hint (window);
2977       set_net_wm_state (window);
2978     }
2979 }
2980 
2981 void
meta_window_make_fullscreen(MetaWindow * window)2982 meta_window_make_fullscreen (MetaWindow  *window)
2983 {
2984   if (!window->fullscreen)
2985     {
2986       meta_window_make_fullscreen_internal (window);
2987       /* move_resize with new constraints
2988        */
2989       meta_window_queue(window, META_QUEUE_MOVE_RESIZE);
2990     }
2991 }
2992 
2993 void
meta_window_unmake_fullscreen(MetaWindow * window)2994 meta_window_unmake_fullscreen (MetaWindow  *window)
2995 {
2996   if (window->fullscreen)
2997     {
2998       MetaRectangle target_rect;
2999 
3000       meta_topic (META_DEBUG_WINDOW_OPS,
3001                   "Unfullscreening %s\n", window->desc);
3002 
3003       window->fullscreen = FALSE;
3004 
3005       if(!META_WINDOW_TILED (window))
3006         target_rect = window->saved_rect;
3007       else
3008         meta_window_get_current_tile_area(window, &target_rect);
3009 
3010       /* Window's size hints may have changed while maximized, making
3011        * saved_rect invalid.  #329152
3012        */
3013       ensure_size_hints_satisfied (&target_rect, &window->size_hints);
3014 
3015       /* Need to update window->has_resize_func before we move_resize()
3016        */
3017       recalc_window_features (window);
3018       set_net_wm_state (window);
3019 
3020       meta_window_move_resize (window,
3021                                FALSE,
3022                                target_rect.x,
3023                                target_rect.y,
3024                                target_rect.width,
3025                                target_rect.height);
3026 
3027       /* Make sure user_rect is current.
3028        */
3029       force_save_user_window_placement (window);
3030 
3031       meta_window_update_layer (window);
3032     }
3033 }
3034 
3035 void
meta_window_update_fullscreen_monitors(MetaWindow * window,unsigned long top,unsigned long bottom,unsigned long left,unsigned long right)3036 meta_window_update_fullscreen_monitors (MetaWindow    *window,
3037                                         unsigned long  top,
3038                                         unsigned long  bottom,
3039                                         unsigned long  left,
3040                                         unsigned long  right)
3041 {
3042   if ((int)top < window->screen->n_xinerama_infos &&
3043       (int)bottom < window->screen->n_xinerama_infos &&
3044       (int)left < window->screen->n_xinerama_infos &&
3045       (int)right < window->screen->n_xinerama_infos)
3046     {
3047       window->fullscreen_monitors[0] = top;
3048       window->fullscreen_monitors[1] = bottom;
3049       window->fullscreen_monitors[2] = left;
3050       window->fullscreen_monitors[3] = right;
3051     }
3052   else
3053     {
3054       window->fullscreen_monitors[0] = -1;
3055     }
3056 
3057   if (window->fullscreen)
3058     {
3059       meta_window_queue(window, META_QUEUE_MOVE_RESIZE);
3060     }
3061 }
3062 
3063 void
meta_window_shade(MetaWindow * window,guint32 timestamp)3064 meta_window_shade (MetaWindow  *window,
3065                    guint32      timestamp)
3066 {
3067   meta_topic (META_DEBUG_WINDOW_OPS,
3068               "Shading %s\n", window->desc);
3069   if (!window->shaded)
3070     {
3071       window->shaded = TRUE;
3072 
3073       meta_window_queue(window, META_QUEUE_MOVE_RESIZE | META_QUEUE_CALC_SHOWING);
3074 
3075       set_allowed_actions_hint (window);
3076 
3077       /* After queuing the calc showing, since _focus flushes it,
3078        * and we need to focus the frame
3079        */
3080       meta_topic (META_DEBUG_FOCUS,
3081                   "Re-focusing window %s after shading it\n",
3082                   window->desc);
3083       meta_window_focus (window, timestamp);
3084 
3085       set_net_wm_state (window);
3086     }
3087 }
3088 
3089 void
meta_window_unshade(MetaWindow * window,guint32 timestamp)3090 meta_window_unshade (MetaWindow  *window,
3091                      guint32      timestamp)
3092 {
3093   meta_topic (META_DEBUG_WINDOW_OPS,
3094               "Unshading %s\n", window->desc);
3095   if (window->shaded)
3096     {
3097       window->shaded = FALSE;
3098       meta_window_queue(window, META_QUEUE_MOVE_RESIZE | META_QUEUE_CALC_SHOWING);
3099 
3100       set_allowed_actions_hint (window);
3101 
3102       /* focus the window */
3103       meta_topic (META_DEBUG_FOCUS,
3104                   "Focusing window %s after unshading it\n",
3105                   window->desc);
3106       meta_window_focus (window, timestamp);
3107 
3108       set_net_wm_state (window);
3109     }
3110 }
3111 
3112 static gboolean
unminimize_func(MetaWindow * window,void * data)3113 unminimize_func (MetaWindow *window,
3114                  void       *data)
3115 {
3116   meta_window_unminimize (window);
3117   return TRUE;
3118 }
3119 
3120 static void
unminimize_window_and_all_transient_parents(MetaWindow * window)3121 unminimize_window_and_all_transient_parents (MetaWindow *window)
3122 {
3123   meta_window_unminimize (window);
3124   meta_window_foreach_ancestor (window, unminimize_func, NULL);
3125 }
3126 
3127 static void
window_activate(MetaWindow * window,guint32 timestamp,MetaClientType source_indication,MetaWorkspace * workspace)3128 window_activate (MetaWindow     *window,
3129                  guint32         timestamp,
3130                  MetaClientType  source_indication,
3131                  MetaWorkspace  *workspace)
3132 {
3133   gboolean can_ignore_outdated_timestamps;
3134   meta_topic (META_DEBUG_FOCUS,
3135               "_NET_ACTIVE_WINDOW message sent for %s at time %u "
3136               "by client type %u.\n",
3137               window->desc, timestamp, source_indication);
3138 
3139   /* Older EWMH spec didn't specify a timestamp; we decide to honor these only
3140    * if the app specifies that it is a pager.
3141    *
3142    * Update: Unconditionally honor 0 timestamps for now; we'll fight
3143    * that battle later.  Just remove the "FALSE &&" in order to only
3144    * honor 0 timestamps for pagers.
3145    */
3146   can_ignore_outdated_timestamps =
3147     (timestamp != 0 || (FALSE && source_indication != META_CLIENT_TYPE_PAGER));
3148   if (XSERVER_TIME_IS_BEFORE (timestamp, window->display->last_user_time) &&
3149       can_ignore_outdated_timestamps)
3150     {
3151       meta_topic (META_DEBUG_FOCUS,
3152                   "last_user_time (%u) is more recent; ignoring "
3153                   " _NET_ACTIVE_WINDOW message.\n",
3154                   window->display->last_user_time);
3155       meta_window_set_demands_attention(window);
3156       return;
3157     }
3158 
3159   /* For those stupid pagers, get a valid timestamp and show a warning */
3160   if (timestamp == 0)
3161     {
3162       meta_warning ("meta_window_activate called by a pager with a 0 timestamp; "
3163                     "the pager needs to be fixed.\n");
3164       timestamp = meta_display_get_current_time_roundtrip (window->display);
3165     }
3166 
3167   meta_window_set_user_time (window, timestamp);
3168 
3169   /* disable show desktop mode unless we're a desktop component */
3170   maybe_leave_show_desktop_mode (window);
3171 
3172   /* Get window on current or given workspace */
3173   if (workspace == NULL)
3174     workspace = window->screen->active_workspace;
3175 
3176   /* For non-transient windows, we just set up a pulsing indicator,
3177      rather than move windows or workspaces.
3178      See http://bugzilla.gnome.org/show_bug.cgi?id=482354 */
3179   if (window->xtransient_for == None &&
3180       !meta_window_located_on_workspace (window, workspace))
3181     {
3182       meta_window_set_demands_attention (window);
3183       /* We've marked it as demanding, don't need to do anything else. */
3184       return;
3185     }
3186   else if (window->xtransient_for != None)
3187     {
3188       /* Move transients to current workspace - preference dialogs should appear over
3189          the source window.  */
3190       meta_window_change_workspace (window, workspace);
3191     }
3192 
3193   if (window->shaded)
3194     meta_window_unshade (window, timestamp);
3195 
3196   unminimize_window_and_all_transient_parents (window);
3197 
3198   if (meta_prefs_get_raise_on_click () ||
3199       source_indication == META_CLIENT_TYPE_PAGER)
3200     meta_window_raise (window);
3201 
3202   meta_topic (META_DEBUG_FOCUS,
3203               "Focusing window %s due to activation\n",
3204               window->desc);
3205   meta_window_focus (window, timestamp);
3206 }
3207 
3208 /* This function exists since most of the functionality in window_activate
3209  * is useful for Marco, but Marco shouldn't need to specify a client
3210  * type for itself.  ;-)
3211  */
3212 void
meta_window_activate(MetaWindow * window,guint32 timestamp)3213 meta_window_activate (MetaWindow     *window,
3214                       guint32         timestamp)
3215 {
3216   /* We're not really a pager, but the behavior we want is the same as if
3217    * we were such.  If we change the pager behavior later, we could revisit
3218    * this and just add extra flags to window_activate.
3219    */
3220   window_activate (window, timestamp, META_CLIENT_TYPE_PAGER, NULL);
3221 }
3222 
3223 void
meta_window_activate_with_workspace(MetaWindow * window,guint32 timestamp,MetaWorkspace * workspace)3224 meta_window_activate_with_workspace (MetaWindow     *window,
3225                                      guint32         timestamp,
3226                                      MetaWorkspace  *workspace)
3227 {
3228   /* We're not really a pager, but the behavior we want is the same as if
3229    * we were such.  If we change the pager behavior later, we could revisit
3230    * this and just add extra flags to window_activate.
3231    */
3232   window_activate (window, timestamp, META_CLIENT_TYPE_APPLICATION, workspace);
3233 }
3234 
3235 /* Manually fix all the weirdness explained in the big comment at the
3236  * beginning of meta_window_move_resize_internal() giving positions
3237  * expected by meta_window_constrain (i.e. positions & sizes of the
3238  * internal or client window).
3239  */
3240 static void
adjust_for_gravity(MetaWindow * window,MetaFrameBorders * borders,gboolean coords_assume_border,int gravity,MetaRectangle * rect)3241 adjust_for_gravity (MetaWindow       *window,
3242                     MetaFrameBorders *borders,
3243                     gboolean          coords_assume_border,
3244                     int               gravity,
3245                     MetaRectangle    *rect)
3246 {
3247   int ref_x, ref_y;
3248   int bw;
3249   int child_x, child_y;
3250   int frame_width, frame_height;
3251 
3252   if (coords_assume_border)
3253     bw = window->border_width;
3254   else
3255     bw = 0;
3256 
3257   if (borders)
3258     {
3259       child_x = borders->visible.left;
3260       child_y = borders->visible.top;
3261       frame_width = child_x + rect->width + borders->visible.right;
3262       frame_height = child_y + rect->height + borders->visible.bottom;
3263     }
3264   else
3265     {
3266       child_x = 0;
3267       child_y = 0;
3268       frame_width = rect->width;
3269       frame_height = rect->height;
3270     }
3271 
3272   /* We're computing position to pass to window_move, which is
3273    * the position of the client window (StaticGravity basically)
3274    *
3275    * (see WM spec description of gravity computation, but note that
3276    * their formulas assume we're honoring the border width, rather
3277    * than compensating for having turned it off)
3278    */
3279   switch (gravity)
3280     {
3281     case NorthWestGravity:
3282       ref_x = rect->x;
3283       ref_y = rect->y;
3284       break;
3285     case NorthGravity:
3286       ref_x = rect->x + rect->width / 2 + bw;
3287       ref_y = rect->y;
3288       break;
3289     case NorthEastGravity:
3290       ref_x = rect->x + rect->width + bw * 2;
3291       ref_y = rect->y;
3292       break;
3293     case WestGravity:
3294       ref_x = rect->x;
3295       ref_y = rect->y + rect->height / 2 + bw;
3296       break;
3297     case CenterGravity:
3298       ref_x = rect->x + rect->width / 2 + bw;
3299       ref_y = rect->y + rect->height / 2 + bw;
3300       break;
3301     case EastGravity:
3302       ref_x = rect->x + rect->width + bw * 2;
3303       ref_y = rect->y + rect->height / 2 + bw;
3304       break;
3305     case SouthWestGravity:
3306       ref_x = rect->x;
3307       ref_y = rect->y + rect->height + bw * 2;
3308       break;
3309     case SouthGravity:
3310       ref_x = rect->x + rect->width / 2 + bw;
3311       ref_y = rect->y + rect->height + bw * 2;
3312       break;
3313     case SouthEastGravity:
3314       ref_x = rect->x + rect->width + bw * 2;
3315       ref_y = rect->y + rect->height + bw * 2;
3316       break;
3317     case StaticGravity:
3318     default:
3319       ref_x = rect->x;
3320       ref_y = rect->y;
3321       break;
3322     }
3323 
3324   switch (gravity)
3325     {
3326     case NorthWestGravity:
3327       rect->x = ref_x + child_x;
3328       rect->y = ref_y + child_y;
3329       break;
3330     case NorthGravity:
3331       rect->x = ref_x - frame_width / 2 + child_x;
3332       rect->y = ref_y + child_y;
3333       break;
3334     case NorthEastGravity:
3335       rect->x = ref_x - frame_width + child_x;
3336       rect->y = ref_y + child_y;
3337       break;
3338     case WestGravity:
3339       rect->x = ref_x + child_x;
3340       rect->y = ref_y - frame_height / 2 + child_y;
3341       break;
3342     case CenterGravity:
3343       rect->x = ref_x - frame_width / 2 + child_x;
3344       rect->y = ref_y - frame_height / 2 + child_y;
3345       break;
3346     case EastGravity:
3347       rect->x = ref_x - frame_width + child_x;
3348       rect->y = ref_y - frame_height / 2 + child_y;
3349       break;
3350     case SouthWestGravity:
3351       rect->x = ref_x + child_x;
3352       rect->y = ref_y - frame_height + child_y;
3353       break;
3354     case SouthGravity:
3355       rect->x = ref_x - frame_width / 2 + child_x;
3356       rect->y = ref_y - frame_height + child_y;
3357       break;
3358     case SouthEastGravity:
3359       rect->x = ref_x - frame_width + child_x;
3360       rect->y = ref_y - frame_height + child_y;
3361       break;
3362     case StaticGravity:
3363     default:
3364       rect->x = ref_x;
3365       rect->y = ref_y;
3366       break;
3367     }
3368 }
3369 
3370 static gboolean
static_gravity_works(MetaDisplay * display)3371 static_gravity_works (MetaDisplay *display)
3372 {
3373   return display->static_gravity_works;
3374 }
3375 
3376 #ifdef HAVE_XSYNC
3377 static void
send_sync_request(MetaWindow * window)3378 send_sync_request (MetaWindow *window)
3379 {
3380   XSyncValue value;
3381   XClientMessageEvent ev;
3382 
3383   window->sync_request_serial++;
3384 
3385   XSyncIntToValue (&value, window->sync_request_serial);
3386 
3387   ev.type = ClientMessage;
3388   ev.window = window->xwindow;
3389   ev.message_type = window->display->atom_WM_PROTOCOLS;
3390   ev.format = 32;
3391   ev.data.l[0] = window->display->atom__NET_WM_SYNC_REQUEST;
3392   /* FIXME: meta_display_get_current_time() is bad, but since calls
3393    * come from meta_window_move_resize_internal (which in turn come
3394    * from all over), I'm not sure what we can do to fix it.  Do we
3395    * want to use _roundtrip, though?
3396    */
3397   ev.data.l[1] = meta_display_get_current_time (window->display);
3398   ev.data.l[2] = XSyncValueLow32 (value);
3399   ev.data.l[3] = XSyncValueHigh32 (value);
3400   ev.data.l[4] = 0;
3401 
3402   /* We don't need to trap errors here as we are already
3403    * inside an error_trap_push()/pop() pair.
3404    */
3405   XSendEvent (window->display->xdisplay,
3406 	      window->xwindow, False, 0, (XEvent*) &ev);
3407 
3408   window->sync_request_time = g_get_real_time ();
3409 }
3410 #endif
3411 
3412 static gboolean
move_attached_dialog(MetaWindow * window,void * data)3413 move_attached_dialog (MetaWindow *window,
3414                       void       *data)
3415 {
3416   MetaWindow *parent = meta_window_get_transient_for (window);
3417 
3418   if (window->type == META_WINDOW_MODAL_DIALOG && parent && parent != window)
3419     /* It ignores x,y for such a dialog */
3420     meta_window_move (window, FALSE, 0, 0);
3421 
3422   return FALSE;
3423 }
3424 
3425 static void
meta_window_move_resize_internal(MetaWindow * window,MetaMoveResizeFlags flags,int gravity,int root_x_nw,int root_y_nw,int w,int h)3426 meta_window_move_resize_internal (MetaWindow          *window,
3427                                   MetaMoveResizeFlags  flags,
3428                                   int                  gravity,
3429                                   int                  root_x_nw,
3430                                   int                  root_y_nw,
3431                                   int                  w,
3432                                   int                  h)
3433 {
3434   /* meta_window_move_resize_internal gets called with very different
3435    * meanings for root_x_nw and root_y_nw.  w & h are always the area
3436    * of the inner or client window (i.e. excluding the frame) and
3437    * gravity is the relevant gravity associated with the request (note
3438    * that gravity is ignored for move-only operations unless its
3439    * e.g. a configure request).  The location is different for
3440    * different cases because of how this function gets called; note
3441    * that in all cases what we want to find out is the upper left
3442    * corner of the position of the inner window:
3443    *
3444    *   Case | Called from (flags; gravity)
3445    *   -----+-----------------------------------------------
3446    *    1   | A resize only ConfigureRequest
3447    *    1   | meta_window_resize
3448    *    1   | meta_window_resize_with_gravity
3449    *    2   | New window
3450    *    2   | Session restore
3451    *    2   | A not-resize-only ConfigureRequest/net_moveresize_window request
3452    *    3   | meta_window_move
3453    *    3   | meta_window_move_resize
3454    *
3455    * For each of the cases, root_x_nw and root_y_nw must be treated as follows:
3456    *
3457    *   (1) They should be entirely ignored; instead the previous position
3458    *       and size of the window should be resized according to the given
3459    *       gravity in order to determine the new position of the window.
3460    *   (2) Needs to be fixed up by adjust_for_gravity() as these
3461    *       coordinates are relative to some corner or side of the outer
3462    *       window (except for the case of StaticGravity) and we want to
3463    *       know the location of the upper left corner of the inner window.
3464    *   (3) These values are already the desired positon of the NW corner
3465    *       of the inner window
3466    */
3467   XWindowChanges values;
3468   unsigned int mask;
3469   gboolean need_configure_notify;
3470   MetaFrameBorders borders;
3471   gboolean need_move_client = FALSE;
3472   gboolean need_move_frame = FALSE;
3473   gboolean need_resize_client = FALSE;
3474   gboolean need_resize_frame = FALSE;
3475   int size_dx;
3476   int size_dy;
3477   gboolean frame_shape_changed = FALSE;
3478   gboolean is_configure_request;
3479   gboolean do_gravity_adjust;
3480   gboolean is_user_action;
3481   gboolean configure_frame_first;
3482   gboolean use_static_gravity;
3483   gboolean have_window_frame;
3484   /* used for the configure request, but may not be final
3485    * destination due to StaticGravity etc.
3486    */
3487   int client_move_x;
3488   int client_move_y;
3489   MetaRectangle new_rect;
3490   MetaRectangle old_rect;
3491 
3492   if (window->frame)
3493     have_window_frame = TRUE;
3494   else
3495     have_window_frame = FALSE;
3496 
3497   is_configure_request = (flags & META_IS_CONFIGURE_REQUEST) != 0;
3498   do_gravity_adjust = (flags & META_DO_GRAVITY_ADJUST) != 0;
3499   is_user_action = (flags & META_IS_USER_ACTION) != 0;
3500 
3501   /* The action has to be a move or a resize or both... */
3502   g_assert (flags & (META_IS_MOVE_ACTION | META_IS_RESIZE_ACTION));
3503 
3504   /* We don't need it in the idle queue anymore. */
3505   meta_window_unqueue (window, META_QUEUE_MOVE_RESIZE);
3506 
3507   meta_window_get_client_root_coords (window, &old_rect);
3508 
3509   meta_topic (META_DEBUG_GEOMETRY,
3510               "Move/resize %s to %d,%d %dx%d%s%s from %d,%d %dx%d\n",
3511               window->desc, root_x_nw, root_y_nw, w, h,
3512               is_configure_request ? " (configure request)" : "",
3513               is_user_action ? " (user move/resize)" : "",
3514               old_rect.x, old_rect.y, old_rect.width, old_rect.height);
3515 
3516   if (have_window_frame)
3517     meta_frame_calc_borders (window->frame, &borders);
3518 
3519   new_rect.x = root_x_nw;
3520   new_rect.y = root_y_nw;
3521   new_rect.width  = w;
3522   new_rect.height = h;
3523 
3524   /* If this is a resize only, the position should be ignored and
3525    * instead obtained by resizing the old rectangle according to the
3526    * relevant gravity.
3527    */
3528   if ((flags & (META_IS_MOVE_ACTION | META_IS_RESIZE_ACTION)) ==
3529       META_IS_RESIZE_ACTION)
3530     {
3531       meta_rectangle_resize_with_gravity (&old_rect,
3532                                           &new_rect,
3533                                           gravity,
3534                                           new_rect.width,
3535                                           new_rect.height);
3536 
3537       meta_topic (META_DEBUG_GEOMETRY,
3538                   "Compensated for gravity in resize action; new pos %d,%d\n",
3539                   new_rect.x, new_rect.y);
3540     }
3541   else if (is_configure_request || do_gravity_adjust)
3542     {
3543       adjust_for_gravity (window,
3544                           have_window_frame ? &borders : NULL,
3545                           /* configure request coords assume
3546                            * the border width existed
3547                            */
3548                           is_configure_request,
3549                           gravity,
3550                           &new_rect);
3551 
3552       meta_topic (META_DEBUG_GEOMETRY,
3553                   "Compensated for configure_request/do_gravity_adjust needing "
3554                   "weird positioning; new pos %d,%d\n",
3555                   new_rect.x, new_rect.y);
3556     }
3557 
3558   meta_window_constrain (window,
3559                          have_window_frame ? &borders : NULL,
3560                          flags,
3561                          gravity,
3562                          &old_rect,
3563                          &new_rect);
3564 
3565   w = new_rect.width;
3566   h = new_rect.height;
3567   root_x_nw = new_rect.x;
3568   root_y_nw = new_rect.y;
3569 
3570   if (w != window->rect.width ||
3571       h != window->rect.height)
3572     need_resize_client = TRUE;
3573 
3574   window->rect.width = w;
3575   window->rect.height = h;
3576 
3577   if (have_window_frame)
3578     {
3579       int frame_size_dx, frame_size_dy, new_w, new_h;
3580 
3581       new_w = window->rect.width + borders.total.left + borders.total.right;
3582 
3583       if (window->shaded)
3584         new_h = borders.total.top;
3585       else
3586         new_h = window->rect.height + borders.total.top + borders.total.bottom;
3587 
3588       frame_size_dx = new_w - window->frame->rect.width;
3589       frame_size_dy = new_h - window->frame->rect.height;
3590 
3591       need_resize_frame = (frame_size_dx != 0 || frame_size_dy != 0);
3592 
3593       window->frame->rect.width = new_w;
3594       window->frame->rect.height = new_h;
3595 
3596       meta_topic (META_DEBUG_GEOMETRY,
3597                   "Calculated frame size %dx%d\n",
3598                   window->frame->rect.width,
3599                   window->frame->rect.height);
3600     }
3601 
3602   /* For nice effect, when growing the window we want to move/resize
3603    * the frame first, when shrinking the window we want to move/resize
3604    * the client first. If we grow one way and shrink the other,
3605    * see which way we're moving "more"
3606    *
3607    * Mail from Owen subject "Suggestion: Gravity and resizing from the left"
3608    * http://mail.gnome.org/archives/wm-spec-list/1999-November/msg00088.html
3609    *
3610    * An annoying fact you need to know in this code is that StaticGravity
3611    * does nothing if you _only_ resize or _only_ move the frame;
3612    * it must move _and_ resize, otherwise you get NorthWestGravity
3613    * behavior. The move and resize must actually occur, it is not
3614    * enough to set CWX | CWWidth but pass in the current size/pos.
3615    */
3616 
3617   if (have_window_frame)
3618     {
3619       int new_x, new_y;
3620       int frame_pos_dx, frame_pos_dy;
3621 
3622       /* Compute new frame coords */
3623       new_x = root_x_nw - borders.total.left;
3624       new_y = root_y_nw - borders.total.top;
3625 
3626       frame_pos_dx = new_x - window->frame->rect.x;
3627       frame_pos_dy = new_y - window->frame->rect.y;
3628 
3629       need_move_frame = (frame_pos_dx != 0 || frame_pos_dy != 0);
3630 
3631       window->frame->rect.x = new_x;
3632       window->frame->rect.y = new_y;
3633 
3634       /* If frame will both move and resize, then StaticGravity
3635        * on the child window will kick in and implicitly move
3636        * the child with respect to the frame. The implicit
3637        * move will keep the child in the same place with
3638        * respect to the root window. If frame only moves
3639        * or only resizes, then the child will just move along
3640        * with the frame.
3641        */
3642 
3643       /* window->rect.x, window->rect.y are relative to frame,
3644        * remember they are the server coords
3645        */
3646 
3647       new_x = borders.total.left;
3648       new_y = borders.total.top;
3649 
3650       if (need_resize_frame && need_move_frame &&
3651           static_gravity_works (window->display))
3652         {
3653           /* static gravity kicks in because frame
3654            * is both moved and resized
3655            */
3656           /* when we move the frame by frame_pos_dx, frame_pos_dy the
3657            * client will implicitly move relative to frame by the
3658            * inverse delta.
3659            *
3660            * When moving client then frame, we move the client by the
3661            * frame delta, to be canceled out by the implicit move by
3662            * the inverse frame delta, resulting in a client at new_x,
3663            * new_y.
3664            *
3665            * When moving frame then client, we move the client
3666            * by the same delta as the frame, because the client
3667            * was "left behind" by the frame - resulting in a client
3668            * at new_x, new_y.
3669            *
3670            * In both cases we need to move the client window
3671            * in all cases where we had to move the frame window.
3672            */
3673 
3674           client_move_x = new_x + frame_pos_dx;
3675           client_move_y = new_y + frame_pos_dy;
3676 
3677           if (need_move_frame)
3678             need_move_client = TRUE;
3679 
3680           use_static_gravity = TRUE;
3681         }
3682       else
3683         {
3684           client_move_x = new_x;
3685           client_move_y = new_y;
3686 
3687           if (client_move_x != window->rect.x ||
3688               client_move_y != window->rect.y)
3689             need_move_client = TRUE;
3690 
3691           use_static_gravity = FALSE;
3692         }
3693 
3694       /* This is the final target position, but not necessarily what
3695        * we pass to XConfigureWindow, due to StaticGravity implicit
3696        * movement.
3697        */
3698       window->rect.x = new_x;
3699       window->rect.y = new_y;
3700     }
3701   else
3702     {
3703       if (root_x_nw != window->rect.x ||
3704           root_y_nw != window->rect.y)
3705         need_move_client = TRUE;
3706 
3707       window->rect.x = root_x_nw;
3708       window->rect.y = root_y_nw;
3709 
3710       client_move_x = window->rect.x;
3711       client_move_y = window->rect.y;
3712 
3713       use_static_gravity = FALSE;
3714     }
3715 
3716   /* If frame extents have changed, fill in other frame fields and
3717      change frame's extents property. */
3718   if (have_window_frame &&
3719       (window->frame->child_x != borders.total.left ||
3720        window->frame->child_y != borders.total.top ||
3721        window->frame->right_width != borders.total.right ||
3722        window->frame->bottom_height != borders.total.bottom))
3723     {
3724       window->frame->child_x = borders.total.left;
3725       window->frame->child_y = borders.total.top;
3726       window->frame->right_width = borders.total.right;
3727       window->frame->bottom_height = borders.total.bottom;
3728 
3729       update_net_frame_extents (window);
3730     }
3731 
3732   /* See ICCCM 4.1.5 for when to send ConfigureNotify */
3733 
3734   need_configure_notify = FALSE;
3735 
3736   /* If this is a configure request and we change nothing, then we
3737    * must send configure notify.
3738    */
3739   if  (is_configure_request &&
3740        !(need_move_client || need_move_frame ||
3741          need_resize_client || need_resize_frame ||
3742          window->border_width != 0))
3743     need_configure_notify = TRUE;
3744 
3745   /* We must send configure notify if we move but don't resize, since
3746    * the client window may not get a real event
3747    */
3748   if ((need_move_client || need_move_frame) &&
3749       !(need_resize_client || need_resize_frame))
3750     need_configure_notify = TRUE;
3751 
3752   /* MapRequest events with a PPosition or UPosition hint with a frame
3753    * are moved by marco without resizing; send a configure notify
3754    * in such cases.  See #322840.  (Note that window->constructing is
3755    * only true iff this call is due to a MapRequest, and when
3756    * PPosition/UPosition hints aren't set, marco seems to send a
3757    * ConfigureNotify anyway due to the above code.)
3758    */
3759   if (window->constructing && have_window_frame &&
3760       ((window->size_hints.flags & PPosition) ||
3761        (window->size_hints.flags & USPosition)))
3762     need_configure_notify = TRUE;
3763 
3764   /* The rest of this function syncs our new size/pos with X as
3765    * efficiently as possible
3766    */
3767 
3768   /* configure frame first if we grow more than we shrink
3769    */
3770   size_dx = w - window->rect.width;
3771   size_dy = h - window->rect.height;
3772 
3773   configure_frame_first = (size_dx + size_dy >= 0);
3774 
3775   if (use_static_gravity)
3776     meta_window_set_gravity (window, StaticGravity);
3777 
3778   if (configure_frame_first && have_window_frame)
3779     frame_shape_changed = meta_frame_sync_to_window (window->frame,
3780                                                      gravity,
3781                                                      need_move_frame,
3782                                                      need_resize_frame);
3783 
3784   values.border_width = 0;
3785   values.x = client_move_x;
3786   values.y = client_move_y;
3787   values.width = window->rect.width;
3788   values.height = window->rect.height;
3789 
3790   mask = 0;
3791   if (is_configure_request && window->border_width != 0)
3792     mask |= CWBorderWidth; /* must force to 0 */
3793   if (need_move_client)
3794     mask |= (CWX | CWY);
3795   if (need_resize_client)
3796     mask |= (CWWidth | CWHeight);
3797 
3798   if (mask != 0)
3799     {
3800       {
3801         int newx, newy;
3802         meta_window_get_position (window, &newx, &newy);
3803         meta_topic (META_DEBUG_GEOMETRY,
3804                     "Syncing new client geometry %d,%d %dx%d, border: %s pos: %s size: %s\n",
3805                     newx, newy,
3806                     window->rect.width, window->rect.height,
3807                     mask & CWBorderWidth ? "true" : "false",
3808                     need_move_client ? "true" : "false",
3809                     need_resize_client ? "true" : "false");
3810       }
3811 
3812       meta_error_trap_push (window->display);
3813 
3814 #ifdef HAVE_XSYNC
3815       if (window->sync_request_counter != None &&
3816 	  window->display->grab_sync_request_alarm != None &&
3817           window->sync_request_time == 0)
3818 	{
3819 	  /* turn off updating */
3820 	  if (window->display->compositor)
3821 	    meta_compositor_set_updates (window->display->compositor, window, FALSE);
3822 
3823 	  send_sync_request (window);
3824 	}
3825 #endif
3826 
3827       XConfigureWindow (window->display->xdisplay,
3828                         window->xwindow,
3829                         mask,
3830                         &values);
3831 
3832       meta_error_trap_pop (window->display, FALSE);
3833     }
3834 
3835   if (!configure_frame_first && have_window_frame)
3836     frame_shape_changed = meta_frame_sync_to_window (window->frame,
3837                                                      gravity,
3838                                                      need_move_frame,
3839                                                      need_resize_frame);
3840 
3841   /* Put gravity back to be nice to lesser window managers */
3842   if (use_static_gravity)
3843     meta_window_set_gravity (window, NorthWestGravity);
3844 
3845   if (need_configure_notify)
3846     send_configure_notify (window);
3847 
3848   if (!window->placed && window->force_save_user_rect && !window->fullscreen)
3849     force_save_user_window_placement (window);
3850   else if (is_user_action)
3851     save_user_window_placement (window);
3852 
3853   if (need_move_frame || need_resize_frame ||
3854       need_move_client || need_resize_client)
3855     {
3856       int newx, newy;
3857       meta_window_get_position (window, &newx, &newy);
3858       meta_topic (META_DEBUG_GEOMETRY,
3859                   "New size/position %d,%d %dx%d (user %d,%d %dx%d)\n",
3860                   newx, newy, window->rect.width, window->rect.height,
3861                   window->user_rect.x, window->user_rect.y,
3862                   window->user_rect.width, window->user_rect.height);
3863     }
3864   else
3865     {
3866       meta_topic (META_DEBUG_GEOMETRY, "Size/position not modified\n");
3867     }
3868 
3869   if (window->display->grab_wireframe_active)
3870     meta_window_update_wireframe (window, root_x_nw, root_y_nw, w, h);
3871   else
3872     meta_window_refresh_resize_popup (window);
3873 
3874   /* Invariants leaving this function are:
3875    *   a) window->rect and frame->rect reflect the actual
3876    *      server-side size/pos of window->xwindow and frame->xwindow
3877    *   b) all constraints are obeyed by window->rect and frame->rect
3878    */
3879   if (frame_shape_changed && window->frame_bounds)
3880     {
3881       cairo_region_destroy (window->frame_bounds);
3882       window->frame_bounds = NULL;
3883     }
3884 
3885   if (meta_prefs_get_attach_modal_dialogs ())
3886     meta_window_foreach_transient (window, move_attached_dialog, NULL);
3887 }
3888 
3889 void
meta_window_resize(MetaWindow * window,gboolean user_op,int w,int h)3890 meta_window_resize (MetaWindow  *window,
3891                     gboolean     user_op,
3892                     int          w,
3893                     int          h)
3894 {
3895   int x, y;
3896   MetaMoveResizeFlags flags;
3897 
3898   meta_window_get_position (window, &x, &y);
3899 
3900   flags = (user_op ? META_IS_USER_ACTION : 0) | META_IS_RESIZE_ACTION;
3901   meta_window_move_resize_internal (window,
3902                                     flags,
3903                                     NorthWestGravity,
3904                                     x, y, w, h);
3905 }
3906 
3907 void
meta_window_move(MetaWindow * window,gboolean user_op,int root_x_nw,int root_y_nw)3908 meta_window_move (MetaWindow  *window,
3909                   gboolean     user_op,
3910                   int          root_x_nw,
3911                   int          root_y_nw)
3912 {
3913   MetaMoveResizeFlags flags =
3914     (user_op ? META_IS_USER_ACTION : 0) | META_IS_MOVE_ACTION;
3915   meta_window_move_resize_internal (window,
3916                                     flags,
3917                                     NorthWestGravity,
3918                                     root_x_nw, root_y_nw,
3919                                     window->rect.width,
3920                                     window->rect.height);
3921 }
3922 
3923 void
meta_window_move_resize(MetaWindow * window,gboolean user_op,int root_x_nw,int root_y_nw,int w,int h)3924 meta_window_move_resize (MetaWindow  *window,
3925                          gboolean     user_op,
3926                          int          root_x_nw,
3927                          int          root_y_nw,
3928                          int          w,
3929                          int          h)
3930 {
3931   MetaMoveResizeFlags flags =
3932     (user_op ? META_IS_USER_ACTION : 0) |
3933     META_IS_MOVE_ACTION | META_IS_RESIZE_ACTION;
3934   meta_window_move_resize_internal (window,
3935                                     flags,
3936                                     NorthWestGravity,
3937                                     root_x_nw, root_y_nw,
3938                                     w, h);
3939 }
3940 
3941 void
meta_window_resize_with_gravity(MetaWindow * window,gboolean user_op,int w,int h,int gravity)3942 meta_window_resize_with_gravity (MetaWindow *window,
3943                                  gboolean     user_op,
3944                                  int          w,
3945                                  int          h,
3946                                  int          gravity)
3947 {
3948   int x, y;
3949   MetaMoveResizeFlags flags;
3950 
3951   meta_window_get_position (window, &x, &y);
3952 
3953   flags = (user_op ? META_IS_USER_ACTION : 0) | META_IS_RESIZE_ACTION;
3954   meta_window_move_resize_internal (window,
3955                                     flags,
3956                                     gravity,
3957                                     x, y, w, h);
3958 }
3959 
3960 static void
meta_window_move_resize_now(MetaWindow * window)3961 meta_window_move_resize_now (MetaWindow  *window)
3962 {
3963   /* If constraints have changed then we want to snap back to wherever
3964    * the user had the window.  We use user_rect for this reason.  See
3965    * also bug 426519 comment 3.
3966    */
3967   meta_window_move_resize (window, FALSE,
3968                            window->user_rect.x,
3969                            window->user_rect.y,
3970                            window->user_rect.width,
3971                            window->user_rect.height);
3972 }
3973 
3974 static gboolean
idle_move_resize(gpointer data)3975 idle_move_resize (gpointer data)
3976 {
3977   GSList *tmp;
3978   GSList *copy;
3979   guint queue_index = GPOINTER_TO_INT (data);
3980 
3981   meta_topic (META_DEBUG_GEOMETRY, "Clearing the move_resize queue\n");
3982 
3983   /* Work with a copy, for reentrancy. The allowed reentrancy isn't
3984    * complete; destroying a window while we're in here would result in
3985    * badness. But it's OK to queue/unqueue move_resizes.
3986    */
3987   copy = g_slist_copy (queue_pending[queue_index]);
3988   g_slist_free (queue_pending[queue_index]);
3989   queue_pending[queue_index] = NULL;
3990   queue_idle[queue_index] = 0;
3991 
3992   destroying_windows_disallowed += 1;
3993 
3994   tmp = copy;
3995   while (tmp != NULL)
3996     {
3997       MetaWindow *window;
3998 
3999       window = tmp->data;
4000 
4001       /* As a side effect, sets window->move_resize_queued = FALSE */
4002       meta_window_move_resize_now (window);
4003 
4004       tmp = tmp->next;
4005     }
4006 
4007   g_slist_free (copy);
4008 
4009   destroying_windows_disallowed -= 1;
4010 
4011   return FALSE;
4012 }
4013 
4014 void
meta_window_get_position(MetaWindow * window,int * x,int * y)4015 meta_window_get_position (MetaWindow  *window,
4016                           int         *x,
4017                           int         *y)
4018 {
4019   if (window->frame)
4020     {
4021       if (x)
4022         *x = window->frame->rect.x + window->frame->child_x;
4023       if (y)
4024         *y = window->frame->rect.y + window->frame->child_y;
4025     }
4026   else
4027     {
4028       if (x)
4029         *x = window->rect.x;
4030       if (y)
4031         *y = window->rect.y;
4032     }
4033 }
4034 
4035 void
meta_window_get_client_root_coords(MetaWindow * window,MetaRectangle * rect)4036 meta_window_get_client_root_coords (MetaWindow    *window,
4037                                     MetaRectangle *rect)
4038 {
4039   meta_window_get_position (window, &rect->x, &rect->y);
4040   rect->width  = window->rect.width;
4041   rect->height = window->rect.height;
4042 }
4043 
4044 void
meta_window_get_gravity_position(MetaWindow * window,int gravity,int * root_x,int * root_y)4045 meta_window_get_gravity_position (MetaWindow  *window,
4046                                   int          gravity,
4047                                   int         *root_x,
4048                                   int         *root_y)
4049 {
4050   MetaRectangle frame_extents;
4051   int w, h;
4052   int x, y;
4053 
4054   w = window->rect.width;
4055   h = window->rect.height;
4056 
4057   if (gravity == StaticGravity)
4058     {
4059       frame_extents = window->rect;
4060       if (window->frame)
4061         {
4062           frame_extents.x = window->frame->rect.x + window->frame->child_x;
4063           frame_extents.y = window->frame->rect.y + window->frame->child_y;
4064         }
4065     }
4066   else
4067     {
4068       if (window->frame == NULL)
4069         frame_extents = window->rect;
4070       else
4071         frame_extents = window->frame->rect;
4072     }
4073 
4074   x = frame_extents.x;
4075   y = frame_extents.y;
4076 
4077   switch (gravity)
4078     {
4079     case NorthGravity:
4080     case CenterGravity:
4081     case SouthGravity:
4082       /* Find center of frame. */
4083       x += frame_extents.width / 2;
4084       /* Center client window on that point. */
4085       x -= w / 2;
4086       break;
4087 
4088     case SouthEastGravity:
4089     case EastGravity:
4090     case NorthEastGravity:
4091       /* Find right edge of frame */
4092       x += frame_extents.width;
4093       /* Align left edge of client at that point. */
4094       x -= w;
4095       break;
4096     default:
4097       break;
4098     }
4099 
4100   switch (gravity)
4101     {
4102     case WestGravity:
4103     case CenterGravity:
4104     case EastGravity:
4105       /* Find center of frame. */
4106       y += frame_extents.height / 2;
4107       /* Center client window there. */
4108       y -= h / 2;
4109       break;
4110     case SouthWestGravity:
4111     case SouthGravity:
4112     case SouthEastGravity:
4113       /* Find south edge of frame */
4114       y += frame_extents.height;
4115       /* Place bottom edge of client there */
4116       y -= h;
4117       break;
4118     default:
4119       break;
4120     }
4121 
4122   if (root_x)
4123     *root_x = x;
4124   if (root_y)
4125     *root_y = y;
4126 }
4127 
4128 void
meta_window_get_geometry(MetaWindow * window,int * x,int * y,int * width,int * height)4129 meta_window_get_geometry (MetaWindow  *window,
4130                           int         *x,
4131                           int         *y,
4132                           int         *width,
4133                           int         *height)
4134 {
4135   meta_window_get_gravity_position (window,
4136                                     window->size_hints.win_gravity,
4137                                     x, y);
4138 
4139   *width = (window->rect.width - window->size_hints.base_width) /
4140     window->size_hints.width_inc;
4141   *height = (window->rect.height - window->size_hints.base_height) /
4142     window->size_hints.height_inc;
4143 }
4144 
4145 /**
4146  * meta_window_get_input_rect:
4147  * @window: a #MetaWindow
4148  * @rect: (out): pointer to an allocated #MetaRectangle
4149  *
4150  * Gets the rectangle that bounds @window that is responsive to mouse events.
4151  * This includes decorations - the visible portion of its border - and (if
4152  * present) any invisible area that we make make responsive to mouse clicks in
4153  * order to allow convenient border dragging.
4154  */
4155 void
meta_window_get_input_rect(const MetaWindow * window,MetaRectangle * rect)4156 meta_window_get_input_rect (const MetaWindow *window,
4157                             MetaRectangle    *rect)
4158 {
4159   if (window->frame)
4160     *rect = window->frame->rect;
4161   else
4162     *rect = window->rect;
4163 }
4164 
4165 /**
4166  * meta_window_get_outer_rect:
4167  * @window: a #MetaWindow
4168  * @rect: (out): pointer to an allocated #MetaRectangle
4169  *
4170  * Gets the rectangle that bounds @window that is responsive to mouse events.
4171  * This includes only what is visible; it doesn't include any extra reactive
4172  * area we add to the edges of windows.
4173  */
4174 void
meta_window_get_outer_rect(const MetaWindow * window,MetaRectangle * rect)4175 meta_window_get_outer_rect (const MetaWindow *window,
4176                             MetaRectangle    *rect)
4177 {
4178   if (window->frame)
4179     {
4180       MetaFrameBorders borders;
4181       *rect = window->frame->rect;
4182       meta_frame_calc_borders (window->frame, &borders);
4183 
4184       rect->x += borders.invisible.left;
4185       rect->y += borders.invisible.top;
4186       rect->width -= borders.invisible.left + borders.invisible.right;
4187       rect->height -= borders.invisible.top + borders.invisible.bottom;
4188     }
4189   else
4190     {
4191       *rect = window->rect;
4192 
4193       if (window->has_custom_frame_extents)
4194         {
4195           const GtkBorder *extents = &window->custom_frame_extents;
4196           rect->x += extents->left;
4197           rect->y += extents->top;
4198           rect->width -= extents->left + extents->right;
4199           rect->height -= extents->top + extents->bottom;
4200         }
4201     }
4202 }
4203 
4204 void
meta_window_get_xor_rect(MetaWindow * window,const MetaRectangle * grab_wireframe_rect,MetaRectangle * xor_rect)4205 meta_window_get_xor_rect (MetaWindow          *window,
4206                           const MetaRectangle *grab_wireframe_rect,
4207                           MetaRectangle       *xor_rect)
4208 {
4209   if (window->frame)
4210     {
4211       xor_rect->x = grab_wireframe_rect->x - window->frame->child_x;
4212       xor_rect->y = grab_wireframe_rect->y - window->frame->child_y;
4213       xor_rect->width = grab_wireframe_rect->width + window->frame->child_x + window->frame->right_width;
4214 
4215       if (window->shaded)
4216         xor_rect->height = window->frame->child_y;
4217       else
4218         xor_rect->height = grab_wireframe_rect->height + window->frame->child_y + window->frame->bottom_height;
4219     }
4220   else
4221     *xor_rect = *grab_wireframe_rect;
4222 }
4223 
4224 /* Figure out the numbers that show up in the
4225  * resize popup when in reduced resources mode.
4226  */
4227 static void
meta_window_get_wireframe_geometry(MetaWindow * window,int * width,int * height)4228 meta_window_get_wireframe_geometry (MetaWindow    *window,
4229                                     int           *width,
4230                                     int           *height)
4231 {
4232   if (!window->display->grab_wireframe_active)
4233     return;
4234 
4235   if ((width == NULL) || (height == NULL))
4236     return;
4237 
4238   if ((window->display->grab_window->size_hints.width_inc <= 1) ||
4239       (window->display->grab_window->size_hints.height_inc <= 1))
4240     {
4241       *width = -1;
4242       *height = -1;
4243       return;
4244     }
4245 
4246   *width = window->display->grab_wireframe_rect.width -
4247       window->display->grab_window->size_hints.base_width;
4248   *width /= window->display->grab_window->size_hints.width_inc;
4249 
4250   *height = window->display->grab_wireframe_rect.height -
4251       window->display->grab_window->size_hints.base_height;
4252   *height /= window->display->grab_window->size_hints.height_inc;
4253 }
4254 
4255 /* XXX META_EFFECT_ALT_TAB, well, this and others */
4256 void
meta_window_begin_wireframe(MetaWindow * window)4257 meta_window_begin_wireframe (MetaWindow *window)
4258 {
4259 
4260   MetaRectangle new_xor;
4261   int display_width, display_height;
4262 
4263   display_width = 0;
4264   display_height = 0;
4265 
4266   meta_window_get_client_root_coords (window,
4267                                       &window->display->grab_wireframe_rect);
4268 
4269   meta_window_get_xor_rect (window, &window->display->grab_wireframe_rect,
4270                             &new_xor);
4271   meta_window_get_wireframe_geometry (window, &display_width, &display_height);
4272 
4273   meta_effects_begin_wireframe (window->screen,
4274                                 &new_xor, display_width, display_height);
4275 
4276   window->display->grab_wireframe_last_xor_rect = new_xor;
4277   window->display->grab_wireframe_last_display_width = display_width;
4278   window->display->grab_wireframe_last_display_height = display_height;
4279 }
4280 
4281 void
meta_window_update_wireframe(MetaWindow * window,int x,int y,int width,int height)4282 meta_window_update_wireframe (MetaWindow *window,
4283                               int         x,
4284                               int         y,
4285                               int         width,
4286                               int         height)
4287 {
4288 
4289   MetaRectangle new_xor;
4290   int display_width, display_height;
4291 
4292   display_width = 0;
4293   display_height = 0;
4294 
4295   window->display->grab_wireframe_rect.x = x;
4296   window->display->grab_wireframe_rect.y = y;
4297   window->display->grab_wireframe_rect.width = width;
4298   window->display->grab_wireframe_rect.height = height;
4299 
4300   meta_window_get_xor_rect (window, &window->display->grab_wireframe_rect,
4301                             &new_xor);
4302   meta_window_get_wireframe_geometry (window, &display_width, &display_height);
4303 
4304   meta_effects_update_wireframe (window->screen,
4305                                  &window->display->grab_wireframe_last_xor_rect,
4306                                  window->display->grab_wireframe_last_display_width,
4307                                  window->display->grab_wireframe_last_display_height,
4308                                  &new_xor, display_width, display_height);
4309 
4310   window->display->grab_wireframe_last_xor_rect = new_xor;
4311   window->display->grab_wireframe_last_display_width = display_width;
4312   window->display->grab_wireframe_last_display_height = display_height;
4313 }
4314 
4315 void
meta_window_end_wireframe(MetaWindow * window)4316 meta_window_end_wireframe (MetaWindow *window)
4317 {
4318   meta_effects_end_wireframe (window->display->grab_window->screen,
4319                               &window->display->grab_wireframe_last_xor_rect,
4320                               window->display->grab_wireframe_last_display_width,
4321                               window->display->grab_wireframe_last_display_height);
4322 }
4323 
4324 const char*
meta_window_get_startup_id(MetaWindow * window)4325 meta_window_get_startup_id (MetaWindow *window)
4326 {
4327   if (window->startup_id == NULL)
4328     {
4329       MetaGroup *group;
4330 
4331       group = meta_window_get_group (window);
4332 
4333       if (group != NULL)
4334         return meta_group_get_startup_id (group);
4335     }
4336 
4337   return window->startup_id;
4338 }
4339 
4340 static MetaWindow*
get_modal_transient(MetaWindow * window)4341 get_modal_transient (MetaWindow *window)
4342 {
4343   GSList *windows;
4344   GSList *tmp;
4345   MetaWindow *modal_transient;
4346 
4347   /* A window can't be the transient of itself, but this is just for
4348    * convenience in the loop below; we manually fix things up at the
4349    * end if no real modal transient was found.
4350    */
4351   modal_transient = window;
4352 
4353   windows = meta_display_list_windows (window->display);
4354   tmp = windows;
4355   while (tmp != NULL)
4356     {
4357       MetaWindow *transient = tmp->data;
4358 
4359       if (transient->xtransient_for == modal_transient->xwindow &&
4360           transient->wm_state_modal)
4361         {
4362           modal_transient = transient;
4363           tmp = windows;
4364           continue;
4365         }
4366 
4367       tmp = tmp->next;
4368     }
4369 
4370   g_slist_free (windows);
4371 
4372   if (window == modal_transient)
4373     modal_transient = NULL;
4374 
4375   return modal_transient;
4376 }
4377 
4378 /* XXX META_EFFECT_FOCUS */
4379 void
meta_window_focus(MetaWindow * window,guint32 timestamp)4380 meta_window_focus (MetaWindow  *window,
4381                    guint32      timestamp)
4382 {
4383   MetaWindow *modal_transient;
4384 
4385   meta_topic (META_DEBUG_FOCUS,
4386               "Setting input focus to window %s, input: %d take_focus: %d\n",
4387               window->desc, window->input, window->take_focus);
4388 
4389   if (window->display->grab_window &&
4390       window->display->grab_window->all_keys_grabbed)
4391     {
4392       meta_topic (META_DEBUG_FOCUS,
4393                   "Current focus window %s has global keygrab, not focusing window %s after all\n",
4394                   window->display->grab_window->desc, window->desc);
4395       return;
4396     }
4397 
4398   modal_transient = get_modal_transient (window);
4399   if (modal_transient != NULL &&
4400       !modal_transient->unmanaging)
4401     {
4402       meta_topic (META_DEBUG_FOCUS,
4403                   "%s has %s as a modal transient, so focusing it instead.\n",
4404                   window->desc, modal_transient->desc);
4405       if (!modal_transient->on_all_workspaces &&
4406           modal_transient->workspace != window->screen->active_workspace)
4407         meta_window_change_workspace (modal_transient,
4408                                       window->screen->active_workspace);
4409       window = modal_transient;
4410     }
4411 
4412   meta_window_flush_calc_showing (window);
4413 
4414   if (!window->mapped && !window->shaded)
4415     {
4416       meta_topic (META_DEBUG_FOCUS,
4417                   "Window %s is not showing, not focusing after all\n",
4418                   window->desc);
4419       return;
4420     }
4421 
4422   /* For output-only or shaded windows, focus the frame.
4423    * This seems to result in the client window getting key events
4424    * though, so I don't know if it's icccm-compliant.
4425    *
4426    * Still, we have to do this or keynav breaks for these windows.
4427    */
4428   if (window->frame &&
4429       (window->shaded ||
4430        !(window->input || window->take_focus)))
4431     {
4432       if (window->frame)
4433         {
4434           meta_topic (META_DEBUG_FOCUS,
4435                       "Focusing frame of %s\n", window->desc);
4436           meta_display_set_input_focus_window (window->display,
4437                                                window,
4438                                                TRUE,
4439                                                timestamp);
4440         }
4441     }
4442   else
4443     {
4444       if (window->input)
4445         {
4446           meta_topic (META_DEBUG_FOCUS,
4447                       "Setting input focus on %s since input = true\n",
4448                       window->desc);
4449           meta_display_set_input_focus_window (window->display,
4450                                                window,
4451                                                FALSE,
4452                                                timestamp);
4453         }
4454 
4455       if (window->take_focus)
4456         {
4457           meta_topic (META_DEBUG_FOCUS,
4458                       "Sending WM_TAKE_FOCUS to %s since take_focus = true\n",
4459                       window->desc);
4460           meta_window_send_icccm_message (window,
4461                                           window->display->atom_WM_TAKE_FOCUS,
4462                                           timestamp);
4463           window->display->expected_focus_window = window;
4464         }
4465     }
4466 
4467   if (window->wm_state_demands_attention)
4468     meta_window_unset_demands_attention(window);
4469 
4470   meta_effect_run_focus(window, NULL, NULL);
4471 }
4472 
4473 static void
meta_window_change_workspace_without_transients(MetaWindow * window,MetaWorkspace * workspace)4474 meta_window_change_workspace_without_transients (MetaWindow    *window,
4475                                                  MetaWorkspace *workspace)
4476 {
4477   meta_verbose ("Changing window %s to workspace %d\n",
4478                 window->desc, meta_workspace_index (workspace));
4479 
4480   /* unstick if stuck. meta_window_unstick would call
4481    * meta_window_change_workspace recursively if the window
4482    * is not in the active workspace.
4483    */
4484   if (window->on_all_workspaces)
4485     meta_window_unstick (window);
4486 
4487   /* See if we're already on this space. If not, make sure we are */
4488   if (window->workspace != workspace)
4489     {
4490       meta_workspace_remove_window (window->workspace, window);
4491       meta_workspace_add_window (workspace, window);
4492     }
4493 }
4494 
4495 static gboolean
change_workspace_foreach(MetaWindow * window,void * data)4496 change_workspace_foreach (MetaWindow *window,
4497                           void       *data)
4498 {
4499   meta_window_change_workspace_without_transients (window, data);
4500   return TRUE;
4501 }
4502 
4503 void
meta_window_change_workspace(MetaWindow * window,MetaWorkspace * workspace)4504 meta_window_change_workspace (MetaWindow    *window,
4505                               MetaWorkspace *workspace)
4506 {
4507   meta_window_change_workspace_without_transients (window, workspace);
4508 
4509   meta_window_foreach_transient (window, change_workspace_foreach,
4510                                  workspace);
4511   meta_window_foreach_ancestor (window, change_workspace_foreach,
4512                                 workspace);
4513 }
4514 
4515 static void
window_stick_impl(MetaWindow * window)4516 window_stick_impl (MetaWindow  *window)
4517 {
4518   GList *tmp;
4519   MetaWorkspace *workspace;
4520 
4521   meta_verbose ("Sticking window %s current on_all_workspaces = %d\n",
4522                 window->desc, window->on_all_workspaces);
4523 
4524   if (window->on_all_workspaces)
4525     return;
4526 
4527   /* We don't change window->workspaces, because we revert
4528    * to that original workspace list if on_all_workspaces is
4529    * toggled back off.
4530    */
4531   window->on_all_workspaces = TRUE;
4532 
4533   /* We do, however, change the MRU lists of all the workspaces
4534    */
4535   tmp = window->screen->workspaces;
4536   while (tmp)
4537     {
4538       workspace = (MetaWorkspace *) tmp->data;
4539       if (!g_list_find (workspace->mru_list, window))
4540         workspace->mru_list = g_list_prepend (workspace->mru_list, window);
4541 
4542       tmp = tmp->next;
4543     }
4544 
4545   meta_window_set_current_workspace_hint (window);
4546 
4547   meta_window_queue(window, META_QUEUE_CALC_SHOWING);
4548 }
4549 
4550 static void
window_unstick_impl(MetaWindow * window)4551 window_unstick_impl (MetaWindow  *window)
4552 {
4553   GList *tmp;
4554   MetaWorkspace *workspace;
4555 
4556   if (!window->on_all_workspaces)
4557     return;
4558 
4559   /* Revert to window->workspaces */
4560 
4561   window->on_all_workspaces = FALSE;
4562 
4563   /* Remove window from MRU lists that it doesn't belong in */
4564   tmp = window->screen->workspaces;
4565   while (tmp)
4566     {
4567       workspace = (MetaWorkspace *) tmp->data;
4568       if (window->workspace != workspace)
4569         workspace->mru_list = g_list_remove (workspace->mru_list, window);
4570       tmp = tmp->next;
4571     }
4572 
4573   /* We change ourselves to the active workspace, since otherwise you'd get
4574    * a weird window-vaporization effect. Once we have UI for being
4575    * on more than one workspace this should probably be add_workspace
4576    * not change_workspace.
4577    */
4578   if (window->screen->active_workspace != window->workspace)
4579     meta_window_change_workspace (window, window->screen->active_workspace);
4580 
4581   meta_window_set_current_workspace_hint (window);
4582 
4583   meta_window_queue(window, META_QUEUE_CALC_SHOWING);
4584 }
4585 
4586 static gboolean
stick_foreach_func(MetaWindow * window,void * data)4587 stick_foreach_func (MetaWindow *window,
4588                     void       *data)
4589 {
4590   gboolean stick;
4591 
4592   stick = *(gboolean*)data;
4593   if (stick)
4594     window_stick_impl (window);
4595   else
4596     window_unstick_impl (window);
4597   return TRUE;
4598 }
4599 
4600 void
meta_window_stick(MetaWindow * window)4601 meta_window_stick (MetaWindow  *window)
4602 {
4603   gboolean stick = TRUE;
4604   window_stick_impl (window);
4605   meta_window_foreach_transient (window,
4606                                  stick_foreach_func,
4607                                  &stick);
4608 }
4609 
4610 void
meta_window_unstick(MetaWindow * window)4611 meta_window_unstick (MetaWindow  *window)
4612 {
4613   gboolean stick = FALSE;
4614   window_unstick_impl (window);
4615   meta_window_foreach_transient (window,
4616                                  stick_foreach_func,
4617                                  &stick);
4618 }
4619 
4620 unsigned long
meta_window_get_net_wm_desktop(MetaWindow * window)4621 meta_window_get_net_wm_desktop (MetaWindow *window)
4622 {
4623   if (window->on_all_workspaces)
4624     return 0xFFFFFFFF;
4625   else
4626     return meta_workspace_index (window->workspace);
4627 }
4628 
4629 static void
update_net_frame_extents(MetaWindow * window)4630 update_net_frame_extents (MetaWindow *window)
4631 {
4632   unsigned long data[4] = { 0, 0, 0, 0 };
4633 
4634   if (window->frame)
4635     {
4636       MetaFrameBorders borders;
4637 
4638       meta_frame_calc_borders (window->frame, &borders);
4639       /* Left */
4640       data[0] = borders.visible.left;
4641       /* Right */
4642       data[1] = borders.visible.right;
4643       /* Top */
4644       data[2] = borders.visible.top;
4645       /* Bottom */
4646       data[3] = borders.visible.bottom;
4647     }
4648 
4649   meta_topic (META_DEBUG_GEOMETRY,
4650               "Setting _NET_FRAME_EXTENTS on managed window 0x%lx "
4651               "to left = %lu, right = %lu, top = %lu, bottom = %lu\n",
4652               window->xwindow, data[0], data[1], data[2], data[3]);
4653 
4654   meta_error_trap_push (window->display);
4655   XChangeProperty (window->display->xdisplay, window->xwindow,
4656                    window->display->atom__NET_FRAME_EXTENTS,
4657                    XA_CARDINAL,
4658                    32, PropModeReplace, (guchar*) data, 4);
4659   meta_error_trap_pop (window->display, FALSE);
4660 }
4661 
4662 void
meta_window_set_current_workspace_hint(MetaWindow * window)4663 meta_window_set_current_workspace_hint (MetaWindow *window)
4664 {
4665   /* FIXME if on more than one workspace, we claim to be "sticky",
4666    * the WM spec doesn't say what to do here.
4667    */
4668   unsigned long data[1];
4669 
4670   if (window->workspace == NULL)
4671     {
4672       /* this happens when unmanaging windows */
4673       return;
4674     }
4675 
4676   data[0] = meta_window_get_net_wm_desktop (window);
4677 
4678   meta_verbose ("Setting _NET_WM_DESKTOP of %s to %lu\n",
4679                 window->desc, data[0]);
4680 
4681   meta_error_trap_push (window->display);
4682   XChangeProperty (window->display->xdisplay, window->xwindow,
4683                    window->display->atom__NET_WM_DESKTOP,
4684                    XA_CARDINAL,
4685                    32, PropModeReplace, (guchar*) data, 1);
4686   meta_error_trap_pop (window->display, FALSE);
4687 }
4688 
4689 static gboolean
find_root_ancestor(MetaWindow * window,void * data)4690 find_root_ancestor (MetaWindow *window,
4691                     void       *data)
4692 {
4693   MetaWindow **ancestor = data;
4694 
4695   /* Overwrite the previously "most-root" ancestor with the new one found */
4696   *ancestor = window;
4697 
4698   /* We want this to continue until meta_window_foreach_ancestor quits because
4699    * there are no more valid ancestors.
4700    */
4701   return TRUE;
4702 }
4703 
4704 MetaWindow *
meta_window_find_root_ancestor(MetaWindow * window)4705 meta_window_find_root_ancestor (MetaWindow *window)
4706 {
4707   MetaWindow *ancestor;
4708   ancestor = window;
4709   meta_window_foreach_ancestor (window, find_root_ancestor, &ancestor);
4710   return ancestor;
4711 }
4712 
4713 void
meta_window_raise(MetaWindow * window)4714 meta_window_raise (MetaWindow  *window)
4715 {
4716   MetaWindow *ancestor;
4717   ancestor = meta_window_find_root_ancestor (window);
4718 
4719   meta_topic (META_DEBUG_WINDOW_OPS,
4720               "Raising window %s, ancestor of %s\n",
4721               ancestor->desc, window->desc);
4722 
4723   /* Raise the ancestor of the window (if the window has no ancestor,
4724    * then ancestor will be set to the window itself); do this because
4725    * it's weird to see windows from other apps stacked between a child
4726    * and parent window of the currently active app.  The stacking
4727    * constraints in stack.c then magically take care of raising all
4728    * the child windows appropriately.
4729    */
4730   if (window->screen->stack == ancestor->screen->stack)
4731     meta_stack_raise (window->screen->stack, ancestor);
4732   else
4733     {
4734       meta_warning (
4735                     "Either stacks aren't per screen or some window has a weird "
4736                     "transient_for hint; window->screen->stack != "
4737                     "ancestor->screen->stack.  window = %s, ancestor = %s.\n",
4738                     window->desc, ancestor->desc);
4739       /* We could raise the window here, but don't want to do that twice and
4740        * so we let the case below handle that.
4741        */
4742     }
4743 
4744   /* Okay, so stacking constraints misses one case: If a window has
4745    * two children and we want to raise one of those children, then
4746    * raising the ancestor isn't enough; we need to also raise the
4747    * correct child.  See bug 307875.
4748    */
4749   if (window != ancestor)
4750     meta_stack_raise (window->screen->stack, window);
4751 }
4752 
4753 void
meta_window_lower(MetaWindow * window)4754 meta_window_lower (MetaWindow  *window)
4755 {
4756   meta_topic (META_DEBUG_WINDOW_OPS,
4757               "Lowering window %s\n", window->desc);
4758 
4759   meta_stack_lower (window->screen->stack, window);
4760 }
4761 
4762 void
meta_window_send_icccm_message(MetaWindow * window,Atom atom,guint32 timestamp)4763 meta_window_send_icccm_message (MetaWindow *window,
4764                                 Atom        atom,
4765                                 guint32     timestamp)
4766 {
4767   /* This comment and code are from twm, copyright
4768    * Open Group, Evans & Sutherland, etc.
4769    */
4770 
4771   /*
4772    * ICCCM Client Messages - Section 4.2.8 of the ICCCM dictates that all
4773    * client messages will have the following form:
4774    *
4775    *     event type	ClientMessage
4776    *     message type	_XA_WM_PROTOCOLS
4777    *     window		tmp->w
4778    *     format		32
4779    *     data[0]		message atom
4780    *     data[1]		time stamp
4781    */
4782 
4783     XClientMessageEvent ev;
4784 
4785     ev.type = ClientMessage;
4786     ev.window = window->xwindow;
4787     ev.message_type = window->display->atom_WM_PROTOCOLS;
4788     ev.format = 32;
4789     ev.data.l[0] = atom;
4790     ev.data.l[1] = timestamp;
4791 
4792     meta_error_trap_push (window->display);
4793     XSendEvent (window->display->xdisplay,
4794                 window->xwindow, False, 0, (XEvent*) &ev);
4795     meta_error_trap_pop (window->display, FALSE);
4796 }
4797 
4798 void
meta_window_move_resize_request(MetaWindow * window,guint value_mask,int gravity,int new_x,int new_y,int new_width,int new_height)4799 meta_window_move_resize_request (MetaWindow *window,
4800                                  guint       value_mask,
4801                                  int         gravity,
4802                                  int         new_x,
4803                                  int         new_y,
4804                                  int         new_width,
4805                                  int         new_height)
4806 {
4807   int x, y, width, height;
4808   gboolean allow_position_change;
4809   gboolean in_grab_op;
4810   MetaMoveResizeFlags flags;
4811 
4812   /* We ignore configure requests while the user is moving/resizing
4813    * the window, since these represent the app sucking and fighting
4814    * the user, most likely due to a bug in the app (e.g. pfaedit
4815    * seemed to do this)
4816    *
4817    * Still have to do the ConfigureNotify and all, but pretend the
4818    * app asked for the current size/position instead of the new one.
4819    */
4820   in_grab_op = FALSE;
4821   if (window->display->grab_op != META_GRAB_OP_NONE &&
4822       window == window->display->grab_window)
4823     {
4824       switch (window->display->grab_op)
4825         {
4826         case META_GRAB_OP_MOVING:
4827         case META_GRAB_OP_RESIZING_SE:
4828         case META_GRAB_OP_RESIZING_S:
4829         case META_GRAB_OP_RESIZING_SW:
4830         case META_GRAB_OP_RESIZING_N:
4831         case META_GRAB_OP_RESIZING_NE:
4832         case META_GRAB_OP_RESIZING_NW:
4833         case META_GRAB_OP_RESIZING_W:
4834         case META_GRAB_OP_RESIZING_E:
4835           in_grab_op = TRUE;
4836           break;
4837         default:
4838           break;
4839         }
4840     }
4841 
4842   /* it's essential to use only the explicitly-set fields,
4843    * and otherwise use our current up-to-date position.
4844    *
4845    * Otherwise you get spurious position changes when the app changes
4846    * size, for example, if window->rect is not in sync with the
4847    * server-side position in effect when the configure request was
4848    * generated.
4849    */
4850   meta_window_get_gravity_position (window,
4851                                     gravity,
4852                                     &x, &y);
4853 
4854   allow_position_change = FALSE;
4855 
4856   if (meta_prefs_get_disable_workarounds ())
4857     {
4858       if (window->type == META_WINDOW_DIALOG ||
4859           window->type == META_WINDOW_MODAL_DIALOG ||
4860           window->type == META_WINDOW_SPLASHSCREEN)
4861         ; /* No position change for these */
4862       else if ((window->size_hints.flags & PPosition) ||
4863                /* USPosition is just stale if window is placed;
4864                 * no --geometry involved here.
4865                 */
4866                ((window->size_hints.flags & USPosition) &&
4867                 !window->placed))
4868         allow_position_change = TRUE;
4869     }
4870   else
4871     {
4872       allow_position_change = TRUE;
4873     }
4874 
4875   if (in_grab_op)
4876     allow_position_change = FALSE;
4877 
4878   if (allow_position_change)
4879     {
4880       if (value_mask & CWX)
4881         x = new_x;
4882       if (value_mask & CWY)
4883         y = new_y;
4884       if (value_mask & (CWX | CWY))
4885         {
4886           /* Once manually positioned, windows shouldn't be placed
4887            * by the window manager.
4888            */
4889           window->placed = TRUE;
4890         }
4891     }
4892   else
4893     {
4894       meta_topic (META_DEBUG_GEOMETRY,
4895 		  "Not allowing position change for window %s PPosition 0x%lx USPosition 0x%lx type %u\n",
4896 		  window->desc, window->size_hints.flags & PPosition,
4897 		  window->size_hints.flags & USPosition,
4898 		  window->type);
4899     }
4900 
4901   width = window->rect.width;
4902   height = window->rect.height;
4903   if (!in_grab_op)
4904     {
4905       if (value_mask & CWWidth)
4906         width = new_width;
4907 
4908       if (value_mask & CWHeight)
4909         height = new_height;
4910     }
4911 
4912   /* ICCCM 4.1.5 */
4913 
4914   /* We're ignoring the value_mask here, since sizes
4915    * not in the mask will be the current window geometry.
4916    */
4917   window->size_hints.x = x;
4918   window->size_hints.y = y;
4919   window->size_hints.width = width;
4920   window->size_hints.height = height;
4921 
4922   /* NOTE: We consider ConfigureRequests to be "user" actions in one
4923    * way, but not in another.  Explanation of the two cases are in the
4924    * next two big comments.
4925    */
4926 
4927   /* The constraints code allows user actions to move windows
4928    * offscreen, etc., and configure request actions would often send
4929    * windows offscreen when users don't want it if not constrained
4930    * (e.g. hitting a dropdown triangle in a fileselector to show more
4931    * options, which makes the window bigger).  Thus we do not set
4932    * META_IS_USER_ACTION in flags to the
4933    * meta_window_move_resize_internal() call.
4934    */
4935   flags = META_IS_CONFIGURE_REQUEST;
4936   if (value_mask & (CWX | CWY))
4937     flags |= META_IS_MOVE_ACTION;
4938   if (value_mask & (CWWidth | CWHeight))
4939     flags |= META_IS_RESIZE_ACTION;
4940 
4941   if (flags & (META_IS_MOVE_ACTION | META_IS_RESIZE_ACTION))
4942    {
4943       const MetaXineramaScreenInfo *xinerama_info;
4944       MetaRectangle rect;
4945 
4946       rect.x = x;
4947       rect.y = y;
4948       rect.width = width;
4949       rect.height = height;
4950 
4951       xinerama_info = meta_screen_get_xinerama_for_rect (window->screen, &rect);
4952 
4953       /* Workaround braindead legacy apps that don't know how to
4954        * fullscreen themselves properly - don't get fooled by
4955        * windows which are client decorated; that's not the same
4956        * as fullscreen, even if there are no struts making the
4957        * workarea smaller than the monitor.
4958        */
4959       if (meta_prefs_get_force_fullscreen() &&
4960           (window->decorated || !meta_window_is_client_decorated (window)) &&
4961           meta_rectangle_equal (&rect, &xinerama_info->rect) &&
4962           window->has_fullscreen_func &&
4963           !window->fullscreen)
4964         {
4965           g_warning ("Treating resize request of legacy application %s as a "
4966                      "fullscreen request", window->desc);
4967           meta_window_make_fullscreen_internal (window);
4968         }
4969       meta_window_move_resize_internal (window,
4970                                       flags,
4971                                       gravity,
4972                                       x,
4973                                       y,
4974                                       width,
4975                                       height);
4976 
4977    }
4978 
4979   /* window->user_rect exists to allow "snapping-back" the window if a
4980    * new strut is set (causing the window to move) and then the strut
4981    * is later removed without the user moving the window in the
4982    * interim.  We'd like to "snap-back" to the position specified by
4983    * ConfigureRequest events (at least the constrained version of the
4984    * ConfigureRequest, since that is guaranteed to be onscreen) so we
4985    * set user_rect here.
4986    *
4987    * See also bug 426519.
4988    */
4989   save_user_window_placement (window);
4990 }
4991 
4992 gboolean
meta_window_configure_request(MetaWindow * window,XEvent * event)4993 meta_window_configure_request (MetaWindow *window,
4994                                XEvent     *event)
4995 {
4996   /* Note that x, y is the corner of the window border,
4997    * and width, height is the size of the window inside
4998    * its border, but that we always deny border requests
4999    * and give windows a border of 0. But we save the
5000    * requested border here.
5001    */
5002   if (event->xconfigurerequest.value_mask & CWBorderWidth)
5003     window->border_width = event->xconfigurerequest.border_width;
5004 
5005   meta_window_move_resize_request(window,
5006                                   event->xconfigurerequest.value_mask,
5007                                   window->size_hints.win_gravity,
5008                                   event->xconfigurerequest.x,
5009                                   event->xconfigurerequest.y,
5010                                   event->xconfigurerequest.width,
5011                                   event->xconfigurerequest.height);
5012 
5013   /* Handle stacking. We only handle raises/lowers, mostly because
5014    * stack.c really can't deal with anything else.  I guess we'll fix
5015    * that if a client turns up that really requires it. Only a very
5016    * few clients even require the raise/lower (and in fact all client
5017    * attempts to deal with stacking order are essentially broken,
5018    * since they have no idea what other clients are involved or how
5019    * the stack looks).
5020    *
5021    * I'm pretty sure no interesting client uses TopIf, BottomIf, or
5022    * Opposite anyway, so the only possible missing thing is
5023    * Above/Below with a sibling set. For now we just pretend there's
5024    * never a sibling set and always do the full raise/lower instead of
5025    * the raise-just-above/below-sibling.
5026    */
5027   if (event->xconfigurerequest.value_mask & CWStackMode)
5028     {
5029       MetaWindow *active_window;
5030       active_window = window->display->expected_focus_window;
5031       if (meta_prefs_get_disable_workarounds () ||
5032           !meta_prefs_get_raise_on_click ())
5033         {
5034           meta_topic (META_DEBUG_STACK,
5035                       "%s sent an xconfigure stacking request; this is "
5036                       "broken behavior and the request is being ignored.\n",
5037                       window->desc);
5038         }
5039       else if (active_window &&
5040                !meta_window_same_application (window, active_window) &&
5041                !meta_window_same_client (window, active_window) &&
5042                XSERVER_TIME_IS_BEFORE (window->net_wm_user_time,
5043                                        active_window->net_wm_user_time))
5044         {
5045           meta_topic (META_DEBUG_STACK,
5046                       "Ignoring xconfigure stacking request from %s (with "
5047                       "user_time %u); currently active application is %s (with "
5048                       "user_time %u).\n",
5049                       window->desc,
5050                       window->net_wm_user_time,
5051                       active_window->desc,
5052                       active_window->net_wm_user_time);
5053           if (event->xconfigurerequest.detail == Above)
5054             meta_window_set_demands_attention(window);
5055         }
5056       else
5057         {
5058           switch (event->xconfigurerequest.detail)
5059             {
5060             case Above:
5061               meta_window_raise (window);
5062               break;
5063             case Below:
5064               meta_window_lower (window);
5065               break;
5066             case TopIf:
5067             case BottomIf:
5068             case Opposite:
5069               break;
5070             }
5071         }
5072     }
5073 
5074   return TRUE;
5075 }
5076 
5077 gboolean
meta_window_property_notify(MetaWindow * window,XEvent * event)5078 meta_window_property_notify (MetaWindow *window,
5079                              XEvent     *event)
5080 {
5081   return process_property_notify (window, &event->xproperty);
5082 }
5083 
5084 #define _NET_WM_MOVERESIZE_SIZE_TOPLEFT      0
5085 #define _NET_WM_MOVERESIZE_SIZE_TOP          1
5086 #define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT     2
5087 #define _NET_WM_MOVERESIZE_SIZE_RIGHT        3
5088 #define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT  4
5089 #define _NET_WM_MOVERESIZE_SIZE_BOTTOM       5
5090 #define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT   6
5091 #define _NET_WM_MOVERESIZE_SIZE_LEFT         7
5092 #define _NET_WM_MOVERESIZE_MOVE              8
5093 #define _NET_WM_MOVERESIZE_SIZE_KEYBOARD     9
5094 #define _NET_WM_MOVERESIZE_MOVE_KEYBOARD    10
5095 #define _NET_WM_MOVERESIZE_CANCEL           11
5096 
5097 gboolean
meta_window_client_message(MetaWindow * window,XEvent * event)5098 meta_window_client_message (MetaWindow *window,
5099                             XEvent     *event)
5100 {
5101   MetaDisplay *display;
5102 
5103   display = window->display;
5104 
5105   if (event->xclient.message_type ==
5106       display->atom__NET_CLOSE_WINDOW)
5107     {
5108       guint32 timestamp;
5109 
5110       if (event->xclient.data.l[0] != 0)
5111 	timestamp = event->xclient.data.l[0];
5112       else
5113         {
5114           meta_warning ("Receiving a NET_CLOSE_WINDOW message for %s without "
5115                         "a timestamp!  This means some buggy (outdated) "
5116                         "application is on the loose!\n",
5117                         window->desc);
5118           timestamp = meta_display_get_current_time (window->display);
5119         }
5120 
5121       meta_window_delete (window, timestamp);
5122 
5123       return TRUE;
5124     }
5125   else if (event->xclient.message_type ==
5126            display->atom__NET_WM_DESKTOP)
5127     {
5128       int space;
5129       MetaWorkspace *workspace;
5130 
5131       space = event->xclient.data.l[0];
5132 
5133       meta_verbose ("Request to move %s to workspace %d\n",
5134                     window->desc, space);
5135 
5136       workspace =
5137         meta_screen_get_workspace_by_index (window->screen,
5138                                             space);
5139 
5140       if (workspace)
5141         {
5142           if (window->on_all_workspaces)
5143             meta_window_unstick (window);
5144           meta_window_change_workspace (window, workspace);
5145         }
5146       else if (space == (int) 0xFFFFFFFF)
5147         {
5148           meta_window_stick (window);
5149         }
5150       else
5151         {
5152           meta_verbose ("No such workspace %d for screen\n", space);
5153         }
5154 
5155       meta_verbose ("Window %s now on_all_workspaces = %d\n",
5156                     window->desc, window->on_all_workspaces);
5157 
5158       return TRUE;
5159     }
5160   else if (event->xclient.message_type ==
5161            display->atom__NET_WM_STATE)
5162     {
5163       gulong action;
5164       Atom first;
5165       Atom second;
5166 
5167       action = event->xclient.data.l[0];
5168       first = event->xclient.data.l[1];
5169       second = event->xclient.data.l[2];
5170 
5171       if (meta_is_verbose ())
5172         {
5173           char *str1;
5174           char *str2;
5175 
5176           meta_error_trap_push (display);
5177           str1 = XGetAtomName (display->xdisplay, first);
5178           if (meta_error_trap_pop_with_return (display, TRUE) != Success)
5179             str1 = NULL;
5180 
5181           meta_error_trap_push (display);
5182           str2 = XGetAtomName (display->xdisplay, second);
5183           if (meta_error_trap_pop_with_return (display, TRUE) != Success)
5184             str2 = NULL;
5185 
5186           meta_verbose ("Request to change _NET_WM_STATE action %lu atom1: %s atom2: %s\n",
5187                         action,
5188                         str1 ? str1 : "(unknown)",
5189                         str2 ? str2 : "(unknown)");
5190 
5191           meta_XFree (str1);
5192           meta_XFree (str2);
5193         }
5194 
5195       if (first == display->atom__NET_WM_STATE_SHADED ||
5196           second == display->atom__NET_WM_STATE_SHADED)
5197         {
5198           gboolean shade;
5199           guint32 timestamp;
5200 
5201           /* Stupid protocol has no timestamp; of course, shading
5202            * sucks anyway so who really cares that we're forced to do
5203            * a roundtrip here?
5204            */
5205           timestamp = meta_display_get_current_time_roundtrip (window->display);
5206 
5207           shade = (action == _NET_WM_STATE_ADD ||
5208                    (action == _NET_WM_STATE_TOGGLE && !window->shaded));
5209           if (shade && window->has_shade_func)
5210             meta_window_shade (window, timestamp);
5211           else
5212             meta_window_unshade (window, timestamp);
5213         }
5214 
5215       if (first == display->atom__NET_WM_STATE_FULLSCREEN ||
5216           second == display->atom__NET_WM_STATE_FULLSCREEN)
5217         {
5218           gboolean make_fullscreen;
5219 
5220           make_fullscreen = (action == _NET_WM_STATE_ADD ||
5221                              (action == _NET_WM_STATE_TOGGLE && !window->fullscreen));
5222           if (make_fullscreen && window->has_fullscreen_func)
5223             meta_window_make_fullscreen (window);
5224           else
5225             meta_window_unmake_fullscreen (window);
5226         }
5227 
5228       if (first == display->atom__NET_WM_STATE_MAXIMIZED_HORZ ||
5229           second == display->atom__NET_WM_STATE_MAXIMIZED_HORZ)
5230         {
5231           gboolean max;
5232 
5233           max = (action == _NET_WM_STATE_ADD ||
5234                  (action == _NET_WM_STATE_TOGGLE &&
5235                   !window->maximized_horizontally));
5236           if (max && window->has_maximize_func)
5237             {
5238               if (meta_prefs_get_raise_on_click ())
5239                 meta_window_raise (window);
5240               meta_window_maximize (window, META_MAXIMIZE_HORIZONTAL);
5241             }
5242           else
5243             {
5244               if (meta_prefs_get_raise_on_click ())
5245                 meta_window_raise (window);
5246               meta_window_unmaximize (window, META_MAXIMIZE_HORIZONTAL);
5247             }
5248         }
5249 
5250       if (first == display->atom__NET_WM_STATE_MAXIMIZED_VERT ||
5251           second == display->atom__NET_WM_STATE_MAXIMIZED_VERT)
5252         {
5253           gboolean max;
5254 
5255           max = (action == _NET_WM_STATE_ADD ||
5256                  (action == _NET_WM_STATE_TOGGLE &&
5257                   !window->maximized_vertically));
5258           if (max && window->has_maximize_func)
5259             {
5260               if (meta_prefs_get_raise_on_click ())
5261                 meta_window_raise (window);
5262               meta_window_maximize (window, META_MAXIMIZE_VERTICAL);
5263             }
5264           else
5265             {
5266               if (meta_prefs_get_raise_on_click ())
5267                 meta_window_raise (window);
5268               meta_window_unmaximize (window, META_MAXIMIZE_VERTICAL);
5269             }
5270         }
5271 
5272       if (first == display->atom__NET_WM_STATE_MODAL ||
5273           second == display->atom__NET_WM_STATE_MODAL)
5274         {
5275           window->wm_state_modal =
5276             (action == _NET_WM_STATE_ADD) ||
5277             (action == _NET_WM_STATE_TOGGLE && !window->wm_state_modal);
5278 
5279           recalc_window_type (window);
5280           meta_window_queue(window, META_QUEUE_MOVE_RESIZE);
5281         }
5282 
5283       if (first == display->atom__NET_WM_STATE_SKIP_PAGER ||
5284           second == display->atom__NET_WM_STATE_SKIP_PAGER)
5285         {
5286           window->wm_state_skip_pager =
5287             (action == _NET_WM_STATE_ADD) ||
5288             (action == _NET_WM_STATE_TOGGLE && !window->skip_pager);
5289 
5290           recalc_window_features (window);
5291           set_net_wm_state (window);
5292         }
5293 
5294       if (first == display->atom__NET_WM_STATE_SKIP_TASKBAR ||
5295           second == display->atom__NET_WM_STATE_SKIP_TASKBAR)
5296         {
5297           window->wm_state_skip_taskbar =
5298             (action == _NET_WM_STATE_ADD) ||
5299             (action == _NET_WM_STATE_TOGGLE && !window->skip_taskbar);
5300 
5301           recalc_window_features (window);
5302           set_net_wm_state (window);
5303         }
5304 
5305       if (first == display->atom__NET_WM_STATE_ABOVE ||
5306           second == display->atom__NET_WM_STATE_ABOVE)
5307         {
5308           window->wm_state_above =
5309             (action == _NET_WM_STATE_ADD) ||
5310             (action == _NET_WM_STATE_TOGGLE && !window->wm_state_above);
5311 
5312           meta_window_update_layer (window);
5313           set_net_wm_state (window);
5314         }
5315 
5316       if (first == display->atom__NET_WM_STATE_BELOW ||
5317           second == display->atom__NET_WM_STATE_BELOW)
5318         {
5319           window->wm_state_below =
5320             (action == _NET_WM_STATE_ADD) ||
5321             (action == _NET_WM_STATE_TOGGLE && !window->wm_state_below);
5322 
5323           meta_window_update_layer (window);
5324           set_net_wm_state (window);
5325         }
5326 
5327       if (first == display->atom__NET_WM_STATE_DEMANDS_ATTENTION ||
5328           second == display->atom__NET_WM_STATE_DEMANDS_ATTENTION)
5329         {
5330           if ((action == _NET_WM_STATE_ADD) ||
5331               (action == _NET_WM_STATE_TOGGLE && !window->wm_state_demands_attention))
5332             meta_window_set_demands_attention (window);
5333           else
5334             meta_window_unset_demands_attention (window);
5335         }
5336 
5337        if (first == display->atom__NET_WM_STATE_STICKY ||
5338           second == display->atom__NET_WM_STATE_STICKY)
5339         {
5340           if ((action == _NET_WM_STATE_ADD) ||
5341               (action == _NET_WM_STATE_TOGGLE && !window->on_all_workspaces))
5342             meta_window_stick (window);
5343           else
5344             meta_window_unstick (window);
5345         }
5346 
5347       return TRUE;
5348     }
5349   else if (event->xclient.message_type ==
5350            display->atom_WM_CHANGE_STATE)
5351     {
5352       meta_verbose ("WM_CHANGE_STATE client message, state: %ld\n",
5353                     event->xclient.data.l[0]);
5354       if (event->xclient.data.l[0] == IconicState)
5355         meta_window_minimize (window);
5356 
5357       return TRUE;
5358     }
5359   else if (event->xclient.message_type ==
5360            display->atom__NET_WM_MOVERESIZE)
5361     {
5362       int x_root;
5363       int y_root;
5364       int action;
5365       MetaGrabOp op;
5366       int button;
5367       guint32 timestamp;
5368 
5369       /* _NET_WM_MOVERESIZE messages are almost certainly going to come from
5370        * clients when users click on the fake "frame" that the client has,
5371        * thus we should also treat such messages as though it were a
5372        * "frame action".
5373        */
5374       gboolean const frame_action = TRUE;
5375 
5376       x_root = event->xclient.data.l[0];
5377       y_root = event->xclient.data.l[1];
5378       action = event->xclient.data.l[2];
5379       button = event->xclient.data.l[3];
5380 
5381       /* FIXME: What a braindead protocol; no timestamp?!? */
5382       timestamp = meta_display_get_current_time_roundtrip (display);
5383       meta_topic (META_DEBUG_WINDOW_OPS,
5384                   "Received _NET_WM_MOVERESIZE message on %s, %d,%d action = %d, button %d\n",
5385                   window->desc,
5386                   x_root, y_root, action, button);
5387 
5388       op = META_GRAB_OP_NONE;
5389       switch (action)
5390         {
5391         case _NET_WM_MOVERESIZE_SIZE_TOPLEFT:
5392           op = META_GRAB_OP_RESIZING_NW;
5393           break;
5394         case _NET_WM_MOVERESIZE_SIZE_TOP:
5395           op = META_GRAB_OP_RESIZING_N;
5396           break;
5397         case _NET_WM_MOVERESIZE_SIZE_TOPRIGHT:
5398           op = META_GRAB_OP_RESIZING_NE;
5399           break;
5400         case _NET_WM_MOVERESIZE_SIZE_RIGHT:
5401           op = META_GRAB_OP_RESIZING_E;
5402           break;
5403         case _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT:
5404           op = META_GRAB_OP_RESIZING_SE;
5405           break;
5406         case _NET_WM_MOVERESIZE_SIZE_BOTTOM:
5407           op = META_GRAB_OP_RESIZING_S;
5408           break;
5409         case _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT:
5410           op = META_GRAB_OP_RESIZING_SW;
5411           break;
5412         case _NET_WM_MOVERESIZE_SIZE_LEFT:
5413           op = META_GRAB_OP_RESIZING_W;
5414           break;
5415         case _NET_WM_MOVERESIZE_MOVE:
5416           op = META_GRAB_OP_MOVING;
5417           break;
5418         case _NET_WM_MOVERESIZE_SIZE_KEYBOARD:
5419           op = META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN;
5420           break;
5421         case _NET_WM_MOVERESIZE_MOVE_KEYBOARD:
5422           op = META_GRAB_OP_KEYBOARD_MOVING;
5423           break;
5424         case _NET_WM_MOVERESIZE_CANCEL:
5425           /* handled below */
5426           break;
5427         default:
5428           break;
5429         }
5430 
5431       if (action == _NET_WM_MOVERESIZE_CANCEL)
5432         {
5433           meta_display_end_grab_op (window->display, timestamp);
5434         }
5435       else if (op != META_GRAB_OP_NONE &&
5436           ((window->has_move_func && op == META_GRAB_OP_KEYBOARD_MOVING) ||
5437            (window->has_resize_func && op == META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN)))
5438         {
5439           meta_window_begin_grab_op (window, op, frame_action, timestamp);
5440         }
5441       else if (op != META_GRAB_OP_NONE &&
5442                ((window->has_move_func && op == META_GRAB_OP_MOVING) ||
5443                (window->has_resize_func &&
5444                 (op != META_GRAB_OP_MOVING &&
5445                  op != META_GRAB_OP_KEYBOARD_MOVING))))
5446         {
5447           /*
5448            * the button SHOULD already be included in the message
5449            */
5450           if (button == 0)
5451             {
5452               int x, y, query_root_x, query_root_y;
5453               Window root, child;
5454               guint mask;
5455 
5456               /* The race conditions in this _NET_WM_MOVERESIZE thing
5457                * are mind-boggling
5458                */
5459               mask = 0;
5460               meta_error_trap_push (window->display);
5461               XQueryPointer (window->display->xdisplay,
5462                              window->xwindow,
5463                              &root, &child,
5464                              &query_root_x, &query_root_y,
5465                              &x, &y,
5466                              &mask);
5467               meta_error_trap_pop (window->display, TRUE);
5468 
5469               if (mask & Button1Mask)
5470                 button = 1;
5471               else if (mask & Button2Mask)
5472                 button = 2;
5473               else if (mask & Button3Mask)
5474                 button = 3;
5475               else
5476                 button = 0;
5477             }
5478 
5479           if (button != 0)
5480             {
5481               meta_topic (META_DEBUG_WINDOW_OPS,
5482                           "Beginning move/resize with button = %d\n", button);
5483               meta_display_begin_grab_op (window->display,
5484                                           window->screen,
5485                                           window,
5486                                           op,
5487                                           FALSE,
5488                                           frame_action,
5489                                           button, 0,
5490                                           timestamp,
5491                                           x_root,
5492                                           y_root);
5493             }
5494         }
5495 
5496       return TRUE;
5497     }
5498   else if (event->xclient.message_type ==
5499            display->atom__NET_MOVERESIZE_WINDOW)
5500     {
5501       int gravity;
5502       guint value_mask;
5503 
5504       gravity = (event->xclient.data.l[0] & 0xff);
5505       value_mask = (event->xclient.data.l[0] & 0xf00) >> 8;
5506 
5507       if (gravity == 0)
5508         gravity = window->size_hints.win_gravity;
5509 
5510       meta_window_move_resize_request(window,
5511                                       value_mask,
5512                                       gravity,
5513                                       event->xclient.data.l[1],  /* x */
5514                                       event->xclient.data.l[2],  /* y */
5515                                       event->xclient.data.l[3],  /* width */
5516                                       event->xclient.data.l[4]); /* height */
5517     }
5518   else if (event->xclient.message_type ==
5519            display->atom__NET_ACTIVE_WINDOW)
5520     {
5521       MetaClientType source_indication;
5522       guint32        timestamp;
5523 
5524       meta_verbose ("_NET_ACTIVE_WINDOW request for window '%s', activating\n",
5525                     window->desc);
5526 
5527       source_indication = event->xclient.data.l[0];
5528       timestamp = event->xclient.data.l[1];
5529 
5530       if (source_indication > META_CLIENT_TYPE_MAX_RECOGNIZED)
5531         source_indication = META_CLIENT_TYPE_UNKNOWN;
5532 
5533       if (timestamp == 0)
5534         {
5535           /* Client using older EWMH _NET_ACTIVE_WINDOW without a timestamp */
5536           meta_warning ("Buggy client sent a _NET_ACTIVE_WINDOW message with a "
5537                         "timestamp of 0 for %s\n",
5538                         window->desc);
5539           timestamp = meta_display_get_current_time (display);
5540         }
5541 
5542       window_activate (window, timestamp, source_indication, NULL);
5543       return TRUE;
5544     }
5545   else if (event->xclient.message_type ==
5546            display->atom__NET_WM_FULLSCREEN_MONITORS)
5547     {
5548       gulong top, bottom, left, right;
5549 
5550       meta_verbose ("_NET_WM_FULLSCREEN_MONITORS request for window '%s'\n",
5551                     window->desc);
5552 
5553       top = event->xclient.data.l[0];
5554       bottom = event->xclient.data.l[1];
5555       left = event->xclient.data.l[2];
5556       right = event->xclient.data.l[3];
5557       /* source_indication = event->xclient.data.l[4]; */
5558 
5559       meta_window_update_fullscreen_monitors (window, top, bottom, left, right);
5560     }
5561   else if (event->xclient.message_type ==
5562            display->atom__GTK_SHOW_WINDOW_MENU)
5563     {
5564       gulong x_root, y_root;
5565       guint32 timestamp;
5566       int button;
5567 
5568       if (meta_prefs_get_raise_on_click ())
5569         meta_window_raise (window);
5570 
5571       timestamp = meta_display_get_current_time_roundtrip (display);
5572       x_root = event->xclient.data.l[1];
5573       y_root = event->xclient.data.l[2];
5574       button = 3;
5575 
5576       meta_window_show_menu (window,
5577                              x_root,
5578                              y_root,
5579                              button,
5580                              timestamp);
5581     }
5582 
5583   return FALSE;
5584 }
5585 
5586 static void
meta_window_appears_focused_changed(MetaWindow * window)5587 meta_window_appears_focused_changed (MetaWindow *window)
5588 {
5589   set_net_wm_state (window);
5590 
5591   if (window->frame)
5592     meta_frame_queue_draw (window->frame);
5593 }
5594 
5595 static void
check_ancestor_focus_appearance(MetaWindow * window)5596 check_ancestor_focus_appearance (MetaWindow *window)
5597 {
5598   MetaWindow *parent = meta_window_get_transient_for (window);
5599 
5600   if (!meta_prefs_get_attach_modal_dialogs ())
5601     return;
5602 
5603   if (window->type != META_WINDOW_MODAL_DIALOG || !parent || parent == window)
5604     return;
5605 
5606   if (parent->frame)
5607     meta_frame_queue_draw (parent->frame);
5608 
5609   check_ancestor_focus_appearance (parent);
5610 }
5611 
5612 gboolean
meta_window_notify_focus(MetaWindow * window,XEvent * event)5613 meta_window_notify_focus (MetaWindow *window,
5614                           XEvent     *event)
5615 {
5616   /* note the event can be on either the window or the frame,
5617    * we focus the frame for shaded windows
5618    */
5619 
5620   /* The event can be FocusIn, FocusOut, or UnmapNotify.
5621    * On UnmapNotify we have to pretend it's focus out,
5622    * because we won't get a focus out if it occurs, apparently.
5623    */
5624 
5625   /* We ignore grabs, though this is questionable.
5626    * It may be better to increase the intelligence of
5627    * the focus window tracking.
5628    *
5629    * The problem is that keybindings for windows are done with
5630    * XGrabKey, which means focus_window disappears and the front of
5631    * the MRU list gets confused from what the user expects once a
5632    * keybinding is used.
5633    */
5634   meta_topic (META_DEBUG_FOCUS,
5635               "Focus %s event received on %s 0x%lx (%s) "
5636               "mode %s detail %s\n",
5637               event->type == FocusIn ? "in" :
5638               event->type == FocusOut ? "out" :
5639               event->type == UnmapNotify ? "unmap" :
5640               "???",
5641               window->desc, event->xany.window,
5642               event->xany.window == window->xwindow ?
5643               "client window" :
5644               (window->frame && event->xany.window == window->frame->xwindow) ?
5645               "frame window" :
5646               "unknown window",
5647               event->type != UnmapNotify ?
5648               meta_event_mode_to_string (event->xfocus.mode) : "n/a",
5649               event->type != UnmapNotify ?
5650               meta_event_detail_to_string (event->xfocus.detail) : "n/a");
5651 
5652   /* FIXME our pointer tracking is broken; see how
5653    * gtk+/gdk/x11/gdkevents-x11.c or XFree86/xc/programs/xterm/misc.c
5654    * handle it for the correct way.  In brief you need to track
5655    * pointer focus and regular focus, and handle EnterNotify in
5656    * PointerRoot mode with no window manager.  However as noted above,
5657    * accurate focus tracking will break things because we want to keep
5658    * windows "focused" when using keybindings on them, and also we
5659    * sometimes "focus" a window by focusing its frame or
5660    * no_focus_window; so this all needs rethinking massively.
5661    *
5662    * My suggestion is to change it so that we clearly separate
5663    * actual keyboard focus tracking using the xterm algorithm,
5664    * and marco's "pretend" focus window, and go through all
5665    * the code and decide which one should be used in each place;
5666    * a hard bit is deciding on a policy for that.
5667    *
5668    * http://bugzilla.gnome.org/show_bug.cgi?id=90382
5669    */
5670 
5671   if ((event->type == FocusIn ||
5672        event->type == FocusOut) &&
5673       (event->xfocus.mode == NotifyGrab ||
5674        event->xfocus.mode == NotifyUngrab ||
5675        /* From WindowMaker, ignore all funky pointer root events */
5676        event->xfocus.detail > NotifyNonlinearVirtual))
5677     {
5678       meta_topic (META_DEBUG_FOCUS,
5679                   "Ignoring focus event generated by a grab or other weirdness\n");
5680       return TRUE;
5681     }
5682 
5683   if (event->type == FocusIn)
5684     {
5685       if (window != window->display->focus_window)
5686         {
5687           meta_topic (META_DEBUG_FOCUS,
5688                       "* Focus --> %s\n", window->desc);
5689           window->display->focus_window = window;
5690           window->has_focus = TRUE;
5691           meta_compositor_set_active_window (window->display->compositor,
5692                                              window->screen, window);
5693 
5694           /* Move to the front of the focusing workspace's MRU list.
5695            * We should only be "removing" it from the MRU list if it's
5696            * not already there.  Note that it's possible that we might
5697            * be processing this FocusIn after we've changed to a
5698            * different workspace; we should therefore update the MRU
5699            * list only if the window is actually on the active
5700            * workspace.
5701            */
5702           if (window->screen->active_workspace &&
5703               meta_window_located_on_workspace (window,
5704                                                 window->screen->active_workspace))
5705             {
5706               GList* link;
5707               link = g_list_find (window->screen->active_workspace->mru_list,
5708                                   window);
5709               g_assert (link);
5710 
5711               window->screen->active_workspace->mru_list =
5712                 g_list_remove_link (window->screen->active_workspace->mru_list,
5713                                     link);
5714               g_list_free (link);
5715 
5716               window->screen->active_workspace->mru_list =
5717                 g_list_prepend (window->screen->active_workspace->mru_list,
5718                                 window);
5719             }
5720 
5721           meta_window_appears_focused_changed (window);
5722 
5723           meta_error_trap_push (window->display);
5724           XInstallColormap (window->display->xdisplay,
5725                             window->colormap);
5726           meta_error_trap_pop (window->display, FALSE);
5727 
5728           /* move into FOCUSED_WINDOW layer */
5729           meta_window_update_layer (window);
5730 
5731           /* Ungrab click to focus button since the sync grab can interfere
5732            * with some things you might do inside the focused window, by
5733            * causing the client to get funky enter/leave events.
5734            *
5735            * The reason we usually have a passive grab on the window is
5736            * so that we can intercept clicks and raise the window in
5737            * response. For click-to-focus we don't need that since the
5738            * focused window is already raised. When raise_on_click is
5739            * FALSE we also don't need that since we don't do anything
5740            * when the window is clicked.
5741            *
5742            * There is dicussion in bugs 102209, 115072, and 461577
5743            */
5744           if (meta_prefs_get_focus_mode () == META_FOCUS_MODE_CLICK ||
5745               !meta_prefs_get_raise_on_click())
5746             meta_display_ungrab_focus_window_button (window->display, window);
5747 
5748           /* parent window become active. */
5749           check_ancestor_focus_appearance (window);
5750         }
5751     }
5752   else if (event->type == FocusOut ||
5753            event->type == UnmapNotify)
5754     {
5755       if (event->type == FocusOut &&
5756           event->xfocus.detail == NotifyInferior)
5757         {
5758           /* This event means the client moved focus to a subwindow */
5759           meta_topic (META_DEBUG_FOCUS,
5760                       "Ignoring focus out on %s with NotifyInferior\n",
5761                       window->desc);
5762           return TRUE;
5763         }
5764 
5765       if (window == window->display->focus_window)
5766         {
5767           meta_topic (META_DEBUG_FOCUS,
5768                       "%s is now the previous focus window due to being focused out or unmapped\n",
5769                       window->desc);
5770 
5771           meta_topic (META_DEBUG_FOCUS,
5772                       "* Focus --> NULL (was %s)\n", window->desc);
5773 
5774           window->display->focus_window = NULL;
5775           window->has_focus = FALSE;
5776 
5777           /* parent window become active. */
5778           check_ancestor_focus_appearance (window);
5779 
5780           meta_window_appears_focused_changed (window);
5781 
5782           meta_compositor_set_active_window (window->display->compositor,
5783                                              window->screen, NULL);
5784 
5785           meta_error_trap_push (window->display);
5786           XUninstallColormap (window->display->xdisplay,
5787                               window->colormap);
5788           meta_error_trap_pop (window->display, FALSE);
5789 
5790           /* move out of FOCUSED_WINDOW layer */
5791           meta_window_update_layer (window);
5792 
5793           /* Re-grab for click to focus and raise-on-click, if necessary */
5794           if (meta_prefs_get_focus_mode () == META_FOCUS_MODE_CLICK ||
5795               !meta_prefs_get_raise_on_click ())
5796             meta_display_grab_focus_window_button (window->display, window);
5797        }
5798     }
5799 
5800   /* Now set _NET_ACTIVE_WINDOW hint */
5801   meta_display_update_active_window_hint (window->display);
5802 
5803   return FALSE;
5804 }
5805 
5806 static gboolean
process_property_notify(MetaWindow * window,XPropertyEvent * event)5807 process_property_notify (MetaWindow     *window,
5808                          XPropertyEvent *event)
5809 {
5810   Window xid = window->xwindow;
5811 
5812   if (meta_is_verbose ()) /* avoid looking up the name if we don't have to */
5813     {
5814       char *property_name = XGetAtomName (window->display->xdisplay,
5815                                           event->atom);
5816 
5817       meta_verbose ("Property notify on %s for %s\n",
5818                     window->desc, property_name);
5819       XFree (property_name);
5820     }
5821 
5822   if (event->atom == window->display->atom__NET_WM_USER_TIME &&
5823       window->user_time_window)
5824     {
5825       xid = window->user_time_window;
5826     }
5827 
5828   meta_window_reload_property_from_xwindow (window, xid, event->atom, FALSE);
5829 
5830   return TRUE;
5831 }
5832 
5833 static void
send_configure_notify(MetaWindow * window)5834 send_configure_notify (MetaWindow *window)
5835 {
5836   XEvent event;
5837 
5838   /* from twm */
5839 
5840   event.type = ConfigureNotify;
5841   event.xconfigure.display = window->display->xdisplay;
5842   event.xconfigure.event = window->xwindow;
5843   event.xconfigure.window = window->xwindow;
5844   event.xconfigure.x = window->rect.x - window->border_width;
5845   event.xconfigure.y = window->rect.y - window->border_width;
5846   if (window->frame)
5847     {
5848       if (window->withdrawn)
5849         {
5850           MetaFrameBorders borders;
5851           /* We reparent the client window and put it to the position
5852            * where the visible top-left of the frame window currently is.
5853            */
5854 
5855           meta_frame_calc_borders (window->frame, &borders);
5856 
5857           event.xconfigure.x = window->frame->rect.x + borders.invisible.left;
5858           event.xconfigure.y = window->frame->rect.y + borders.invisible.top;
5859         }
5860       else
5861         {
5862           /* Need to be in root window coordinates */
5863           event.xconfigure.x += window->frame->rect.x;
5864           event.xconfigure.y += window->frame->rect.y;
5865         }
5866     }
5867   event.xconfigure.width = window->rect.width;
5868   event.xconfigure.height = window->rect.height;
5869   event.xconfigure.border_width = window->border_width; /* requested not actual */
5870   event.xconfigure.above = None; /* FIXME */
5871   event.xconfigure.override_redirect = False;
5872 
5873   meta_topic (META_DEBUG_GEOMETRY,
5874               "Sending synthetic configure notify to %s with x: %d y: %d w: %d h: %d\n",
5875               window->desc,
5876               event.xconfigure.x, event.xconfigure.y,
5877               event.xconfigure.width, event.xconfigure.height);
5878 
5879   meta_error_trap_push (window->display);
5880   XSendEvent (window->display->xdisplay,
5881               window->xwindow,
5882               False, StructureNotifyMask, &event);
5883   meta_error_trap_pop (window->display, FALSE);
5884 }
5885 
5886 gboolean
meta_window_get_icon_geometry(MetaWindow * window,MetaRectangle * rect)5887 meta_window_get_icon_geometry (MetaWindow    *window,
5888                                MetaRectangle *rect)
5889 {
5890   gulong *geometry = NULL;
5891   int nitems;
5892 
5893   if (meta_prop_get_cardinal_list (window->display,
5894                                    window->xwindow,
5895                                    window->display->atom__NET_WM_ICON_GEOMETRY,
5896                                    &geometry, &nitems))
5897     {
5898       if (nitems != 4)
5899         {
5900           meta_verbose ("_NET_WM_ICON_GEOMETRY on %s has %d values instead of 4\n",
5901                         window->desc, nitems);
5902           meta_XFree (geometry);
5903           return FALSE;
5904         }
5905 
5906       if (rect)
5907         {
5908           rect->x = geometry[0];
5909           rect->y = geometry[1];
5910           rect->width = geometry[2];
5911           rect->height = geometry[3];
5912         }
5913 
5914       meta_XFree (geometry);
5915 
5916       return TRUE;
5917     }
5918 
5919   return FALSE;
5920 }
5921 
5922 static Window
read_client_leader(MetaDisplay * display,Window xwindow)5923 read_client_leader (MetaDisplay *display,
5924                     Window       xwindow)
5925 {
5926   Window retval = None;
5927 
5928   meta_prop_get_window (display, xwindow,
5929                         display->atom_WM_CLIENT_LEADER,
5930                         &retval);
5931 
5932   return retval;
5933 }
5934 
5935 typedef struct
5936 {
5937   Window leader;
5938 } ClientLeaderData;
5939 
5940 static gboolean
find_client_leader_func(MetaWindow * ancestor,void * data)5941 find_client_leader_func (MetaWindow *ancestor,
5942                          void       *data)
5943 {
5944   ClientLeaderData *d;
5945 
5946   d = data;
5947 
5948   d->leader = read_client_leader (ancestor->display,
5949                                   ancestor->xwindow);
5950 
5951   /* keep going if no client leader found */
5952   return d->leader == None;
5953 }
5954 
5955 static void
update_sm_hints(MetaWindow * window)5956 update_sm_hints (MetaWindow *window)
5957 {
5958   Window leader;
5959 
5960   window->xclient_leader = None;
5961   window->sm_client_id = NULL;
5962 
5963   /* If not on the current window, we can get the client
5964    * leader from transient parents. If we find a client
5965    * leader, we read the SM_CLIENT_ID from it.
5966    */
5967   leader = read_client_leader (window->display, window->xwindow);
5968   if (leader == None)
5969     {
5970       ClientLeaderData d;
5971       d.leader = None;
5972       meta_window_foreach_ancestor (window, find_client_leader_func,
5973                                     &d);
5974       leader = d.leader;
5975     }
5976 
5977   if (leader != None)
5978     {
5979       char *str;
5980 
5981       window->xclient_leader = leader;
5982 
5983       if (meta_prop_get_latin1_string (window->display, leader,
5984                                        window->display->atom_SM_CLIENT_ID,
5985                                        &str))
5986         {
5987           window->sm_client_id = g_strdup (str);
5988           meta_XFree (str);
5989         }
5990     }
5991   else
5992     {
5993       meta_verbose ("Didn't find a client leader for %s\n", window->desc);
5994 
5995       if (!meta_prefs_get_disable_workarounds ())
5996         {
5997           /* Some broken apps (kdelibs fault?) set SM_CLIENT_ID on the app
5998            * instead of the client leader
5999            */
6000           char *str;
6001 
6002           str = NULL;
6003           if (meta_prop_get_latin1_string (window->display, window->xwindow,
6004                                            window->display->atom_SM_CLIENT_ID,
6005                                            &str))
6006             {
6007               if (window->sm_client_id == NULL) /* first time through */
6008                 meta_warning (_("Window %s sets SM_CLIENT_ID on itself, instead of on the WM_CLIENT_LEADER window as specified in the ICCCM.\n"),
6009                               window->desc);
6010 
6011               window->sm_client_id = g_strdup (str);
6012               meta_XFree (str);
6013             }
6014         }
6015     }
6016 
6017   meta_verbose ("Window %s client leader: 0x%lx SM_CLIENT_ID: '%s'\n",
6018                 window->desc, window->xclient_leader,
6019                 window->sm_client_id ? window->sm_client_id : "none");
6020 }
6021 
6022 void
meta_window_update_role(MetaWindow * window)6023 meta_window_update_role (MetaWindow *window)
6024 {
6025   char *str;
6026 
6027   if (window->role)
6028     g_free (window->role);
6029   window->role = NULL;
6030 
6031   if (meta_prop_get_latin1_string (window->display, window->xwindow,
6032                                    window->display->atom_WM_WINDOW_ROLE,
6033                                    &str))
6034     {
6035       window->role = g_strdup (str);
6036       meta_XFree (str);
6037     }
6038 
6039   meta_verbose ("Updated role of %s to '%s'\n",
6040                 window->desc, window->role ? window->role : "null");
6041 }
6042 
6043 void
meta_window_update_net_wm_type(MetaWindow * window)6044 meta_window_update_net_wm_type (MetaWindow *window)
6045 {
6046   int n_atoms;
6047   Atom *atoms;
6048   int i;
6049 
6050   window->type_atom = None;
6051   n_atoms = 0;
6052   atoms = NULL;
6053 
6054   meta_prop_get_atom_list (window->display, window->xwindow,
6055                            window->display->atom__NET_WM_WINDOW_TYPE,
6056                            &atoms, &n_atoms);
6057 
6058   i = 0;
6059   while (i < n_atoms)
6060     {
6061       /* We break as soon as we find one we recognize,
6062        * supposed to prefer those near the front of the list
6063        */
6064       if (atoms[i] == window->display->atom__NET_WM_WINDOW_TYPE_DESKTOP ||
6065           atoms[i] == window->display->atom__NET_WM_WINDOW_TYPE_DOCK ||
6066           atoms[i] == window->display->atom__NET_WM_WINDOW_TYPE_TOOLBAR ||
6067           atoms[i] == window->display->atom__NET_WM_WINDOW_TYPE_MENU ||
6068           atoms[i] == window->display->atom__NET_WM_WINDOW_TYPE_DIALOG ||
6069           atoms[i] == window->display->atom__NET_WM_WINDOW_TYPE_NORMAL ||
6070           atoms[i] == window->display->atom__NET_WM_WINDOW_TYPE_UTILITY ||
6071           atoms[i] == window->display->atom__NET_WM_WINDOW_TYPE_SPLASH)
6072         {
6073           window->type_atom = atoms[i];
6074           break;
6075         }
6076 
6077       ++i;
6078     }
6079 
6080   meta_XFree (atoms);
6081 
6082   if (meta_is_verbose ())
6083     {
6084       char *str;
6085 
6086       str = NULL;
6087       if (window->type_atom != None)
6088         {
6089           meta_error_trap_push (window->display);
6090           str = XGetAtomName (window->display->xdisplay, window->type_atom);
6091           meta_error_trap_pop (window->display, TRUE);
6092         }
6093 
6094       meta_verbose ("Window %s type atom %s\n", window->desc,
6095                     str ? str : "(none)");
6096 
6097       if (str)
6098         meta_XFree (str);
6099     }
6100 
6101   meta_window_recalc_window_type (window);
6102 }
6103 
6104 static void
redraw_icon(MetaWindow * window)6105 redraw_icon (MetaWindow *window)
6106 {
6107   /* We could probably be smart and just redraw the icon here,
6108    * instead of the whole frame.
6109    */
6110   if (window->frame && (window->mapped || window->frame->mapped))
6111     meta_ui_queue_frame_draw (window->screen->ui, window->frame->xwindow);
6112 }
6113 
6114 void
meta_window_update_icon_now(MetaWindow * window)6115 meta_window_update_icon_now (MetaWindow *window)
6116 {
6117   GdkPixbuf *icon;
6118   GdkPixbuf *mini_icon;
6119 
6120   icon = NULL;
6121   mini_icon = NULL;
6122 
6123   int icon_size = meta_prefs_get_icon_size();
6124 
6125   if (meta_read_icons (window->screen,
6126                        window->xwindow,
6127                        window->res_name,
6128                        &window->icon_cache,
6129                        window->wm_hints_pixmap,
6130                        window->wm_hints_mask,
6131                        &icon,
6132                        icon_size, /* width  */
6133                        icon_size, /* height */
6134                        &mini_icon,
6135                        META_MINI_ICON_WIDTH,
6136                        META_MINI_ICON_HEIGHT))
6137     {
6138       if (window->icon)
6139         g_object_unref (G_OBJECT (window->icon));
6140 
6141       if (window->mini_icon)
6142         g_object_unref (G_OBJECT (window->mini_icon));
6143 
6144       window->icon = icon;
6145       window->mini_icon = mini_icon;
6146 
6147       redraw_icon (window);
6148     }
6149 
6150   g_assert (window->icon);
6151   g_assert (window->mini_icon);
6152 }
6153 
6154 static gboolean
idle_update_icon(gpointer data)6155 idle_update_icon (gpointer data)
6156 {
6157   GSList *tmp;
6158   GSList *copy;
6159   guint queue_index = GPOINTER_TO_INT (data);
6160 
6161   meta_topic (META_DEBUG_GEOMETRY, "Clearing the update_icon queue\n");
6162 
6163   /* Work with a copy, for reentrancy. The allowed reentrancy isn't
6164    * complete; destroying a window while we're in here would result in
6165    * badness. But it's OK to queue/unqueue update_icons.
6166    */
6167   copy = g_slist_copy (queue_pending[queue_index]);
6168   g_slist_free (queue_pending[queue_index]);
6169   queue_pending[queue_index] = NULL;
6170   queue_idle[queue_index] = 0;
6171 
6172   destroying_windows_disallowed += 1;
6173 
6174   tmp = copy;
6175   while (tmp != NULL)
6176     {
6177       MetaWindow *window;
6178 
6179       window = tmp->data;
6180 
6181       meta_window_update_icon_now (window);
6182       window->is_in_queues &= ~META_QUEUE_UPDATE_ICON;
6183 
6184       tmp = tmp->next;
6185     }
6186 
6187   g_slist_free (copy);
6188 
6189   destroying_windows_disallowed -= 1;
6190 
6191   return FALSE;
6192 }
6193 
6194 MetaWorkspace *
meta_window_get_workspace(MetaWindow * window)6195 meta_window_get_workspace (MetaWindow *window)
6196 {
6197   if (window->on_all_workspaces)
6198     return window->screen->active_workspace;
6199   else
6200     return window->workspace;
6201 }
6202 
6203 GList*
meta_window_get_workspaces(MetaWindow * window)6204 meta_window_get_workspaces (MetaWindow *window)
6205 {
6206   if (window->on_all_workspaces)
6207     return window->screen->workspaces;
6208   else
6209     return window->workspace->list_containing_self;
6210 }
6211 
6212 static void
invalidate_work_areas(MetaWindow * window)6213 invalidate_work_areas (MetaWindow *window)
6214 {
6215   GList *tmp;
6216 
6217   tmp = meta_window_get_workspaces (window);
6218 
6219   while (tmp != NULL)
6220     {
6221       meta_workspace_invalidate_work_area (tmp->data);
6222       tmp = tmp->next;
6223     }
6224 }
6225 
6226 void
meta_window_update_struts(MetaWindow * window)6227 meta_window_update_struts (MetaWindow *window)
6228 {
6229   GSList *old_struts;
6230   GSList *new_struts;
6231   GSList *old_iter, *new_iter;
6232   gulong *struts = NULL;
6233   int nitems;
6234   gboolean changed;
6235 
6236   meta_verbose ("Updating struts for %s\n", window->desc);
6237 
6238   old_struts = window->struts;
6239   new_struts = NULL;
6240 
6241   if (meta_prop_get_cardinal_list (window->display,
6242                                    window->xwindow,
6243                                    window->display->atom__GNOME_WM_STRUT_AREA,
6244                                    &struts, &nitems))
6245     {
6246       if (nitems != 4)
6247         {
6248           meta_verbose ("_GNOME_WM_STRUT_AREA on %s has %d values instead of 4\n",
6249                         window->desc, nitems);
6250         }
6251       else
6252         {
6253           MetaStrut *temp;
6254           MetaSide side;
6255           gboolean valid;
6256           int i;
6257 
6258           temp = g_new (MetaStrut, 1);
6259 
6260           temp->rect.x = struts[0];
6261           temp->rect.y = struts[1];
6262           temp->rect.width = struts[2];
6263           temp->rect.height = struts[3];
6264 
6265           side = META_SIDE_LEFT;
6266           valid = FALSE;
6267 
6268           for (i = 0; i < window->screen->n_xinerama_infos; i++)
6269             {
6270               MetaRectangle monitor;
6271 
6272               monitor = window->screen->xinerama_infos[i].rect;
6273               if (!meta_rectangle_contains_rect (&monitor, &temp->rect))
6274                 continue;
6275 
6276               if (temp->rect.height > temp->rect.width)
6277                 {
6278                   if (temp->rect.x == monitor.x)
6279                     {
6280                       side = META_SIDE_LEFT;
6281                       valid = TRUE;
6282                     }
6283                   else if (temp->rect.x + temp->rect.width == monitor.x + monitor.width)
6284                     {
6285                       side = META_SIDE_RIGHT;
6286                       valid = TRUE;
6287                     }
6288                 }
6289               else
6290                 {
6291                   if (temp->rect.y == monitor.y)
6292                     {
6293                       side = META_SIDE_TOP;
6294                       valid = TRUE;
6295                     }
6296                   else if (temp->rect.y + temp->rect.height == monitor.y + monitor.height)
6297                     {
6298                       side = META_SIDE_BOTTOM;
6299                       valid = TRUE;
6300                     }
6301                 }
6302             }
6303 
6304           if (valid)
6305             {
6306               temp->side = side;
6307               temp->edge = META_EDGE_XINERAMA;
6308 
6309               new_struts = g_slist_prepend (new_struts, temp);
6310             }
6311           else
6312             {
6313               g_free (temp);
6314             }
6315         }
6316 
6317       meta_XFree (struts);
6318     }
6319   else
6320     {
6321       meta_verbose ("No _GNOME_WM_STRUT_AREA property for %s\n",
6322                     window->desc);
6323     }
6324 
6325   if (!new_struts &&
6326       meta_prop_get_cardinal_list (window->display,
6327                                    window->xwindow,
6328                                    window->display->atom__NET_WM_STRUT_PARTIAL,
6329                                    &struts, &nitems))
6330     {
6331       if (nitems != 12)
6332         meta_verbose ("_NET_WM_STRUT_PARTIAL on %s has %d values instead "
6333                       "of 12\n",
6334                       window->desc, nitems);
6335       else
6336         {
6337           /* Pull out the strut info for each side in the hint */
6338           int i;
6339           for (i=0; i<4; i++)
6340             {
6341               MetaStrut *temp;
6342               int thickness, strut_begin, strut_end;
6343 
6344               thickness = struts[i];
6345               if (thickness == 0)
6346                 continue;
6347               strut_begin = struts[4+(i*2)];
6348               strut_end   = struts[4+(i*2)+1];
6349 
6350               temp = g_new (MetaStrut, 1);
6351               temp->side = 1 << i; /* See MetaSide def.  Matches nicely, eh? */
6352               temp->edge = META_EDGE_SCREEN;
6353               temp->rect = window->screen->rect;
6354               switch (temp->side)
6355                 {
6356                 case META_SIDE_RIGHT:
6357                   temp->rect.x = BOX_RIGHT(temp->rect) - thickness;
6358                   /* Intentionally fall through without breaking */
6359                 case META_SIDE_LEFT:
6360                   temp->rect.width  = thickness;
6361                   temp->rect.y      = strut_begin;
6362                   temp->rect.height = strut_end - strut_begin + 1;
6363                   break;
6364                 case META_SIDE_BOTTOM:
6365                   temp->rect.y = BOX_BOTTOM(temp->rect) - thickness;
6366                   /* Intentionally fall through without breaking */
6367                 case META_SIDE_TOP:
6368                   temp->rect.height = thickness;
6369                   temp->rect.x      = strut_begin;
6370                   temp->rect.width  = strut_end - strut_begin + 1;
6371                   break;
6372                 default:
6373                   g_assert_not_reached ();
6374                 }
6375 
6376               new_struts = g_slist_prepend (new_struts, temp);
6377             }
6378 
6379           meta_verbose ("_NET_WM_STRUT_PARTIAL struts %lu %lu %lu %lu for "
6380                         "window %s\n",
6381                         struts[0], struts[1], struts[2], struts[3],
6382                         window->desc);
6383         }
6384       meta_XFree (struts);
6385     }
6386   else
6387     {
6388       meta_verbose ("No _NET_WM_STRUT property for %s\n",
6389                     window->desc);
6390     }
6391 
6392   if (!new_struts &&
6393       meta_prop_get_cardinal_list (window->display,
6394                                    window->xwindow,
6395                                    window->display->atom__NET_WM_STRUT,
6396                                    &struts, &nitems))
6397     {
6398       if (nitems != 4)
6399         meta_verbose ("_NET_WM_STRUT on %s has %d values instead of 4\n",
6400                       window->desc, nitems);
6401       else
6402         {
6403           /* Pull out the strut info for each side in the hint */
6404           int i;
6405           for (i=0; i<4; i++)
6406             {
6407               MetaStrut *temp;
6408               int thickness;
6409 
6410               thickness = struts[i];
6411               if (thickness == 0)
6412                 continue;
6413 
6414               temp = g_new (MetaStrut, 1);
6415               temp->side = 1 << i;
6416               temp->edge = META_EDGE_SCREEN;
6417               temp->rect = window->screen->rect;
6418               switch (temp->side)
6419                 {
6420                 case META_SIDE_RIGHT:
6421                   temp->rect.x = BOX_RIGHT(temp->rect) - thickness;
6422                   /* Intentionally fall through without breaking */
6423                 case META_SIDE_LEFT:
6424                   temp->rect.width  = thickness;
6425                   break;
6426                 case META_SIDE_BOTTOM:
6427                   temp->rect.y = BOX_BOTTOM(temp->rect) - thickness;
6428                   /* Intentionally fall through without breaking */
6429                 case META_SIDE_TOP:
6430                   temp->rect.height = thickness;
6431                   break;
6432                 default:
6433                   g_assert_not_reached ();
6434                 }
6435 
6436               new_struts = g_slist_prepend (new_struts, temp);
6437             }
6438 
6439           meta_verbose ("_NET_WM_STRUT struts %lu %lu %lu %lu for window %s\n",
6440                         struts[0], struts[1], struts[2], struts[3],
6441                         window->desc);
6442         }
6443       meta_XFree (struts);
6444     }
6445   else if (!new_struts)
6446     {
6447       meta_verbose ("No _NET_WM_STRUT property for %s\n",
6448                     window->desc);
6449     }
6450 
6451   /* Determine whether old_struts and new_struts are the same */
6452   old_iter = old_struts;
6453   new_iter = new_struts;
6454   while (old_iter && new_iter)
6455     {
6456       MetaStrut *old_strut = (MetaStrut*) old_iter->data;
6457       MetaStrut *new_strut = (MetaStrut*) new_iter->data;
6458 
6459       if (old_strut->side != new_strut->side ||
6460           old_strut->edge != new_strut->edge ||
6461           !meta_rectangle_equal (&old_strut->rect, &new_strut->rect))
6462         break;
6463 
6464       old_iter = old_iter->next;
6465       new_iter = new_iter->next;
6466     }
6467   changed = (old_iter != NULL || new_iter != NULL);
6468 
6469   /* Update appropriately */
6470   g_slist_free_full (old_struts, g_free);
6471   window->struts = new_struts;
6472   if (changed)
6473     {
6474       meta_topic (META_DEBUG_WORKAREA,
6475                   "Invalidating work areas of window %s due to struts update\n",
6476                   window->desc);
6477       invalidate_work_areas (window);
6478     }
6479   else
6480     {
6481       meta_topic (META_DEBUG_WORKAREA,
6482                   "Struts on %s were unchanged\n", window->desc);
6483     }
6484 }
6485 
6486 void
meta_window_recalc_window_type(MetaWindow * window)6487 meta_window_recalc_window_type (MetaWindow *window)
6488 {
6489   recalc_window_type (window);
6490 }
6491 
6492 static void
recalc_window_type(MetaWindow * window)6493 recalc_window_type (MetaWindow *window)
6494 {
6495   MetaWindowType old_type;
6496 
6497   old_type = window->type;
6498 
6499   if (window->type_atom != None)
6500     {
6501       if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_DESKTOP)
6502         window->type = META_WINDOW_DESKTOP;
6503       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_DOCK)
6504         window->type = META_WINDOW_DOCK;
6505       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_TOOLBAR)
6506         window->type = META_WINDOW_TOOLBAR;
6507       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_MENU)
6508         window->type = META_WINDOW_MENU;
6509       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_DIALOG)
6510         window->type = META_WINDOW_DIALOG;
6511       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_NORMAL)
6512         window->type = META_WINDOW_NORMAL;
6513       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_UTILITY)
6514         window->type = META_WINDOW_UTILITY;
6515       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_SPLASH)
6516         window->type = META_WINDOW_SPLASHSCREEN;
6517       else
6518         meta_bug ("Set a type atom for %s that wasn't handled in recalc_window_type\n",
6519                   window->desc);
6520     }
6521   else if (window->xtransient_for != None)
6522     {
6523       window->type = META_WINDOW_DIALOG;
6524     }
6525   else
6526     {
6527       window->type = META_WINDOW_NORMAL;
6528     }
6529 
6530   if (window->type == META_WINDOW_DIALOG &&
6531       window->wm_state_modal)
6532     window->type = META_WINDOW_MODAL_DIALOG;
6533 
6534   meta_verbose ("Calculated type %u for %s, old type %u\n",
6535                 window->type, window->desc, old_type);
6536 
6537   if (old_type != window->type)
6538     {
6539       recalc_window_features (window);
6540 
6541       set_net_wm_state (window);
6542 
6543       /* Update frame */
6544       if (window->decorated)
6545         meta_window_ensure_frame (window);
6546       else
6547         meta_window_destroy_frame (window);
6548 
6549       /* update stacking constraints */
6550       meta_window_update_layer (window);
6551 
6552       meta_window_grab_keys (window);
6553     }
6554 }
6555 
6556 static void
set_allowed_actions_hint(MetaWindow * window)6557 set_allowed_actions_hint (MetaWindow *window)
6558 {
6559 #define MAX_N_ACTIONS 12
6560   unsigned long data[MAX_N_ACTIONS];
6561   int i;
6562 
6563   i = 0;
6564   if (window->has_move_func &&
6565       !window->minimized)
6566     {
6567       data[i] = window->display->atom__NET_WM_ACTION_MOVE;
6568       ++i;
6569     }
6570   if (window->has_resize_func &&
6571       !window->minimized      &&
6572       !window->shaded         &&
6573       !window->tiled          &&
6574       !(window->maximized_horizontally && window->maximized_vertically))
6575     {
6576       data[i] = window->display->atom__NET_WM_ACTION_RESIZE;
6577       ++i;
6578     }
6579   if (window->has_fullscreen_func &&
6580       !window->minimized          &&
6581       !window->shaded)
6582     {
6583       data[i] = window->display->atom__NET_WM_ACTION_FULLSCREEN;
6584       ++i;
6585     }
6586   if (window->has_minimize_func)
6587     {
6588       data[i] = window->display->atom__NET_WM_ACTION_MINIMIZE;
6589       ++i;
6590     }
6591   if (window->has_shade_func &&
6592       !window->minimized)
6593     {
6594       data[i] = window->display->atom__NET_WM_ACTION_SHADE;
6595       ++i;
6596     }
6597   /* sticky according to EWMH is different from marco's sticky;
6598    * marco doesn't support EWMH sticky
6599    */
6600   if (window->has_maximize_func &&
6601       !window->minimized        &&
6602       !window->shaded)
6603     {
6604       data[i] = window->display->atom__NET_WM_ACTION_MAXIMIZE_HORZ;
6605       ++i;
6606       data[i] = window->display->atom__NET_WM_ACTION_MAXIMIZE_VERT;
6607       ++i;
6608     }
6609   /* We always allow this */
6610   data[i] = window->display->atom__NET_WM_ACTION_CHANGE_DESKTOP;
6611   ++i;
6612   if (window->has_close_func)
6613     {
6614       data[i] = window->display->atom__NET_WM_ACTION_CLOSE;
6615       ++i;
6616     }
6617 
6618   /* I guess we always allow above/below operations */
6619   data[i] = window->display->atom__NET_WM_ACTION_ABOVE;
6620   ++i;
6621   data[i] = window->display->atom__NET_WM_ACTION_BELOW;
6622   ++i;
6623 
6624   g_assert (i <= MAX_N_ACTIONS);
6625 
6626   meta_verbose ("Setting _NET_WM_ALLOWED_ACTIONS with %d atoms\n", i);
6627 
6628   meta_error_trap_push (window->display);
6629   XChangeProperty (window->display->xdisplay, window->xwindow,
6630                    window->display->atom__NET_WM_ALLOWED_ACTIONS,
6631                    XA_ATOM,
6632                    32, PropModeReplace, (guchar*) data, i);
6633   meta_error_trap_pop (window->display, FALSE);
6634 #undef MAX_N_ACTIONS
6635 }
6636 
6637 void
meta_window_recalc_features(MetaWindow * window)6638 meta_window_recalc_features (MetaWindow *window)
6639 {
6640   recalc_window_features (window);
6641 }
6642 
6643 static void
recalc_window_features(MetaWindow * window)6644 recalc_window_features (MetaWindow *window)
6645 {
6646   gboolean old_has_close_func;
6647   gboolean old_has_minimize_func;
6648   gboolean old_has_move_func;
6649   gboolean old_has_resize_func;
6650   gboolean old_has_shade_func;
6651   gboolean old_always_sticky;
6652 
6653   old_has_close_func = window->has_close_func;
6654   old_has_minimize_func = window->has_minimize_func;
6655   old_has_move_func = window->has_move_func;
6656   old_has_resize_func = window->has_resize_func;
6657   old_has_shade_func = window->has_shade_func;
6658   old_always_sticky = window->always_sticky;
6659 
6660   /* Use MWM hints initially */
6661   window->decorated = window->mwm_decorated;
6662   window->border_only = window->mwm_border_only;
6663   window->has_close_func = window->mwm_has_close_func;
6664   window->has_minimize_func = window->mwm_has_minimize_func;
6665   window->has_maximize_func = window->mwm_has_maximize_func;
6666   window->has_move_func = window->mwm_has_move_func;
6667 
6668   window->has_resize_func = TRUE;
6669 
6670   /* If min_size == max_size, then don't allow resize */
6671   if (window->size_hints.min_width == window->size_hints.max_width &&
6672       window->size_hints.min_height == window->size_hints.max_height)
6673     window->has_resize_func = FALSE;
6674   else if (!window->mwm_has_resize_func)
6675     {
6676       /* We ignore mwm_has_resize_func because WM_NORMAL_HINTS is the
6677        * authoritative source for that info. Some apps such as mplayer or
6678        * xine disable resize via MWM but not WM_NORMAL_HINTS, but that
6679        * leads to e.g. us not fullscreening their windows.  Apps that set
6680        * MWM but not WM_NORMAL_HINTS are basically broken. We complain
6681        * about these apps but make them work.
6682        */
6683 
6684       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"),
6685                     window->desc,
6686                     window->size_hints.min_width,
6687                     window->size_hints.min_height,
6688                     window->size_hints.max_width,
6689                     window->size_hints.max_height);
6690     }
6691 
6692   window->has_shade_func = TRUE;
6693   window->has_fullscreen_func = TRUE;
6694 
6695   window->always_sticky = FALSE;
6696 
6697   /* Semantic category overrides the MWM hints */
6698   if (window->type == META_WINDOW_TOOLBAR)
6699     window->decorated = FALSE;
6700 
6701   if (window->type == META_WINDOW_MODAL_DIALOG && meta_prefs_get_attach_modal_dialogs ())
6702     {
6703       MetaWindow *parent = meta_window_get_transient_for (window);
6704       if (parent)
6705         {
6706           window->has_resize_func = FALSE;
6707           window->border_only = TRUE;
6708         }
6709     }
6710 
6711   if (window->type == META_WINDOW_DESKTOP ||
6712       window->type == META_WINDOW_DOCK)
6713     window->always_sticky = TRUE;
6714 
6715   if (window->type == META_WINDOW_DESKTOP ||
6716       window->type == META_WINDOW_DOCK ||
6717       window->type == META_WINDOW_SPLASHSCREEN)
6718     {
6719       window->decorated = FALSE;
6720       window->has_close_func = FALSE;
6721       window->has_shade_func = FALSE;
6722 
6723       /* FIXME this keeps panels and things from using
6724        * NET_WM_MOVERESIZE; the problem is that some
6725        * panels (edge panels) have fixed possible locations,
6726        * and others ("floating panels") do not.
6727        *
6728        * Perhaps we should require edge panels to explicitly
6729        * disable movement?
6730        */
6731       window->has_move_func = FALSE;
6732       window->has_resize_func = FALSE;
6733     }
6734 
6735   if (window->type != META_WINDOW_NORMAL)
6736     {
6737       window->has_minimize_func = FALSE;
6738       window->has_maximize_func = FALSE;
6739       window->has_fullscreen_func = FALSE;
6740     }
6741 
6742   if (!window->has_resize_func)
6743     {
6744       window->has_maximize_func = FALSE;
6745 
6746       /* don't allow fullscreen if we can't resize, unless the size
6747        * is entire screen size (kind of broken, because we
6748        * actually fullscreen to xinerama head size not screen size)
6749        */
6750       if (window->size_hints.min_width == window->screen->rect.width &&
6751           window->size_hints.min_height == window->screen->rect.height)
6752         ; /* leave fullscreen available */
6753       else
6754         window->has_fullscreen_func = FALSE;
6755     }
6756 
6757   /* We leave fullscreen windows decorated, just push the frame outside
6758    * the screen. This avoids flickering to unparent them.
6759    *
6760    * Note that setting has_resize_func = FALSE here must come after the
6761    * above code that may disable fullscreen, because if the window
6762    * is not resizable purely due to fullscreen, we don't want to
6763    * disable fullscreen mode.
6764    */
6765   if (window->fullscreen)
6766     {
6767       window->has_shade_func = FALSE;
6768       window->has_move_func = FALSE;
6769       window->has_resize_func = FALSE;
6770       window->has_maximize_func = FALSE;
6771     }
6772 
6773   meta_topic (META_DEBUG_WINDOW_OPS,
6774               "Window %s fullscreen = %d not resizable, maximizable = %d fullscreenable = %d min size %dx%d max size %dx%d\n",
6775               window->desc,
6776               window->fullscreen,
6777               window->has_maximize_func, window->has_fullscreen_func,
6778               window->size_hints.min_width,
6779               window->size_hints.min_height,
6780               window->size_hints.max_width,
6781               window->size_hints.max_height);
6782 
6783   /* no shading if not decorated */
6784   if (!window->decorated || window->border_only)
6785     window->has_shade_func = FALSE;
6786 
6787   window->skip_taskbar = FALSE;
6788   window->skip_pager = FALSE;
6789 
6790   if (window->wm_state_skip_taskbar)
6791     window->skip_taskbar = TRUE;
6792 
6793   if (window->wm_state_skip_pager)
6794     window->skip_pager = TRUE;
6795 
6796   switch (window->type)
6797     {
6798       /* Force skip taskbar/pager on these window types */
6799     case META_WINDOW_DESKTOP:
6800     case META_WINDOW_DOCK:
6801     case META_WINDOW_TOOLBAR:
6802     case META_WINDOW_MENU:
6803     case META_WINDOW_UTILITY:
6804     case META_WINDOW_SPLASHSCREEN:
6805       window->skip_taskbar = TRUE;
6806       window->skip_pager = TRUE;
6807       break;
6808 
6809     case META_WINDOW_DIALOG:
6810     case META_WINDOW_MODAL_DIALOG:
6811       /* only skip taskbar if we have a real transient parent */
6812       if (window->xtransient_for != None &&
6813           window->xtransient_for != window->screen->xroot)
6814         window->skip_taskbar = TRUE;
6815       break;
6816 
6817     case META_WINDOW_NORMAL:
6818       break;
6819     }
6820 
6821   meta_topic (META_DEBUG_WINDOW_OPS,
6822               "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",
6823               window->desc,
6824               window->decorated,
6825               window->border_only,
6826               window->has_close_func,
6827               window->has_minimize_func,
6828               window->has_maximize_func,
6829               window->has_move_func,
6830               window->has_shade_func,
6831               window->skip_taskbar,
6832               window->skip_pager);
6833 
6834   /* FIXME:
6835    * Lame workaround for recalc_window_features
6836    * being used overzealously. The fix is to
6837    * only recalc_window_features when something
6838    * has actually changed.
6839    */
6840   if (window->constructing                               ||
6841       old_has_close_func != window->has_close_func       ||
6842       old_has_minimize_func != window->has_minimize_func ||
6843       old_has_move_func != window->has_move_func         ||
6844       old_has_resize_func != window->has_resize_func     ||
6845       old_has_shade_func != window->has_shade_func       ||
6846       old_always_sticky != window->always_sticky)
6847     set_allowed_actions_hint (window);
6848 
6849   /* FIXME perhaps should ensure if we don't have a shade func,
6850    * we aren't shaded, etc.
6851    */
6852 }
6853 
6854 static void
menu_callback(MetaWindowMenu * menu,Display * xdisplay,Window client_xwindow,guint32 timestamp,MetaMenuOp op,int workspace_index,gpointer data)6855 menu_callback (MetaWindowMenu *menu,
6856                Display        *xdisplay,
6857                Window          client_xwindow,
6858                guint32         timestamp,
6859                MetaMenuOp      op,
6860                int             workspace_index,
6861                gpointer        data)
6862 {
6863   MetaDisplay *display;
6864   MetaWindow *window;
6865   MetaWorkspace *workspace;
6866 
6867   display = meta_display_for_x_display (xdisplay);
6868   window = meta_display_lookup_x_window (display, client_xwindow);
6869   workspace = NULL;
6870 
6871   if (window != NULL) /* window can be NULL */
6872     {
6873       meta_verbose ("Menu op %u on %s\n", op, window->desc);
6874 
6875       switch (op)
6876         {
6877         case META_MENU_OP_NONE:
6878           /* nothing */
6879           break;
6880 
6881         case META_MENU_OP_DELETE:
6882           meta_window_delete (window, timestamp);
6883           break;
6884 
6885         case META_MENU_OP_MINIMIZE:
6886           meta_window_minimize (window);
6887           break;
6888 
6889         case META_MENU_OP_UNMAXIMIZE:
6890           meta_window_unmaximize (window,
6891                                   META_MAXIMIZE_HORIZONTAL |
6892                                   META_MAXIMIZE_VERTICAL);
6893           break;
6894 
6895         case META_MENU_OP_MAXIMIZE:
6896           meta_window_maximize (window,
6897                                 META_MAXIMIZE_HORIZONTAL |
6898                                 META_MAXIMIZE_VERTICAL);
6899           break;
6900 
6901         case META_MENU_OP_UNSHADE:
6902           meta_window_unshade (window, timestamp);
6903           break;
6904 
6905         case META_MENU_OP_SHADE:
6906           meta_window_shade (window, timestamp);
6907           break;
6908 
6909         case META_MENU_OP_MOVE_LEFT:
6910           workspace = meta_workspace_get_neighbor (window->screen->active_workspace,
6911                                                    META_MOTION_LEFT);
6912           break;
6913 
6914         case META_MENU_OP_MOVE_RIGHT:
6915           workspace = meta_workspace_get_neighbor (window->screen->active_workspace,
6916                                                    META_MOTION_RIGHT);
6917           break;
6918 
6919         case META_MENU_OP_MOVE_UP:
6920           workspace = meta_workspace_get_neighbor (window->screen->active_workspace,
6921                                                    META_MOTION_UP);
6922           break;
6923 
6924         case META_MENU_OP_MOVE_DOWN:
6925           workspace = meta_workspace_get_neighbor (window->screen->active_workspace,
6926                                                    META_MOTION_DOWN);
6927           break;
6928 
6929         case META_MENU_OP_WORKSPACES:
6930           workspace = meta_screen_get_workspace_by_index (window->screen,
6931                                                           workspace_index);
6932           break;
6933 
6934         case META_MENU_OP_STICK:
6935           meta_window_stick (window);
6936           break;
6937 
6938         case META_MENU_OP_UNSTICK:
6939           meta_window_unstick (window);
6940           break;
6941 
6942         case META_MENU_OP_ABOVE:
6943         case META_MENU_OP_UNABOVE:
6944           if (window->wm_state_above == FALSE)
6945             meta_window_make_above (window);
6946           else
6947             meta_window_unmake_above (window);
6948           break;
6949 
6950         case META_MENU_OP_MOVE:
6951           meta_window_begin_grab_op (window,
6952                                      META_GRAB_OP_KEYBOARD_MOVING,
6953                                      TRUE,
6954                                      timestamp);
6955           break;
6956 
6957         case META_MENU_OP_RESIZE:
6958           meta_window_begin_grab_op (window,
6959                                      META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN,
6960                                      TRUE,
6961                                      timestamp);
6962           break;
6963 
6964         case META_MENU_OP_RECOVER:
6965           meta_window_shove_titlebar_onscreen (window);
6966           break;
6967 
6968         default:
6969           meta_warning (G_STRLOC": Unknown window op\n");
6970           break;
6971         }
6972 
6973       if (workspace)
6974 	{
6975 	  meta_window_change_workspace (window,
6976 					workspace);
6977 #if 0
6978 	  meta_workspace_activate (workspace);
6979 	  meta_window_raise (window);
6980 #endif
6981 	}
6982     }
6983   else
6984     {
6985       meta_verbose ("Menu callback on nonexistent window\n");
6986     }
6987 
6988   if (display->window_menu == menu)
6989     {
6990       display->window_menu = NULL;
6991       display->window_with_menu = NULL;
6992     }
6993 
6994   meta_ui_window_menu_free (menu);
6995 }
6996 
6997 void
meta_window_show_menu(MetaWindow * window,int root_x,int root_y,int button,guint32 timestamp)6998 meta_window_show_menu (MetaWindow *window,
6999                        int         root_x,
7000                        int         root_y,
7001                        int         button,
7002                        guint32     timestamp)
7003 {
7004   MetaMenuOp ops;
7005   MetaMenuOp insensitive;
7006   MetaWindowMenu *menu;
7007   MetaWorkspaceLayout layout;
7008   int n_workspaces;
7009   gboolean ltr;
7010 
7011   if (window->display->window_menu)
7012     {
7013       meta_ui_window_menu_free (window->display->window_menu);
7014       window->display->window_menu = NULL;
7015       window->display->window_with_menu = NULL;
7016     }
7017 
7018   ops = META_MENU_OP_NONE;
7019   insensitive = META_MENU_OP_NONE;
7020 
7021   ops |= (META_MENU_OP_DELETE | META_MENU_OP_MINIMIZE | META_MENU_OP_MOVE | META_MENU_OP_RESIZE);
7022 
7023   if (!meta_window_titlebar_is_onscreen (window) &&
7024       window->type != META_WINDOW_DOCK &&
7025       window->type != META_WINDOW_DESKTOP)
7026     ops |= META_MENU_OP_RECOVER;
7027 
7028   n_workspaces = meta_screen_get_n_workspaces (window->screen);
7029 
7030   if (n_workspaces > 1)
7031     ops |= META_MENU_OP_WORKSPACES;
7032 
7033   meta_screen_calc_workspace_layout (window->screen,
7034                                      n_workspaces,
7035                                      meta_workspace_index ( window->screen->active_workspace),
7036                                      &layout);
7037 
7038   if (!window->on_all_workspaces)
7039     {
7040       ltr = meta_ui_get_direction() == META_UI_DIRECTION_LTR;
7041 
7042       if (layout.current_col > 0)
7043         ops |= ltr ? META_MENU_OP_MOVE_LEFT : META_MENU_OP_MOVE_RIGHT;
7044       if ((layout.current_col < layout.cols - 1) &&
7045           (layout.current_row * layout.cols + (layout.current_col + 1) < n_workspaces))
7046         ops |= ltr ? META_MENU_OP_MOVE_RIGHT : META_MENU_OP_MOVE_LEFT;
7047       if (layout.current_row > 0)
7048         ops |= META_MENU_OP_MOVE_UP;
7049       if ((layout.current_row < layout.rows - 1) &&
7050           ((layout.current_row + 1) * layout.cols + layout.current_col < n_workspaces))
7051         ops |= META_MENU_OP_MOVE_DOWN;
7052     }
7053 
7054   meta_screen_free_workspace_layout (&layout);
7055 
7056   if (META_WINDOW_MAXIMIZED (window))
7057     ops |= META_MENU_OP_UNMAXIMIZE;
7058   else
7059     ops |= META_MENU_OP_MAXIMIZE;
7060 
7061 #if 0
7062   if (window->shaded)
7063     ops |= META_MENU_OP_UNSHADE;
7064   else
7065     ops |= META_MENU_OP_SHADE;
7066 #endif
7067 
7068   ops |= META_MENU_OP_UNSTICK;
7069   ops |= META_MENU_OP_STICK;
7070 
7071   if (window->wm_state_above)
7072     ops |= META_MENU_OP_UNABOVE;
7073   else
7074     ops |= META_MENU_OP_ABOVE;
7075 
7076   if (!window->has_maximize_func)
7077     insensitive |= META_MENU_OP_UNMAXIMIZE | META_MENU_OP_MAXIMIZE;
7078 
7079   if (!window->has_minimize_func)
7080     insensitive |= META_MENU_OP_MINIMIZE;
7081 
7082   /*if (!window->has_close_func)
7083     insensitive |= META_MENU_OP_DELETE;*/
7084 
7085   if (!window->has_shade_func)
7086     insensitive |= META_MENU_OP_SHADE | META_MENU_OP_UNSHADE;
7087 
7088   if (!META_WINDOW_ALLOWS_MOVE (window))
7089     insensitive |= META_MENU_OP_MOVE;
7090 
7091   if (!META_WINDOW_ALLOWS_RESIZE (window))
7092     insensitive |= META_MENU_OP_RESIZE;
7093 
7094    if (window->always_sticky)
7095      insensitive |= META_MENU_OP_STICK | META_MENU_OP_UNSTICK | META_MENU_OP_WORKSPACES;
7096 
7097   if ((window->type == META_WINDOW_DESKTOP) ||
7098       (window->type == META_WINDOW_DOCK) ||
7099       (window->type == META_WINDOW_SPLASHSCREEN))
7100     insensitive |= META_MENU_OP_ABOVE | META_MENU_OP_UNABOVE;
7101 
7102   /* If all operations are disabled, just quit without showing the menu.
7103    * This is the case, for example, with META_WINDOW_DESKTOP windows.
7104    */
7105   if ((ops & ~insensitive) == 0)
7106     return;
7107 
7108   menu =
7109     meta_ui_window_menu_new (window->screen->ui,
7110                              window->xwindow,
7111                              ops,
7112                              insensitive,
7113                              meta_window_get_net_wm_desktop (window),
7114                              meta_screen_get_n_workspaces (window->screen),
7115                              menu_callback,
7116                              NULL);
7117 
7118   window->display->window_menu = menu;
7119   window->display->window_with_menu = window;
7120 
7121   meta_verbose ("Popping up window menu for %s\n", window->desc);
7122 
7123   meta_ui_window_menu_popup (menu, root_x, root_y, button, timestamp);
7124 }
7125 
7126 void
meta_window_shove_titlebar_onscreen(MetaWindow * window)7127 meta_window_shove_titlebar_onscreen (MetaWindow *window)
7128 {
7129   MetaRectangle  outer_rect;
7130   GList         *onscreen_region;
7131   int            horiz_amount, vert_amount;
7132   int            newx, newy;
7133 
7134   /* If there's no titlebar, don't bother */
7135   if (!window->frame)
7136     return;
7137 
7138   /* Get the basic info we need */
7139   meta_window_get_outer_rect (window, &outer_rect);
7140   onscreen_region = window->screen->active_workspace->screen_region;
7141 
7142   /* Extend the region (just in case the window is too big to fit on the
7143    * screen), then shove the window on screen, then return the region to
7144    * normal.
7145    */
7146   horiz_amount = outer_rect.width;
7147   vert_amount  = outer_rect.height;
7148   meta_rectangle_expand_region (onscreen_region,
7149                                 horiz_amount,
7150                                 horiz_amount,
7151                                 0,
7152                                 vert_amount);
7153   meta_rectangle_shove_into_region(onscreen_region,
7154                                    FIXED_DIRECTION_X,
7155                                    &outer_rect);
7156   meta_rectangle_expand_region (onscreen_region,
7157                                 -horiz_amount,
7158                                 -horiz_amount,
7159                                 0,
7160                                 -vert_amount);
7161 
7162   newx = outer_rect.x + window->frame->child_x;
7163   newy = outer_rect.y + window->frame->child_y;
7164   meta_window_move_resize (window,
7165                            FALSE,
7166                            newx,
7167                            newy,
7168                            window->rect.width,
7169                            window->rect.height);
7170 }
7171 
7172 gboolean
meta_window_titlebar_is_onscreen(MetaWindow * window)7173 meta_window_titlebar_is_onscreen (MetaWindow *window)
7174 {
7175   MetaRectangle  titlebar_rect;
7176   GList         *onscreen_region;
7177   gboolean       is_onscreen;
7178 
7179   const int min_height_needed  = 8;
7180   const int min_width_percent  = 0.5;
7181   const int min_width_absolute = 50;
7182 
7183   /* Titlebar can't be offscreen if there is no titlebar... */
7184   if (!window->frame)
7185     return TRUE;
7186 
7187   /* Get the rectangle corresponding to the titlebar */
7188   meta_window_get_outer_rect (window, &titlebar_rect);
7189   titlebar_rect.height = window->frame->child_y;
7190 
7191   /* Run through the spanning rectangles for the screen and see if one of
7192    * them overlaps with the titlebar sufficiently to consider it onscreen.
7193    */
7194   is_onscreen = FALSE;
7195   onscreen_region = window->screen->active_workspace->screen_region;
7196   while (onscreen_region)
7197     {
7198       MetaRectangle *spanning_rect = onscreen_region->data;
7199       MetaRectangle overlap;
7200 
7201       meta_rectangle_intersect (&titlebar_rect, spanning_rect, &overlap);
7202       if (overlap.height > MIN (titlebar_rect.height, min_height_needed) &&
7203           overlap.width  > MIN (titlebar_rect.width * min_width_percent,
7204                                 min_width_absolute))
7205         {
7206           is_onscreen = TRUE;
7207           break;
7208         }
7209 
7210       onscreen_region = onscreen_region->next;
7211     }
7212 
7213   return is_onscreen;
7214 }
7215 
7216 static double
time_diff(const gint64 first,const gint64 second)7217 time_diff (const gint64 first,
7218            const gint64 second)
7219 {
7220   return (first - second) / 1000.0;
7221 }
7222 
7223 static gboolean
check_moveresize_frequency(MetaWindow * window,gdouble * remaining)7224 check_moveresize_frequency (MetaWindow *window,
7225 			    gdouble    *remaining)
7226 {
7227   gint64 current_time;
7228   current_time = g_get_real_time ();
7229 
7230 #ifdef HAVE_XSYNC
7231   if (!window->disable_sync &&
7232       window->display->grab_sync_request_alarm != None)
7233     {
7234       if (window->sync_request_time != 0)
7235         {
7236           double elapsed =
7237             time_diff (current_time, window->sync_request_time);
7238 
7239 	  if (elapsed < 1000.0)
7240 	    {
7241 	      /* We want to be sure that the timeout happens at
7242 	       * a time where elapsed will definitely be
7243 	       * greater than 1000, so we can disable sync
7244 	       */
7245 	      if (remaining)
7246 		*remaining = 1000.0 - elapsed + 100;
7247 
7248 	      return FALSE;
7249 	    }
7250 	  else
7251 	    {
7252 	      /* We have now waited for more than a second for the
7253 	       * application to respond to the sync request
7254 	       */
7255 	      window->disable_sync = TRUE;
7256 	      return TRUE;
7257 	    }
7258 	}
7259       else
7260 	{
7261 	  /* No outstanding sync requests. Go ahead and resize
7262 	   */
7263 	  return TRUE;
7264 	}
7265     }
7266   else
7267 #endif /* HAVE_XSYNC */
7268     {
7269       const double max_resizes_per_second = 25.0;
7270       const double ms_between_resizes = 1000.0 / max_resizes_per_second;
7271       double elapsed;
7272 
7273       elapsed = time_diff (current_time, window->display->grab_last_moveresize_time);
7274 
7275       if (elapsed >= 0.0 && elapsed < ms_between_resizes)
7276 	{
7277 	  meta_topic (META_DEBUG_RESIZING,
7278 		      "Delaying move/resize as only %g of %g ms elapsed\n",
7279 		      elapsed, ms_between_resizes);
7280 
7281 	  if (remaining)
7282 	    *remaining = (ms_between_resizes - elapsed);
7283 
7284 	  return FALSE;
7285 	}
7286 
7287       meta_topic (META_DEBUG_RESIZING,
7288 		  " Checked moveresize freq, allowing move/resize now (%g of %g seconds elapsed)\n",
7289 		  elapsed / 1000.0, 1.0 / max_resizes_per_second);
7290 
7291       return TRUE;
7292     }
7293 }
7294 
7295 static gboolean
update_move_timeout(gpointer data)7296 update_move_timeout (gpointer data)
7297 {
7298   MetaWindow *window = data;
7299 
7300   update_move (window,
7301                window->display->grab_last_user_action_was_snap,
7302                window->display->grab_latest_motion_x,
7303                window->display->grab_latest_motion_y);
7304 
7305   return FALSE;
7306 }
7307 
7308 static void
update_move(MetaWindow * window,gboolean snap,int x,int y)7309 update_move (MetaWindow  *window,
7310              gboolean     snap,
7311              int          x,
7312              int          y)
7313 {
7314   int dx, dy;
7315   int new_x, new_y;
7316   MetaRectangle old;
7317   int shake_threshold;
7318   MetaDisplay *display = window->display;
7319 
7320   display->grab_latest_motion_x = x;
7321   display->grab_latest_motion_y = y;
7322 
7323   dx = x - display->grab_anchor_root_x;
7324   dy = y - display->grab_anchor_root_y;
7325 
7326   new_x = display->grab_anchor_window_pos.x + dx;
7327   new_y = display->grab_anchor_window_pos.y + dy;
7328 
7329   meta_verbose ("x,y = %d,%d anchor ptr %d,%d anchor pos %d,%d dx,dy %d,%d\n",
7330                 x, y,
7331                 display->grab_anchor_root_x,
7332                 display->grab_anchor_root_y,
7333                 display->grab_anchor_window_pos.x,
7334                 display->grab_anchor_window_pos.y,
7335                 dx, dy);
7336 
7337   /* Don't bother doing anything if no move has been specified.  (This
7338    * happens often, even in keyboard moving, due to the warping of the
7339    * pointer.
7340    */
7341   if (dx == 0 && dy == 0)
7342     return;
7343 
7344   /* Originally for detaching maximized windows, but we use this
7345    * for the zones at the sides of the monitor where trigger tiling
7346    * because it's about the right size
7347    */
7348 
7349 #define DRAG_THRESHOLD_TO_SHAKE_THRESHOLD_FACTOR 2
7350   shake_threshold = meta_ui_get_drag_threshold (window->screen->ui) *
7351     DRAG_THRESHOLD_TO_SHAKE_THRESHOLD_FACTOR;
7352 
7353   if (snap)
7354     {
7355       /* We don't want to tile while snapping. Also, clear any previous tile
7356          request. */
7357       window->tile_mode = META_TILE_NONE;
7358       window->tile_monitor_number = -1;
7359       window->tile_cycle = META_TILE_CYCLE_NONE;
7360     }
7361   else if (meta_prefs_get_allow_tiling () &&
7362            !META_WINDOW_MAXIMIZED (window) &&
7363            !META_WINDOW_TILED (window))
7364     {
7365       const MetaXineramaScreenInfo *monitor;
7366       MetaRectangle work_area;
7367 
7368       /* For side-by-side tiling we are interested in the inside vertical
7369        * edges of the work area of the monitor where the pointer is located,
7370        * and in the outside top edge for maximized tiling.
7371        *
7372        * For maximized tiling we use the outside edge instead of the
7373        * inside edge, because we don't want to force users to maximize
7374        * windows they are placing near the top of their screens.
7375        *
7376        * The "current" idea of meta_window_get_work_area_for_xinerama() and
7377        * meta_screen_get_current_xinerama() is slightly different: the former
7378        * refers to the monitor which contains the largest part of the window,
7379        * the latter to the one where the pointer is located.
7380        */
7381       monitor = meta_screen_get_current_xinerama (window->screen);
7382       meta_window_get_work_area_for_xinerama (window,
7383                                               monitor->number,
7384                                               &work_area);
7385 
7386       /* Check if the cursor is in a position which triggers tiling
7387        * and set tile_mode accordingly.
7388        */
7389       MetaTileMode old_tile_mode = window->tile_mode;
7390       window->tile_mode = calculate_tiling_mode(x, y, window, monitor,
7391                                                 work_area, shake_threshold);
7392 
7393       if (window->tile_mode != META_TILE_NONE)
7394         window->tile_monitor_number = monitor->number;
7395 
7396       /* Reset resized and cycle flags when changing tile mode */
7397       if (old_tile_mode != window->tile_mode)
7398         {
7399           window->tile_resized = FALSE;
7400           window->tile_cycle = META_TILE_CYCLE_NONE;
7401         }
7402     }
7403 
7404   /* shake loose (unmaximize) maximized or tiled window if dragged beyond
7405    * the threshold in the Y direction. Tiled windows can also be pulled
7406    * loose via X motion.
7407    */
7408 
7409   if ((META_WINDOW_MAXIMIZED (window) && ABS (dy) >= shake_threshold) ||
7410       (META_WINDOW_TILED (window) && (MAX (ABS (dx), ABS (dy)) >= shake_threshold)))
7411     {
7412       double prop;
7413 
7414       /* Shake loose, so that the window snaps back to maximized
7415        * when dragged near the top; do not snap back if the window
7416        * was tiled.
7417        */
7418       window->shaken_loose = META_WINDOW_MAXIMIZED (window);
7419       window->tile_mode = META_TILE_NONE;
7420       window->tile_cycle = META_TILE_CYCLE_NONE;
7421       window->tiled = FALSE;
7422 
7423       /* move the unmaximized window to the cursor */
7424       prop =
7425         ((double)(x - display->grab_initial_window_pos.x)) /
7426         ((double)display->grab_initial_window_pos.width);
7427 
7428       display->grab_initial_window_pos.x =
7429         x - window->saved_rect.width * prop;
7430       display->grab_initial_window_pos.y = y;
7431 
7432       if (window->frame)
7433         {
7434           display->grab_initial_window_pos.y += window->frame->child_y / 2;
7435         }
7436 
7437       window->saved_rect.x = display->grab_initial_window_pos.x;
7438       window->saved_rect.y = display->grab_initial_window_pos.y;
7439       display->grab_anchor_root_x = x;
7440       display->grab_anchor_root_y = y;
7441 
7442       meta_window_unmaximize (window,
7443                               META_MAXIMIZE_HORIZONTAL |
7444                               META_MAXIMIZE_VERTICAL);
7445 
7446       return;
7447     }
7448   /* remaximize window on an other xinerama monitor if window has
7449    * been shaken loose or it is still maximized (then move straight)
7450    */
7451   else if (window->shaken_loose || META_WINDOW_MAXIMIZED (window))
7452     {
7453       const MetaXineramaScreenInfo *wxinerama;
7454       MetaRectangle work_area;
7455       int monitor;
7456 
7457       wxinerama = meta_screen_get_xinerama_for_window (window->screen, window);
7458 
7459       for (monitor = 0; monitor < window->screen->n_xinerama_infos; monitor++)
7460         {
7461           meta_window_get_work_area_for_xinerama (window, monitor, &work_area);
7462 
7463           /* check if cursor is near the top of a xinerama work area */
7464           if (x >= work_area.x + shake_threshold &&
7465               x < (work_area.x + work_area.width - shake_threshold) &&
7466               y >= work_area.y &&
7467               y < (work_area.y + shake_threshold))
7468             {
7469               /* move the saved rect if window will become maximized on an
7470                * other monitor so user isn't surprised on a later unmaximize
7471                */
7472               if (wxinerama->number != monitor)
7473                 {
7474                   window->saved_rect.x = work_area.x;
7475                   window->saved_rect.y = work_area.y;
7476 
7477                   if (window->frame)
7478                     {
7479                       window->saved_rect.x += window->frame->child_x;
7480                       window->saved_rect.y += window->frame->child_y;
7481                     }
7482 
7483                   window->user_rect.x = window->saved_rect.x;
7484                   window->user_rect.y = window->saved_rect.y;
7485 
7486                   meta_window_unmaximize (window,
7487                                           META_MAXIMIZE_HORIZONTAL |
7488                                           META_MAXIMIZE_VERTICAL);
7489                 }
7490 
7491               display->grab_initial_window_pos = work_area;
7492               display->grab_anchor_root_x = x;
7493               display->grab_anchor_root_y = y;
7494               window->shaken_loose = FALSE;
7495 
7496               window->tile_mode = META_TILE_NONE;
7497               window->tile_cycle = META_TILE_CYCLE_NONE;
7498 
7499               meta_window_maximize (window,
7500                                     META_MAXIMIZE_HORIZONTAL |
7501                                     META_MAXIMIZE_VERTICAL);
7502 
7503               return;
7504             }
7505         }
7506     }
7507 
7508   /* Delay showing the tile preview slightly to make it more unlikely to
7509    * trigger it unwittingly, e.g. when shaking loose the window or moving
7510    * it to another monitor.
7511    */
7512 
7513   meta_screen_tile_preview_update (window->screen,
7514                                    window->tile_mode != META_TILE_NONE);
7515 
7516   if (display->grab_wireframe_active)
7517     old = display->grab_wireframe_rect;
7518   else
7519     meta_window_get_client_root_coords (window, &old);
7520 
7521   /* Don't allow movement in the maximized directions or while tiled */
7522 
7523   if (window->maximized_horizontally || META_WINDOW_TILED (window))
7524     {
7525       new_x = old.x;
7526     }
7527 
7528   if (window->maximized_vertically || META_WINDOW_CORNER_TILED(window))
7529     {
7530       new_y = old.y;
7531     }
7532 
7533   /* Do any edge resistance/snapping */
7534   meta_window_edge_resistance_for_move (window,
7535                                         old.x,
7536                                         old.y,
7537                                         &new_x,
7538                                         &new_y,
7539                                         update_move_timeout,
7540                                         snap,
7541                                         FALSE);
7542 
7543   if (display->compositor)
7544     {
7545       int root_x = new_x - display->grab_anchor_window_pos.x + display->grab_anchor_root_x;
7546       int root_y = new_y - display->grab_anchor_window_pos.y + display->grab_anchor_root_y;
7547 
7548       meta_compositor_update_move (display->compositor,
7549 				   window, root_x, root_y);
7550     }
7551 
7552   if (display->grab_wireframe_active)
7553     meta_window_update_wireframe (window, new_x, new_y,
7554                                   display->grab_wireframe_rect.width,
7555                                   display->grab_wireframe_rect.height);
7556   else
7557     meta_window_move (window, TRUE, new_x, new_y);
7558 }
7559 
calculate_tiling_mode(int x,int y,MetaWindow * window,const MetaXineramaScreenInfo * monitor,MetaRectangle work_area,int shake_threshold)7560 static MetaTileMode calculate_tiling_mode(int x,
7561                                           int y,
7562                                           MetaWindow *window,
7563                                           const MetaXineramaScreenInfo *monitor,
7564                                           MetaRectangle work_area,
7565                                           int shake_threshold)
7566 {
7567   if (meta_window_can_tile (window) &&
7568       x >= monitor->rect.x
7569       && x < (work_area.x + shake_threshold))
7570     {
7571       if(y >= monitor->rect.y && y < work_area.y + shake_threshold)
7572         return META_TILE_TOP_LEFT;
7573       else if(y < monitor->rect.y + monitor->rect.height
7574               && y > work_area.y + work_area.height - shake_threshold)
7575         return META_TILE_BOTTOM_LEFT;
7576       else
7577         return META_TILE_LEFT;
7578     }
7579 
7580   else if (meta_window_can_tile (window) &&
7581            x >= work_area.x + work_area.width - shake_threshold &&
7582            x < (monitor->rect.x + monitor->rect.width))
7583     {
7584       if(y >= monitor->rect.y && y < work_area.y + shake_threshold)
7585         return META_TILE_TOP_RIGHT;
7586       else if(y < monitor->rect.y + monitor->rect.height
7587               && y > work_area.y + work_area.height - shake_threshold)
7588         return META_TILE_BOTTOM_RIGHT;
7589       else
7590         return META_TILE_RIGHT;
7591     }
7592   else if (meta_window_can_tile_maximized (window) &&
7593            y >= monitor->rect.y && y <= work_area.y &&
7594            meta_prefs_get_allow_top_tiling ())
7595     return META_TILE_MAXIMIZED;
7596   else
7597     return META_TILE_NONE;
7598 
7599 }
7600 
7601 static gboolean
update_resize_timeout(gpointer data)7602 update_resize_timeout (gpointer data)
7603 {
7604   MetaWindow *window = data;
7605 
7606   update_resize (window,
7607                  window->display->grab_last_user_action_was_snap,
7608                  window->display->grab_latest_motion_x,
7609                  window->display->grab_latest_motion_y,
7610                  TRUE);
7611   return FALSE;
7612 }
7613 
7614 static void
update_resize(MetaWindow * window,gboolean snap,int x,int y,gboolean force)7615 update_resize (MetaWindow *window,
7616                gboolean    snap,
7617                int x, int y,
7618                gboolean force)
7619 {
7620   int dx, dy;
7621   int new_w, new_h;
7622   int gravity;
7623   MetaRectangle old;
7624   int new_x, new_y;
7625   double remaining;
7626 
7627   window->display->grab_latest_motion_x = x;
7628   window->display->grab_latest_motion_y = y;
7629 
7630   dx = x - window->display->grab_anchor_root_x;
7631   dy = y - window->display->grab_anchor_root_y;
7632 
7633   new_w = window->display->grab_anchor_window_pos.width;
7634   new_h = window->display->grab_anchor_window_pos.height;
7635 
7636   /* Don't bother doing anything if no move has been specified.  (This
7637    * happens often, even in keyboard resizing, due to the warping of the
7638    * pointer.
7639    */
7640   if (dx == 0 && dy == 0)
7641     return;
7642 
7643   /* FIXME this is only used in wireframe mode */
7644   new_x = window->display->grab_anchor_window_pos.x;
7645   new_y = window->display->grab_anchor_window_pos.y;
7646 
7647   if (window->display->grab_op == META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN)
7648     {
7649       if ((dx > 0) && (dy > 0))
7650         {
7651           window->display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_SE;
7652           meta_window_update_keyboard_resize (window, TRUE);
7653         }
7654       else if ((dx < 0) && (dy > 0))
7655         {
7656           window->display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_SW;
7657           meta_window_update_keyboard_resize (window, TRUE);
7658         }
7659       else if ((dx > 0) && (dy < 0))
7660         {
7661           window->display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_NE;
7662           meta_window_update_keyboard_resize (window, TRUE);
7663         }
7664       else if ((dx < 0) && (dy < 0))
7665         {
7666           window->display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_NW;
7667           meta_window_update_keyboard_resize (window, TRUE);
7668         }
7669       else if (dx < 0)
7670         {
7671           window->display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_W;
7672           meta_window_update_keyboard_resize (window, TRUE);
7673         }
7674       else if (dx > 0)
7675         {
7676           window->display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_E;
7677           meta_window_update_keyboard_resize (window, TRUE);
7678         }
7679       else if (dy > 0)
7680         {
7681           window->display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_S;
7682           meta_window_update_keyboard_resize (window, TRUE);
7683         }
7684       else if (dy < 0)
7685         {
7686           window->display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_N;
7687           meta_window_update_keyboard_resize (window, TRUE);
7688         }
7689     }
7690 
7691   /* FIXME: This stupidity only needed because of wireframe mode and
7692    * the fact that wireframe isn't making use of
7693    * meta_rectangle_resize_with_gravity().  If we were to use that, we
7694    * could just increment new_w and new_h by dx and dy in all cases.
7695    */
7696   switch (window->display->grab_op)
7697     {
7698     case META_GRAB_OP_RESIZING_SE:
7699     case META_GRAB_OP_RESIZING_NE:
7700     case META_GRAB_OP_RESIZING_E:
7701     case META_GRAB_OP_KEYBOARD_RESIZING_SE:
7702     case META_GRAB_OP_KEYBOARD_RESIZING_NE:
7703     case META_GRAB_OP_KEYBOARD_RESIZING_E:
7704       new_w += dx;
7705       break;
7706 
7707     case META_GRAB_OP_RESIZING_NW:
7708     case META_GRAB_OP_RESIZING_SW:
7709     case META_GRAB_OP_RESIZING_W:
7710     case META_GRAB_OP_KEYBOARD_RESIZING_NW:
7711     case META_GRAB_OP_KEYBOARD_RESIZING_SW:
7712     case META_GRAB_OP_KEYBOARD_RESIZING_W:
7713       new_w -= dx;
7714       new_x += dx;
7715       break;
7716 
7717     default:
7718       break;
7719     }
7720 
7721   switch (window->display->grab_op)
7722     {
7723     case META_GRAB_OP_RESIZING_SE:
7724     case META_GRAB_OP_RESIZING_S:
7725     case META_GRAB_OP_RESIZING_SW:
7726     case META_GRAB_OP_KEYBOARD_RESIZING_SE:
7727     case META_GRAB_OP_KEYBOARD_RESIZING_S:
7728     case META_GRAB_OP_KEYBOARD_RESIZING_SW:
7729       new_h += dy;
7730       break;
7731 
7732     case META_GRAB_OP_RESIZING_N:
7733     case META_GRAB_OP_RESIZING_NE:
7734     case META_GRAB_OP_RESIZING_NW:
7735     case META_GRAB_OP_KEYBOARD_RESIZING_N:
7736     case META_GRAB_OP_KEYBOARD_RESIZING_NE:
7737     case META_GRAB_OP_KEYBOARD_RESIZING_NW:
7738       new_h -= dy;
7739       new_y += dy;
7740       break;
7741     default:
7742       break;
7743     }
7744 
7745   if (!check_moveresize_frequency (window, &remaining) && !force)
7746     {
7747       /* we are ignoring an event here, so we schedule a
7748        * compensation event when we would otherwise not ignore
7749        * an event. Otherwise we can become stuck if the user never
7750        * generates another event.
7751        */
7752       if (!window->display->grab_resize_timeout_id)
7753 	{
7754 	  window->display->grab_resize_timeout_id =
7755 	    g_timeout_add ((int)remaining, update_resize_timeout, window);
7756 	}
7757 
7758       return;
7759     }
7760 
7761   /* If we get here, it means the client should have redrawn itself */
7762   if (window->display->compositor)
7763     meta_compositor_set_updates (window->display->compositor, window, TRUE);
7764 
7765   /* Remove any scheduled compensation events */
7766   if (window->display->grab_resize_timeout_id)
7767     {
7768       g_source_remove (window->display->grab_resize_timeout_id);
7769       window->display->grab_resize_timeout_id = 0;
7770     }
7771 
7772   if (window->display->grab_wireframe_active)
7773     old = window->display->grab_wireframe_rect;
7774   else
7775     old = window->rect;  /* Don't actually care about x,y */
7776 
7777   /* One sided resizing ought to actually be one-sided, despite the fact that
7778    * aspect ratio windows don't interact nicely with the above stuff.  So,
7779    * to avoid some nasty flicker, we enforce that.
7780    */
7781   switch (window->display->grab_op)
7782     {
7783     case META_GRAB_OP_RESIZING_S:
7784     case META_GRAB_OP_RESIZING_N:
7785       new_w = old.width;
7786       break;
7787 
7788     case META_GRAB_OP_RESIZING_E:
7789     case META_GRAB_OP_RESIZING_W:
7790       new_h = old.height;
7791       break;
7792 
7793     default:
7794       break;
7795     }
7796 
7797   /* compute gravity of client during operation */
7798   gravity = meta_resize_gravity_from_grab_op (window->display->grab_op);
7799   g_assert (gravity >= 0);
7800 
7801   /* Do any edge resistance/snapping */
7802   meta_window_edge_resistance_for_resize (window,
7803                                           old.width,
7804                                           old.height,
7805                                           &new_w,
7806                                           &new_h,
7807                                           gravity,
7808                                           update_resize_timeout,
7809                                           snap,
7810                                           FALSE);
7811 
7812   if (window->display->grab_wireframe_active)
7813     {
7814       if ((new_x + new_w <= new_x) || (new_y + new_h <= new_y))
7815         return;
7816 
7817       /* FIXME This is crap. For example, the wireframe isn't
7818        * constrained in the way that a real resize would be. An
7819        * obvious elegant solution is to unmap the window during
7820        * wireframe, but still resize it; however, that probably
7821        * confuses broken clients that have problems with opaque
7822        * resize, they probably don't track their visibility.
7823        */
7824       meta_window_update_wireframe (window, new_x, new_y, new_w, new_h);
7825     }
7826   else
7827     {
7828       /* We don't need to update unless the specified width and height
7829        * are actually different from what we had before.
7830        */
7831       if (old.width != new_w || old.height != new_h)
7832         meta_window_resize_with_gravity (window, TRUE, new_w, new_h, gravity);
7833     }
7834 
7835   /* Store the latest resize time, if we actually resized. */
7836   if (window->rect.width != old.width || window->rect.height != old.height)
7837     window->display->grab_last_moveresize_time = g_get_real_time ();
7838 }
7839 
7840 typedef struct
7841 {
7842   const XEvent *current_event;
7843   int           count;
7844   guint32       last_time;
7845 } EventScannerData;
7846 
7847 static Bool
find_last_time_predicate(Display * display,XEvent * xevent,XPointer arg)7848 find_last_time_predicate (Display  *display,
7849                           XEvent   *xevent,
7850                           XPointer  arg)
7851 {
7852   EventScannerData *esd = (void*) arg;
7853 
7854   if (esd->current_event->type == xevent->type &&
7855       esd->current_event->xany.window == xevent->xany.window)
7856     {
7857       esd->count += 1;
7858       esd->last_time = xevent->xmotion.time;
7859     }
7860 
7861   return False;
7862 }
7863 
7864 static gboolean
check_use_this_motion_notify(MetaWindow * window,XEvent * event)7865 check_use_this_motion_notify (MetaWindow *window,
7866                               XEvent     *event)
7867 {
7868   EventScannerData esd;
7869   XEvent useless;
7870 
7871   /* This code is copied from Owen's GDK code. */
7872 
7873   if (window->display->grab_motion_notify_time != 0)
7874     {
7875       /* == is really the right test, but I'm all for paranoia */
7876       if (window->display->grab_motion_notify_time <=
7877           event->xmotion.time)
7878         {
7879           meta_topic (META_DEBUG_RESIZING,
7880                       "Arrived at event with time %u (waiting for %u), using it\n",
7881                       (unsigned int)event->xmotion.time,
7882                       window->display->grab_motion_notify_time);
7883 
7884           window->display->grab_motion_notify_time = 0;
7885           return TRUE;
7886         }
7887       else
7888         return FALSE; /* haven't reached the saved timestamp yet */
7889     }
7890 
7891   esd.current_event = event;
7892   esd.count = 0;
7893   esd.last_time = 0;
7894 
7895   /* "useless" isn't filled in because the predicate never returns True */
7896   XCheckIfEvent (window->display->xdisplay,
7897                  &useless,
7898                  find_last_time_predicate,
7899                  (XPointer) &esd);
7900 
7901   if (esd.count > 0)
7902     meta_topic (META_DEBUG_RESIZING,
7903                 "Will skip %d motion events and use the event with time %u\n",
7904                 esd.count, (unsigned int) esd.last_time);
7905 
7906   if (esd.last_time == 0)
7907     return TRUE;
7908   else
7909     {
7910       /* Save this timestamp, and ignore all motion notify
7911        * until we get to the one with this stamp.
7912        */
7913       window->display->grab_motion_notify_time = esd.last_time;
7914       return FALSE;
7915     }
7916 }
7917 
7918 static void
update_tile_mode(MetaWindow * window)7919 update_tile_mode (MetaWindow *window)
7920 {
7921   switch (window->tile_mode)
7922     {
7923       case META_TILE_LEFT:
7924       case META_TILE_RIGHT:
7925           if (!META_WINDOW_SIDE_TILED (window))
7926               window->tile_mode = META_TILE_NONE;
7927           break;
7928     }
7929 }
7930 
7931 void
meta_window_handle_mouse_grab_op_event(MetaWindow * window,XEvent * event)7932 meta_window_handle_mouse_grab_op_event (MetaWindow *window,
7933                                         XEvent     *event)
7934 {
7935 #ifdef HAVE_XSYNC
7936   if (event->type == (window->display->xsync_event_base + XSyncAlarmNotify))
7937     {
7938       meta_topic (META_DEBUG_RESIZING,
7939                   "Alarm event received last motion x = %d y = %d\n",
7940                   window->display->grab_latest_motion_x,
7941                   window->display->grab_latest_motion_y);
7942 
7943       /* If sync was previously disabled, turn it back on and hope
7944        * the application has come to its senses (maybe it was just
7945        * busy with a pagefault or a long computation).
7946        */
7947       window->disable_sync = FALSE;
7948       window->sync_request_time = 0;
7949 
7950       /* This means we are ready for another configure. */
7951       switch (window->display->grab_op)
7952         {
7953         case META_GRAB_OP_RESIZING_E:
7954         case META_GRAB_OP_RESIZING_W:
7955         case META_GRAB_OP_RESIZING_S:
7956         case META_GRAB_OP_RESIZING_N:
7957         case META_GRAB_OP_RESIZING_SE:
7958         case META_GRAB_OP_RESIZING_SW:
7959         case META_GRAB_OP_RESIZING_NE:
7960         case META_GRAB_OP_RESIZING_NW:
7961         case META_GRAB_OP_KEYBOARD_RESIZING_S:
7962         case META_GRAB_OP_KEYBOARD_RESIZING_N:
7963         case META_GRAB_OP_KEYBOARD_RESIZING_W:
7964         case META_GRAB_OP_KEYBOARD_RESIZING_E:
7965         case META_GRAB_OP_KEYBOARD_RESIZING_SE:
7966         case META_GRAB_OP_KEYBOARD_RESIZING_NE:
7967         case META_GRAB_OP_KEYBOARD_RESIZING_SW:
7968         case META_GRAB_OP_KEYBOARD_RESIZING_NW:
7969           /* no pointer round trip here, to keep in sync */
7970           update_resize (window,
7971                          window->display->grab_last_user_action_was_snap,
7972                          window->display->grab_latest_motion_x,
7973                          window->display->grab_latest_motion_y,
7974                          TRUE);
7975           break;
7976 
7977         default:
7978           break;
7979         }
7980     }
7981 #endif /* HAVE_XSYNC */
7982 
7983   switch (event->type)
7984     {
7985     case ButtonRelease:
7986       meta_display_check_threshold_reached (window->display,
7987                                             event->xbutton.x_root,
7988                                             event->xbutton.y_root);
7989       /* If the user was snap moving then ignore the button release
7990        * because they may have let go of shift before releasing the
7991        * mouse button and they almost certainly do not want a
7992        * non-snapped movement to occur from the button release.
7993        */
7994       if (!window->display->grab_last_user_action_was_snap)
7995         {
7996           if (meta_grab_op_is_moving (window->display->grab_op))
7997             {
7998               if (window->tile_mode == META_TILE_MAXIMIZED)
7999 	        {
8000                   meta_window_maximize (window, META_MAXIMIZE_VERTICAL |
8001                                                 META_MAXIMIZE_HORIZONTAL);
8002                   window->tile_mode = META_TILE_NONE;
8003 		}
8004 	      else if (window->tile_mode != META_TILE_NONE)
8005 	        {
8006 		  meta_window_tile (window);
8007 		}
8008 	      else if (event->xbutton.root == window->screen->xroot)
8009                 update_move (window, event->xbutton.state & ShiftMask,
8010                              event->xbutton.x_root, event->xbutton.y_root);
8011             }
8012           else if (meta_grab_op_is_resizing (window->display->grab_op))
8013             {
8014               if (event->xbutton.root == window->screen->xroot)
8015                 update_resize (window,
8016                                event->xbutton.state & ShiftMask,
8017                                event->xbutton.x_root,
8018                                event->xbutton.y_root,
8019                                TRUE);
8020 	      if (window->display->compositor)
8021 		meta_compositor_set_updates (window->display->compositor, window, TRUE);
8022 
8023               /* If a tiled window has been dragged free with a
8024                * mouse resize without snapping back to the tiled
8025                * state, it will end up with an inconsistent tile
8026                * mode on mouse release; cleaning the mode earlier
8027                * would break the ability to snap back to the tiled
8028                * state, so we wait until mouse release.
8029                */
8030               update_tile_mode (window);
8031             }
8032         }
8033 
8034       meta_display_end_grab_op (window->display, event->xbutton.time);
8035       break;
8036 
8037     case MotionNotify:
8038       meta_display_check_threshold_reached (window->display,
8039                                             event->xmotion.x_root,
8040                                             event->xmotion.y_root);
8041       if (meta_grab_op_is_moving (window->display->grab_op))
8042         {
8043           if (event->xmotion.root == window->screen->xroot)
8044             {
8045               if (check_use_this_motion_notify (window,
8046                                                 event))
8047                 update_move (window,
8048                              event->xmotion.state & ShiftMask,
8049                              event->xmotion.x_root,
8050                              event->xmotion.y_root);
8051             }
8052         }
8053       else if (meta_grab_op_is_resizing (window->display->grab_op))
8054         {
8055           if (event->xmotion.root == window->screen->xroot)
8056             {
8057               if (check_use_this_motion_notify (window,
8058                                                 event))
8059                 update_resize (window,
8060                                event->xmotion.state & ShiftMask,
8061                                event->xmotion.x_root,
8062                                event->xmotion.y_root,
8063                                FALSE);
8064             }
8065         }
8066       break;
8067 
8068     default:
8069       break;
8070     }
8071 }
8072 
8073 void
meta_window_set_gravity(MetaWindow * window,int gravity)8074 meta_window_set_gravity (MetaWindow *window,
8075                          int         gravity)
8076 {
8077   XSetWindowAttributes attrs;
8078 
8079   meta_verbose ("Setting gravity of %s to %d\n", window->desc, gravity);
8080 
8081   attrs.win_gravity = gravity;
8082 
8083   meta_error_trap_push (window->display);
8084 
8085   XChangeWindowAttributes (window->display->xdisplay,
8086                            window->xwindow,
8087                            CWWinGravity,
8088                            &attrs);
8089 
8090   meta_error_trap_pop (window->display, FALSE);
8091 }
8092 
8093 static void
get_work_area_xinerama(MetaWindow * window,MetaRectangle * area,int which_xinerama)8094 get_work_area_xinerama (MetaWindow    *window,
8095                         MetaRectangle *area,
8096                         int            which_xinerama)
8097 {
8098   GList *tmp;
8099 
8100   g_assert (which_xinerama >= 0);
8101 
8102   /* Initialize to the whole xinerama */
8103   *area = window->screen->xinerama_infos[which_xinerama].rect;
8104 
8105   tmp = meta_window_get_workspaces (window);
8106   while (tmp != NULL)
8107     {
8108       MetaRectangle workspace_work_area;
8109       meta_workspace_get_work_area_for_xinerama (tmp->data,
8110                                                  which_xinerama,
8111                                                  &workspace_work_area);
8112       meta_rectangle_intersect (area,
8113                                 &workspace_work_area,
8114                                 area);
8115       tmp = tmp->next;
8116     }
8117 
8118   meta_topic (META_DEBUG_WORKAREA,
8119               "Window %s xinerama %d has work area %d,%d %d x %d\n",
8120               window->desc, which_xinerama,
8121               area->x, area->y, area->width, area->height);
8122 }
8123 
8124 void
meta_window_get_work_area_current_xinerama(MetaWindow * window,MetaRectangle * area)8125 meta_window_get_work_area_current_xinerama (MetaWindow    *window,
8126 					    MetaRectangle *area)
8127 {
8128   const MetaXineramaScreenInfo *xinerama = NULL;
8129   xinerama = meta_screen_get_xinerama_for_window (window->screen,
8130 						  window);
8131 
8132   meta_window_get_work_area_for_xinerama (window,
8133                                           xinerama->number,
8134                                           area);
8135 }
8136 
8137 void
meta_window_get_work_area_for_xinerama(MetaWindow * window,int which_xinerama,MetaRectangle * area)8138 meta_window_get_work_area_for_xinerama (MetaWindow    *window,
8139 					int            which_xinerama,
8140 					MetaRectangle *area)
8141 {
8142   g_return_if_fail (which_xinerama >= 0);
8143 
8144   get_work_area_xinerama (window,
8145                           area,
8146                           which_xinerama);
8147 }
8148 
8149 void
meta_window_get_work_area_all_xineramas(MetaWindow * window,MetaRectangle * area)8150 meta_window_get_work_area_all_xineramas (MetaWindow    *window,
8151                                          MetaRectangle *area)
8152 {
8153   GList *tmp;
8154 
8155   /* Initialize to the whole screen */
8156   *area = window->screen->rect;
8157 
8158   tmp = meta_window_get_workspaces (window);
8159   while (tmp != NULL)
8160     {
8161       MetaRectangle workspace_work_area;
8162       meta_workspace_get_work_area_all_xineramas (tmp->data,
8163                                                   &workspace_work_area);
8164       meta_rectangle_intersect (area,
8165                                 &workspace_work_area,
8166                                 area);
8167       tmp = tmp->next;
8168     }
8169 
8170   meta_topic (META_DEBUG_WORKAREA,
8171               "Window %s has whole-screen work area %d,%d %d x %d\n",
8172               window->desc, area->x, area->y, area->width, area->height);
8173 }
8174 
8175 void
meta_window_get_current_tile_area(MetaWindow * window,MetaRectangle * tile_area)8176 meta_window_get_current_tile_area (MetaWindow    *window,
8177                                    MetaRectangle *tile_area)
8178 {
8179   int tile_monitor_number;
8180   int width;
8181   double tile_ratio;
8182 
8183   g_return_if_fail (window->tile_mode != META_TILE_NONE);
8184 
8185   /* I don't know how to detect monitor configuration changes, so I have to take into account that
8186    * tile_monitor_number might be invalid. If this happens, I replace it with whatever monitor
8187    * the window is currently on. This is usually the correct monitor anyway, only in some special
8188    * cases is the real monitor number actually required (e.g. the window is being moved with the mouse but
8189    * is still mostly on the wrong monitor).
8190    */
8191   if (window->tile_monitor_number >= window->screen->n_xinerama_infos)
8192     {
8193       window->tile_monitor_number = meta_screen_get_xinerama_for_window (window->screen, window)->number;
8194     }
8195 
8196   tile_monitor_number = window->tile_monitor_number;
8197   if (tile_monitor_number < 0)
8198     {
8199       meta_warning ("%s called with an invalid monitor number; using 0 instead\n", G_STRFUNC);
8200       tile_monitor_number = 0;
8201     }
8202 
8203   meta_window_get_work_area_for_xinerama (window, tile_monitor_number, tile_area);
8204 
8205   width = tile_area->width;
8206 
8207   switch (window->tile_cycle)
8208     {
8209       case META_TILE_CYCLE_33:
8210         tile_ratio = 1 / 3.0;
8211         break;
8212       case META_TILE_CYCLE_25:
8213         tile_ratio = 1 / 4.0;
8214         break;
8215       case META_TILE_CYCLE_100:
8216         tile_ratio = 1 / 1.0;
8217         break;
8218       case META_TILE_CYCLE_75:
8219         tile_ratio = 3 / 4.0;
8220         break;
8221       case META_TILE_CYCLE_66:
8222         tile_ratio = 2 / 3.0;
8223         break;
8224       case META_TILE_CYCLE_50:
8225       case META_TILE_CYCLE_NONE:
8226       default:
8227         tile_ratio = 1 / 2.0;
8228         break;
8229     }
8230 
8231   if (window->tile_mode != META_TILE_NONE  &&
8232       window->tile_mode != META_TILE_MAXIMIZED)
8233     width = (int)(tile_area->width * tile_ratio);
8234 
8235   if(window->tile_mode == META_TILE_BOTTOM_LEFT ||
8236      window->tile_mode == META_TILE_BOTTOM_RIGHT ||
8237      window->tile_mode == META_TILE_TOP_LEFT ||
8238      window->tile_mode == META_TILE_TOP_RIGHT)
8239     tile_area->height /= 2;
8240 
8241   if (window->tile_mode == META_TILE_RIGHT ||
8242       window->tile_mode == META_TILE_TOP_RIGHT ||
8243       window->tile_mode == META_TILE_BOTTOM_RIGHT)
8244     tile_area->x += tile_area->width - width;
8245 
8246   if(window->tile_mode == META_TILE_BOTTOM_LEFT ||
8247      window->tile_mode == META_TILE_BOTTOM_RIGHT)
8248     tile_area->y += tile_area->height;
8249 
8250   tile_area->width = width;
8251 }
8252 
8253 gboolean
meta_window_same_application(MetaWindow * window,MetaWindow * other_window)8254 meta_window_same_application (MetaWindow *window,
8255                               MetaWindow *other_window)
8256 {
8257   MetaGroup *group       = meta_window_get_group (window);
8258   MetaGroup *other_group = meta_window_get_group (other_window);
8259 
8260   return
8261     group!=NULL &&
8262     other_group!=NULL &&
8263     group==other_group;
8264 }
8265 
8266 /* Generally meta_window_same_application() is a better idea
8267  * of "sameness", since it handles the case where multiple apps
8268  * want to look like the same app or the same app wants to look
8269  * like multiple apps, but in the case of workarounds for legacy
8270  * applications (which likely aren't setting the group properly
8271  * anyways), it may be desirable to check this as well.
8272  */
8273 static gboolean
meta_window_same_client(MetaWindow * window,MetaWindow * other_window)8274 meta_window_same_client (MetaWindow *window,
8275                          MetaWindow *other_window)
8276 {
8277   int resource_mask = window->display->xdisplay->resource_mask;
8278 
8279   return ((window->xwindow & ~resource_mask) ==
8280           (other_window->xwindow & ~resource_mask));
8281 }
8282 
8283 void
meta_window_refresh_resize_popup(MetaWindow * window)8284 meta_window_refresh_resize_popup (MetaWindow *window)
8285 {
8286   if (window->display->grab_op == META_GRAB_OP_NONE)
8287     return;
8288 
8289   if (window->display->grab_window != window)
8290     return;
8291 
8292   /* We shouldn't ever get called when the wireframe is active
8293    * because that's handled by a different code path in effects.c
8294    */
8295   if (window->display->grab_wireframe_active)
8296     {
8297       meta_topic (META_DEBUG_WINDOW_OPS,
8298                   "refresh_resize_popup called when wireframe active\n");
8299       return;
8300     }
8301 
8302   switch (window->display->grab_op)
8303     {
8304     case META_GRAB_OP_RESIZING_SE:
8305     case META_GRAB_OP_RESIZING_S:
8306     case META_GRAB_OP_RESIZING_SW:
8307     case META_GRAB_OP_RESIZING_N:
8308     case META_GRAB_OP_RESIZING_NE:
8309     case META_GRAB_OP_RESIZING_NW:
8310     case META_GRAB_OP_RESIZING_W:
8311     case META_GRAB_OP_RESIZING_E:
8312     case META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN:
8313     case META_GRAB_OP_KEYBOARD_RESIZING_S:
8314     case META_GRAB_OP_KEYBOARD_RESIZING_N:
8315     case META_GRAB_OP_KEYBOARD_RESIZING_W:
8316     case META_GRAB_OP_KEYBOARD_RESIZING_E:
8317     case META_GRAB_OP_KEYBOARD_RESIZING_SE:
8318     case META_GRAB_OP_KEYBOARD_RESIZING_NE:
8319     case META_GRAB_OP_KEYBOARD_RESIZING_SW:
8320     case META_GRAB_OP_KEYBOARD_RESIZING_NW:
8321       break;
8322 
8323     default:
8324       /* Not resizing */
8325       return;
8326     }
8327 
8328   if (window->display->grab_resize_popup == NULL)
8329     {
8330       gint scale = gdk_window_get_scale_factor (gdk_get_default_root_window ());
8331       /* Display the resize popup only for windows that report an
8332        * increment hint that's larger than the scale factor. */
8333       if (window->size_hints.width_inc > scale ||
8334           window->size_hints.height_inc > scale)
8335         window->display->grab_resize_popup =
8336           meta_ui_resize_popup_new (window->display->xdisplay,
8337                                     window->screen->number);
8338     }
8339 
8340   if (window->display->grab_resize_popup != NULL)
8341     {
8342       MetaRectangle rect;
8343 
8344       if (window->display->grab_wireframe_active)
8345         rect = window->display->grab_wireframe_rect;
8346       else
8347         meta_window_get_client_root_coords (window, &rect);
8348 
8349       meta_ui_resize_popup_set (window->display->grab_resize_popup,
8350                                 rect,
8351                                 window->size_hints.base_width,
8352                                 window->size_hints.base_height,
8353                                 window->size_hints.width_inc,
8354                                 window->size_hints.height_inc);
8355 
8356       meta_ui_resize_popup_set_showing (window->display->grab_resize_popup,
8357                                         TRUE);
8358     }
8359 }
8360 
8361 void
meta_window_foreach_transient(MetaWindow * window,MetaWindowForeachFunc func,void * data)8362 meta_window_foreach_transient (MetaWindow            *window,
8363                                MetaWindowForeachFunc  func,
8364                                void                  *data)
8365 {
8366   GSList *windows;
8367   GSList *tmp;
8368 
8369   windows = meta_display_list_windows (window->display);
8370 
8371   tmp = windows;
8372   while (tmp != NULL)
8373     {
8374       MetaWindow *transient = tmp->data;
8375 
8376       if (meta_window_is_ancestor_of_transient (window, transient))
8377         {
8378           if (!(* func) (transient, data))
8379             break;
8380         }
8381 
8382       tmp = tmp->next;
8383     }
8384 
8385   g_slist_free (windows);
8386 }
8387 
8388 void
meta_window_foreach_ancestor(MetaWindow * window,MetaWindowForeachFunc func,void * data)8389 meta_window_foreach_ancestor (MetaWindow            *window,
8390                               MetaWindowForeachFunc  func,
8391                               void                  *data)
8392 {
8393   MetaWindow *w;
8394   MetaWindow *tortoise;
8395 
8396   w = window;
8397   tortoise = window;
8398   while (TRUE)
8399     {
8400       if (w->xtransient_for == None ||
8401           w->transient_parent_is_root_window)
8402         break;
8403 
8404       w = meta_display_lookup_x_window (w->display, w->xtransient_for);
8405 
8406       if (w == NULL || w == tortoise)
8407         break;
8408 
8409       if (!(* func) (w, data))
8410         break;
8411 
8412       if (w->xtransient_for == None ||
8413           w->transient_parent_is_root_window)
8414         break;
8415 
8416       w = meta_display_lookup_x_window (w->display, w->xtransient_for);
8417 
8418       if (w == NULL || w == tortoise)
8419         break;
8420 
8421       if (!(* func) (w, data))
8422         break;
8423 
8424       tortoise = meta_display_lookup_x_window (tortoise->display,
8425                                                tortoise->xtransient_for);
8426 
8427       /* "w" should have already covered all ground covered by the
8428        * tortoise, so the following must hold.
8429        */
8430       g_assert (tortoise != NULL);
8431       g_assert (tortoise->xtransient_for != None);
8432       g_assert (!tortoise->transient_parent_is_root_window);
8433     }
8434 }
8435 
8436 typedef struct
8437 {
8438   MetaWindow *ancestor;
8439   gboolean found;
8440 } FindAncestorData;
8441 
8442 static gboolean
find_ancestor_func(MetaWindow * window,void * data)8443 find_ancestor_func (MetaWindow *window,
8444                     void       *data)
8445 {
8446   FindAncestorData *d = data;
8447 
8448   if (window == d->ancestor)
8449     {
8450       d->found = TRUE;
8451       return FALSE;
8452     }
8453 
8454   return TRUE;
8455 }
8456 
8457 gboolean
meta_window_is_ancestor_of_transient(MetaWindow * window,MetaWindow * transient)8458 meta_window_is_ancestor_of_transient (MetaWindow *window,
8459                                       MetaWindow *transient)
8460 {
8461   FindAncestorData d;
8462 
8463   d.ancestor = window;
8464   d.found = FALSE;
8465 
8466   meta_window_foreach_ancestor (transient, find_ancestor_func, &d);
8467 
8468   return d.found;
8469 }
8470 
8471 /* Warp pointer to location appropriate for grab,
8472  * return root coordinates where pointer ended up.
8473  */
8474 static gboolean
warp_grab_pointer(MetaWindow * window,MetaGrabOp grab_op,int * x,int * y)8475 warp_grab_pointer (MetaWindow          *window,
8476                    MetaGrabOp           grab_op,
8477                    int                 *x,
8478                    int                 *y)
8479 {
8480   MetaRectangle  rect;
8481   MetaDisplay   *display;
8482 
8483   display = window->display;
8484 
8485   /* We may not have done begin_grab_op yet, i.e. may not be in a grab
8486    */
8487 
8488   if (window == display->grab_window && display->grab_wireframe_active)
8489     {
8490       meta_window_get_xor_rect (window, &display->grab_wireframe_rect, &rect);
8491     }
8492   else
8493     {
8494       meta_window_get_outer_rect (window, &rect);
8495     }
8496 
8497   switch (grab_op)
8498     {
8499       case META_GRAB_OP_KEYBOARD_MOVING:
8500       case META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN:
8501         *x = rect.width / 2;
8502         *y = rect.height / 2;
8503         break;
8504 
8505       case META_GRAB_OP_KEYBOARD_RESIZING_S:
8506         *x = rect.width / 2;
8507         *y = rect.height - 1;
8508         break;
8509 
8510       case META_GRAB_OP_KEYBOARD_RESIZING_N:
8511         *x = rect.width / 2;
8512         *y = 0;
8513         break;
8514 
8515       case META_GRAB_OP_KEYBOARD_RESIZING_W:
8516         *x = 0;
8517         *y = rect.height / 2;
8518         break;
8519 
8520       case META_GRAB_OP_KEYBOARD_RESIZING_E:
8521         *x = rect.width - 1;
8522         *y = rect.height / 2;
8523         break;
8524 
8525       case META_GRAB_OP_KEYBOARD_RESIZING_SE:
8526         *x = rect.width - 1;
8527         *y = rect.height - 1;
8528         break;
8529 
8530       case META_GRAB_OP_KEYBOARD_RESIZING_NE:
8531         *x = rect.width - 1;
8532         *y = 0;
8533         break;
8534 
8535       case META_GRAB_OP_KEYBOARD_RESIZING_SW:
8536         *x = 0;
8537         *y = rect.height - 1;
8538         break;
8539 
8540       case META_GRAB_OP_KEYBOARD_RESIZING_NW:
8541         *x = 0;
8542         *y = 0;
8543         break;
8544 
8545       default:
8546         return FALSE;
8547     }
8548 
8549   *x += rect.x;
8550   *y += rect.y;
8551 
8552   /* Avoid weird bouncing at the screen edge; see bug 154706 */
8553   *x = CLAMP (*x, 0, window->screen->rect.width-1);
8554   *y = CLAMP (*y, 0, window->screen->rect.height-1);
8555 
8556   meta_error_trap_push (display);
8557 
8558   meta_topic (META_DEBUG_WINDOW_OPS,
8559               "Warping pointer to %d,%d with window at %d,%d\n",
8560               *x, *y, rect.x, rect.y);
8561 
8562   /* Need to update the grab positions so that the MotionNotify and other
8563    * events generated by the XWarpPointer() call below don't cause complete
8564    * funkiness.  See bug 124582 and bug 122670.
8565    */
8566   display->grab_anchor_root_x = *x;
8567   display->grab_anchor_root_y = *y;
8568   display->grab_latest_motion_x = *x;
8569   display->grab_latest_motion_y = *y;
8570   if (display->grab_wireframe_active)
8571     display->grab_anchor_window_pos = display->grab_wireframe_rect;
8572   else
8573     meta_window_get_client_root_coords (window,
8574                                         &display->grab_anchor_window_pos);
8575 
8576   XWarpPointer (display->xdisplay,
8577                 None,
8578                 window->screen->xroot,
8579                 0, 0, 0, 0,
8580                 *x, *y);
8581 
8582   if (meta_error_trap_pop_with_return (display, FALSE) != Success)
8583     {
8584       meta_verbose ("Failed to warp pointer for window %s\n",
8585                     window->desc);
8586       return FALSE;
8587     }
8588 
8589   return TRUE;
8590 }
8591 
8592 void
meta_window_begin_grab_op(MetaWindow * window,MetaGrabOp op,gboolean frame_action,guint32 timestamp)8593 meta_window_begin_grab_op (MetaWindow *window,
8594                            MetaGrabOp  op,
8595                            gboolean    frame_action,
8596                            guint32     timestamp)
8597 {
8598   int x, y;
8599 
8600   warp_grab_pointer (window,
8601                      op, &x, &y);
8602 
8603   meta_display_begin_grab_op (window->display,
8604                               window->screen,
8605                               window,
8606                               op,
8607                               FALSE,
8608                               frame_action,
8609                               0 /* button */,
8610                               0,
8611                               timestamp,
8612                               x, y);
8613 }
8614 
8615 void
meta_window_update_keyboard_resize(MetaWindow * window,gboolean update_cursor)8616 meta_window_update_keyboard_resize (MetaWindow *window,
8617                                     gboolean    update_cursor)
8618 {
8619   int x, y;
8620 
8621   warp_grab_pointer (window,
8622                      window->display->grab_op,
8623                      &x, &y);
8624 
8625   if (update_cursor)
8626     {
8627       guint32 timestamp;
8628       /* FIXME: Using CurrentTime is really bad mojo */
8629       timestamp = CurrentTime;
8630       meta_display_set_grab_op_cursor (window->display,
8631                                        NULL,
8632                                        window->display->grab_op,
8633                                        TRUE,
8634                                        window->display->grab_xwindow,
8635                                        timestamp);
8636     }
8637 }
8638 
8639 void
meta_window_update_keyboard_move(MetaWindow * window)8640 meta_window_update_keyboard_move (MetaWindow *window)
8641 {
8642   int x, y;
8643 
8644   warp_grab_pointer (window,
8645                      window->display->grab_op,
8646                      &x, &y);
8647 }
8648 
8649 void
meta_window_update_layer(MetaWindow * window)8650 meta_window_update_layer (MetaWindow *window)
8651 {
8652   MetaGroup *group;
8653 
8654   meta_stack_freeze (window->screen->stack);
8655   group = meta_window_get_group (window);
8656   if (group)
8657     meta_group_update_layers (group);
8658   else
8659     meta_stack_update_layer (window->screen->stack, window);
8660   meta_stack_thaw (window->screen->stack);
8661 }
8662 
8663 /* ensure_mru_position_after ensures that window appears after
8664  * below_this_one in the active_workspace's mru_list (i.e. it treats
8665  * window as having been less recently used than below_this_one)
8666  */
8667 static void
ensure_mru_position_after(MetaWindow * window,MetaWindow * after_this_one)8668 ensure_mru_position_after (MetaWindow *window,
8669                            MetaWindow *after_this_one)
8670 {
8671   /* This is sort of slow since it runs through the entire list more
8672    * than once (especially considering the fact that we expect the
8673    * windows of interest to be the first two elements in the list),
8674    * but it doesn't matter while we're only using it on new window
8675    * map.
8676    */
8677 
8678   GList* active_mru_list;
8679   GList* window_position;
8680   GList* after_this_one_position;
8681 
8682   active_mru_list         = window->screen->active_workspace->mru_list;
8683   window_position         = g_list_find (active_mru_list, window);
8684   after_this_one_position = g_list_find (active_mru_list, after_this_one);
8685 
8686   /* after_this_one_position is NULL when we switch workspaces, but in
8687    * that case we don't need to do any MRU shuffling so we can simply
8688    * return.
8689    */
8690   if (after_this_one_position == NULL)
8691     return;
8692 
8693   if (g_list_length (window_position) > g_list_length (after_this_one_position))
8694     {
8695       window->screen->active_workspace->mru_list =
8696         g_list_delete_link (window->screen->active_workspace->mru_list,
8697                             window_position);
8698 
8699       window->screen->active_workspace->mru_list =
8700         g_list_insert_before (window->screen->active_workspace->mru_list,
8701                               after_this_one_position->next,
8702                               window);
8703     }
8704 }
8705 
8706 void
meta_window_stack_just_below(MetaWindow * window,MetaWindow * below_this_one)8707 meta_window_stack_just_below (MetaWindow *window,
8708                               MetaWindow *below_this_one)
8709 {
8710   g_return_if_fail (window         != NULL);
8711   g_return_if_fail (below_this_one != NULL);
8712 
8713   if (window->stack_position > below_this_one->stack_position)
8714     {
8715       meta_topic (META_DEBUG_STACK,
8716                   "Setting stack position of window %s to %d (making it below window %s).\n",
8717                   window->desc,
8718                   below_this_one->stack_position,
8719                   below_this_one->desc);
8720       meta_window_set_stack_position (window, below_this_one->stack_position);
8721     }
8722   else
8723     {
8724       meta_topic (META_DEBUG_STACK,
8725                   "Window %s  was already below window %s.\n",
8726                   window->desc, below_this_one->desc);
8727     }
8728 }
8729 
8730 void
meta_window_set_user_time(MetaWindow * window,guint32 timestamp)8731 meta_window_set_user_time (MetaWindow *window,
8732                            guint32     timestamp)
8733 {
8734   /* FIXME: If Soeren's suggestion in bug 151984 is implemented, it will allow
8735    * us to sanity check the timestamp here and ensure it doesn't correspond to
8736    * a future time.
8737    */
8738 
8739   /* Only update the time if this timestamp is newer... */
8740   if (window->net_wm_user_time_set &&
8741       XSERVER_TIME_IS_BEFORE (timestamp, window->net_wm_user_time))
8742     {
8743       meta_topic (META_DEBUG_STARTUP,
8744                   "Window %s _NET_WM_USER_TIME not updated to %u, because it "
8745                   "is less than %u\n",
8746                   window->desc, timestamp, window->net_wm_user_time);
8747     }
8748   else
8749     {
8750       meta_topic (META_DEBUG_STARTUP,
8751                   "Window %s has _NET_WM_USER_TIME of %u\n",
8752                   window->desc, timestamp);
8753       window->net_wm_user_time_set = TRUE;
8754       window->net_wm_user_time = timestamp;
8755       if (XSERVER_TIME_IS_BEFORE (window->display->last_user_time, timestamp))
8756         window->display->last_user_time = timestamp;
8757 
8758       /* If this is a terminal, user interaction with it means the user likely
8759        * doesn't want to have focus transferred for now due to new windows.
8760        */
8761       if (meta_prefs_get_focus_new_windows () ==
8762                META_FOCUS_NEW_WINDOWS_STRICT &&
8763           __window_is_terminal (window))
8764         window->display->allow_terminal_deactivation = FALSE;
8765     }
8766 }
8767 
8768 /* Sets the demands_attention hint on a window, but only
8769  * if it's at least partially obscured (see #305882).
8770  */
8771 void
meta_window_set_demands_attention(MetaWindow * window)8772 meta_window_set_demands_attention (MetaWindow *window)
8773 {
8774   MetaRectangle candidate_rect, other_rect;
8775   GList *stack = window->screen->stack->sorted;
8776   MetaWindow *other_window;
8777   gboolean obscured = FALSE;
8778 
8779   MetaWorkspace *workspace = window->screen->active_workspace;
8780   if (workspace!=window->workspace)
8781     {
8782       /* windows on other workspaces are necessarily obscured */
8783       obscured = TRUE;
8784     }
8785   else if (window->minimized)
8786     {
8787       obscured = TRUE;
8788     }
8789   else
8790     {
8791       meta_window_get_outer_rect (window, &candidate_rect);
8792 
8793       /* The stack is sorted with the top windows first. */
8794 
8795       while (stack != NULL && stack->data != window)
8796         {
8797           other_window = stack->data;
8798           stack = stack->next;
8799 
8800           if (other_window->on_all_workspaces ||
8801               window->on_all_workspaces ||
8802               other_window->workspace == window->workspace)
8803             {
8804               meta_window_get_outer_rect (other_window, &other_rect);
8805 
8806               if (meta_rectangle_overlap (&candidate_rect, &other_rect))
8807                 {
8808                   obscured = TRUE;
8809                   break;
8810                 }
8811             }
8812         }
8813     }
8814 
8815   if (obscured)
8816     {
8817       meta_topic (META_DEBUG_WINDOW_OPS,
8818                   "Marking %s as needing attention\n",
8819                   window->desc);
8820 
8821       window->wm_state_demands_attention = TRUE;
8822       set_net_wm_state (window);
8823     }
8824   else
8825     {
8826       /* If the window's in full view, there's no point setting the flag. */
8827 
8828       meta_topic (META_DEBUG_WINDOW_OPS,
8829                  "Not marking %s as needing attention because "
8830                  "it's in full view\n",
8831                  window->desc);
8832     }
8833 }
8834 
8835 void
meta_window_unset_demands_attention(MetaWindow * window)8836 meta_window_unset_demands_attention (MetaWindow *window)
8837 {
8838   meta_topic (META_DEBUG_WINDOW_OPS,
8839       "Marking %s as not needing attention\n", window->desc);
8840 
8841   window->wm_state_demands_attention = FALSE;
8842   set_net_wm_state (window);
8843 }
8844 
8845 MetaFrame *
meta_window_get_frame(MetaWindow * window)8846 meta_window_get_frame (MetaWindow *window)
8847 {
8848   return window->frame;
8849 }
8850 
8851 static gboolean
transient_has_focus(MetaWindow * window,void * data)8852 transient_has_focus (MetaWindow *window,
8853                      void       *data)
8854 {
8855   if (window->type == META_WINDOW_MODAL_DIALOG && meta_window_appears_focused (window))
8856     *((gboolean *)data) = TRUE;
8857 
8858   return FALSE;
8859 }
8860 
8861 gboolean
meta_window_appears_focused(MetaWindow * window)8862 meta_window_appears_focused (MetaWindow *window)
8863 {
8864   if (!window->has_focus && meta_prefs_get_attach_modal_dialogs ())
8865     {
8866       gboolean focus = FALSE;
8867       meta_window_foreach_transient (window, transient_has_focus, &focus);
8868       return focus;
8869     }
8870 
8871   if (window->has_focus)
8872     return TRUE;
8873 
8874   if (window->type == META_WINDOW_DOCK ||
8875       window->type == META_WINDOW_SPLASHSCREEN)
8876     return TRUE;
8877 
8878   return FALSE;
8879 }
8880 
8881 gboolean
meta_window_has_focus(MetaWindow * window)8882 meta_window_has_focus (MetaWindow *window)
8883 {
8884   return window->has_focus;
8885 }
8886 
8887 gboolean
meta_window_is_shaded(MetaWindow * window)8888 meta_window_is_shaded (MetaWindow *window)
8889 {
8890   return window->shaded;
8891 }
8892 
8893 MetaRectangle *
meta_window_get_rect(MetaWindow * window)8894 meta_window_get_rect (MetaWindow *window)
8895 {
8896   return &window->rect;
8897 }
8898 
8899 MetaScreen *
meta_window_get_screen(MetaWindow * window)8900 meta_window_get_screen (MetaWindow *window)
8901 {
8902   return window->screen;
8903 }
8904 
8905 MetaDisplay *
meta_window_get_display(MetaWindow * window)8906 meta_window_get_display (MetaWindow *window)
8907 {
8908   return window->display;
8909 }
8910 
8911 Window
meta_window_get_xwindow(MetaWindow * window)8912 meta_window_get_xwindow (MetaWindow *window)
8913 {
8914   return window->xwindow;
8915 }
8916 
8917 /**
8918  * meta_window_get_transient_for:
8919  * @window: a #MetaWindow
8920  *
8921  * Returns the #MetaWindow for the window that is pointed to by the
8922  * WM_TRANSIENT_FOR hint on this window (see XGetTransientForHint()
8923  * or XSetTransientForHint()). Marco keeps transient windows above their
8924  * parents. A typical usage of this hint is for a dialog that wants to stay
8925  * above its associated window.
8926  *
8927  * Return value: (transfer none): the window this window is transient for, or
8928  * %NULL if the WM_TRANSIENT_FOR hint is unset or does not point to a toplevel
8929  * window that Marco knows about.
8930  */
8931 MetaWindow *
meta_window_get_transient_for(MetaWindow * window)8932 meta_window_get_transient_for (MetaWindow *window)
8933 {
8934   if (window->xtransient_for)
8935     return meta_display_lookup_x_window (window->display, window->xtransient_for);
8936   else
8937     return NULL;
8938 }
8939 
8940 gboolean
meta_window_is_maximized(MetaWindow * window)8941 meta_window_is_maximized (MetaWindow *window)
8942 {
8943   return META_WINDOW_MAXIMIZED (window);
8944 }
8945 
8946 gboolean
meta_window_is_tiled_left(MetaWindow * window)8947 meta_window_is_tiled_left (MetaWindow *window)
8948 {
8949   return META_WINDOW_TILED_LEFT (window);
8950 }
8951 
8952 gboolean
meta_window_is_tiled_right(MetaWindow * window)8953 meta_window_is_tiled_right (MetaWindow *window)
8954 {
8955   return META_WINDOW_TILED_RIGHT (window);
8956 }
8957 
8958 /**
8959  * meta_window_is_client_decorated:
8960  *
8961  * Check if if the window has decorations drawn by the client.
8962  * (window->decorated refers only to whether we should add decorations)
8963  */
8964 gboolean
meta_window_is_client_decorated(MetaWindow * window)8965 meta_window_is_client_decorated (MetaWindow *window)
8966 {
8967   /* Currently the implementation here is hackish -
8968    * has_custom_frame_extents() is set if _GTK_FRAME_EXTENTS is set
8969    * to any value even 0. GTK+ always sets _GTK_FRAME_EXTENTS for
8970    * client-side-decorated window, even if the value is 0 because
8971    * the window is maxized and has no invisible borders or shadows.
8972    */
8973   return window->has_custom_frame_extents;
8974 }
8975 
8976 /**
8977  * meta_window_get_frame_bounds:
8978  *
8979  * Gets a region representing the outer bounds of the window's frame.
8980  *
8981  * Return value: (transfer none) (allow-none): a #cairo_region_t
8982  * holding the outer bounds of the window, or %NULL if the window
8983  * doesn't have a frame.
8984  */
8985 cairo_region_t *
meta_window_get_frame_bounds(MetaWindow * window)8986 meta_window_get_frame_bounds (MetaWindow *window)
8987 {
8988   if (!window->frame_bounds)
8989     {
8990       if (window->frame)
8991         window->frame_bounds = meta_frame_get_frame_bounds (window->frame);
8992     }
8993 
8994   return window->frame_bounds;
8995 }
8996 
8997 static void
meta_window_finalize(GObject * object)8998 meta_window_finalize (GObject *object)
8999 {
9000   MetaWindow *window;
9001 
9002   window = META_WINDOW (object);
9003 
9004   g_clear_object (&window->icon);
9005   g_clear_object (&window->mini_icon);
9006 
9007   g_clear_pointer (&window->frame_bounds, cairo_region_destroy);
9008 
9009   meta_icon_cache_free (&window->icon_cache);
9010 
9011   g_clear_pointer (&window->sm_client_id, g_free);
9012   g_clear_pointer (&window->wm_client_machine, g_free);
9013   g_clear_pointer (&window->startup_id, g_free);
9014   g_clear_pointer (&window->role, g_free);
9015   g_clear_pointer (&window->res_class, g_free);
9016   g_clear_pointer (&window->res_name, g_free);
9017   g_clear_pointer (&window->title, g_free);
9018   g_clear_pointer (&window->icon_name, g_free);
9019   g_clear_pointer (&window->desc, g_free);
9020   g_clear_pointer (&window->gtk_theme_variant, g_free);
9021 
9022   G_OBJECT_CLASS (meta_window_parent_class)->finalize (object);
9023 }
9024 
9025 static void
meta_window_class_init(MetaWindowClass * window_class)9026 meta_window_class_init (MetaWindowClass *window_class)
9027 {
9028   GObjectClass *object_class;
9029 
9030   object_class = G_OBJECT_CLASS (window_class);
9031 
9032   object_class->finalize = meta_window_finalize;
9033 }
9034 
9035 static void
meta_window_init(MetaWindow * window)9036 meta_window_init (MetaWindow *window)
9037 {
9038 }
9039