1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 
3 /*
4  * Copyright (C) 2001 Havoc Pennington, Anders Carlsson
5  * Copyright (C) 2002, 2003 Red Hat, Inc.
6  * Copyright (C) 2003 Rob Adams
7  * Copyright (C) 2004-2006 Elijah Newren
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License as
11  * published by the Free Software Foundation; either version 2 of the
12  * License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA
22  * 02110-1335, USA.
23  */
24 
25 /**
26  * SECTION:window
27  * @title: MetaWindow
28  * @short_description: Muffin X managed windows
29  */
30 
31 #include <config.h>
32 #include "window-private.h"
33 #include "boxes-private.h"
34 #include "edge-resistance.h"
35 #include "util-private.h"
36 #include "frame.h"
37 #include <meta/errors.h>
38 #include "workspace-private.h"
39 #include "stack.h"
40 #include "keybindings-private.h"
41 #include "ui.h"
42 #include "place.h"
43 #include "session.h"
44 #include <meta/prefs.h>
45 #include "resizepopup.h"
46 #include "xprops.h"
47 #include <meta/group.h>
48 #include "window-props.h"
49 #include "constraints.h"
50 #include "muffin-enum-types.h"
51 #include <clutter/clutter.h>
52 
53 #include <X11/Xatom.h>
54 #include <X11/Xlibint.h> /* For display->resource_mask */
55 #include <X11/Xlib-xcb.h>
56 #include <xcb/res.h>
57 #include <string.h>
58 #include <math.h>
59 
60 #ifdef HAVE_SHAPE
61 #include <X11/extensions/shape.h>
62 #endif
63 #include <X11/XKBlib.h>
64 #include <X11/extensions/Xcomposite.h>
65 #include <gdk/gdkx.h>
66 
67 static int destroying_windows_disallowed = 0;
68 
69 
70 static void     update_sm_hints           (MetaWindow     *window);
71 static void     update_net_frame_extents  (MetaWindow     *window);
72 static void     recalc_window_type        (MetaWindow     *window);
73 static void     recalc_window_features    (MetaWindow     *window);
74 static void     invalidate_work_areas     (MetaWindow     *window);
75 static void     recalc_window_type        (MetaWindow     *window);
76 static void     set_wm_state_on_xwindow   (MetaDisplay    *display,
77                                            Window          xwindow,
78                                            int             state);
79 static void     set_wm_state              (MetaWindow     *window,
80                                            int             state);
81 static void     set_net_wm_state          (MetaWindow     *window);
82 static void     meta_window_set_above     (MetaWindow     *window,
83                                            gboolean        new_value);
84 
85 static void     send_configure_notify     (MetaWindow     *window);
86 static gboolean process_property_notify   (MetaWindow     *window,
87                                            XPropertyEvent *event);
88 
89 static void     meta_window_force_placement (MetaWindow     *window);
90 
91 static void     meta_window_show          (MetaWindow     *window);
92 static void     meta_window_hide          (MetaWindow     *window);
93 
94 static gboolean meta_window_same_client (MetaWindow *window,
95                                          MetaWindow *other_window);
96 
97 static void     meta_window_save_rect         (MetaWindow    *window);
98 static void     save_user_window_placement    (MetaWindow    *window);
99 static void     force_save_user_window_placement (MetaWindow    *window);
100 
101 static void meta_window_move_resize_internal (MetaWindow         *window,
102                                               MetaMoveResizeFlags flags,
103                                               int                 resize_gravity,
104                                               int                 root_x_nw,
105                                               int                 root_y_nw,
106                                               int                 w,
107                                               int                 h);
108 
109 static void     ensure_mru_position_after (MetaWindow *window,
110                                            MetaWindow *after_this_one);
111 
112 
113 static void meta_window_move_resize_now (MetaWindow  *window);
114 
115 static void meta_window_unqueue (MetaWindow *window, guint queuebits);
116 
117 static void     update_move           (MetaWindow   *window,
118                                        gboolean      legacy_snap,
119                                        gboolean      snap_mode,
120                                        int           x,
121                                        int           y);
122 static gboolean update_move_timeout   (gpointer data);
123 static void     update_resize         (MetaWindow   *window,
124                                        gboolean      snap,
125                                        int           x,
126                                        int           y,
127                                        gboolean      force);
128 static gboolean update_resize_timeout (gpointer data);
129 static gboolean should_be_on_all_workspaces (MetaWindow *window);
130 
131 static void meta_window_flush_calc_showing   (MetaWindow *window);
132 
133 static gboolean queue_calc_showing_func (MetaWindow *window,
134                                          void       *data);
135 
136 static void meta_window_apply_session_info (MetaWindow                  *window,
137                                             const MetaWindowSessionInfo *info);
138 static void meta_window_move_between_rects (MetaWindow          *window,
139                                             const MetaRectangle *old_area,
140                                             const MetaRectangle *new_area);
141 
142 static void unmaximize_window_before_freeing (MetaWindow        *window);
143 static void unminimize_window_and_all_transient_parents (MetaWindow *window);
144 
145 static void meta_window_update_monitor (MetaWindow *window);
146 
147 static void normalize_tile_state (MetaWindow *window);
148 
149 static unsigned int get_mask_from_snap_keysym (MetaWindow *window);
150 
151 static void update_edge_constraints (MetaWindow *window);
152 static void update_gtk_edge_constraints (MetaWindow *window);
153 
154 /* Idle handlers for the three queues (run with meta_later_add()). The
155  * "data" parameter in each case will be a GINT_TO_POINTER of the
156  * index into the queue arrays to use.
157  *
158  * TODO: Possibly there is still some code duplication among these, which we
159  * need to sort out at some point.
160  */
161 static gboolean idle_calc_showing (gpointer data);
162 static gboolean idle_move_resize (gpointer data);
163 
164 G_DEFINE_TYPE (MetaWindow, meta_window, G_TYPE_OBJECT);
165 
166 #define SNAP_DELAY 2000
167 
168 enum {
169   PROP_0,
170 
171   PROP_TITLE,
172   PROP_DECORATED,
173   PROP_FULLSCREEN,
174   PROP_MAXIMIZED_HORIZONTALLY,
175   PROP_MAXIMIZED_VERTICALLY,
176   PROP_TILE_TYPE,
177   PROP_MINIMIZED,
178   PROP_WINDOW_TYPE,
179   PROP_USER_TIME,
180   PROP_DEMANDS_ATTENTION,
181   PROP_URGENT,
182   PROP_MUFFIN_HINTS,
183   PROP_APPEARS_FOCUSED,
184   PROP_RESIZEABLE,
185   PROP_ABOVE,
186   PROP_WM_CLASS,
187   PROP_GTK_APPLICATION_ID,
188   PROP_GTK_UNIQUE_BUS_NAME,
189   PROP_GTK_APPLICATION_OBJECT_PATH,
190   PROP_GTK_WINDOW_OBJECT_PATH,
191   PROP_GTK_APP_MENU_OBJECT_PATH,
192   PROP_GTK_MENUBAR_OBJECT_PATH,
193   PROP_PROGRESS,
194   PROP_PROGRESS_PULSE
195 };
196 
197 enum
198 {
199   WORKSPACE_CHANGED,
200   FOCUS,
201   RAISED,
202   UNMANAGED,
203   SIZE_CHANGED,
204   POSITION_CHANGED,
205   ICON_CHANGED,
206 
207   LAST_SIGNAL
208 };
209 
210 typedef enum
211 {
212     ZONE_TOP =      1 << 0,
213     ZONE_RIGHT =    1 << 1,
214     ZONE_BOTTOM =   1 << 2,
215     ZONE_LEFT =     1 << 3,
216     ZONE_ULC =      ZONE_TOP | ZONE_LEFT,
217     ZONE_LLC =      ZONE_BOTTOM | ZONE_LEFT,
218     ZONE_URC =      ZONE_TOP | ZONE_RIGHT,
219     ZONE_LRC =      ZONE_BOTTOM | ZONE_RIGHT
220 } TileZone;
221 
222 static guint window_signals[LAST_SIGNAL] = { 0 };
223 
224 static void
prefs_changed_callback(MetaPreference pref,gpointer data)225 prefs_changed_callback (MetaPreference pref,
226                         gpointer       data)
227 {
228   MetaWindow *window = data;
229 
230   if (pref != META_PREF_WORKSPACES_ONLY_ON_PRIMARY)
231     return;
232 
233   meta_window_update_on_all_workspaces (window);
234 
235   meta_window_queue (window, META_QUEUE_CALC_SHOWING);
236 }
237 
238 static void
meta_window_finalize(GObject * object)239 meta_window_finalize (GObject *object)
240 {
241   MetaWindow *window = META_WINDOW (object);
242 
243   if (window->icon)
244     g_object_unref (G_OBJECT (window->icon));
245 
246   if (window->frame_bounds)
247     cairo_region_destroy (window->frame_bounds);
248 
249   if (window->opaque_region)
250     cairo_region_destroy (window->opaque_region);
251 
252   meta_icon_cache_free (&window->icon_cache);
253 
254   free (window->sm_client_id);
255   free (window->wm_client_machine);
256   free (window->startup_id);
257   free (window->muffin_hints);
258   free (window->role);
259   free (window->res_class);
260   free (window->res_name);
261   free (window->title);
262   free (window->icon_name);
263   free (window->theme_icon_name);
264   free (window->desc);
265   free (window->gtk_theme_variant);
266   free (window->gtk_application_id);
267   free (window->gtk_unique_bus_name);
268   free (window->gtk_application_object_path);
269   free (window->gtk_window_object_path);
270   free (window->gtk_app_menu_object_path);
271   free (window->gtk_menubar_object_path);
272 
273   G_OBJECT_CLASS (meta_window_parent_class)->finalize (object);
274 }
275 
276 static void
meta_window_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)277 meta_window_get_property(GObject         *object,
278                          guint            prop_id,
279                          GValue          *value,
280                          GParamSpec      *pspec)
281 {
282   MetaWindow *win = META_WINDOW (object);
283 
284   switch (prop_id)
285     {
286     case PROP_TITLE:
287       g_value_set_string (value, win->title);
288       break;
289     case PROP_DECORATED:
290       g_value_set_boolean (value, win->decorated);
291       break;
292     case PROP_FULLSCREEN:
293       g_value_set_boolean (value, win->fullscreen);
294       break;
295     case PROP_MAXIMIZED_HORIZONTALLY:
296       g_value_set_boolean (value, win->maximized_horizontally);
297       break;
298     case PROP_MAXIMIZED_VERTICALLY:
299       g_value_set_boolean (value, win->maximized_vertically);
300       break;
301     case PROP_TILE_TYPE:
302       g_value_set_int (value, win->tile_type);
303       break;
304     case PROP_MINIMIZED:
305       g_value_set_boolean (value, win->minimized);
306       break;
307     case PROP_WINDOW_TYPE:
308       g_value_set_enum (value, win->type);
309       break;
310     case PROP_USER_TIME:
311       g_value_set_uint (value, win->net_wm_user_time);
312       break;
313     case PROP_DEMANDS_ATTENTION:
314       g_value_set_boolean (value, win->wm_state_demands_attention);
315       break;
316     case PROP_URGENT:
317       g_value_set_boolean (value, win->wm_hints_urgent);
318       break;
319     case PROP_MUFFIN_HINTS:
320       g_value_set_string (value, win->muffin_hints);
321       break;
322     case PROP_APPEARS_FOCUSED:
323       g_value_set_boolean (value, meta_window_appears_focused (win));
324       break;
325     case PROP_WM_CLASS:
326       g_value_set_string (value, win->res_class);
327       break;
328     case PROP_RESIZEABLE:
329       g_value_set_boolean (value, win->has_resize_func);
330       break;
331     case PROP_ABOVE:
332       g_value_set_boolean (value, win->wm_state_above);
333       break;
334     case PROP_GTK_APPLICATION_ID:
335       g_value_set_string (value, win->gtk_application_id);
336       break;
337     case PROP_GTK_UNIQUE_BUS_NAME:
338       g_value_set_string (value, win->gtk_unique_bus_name);
339       break;
340     case PROP_GTK_APPLICATION_OBJECT_PATH:
341       g_value_set_string (value, win->gtk_application_object_path);
342       break;
343     case PROP_GTK_WINDOW_OBJECT_PATH:
344       g_value_set_string (value, win->gtk_window_object_path);
345       break;
346     case PROP_GTK_APP_MENU_OBJECT_PATH:
347       g_value_set_string (value, win->gtk_app_menu_object_path);
348       break;
349     case PROP_GTK_MENUBAR_OBJECT_PATH:
350       g_value_set_string (value, win->gtk_menubar_object_path);
351       break;
352     case PROP_PROGRESS:
353       g_value_set_uint (value, win->progress);
354       break;
355     case PROP_PROGRESS_PULSE:
356       g_value_set_boolean (value, win->progress_pulse);
357       break;
358     default:
359       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
360       break;
361     }
362 }
363 
364 static void
meta_window_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)365 meta_window_set_property(GObject         *object,
366                          guint            prop_id,
367                          const GValue    *value,
368                          GParamSpec      *pspec)
369 {
370   switch (prop_id)
371     {
372     default:
373       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
374       break;
375     }
376 }
377 
378 static void
meta_window_class_init(MetaWindowClass * klass)379 meta_window_class_init (MetaWindowClass *klass)
380 {
381   GObjectClass *object_class = G_OBJECT_CLASS (klass);
382 
383   object_class->finalize = meta_window_finalize;
384 
385   object_class->get_property = meta_window_get_property;
386   object_class->set_property = meta_window_set_property;
387 
388   g_object_class_install_property (object_class,
389                                    PROP_TITLE,
390                                    g_param_spec_string ("title",
391                                                         "Title",
392                                                         "The title of the window",
393                                                         NULL,
394                                                         G_PARAM_READABLE));
395 
396   g_object_class_install_property (object_class,
397                                    PROP_DECORATED,
398                                    g_param_spec_boolean ("decorated",
399                                                          "Decorated",
400                                                          "Whether window is decorated",
401                                                          TRUE,
402                                                          G_PARAM_READABLE));
403 
404   g_object_class_install_property (object_class,
405                                    PROP_FULLSCREEN,
406                                    g_param_spec_boolean ("fullscreen",
407                                                          "Fullscreen",
408                                                          "Whether window is fullscreened",
409                                                          FALSE,
410                                                          G_PARAM_READABLE));
411 
412   g_object_class_install_property (object_class,
413                                    PROP_MAXIMIZED_HORIZONTALLY,
414                                    g_param_spec_boolean ("maximized-horizontally",
415                                                          "Maximized horizontally",
416                                                          "Whether window is maximized horizontally",
417                                                          FALSE,
418                                                          G_PARAM_READABLE));
419 
420   g_object_class_install_property (object_class,
421                                    PROP_MAXIMIZED_VERTICALLY,
422                                    g_param_spec_boolean ("maximized-vertically",
423                                                          "Maximizing vertically",
424                                                          "Whether window is maximized vertically",
425                                                          FALSE,
426                                                          G_PARAM_READABLE));
427 
428   g_object_class_install_property (object_class,
429                                    PROP_TILE_TYPE,
430                                    g_param_spec_int     ("tile-type",
431                                                          "Window is tiled or snapped",
432                                                          "Whether window is tiled or snapped",
433                                                          META_WINDOW_TILE_TYPE_NONE,
434                                                          META_WINDOW_TILE_TYPE_SNAPPED,
435                                                          META_WINDOW_TILE_TYPE_NONE,
436                                                          G_PARAM_READABLE));
437 
438   g_object_class_install_property (object_class,
439                                    PROP_MINIMIZED,
440                                    g_param_spec_boolean ("minimized",
441                                                          "Minimizing",
442                                                          "Whether window is minimized",
443                                                          FALSE,
444                                                          G_PARAM_READABLE));
445 
446   g_object_class_install_property (object_class,
447                                    PROP_WINDOW_TYPE,
448                                    g_param_spec_enum ("window-type",
449                                                       "Window Type",
450                                                       "The type of the window",
451                                                       META_TYPE_WINDOW_TYPE,
452                                                       META_WINDOW_NORMAL,
453                                                       G_PARAM_READABLE));
454 
455   g_object_class_install_property (object_class,
456                                    PROP_USER_TIME,
457                                    g_param_spec_uint ("user-time",
458                                                       "User time",
459                                                       "Timestamp of last user interaction",
460                                                       0,
461                                                       G_MAXUINT,
462                                                       0,
463                                                       G_PARAM_READABLE));
464 
465   g_object_class_install_property (object_class,
466                                    PROP_DEMANDS_ATTENTION,
467                                    g_param_spec_boolean ("demands-attention",
468                                                          "Demands Attention",
469                                                          "Whether the window has _NET_WM_STATE_DEMANDS_ATTENTION set",
470                                                          FALSE,
471                                                          G_PARAM_READABLE));
472 
473   g_object_class_install_property (object_class,
474                                    PROP_URGENT,
475                                    g_param_spec_boolean ("urgent",
476                                                          "Urgent",
477                                                          "Whether the urgent flag of WM_HINTS is set",
478                                                          FALSE,
479                                                          G_PARAM_READABLE));
480 
481   g_object_class_install_property (object_class,
482                                    PROP_MUFFIN_HINTS,
483                                    g_param_spec_string ("muffin-hints",
484                                                         "_MUFFIN_HINTS",
485                                                         "Contents of the _MUFFIN_HINTS property of this window",
486                                                         NULL,
487                                                         G_PARAM_READABLE));
488   g_object_class_install_property (object_class,
489                                    PROP_APPEARS_FOCUSED,
490                                    g_param_spec_boolean ("appears-focused",
491                                                          "Appears focused",
492                                                          "Whether the window is drawn as being focused",
493                                                          FALSE,
494                                                          G_PARAM_READABLE));
495 
496   g_object_class_install_property (object_class,
497                                    PROP_RESIZEABLE,
498                                    g_param_spec_boolean ("resizeable",
499                                                          "Resizeable",
500                                                          "Whether the window can be resized",
501                                                          FALSE,
502                                                          G_PARAM_READABLE));
503 
504   g_object_class_install_property (object_class,
505                                    PROP_ABOVE,
506                                    g_param_spec_boolean ("above",
507                                                          "Above",
508                                                          "Whether the window is shown as always-on-top",
509                                                          FALSE,
510                                                          G_PARAM_READABLE));
511 
512   g_object_class_install_property (object_class,
513                                    PROP_WM_CLASS,
514                                    g_param_spec_string ("wm-class",
515                                                         "WM_CLASS",
516                                                         "Contents of the WM_CLASS property of this window",
517                                                         NULL,
518                                                         G_PARAM_READABLE));
519 
520   g_object_class_install_property (object_class,
521                                    PROP_GTK_APPLICATION_ID,
522                                    g_param_spec_string ("gtk-application-id",
523                                                         "_GTK_APPLICATION_ID",
524                                                         "Contents of the _GTK_APPLICATION_ID property of this window",
525                                                         NULL,
526                                                         G_PARAM_READABLE));
527 
528   g_object_class_install_property (object_class,
529                                    PROP_GTK_UNIQUE_BUS_NAME,
530                                    g_param_spec_string ("gtk-unique-bus-name",
531                                                         "_GTK_UNIQUE_BUS_NAME",
532                                                         "Contents of the _GTK_UNIQUE_BUS_NAME property of this window",
533                                                         NULL,
534                                                         G_PARAM_READABLE));
535 
536   g_object_class_install_property (object_class,
537                                    PROP_GTK_APPLICATION_OBJECT_PATH,
538                                    g_param_spec_string ("gtk-application-object-path",
539                                                         "_GTK_APPLICATION_OBJECT_PATH",
540                                                         "Contents of the _GTK_APPLICATION_OBJECT_PATH property of this window",
541                                                         NULL,
542                                                         G_PARAM_READABLE));
543 
544   g_object_class_install_property (object_class,
545                                    PROP_GTK_WINDOW_OBJECT_PATH,
546                                    g_param_spec_string ("gtk-window-object-path",
547                                                         "_GTK_WINDOW_OBJECT_PATH",
548                                                         "Contents of the _GTK_WINDOW_OBJECT_PATH property of this window",
549                                                         NULL,
550                                                         G_PARAM_READABLE));
551 
552   g_object_class_install_property (object_class,
553                                    PROP_GTK_APP_MENU_OBJECT_PATH,
554                                    g_param_spec_string ("gtk-app-menu-object-path",
555                                                         "_GTK_APP_MENU_OBJECT_PATH",
556                                                         "Contents of the _GTK_APP_MENU_OBJECT_PATH property of this window",
557                                                         NULL,
558                                                         G_PARAM_READABLE));
559 
560   g_object_class_install_property (object_class,
561                                    PROP_GTK_MENUBAR_OBJECT_PATH,
562                                    g_param_spec_string ("gtk-menubar-object-path",
563                                                         "_GTK_MENUBAR_OBJECT_PATH",
564                                                         "Contents of the _GTK_MENUBAR_OBJECT_PATH property of this window",
565                                                         NULL,
566                                                         G_PARAM_READABLE));
567 
568   g_object_class_install_property (object_class,
569                                    PROP_PROGRESS,
570                                    g_param_spec_uint ("progress",
571                                                       "Progress",
572                                                       "The progress of some long-running operation",
573                                                       0,
574                                                       100,
575                                                       0,
576                                                       G_PARAM_READABLE));
577 
578   g_object_class_install_property (object_class,
579                                    PROP_PROGRESS_PULSE,
580                                    g_param_spec_boolean ("progress-pulse",
581                                                          "Pulsing progress",
582                                                          "Show indeterminate or ongoing progress of an operation.",
583                                                          FALSE,
584                                                          G_PARAM_READABLE));
585 
586   window_signals[WORKSPACE_CHANGED] =
587     g_signal_new ("workspace-changed",
588                   G_TYPE_FROM_CLASS (object_class),
589                   G_SIGNAL_RUN_LAST,
590                   G_STRUCT_OFFSET (MetaWindowClass, workspace_changed),
591                   NULL, NULL, NULL,
592                   G_TYPE_NONE, 1,
593                   G_TYPE_INT);
594 
595   window_signals[FOCUS] =
596     g_signal_new ("focus",
597                   G_TYPE_FROM_CLASS (object_class),
598                   G_SIGNAL_RUN_LAST,
599                   G_STRUCT_OFFSET (MetaWindowClass, focus),
600                   NULL, NULL, NULL,
601                   G_TYPE_NONE, 0);
602 
603   window_signals[RAISED] =
604     g_signal_new ("raised",
605                   G_TYPE_FROM_CLASS (object_class),
606                   G_SIGNAL_RUN_LAST,
607                   G_STRUCT_OFFSET (MetaWindowClass, raised),
608                   NULL, NULL, NULL,
609                   G_TYPE_NONE, 0);
610 
611   window_signals[UNMANAGED] =
612     g_signal_new ("unmanaged",
613                   G_TYPE_FROM_CLASS (object_class),
614                   G_SIGNAL_RUN_LAST,
615                   G_STRUCT_OFFSET (MetaWindowClass, unmanaged),
616                   NULL, NULL, NULL,
617                   G_TYPE_NONE, 0);
618 
619   window_signals[POSITION_CHANGED] =
620     g_signal_new ("position-changed",
621                   G_TYPE_FROM_CLASS (object_class),
622                   G_SIGNAL_RUN_LAST,
623                   0,
624                   NULL, NULL, NULL,
625                   G_TYPE_NONE, 0);
626 
627   window_signals[SIZE_CHANGED] =
628     g_signal_new ("size-changed",
629                   G_TYPE_FROM_CLASS (object_class),
630                   G_SIGNAL_RUN_LAST,
631                   0,
632                   NULL, NULL, NULL,
633                   G_TYPE_NONE, 0);
634 
635   window_signals[ICON_CHANGED] =
636     g_signal_new ("icon-changed",
637                   G_TYPE_FROM_CLASS (object_class),
638                   G_SIGNAL_RUN_LAST,
639                   0,
640                   NULL, NULL, NULL,
641                   G_TYPE_NONE, 0);
642 }
643 
644 static void
meta_window_init(MetaWindow * self)645 meta_window_init (MetaWindow *self)
646 {
647   meta_prefs_add_listener (prefs_changed_callback, self);
648 }
649 
650 #ifdef WITH_VERBOSE_MODE
651 static const char*
wm_state_to_string(int state)652 wm_state_to_string (int state)
653 {
654   switch (state)
655     {
656     case NormalState:
657       return "NormalState";
658     case IconicState:
659       return "IconicState";
660     case WithdrawnState:
661       return "WithdrawnState";
662     }
663 
664   return "Unknown";
665 }
666 #endif
667 
668 static gboolean
is_desktop_or_dock_foreach(MetaWindow * window,void * data)669 is_desktop_or_dock_foreach (MetaWindow *window,
670                             void       *data)
671 {
672   gboolean *result = data;
673 
674   *result =
675     window->type == META_WINDOW_DESKTOP ||
676     window->type == META_WINDOW_DOCK;
677   if (*result)
678     return FALSE; /* stop as soon as we find one */
679   else
680     return TRUE;
681 }
682 
683 /* window is the window that's newly mapped provoking
684  * the possible change
685  */
686 static void
maybe_leave_show_desktop_mode(MetaWindow * window)687 maybe_leave_show_desktop_mode (MetaWindow *window)
688 {
689   gboolean is_desktop_or_dock;
690 
691   if (!window->screen->active_workspace->showing_desktop)
692     return;
693 
694   /* If the window is a transient for the dock or desktop, don't
695    * leave show desktop mode when the window opens. That's
696    * so you can e.g. hide all windows, manipulate a file on
697    * the desktop via a dialog, then unshow windows again.
698    */
699   is_desktop_or_dock = FALSE;
700   is_desktop_or_dock_foreach (window,
701                               &is_desktop_or_dock);
702 
703   meta_window_foreach_ancestor (window, is_desktop_or_dock_foreach,
704                                 &is_desktop_or_dock);
705 
706   if (!is_desktop_or_dock)
707     {
708       meta_screen_minimize_all_on_active_workspace_except (window->screen,
709                                                            window);
710       meta_screen_unshow_desktop (window->screen);
711     }
712 }
713 
714 static gboolean
client_window_should_be_mapped(MetaWindow * window)715 client_window_should_be_mapped (MetaWindow *window)
716 {
717   return !window->shaded;
718 }
719 
720 static void
sync_client_window_mapped(MetaWindow * window)721 sync_client_window_mapped (MetaWindow *window)
722 {
723   gboolean should_be_mapped;
724 
725   g_return_if_fail (!window->override_redirect);
726 
727   should_be_mapped = client_window_should_be_mapped (window);
728   if (window->mapped == should_be_mapped)
729     return;
730 
731   window->mapped = should_be_mapped;
732 
733   meta_error_trap_push (window->display);
734   if (should_be_mapped)
735     {
736       XMapWindow (window->display->xdisplay, window->xwindow);
737     }
738   else
739     {
740       XUnmapWindow (window->display->xdisplay, window->xwindow);
741       window->unmaps_pending ++;
742     }
743   meta_error_trap_pop (window->display);
744 }
745 
746 static void
update_client_pid(MetaWindow * window)747 update_client_pid (MetaWindow *window)
748 {
749   MetaDisplay *display = window->display;
750   xcb_connection_t *xcb = XGetXCBConnection (display->xdisplay);
751   xcb_res_client_id_spec_t spec = { 0 };
752   xcb_res_query_client_ids_cookie_t cookie;
753   xcb_res_query_client_ids_reply_t *reply = NULL;
754 
755   spec.client = window->xwindow;
756   spec.mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID;
757 
758   cookie = xcb_res_query_client_ids (xcb, 1, &spec);
759   reply = xcb_res_query_client_ids_reply (xcb, cookie, NULL);
760 
761   if (reply == NULL)
762     {
763       window->client_pid = -1;
764       return;
765     }
766 
767   int pid = -1, *value;
768   xcb_res_client_id_value_iterator_t it;
769   for (it = xcb_res_query_client_ids_ids_iterator (reply);
770        it.rem;
771        xcb_res_client_id_value_next (&it))
772     {
773       spec = it.data->spec;
774       if (spec.mask & XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID)
775         {
776           value = xcb_res_client_id_value_value (it.data);
777           window->client_pid = *value;
778           break;
779         }
780     }
781 
782   free (reply);
783 }
784 
785 LOCAL_SYMBOL MetaWindow*
meta_window_new(MetaDisplay * display,Window xwindow,gboolean must_be_viewable)786 meta_window_new (MetaDisplay *display,
787                  Window       xwindow,
788                  gboolean     must_be_viewable)
789 {
790   XWindowAttributes attrs;
791   MetaWindow *window;
792 
793   meta_display_grab (display);
794   meta_error_trap_push (display); /* Push a trap over all of window
795                                    * creation, to reduce XSync() calls
796                                    */
797 
798   meta_error_trap_push_with_return (display);
799 
800   if (XGetWindowAttributes (display->xdisplay,xwindow, &attrs))
801    {
802       if(meta_error_trap_pop_with_return (display) != Success)
803        {
804           meta_verbose ("Failed to get attributes for window 0x%lx\n",
805                         xwindow);
806           meta_error_trap_pop (display);
807           meta_display_ungrab (display);
808           return NULL;
809        }
810       window = meta_window_new_with_attrs (display, xwindow,
811                                            must_be_viewable,
812                                            META_COMP_EFFECT_CREATE,
813                                            &attrs);
814    }
815   else
816    {
817          meta_error_trap_pop_with_return (display);
818          meta_verbose ("Failed to get attributes for window 0x%lx\n",
819                         xwindow);
820          meta_error_trap_pop (display);
821          meta_display_ungrab (display);
822          return NULL;
823    }
824 
825 
826   meta_error_trap_pop (display);
827   meta_display_ungrab (display);
828 
829   return window;
830 }
831 
832 /* The MUFFIN_WM_CLASS_FILTER environment variable is designed for
833  * performance and regression testing environments where we want to do
834  * tests with only a limited set of windows and ignore all other windows
835  *
836  * When it is set to a comma separated list of WM_CLASS class names, all
837  * windows not matching the list will be ignored.
838  *
839  * Returns TRUE if window has been filtered out and should be ignored.
840  */
841 static gboolean
maybe_filter_window(MetaDisplay * display,Window xwindow,gboolean must_be_viewable,XWindowAttributes * attrs)842 maybe_filter_window (MetaDisplay       *display,
843                      Window             xwindow,
844                      gboolean           must_be_viewable,
845                      XWindowAttributes *attrs)
846 {
847   static char **filter_wm_classes = NULL;
848   static gboolean initialized = FALSE;
849   XClassHint class_hint;
850   gboolean filtered;
851   Status success;
852   int i;
853 
854   if (!initialized)
855     {
856       const char *filter_string = g_getenv ("MUFFIN_WM_CLASS_FILTER");
857       if (filter_string)
858         filter_wm_classes = g_strsplit (filter_string, ",", -1);
859       initialized = TRUE;
860     }
861 
862   if (!filter_wm_classes || !filter_wm_classes[0])
863     return FALSE;
864 
865   filtered = TRUE;
866 
867   meta_error_trap_push (display);
868   success = XGetClassHint (display->xdisplay, xwindow, &class_hint);
869 
870   if (success)
871     {
872       for (i = 0; filter_wm_classes[i]; i++)
873         {
874           if (strcmp (class_hint.res_class, filter_wm_classes[i]) == 0)
875             {
876               filtered = FALSE;
877               break;
878             }
879         }
880 
881       XFree (class_hint.res_name);
882       XFree (class_hint.res_class);
883     }
884 
885   if (filtered)
886     {
887       /* We want to try and get the window managed by the next WM that come along,
888        * so we need to make sure that windows that are requested to be mapped while
889        * Muffin is running (!must_be_viewable), or windows already viewable at startup
890        * get a non-withdrawn WM_STATE property. Previously unmapped windows are left
891        * with whatever WM_STATE property they had.
892        */
893       if (!must_be_viewable || attrs->map_state == IsViewable)
894         {
895           gulong old_state;
896 
897           if (!meta_prop_get_cardinal_with_atom_type (display, xwindow,
898                                                       display->atom_WM_STATE,
899                                                       display->atom_WM_STATE,
900                                                       &old_state))
901             old_state = WithdrawnState;
902 
903           if (old_state == WithdrawnState)
904             set_wm_state_on_xwindow (display, xwindow, NormalState);
905         }
906 
907       /* Make sure filtered windows are hidden from view */
908       XUnmapWindow (display->xdisplay, xwindow);
909     }
910 
911   meta_error_trap_pop (display);
912 
913   return filtered;
914 }
915 
916 LOCAL_SYMBOL gboolean
meta_window_should_attach_to_parent(MetaWindow * window)917 meta_window_should_attach_to_parent (MetaWindow *window)
918 {
919   MetaWindow *parent;
920 
921   if (!meta_prefs_get_attach_modal_dialogs () ||
922       window->type != META_WINDOW_MODAL_DIALOG)
923     return FALSE;
924 
925   parent = meta_window_get_transient_for (window);
926   if (!parent)
927     return FALSE;
928 
929   switch (parent->type)
930     {
931     case META_WINDOW_NORMAL:
932     case META_WINDOW_DIALOG:
933     case META_WINDOW_MODAL_DIALOG:
934       return TRUE;
935 
936     default:
937       return FALSE;
938     }
939 }
940 
941 LOCAL_SYMBOL LOCAL_SYMBOL MetaWindow*
meta_window_new_with_attrs(MetaDisplay * display,Window xwindow,gboolean must_be_viewable,MetaCompEffect effect,XWindowAttributes * attrs)942 meta_window_new_with_attrs (MetaDisplay       *display,
943                             Window             xwindow,
944                             gboolean           must_be_viewable,
945                             MetaCompEffect     effect,
946                             XWindowAttributes *attrs)
947 {
948   MetaWindow *window;
949   GSList *tmp;
950   MetaWorkspace *space;
951   gulong existing_wm_state;
952   gulong event_mask;
953   MetaMoveResizeFlags flags;
954   gboolean has_shape;
955   MetaScreen *screen;
956 
957   g_assert (attrs != NULL);
958 
959   meta_verbose ("Attempting to manage 0x%lx\n", xwindow);
960 
961   if (meta_display_xwindow_is_a_no_focus_window (display, xwindow))
962     {
963       meta_verbose ("Not managing no_focus_window 0x%lx\n",
964                     xwindow);
965       return NULL;
966     }
967 
968   screen = NULL;
969   for (tmp = display->screens; tmp != NULL; tmp = tmp->next)
970     {
971       MetaScreen *scr = tmp->data;
972 
973       if (scr->xroot == attrs->root)
974         {
975           screen = tmp->data;
976           break;
977         }
978     }
979 
980   g_assert (screen);
981 
982   /* A black list of override redirect windows that we don't need to manage: */
983   if (attrs->override_redirect &&
984       (xwindow == screen->no_focus_window ||
985        xwindow == screen->flash_window ||
986        xwindow == screen->wm_sn_selection_window ||
987        attrs->class == InputOnly ||
988        /* any windows created via meta_create_offscreen_window: */
989        (attrs->x == -100 && attrs->y == -100
990 	&& attrs->width == 1 && attrs->height == 1) ||
991        xwindow == screen->wm_cm_selection_window ||
992        xwindow == screen->guard_window ||
993        xwindow == screen->composite_overlay_window
994       )
995      ) {
996     meta_verbose ("Not managing our own windows\n");
997     return NULL;
998   }
999 
1000   if (maybe_filter_window (display, xwindow, must_be_viewable, attrs))
1001     {
1002       meta_verbose ("Not managing filtered window\n");
1003       return NULL;
1004     }
1005 
1006   /* Grab server */
1007   meta_display_grab (display);
1008   meta_error_trap_push (display); /* Push a trap over all of window
1009                                    * creation, to reduce XSync() calls
1010                                    */
1011 
1012   meta_verbose ("must_be_viewable = %d attrs->map_state = %d (%s)\n",
1013                 must_be_viewable,
1014                 attrs->map_state,
1015                 (attrs->map_state == IsUnmapped) ?
1016                 "IsUnmapped" :
1017                 (attrs->map_state == IsViewable) ?
1018                 "IsViewable" :
1019                 (attrs->map_state == IsUnviewable) ?
1020                 "IsUnviewable" :
1021                 "(unknown)");
1022 
1023   existing_wm_state = WithdrawnState;
1024   if (must_be_viewable && attrs->map_state != IsViewable)
1025     {
1026       /* Only manage if WM_STATE is IconicState or NormalState */
1027       gulong state;
1028 
1029       /* WM_STATE isn't a cardinal, it's type WM_STATE, but is an int */
1030       if (!(meta_prop_get_cardinal_with_atom_type (display, xwindow,
1031                                                    display->atom_WM_STATE,
1032                                                    display->atom_WM_STATE,
1033                                                    &state) &&
1034             (state == IconicState || state == NormalState)))
1035         {
1036           meta_verbose ("Deciding not to manage unmapped or unviewable window 0x%lx\n", xwindow);
1037           meta_error_trap_pop (display);
1038           meta_display_ungrab (display);
1039           return NULL;
1040         }
1041 
1042       existing_wm_state = state;
1043       meta_verbose ("WM_STATE of %lx = %s\n", xwindow,
1044                     wm_state_to_string (existing_wm_state));
1045     }
1046 
1047   meta_error_trap_push_with_return (display);
1048 
1049   /*
1050    * XAddToSaveSet can only be called on windows created by a different client.
1051    * with Muffin we want to be able to create manageable windows from within
1052    * the process (such as a dummy desktop window), so we do not want this
1053    * call failing to prevent the window from being managed -- wrap it in its
1054    * own error trap (we use the _with_return() version here to ensure that
1055    * XSync() is done on the pop, otherwise the error will not get caught).
1056    */
1057   meta_error_trap_push_with_return (display);
1058   XAddToSaveSet (display->xdisplay, xwindow);
1059   meta_error_trap_pop_with_return (display);
1060 
1061   event_mask =
1062     PropertyChangeMask | EnterWindowMask | LeaveWindowMask |
1063     FocusChangeMask | ColormapChangeMask;
1064   if (attrs->override_redirect)
1065     event_mask |= StructureNotifyMask;
1066 
1067   /* If the window is from this client (a menu, say) we need to augment
1068    * the event mask, not replace it. For windows from other clients,
1069    * attrs->your_event_mask will be empty at this point.
1070    */
1071   XSelectInput (display->xdisplay, xwindow, attrs->your_event_mask | event_mask);
1072 
1073   has_shape = FALSE;
1074 #ifdef HAVE_SHAPE
1075   if (META_DISPLAY_HAS_SHAPE (display))
1076     {
1077       int x_bounding, y_bounding, x_clip, y_clip;
1078       unsigned w_bounding, h_bounding, w_clip, h_clip;
1079       int bounding_shaped, clip_shaped;
1080 
1081       XShapeSelectInput (display->xdisplay, xwindow, ShapeNotifyMask);
1082 
1083       XShapeQueryExtents (display->xdisplay, xwindow,
1084                           &bounding_shaped, &x_bounding, &y_bounding,
1085                           &w_bounding, &h_bounding,
1086                           &clip_shaped, &x_clip, &y_clip,
1087                           &w_clip, &h_clip);
1088 
1089       has_shape = bounding_shaped != FALSE;
1090 
1091       meta_topic (META_DEBUG_SHAPES,
1092                   "Window has_shape = %d extents %d,%d %u x %u\n",
1093                   has_shape, x_bounding, y_bounding,
1094                   w_bounding, h_bounding);
1095     }
1096 #endif
1097 
1098   /* Get rid of any borders */
1099   if (attrs->border_width != 0)
1100     XSetWindowBorderWidth (display->xdisplay, xwindow, 0);
1101 
1102   /* Get rid of weird gravities */
1103   if (attrs->win_gravity != NorthWestGravity)
1104     {
1105       XSetWindowAttributes set_attrs;
1106 
1107       set_attrs.win_gravity = NorthWestGravity;
1108 
1109       XChangeWindowAttributes (display->xdisplay,
1110                                xwindow,
1111                                CWWinGravity,
1112                                &set_attrs);
1113     }
1114 
1115   if (meta_error_trap_pop_with_return (display) != Success)
1116     {
1117       meta_verbose ("Window 0x%lx disappeared just as we tried to manage it\n",
1118                     xwindow);
1119       meta_error_trap_pop (display);
1120       meta_display_ungrab (display);
1121       return NULL;
1122     }
1123 
1124 
1125   window = g_object_new (META_TYPE_WINDOW, NULL);
1126 
1127   window->constructing = TRUE;
1128 
1129   window->dialog_pid = -1;
1130 
1131   window->xwindow = xwindow;
1132 
1133   /* this is in window->screen->display, but that's too annoying to
1134    * type
1135    */
1136   window->display = display;
1137   window->workspace = NULL;
1138 
1139 #ifdef HAVE_XSYNC
1140   window->sync_request_counter = None;
1141   window->sync_request_serial = 0;
1142   window->sync_request_timeout_id = 0;
1143   window->sync_request_alarm = None;
1144 #endif
1145 
1146   window->screen = screen;
1147 
1148   window->desc = g_strdup_printf ("0x%lx", window->xwindow);
1149 
1150   // -1 is used as a result in get_client_pid() if it's called with a non-window-argument
1151   window->client_pid = -2;
1152 
1153   window->override_redirect = attrs->override_redirect;
1154 
1155   /* avoid tons of stack updates */
1156   meta_stack_freeze (window->screen->stack);
1157 
1158   window->has_shape = has_shape;
1159 
1160   window->rect.x = attrs->x;
1161   window->rect.y = attrs->y;
1162   window->rect.width = attrs->width;
1163   window->rect.height = attrs->height;
1164 
1165   /* And border width, size_hints are the "request" */
1166   window->border_width = attrs->border_width;
1167   window->size_hints.x = attrs->x;
1168   window->size_hints.y = attrs->y;
1169   window->size_hints.width = attrs->width;
1170   window->size_hints.height = attrs->height;
1171   /* initialize the remaining size_hints as if size_hints.flags were zero */
1172   meta_set_normal_hints (window, NULL);
1173 
1174   /* And this is our unmaximized size */
1175   window->saved_rect = window->rect;
1176   window->user_rect = window->rect;
1177   window->snapped_rect = window->rect;
1178 
1179   window->depth = attrs->depth;
1180   window->xvisual = attrs->visual;
1181   window->colormap = attrs->colormap;
1182 
1183   window->title = NULL;
1184   window->icon_name = NULL;
1185   window->icon = NULL;
1186   window->icon_size = -1;
1187   meta_icon_cache_init (&window->icon_cache);
1188   window->theme_icon_name = NULL;
1189   window->wm_hints_pixmap = None;
1190   window->wm_hints_mask = None;
1191   window->wm_hints_urgent = FALSE;
1192 
1193   window->frame = NULL;
1194   window->has_focus = FALSE;
1195   window->attached_focus_window = NULL;
1196 
1197   window->hide_titlebar_when_maximized = FALSE;
1198 
1199   window->maximized_horizontally = FALSE;
1200   window->maximized_vertically = FALSE;
1201   window->tile_type = META_WINDOW_TILE_TYPE_NONE;
1202   window->snap_queued = FALSE;
1203   window->current_proximity_zone = 0;
1204   window->mouse_on_edge = FALSE;
1205   window->resizing_tile_type = META_WINDOW_TILE_TYPE_NONE;
1206   window->custom_snap_size = FALSE;
1207   window->zone_queued = ZONE_NONE;
1208   window->maximize_horizontally_after_placement = FALSE;
1209   window->maximize_vertically_after_placement = FALSE;
1210   window->minimize_after_placement = FALSE;
1211   window->tile_after_placement = FALSE;
1212   window->move_after_placement = FALSE;
1213   window->fullscreen = FALSE;
1214   window->fullscreen_after_placement = FALSE;
1215   window->fullscreen_monitors[0] = -1;
1216   window->require_fully_onscreen = TRUE;
1217   window->require_on_single_monitor = TRUE;
1218   window->require_titlebar_visible = TRUE;
1219   window->on_all_workspaces = FALSE;
1220   window->on_all_workspaces_requested = FALSE;
1221   window->tile_mode = META_TILE_NONE;
1222   window->last_tile_mode = META_TILE_NONE;
1223   window->resize_tile_mode = META_TILE_NONE;
1224   window->maybe_retile_maximize = FALSE;
1225   window->tile_monitor_number = -1;
1226   window->shaded = FALSE;
1227   window->initially_iconic = FALSE;
1228   window->minimized = FALSE;
1229   window->tab_unminimized = FALSE;
1230   window->iconic = FALSE;
1231   window->mapped = attrs->map_state != IsUnmapped;
1232   window->hidden = FALSE;
1233   window->visible_to_compositor = FALSE;
1234   window->pending_compositor_effect = effect;
1235   /* if already mapped, no need to worry about focus-on-first-time-showing */
1236   window->showing_for_first_time = !window->mapped;
1237   /* if already mapped we don't want to do the placement thing;
1238    * override-redirect windows are placed by the app */
1239   window->placed = ((window->mapped && !window->hidden) || window->override_redirect);
1240 #ifdef WITH_VERBOSE_MODE
1241   if (window->placed)
1242     meta_topic (META_DEBUG_PLACEMENT,
1243                 "Not placing window 0x%lx since it's already mapped\n",
1244                 xwindow);
1245 #endif
1246   window->force_save_user_rect = TRUE;
1247   window->denied_focus_and_not_transient = FALSE;
1248   window->unmanaging = FALSE;
1249   window->is_in_queues = 0;
1250   window->keys_grabbed = FALSE;
1251   window->grab_on_frame = FALSE;
1252   window->all_keys_grabbed = FALSE;
1253   window->withdrawn = FALSE;
1254   window->initial_workspace_set = FALSE;
1255   window->initial_timestamp_set = FALSE;
1256   window->net_wm_user_time_set = FALSE;
1257   window->user_time_window = None;
1258   window->take_focus = FALSE;
1259   window->delete_window = FALSE;
1260   window->net_wm_ping = FALSE;
1261   window->input = TRUE;
1262   window->calc_placement = FALSE;
1263   window->shaken_loose = FALSE;
1264   window->have_focus_click_grab = FALSE;
1265   window->disable_sync = FALSE;
1266 
1267   window->unmaps_pending = 0;
1268 
1269   window->mwm_decorated = TRUE;
1270   window->mwm_border_only = FALSE;
1271   window->mwm_has_close_func = TRUE;
1272   window->mwm_has_minimize_func = TRUE;
1273   window->mwm_has_maximize_func = TRUE;
1274   window->mwm_has_move_func = TRUE;
1275   window->mwm_has_resize_func = TRUE;
1276 
1277   window->decorated = TRUE;
1278   window->has_close_func = TRUE;
1279   window->has_minimize_func = TRUE;
1280   window->has_maximize_func = TRUE;
1281   window->has_move_func = TRUE;
1282   window->has_resize_func = TRUE;
1283 
1284   window->has_shade_func = TRUE;
1285 
1286   window->has_fullscreen_func = TRUE;
1287 
1288   window->always_sticky = FALSE;
1289 
1290   window->wm_state_modal = FALSE;
1291   window->skip_taskbar = FALSE;
1292   window->skip_pager = FALSE;
1293   window->wm_state_skip_taskbar = FALSE;
1294   window->wm_state_skip_pager = FALSE;
1295   window->wm_state_above = FALSE;
1296   window->wm_state_below = FALSE;
1297   window->wm_state_demands_attention = FALSE;
1298 
1299   window->res_class = NULL;
1300   window->res_name = NULL;
1301   window->role = NULL;
1302   window->sm_client_id = NULL;
1303   window->wm_client_machine = NULL;
1304   window->startup_id = NULL;
1305 
1306   window->net_wm_pid = -1;
1307 
1308   window->xtransient_for = None;
1309   window->xclient_leader = None;
1310   window->transient_parent_is_root_window = FALSE;
1311 
1312   window->type = META_WINDOW_NORMAL;
1313   window->type_atom = None;
1314 
1315   window->struts = NULL;
1316 
1317   window->using_net_wm_name              = FALSE;
1318   window->using_net_wm_visible_name      = FALSE;
1319   window->using_net_wm_icon_name         = FALSE;
1320   window->using_net_wm_visible_icon_name = FALSE;
1321 
1322   window->need_reread_icon = TRUE;
1323 
1324   window->layer = META_LAYER_LAST; /* invalid value */
1325   window->stack_position = -1;
1326   window->initial_workspace = 0; /* not used */
1327   window->initial_timestamp = 0; /* not used */
1328 
1329   window->compositor_private = NULL;
1330 
1331   window->monitor = meta_screen_get_monitor_for_window (window->screen, window);
1332 
1333   window->tile_match = NULL;
1334 
1335   if (window->override_redirect)
1336     {
1337       window->decorated = FALSE;
1338       window->always_sticky = TRUE;
1339       window->has_close_func = FALSE;
1340       window->has_shade_func = FALSE;
1341       window->has_move_func = FALSE;
1342       window->has_resize_func = FALSE;
1343     }
1344 
1345   meta_display_register_x_window (display, &window->xwindow, window);
1346 
1347   /* Assign this #MetaWindow a sequence number which can be used
1348    * for sorting.
1349    */
1350   window->stable_sequence = ++display->window_sequence_counter;
1351 
1352   /* assign the window to its group, or create a new group if needed
1353    */
1354   window->group = NULL;
1355   window->xgroup_leader = None;
1356   meta_window_compute_group (window);
1357 
1358   meta_window_load_initial_properties (window);
1359 
1360   if (!window->override_redirect)
1361     {
1362       update_sm_hints (window); /* must come after transient_for */
1363 
1364       meta_window_update_role (window);
1365     }
1366 
1367   meta_window_update_net_wm_type (window);
1368 
1369   if (window->decorated)
1370     meta_window_ensure_frame (window);
1371 
1372   if (window->initially_iconic)
1373     {
1374       /* WM_HINTS said minimized */
1375       window->minimized = TRUE;
1376       meta_verbose ("Window %s asked to start out minimized\n", window->desc);
1377     }
1378 
1379   if (existing_wm_state == IconicState)
1380     {
1381       /* WM_STATE said minimized */
1382       window->minimized = TRUE;
1383       meta_verbose ("Window %s had preexisting WM_STATE = IconicState, minimizing\n",
1384                     window->desc);
1385 
1386       /* Assume window was previously placed, though perhaps it's
1387        * been iconic its whole life, we have no way of knowing.
1388        */
1389       window->placed = TRUE;
1390     }
1391 
1392   /* Apply any window attributes such as initial workspace
1393    * based on startup notification
1394    */
1395   meta_screen_apply_startup_properties (window->screen, window);
1396 
1397   /* Try to get a "launch timestamp" for the window.  If the window is
1398    * a transient, we'd like to be able to get a last-usage timestamp
1399    * from the parent window.  If the window has no parent, there isn't
1400    * much we can do...except record the current time so that any children
1401    * can use this time as a fallback.
1402    */
1403   if (!window->override_redirect && !window->net_wm_user_time_set) {
1404     MetaWindow *parent = NULL;
1405     if (window->xtransient_for)
1406       parent = meta_display_lookup_x_window (window->display,
1407                                              window->xtransient_for);
1408 
1409     /* First, maybe the app was launched with startup notification using an
1410      * obsolete version of the spec; use that timestamp if it exists.
1411      */
1412     if (window->initial_timestamp_set)
1413       /* NOTE: Do NOT toggle net_wm_user_time_set to true; this is just
1414        * being recorded as a fallback for potential transients
1415        */
1416       window->net_wm_user_time = window->initial_timestamp;
1417     else if (parent != NULL)
1418       meta_window_set_user_time(window, parent->net_wm_user_time);
1419     else
1420       /* NOTE: Do NOT toggle net_wm_user_time_set to true; this is just
1421        * being recorded as a fallback for potential transients
1422        */
1423       window->net_wm_user_time =
1424         meta_display_get_current_time_roundtrip (window->display);
1425   }
1426 
1427   window->attached = meta_window_should_attach_to_parent (window);
1428   if (window->attached)
1429     recalc_window_features (window);
1430 
1431   meta_window_grab_keys (window);
1432   if (window->type != META_WINDOW_DOCK && !window->override_redirect)
1433     {
1434       meta_display_grab_window_buttons (window->display, window->xwindow);
1435       meta_display_grab_focus_window_button (window->display, window);
1436     }
1437 
1438   if (window->type == META_WINDOW_DESKTOP ||
1439       window->type == META_WINDOW_DOCK)
1440     {
1441       /* Change the default, but don't enforce this if the user
1442        * focuses the dock/desktop and unsticks it using key shortcuts.
1443        * Need to set this before adding to the workspaces so the MRU
1444        * lists will be updated.
1445        */
1446       window->on_all_workspaces_requested = TRUE;
1447     }
1448 
1449   window->on_all_workspaces = should_be_on_all_workspaces (window);
1450 
1451   /* For the workspace, first honor hints,
1452    * if that fails put transients with parents,
1453    * otherwise put window on active space
1454    */
1455 
1456   if (window->initial_workspace_set)
1457     {
1458       if (window->initial_workspace == (int) 0xFFFFFFFF)
1459         {
1460           meta_topic (META_DEBUG_PLACEMENT,
1461                       "Window %s is initially on all spaces\n",
1462                       window->desc);
1463 
1464 	  /* need to set on_all_workspaces first so that it will be
1465 	   * added to all the MRU lists
1466 	   */
1467           window->on_all_workspaces_requested = TRUE;
1468           window->on_all_workspaces = TRUE;
1469           meta_workspace_add_window (window->screen->active_workspace, window);
1470         }
1471       else
1472         {
1473           meta_topic (META_DEBUG_PLACEMENT,
1474                       "Window %s is initially on space %d\n",
1475                       window->desc, window->initial_workspace);
1476 
1477           space =
1478             meta_screen_get_workspace_by_index (window->screen,
1479                                                 window->initial_workspace);
1480 
1481           if (space)
1482             meta_workspace_add_window (space, window);
1483         }
1484     }
1485 
1486   /* override-redirect windows are subtly different from other windows
1487    * with window->on_all_workspaces == TRUE. Other windows are part of
1488    * some workspace (so they can return to that if the flag is turned off),
1489    * but appear on other workspaces. override-redirect windows are part
1490    * of no workspace.
1491    */
1492   if (!window->override_redirect)
1493     {
1494       if (window->workspace == NULL &&
1495           window->xtransient_for != None)
1496         {
1497           /* Try putting dialog on parent's workspace */
1498           MetaWindow *parent;
1499 
1500           parent = meta_display_lookup_x_window (window->display,
1501                                                  window->xtransient_for);
1502 
1503           if (parent && parent->workspace)
1504             {
1505               meta_topic (META_DEBUG_PLACEMENT,
1506                           "Putting window %s on same workspace as parent %s\n",
1507                           window->desc, parent->desc);
1508 
1509               if (parent->on_all_workspaces_requested)
1510                 {
1511                   window->on_all_workspaces_requested = TRUE;
1512                   window->on_all_workspaces = TRUE;
1513                 }
1514 
1515               /* this will implicitly add to the appropriate MRU lists
1516                */
1517               meta_workspace_add_window (parent->workspace, window);
1518             }
1519         }
1520 
1521       if (window->workspace == NULL)
1522         {
1523           meta_topic (META_DEBUG_PLACEMENT,
1524                       "Putting window %s on active workspace\n",
1525                       window->desc);
1526 
1527           space = window->screen->active_workspace;
1528 
1529           meta_workspace_add_window (space, window);
1530         }
1531 
1532       /* for the various on_all_workspaces = TRUE possible above */
1533       meta_window_set_current_workspace_hint (window);
1534 
1535       meta_window_update_struts (window);
1536     }
1537 
1538   g_signal_emit_by_name (window->screen, "window-entered-monitor", window->monitor->number, window);
1539   g_signal_emit_by_name (window->screen, "window-added", window, window->monitor->number);
1540 
1541   /* Must add window to stack before doing move/resize, since the
1542    * window might have fullscreen size (i.e. should have been
1543    * fullscreen'd; acrobat is one such braindead case; it withdraws
1544    * and remaps its window whenever trying to become fullscreen...)
1545    * and thus constraints may try to auto-fullscreen it which also
1546    * means restacking it.
1547    */
1548   if (!window->override_redirect)
1549     meta_stack_add (window->screen->stack,
1550                     window);
1551   else
1552     window->layer = META_LAYER_OVERRIDE_REDIRECT; /* otherwise set by MetaStack */
1553 
1554   /* Put our state back where it should be,
1555    * passing TRUE for is_configure_request, ICCCM says
1556    * initial map is handled same as configure request
1557    */
1558   flags =
1559     META_IS_CONFIGURE_REQUEST | META_IS_MOVE_ACTION | META_IS_RESIZE_ACTION;
1560   if (!window->override_redirect)
1561     meta_window_move_resize_internal (window,
1562                                       flags,
1563                                       window->size_hints.win_gravity,
1564                                       window->size_hints.x,
1565                                       window->size_hints.y,
1566                                       window->size_hints.width,
1567                                       window->size_hints.height);
1568 
1569   /* Now try applying saved stuff from the session */
1570   {
1571     const MetaWindowSessionInfo *info;
1572 
1573     info = meta_window_lookup_saved_state (window);
1574 
1575     if (info)
1576       {
1577         meta_window_apply_session_info (window, info);
1578         meta_window_release_saved_state (info);
1579       }
1580   }
1581 
1582   if (!window->override_redirect)
1583     {
1584       /* FIXME we have a tendency to set this then immediately
1585        * change it again.
1586        */
1587       set_wm_state (window, window->iconic ? IconicState : NormalState);
1588       set_net_wm_state (window);
1589     }
1590 
1591   meta_compositor_add_window (screen->display->compositor, window);
1592 
1593   /* Sync stack changes */
1594   meta_stack_thaw (window->screen->stack);
1595 
1596   /* Usually the we'll have queued a stack sync anyways, because we've
1597    * added a new frame window or restacked. But if an undecorated
1598    * window is mapped, already stacked in the right place, then we
1599    * might need to do this explicitly.
1600    */
1601   meta_stack_tracker_queue_sync_stack (window->screen->stack_tracker);
1602 
1603   /* disable show desktop mode unless we're a desktop component */
1604   maybe_leave_show_desktop_mode (window);
1605 
1606   meta_window_queue (window, META_QUEUE_CALC_SHOWING);
1607   /* See bug 303284; a transient of the given window can already exist, in which
1608    * case we think it should probably be shown.
1609    */
1610   meta_window_foreach_transient (window,
1611                                  queue_calc_showing_func,
1612                                  NULL);
1613   /* See bug 334899; the window may have minimized ancestors
1614    * which need to be shown.
1615    *
1616    * However, we shouldn't unminimize windows here when opening
1617    * a new display because that breaks passing _NET_WM_STATE_HIDDEN
1618    * between window managers when replacing them; see bug 358042.
1619    *
1620    * And we shouldn't unminimize windows if they were initially
1621    * iconic.
1622    */
1623   if (!window->override_redirect &&
1624       !display->display_opening &&
1625       !window->initially_iconic)
1626     unminimize_window_and_all_transient_parents (window);
1627 
1628   meta_error_trap_pop (display); /* pop the XSync()-reducing trap */
1629   meta_display_ungrab (display);
1630 
1631   window->constructing = FALSE;
1632 
1633   meta_display_notify_window_created (display, window);
1634 
1635   if (window->wm_state_demands_attention)
1636     g_signal_emit_by_name (window->display, "window-demands-attention", window);
1637 
1638   if (window->wm_hints_urgent)
1639     g_signal_emit_by_name (window->display, "window-marked-urgent", window);
1640 
1641   return window;
1642 }
1643 
1644 /* This function should only be called from the end of meta_window_new_with_attrs () */
1645 static void
meta_window_apply_session_info(MetaWindow * window,const MetaWindowSessionInfo * info)1646 meta_window_apply_session_info (MetaWindow *window,
1647                                 const MetaWindowSessionInfo *info)
1648 {
1649   if (info->stack_position_set)
1650     {
1651       meta_topic (META_DEBUG_SM,
1652                   "Restoring stack position %d for window %s\n",
1653                   info->stack_position, window->desc);
1654 
1655       /* FIXME well, I'm not sure how to do this. */
1656     }
1657 
1658   if (info->minimized_set)
1659     {
1660       meta_topic (META_DEBUG_SM,
1661                   "Restoring minimized state %d for window %s\n",
1662                   info->minimized, window->desc);
1663 
1664       if (window->has_minimize_func && info->minimized)
1665         meta_window_minimize (window);
1666     }
1667 
1668   if (info->maximized_set)
1669     {
1670       meta_topic (META_DEBUG_SM,
1671                   "Restoring maximized state %d for window %s\n",
1672                   info->maximized, window->desc);
1673 
1674       if (window->has_maximize_func && info->maximized)
1675         {
1676           meta_window_maximize (window,
1677                                 META_MAXIMIZE_HORIZONTAL |
1678                                 META_MAXIMIZE_VERTICAL);
1679 
1680           if (info->saved_rect_set)
1681             {
1682               meta_topic (META_DEBUG_SM,
1683                           "Restoring saved rect %d,%d %dx%d for window %s\n",
1684                           info->saved_rect.x,
1685                           info->saved_rect.y,
1686                           info->saved_rect.width,
1687                           info->saved_rect.height,
1688                           window->desc);
1689 
1690               window->saved_rect.x = info->saved_rect.x;
1691               window->saved_rect.y = info->saved_rect.y;
1692               window->saved_rect.width = info->saved_rect.width;
1693               window->saved_rect.height = info->saved_rect.height;
1694             }
1695 	}
1696     }
1697 
1698   if (info->on_all_workspaces_set)
1699     {
1700       window->on_all_workspaces_requested = info->on_all_workspaces;
1701       meta_window_update_on_all_workspaces (window);
1702       meta_topic (META_DEBUG_SM,
1703                   "Restoring sticky state %d for window %s\n",
1704                   window->on_all_workspaces_requested, window->desc);
1705     }
1706 
1707   if (info->workspace_indices)
1708     {
1709       GSList *tmp;
1710       GSList *spaces;
1711 
1712       spaces = NULL;
1713 
1714       tmp = info->workspace_indices;
1715       while (tmp != NULL)
1716         {
1717           MetaWorkspace *space;
1718 
1719           space =
1720             meta_screen_get_workspace_by_index (window->screen,
1721                                                 GPOINTER_TO_INT (tmp->data));
1722 
1723           if (space)
1724             spaces = g_slist_prepend (spaces, space);
1725 
1726           tmp = tmp->next;
1727         }
1728 
1729       if (spaces)
1730         {
1731           /* This briefly breaks the invariant that we are supposed
1732            * to always be on some workspace. But we paranoically
1733            * ensured that one of the workspaces from the session was
1734            * indeed valid, so we know we'll go right back to one.
1735            */
1736           if (window->workspace)
1737             meta_workspace_remove_window (window->workspace, window);
1738 
1739           /* Only restore to the first workspace if the window
1740            * happened to be on more than one, since we have replaces
1741            * window->workspaces with window->workspace
1742            */
1743           meta_workspace_add_window (spaces->data, window);
1744 
1745           meta_topic (META_DEBUG_SM,
1746                       "Restoring saved window %s to workspace %d\n",
1747                       window->desc,
1748                       meta_workspace_index (spaces->data));
1749 
1750           g_slist_free (spaces);
1751         }
1752     }
1753 
1754   if (info->geometry_set)
1755     {
1756       int x, y, w, h;
1757       MetaMoveResizeFlags flags;
1758 
1759       window->placed = TRUE; /* don't do placement algorithms later */
1760 
1761       x = info->rect.x;
1762       y = info->rect.y;
1763 
1764       w = window->size_hints.base_width +
1765         info->rect.width * window->size_hints.width_inc;
1766       h = window->size_hints.base_height +
1767         info->rect.height * window->size_hints.height_inc;
1768 
1769       /* Force old gravity, ignoring anything now set */
1770       window->size_hints.win_gravity = info->gravity;
1771 
1772       meta_topic (META_DEBUG_SM,
1773                   "Restoring pos %d,%d size %d x %d for %s\n",
1774                   x, y, w, h, window->desc);
1775 
1776       flags = META_DO_GRAVITY_ADJUST | META_IS_MOVE_ACTION | META_IS_RESIZE_ACTION;
1777       meta_window_move_resize_internal (window,
1778                                         flags,
1779                                         window->size_hints.win_gravity,
1780                                         x, y, w, h);
1781     }
1782 }
1783 
1784 static gboolean
detach_foreach_func(MetaWindow * window,void * data)1785 detach_foreach_func (MetaWindow *window,
1786                      void       *data)
1787 {
1788   if (window->attached)
1789     {
1790       GList **children = data;
1791       MetaWindow *parent;
1792 
1793       /* Only return the immediate children of the window being unmanaged */
1794       parent = meta_window_get_transient_for (window);
1795       if (parent->unmanaging)
1796         *children = g_list_prepend (*children, window);
1797     }
1798 
1799   return TRUE;
1800 }
1801 
1802 LOCAL_SYMBOL void
meta_window_unmanage(MetaWindow * window,guint32 timestamp)1803 meta_window_unmanage (MetaWindow  *window,
1804                       guint32      timestamp)
1805 {
1806   GList *tmp;
1807 
1808   meta_verbose ("Unmanaging 0x%lx\n", window->xwindow);
1809 
1810   if (window->visible_to_compositor || meta_window_is_attached_dialog (window))
1811     meta_compositor_hide_window (window->display->compositor, window,
1812                                  META_COMP_EFFECT_DESTROY);
1813 
1814   meta_compositor_remove_window (window->display->compositor, window);
1815 
1816   if (window->display->window_with_menu == window)
1817     {
1818       meta_ui_window_menu_free (window->display->window_menu);
1819       window->display->window_menu = NULL;
1820       window->display->window_with_menu = NULL;
1821     }
1822 
1823   if (destroying_windows_disallowed > 0)
1824     meta_bug ("Tried to destroy window %s while destruction was not allowed\n",
1825               window->desc);
1826 
1827   window->unmanaging = TRUE;
1828 
1829   if (meta_prefs_get_attach_modal_dialogs ())
1830     {
1831       GList *attached_children = NULL, *iter;
1832 
1833       /* Detach any attached dialogs by unmapping and letting them
1834        * be remapped after @window is destroyed.
1835        */
1836       meta_window_foreach_transient (window,
1837                                      detach_foreach_func,
1838                                      &attached_children);
1839       for (iter = attached_children; iter; iter = iter->next)
1840         meta_window_unmanage (iter->data, timestamp);
1841       g_list_free (attached_children);
1842     }
1843 
1844   if (window->fullscreen)
1845     {
1846       MetaGroup *group;
1847 
1848       /* If the window is fullscreen, it may be forcing
1849        * other windows in its group to a higher layer
1850        */
1851 
1852       meta_stack_freeze (window->screen->stack);
1853       group = meta_window_get_group (window);
1854       if (group)
1855         meta_group_update_layers (group);
1856       meta_stack_thaw (window->screen->stack);
1857     }
1858 
1859   meta_window_shutdown_group (window); /* safe to do this early as
1860                                         * group.c won't re-add to the
1861                                         * group if window->unmanaging
1862                                         */
1863 
1864   /* If we have the focus, focus some other window.
1865    * This is done first, so that if the unmap causes
1866    * an EnterNotify the EnterNotify will have final say
1867    * on what gets focused, maintaining sloppy focus
1868    * invariants.
1869    */
1870   if (meta_window_appears_focused (window))
1871     meta_window_propagate_focus_appearance (window, FALSE);
1872   if (window->has_focus)
1873     {
1874       meta_topic (META_DEBUG_FOCUS,
1875                   "Focusing default window since we're unmanaging %s\n",
1876                   window->desc);
1877       meta_workspace_focus_default_window (window->screen->active_workspace,
1878                                            window,
1879                                            timestamp);
1880     }
1881   else if (window->display->expected_focus_window == window)
1882     {
1883       meta_topic (META_DEBUG_FOCUS,
1884                   "Focusing default window since expected focus window freed %s\n",
1885                   window->desc);
1886       window->display->expected_focus_window = NULL;
1887       meta_workspace_focus_default_window (window->screen->active_workspace,
1888                                            window,
1889                                            timestamp);
1890     }
1891   else
1892     {
1893       meta_topic (META_DEBUG_FOCUS,
1894                   "Unmanaging window %s which doesn't currently have focus\n",
1895                   window->desc);
1896     }
1897 
1898   if (window->struts)
1899     {
1900       meta_free_gslist_and_elements (window->struts);
1901       window->struts = NULL;
1902 
1903       meta_topic (META_DEBUG_WORKAREA,
1904                   "Unmanaging window %s which has struts, so invalidating work areas\n",
1905                   window->desc);
1906       invalidate_work_areas (window);
1907     }
1908 
1909 #ifdef HAVE_XSYNC
1910   if (window->sync_request_timeout_id)
1911     {
1912       g_source_remove (window->sync_request_timeout_id);
1913       window->sync_request_timeout_id = 0;
1914     }
1915 #endif
1916 
1917   if (window->display->grab_window == window)
1918     meta_display_end_grab_op (window->display, timestamp);
1919 
1920   g_assert (window->display->grab_window != window);
1921 
1922   if (window->display->focus_window == window)
1923     {
1924       window->display->focus_window = NULL;
1925       g_object_notify (G_OBJECT (window->display), "focus-window");
1926     }
1927 
1928   if (window->maximized_horizontally || window->maximized_vertically ||
1929       window->tile_type != META_WINDOW_TILE_TYPE_NONE)
1930     unmaximize_window_before_freeing (window);
1931 
1932   meta_window_unqueue (window, META_QUEUE_CALC_SHOWING |
1933                                META_QUEUE_MOVE_RESIZE);
1934   meta_window_free_delete_dialog (window);
1935 
1936   if (window->workspace)
1937     meta_workspace_remove_window (window->workspace, window);
1938 
1939   g_assert (window->workspace == NULL);
1940 
1941 #ifndef G_DISABLE_CHECKS
1942   tmp = window->screen->workspaces;
1943   while (tmp != NULL)
1944     {
1945       MetaWorkspace *workspace = tmp->data;
1946 
1947       g_assert (g_list_find (workspace->windows, window) == NULL);
1948       g_assert (g_list_find (workspace->mru_list, window) == NULL);
1949 
1950       tmp = tmp->next;
1951     }
1952 #endif
1953 
1954   if (window->monitor)
1955     {
1956       g_signal_emit_by_name (window->screen, "window-left-monitor",
1957                              window->monitor->number, window);
1958       window->monitor = NULL;
1959     }
1960 
1961   if (!window->override_redirect)
1962     meta_stack_remove (window->screen->stack, window);
1963 
1964   meta_window_destroy_sync_request_alarm (window);
1965 
1966   /* If an undecorated window is being withdrawn, that will change the
1967    * stack as presented to the compositing manager, without actually
1968    * changing the stacking order of X windows.
1969    */
1970   meta_stack_tracker_queue_sync_stack (window->screen->stack_tracker);
1971 
1972   if (window->withdrawn)
1973     {
1974       /* We need to clean off the window's state so it
1975        * won't be restored if the app maps it again.
1976        */
1977       meta_error_trap_push (window->display);
1978       meta_verbose ("Cleaning state from window %s\n", window->desc);
1979       XDeleteProperty (window->display->xdisplay,
1980                        window->xwindow,
1981                        window->display->atom__NET_WM_DESKTOP);
1982       XDeleteProperty (window->display->xdisplay,
1983                        window->xwindow,
1984                        window->display->atom__NET_WM_STATE);
1985       XDeleteProperty (window->display->xdisplay,
1986                        window->xwindow,
1987                        window->display->atom__NET_WM_FULLSCREEN_MONITORS);
1988       set_wm_state (window, WithdrawnState);
1989       meta_error_trap_pop (window->display);
1990     }
1991   else
1992     {
1993       /* We need to put WM_STATE so that others will understand it on
1994        * restart.
1995        */
1996       if (!window->minimized)
1997         {
1998           meta_error_trap_push (window->display);
1999           set_wm_state (window, NormalState);
2000           meta_error_trap_pop (window->display);
2001         }
2002 
2003       /* If we're unmanaging a window that is not withdrawn, then
2004        * either (a) muffin is exiting, in which case we need to map
2005        * the window so the next WM will know that it's not Withdrawn,
2006        * or (b) we want to create a new MetaWindow to replace the
2007        * current one, which will happen automatically if we re-map
2008        * the X Window.
2009        */
2010       meta_error_trap_push (window->display);
2011       XMapWindow (window->display->xdisplay,
2012                   window->xwindow);
2013       meta_error_trap_pop (window->display);
2014     }
2015 
2016   meta_window_ungrab_keys (window);
2017   meta_display_ungrab_window_buttons (window->display, window->xwindow);
2018   meta_display_ungrab_focus_window_button (window->display, window);
2019 
2020   meta_display_unregister_x_window (window->display, window->xwindow);
2021 
2022 
2023   meta_error_trap_push (window->display);
2024 
2025   /* Put back anything we messed up */
2026   if (window->border_width != 0)
2027     XSetWindowBorderWidth (window->display->xdisplay,
2028                            window->xwindow,
2029                            window->border_width);
2030 
2031   /* No save set */
2032   XRemoveFromSaveSet (window->display->xdisplay,
2033                       window->xwindow);
2034 
2035   /* Even though the window is now unmanaged, we can't unselect events. This
2036    * window might be a window from this process, like a GdkMenu, in
2037    * which case it will have pointer events and so forth selected
2038    * for it by GDK. There's no way to disentangle those events from the events
2039    * we've selected. Even for a window from a different X client,
2040    * GDK could also have selected events for it for IPC purposes, so we
2041    * can't unselect in that case either.
2042    *
2043    * Similarly, we can't unselected for events on window->user_time_window.
2044    * It might be our own GDK focus window, or it might be a window that a
2045    * different client is using for multiple different things:
2046    * _NET_WM_USER_TIME_WINDOW and IPC, perhaps.
2047    */
2048 
2049   if (window->user_time_window != None)
2050     {
2051       meta_display_unregister_x_window (window->display,
2052                                         window->user_time_window);
2053       window->user_time_window = None;
2054     }
2055 
2056 #ifdef HAVE_SHAPE
2057   if (META_DISPLAY_HAS_SHAPE (window->display))
2058     XShapeSelectInput (window->display->xdisplay, window->xwindow, NoEventMask);
2059 #endif
2060 
2061   meta_error_trap_pop (window->display);
2062 
2063   meta_prefs_remove_listener (prefs_changed_callback, window);
2064 
2065   meta_screen_queue_check_fullscreen (window->screen);
2066 
2067   if (window->frame)
2068     {
2069       /* The XReparentWindow call in meta_window_destroy_frame() moves the
2070        * window so we need to send a configure notify; see bug 399552.  (We
2071        * also do this just in case a window got unmaximized.)
2072        */
2073       send_configure_notify (window);
2074 
2075       meta_window_destroy_frame (window);
2076     }
2077 
2078   g_signal_emit (window, window_signals[UNMANAGED], 0);
2079   g_signal_emit_by_name (window->screen, "window-removed", window);
2080 
2081   g_object_unref (window);
2082 }
2083 
2084 static gboolean
should_be_on_all_workspaces(MetaWindow * window)2085 should_be_on_all_workspaces (MetaWindow *window)
2086 {
2087   return
2088     window->on_all_workspaces_requested ||
2089     window->override_redirect ||
2090     (meta_prefs_get_workspaces_only_on_primary () &&
2091      !meta_window_is_on_primary_monitor (window));
2092 }
2093 
2094 LOCAL_SYMBOL void
meta_window_update_on_all_workspaces(MetaWindow * window)2095 meta_window_update_on_all_workspaces (MetaWindow *window)
2096 {
2097   gboolean old_value;
2098 
2099   old_value = window->on_all_workspaces;
2100 
2101   window->on_all_workspaces = should_be_on_all_workspaces (window);
2102 
2103   if (window->on_all_workspaces != old_value &&
2104       !window->override_redirect)
2105     {
2106       if (window->on_all_workspaces)
2107         {
2108           GList* tmp = window->screen->workspaces;
2109 
2110           /* Add to all MRU lists */
2111           while (tmp)
2112             {
2113               MetaWorkspace* work = (MetaWorkspace*) tmp->data;
2114               if (!g_list_find (work->mru_list, window))
2115                 work->mru_list = g_list_prepend (work->mru_list, window);
2116 
2117               tmp = tmp->next;
2118             }
2119         }
2120       else
2121         {
2122           GList* tmp = window->screen->workspaces;
2123 
2124           /* Remove from MRU lists except the window's workspace */
2125           while (tmp)
2126             {
2127               MetaWorkspace* work = (MetaWorkspace*) tmp->data;
2128               if (work != window->workspace)
2129                 work->mru_list = g_list_remove (work->mru_list, window);
2130               tmp = tmp->next;
2131             }
2132         }
2133       meta_window_set_current_workspace_hint (window);
2134     }
2135     meta_screen_update_snapped_windows (window->screen);
2136 }
2137 
2138 static void
set_wm_state_on_xwindow(MetaDisplay * display,Window xwindow,int state)2139 set_wm_state_on_xwindow (MetaDisplay *display,
2140                          Window       xwindow,
2141                          int          state)
2142 {
2143   unsigned long data[2];
2144 
2145   /* Muffin doesn't use icon windows, so data[1] should be None
2146    * according to the ICCCM 2.0 Section 4.1.3.1.
2147    */
2148   data[0] = state;
2149   data[1] = None;
2150 
2151   meta_error_trap_push (display);
2152   XChangeProperty (display->xdisplay, xwindow,
2153                    display->atom_WM_STATE,
2154                    display->atom_WM_STATE,
2155                    32, PropModeReplace, (guchar*) data, 2);
2156   meta_error_trap_pop (display);
2157 }
2158 
2159 static void
set_wm_state(MetaWindow * window,int state)2160 set_wm_state (MetaWindow *window,
2161               int         state)
2162 {
2163   meta_verbose ("Setting wm state %s on %s\n",
2164                 wm_state_to_string (state), window->desc);
2165 
2166   set_wm_state_on_xwindow (window->display, window->xwindow, state);
2167 }
2168 
2169 static void
set_net_wm_state(MetaWindow * window)2170 set_net_wm_state (MetaWindow *window)
2171 {
2172   int i;
2173   unsigned long data[14];
2174 
2175   i = 0;
2176   if (window->shaded)
2177     {
2178       data[i] = window->display->atom__NET_WM_STATE_SHADED;
2179       ++i;
2180     }
2181   if (window->wm_state_modal)
2182     {
2183       data[i] = window->display->atom__NET_WM_STATE_MODAL;
2184       ++i;
2185     }
2186   if (window->skip_pager)
2187     {
2188       data[i] = window->display->atom__NET_WM_STATE_SKIP_PAGER;
2189       ++i;
2190     }
2191   if (window->skip_taskbar)
2192     {
2193       data[i] = window->display->atom__NET_WM_STATE_SKIP_TASKBAR;
2194       ++i;
2195     }
2196   if (window->maximized_horizontally)
2197     {
2198       data[i] = window->display->atom__NET_WM_STATE_MAXIMIZED_HORZ;
2199       ++i;
2200     }
2201   /* As of 3.10, Gtk considers _NET_WM_STATE_MAXIMIZED_VERT to be a tiled window also */
2202   if (window->maximized_vertically || window->tile_type != META_WINDOW_TILE_TYPE_NONE)
2203     {
2204       data[i] = window->display->atom__NET_WM_STATE_MAXIMIZED_VERT;
2205       ++i;
2206     }
2207   if (window->tile_type != META_WINDOW_TILE_TYPE_NONE)
2208     {
2209       data[i] = window->display->atom__NET_WM_STATE_TILED;
2210       ++i;
2211     }
2212   if (window->fullscreen)
2213     {
2214       data[i] = window->display->atom__NET_WM_STATE_FULLSCREEN;
2215       ++i;
2216     }
2217   if (!meta_window_showing_on_its_workspace (window) || window->shaded)
2218     {
2219       data[i] = window->display->atom__NET_WM_STATE_HIDDEN;
2220       ++i;
2221     }
2222   if (window->wm_state_above)
2223     {
2224       data[i] = window->display->atom__NET_WM_STATE_ABOVE;
2225       ++i;
2226     }
2227   if (window->wm_state_below)
2228     {
2229       data[i] = window->display->atom__NET_WM_STATE_BELOW;
2230       ++i;
2231     }
2232   if (window->wm_state_demands_attention)
2233     {
2234       data[i] = window->display->atom__NET_WM_STATE_DEMANDS_ATTENTION;
2235       ++i;
2236     }
2237   if (window->on_all_workspaces_requested)
2238     {
2239       data[i] = window->display->atom__NET_WM_STATE_STICKY;
2240       ++i;
2241     }
2242   if (meta_window_appears_focused (window))
2243     {
2244       data[i] = window->display->atom__NET_WM_STATE_FOCUSED;
2245       ++i;
2246     }
2247 
2248   meta_verbose ("Setting _NET_WM_STATE with %d atoms\n", i);
2249 
2250   meta_error_trap_push (window->display);
2251   XChangeProperty (window->display->xdisplay, window->xwindow,
2252                    window->display->atom__NET_WM_STATE,
2253                    XA_ATOM,
2254                    32, PropModeReplace, (guchar*) data, i);
2255   meta_error_trap_pop (window->display);
2256 
2257   if (window->fullscreen)
2258     {
2259       data[0] = window->fullscreen_monitors[0];
2260       data[1] = window->fullscreen_monitors[1];
2261       data[2] = window->fullscreen_monitors[2];
2262       data[3] = window->fullscreen_monitors[3];
2263 
2264       meta_verbose ("Setting _NET_WM_FULLSCREEN_MONITORS\n");
2265       meta_error_trap_push (window->display);
2266       XChangeProperty (window->display->xdisplay,
2267                        window->xwindow,
2268                        window->display->atom__NET_WM_FULLSCREEN_MONITORS,
2269                        XA_CARDINAL, 32, PropModeReplace,
2270                        (guchar*) data, 4);
2271       meta_error_trap_pop (window->display);
2272     }
2273 
2274   if (window->tile_type != META_WINDOW_TILE_TYPE_NONE)
2275   {
2276     MetaRectangle rect;
2277     meta_window_get_outer_rect (window, &rect);
2278     data[0] = (unsigned long) window->tile_mode;
2279     data[1] = (unsigned long) window->tile_type;
2280     data[2] = (unsigned long) rect.x;
2281     data[3] = (unsigned long) rect.y;
2282     data[4] = (unsigned long) rect.width;
2283     data[5] = (unsigned long) rect.height;
2284     data[6] = (unsigned long) window->tile_monitor_number;
2285     data[7] = (unsigned long) window->custom_snap_size ? 1 : 0;
2286 
2287     meta_error_trap_push (window->display);
2288     XChangeProperty (window->display->xdisplay, window->xwindow,
2289                      window->display->atom__NET_WM_WINDOW_TILE_INFO,
2290                      XA_CARDINAL,
2291                      32, PropModeReplace, (guchar*) data, 8);
2292     meta_error_trap_pop (window->display);
2293   } else {
2294     meta_error_trap_push (window->display);
2295     XDeleteProperty (window->display->xdisplay,
2296                      window->xwindow,
2297                      window->display->atom__NET_WM_WINDOW_TILE_INFO);
2298     meta_error_trap_pop (window->display);
2299   }
2300 
2301   meta_error_trap_push (window->display);
2302   update_gtk_edge_constraints (window);
2303   meta_error_trap_pop (window->display);
2304 }
2305 
2306 LOCAL_SYMBOL gboolean
meta_window_located_on_workspace(MetaWindow * window,MetaWorkspace * workspace)2307 meta_window_located_on_workspace (MetaWindow    *window,
2308                                   MetaWorkspace *workspace)
2309 {
2310   return (window->on_all_workspaces && window->screen == workspace->screen) ||
2311     (window->workspace == workspace);
2312 }
2313 
2314 static gboolean
is_minimized_foreach(MetaWindow * window,void * data)2315 is_minimized_foreach (MetaWindow *window,
2316                       void       *data)
2317 {
2318   gboolean *result = data;
2319 
2320   *result = window->minimized;
2321   if (*result)
2322     return FALSE; /* stop as soon as we find one */
2323   else
2324     return TRUE;
2325 }
2326 
2327 static gboolean
ancestor_is_minimized(MetaWindow * window)2328 ancestor_is_minimized (MetaWindow *window)
2329 {
2330   gboolean is_minimized;
2331 
2332   is_minimized = FALSE;
2333 
2334   meta_window_foreach_ancestor (window, is_minimized_foreach, &is_minimized);
2335 
2336   return is_minimized;
2337 }
2338 
2339 /**
2340  * meta_window_showing_on_its_workspace:
2341  * @window: A #MetaWindow
2342  *
2343  * Returns: %TRUE if window would be visible, if its workspace was current
2344  */
2345 gboolean
meta_window_showing_on_its_workspace(MetaWindow * window)2346 meta_window_showing_on_its_workspace (MetaWindow *window)
2347 {
2348   gboolean is_desktop_or_dock;
2349   MetaWorkspace* workspace_of_window;
2350 
2351   /* 1. See if we're minimized */
2352   if (window->minimized)
2353     return FALSE;
2354 
2355   /* 2. See if we're in "show desktop" mode */
2356   is_desktop_or_dock = FALSE;
2357   is_desktop_or_dock_foreach (window,
2358                               &is_desktop_or_dock);
2359 
2360   meta_window_foreach_ancestor (window, is_desktop_or_dock_foreach,
2361                                 &is_desktop_or_dock);
2362 
2363   if (window->on_all_workspaces)
2364     workspace_of_window = window->screen->active_workspace;
2365   else if (window->workspace)
2366     workspace_of_window = window->workspace;
2367   else /* This only seems to be needed for startup */
2368     workspace_of_window = NULL;
2369 
2370   if (workspace_of_window && workspace_of_window->showing_desktop &&
2371       !is_desktop_or_dock)
2372     return FALSE;
2373 
2374   /* 3. See if an ancestor is minimized (note that
2375    *    ancestor's "mapped" field may not be up to date
2376    *    since it's being computed in this same idle queue)
2377    */
2378 
2379   if (ancestor_is_minimized (window))
2380     return FALSE;
2381 
2382   return TRUE;
2383 }
2384 
2385 LOCAL_SYMBOL gboolean
meta_window_should_be_showing(MetaWindow * window)2386 meta_window_should_be_showing (MetaWindow  *window)
2387 {
2388   gboolean on_workspace;
2389 
2390   meta_verbose ("Should be showing for window %s\n", window->desc);
2391 
2392   /* See if we're on the workspace */
2393   on_workspace = meta_window_located_on_workspace (window,
2394                                                    window->screen->active_workspace);
2395 
2396   if (!on_workspace)
2397     meta_verbose ("Window %s is not on workspace %d\n",
2398                   window->desc,
2399                   meta_workspace_index (window->screen->active_workspace));
2400   else
2401     meta_verbose ("Window %s is on the active workspace %d\n",
2402                   window->desc,
2403                   meta_workspace_index (window->screen->active_workspace));
2404 
2405   if (window->on_all_workspaces)
2406     meta_verbose ("Window %s is on all workspaces\n", window->desc);
2407 
2408   return on_workspace && meta_window_showing_on_its_workspace (window);
2409 }
2410 
2411 static void
implement_showing(MetaWindow * window,gboolean showing)2412 implement_showing (MetaWindow *window,
2413                    gboolean    showing)
2414 {
2415   /* Actually show/hide the window */
2416   meta_verbose ("Implement showing = %d for window %s\n",
2417                 showing, window->desc);
2418 
2419   if (!showing)
2420     {
2421       /* When we manage a new window, we normally delay placing it
2422        * until it is is first shown, but if we're previewing hidden
2423        * windows we might want to know where they are on the screen,
2424        * so we should place the window even if we're hiding it rather
2425        * than showing it.
2426        */
2427       if (!window->placed)
2428         meta_window_force_placement (window);
2429 
2430       meta_window_hide (window);
2431     }
2432   else
2433     meta_window_show (window);
2434 
2435   if (!window->override_redirect)
2436     sync_client_window_mapped (window);
2437 
2438   window->pending_compositor_effect = META_COMP_EFFECT_NONE;
2439 }
2440 
2441 static void
meta_window_calc_showing(MetaWindow * window)2442 meta_window_calc_showing (MetaWindow  *window)
2443 {
2444   implement_showing (window, meta_window_should_be_showing (window));
2445 }
2446 
2447 static guint queue_later[NUMBER_OF_QUEUES] = {0, 0, 0};
2448 static GSList *queue_pending[NUMBER_OF_QUEUES] = {NULL, NULL, NULL};
2449 
2450 static int
stackcmp(gconstpointer a,gconstpointer b)2451 stackcmp (gconstpointer a, gconstpointer b)
2452 {
2453   MetaWindow *aw = (gpointer) a;
2454   MetaWindow *bw = (gpointer) b;
2455 
2456   if (aw->screen != bw->screen)
2457     return 0; /* don't care how they sort with respect to each other */
2458   else
2459     return meta_stack_windows_cmp (aw->screen->stack,
2460                                    aw, bw);
2461 }
2462 
2463 static gboolean
idle_calc_showing(gpointer data)2464 idle_calc_showing (gpointer data)
2465 {
2466   GSList *tmp;
2467   GSList *copy;
2468   GSList *should_show;
2469   GSList *should_hide;
2470   GSList *unplaced;
2471   GSList *displays;
2472   MetaWindow *first_window;
2473   guint queue_index = GPOINTER_TO_INT (data);
2474 
2475   g_return_val_if_fail (queue_pending[queue_index] != NULL, FALSE);
2476 
2477   meta_topic (META_DEBUG_WINDOW_STATE,
2478               "Clearing the calc_showing queue\n");
2479 
2480   /* Work with a copy, for reentrancy. The allowed reentrancy isn't
2481    * complete; destroying a window while we're in here would result in
2482    * badness. But it's OK to queue/unqueue calc_showings.
2483    */
2484   copy = g_slist_copy (queue_pending[queue_index]);
2485   g_slist_free (queue_pending[queue_index]);
2486   queue_pending[queue_index] = NULL;
2487   queue_later[queue_index] = 0;
2488 
2489   destroying_windows_disallowed += 1;
2490 
2491   /* We map windows from top to bottom and unmap from bottom to
2492    * top, to avoid extra expose events. The exception is
2493    * for unplaced windows, which have to be mapped from bottom to
2494    * top so placement works.
2495    */
2496   should_show = NULL;
2497   should_hide = NULL;
2498   unplaced = NULL;
2499   displays = NULL;
2500 
2501   tmp = copy;
2502   while (tmp != NULL)
2503     {
2504       MetaWindow *window;
2505 
2506       window = tmp->data;
2507 
2508       if (!window->placed)
2509         unplaced = g_slist_prepend (unplaced, window);
2510       else if (meta_window_should_be_showing (window))
2511         should_show = g_slist_prepend (should_show, window);
2512       else
2513         should_hide = g_slist_prepend (should_hide, window);
2514 
2515       tmp = tmp->next;
2516     }
2517 
2518   /* bottom to top */
2519   unplaced = g_slist_sort (unplaced, stackcmp);
2520   should_hide = g_slist_sort (should_hide, stackcmp);
2521   /* top to bottom */
2522   should_show = g_slist_sort (should_show, stackcmp);
2523   should_show = g_slist_reverse (should_show);
2524 
2525   first_window = copy->data;
2526 
2527   meta_display_grab (first_window->display);
2528 
2529   tmp = unplaced;
2530   while (tmp != NULL)
2531     {
2532       MetaWindow *window;
2533 
2534       window = tmp->data;
2535 
2536       meta_window_calc_showing (window);
2537 
2538       tmp = tmp->next;
2539     }
2540 
2541   tmp = should_show;
2542   while (tmp != NULL)
2543     {
2544       MetaWindow *window;
2545 
2546       window = tmp->data;
2547 
2548       implement_showing (window, TRUE);
2549 
2550       tmp = tmp->next;
2551     }
2552 
2553   tmp = should_hide;
2554   while (tmp != NULL)
2555     {
2556       MetaWindow *window;
2557 
2558       window = tmp->data;
2559 
2560       implement_showing (window, FALSE);
2561 
2562       tmp = tmp->next;
2563     }
2564 
2565   tmp = copy;
2566   while (tmp != NULL)
2567     {
2568       MetaWindow *window;
2569 
2570       window = tmp->data;
2571 
2572       /* important to set this here for reentrancy -
2573        * if we queue a window again while it's in "copy",
2574        * then queue_calc_showing will just return since
2575        * we are still in the calc_showing queue
2576        */
2577       window->is_in_queues &= ~META_QUEUE_CALC_SHOWING;
2578 
2579       tmp = tmp->next;
2580     }
2581 
2582   if (meta_prefs_get_focus_mode () != C_DESKTOP_FOCUS_MODE_CLICK)
2583     {
2584       /* When display->mouse_mode is false, we want to ignore
2585        * EnterNotify events unless they come from mouse motion.  To do
2586        * that, we set a sentinel property on the root window if we're
2587        * not in mouse_mode.
2588        */
2589       tmp = should_show;
2590       while (tmp != NULL)
2591         {
2592           MetaWindow *window = tmp->data;
2593 
2594           if (!window->display->mouse_mode)
2595             meta_display_increment_focus_sentinel (window->display);
2596 
2597           tmp = tmp->next;
2598         }
2599     }
2600 
2601   meta_display_ungrab (first_window->display);
2602 
2603   g_slist_free (copy);
2604 
2605   g_slist_free (unplaced);
2606   g_slist_free (should_show);
2607   g_slist_free (should_hide);
2608   g_slist_free (displays);
2609 
2610   destroying_windows_disallowed -= 1;
2611 
2612   return FALSE;
2613 }
2614 
2615 #ifdef WITH_VERBOSE_MODE
2616 static const gchar* meta_window_queue_names[NUMBER_OF_QUEUES] =
2617   {"calc_showing", "move_resize", "update_icon"};
2618 #endif
2619 
2620 static void
meta_window_unqueue(MetaWindow * window,guint queuebits)2621 meta_window_unqueue (MetaWindow *window, guint queuebits)
2622 {
2623   gint queuenum;
2624 
2625   for (queuenum=0; queuenum<NUMBER_OF_QUEUES; queuenum++)
2626     {
2627       if ((queuebits & 1<<queuenum) /* they have asked to unqueue */
2628           &&
2629           (window->is_in_queues & 1<<queuenum)) /* it's in the queue */
2630         {
2631 
2632           meta_topic (META_DEBUG_WINDOW_STATE,
2633               "Removing %s from the %s queue\n",
2634               window->desc,
2635               meta_window_queue_names[queuenum]);
2636 
2637           /* Note that window may not actually be in the queue
2638            * because it may have been in "copy" inside the idle handler
2639            */
2640           queue_pending[queuenum] = g_slist_remove (queue_pending[queuenum], window);
2641           window->is_in_queues &= ~(1<<queuenum);
2642 
2643           /* Okay, so maybe we've used up all the entries in the queue.
2644            * In that case, we should kill the function that deals with
2645            * the queue, because there's nothing left for it to do.
2646            */
2647           if (queue_pending[queuenum] == NULL && queue_later[queuenum] != 0)
2648             {
2649               meta_later_remove (queue_later[queuenum]);
2650               queue_later[queuenum] = 0;
2651             }
2652         }
2653     }
2654 }
2655 
2656 static void
meta_window_flush_calc_showing(MetaWindow * window)2657 meta_window_flush_calc_showing (MetaWindow *window)
2658 {
2659   if (window->is_in_queues & META_QUEUE_CALC_SHOWING)
2660     {
2661       meta_window_unqueue (window, META_QUEUE_CALC_SHOWING);
2662       meta_window_calc_showing (window);
2663     }
2664 }
2665 
2666 LOCAL_SYMBOL void
meta_window_queue(MetaWindow * window,guint queuebits)2667 meta_window_queue (MetaWindow *window, guint queuebits)
2668 {
2669 
2670   /* Easier to debug by checking here rather than in the idle */
2671   g_return_if_fail (!window->override_redirect || (queuebits & META_QUEUE_MOVE_RESIZE) == 0);
2672 
2673   for (guint queuenum=0; queuenum<NUMBER_OF_QUEUES; queuenum++)
2674     {
2675       if (queuebits & 1<<queuenum)
2676         {
2677           /* Data which varies between queues.
2678            * Yes, these do look a lot like associative arrays:
2679            * I seem to be turning into a Perl programmer.
2680            */
2681 
2682           const MetaLaterType window_queue_later_when[NUMBER_OF_QUEUES] =
2683             {
2684               META_LATER_CALC_SHOWING,  /* CALC_SHOWING */
2685               META_LATER_RESIZE,        /* MOVE_RESIZE */
2686             };
2687 
2688           const GSourceFunc window_queue_later_handler[NUMBER_OF_QUEUES] =
2689             {
2690               idle_calc_showing,
2691               idle_move_resize
2692             };
2693 
2694           /* If we're about to drop the window, there's no point in putting
2695            * it on a queue.
2696            */
2697           if (window->unmanaging)
2698             break;
2699 
2700           /* If the window already claims to be in that queue, there's no
2701            * point putting it in the queue.
2702            */
2703           if (window->is_in_queues & 1<<queuenum)
2704             break;
2705 
2706           meta_topic (META_DEBUG_WINDOW_STATE,
2707               "Putting %s in the %s queue\n",
2708               window->desc,
2709               meta_window_queue_names[queuenum]);
2710 
2711           /* So, mark it as being in this queue. */
2712           window->is_in_queues |= 1<<queuenum;
2713 
2714           /* There's not a lot of point putting things into a queue if
2715            * nobody's on the other end pulling them out. Therefore,
2716            * let's check to see whether an idle handler exists to do
2717            * that. If not, we'll create one.
2718            */
2719 
2720           if (queue_later[queuenum] == 0)
2721             queue_later[queuenum] = meta_later_add
2722               (
2723                 window_queue_later_when[queuenum],
2724                 window_queue_later_handler[queuenum],
2725                 GUINT_TO_POINTER(queuenum),
2726                 NULL
2727               );
2728 
2729           /* And now we actually put it on the queue. */
2730           queue_pending[queuenum] = g_slist_prepend (queue_pending[queuenum],
2731                                                      window);
2732       }
2733   }
2734 }
2735 
2736 static gboolean
intervening_user_event_occurred(MetaWindow * window)2737 intervening_user_event_occurred (MetaWindow *window)
2738 {
2739   guint32 compare;
2740   MetaWindow *focus_window;
2741 
2742   focus_window = window->display->focus_window;
2743 
2744   meta_topic (META_DEBUG_STARTUP,
2745               "COMPARISON:\n"
2746               "  net_wm_user_time_set : %d\n"
2747               "  net_wm_user_time     : %u\n"
2748               "  initial_timestamp_set: %d\n"
2749               "  initial_timestamp    : %u\n",
2750               window->net_wm_user_time_set,
2751               window->net_wm_user_time,
2752               window->initial_timestamp_set,
2753               window->initial_timestamp);
2754 #ifdef WITH_VERBOSE_MODE
2755   if (focus_window != NULL)
2756     {
2757       meta_topic (META_DEBUG_STARTUP,
2758                   "COMPARISON (continued):\n"
2759                   "  focus_window             : %s\n"
2760                   "  fw->net_wm_user_time_set : %d\n"
2761                   "  fw->net_wm_user_time     : %u\n",
2762                   focus_window->desc,
2763                   focus_window->net_wm_user_time_set,
2764                   focus_window->net_wm_user_time);
2765     }
2766 #endif
2767   /* We expect the most common case for not focusing a new window
2768    * to be when a hint to not focus it has been set.  Since we can
2769    * deal with that case rapidly, we use special case it--this is
2770    * merely a preliminary optimization.  :)
2771    */
2772   if ( ((window->net_wm_user_time_set == TRUE) &&
2773        (window->net_wm_user_time == 0))
2774       ||
2775        ((window->initial_timestamp_set == TRUE) &&
2776        (window->initial_timestamp == 0)))
2777     {
2778       meta_topic (META_DEBUG_STARTUP,
2779                   "window %s explicitly requested no focus\n",
2780                   window->desc);
2781       return TRUE;
2782     }
2783 
2784   if (!(window->net_wm_user_time_set) && !(window->initial_timestamp_set))
2785     {
2786       meta_topic (META_DEBUG_STARTUP,
2787                   "no information about window %s found\n",
2788                   window->desc);
2789       return FALSE;
2790     }
2791 
2792   if (focus_window != NULL &&
2793       !focus_window->net_wm_user_time_set)
2794     {
2795       meta_topic (META_DEBUG_STARTUP,
2796                   "focus window, %s, doesn't have a user time set yet!\n",
2797                   window->desc);
2798       return FALSE;
2799     }
2800 
2801   /* To determine the "launch" time of an application,
2802    * startup-notification can set the TIMESTAMP and the
2803    * application (usually via its toolkit such as gtk or qt) can
2804    * set the _NET_WM_USER_TIME.  If both are set, we need to be
2805    * using the newer of the two values.
2806    *
2807    * See http://bugzilla.gnome.org/show_bug.cgi?id=573922
2808    */
2809   compare = 0;
2810   if (window->net_wm_user_time_set &&
2811       window->initial_timestamp_set)
2812     compare =
2813       XSERVER_TIME_IS_BEFORE (window->net_wm_user_time,
2814                               window->initial_timestamp) ?
2815       window->initial_timestamp : window->net_wm_user_time;
2816   else if (window->net_wm_user_time_set)
2817     compare = window->net_wm_user_time;
2818   else if (window->initial_timestamp_set)
2819     compare = window->initial_timestamp;
2820 
2821   if ((focus_window != NULL) &&
2822       XSERVER_TIME_IS_BEFORE (compare, focus_window->net_wm_user_time))
2823     {
2824       meta_topic (META_DEBUG_STARTUP,
2825                   "window %s focus prevented by other activity; %u < %u\n",
2826                   window->desc,
2827                   compare,
2828                   focus_window->net_wm_user_time);
2829       return TRUE;
2830     }
2831   else
2832     {
2833       meta_topic (META_DEBUG_STARTUP,
2834                   "new window %s with no intervening events\n",
2835                   window->desc);
2836       return FALSE;
2837     }
2838 }
2839 
2840 /* This function is an ugly hack.  It's experimental in nature and ought to be
2841  * replaced by a real hint from the app to the WM if we decide the experimental
2842  * behavior is worthwhile.  The basic idea is to get more feedback about how
2843  * usage scenarios of "strict" focus users and what they expect.  See #326159.
2844  */
2845 LOCAL_SYMBOL gboolean
__window_is_terminal(MetaWindow * window)2846 __window_is_terminal (MetaWindow *window)
2847 {
2848   if (window == NULL || window->res_class == NULL)
2849     return FALSE;
2850 
2851   /*
2852    * Compare res_class, which is not user-settable, and thus theoretically
2853    * a more-reliable indication of term-ness.
2854    */
2855 
2856   /* gnome-terminal -- if you couldn't guess */
2857   if (strcmp (window->res_class, "Gnome-terminal") == 0)
2858     return TRUE;
2859   /* xterm, rxvt, aterm */
2860   else if (strcmp (window->res_class, "XTerm") == 0)
2861     return TRUE;
2862   /* konsole, KDE's terminal program */
2863   else if (strcmp (window->res_class, "Konsole") == 0)
2864     return TRUE;
2865   /* rxvt-unicode */
2866   else if (strcmp (window->res_class, "URxvt") == 0)
2867     return TRUE;
2868   /* eterm */
2869   else if (strcmp (window->res_class, "Eterm") == 0)
2870     return TRUE;
2871   /* KTerm -- some terminal not KDE based; so not like Konsole */
2872   else if (strcmp (window->res_class, "KTerm") == 0)
2873     return TRUE;
2874   /* Multi-gnome-terminal */
2875   else if (strcmp (window->res_class, "Multi-gnome-terminal") == 0)
2876     return TRUE;
2877   /* mlterm ("multi lingual terminal emulator on X") */
2878   else if (strcmp (window->res_class, "mlterm") == 0)
2879     return TRUE;
2880   /* Terminal -- XFCE Terminal */
2881   else if (strcmp (window->res_class, "Terminal") == 0)
2882     return TRUE;
2883 
2884   return FALSE;
2885 }
2886 
2887 /* This function determines what state the window should have assuming that it
2888  * and the focus_window have no relation
2889  */
2890 static void
window_state_on_map(MetaWindow * window,gboolean * takes_focus,gboolean * places_on_top)2891 window_state_on_map (MetaWindow *window,
2892                      gboolean *takes_focus,
2893                      gboolean *places_on_top)
2894 {
2895   gboolean intervening_events;
2896 
2897   intervening_events = intervening_user_event_occurred (window);
2898 
2899   *takes_focus = !intervening_events;
2900   *places_on_top = *takes_focus;
2901 
2902   /* don't initially focus windows that are intended to not accept
2903    * focus
2904    */
2905   if (!(window->input || window->take_focus))
2906     {
2907       *takes_focus = FALSE;
2908       return;
2909     }
2910 
2911   /* Terminal usage may be different; some users intend to launch
2912    * many apps in quick succession or to just view things in the new
2913    * window while still interacting with the terminal.  In that case,
2914    * apps launched from the terminal should not take focus.  This
2915    * isn't quite the same as not allowing focus to transfer from
2916    * terminals due to new window map, but the latter is a much easier
2917    * approximation to enforce so we do that.
2918    */
2919   if (*takes_focus &&
2920       meta_prefs_get_focus_new_windows () == C_DESKTOP_FOCUS_NEW_WINDOWS_STRICT &&
2921       !window->display->allow_terminal_deactivation &&
2922       __window_is_terminal (window->display->focus_window) &&
2923       !meta_window_is_ancestor_of_transient (window->display->focus_window,
2924                                              window))
2925     {
2926       meta_topic (META_DEBUG_FOCUS,
2927                   "focus_window is terminal; not focusing new window.\n");
2928       *takes_focus = FALSE;
2929       *places_on_top = FALSE;
2930     }
2931 
2932   switch (window->type)
2933     {
2934     case META_WINDOW_UTILITY:
2935     case META_WINDOW_TOOLBAR:
2936       *takes_focus = FALSE;
2937       *places_on_top = FALSE;
2938       break;
2939     case META_WINDOW_DOCK:
2940     case META_WINDOW_DESKTOP:
2941     case META_WINDOW_SPLASHSCREEN:
2942     case META_WINDOW_MENU:
2943     /* override redirect types: */
2944     case META_WINDOW_DROPDOWN_MENU:
2945     case META_WINDOW_POPUP_MENU:
2946     case META_WINDOW_TOOLTIP:
2947     case META_WINDOW_NOTIFICATION:
2948     case META_WINDOW_COMBO:
2949     case META_WINDOW_DND:
2950     case META_WINDOW_OVERRIDE_OTHER:
2951       /* don't focus any of these; places_on_top may be irrelevant for some of
2952        * these (e.g. dock)--but you never know--the focus window might also be
2953        * of the same type in some weird situation...
2954        */
2955       *takes_focus = FALSE;
2956       break;
2957     case META_WINDOW_NORMAL:
2958     case META_WINDOW_DIALOG:
2959     case META_WINDOW_MODAL_DIALOG:
2960       /* The default is correct for these */
2961       break;
2962     }
2963 }
2964 
2965 static gboolean
windows_overlap(const MetaWindow * w1,const MetaWindow * w2)2966 windows_overlap (const MetaWindow *w1, const MetaWindow *w2)
2967 {
2968   MetaRectangle w1rect, w2rect;
2969   meta_window_get_outer_rect (w1, &w1rect);
2970   meta_window_get_outer_rect (w2, &w2rect);
2971   return meta_rectangle_overlap (&w1rect, &w2rect);
2972 }
2973 
2974 /* Returns whether a new window would be covered by any
2975  * existing window on the same workspace that is set
2976  * to be "above" ("always on top").  A window that is not
2977  * set "above" would be underneath the new window anyway.
2978  *
2979  * We take "covered" to mean even partially covered, but
2980  * some people might prefer entirely covered.  I think it
2981  * is more useful to behave this way if any part of the
2982  * window is covered, because a partial coverage could be
2983  * (say) ninety per cent and almost indistinguishable from total.
2984  */
2985 static gboolean
window_would_be_covered(const MetaWindow * newbie)2986 window_would_be_covered (const MetaWindow *newbie)
2987 {
2988   MetaWorkspace *workspace = newbie->workspace;
2989   GList *tmp, *windows;
2990 
2991   windows = meta_workspace_list_windows (workspace);
2992 
2993   tmp = windows;
2994   while (tmp != NULL)
2995     {
2996       MetaWindow *w = tmp->data;
2997 
2998       if (w->wm_state_above && w != newbie)
2999         {
3000           /* We have found a window that is "above". Perhaps it overlaps. */
3001           if (windows_overlap (w, newbie))
3002             {
3003               g_list_free (windows); /* clean up... */
3004               return TRUE; /* yes, it does */
3005             }
3006         }
3007 
3008       tmp = tmp->next;
3009     }
3010 
3011   g_list_free (windows);
3012   return FALSE; /* none found */
3013 }
3014 
3015 static void
meta_window_force_placement(MetaWindow * window)3016 meta_window_force_placement (MetaWindow *window)
3017 {
3018   if (window->placed)
3019     return;
3020 
3021   /* We have to recalc the placement here since other windows may
3022    * have been mapped/placed since we last did constrain_position
3023    */
3024 
3025   /* calc_placement is an efficiency hack to avoid
3026    * multiple placement calculations before we finally
3027    * show the window.
3028    */
3029   window->calc_placement = TRUE;
3030   meta_window_move_resize_now (window);
3031   window->calc_placement = FALSE;
3032 
3033   /* don't ever do the initial position constraint thing again.
3034    * This is toggled here so that initially-iconified windows
3035    * still get placed when they are ultimately shown.
3036    */
3037   window->placed = TRUE;
3038 
3039   /* Don't want to accidentally reuse the fact that we had been denied
3040    * focus in any future constraints unless we're denied focus again.
3041    */
3042   window->denied_focus_and_not_transient = FALSE;
3043 }
3044 
3045 static void
meta_window_show(MetaWindow * window)3046 meta_window_show (MetaWindow *window)
3047 {
3048   gboolean did_show;
3049   gboolean takes_focus_on_map;
3050   gboolean place_on_top_on_map;
3051   gboolean needs_stacking_adjustment;
3052   MetaWindow *focus_window;
3053   gboolean notify_demands_attention = FALSE;
3054 
3055   meta_topic (META_DEBUG_WINDOW_STATE,
3056               "Showing window %s, shaded: %d iconic: %d placed: %d\n",
3057               window->desc, window->shaded, window->iconic, window->placed);
3058 
3059   focus_window = window->display->focus_window;  /* May be NULL! */
3060   did_show = FALSE;
3061   window_state_on_map (window, &takes_focus_on_map, &place_on_top_on_map);
3062   needs_stacking_adjustment = FALSE;
3063 
3064   meta_topic (META_DEBUG_WINDOW_STATE,
3065               "Window %s %s focus on map, and %s place on top on map.\n",
3066               window->desc,
3067               takes_focus_on_map ? "does" : "does not",
3068               place_on_top_on_map ? "does" : "does not");
3069 
3070   /* Now, in some rare cases we should *not* put a new window on top.
3071    * These cases include certain types of windows showing for the first
3072    * time, and any window which would be covered because of another window
3073    * being set "above" ("always on top").
3074    *
3075    * FIXME: Although "place_on_top_on_map" and "takes_focus_on_map" are
3076    * generally based on the window type, there is a special case when the
3077    * focus window is a terminal for them both to be false; this should
3078    * probably rather be a term in the "if" condition below.
3079    */
3080 
3081   if ( focus_window != NULL && window->showing_for_first_time &&
3082       ( (!place_on_top_on_map && !takes_focus_on_map) ||
3083       window_would_be_covered (window) )
3084     ) {
3085       if (!meta_window_is_ancestor_of_transient (focus_window, window))
3086         {
3087           needs_stacking_adjustment = TRUE;
3088           if (!window->placed)
3089             window->denied_focus_and_not_transient = TRUE;
3090         }
3091     }
3092 
3093   if (!window->placed)
3094     {
3095       meta_window_force_placement (window);
3096     }
3097 
3098   if (needs_stacking_adjustment)
3099     {
3100       gboolean overlap;
3101 
3102       /* This window isn't getting focus on map.  We may need to do some
3103        * special handing with it in regards to
3104        *   - the stacking of the window
3105        *   - the MRU position of the window
3106        *   - the demands attention setting of the window
3107        *
3108        * Firstly, set the flag so we don't give the window focus anyway
3109        * and confuse people.
3110        */
3111 
3112       takes_focus_on_map = FALSE;
3113 
3114       overlap = windows_overlap (window, focus_window);
3115 
3116       /* We want alt tab to go to the denied-focus window */
3117       ensure_mru_position_after (window, focus_window);
3118 
3119       /* We don't want the denied-focus window to obscure the focus
3120        * window, and if we're in both click-to-focus mode and
3121        * raise-on-click mode then we want to maintain the invariant
3122        * that MRU order == stacking order.  The need for this if
3123        * comes from the fact that in sloppy/mouse focus the focus
3124        * window may not overlap other windows and also can be
3125        * considered "below" them; this combination means that
3126        * placing the denied-focus window "below" the focus window
3127        * in the stack when it doesn't overlap it confusingly places
3128        * that new window below a lot of other windows.
3129        */
3130       if (overlap ||
3131           (meta_prefs_get_focus_mode () == C_DESKTOP_FOCUS_MODE_CLICK &&
3132            meta_prefs_get_raise_on_click ()))
3133         meta_window_stack_just_below (window, focus_window);
3134 
3135       /* If the window will be obscured by the focus window, then the
3136        * user might not notice the window appearing so set the
3137        * demands attention hint.
3138        *
3139        * We set the hint ourselves rather than calling
3140        * meta_window_set_demands_attention() because that would cause
3141        * a recalculation of overlap, and a call to set_net_wm_state()
3142        * which we are going to call ourselves here a few lines down.
3143        */
3144       if (overlap)
3145         {
3146           if (!window->wm_state_demands_attention)
3147             {
3148               window->wm_state_demands_attention = TRUE;
3149               notify_demands_attention = TRUE;
3150             }
3151         }
3152     }
3153 
3154   if (window->hidden)
3155     {
3156       meta_stack_freeze (window->screen->stack);
3157       window->hidden = FALSE;
3158       meta_stack_thaw (window->screen->stack);
3159       did_show = TRUE;
3160     }
3161 
3162   if (window->iconic)
3163     {
3164       window->iconic = FALSE;
3165       set_wm_state (window, NormalState);
3166     }
3167 
3168   if (!window->visible_to_compositor)
3169     {
3170       window->visible_to_compositor = TRUE;
3171 
3172       MetaCompEffect effect = META_COMP_EFFECT_NONE;
3173 
3174       switch (window->pending_compositor_effect)
3175         {
3176         case META_COMP_EFFECT_CREATE:
3177         case META_COMP_EFFECT_UNMINIMIZE:
3178           effect = window->pending_compositor_effect;
3179           break;
3180         case META_COMP_EFFECT_NONE:
3181         case META_COMP_EFFECT_DESTROY:
3182         case META_COMP_EFFECT_MINIMIZE:
3183           break;
3184         }
3185 
3186       meta_compositor_show_window (window->display->compositor,
3187                                    window, effect);
3188     }
3189 
3190   /* We don't want to worry about all cases from inside
3191    * implement_showing(); we only want to worry about focus if this
3192    * window has not been shown before.
3193    */
3194   if (window->showing_for_first_time)
3195     {
3196       window->showing_for_first_time = FALSE;
3197       if (takes_focus_on_map)
3198         {
3199           guint32     timestamp;
3200 
3201           timestamp = meta_display_get_current_time_roundtrip (window->display);
3202 
3203           meta_window_focus (window, timestamp);
3204 
3205           if (window->move_after_placement)
3206             {
3207               timestamp = meta_display_get_current_time_roundtrip (window->display);
3208               meta_window_begin_grab_op(window, META_GRAB_OP_KEYBOARD_MOVING,
3209                                         FALSE, timestamp);
3210               window->move_after_placement = FALSE;
3211             }
3212         }
3213       else
3214         {
3215           /* Prevent EnterNotify events in sloppy/mouse focus from
3216            * erroneously focusing the window that had been denied
3217            * focus.  FIXME: This introduces a race; I have a couple
3218            * ideas for a better way to accomplish the same thing, but
3219            * they're more involved so do it this way for now.
3220            */
3221           meta_display_increment_focus_sentinel (window->display);
3222         }
3223     }
3224 
3225   set_net_wm_state (window);
3226 
3227   if (did_show && window->struts)
3228     {
3229       meta_topic (META_DEBUG_WORKAREA,
3230                   "Mapped window %s with struts, so invalidating work areas\n",
3231                   window->desc);
3232       invalidate_work_areas (window);
3233     }
3234 
3235   if (did_show)
3236     meta_screen_queue_check_fullscreen (window->screen);
3237 
3238   /*
3239    * Now that we have shown the window, we no longer want to consider the
3240    * initial timestamp in any subsequent deliberations whether to focus this
3241    * window or not, so clear the flag.
3242    *
3243    * See http://bugzilla.gnome.org/show_bug.cgi?id=573922
3244    */
3245   window->initial_timestamp_set = FALSE;
3246 
3247   if (notify_demands_attention)
3248     {
3249       g_object_notify (G_OBJECT (window), "demands-attention");
3250       g_signal_emit_by_name (window->display, "window-demands-attention",
3251                              window);
3252     }
3253 }
3254 
3255 static void
meta_window_hide(MetaWindow * window)3256 meta_window_hide (MetaWindow *window)
3257 {
3258   gboolean did_hide;
3259 
3260   meta_topic (META_DEBUG_WINDOW_STATE,
3261               "Hiding window %s\n", window->desc);
3262 
3263   if (window->visible_to_compositor)
3264     {
3265       window->visible_to_compositor = FALSE;
3266 
3267       MetaCompEffect effect = META_COMP_EFFECT_NONE;
3268 
3269       switch (window->pending_compositor_effect)
3270         {
3271         case META_COMP_EFFECT_CREATE:
3272         case META_COMP_EFFECT_UNMINIMIZE:
3273         case META_COMP_EFFECT_NONE:
3274           break;
3275         case META_COMP_EFFECT_DESTROY:
3276         case META_COMP_EFFECT_MINIMIZE:
3277           effect = window->pending_compositor_effect;
3278           break;
3279         }
3280 
3281       meta_compositor_hide_window (window->display->compositor,
3282                                    window, effect);
3283     }
3284 
3285   did_hide = FALSE;
3286 
3287   if (!window->hidden)
3288     {
3289       meta_stack_freeze (window->screen->stack);
3290       window->hidden = TRUE;
3291       meta_stack_thaw (window->screen->stack);
3292 
3293       did_hide = TRUE;
3294     }
3295 
3296   if (!window->iconic)
3297     {
3298       window->iconic = TRUE;
3299       set_wm_state (window, IconicState);
3300     }
3301 
3302   set_net_wm_state (window);
3303 
3304   if (did_hide && window->struts)
3305     {
3306       meta_topic (META_DEBUG_WORKAREA,
3307                   "Unmapped window %s with struts, so invalidating work areas\n",
3308                   window->desc);
3309       invalidate_work_areas (window);
3310     }
3311 
3312   /* The check on expected_focus_window is a temporary workaround for
3313    *  https://bugzilla.gnome.org/show_bug.cgi?id=597352
3314    * We may have already switched away from this window but not yet
3315    * gotten FocusIn/FocusOut events. A more complete comprehensive
3316    * fix for these type of issues is described in the bug.
3317    */
3318   if (window->has_focus &&
3319       window == window->display->expected_focus_window)
3320     {
3321       MetaWindow *not_this_one = NULL;
3322       MetaWorkspace *my_workspace = meta_window_get_workspace (window);
3323       guint32 timestamp = meta_display_get_current_time_roundtrip (window->display);
3324 
3325       /*
3326        * If this window is modal, passing the not_this_one window to
3327        * _focus_default_window() makes the focus to be given to this window's
3328        * ancestor. This can only be the case if the window is on the currently
3329        * active workspace; when it is not, we need to pass in NULL, so as to
3330        * focus the default window for the active workspace (this scenario
3331        * arises when we are switching workspaces).
3332        */
3333       if (window->type == META_WINDOW_MODAL_DIALOG &&
3334           my_workspace == window->screen->active_workspace)
3335         not_this_one = window;
3336 
3337       meta_workspace_focus_default_window (window->screen->active_workspace,
3338                                            not_this_one,
3339                                            timestamp);
3340     }
3341 
3342   if (did_hide)
3343     meta_screen_queue_check_fullscreen (window->screen);
3344 }
3345 
3346 static gboolean
queue_calc_showing_func(MetaWindow * window,void * data)3347 queue_calc_showing_func (MetaWindow *window,
3348                          void       *data)
3349 {
3350   meta_window_queue(window, META_QUEUE_CALC_SHOWING);
3351   return TRUE;
3352 }
3353 
3354 void
meta_window_minimize(MetaWindow * window)3355 meta_window_minimize (MetaWindow  *window)
3356 {
3357   g_return_if_fail (!window->override_redirect);
3358 
3359   if (!window->minimized)
3360     {
3361       window->minimized = TRUE;
3362       window->pending_compositor_effect = META_COMP_EFFECT_MINIMIZE;
3363       meta_window_queue(window, META_QUEUE_CALC_SHOWING);
3364 
3365       meta_window_foreach_transient (window,
3366                                      queue_calc_showing_func,
3367                                      NULL);
3368 #ifdef WITH_VERBOSE_MODE
3369       if (window->has_focus)
3370         {
3371           meta_topic (META_DEBUG_FOCUS,
3372                       "Focusing default window due to minimization of focus window %s\n",
3373                       window->desc);
3374         }
3375       else
3376         {
3377           meta_topic (META_DEBUG_FOCUS,
3378                       "Minimizing window %s which doesn't have the focus\n",
3379                       window->desc);
3380         }
3381 #endif
3382       g_object_notify (G_OBJECT (window), "minimized");
3383     }
3384 
3385   meta_screen_update_snapped_windows (window->screen);
3386 }
3387 
3388 void
meta_window_unminimize(MetaWindow * window)3389 meta_window_unminimize (MetaWindow  *window)
3390 {
3391   g_return_if_fail (!window->override_redirect);
3392 
3393   if (window->minimized)
3394     {
3395       window->minimized = FALSE;
3396       window->pending_compositor_effect = META_COMP_EFFECT_UNMINIMIZE;
3397       meta_window_queue(window, META_QUEUE_CALC_SHOWING);
3398 
3399       meta_window_foreach_transient (window,
3400                                      queue_calc_showing_func,
3401                                      NULL);
3402       g_object_notify (G_OBJECT (window), "minimized");
3403     }
3404 
3405   meta_screen_update_snapped_windows (window->screen);
3406 }
3407 
3408 static void
ensure_size_hints_satisfied(MetaRectangle * rect,const XSizeHints * size_hints)3409 ensure_size_hints_satisfied (MetaRectangle    *rect,
3410                              const XSizeHints *size_hints)
3411 {
3412   int minw, minh, maxw, maxh;   /* min/max width/height                      */
3413   int basew, baseh, winc, hinc; /* base width/height, width/height increment */
3414   int extra_width, extra_height;
3415 
3416   minw  = size_hints->min_width;  minh  = size_hints->min_height;
3417   maxw  = size_hints->max_width;  maxh  = size_hints->max_height;
3418   basew = size_hints->base_width; baseh = size_hints->base_height;
3419   winc  = size_hints->width_inc;  hinc  = size_hints->height_inc;
3420 
3421   /* First, enforce min/max size constraints */
3422   rect->width  = CLAMP (rect->width,  minw, maxw);
3423   rect->height = CLAMP (rect->height, minh, maxh);
3424 
3425   /* Now, verify size increment constraints are satisfied, or make them be */
3426   extra_width  = (rect->width  - basew) % winc;
3427   extra_height = (rect->height - baseh) % hinc;
3428 
3429   rect->width  -= extra_width;
3430   rect->height -= extra_height;
3431 
3432   /* Adjusting width/height down, as done above, may violate minimum size
3433    * constraints, so one last fix.
3434    */
3435   if (rect->width  < minw)
3436     rect->width  += ((minw - rect->width)/winc  + 1)*winc;
3437   if (rect->height < minh)
3438     rect->height += ((minh - rect->height)/hinc + 1)*hinc;
3439 }
3440 
3441 static void
meta_window_save_rect(MetaWindow * window)3442 meta_window_save_rect (MetaWindow *window)
3443 {
3444   if (!(META_WINDOW_MAXIMIZED (window) || META_WINDOW_TILED_OR_SNAPPED (window) || window->fullscreen))
3445     {
3446       /* save size/pos as appropriate args for move_resize */
3447       if (!window->maximized_horizontally)
3448         {
3449           window->saved_rect.x      = window->rect.x;
3450           window->saved_rect.width  = window->rect.width;
3451           if (window->frame)
3452             window->saved_rect.x   += window->frame->rect.x;
3453         }
3454       if (!window->maximized_vertically)
3455         {
3456           window->saved_rect.y      = window->rect.y;
3457           window->saved_rect.height = window->rect.height;
3458           if (window->frame)
3459             window->saved_rect.y   += window->frame->rect.y;
3460         }
3461     }
3462 }
3463 
3464 /*
3465  * Save the user_rect regardless of whether the window is maximized or
3466  * fullscreen. See save_user_window_placement() for most uses.
3467  *
3468  * \param window  Store current position of this window for future reference
3469  */
3470 static void
force_save_user_window_placement(MetaWindow * window)3471 force_save_user_window_placement (MetaWindow *window)
3472 {
3473   meta_window_get_client_root_coords (window, &window->user_rect);
3474 }
3475 
3476 /*
3477  * Save the user_rect, but only if the window is neither maximized nor
3478  * fullscreen, otherwise the window may snap back to those dimensions
3479  * (bug #461927).
3480  *
3481  * \param window  Store current position of this window for future reference
3482  */
3483 static void
save_user_window_placement(MetaWindow * window)3484 save_user_window_placement (MetaWindow *window)
3485 {
3486   if (!(META_WINDOW_MAXIMIZED (window) || META_WINDOW_TILED_OR_SNAPPED (window) || window->fullscreen))
3487     {
3488       MetaRectangle user_rect;
3489 
3490       meta_window_get_client_root_coords (window, &user_rect);
3491 
3492       if (!window->maximized_horizontally)
3493 	{
3494 	  window->user_rect.x     = user_rect.x;
3495 	  window->user_rect.width = user_rect.width;
3496 	}
3497       if (!window->maximized_vertically)
3498 	{
3499 	  window->user_rect.y      = user_rect.y;
3500 	  window->user_rect.height = user_rect.height;
3501 	}
3502     }
3503 }
3504 
3505 LOCAL_SYMBOL void
meta_window_maximize_internal(MetaWindow * window,MetaMaximizeFlags directions,MetaRectangle * saved_rect)3506 meta_window_maximize_internal (MetaWindow        *window,
3507                                MetaMaximizeFlags  directions,
3508                                MetaRectangle     *saved_rect)
3509 {
3510   /* At least one of the two directions ought to be set */
3511   gboolean maximize_horizontally, maximize_vertically;
3512   maximize_horizontally = directions & META_MAXIMIZE_HORIZONTAL;
3513   maximize_vertically   = directions & META_MAXIMIZE_VERTICAL;
3514   g_assert (maximize_horizontally || maximize_vertically);
3515 
3516   meta_topic (META_DEBUG_WINDOW_OPS,
3517               "Maximizing %s%s\n",
3518               window->desc,
3519               maximize_horizontally && maximize_vertically ? "" :
3520                 maximize_horizontally ? " horizontally" :
3521                   maximize_vertically ? " vertically" : "BUGGGGG");
3522 
3523   if (saved_rect != NULL)
3524     window->saved_rect = *saved_rect;
3525   else
3526     meta_window_save_rect (window);
3527 
3528   meta_window_set_tile_type (window, META_WINDOW_TILE_TYPE_NONE);
3529   window->tile_mode = META_TILE_NONE;
3530   normalize_tile_state (window);
3531 
3532   window->maximized_horizontally =
3533     window->maximized_horizontally || maximize_horizontally;
3534   window->maximized_vertically =
3535     window->maximized_vertically   || maximize_vertically;
3536   if (maximize_horizontally || maximize_vertically)
3537     window->force_save_user_rect = FALSE;
3538 
3539   if (window->maximized_horizontally && window->maximized_vertically)
3540   {
3541     window->saved_maximize = TRUE;
3542   }
3543 
3544   /* Update the edge constraints */
3545   update_edge_constraints (window);;
3546 
3547   recalc_window_features (window);
3548   set_net_wm_state (window);
3549 
3550   if (window->monitor->in_fullscreen)
3551     meta_screen_queue_check_fullscreen (window->screen);
3552 
3553   g_object_freeze_notify (G_OBJECT (window));
3554   g_object_notify (G_OBJECT (window), "maximized-horizontally");
3555   g_object_notify (G_OBJECT (window), "maximized-vertically");
3556   g_object_thaw_notify (G_OBJECT (window));
3557 }
3558 
3559 void
meta_window_maximize(MetaWindow * window,MetaMaximizeFlags directions)3560 meta_window_maximize (MetaWindow        *window,
3561                       MetaMaximizeFlags  directions)
3562 {
3563   MetaRectangle *saved_rect = NULL;
3564   gboolean maximize_horizontally, maximize_vertically;
3565 
3566   g_return_if_fail (!window->override_redirect);
3567 
3568   /* At least one of the two directions ought to be set */
3569   maximize_horizontally = directions & META_MAXIMIZE_HORIZONTAL;
3570   maximize_vertically   = directions & META_MAXIMIZE_VERTICAL;
3571   g_assert (maximize_horizontally || maximize_vertically);
3572 
3573   /* Only do something if the window isn't already maximized in the
3574    * given direction(s).
3575    */
3576   if ((maximize_horizontally && !window->maximized_horizontally) ||
3577       (maximize_vertically   && !window->maximized_vertically))
3578     {
3579       if (window->shaded && maximize_vertically)
3580         {
3581           /* Shading sucks anyway; I'm not adding a timestamp argument
3582            * to this function just for this niche usage & corner case.
3583            */
3584           guint32 timestamp =
3585             meta_display_get_current_time_roundtrip (window->display);
3586           meta_window_unshade (window, timestamp);
3587         }
3588 
3589       /* if the window hasn't been placed yet, we'll maximize it then
3590        */
3591       if (!window->placed)
3592 	{
3593 	  window->maximize_horizontally_after_placement =
3594             window->maximize_horizontally_after_placement ||
3595             maximize_horizontally;
3596 	  window->maximize_vertically_after_placement =
3597             window->maximize_vertically_after_placement ||
3598             maximize_vertically;
3599 	  return;
3600 	}
3601 
3602     if ((window->tile_mode != META_TILE_NONE ||
3603         window->last_tile_mode != META_TILE_NONE) &&
3604         window->tile_mode != META_TILE_MAXIMIZE)
3605       {
3606         saved_rect = &window->saved_rect;
3607 
3608         window->maximized_vertically = FALSE;
3609       }
3610 
3611     meta_window_maximize_internal (window,
3612                                    directions,
3613                                    saved_rect);
3614 
3615     MetaRectangle old_rect;
3616     MetaRectangle new_rect;
3617     gboolean desktop_effects = meta_prefs_get_desktop_effects ();
3618 
3619     if (desktop_effects)
3620       meta_window_get_outer_rect (window, &old_rect);
3621 
3622     meta_window_move_resize_now (window);
3623 
3624     if (desktop_effects)
3625       {
3626         meta_window_get_outer_rect (window, &new_rect);
3627         meta_compositor_maximize_window (window->display->compositor,
3628                                         window,
3629                                         &old_rect,
3630                                         &new_rect);
3631       }
3632     }
3633 
3634   meta_screen_tile_preview_hide (window->screen);
3635   normalize_tile_state (window);
3636 }
3637 
3638 /**
3639  * meta_window_get_maximized:
3640  *
3641  * Gets the current maximization state of the window, as combination
3642  * of the %META_MAXIMIZE_HORIZONTAL and %META_MAXIMIZE_VERTICAL flags;
3643  *
3644  * Return value: current maximization state
3645  */
3646 MetaMaximizeFlags
meta_window_get_maximized(MetaWindow * window)3647 meta_window_get_maximized (MetaWindow *window)
3648 {
3649   return ((window->maximized_horizontally ? META_MAXIMIZE_HORIZONTAL : 0) |
3650           (window->maximized_vertically ? META_MAXIMIZE_VERTICAL : 0));
3651 }
3652 
3653 /**
3654  * meta_window_is_fullscreen:
3655  *
3656  * Return value: %TRUE if the window is currently fullscreen
3657  */
3658 gboolean
meta_window_is_fullscreen(MetaWindow * window)3659 meta_window_is_fullscreen (MetaWindow *window)
3660 {
3661   return window->fullscreen;
3662 }
3663 
3664 /**
3665  * meta_window_get_all_monitors:
3666  * @window: The #MetaWindow
3667  * @length: (out caller-allocates): gint holding the length, may be %NULL to
3668  *                                  ignore
3669  *
3670  * Returns: (array length=length) (element-type gint) (transfer container):
3671  *           List of the monitor indices the window is on.
3672  */
3673 gint *
meta_window_get_all_monitors(MetaWindow * window,gsize * length)3674 meta_window_get_all_monitors (MetaWindow *window, gsize *length)
3675 {
3676   GArray *monitors;
3677   MetaRectangle window_rect;
3678   int i;
3679 
3680   monitors = g_array_new (FALSE, FALSE, sizeof (int));
3681 
3682   if (meta_window_is_client_decorated (window))
3683     {
3684       window_rect = window->rect;
3685     }
3686   else
3687     {
3688       meta_window_get_outer_rect (window, &window_rect);
3689     }
3690 
3691   for (i = 0; i < window->screen->n_monitor_infos; i++)
3692     {
3693       MetaRectangle *monitor_rect = &window->screen->monitor_infos[i].rect;
3694 
3695       if (window->fullscreen)
3696         {
3697           if (meta_rectangle_contains_rect (&window_rect, monitor_rect))
3698             {
3699               g_array_append_val (monitors, i);
3700             }
3701         }
3702       else
3703         {
3704           if (meta_rectangle_overlap (&window_rect, monitor_rect))
3705             {
3706               g_array_append_val (monitors, i);
3707             }
3708         }
3709     }
3710 
3711   if (length)
3712     *length = monitors->len;
3713 
3714   i = -1;
3715   g_array_append_val (monitors, i);
3716 
3717   return (gint*) g_array_free (monitors, FALSE);
3718 }
3719 
3720 /**
3721 * meta_window_is_monitor_sized:
3722  *
3723  * Return value: %TRUE if the window is occupies an entire monitor or
3724  *               the whole screen.
3725  */
3726 gboolean
meta_window_is_monitor_sized(MetaWindow * window)3727 meta_window_is_monitor_sized (MetaWindow *window)
3728 {
3729   if (window->fullscreen)
3730     return TRUE;
3731 
3732   if (window->override_redirect)
3733     {
3734       MetaRectangle window_rect, monitor_rect;
3735       int screen_width, screen_height;
3736 
3737       meta_screen_get_size (window->screen, &screen_width, &screen_height);
3738       meta_window_get_outer_rect (window, &window_rect);
3739 
3740       if (window_rect.x == 0 && window_rect.y == 0 &&
3741           window_rect.width == screen_width && window_rect.height == screen_height)
3742         return TRUE;
3743 
3744       meta_screen_get_monitor_geometry (window->screen, window->monitor->number, &monitor_rect);
3745 
3746       if (meta_rectangle_equal (&window_rect, &monitor_rect))
3747         return TRUE;
3748     }
3749 
3750   return FALSE;
3751 }
3752 
3753 /**
3754  * meta_window_is_on_primary_monitor:
3755  *
3756  * Return value: %TRUE if the window is on the primary monitor
3757  */
3758 gboolean
meta_window_is_on_primary_monitor(MetaWindow * window)3759 meta_window_is_on_primary_monitor (MetaWindow *window)
3760 {
3761   return window->monitor->is_primary;
3762 }
3763 
3764 /**
3765  * meta_window_requested_bypass_compositor:
3766  *
3767  * Return value: %TRUE if the window requested to bypass the compositor
3768  */
3769 gboolean
meta_window_requested_bypass_compositor(MetaWindow * window)3770 meta_window_requested_bypass_compositor (MetaWindow *window)
3771 {
3772   return window->bypass_compositor == _NET_WM_BYPASS_COMPOSITOR_HINT_ON;
3773 }
3774 
3775 /**
3776  * meta_window_requested_dont_bypass_compositor:
3777  *
3778  * Return value: %TRUE if the window requested to opt out of unredirecting
3779  */
3780 gboolean
meta_window_requested_dont_bypass_compositor(MetaWindow * window)3781 meta_window_requested_dont_bypass_compositor (MetaWindow *window)
3782 {
3783   return window->bypass_compositor == _NET_WM_BYPASS_COMPOSITOR_HINT_OFF;
3784 }
3785 
3786 static void
normalize_tile_state(MetaWindow * window)3787 normalize_tile_state (MetaWindow *window)
3788 {
3789   window->snap_queued = FALSE;
3790   window->resize_tile_mode = META_TILE_NONE;
3791   window->resizing_tile_type = META_WINDOW_TILE_TYPE_NONE;
3792   meta_screen_update_snapped_windows (window->screen);
3793 }
3794 
3795 LOCAL_SYMBOL void
meta_window_real_tile(MetaWindow * window,gboolean force)3796 meta_window_real_tile (MetaWindow *window, gboolean force)
3797 {
3798 /* Don't do anything if no tiling is requested or we're already tiled */
3799   if (window->tile_mode == META_TILE_NONE || (META_WINDOW_TILED_OR_SNAPPED (window) && !force))
3800     return;
3801 
3802   if (window->last_tile_mode == META_TILE_NONE &&
3803       window->resizing_tile_type == META_WINDOW_TILE_TYPE_NONE &&
3804       !META_WINDOW_MAXIMIZED (window))
3805   {
3806      meta_window_save_rect (window);
3807   }
3808 
3809   window->maximized_horizontally = FALSE;
3810   window->maximized_vertically = FALSE;
3811 
3812   if (window->tile_mode != META_TILE_NONE) {
3813       if (window->snap_queued || window->resizing_tile_type == META_WINDOW_TILE_TYPE_SNAPPED) {
3814         meta_window_set_tile_type (window, META_WINDOW_TILE_TYPE_SNAPPED);
3815       } else {
3816         meta_window_set_tile_type (window, META_WINDOW_TILE_TYPE_TILED);
3817       }
3818   } else {
3819       meta_window_set_tile_type (window, META_WINDOW_TILE_TYPE_NONE);
3820   }
3821 
3822   recalc_window_features (window);
3823 
3824   meta_screen_tile_preview_update (window->screen, FALSE);
3825 
3826   if (window->resize_tile_mode == META_TILE_NONE)
3827     {
3828       MetaRectangle old_rect;
3829       MetaRectangle new_rect;
3830       gboolean desktop_effects = meta_prefs_get_desktop_effects ();
3831 
3832       if (desktop_effects)
3833         meta_window_get_input_rect (window, &old_rect);
3834 
3835       meta_window_move_resize_now (window);
3836 
3837       if (desktop_effects)
3838         {
3839           meta_window_get_input_rect (window, &new_rect);
3840           meta_compositor_tile_window (window->display->compositor,
3841                                       window,
3842                                       &old_rect,
3843                                       &new_rect);
3844         }
3845 
3846       if (window->frame)
3847         meta_ui_queue_frame_draw (window->screen->ui,
3848                                   window->frame->xwindow);
3849     }
3850   else
3851     {
3852       /* move_resize with new tiling constraints
3853        */
3854       meta_window_queue (window, META_QUEUE_MOVE_RESIZE);
3855     }
3856 
3857   normalize_tile_state (window);
3858 
3859   set_net_wm_state (window);
3860 
3861   meta_screen_tile_preview_hide (window->screen);
3862   meta_window_get_outer_rect (window, &window->snapped_rect);
3863 }
3864 
3865 static gboolean
meta_window_can_tile_maximized(MetaWindow * window)3866 meta_window_can_tile_maximized (MetaWindow *window)
3867 {
3868   return window->has_maximize_func;
3869 }
3870 
3871 LOCAL_SYMBOL gboolean
meta_window_can_tile_side_by_side(MetaWindow * window)3872 meta_window_can_tile_side_by_side (MetaWindow *window)
3873 {
3874   int monitor;
3875   MetaRectangle tile_area;
3876   MetaFrameBorders borders;
3877 
3878   if (!meta_window_can_tile_maximized (window))
3879     return FALSE;
3880 
3881   monitor = meta_screen_get_current_monitor (window->screen);
3882   meta_window_get_work_area_for_monitor (window, monitor, &tile_area);
3883 
3884   tile_area.width /= 2;
3885 
3886   meta_frame_calc_borders (window->frame, &borders);
3887   meta_window_unextend_by_frame (window, &tile_area, &borders);
3888 
3889   return tile_area.width >= window->size_hints.min_width &&
3890          tile_area.height >= window->size_hints.min_height;
3891 }
3892 
3893 LOCAL_SYMBOL gboolean
meta_window_can_tile_top_bottom(MetaWindow * window)3894 meta_window_can_tile_top_bottom (MetaWindow *window)
3895 {
3896   int monitor;
3897   MetaRectangle tile_area;
3898   MetaFrameBorders borders;
3899 
3900   if (!meta_window_can_tile_maximized (window))
3901     return FALSE;
3902 
3903   monitor = meta_screen_get_current_monitor (window->screen);
3904   meta_window_get_work_area_for_monitor (window, monitor, &tile_area);
3905 
3906   tile_area.height /= 2;
3907 
3908   meta_frame_calc_borders (window->frame, &borders);
3909   meta_window_unextend_by_frame (window, &tile_area, &borders);
3910 
3911   return tile_area.width >= window->size_hints.min_width &&
3912          tile_area.height >= window->size_hints.min_height;
3913 }
3914 
3915 LOCAL_SYMBOL gboolean
meta_window_can_tile_corner(MetaWindow * window)3916 meta_window_can_tile_corner (MetaWindow *window)
3917 {
3918   int monitor;
3919   MetaRectangle tile_area;
3920   MetaFrameBorders borders;
3921 
3922   if (!meta_window_can_tile_maximized (window))
3923     return FALSE;
3924 
3925   monitor = meta_screen_get_current_monitor (window->screen);
3926   meta_window_get_work_area_for_monitor (window, monitor, &tile_area);
3927 
3928   tile_area.width /= 2;
3929   tile_area.height /= 2;
3930 
3931   meta_frame_calc_borders (window->frame, &borders);
3932   meta_window_unextend_by_frame (window, &tile_area, &borders);
3933 
3934   return tile_area.width >= window->size_hints.min_width &&
3935          tile_area.height >= window->size_hints.min_height;
3936 }
3937 
3938 static void
unmaximize_window_before_freeing(MetaWindow * window)3939 unmaximize_window_before_freeing (MetaWindow        *window)
3940 {
3941   meta_topic (META_DEBUG_WINDOW_OPS,
3942               "Unmaximizing %s just before freeing\n",
3943               window->desc);
3944 
3945   window->maximized_horizontally = FALSE;
3946   window->maximized_vertically = FALSE;
3947   window->custom_snap_size = FALSE;
3948   if (window->tile_type != META_WINDOW_TILE_TYPE_NONE) {
3949     meta_window_set_tile_type (window, META_WINDOW_TILE_TYPE_NONE);
3950     meta_screen_update_snapped_windows (window->screen);
3951   }
3952 
3953   if (window->withdrawn)                /* See bug #137185 */
3954     {
3955       window->rect = window->saved_rect;
3956       set_net_wm_state (window);
3957     }
3958   else if (window->screen->closing)     /* See bug #358042 */
3959     {
3960       /* Do NOT update net_wm_state: this screen is closing,
3961        * it likely will be managed by another window manager
3962        * that will need the current _NET_WM_STATE atoms.
3963        * Moreover, it will need to know the unmaximized geometry,
3964        * therefore move_resize the window to saved_rect here
3965        * before closing it. */
3966       meta_window_move_resize (window,
3967                                FALSE,
3968                                window->saved_rect.x,
3969                                window->saved_rect.y,
3970                                window->saved_rect.width,
3971                                window->saved_rect.height);
3972     }
3973 }
3974 
3975 static void
meta_window_unmaximize_internal(MetaWindow * window,MetaMaximizeFlags directions,MetaRectangle * desired_rect,int gravity)3976 meta_window_unmaximize_internal (MetaWindow        *window,
3977                                  MetaMaximizeFlags  directions,
3978                                  MetaRectangle     *desired_rect,
3979                                  int                gravity)
3980 {
3981   gboolean unmaximize_horizontally, unmaximize_vertically;
3982   g_return_if_fail (!window->override_redirect);
3983 
3984   /* At least one of the two directions ought to be set */
3985   unmaximize_horizontally = directions & META_MAXIMIZE_HORIZONTAL;
3986   unmaximize_vertically   = directions & META_MAXIMIZE_VERTICAL;
3987 
3988   if (unmaximize_horizontally && unmaximize_vertically)
3989     window->saved_maximize = FALSE;
3990 
3991   /* Only do something if the window isn't already maximized in the
3992    * given direction(s).
3993    */
3994   if ((unmaximize_horizontally && window->maximized_horizontally) ||
3995       (unmaximize_vertically   && window->maximized_vertically) ||
3996       window->tile_type == META_WINDOW_TILE_TYPE_NONE ||
3997       window->tile_mode == META_TILE_NONE)
3998     {
3999       MetaRectangle target_rect;
4000       MetaRectangle work_area;
4001 
4002       window->last_tile_mode = META_TILE_NONE;
4003       window->tile_mode = META_TILE_NONE;
4004       window->resizing_tile_type = META_WINDOW_TILE_TYPE_NONE;
4005       meta_window_set_tile_type (window, META_WINDOW_TILE_TYPE_NONE);
4006 
4007       meta_window_get_work_area_for_monitor (window, window->monitor->number, &work_area);
4008 
4009       meta_topic (META_DEBUG_WINDOW_OPS,
4010                   "Unmaximizing %s%s\n",
4011                   window->desc,
4012                   unmaximize_horizontally && unmaximize_vertically ? "" :
4013                     unmaximize_horizontally ? " horizontally" :
4014                       unmaximize_vertically ? " vertically" : "BUGGGGG");
4015 
4016       window->maximized_horizontally =
4017         window->maximized_horizontally && !unmaximize_horizontally;
4018       window->maximized_vertically =
4019         window->maximized_vertically   && !unmaximize_vertically;
4020 
4021       /* Update the edge constraints */
4022       update_edge_constraints (window);
4023 
4024       /* recalc_features() will eventually clear the cached frame
4025        * extents, but we need the correct frame extents in the code below,
4026        * so invalidate the old frame extents manually up front.
4027        */
4028       meta_window_frame_size_changed (window);
4029 
4030       /* Unmaximize to the saved_rect position in the direction(s)
4031        * being unmaximized.
4032        */
4033       meta_window_get_client_root_coords (window, &target_rect);
4034 
4035       if (unmaximize_horizontally)
4036         {
4037           target_rect.x     = desired_rect->x;
4038           target_rect.width = desired_rect->width;
4039         }
4040       if (unmaximize_vertically)
4041         {
4042           target_rect.y      = desired_rect->y;
4043           target_rect.height = desired_rect->height;
4044         }
4045 
4046       /* Window's size hints may have changed while maximized, making
4047        * saved_rect invalid.  #329152
4048        */
4049       ensure_size_hints_satisfied (&target_rect, &window->size_hints);
4050 
4051       if (window->resizing_tile_type == META_WINDOW_TILE_TYPE_NONE)
4052         {
4053           MetaRectangle old_rect, new_rect;
4054           gboolean desktop_effects = meta_prefs_get_desktop_effects ();
4055 
4056           if (desktop_effects)
4057             meta_window_get_outer_rect (window, &old_rect);
4058 
4059           meta_window_move_resize_internal (window,
4060                                             META_IS_MOVE_ACTION | META_IS_RESIZE_ACTION,
4061                                             gravity,
4062                                             target_rect.x,
4063                                             target_rect.y,
4064                                             target_rect.width,
4065                                             target_rect.height);
4066 
4067           if (desktop_effects)
4068             {
4069               meta_window_get_outer_rect (window, &new_rect);
4070               meta_compositor_unmaximize_window (window->display->compositor,
4071                                                 window,
4072                                                 &old_rect,
4073                                                 &new_rect);
4074             }
4075         }
4076       else
4077         {
4078           meta_window_move_resize_internal (window,
4079                                             META_IS_MOVE_ACTION | META_IS_RESIZE_ACTION,
4080                                             gravity,
4081                                             target_rect.x,
4082                                             target_rect.y,
4083                                             target_rect.width,
4084                                             target_rect.height);
4085         }
4086 
4087       /* Make sure user_rect is current.
4088        */
4089       force_save_user_window_placement (window);
4090 
4091       /* When we unmaximize, if we're doing a mouse move also we could
4092        * get the window suddenly jumping to the upper left corner of
4093        * the workspace, since that's where it was when the grab op
4094        * started.  So we need to update the grab state. We have to do
4095        * it after the actual operation, as the window may have been moved
4096        * by constraints.
4097        */
4098       if (meta_grab_op_is_moving (window->display->grab_op) &&
4099           window->display->grab_window == window)
4100         {
4101           window->display->grab_anchor_window_pos = window->user_rect;
4102         }
4103 
4104       if (window->resizing_tile_type == META_WINDOW_TILE_TYPE_NONE)
4105           window->custom_snap_size = FALSE;
4106 
4107       meta_screen_update_snapped_windows (window->screen);
4108 
4109       recalc_window_features (window);
4110       set_net_wm_state (window);
4111       if (!window->monitor->in_fullscreen)
4112         meta_screen_queue_check_fullscreen (window->screen);
4113     }
4114 
4115     g_object_freeze_notify (G_OBJECT (window));
4116     g_object_notify (G_OBJECT (window), "maximized-horizontally");
4117     g_object_notify (G_OBJECT (window), "maximized-vertically");
4118     g_object_thaw_notify (G_OBJECT (window));
4119 }
4120 
4121 void
meta_window_unmaximize(MetaWindow * window,MetaMaximizeFlags directions)4122 meta_window_unmaximize (MetaWindow        *window,
4123                         MetaMaximizeFlags  directions)
4124 {
4125   /* Restore tiling if necessary */
4126   if (window->tile_mode == META_TILE_LEFT ||
4127       window->tile_mode == META_TILE_RIGHT)
4128     {
4129       window->maximized_horizontally = FALSE;
4130       meta_window_real_tile (window, FALSE);
4131       return;
4132     }
4133 
4134   meta_window_unmaximize_internal (window, directions, &window->saved_rect,
4135                                    NorthWestGravity);
4136 }
4137 
4138 /* Like meta_window_unmaximize(), but instead of unmaximizing to the
4139  * saved position, we give the new desired size, and the gravity that
4140  * determines the positioning relationship between the area occupied
4141  * maximized and the new are. The arguments are similar to
4142  * meta_window_resize_with_gravity().
4143  * Unlike meta_window_unmaximize(), tiling is not restored for windows
4144  * with a tile mode other than META_TILE_NONE.
4145  */
4146 LOCAL_SYMBOL void
meta_window_unmaximize_with_gravity(MetaWindow * window,MetaMaximizeFlags directions,int new_width,int new_height,int gravity)4147 meta_window_unmaximize_with_gravity (MetaWindow        *window,
4148                                      MetaMaximizeFlags  directions,
4149                                      int                new_width,
4150                                      int                new_height,
4151                                      int                gravity)
4152 {
4153   MetaRectangle desired_rect;
4154 
4155   meta_window_get_position (window, &desired_rect.x, &desired_rect.y);
4156   desired_rect.width = new_width;
4157   desired_rect.height = new_height;
4158 
4159   meta_window_unmaximize_internal (window, directions, &desired_rect, gravity);
4160 }
4161 
4162 LOCAL_SYMBOL void
meta_window_make_above(MetaWindow * window)4163 meta_window_make_above (MetaWindow  *window)
4164 {
4165   g_return_if_fail (!window->override_redirect);
4166 
4167   meta_window_set_above (window, TRUE);
4168   meta_window_raise (window);
4169 }
4170 
4171 LOCAL_SYMBOL void
meta_window_unmake_above(MetaWindow * window)4172 meta_window_unmake_above (MetaWindow  *window)
4173 {
4174   g_return_if_fail (!window->override_redirect);
4175 
4176   meta_window_set_above (window, FALSE);
4177   meta_window_raise (window);
4178 }
4179 
4180 static void
meta_window_set_above(MetaWindow * window,gboolean new_value)4181 meta_window_set_above (MetaWindow *window,
4182                        gboolean    new_value)
4183 {
4184   new_value = new_value != FALSE;
4185   if (new_value == window->wm_state_above)
4186     return;
4187 
4188   window->wm_state_above = new_value;
4189   meta_window_update_layer (window);
4190   set_net_wm_state (window);
4191   meta_window_frame_size_changed (window);
4192   g_object_notify (G_OBJECT (window), "above");
4193 }
4194 
4195 LOCAL_SYMBOL LOCAL_SYMBOL void
meta_window_make_fullscreen_internal(MetaWindow * window)4196 meta_window_make_fullscreen_internal (MetaWindow  *window)
4197 {
4198   if (!window->fullscreen)
4199     {
4200       meta_topic (META_DEBUG_WINDOW_OPS,
4201                   "Fullscreening %s\n", window->desc);
4202 
4203       if (window->shaded)
4204         {
4205           /* Shading sucks anyway; I'm not adding a timestamp argument
4206            * to this function just for this niche usage & corner case.
4207            */
4208           guint32 timestamp =
4209             meta_display_get_current_time_roundtrip (window->display);
4210           meta_window_unshade (window, timestamp);
4211         }
4212 
4213       meta_window_save_rect (window);
4214 
4215       window->fullscreen = TRUE;
4216       window->force_save_user_rect = FALSE;
4217 
4218       meta_stack_freeze (window->screen->stack);
4219 
4220       meta_window_raise (window);
4221       meta_stack_thaw (window->screen->stack);
4222 
4223       recalc_window_features (window);
4224       set_net_wm_state (window);
4225 
4226       /* For the auto-minimize feature, if we fail to get focus */
4227       meta_screen_queue_check_fullscreen (window->screen);
4228 
4229       meta_stack_tracker_queue_sync_stack (window->screen->stack_tracker);
4230       g_object_notify (G_OBJECT (window), "fullscreen");
4231     }
4232 }
4233 
4234 LOCAL_SYMBOL void
meta_window_make_fullscreen(MetaWindow * window)4235 meta_window_make_fullscreen (MetaWindow  *window)
4236 {
4237   g_return_if_fail (!window->override_redirect);
4238 
4239   if (!window->fullscreen)
4240     {
4241       meta_window_make_fullscreen_internal (window);
4242       /* move_resize with new constraints
4243        */
4244       meta_window_queue(window, META_QUEUE_MOVE_RESIZE);
4245     }
4246 }
4247 
4248 LOCAL_SYMBOL void
meta_window_unmake_fullscreen(MetaWindow * window)4249 meta_window_unmake_fullscreen (MetaWindow  *window)
4250 {
4251   g_return_if_fail (!window->override_redirect);
4252 
4253   if (window->fullscreen)
4254     {
4255       MetaRectangle target_rect;
4256 
4257       meta_topic (META_DEBUG_WINDOW_OPS,
4258                   "Unfullscreening %s\n", window->desc);
4259 
4260       window->fullscreen = FALSE;
4261       target_rect = window->saved_rect;
4262 
4263       /* Window's size hints may have changed while maximized, making
4264        * saved_rect invalid.  #329152
4265        */
4266       ensure_size_hints_satisfied (&target_rect, &window->size_hints);
4267 
4268       /* Need to update window->has_resize_func before we move_resize()
4269        */
4270       recalc_window_features (window);
4271       set_net_wm_state (window);
4272 
4273       meta_window_move_resize (window,
4274                                FALSE,
4275                                target_rect.x,
4276                                target_rect.y,
4277                                target_rect.width,
4278                                target_rect.height);
4279 
4280       /* Make sure user_rect is current.
4281        */
4282       force_save_user_window_placement (window);
4283 
4284       meta_screen_queue_check_fullscreen (window->screen);
4285 
4286       meta_stack_tracker_queue_sync_stack (window->screen->stack_tracker);
4287       g_object_notify (G_OBJECT (window), "fullscreen");
4288     }
4289 }
4290 
4291 LOCAL_SYMBOL void
meta_window_update_fullscreen_monitors(MetaWindow * window,unsigned long top,unsigned long bottom,unsigned long left,unsigned long right)4292 meta_window_update_fullscreen_monitors (MetaWindow    *window,
4293                                         unsigned long  top,
4294                                         unsigned long  bottom,
4295                                         unsigned long  left,
4296                                         unsigned long  right)
4297 {
4298   if ((int)top < window->screen->n_monitor_infos &&
4299       (int)bottom < window->screen->n_monitor_infos &&
4300       (int)left < window->screen->n_monitor_infos &&
4301       (int)right < window->screen->n_monitor_infos)
4302     {
4303       window->fullscreen_monitors[0] = top;
4304       window->fullscreen_monitors[1] = bottom;
4305       window->fullscreen_monitors[2] = left;
4306       window->fullscreen_monitors[3] = right;
4307     }
4308   else
4309     {
4310       window->fullscreen_monitors[0] = -1;
4311     }
4312 
4313   if (window->fullscreen)
4314     {
4315       meta_window_queue(window, META_QUEUE_MOVE_RESIZE);
4316     }
4317 }
4318 
4319 LOCAL_SYMBOL void
meta_window_shade(MetaWindow * window,guint32 timestamp)4320 meta_window_shade (MetaWindow  *window,
4321                    guint32      timestamp)
4322 {
4323   g_return_if_fail (!window->override_redirect);
4324 
4325   meta_topic (META_DEBUG_WINDOW_OPS,
4326               "Shading %s\n", window->desc);
4327   if (!window->shaded)
4328     {
4329       window->shaded = TRUE;
4330 
4331       meta_window_queue(window, META_QUEUE_MOVE_RESIZE | META_QUEUE_CALC_SHOWING);
4332       meta_window_frame_size_changed (window);
4333 
4334       /* After queuing the calc showing, since _focus flushes it,
4335        * and we need to focus the frame
4336        */
4337       meta_topic (META_DEBUG_FOCUS,
4338                   "Re-focusing window %s after shading it\n",
4339                   window->desc);
4340       meta_window_focus (window, timestamp);
4341 
4342       set_net_wm_state (window);
4343     }
4344 }
4345 
4346 LOCAL_SYMBOL void
meta_window_unshade(MetaWindow * window,guint32 timestamp)4347 meta_window_unshade (MetaWindow  *window,
4348                      guint32      timestamp)
4349 {
4350   g_return_if_fail (!window->override_redirect);
4351 
4352   meta_topic (META_DEBUG_WINDOW_OPS,
4353               "Unshading %s\n", window->desc);
4354   if (window->shaded)
4355     {
4356       window->shaded = FALSE;
4357       meta_window_queue(window, META_QUEUE_MOVE_RESIZE | META_QUEUE_CALC_SHOWING);
4358       meta_window_frame_size_changed (window);
4359 
4360       /* focus the window */
4361       meta_topic (META_DEBUG_FOCUS,
4362                   "Focusing window %s after unshading it\n",
4363                   window->desc);
4364       meta_window_focus (window, timestamp);
4365 
4366       set_net_wm_state (window);
4367     }
4368 }
4369 
4370 #define OPACITY_STEP 32
4371 
4372 LOCAL_SYMBOL void
meta_window_adjust_opacity(MetaWindow * window,gboolean increase)4373 meta_window_adjust_opacity (MetaWindow   *window,
4374                             gboolean      increase)
4375 {
4376   ClutterActor *actor = CLUTTER_ACTOR (meta_window_get_compositor_private (window));
4377 
4378   gint current_opacity, new_opacity;
4379 
4380   current_opacity = clutter_actor_get_opacity (actor);
4381 
4382   if (increase) {
4383     new_opacity = MIN (current_opacity + OPACITY_STEP, 255);
4384   } else {
4385     new_opacity = MAX (current_opacity - OPACITY_STEP, MAX (0, meta_prefs_get_min_win_opacity ()));
4386   }
4387 
4388   if (new_opacity != current_opacity) {
4389     clutter_actor_set_opacity (actor, (guint8) new_opacity);
4390   }
4391 }
4392 
4393 void
meta_window_reset_opacity(MetaWindow * window)4394 meta_window_reset_opacity (MetaWindow *window)
4395 {
4396     ClutterActor *actor = CLUTTER_ACTOR (meta_window_get_compositor_private (window));
4397 
4398     clutter_actor_set_opacity (actor, 255);
4399 }
4400 
4401 static gboolean
unminimize_func(MetaWindow * window,void * data)4402 unminimize_func (MetaWindow *window,
4403                  void       *data)
4404 {
4405   meta_window_unminimize (window);
4406   return TRUE;
4407 }
4408 
4409 static void
unminimize_window_and_all_transient_parents(MetaWindow * window)4410 unminimize_window_and_all_transient_parents (MetaWindow *window)
4411 {
4412   meta_window_unminimize (window);
4413   meta_window_foreach_ancestor (window, unminimize_func, NULL);
4414 }
4415 
4416 static void
window_activate(MetaWindow * window,guint32 timestamp,MetaClientType source_indication,MetaWorkspace * workspace)4417 window_activate (MetaWindow     *window,
4418                  guint32         timestamp,
4419                  MetaClientType  source_indication,
4420                  MetaWorkspace  *workspace)
4421 {
4422   meta_topic (META_DEBUG_FOCUS,
4423               "_NET_ACTIVE_WINDOW message sent for %s at time %u "
4424               "by client type %u.\n",
4425               window->desc, timestamp, source_indication);
4426 
4427   if (timestamp != 0 &&
4428       XSERVER_TIME_IS_BEFORE (timestamp, window->display->last_user_time))
4429     {
4430       meta_topic (META_DEBUG_FOCUS,
4431                   "last_user_time (%u) is more recent; ignoring "
4432                   " _NET_ACTIVE_WINDOW message.\n",
4433                   window->display->last_user_time);
4434       meta_window_set_demands_attention(window);
4435       return;
4436     }
4437 
4438   if (timestamp == 0)
4439     timestamp = meta_display_get_current_time_roundtrip (window->display);
4440 
4441   meta_window_set_user_time (window, timestamp);
4442 
4443   /* disable show desktop mode unless we're a desktop component */
4444   maybe_leave_show_desktop_mode (window);
4445 
4446   /* Get window on current or given workspace */
4447   if (workspace == NULL)
4448     workspace = window->screen->active_workspace;
4449 
4450   /* For non-transient windows, we just set up a pulsing indicator,
4451      rather than move windows or workspaces.
4452      See http://bugzilla.gnome.org/show_bug.cgi?id=482354 */
4453   if (window->xtransient_for == None &&
4454       !meta_window_located_on_workspace (window, window->screen->active_workspace))
4455     {
4456       meta_window_set_demands_attention (window);
4457       /* We've marked it as demanding, don't need to do anything else. */
4458       return;
4459     }
4460   else if (window->xtransient_for != None)
4461     {
4462       /* Move transients to current workspace - preference dialogs should appear over
4463          the source window.  */
4464         meta_window_change_workspace (window, workspace);
4465     }
4466 
4467   if (window->shaded)
4468     meta_window_unshade (window, timestamp);
4469 
4470   unminimize_window_and_all_transient_parents (window);
4471 
4472   if (meta_prefs_get_raise_on_click () ||
4473       source_indication == META_CLIENT_TYPE_PAGER)
4474     meta_window_raise (window);
4475 
4476   meta_topic (META_DEBUG_FOCUS,
4477               "Focusing window %s due to activation\n",
4478               window->desc);
4479   meta_window_focus (window, timestamp);
4480 }
4481 
4482 /* This function exists since most of the functionality in window_activate
4483  * is useful for Muffin, but Muffin shouldn't need to specify a client
4484  * type for itself.  ;-)
4485  */
4486 void
meta_window_activate(MetaWindow * window,guint32 timestamp)4487 meta_window_activate (MetaWindow     *window,
4488                       guint32         timestamp)
4489 {
4490   g_return_if_fail (!window->override_redirect);
4491 
4492   /* We're not really a pager, but the behavior we want is the same as if
4493    * we were such.  If we change the pager behavior later, we could revisit
4494    * this and just add extra flags to window_activate.
4495    */
4496   window_activate (window, timestamp, META_CLIENT_TYPE_PAGER, NULL);
4497 }
4498 
4499 void
meta_window_activate_with_workspace(MetaWindow * window,guint32 timestamp,MetaWorkspace * workspace)4500 meta_window_activate_with_workspace (MetaWindow     *window,
4501                                      guint32         timestamp,
4502                                      MetaWorkspace  *workspace)
4503 {
4504   g_return_if_fail (!window->override_redirect);
4505 
4506   /* We're not really a pager, but the behavior we want is the same as if
4507    * we were such.  If we change the pager behavior later, we could revisit
4508    * this and just add extra flags to window_activate.
4509    */
4510   window_activate (window, timestamp, META_CLIENT_TYPE_APPLICATION, workspace);
4511 }
4512 
4513 /* Manually fix all the weirdness explained in the big comment at the
4514  * beginning of meta_window_move_resize_internal() giving positions
4515  * expected by meta_window_constrain (i.e. positions & sizes of the
4516  * internal or client window).
4517  */
4518 static void
adjust_for_gravity(MetaWindow * window,MetaFrameBorders * borders,gboolean coords_assume_border,int gravity,MetaRectangle * rect)4519 adjust_for_gravity (MetaWindow        *window,
4520                     MetaFrameBorders  *borders,
4521                     gboolean           coords_assume_border,
4522                     int                gravity,
4523                     MetaRectangle     *rect)
4524 {
4525   int ref_x, ref_y;
4526   int bw;
4527   int child_x, child_y;
4528   int frame_width, frame_height;
4529 
4530   if (coords_assume_border)
4531     bw = window->border_width;
4532   else
4533     bw = 0;
4534 
4535   if (borders)
4536     {
4537       child_x = borders->visible.left;
4538       child_y = borders->visible.top;
4539       frame_width = child_x + rect->width + borders->visible.right;
4540       frame_height = child_y + rect->height + borders->visible.bottom;
4541     }
4542   else
4543     {
4544       child_x = 0;
4545       child_y = 0;
4546       frame_width = rect->width;
4547       frame_height = rect->height;
4548     }
4549 
4550   /* We're computing position to pass to window_move, which is
4551    * the position of the client window (StaticGravity basically)
4552    *
4553    * (see WM spec description of gravity computation, but note that
4554    * their formulas assume we're honoring the border width, rather
4555    * than compensating for having turned it off)
4556    */
4557   switch (gravity)
4558     {
4559     case NorthWestGravity:
4560       ref_x = rect->x;
4561       ref_y = rect->y;
4562       break;
4563     case NorthGravity:
4564       ref_x = rect->x + rect->width / 2 + bw;
4565       ref_y = rect->y;
4566       break;
4567     case NorthEastGravity:
4568       ref_x = rect->x + rect->width + bw * 2;
4569       ref_y = rect->y;
4570       break;
4571     case WestGravity:
4572       ref_x = rect->x;
4573       ref_y = rect->y + rect->height / 2 + bw;
4574       break;
4575     case CenterGravity:
4576       ref_x = rect->x + rect->width / 2 + bw;
4577       ref_y = rect->y + rect->height / 2 + bw;
4578       break;
4579     case EastGravity:
4580       ref_x = rect->x + rect->width + bw * 2;
4581       ref_y = rect->y + rect->height / 2 + bw;
4582       break;
4583     case SouthWestGravity:
4584       ref_x = rect->x;
4585       ref_y = rect->y + rect->height + bw * 2;
4586       break;
4587     case SouthGravity:
4588       ref_x = rect->x + rect->width / 2 + bw;
4589       ref_y = rect->y + rect->height + bw * 2;
4590       break;
4591     case SouthEastGravity:
4592       ref_x = rect->x + rect->width + bw * 2;
4593       ref_y = rect->y + rect->height + bw * 2;
4594       break;
4595     case StaticGravity:
4596     default:
4597       ref_x = rect->x;
4598       ref_y = rect->y;
4599       break;
4600     }
4601 
4602   switch (gravity)
4603     {
4604     case NorthWestGravity:
4605       rect->x = ref_x + child_x;
4606       rect->y = ref_y + child_y;
4607       break;
4608     case NorthGravity:
4609       rect->x = ref_x - frame_width / 2 + child_x;
4610       rect->y = ref_y + child_y;
4611       break;
4612     case NorthEastGravity:
4613       rect->x = ref_x - frame_width + child_x;
4614       rect->y = ref_y + child_y;
4615       break;
4616     case WestGravity:
4617       rect->x = ref_x + child_x;
4618       rect->y = ref_y - frame_height / 2 + child_y;
4619       break;
4620     case CenterGravity:
4621       rect->x = ref_x - frame_width / 2 + child_x;
4622       rect->y = ref_y - frame_height / 2 + child_y;
4623       break;
4624     case EastGravity:
4625       rect->x = ref_x - frame_width + child_x;
4626       rect->y = ref_y - frame_height / 2 + child_y;
4627       break;
4628     case SouthWestGravity:
4629       rect->x = ref_x + child_x;
4630       rect->y = ref_y - frame_height + child_y;
4631       break;
4632     case SouthGravity:
4633       rect->x = ref_x - frame_width / 2 + child_x;
4634       rect->y = ref_y - frame_height + child_y;
4635       break;
4636     case SouthEastGravity:
4637       rect->x = ref_x - frame_width + child_x;
4638       rect->y = ref_y - frame_height + child_y;
4639       break;
4640     case StaticGravity:
4641     default:
4642       rect->x = ref_x;
4643       rect->y = ref_y;
4644       break;
4645     }
4646 }
4647 
4648 static gboolean
static_gravity_works(MetaDisplay * display)4649 static_gravity_works (MetaDisplay *display)
4650 {
4651   return display->static_gravity_works;
4652 }
4653 
4654 void
meta_window_create_sync_request_alarm(MetaWindow * window)4655 meta_window_create_sync_request_alarm (MetaWindow *window)
4656 {
4657 #ifdef HAVE_XSYNC
4658   XSyncAlarmAttributes values;
4659   XSyncValue init;
4660 
4661   if (window->sync_request_counter == None ||
4662       window->sync_request_alarm != None)
4663     return;
4664 
4665   meta_error_trap_push_with_return (window->display);
4666 
4667   /* In the new (extended style), the counter value is initialized by
4668    * the client before mapping the window. In the old style, we're
4669    * responsible for setting the initial value of the counter.
4670    */
4671   if (window->extended_sync_request_counter)
4672     {
4673       if (!XSyncQueryCounter(window->display->xdisplay,
4674                              window->sync_request_counter,
4675                              &init))
4676         {
4677           meta_error_trap_pop_with_return (window->display);
4678           window->sync_request_counter = None;
4679           return;
4680         }
4681 
4682       window->sync_request_serial =
4683         XSyncValueLow32 (init) + ((gint64)XSyncValueHigh32 (init) << 32);
4684 
4685       /* if the value is odd, the window starts off with updates frozen */
4686       meta_compositor_set_updates_frozen (window->display->compositor, window,
4687                                           meta_window_updates_are_frozen (window));
4688     }
4689   else
4690     {
4691       XSyncIntToValue (&init, 0);
4692       XSyncSetCounter (window->display->xdisplay,
4693                        window->sync_request_counter, init);
4694       window->sync_request_serial = 0;
4695     }
4696 
4697   values.trigger.counter = window->sync_request_counter;
4698   values.trigger.test_type = XSyncPositiveComparison;
4699 
4700   /* Initialize to one greater than the current value */
4701   values.trigger.value_type = XSyncRelative;
4702   XSyncIntToValue (&values.trigger.wait_value, 1);
4703 
4704   /* After triggering, increment test_value by this until
4705    * until the test condition is false */
4706   XSyncIntToValue (&values.delta, 1);
4707 
4708   /* we want events (on by default anyway) */
4709   values.events = True;
4710 
4711   window->sync_request_alarm = XSyncCreateAlarm (window->display->xdisplay,
4712                                                  XSyncCACounter |
4713                                                  XSyncCAValueType |
4714                                                  XSyncCAValue |
4715                                                  XSyncCATestType |
4716                                                  XSyncCADelta |
4717                                                  XSyncCAEvents,
4718                                                  &values);
4719 
4720   if (meta_error_trap_pop_with_return (window->display) == Success)
4721     meta_display_register_sync_alarm (window->display, &window->sync_request_alarm, window);
4722   else
4723     {
4724       window->sync_request_alarm = None;
4725       window->sync_request_counter = None;
4726     }
4727 #endif
4728 }
4729 
4730 void
meta_window_destroy_sync_request_alarm(MetaWindow * window)4731 meta_window_destroy_sync_request_alarm (MetaWindow *window)
4732 {
4733 #ifdef HAVE_XSYNC
4734   if (window->sync_request_alarm != None)
4735     {
4736       /* Has to be unregistered _before_ clearing the structure field */
4737       meta_display_unregister_sync_alarm (window->display, window->sync_request_alarm);
4738       XSyncDestroyAlarm (window->display->xdisplay,
4739                          window->sync_request_alarm);
4740       window->sync_request_alarm = None;
4741     }
4742 #endif /* HAVE_XSYNC */
4743 }
4744 
4745 #ifdef HAVE_XSYNC
4746 static gboolean
sync_request_timeout(gpointer data)4747 sync_request_timeout (gpointer data)
4748 {
4749   MetaWindow *window = data;
4750 
4751   window->sync_request_timeout_id = 0;
4752 
4753   /* We have now waited for more than a second for the
4754    * application to respond to the sync request
4755    */
4756   window->disable_sync = TRUE;
4757 
4758   /* Reset the wait serial, so we don't continue freezing
4759    * window updates
4760    */
4761   window->sync_request_wait_serial = 0;
4762   meta_compositor_set_updates_frozen (window->display->compositor, window,
4763                                       meta_window_updates_are_frozen (window));
4764 
4765   if (window == window->display->grab_window &&
4766       meta_grab_op_is_resizing (window->display->grab_op))
4767     {
4768       update_resize (window,
4769                      window->display->grab_last_user_action_was_snap,
4770                      window->display->grab_latest_motion_x,
4771                      window->display->grab_latest_motion_y,
4772                      TRUE);
4773     }
4774 
4775   return FALSE;
4776 }
4777 
4778 static void
send_sync_request(MetaWindow * window)4779 send_sync_request (MetaWindow *window)
4780 {
4781   XClientMessageEvent ev;
4782   gint64 wait_serial;
4783 
4784   /* For the old style of _NET_WM_SYNC_REQUEST_COUNTER, we just have to
4785    * increase the value, but for the new "extended" style we need to
4786    * pick an even (unfrozen) value sufficiently ahead of the last serial
4787    * that we received from the client; the same code still works
4788    * for the old style. The increment of 240 is specified by the EWMH
4789    * and is (1 second) * (60fps) * (an increment of 4 per frame).
4790    */
4791   wait_serial = window->sync_request_serial + 240;
4792 
4793   window->sync_request_wait_serial = wait_serial;
4794 
4795   ev.type = ClientMessage;
4796   ev.window = window->xwindow;
4797   ev.message_type = window->display->atom_WM_PROTOCOLS;
4798   ev.format = 32;
4799   ev.data.l[0] = window->display->atom__NET_WM_SYNC_REQUEST;
4800 
4801   ev.data.l[1] = window->display->current_time;
4802   ev.data.l[2] = wait_serial & G_GUINT64_CONSTANT(0xffffffff);
4803   ev.data.l[3] = wait_serial >> 32;
4804   ev.data.l[4] = window->extended_sync_request_counter ? 1 : 0;
4805 
4806   /* We don't need to trap errors here as we are already
4807    * inside an error_trap_push()/pop() pair.
4808    */
4809   XSendEvent (window->display->xdisplay,
4810 	      window->xwindow, False, 0, (XEvent*) &ev);
4811 
4812   /* We give the window 1 sec to respond to _NET_WM_SYNC_REQUEST;
4813    * if this time expires, we consider the window unresponsive
4814    * and resize it unsynchonized.
4815    */
4816   window->sync_request_timeout_id = g_timeout_add (1000,
4817                                                    sync_request_timeout,
4818                                                    window);
4819 
4820   meta_compositor_set_updates_frozen (window->display->compositor, window,
4821                                       meta_window_updates_are_frozen (window));
4822 }
4823 #endif
4824 
4825 /**
4826  * meta_window_updates_are_frozen:
4827  * @window: a #MetaWindow
4828  *
4829  * Gets whether the compositor should be updating the window contents;
4830  * window content updates may be frozen at client request by setting
4831  * an odd value in the extended _NET_WM_SYNC_REQUEST_COUNTER counter r
4832  * by the window manager during a resize operation while waiting for
4833  * the client to redraw.
4834  *
4835  * Return value: %TRUE if updates are currently frozen
4836  */
4837 gboolean
meta_window_updates_are_frozen(MetaWindow * window)4838 meta_window_updates_are_frozen (MetaWindow *window)
4839 {
4840 #ifdef HAVE_XSYNC
4841   if (window->extended_sync_request_counter &&
4842       window->sync_request_serial % 2 == 1)
4843     return TRUE;
4844 
4845   if (window->sync_request_serial < window->sync_request_wait_serial)
4846     return TRUE;
4847 #endif
4848 
4849   return FALSE;
4850 }
4851 
4852 static gboolean
maybe_move_attached_dialog(MetaWindow * window,void * data)4853 maybe_move_attached_dialog (MetaWindow *window,
4854                             void       *data)
4855 {
4856   if (meta_window_is_attached_dialog (window))
4857     /* It ignores x,y for such a dialog  */
4858     meta_window_move (window, FALSE, 0, 0);
4859 
4860   return FALSE;
4861 }
4862 
4863 /**
4864  * meta_window_get_monitor:
4865  * @window: a #MetaWindow
4866  *
4867  * Gets index of the monitor that this window is on.
4868  *
4869  * Return Value: The index of the monitor in the screens monitor list
4870  */
4871 int
meta_window_get_monitor(MetaWindow * window)4872 meta_window_get_monitor (MetaWindow *window)
4873 {
4874   g_return_val_if_fail (META_IS_WINDOW (window), -1);
4875 
4876   if (window->monitor == NULL)
4877     return -1;
4878 
4879   return window->monitor->number;
4880 }
4881 
4882 /* This is called when the monitor setup has changed. The window->monitor
4883  * reference is still "valid", but refer to the previous monitor setup */
4884 LOCAL_SYMBOL void
meta_window_update_for_monitors_changed(MetaWindow * window)4885 meta_window_update_for_monitors_changed (MetaWindow *window)
4886 {
4887   const MetaMonitorInfo *old, *new;
4888   int i;
4889 
4890   if (window->override_redirect)
4891     {
4892       meta_window_update_monitor (window);
4893       return;
4894     }
4895 
4896   old = window->monitor;
4897 
4898   /* Start on primary */
4899   new = &window->screen->monitor_infos[window->screen->primary_monitor_index];
4900 
4901   /* But, if we can find the old output on a new monitor, use that */
4902   for (i = 0; i < window->screen->n_monitor_infos; i++)
4903     {
4904       MetaMonitorInfo *info = &window->screen->monitor_infos[i];
4905 
4906       if (info->output == old->output)
4907         {
4908           new = info;
4909           break;
4910         }
4911     }
4912 
4913   if (window->tile_mode != META_TILE_NONE)
4914     window->tile_monitor_number = new->number;
4915 
4916   /* This will eventually reach meta_window_update_monitor that
4917    * will send leave/enter-monitor events. The old != new monitor
4918    * check will always fail (due to the new monitor_infos set) so
4919    * we will always send the events, even if the new and old monitor
4920    * index is the same. That is right, since the enumeration of the
4921    * monitors changed and the same index could be refereing
4922    * to a different monitor. */
4923   meta_window_move_between_rects (window,
4924                                   &old->rect,
4925                                   &new->rect);
4926 }
4927 
4928 static void
meta_window_update_monitor(MetaWindow * window)4929 meta_window_update_monitor (MetaWindow *window)
4930 {
4931   const MetaMonitorInfo *old;
4932 
4933   old = window->monitor;
4934   window->monitor = meta_screen_get_monitor_for_window (window->screen, window);
4935   if (old != window->monitor)
4936     {
4937       meta_window_update_on_all_workspaces (window);
4938 
4939       /* If workspaces only on primary and we moved back to primary, ensure that the
4940        * window is now in that workspace. We do this because while the window is on a
4941        * non-primary monitor it is always visible, so it would be very jarring if it
4942        * disappeared when it crossed the monitor border.
4943        * The one time we want it to both change to the primary monitor and a non-active
4944        * workspace is when dropping the window on some other workspace thumbnail directly.
4945        * That should be handled by explicitly moving the window before changing the
4946        * workspace
4947        * Don't do this if old == NULL, because thats what happens when starting up, and
4948        * we don't want to move all windows around from a previous WM instance. Nor do
4949        * we want it when moving from one primary monitor to another (can happen during
4950        * screen reconfiguration.
4951        */
4952       if (meta_prefs_get_workspaces_only_on_primary () &&
4953           meta_window_is_on_primary_monitor (window)  &&
4954           old != NULL && !old->is_primary &&
4955           window->screen->active_workspace != window->workspace)
4956         meta_window_change_workspace (window, window->screen->active_workspace);
4957 
4958       if (old) {
4959         meta_screen_queue_check_fullscreen (window->screen);
4960         g_signal_emit_by_name (window->screen, "window-left-monitor", old->number, window);
4961       }
4962       g_signal_emit_by_name (window->screen, "window-entered-monitor", window->monitor->number, window);
4963 
4964       g_signal_emit_by_name (window->screen, "window-monitor-changed", window, window->monitor->number);
4965 
4966       /* If we're changing monitors, we need to update the has_maximize_func flag,
4967        * as the working area has changed. */
4968       recalc_window_features (window);
4969     }
4970 }
4971 
4972 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)4973 meta_window_move_resize_internal (MetaWindow          *window,
4974                                   MetaMoveResizeFlags  flags,
4975                                   int                  gravity,
4976                                   int                  root_x_nw,
4977                                   int                  root_y_nw,
4978                                   int                  w,
4979                                   int                  h)
4980 {
4981   /* meta_window_move_resize_internal gets called with very different
4982    * meanings for root_x_nw and root_y_nw.  w & h are always the area
4983    * of the inner or client window (i.e. excluding the frame) and
4984    * gravity is the relevant gravity associated with the request (note
4985    * that gravity is ignored for move-only operations unless its
4986    * e.g. a configure request).  The location is different for
4987    * different cases because of how this function gets called; note
4988    * that in all cases what we want to find out is the upper left
4989    * corner of the position of the inner window:
4990    *
4991    *   Case | Called from (flags; gravity)
4992    *   -----+-----------------------------------------------
4993    *    1   | A resize only ConfigureRequest
4994    *    1   | meta_window_resize
4995    *    1   | meta_window_resize_with_gravity
4996    *    2   | New window
4997    *    2   | Session restore
4998    *    2   | A not-resize-only ConfigureRequest/net_moveresize_window request
4999    *    3   | meta_window_move
5000    *    3   | meta_window_move_resize
5001    *
5002    * For each of the cases, root_x_nw and root_y_nw must be treated as follows:
5003    *
5004    *   (1) They should be entirely ignored; instead the previous position
5005    *       and size of the window should be resized according to the given
5006    *       gravity in order to determine the new position of the window.
5007    *   (2) Needs to be fixed up by adjust_for_gravity() as these
5008    *       coordinates are relative to some corner or side of the outer
5009    *       window (except for the case of StaticGravity) and we want to
5010    *       know the location of the upper left corner of the inner window.
5011    *   (3) These values are already the desired positon of the NW corner
5012    *       of the inner window
5013    */
5014   XWindowChanges values;
5015   unsigned int mask;
5016   gboolean need_configure_notify;
5017   MetaFrameBorders borders;
5018   gboolean need_move_client = FALSE;
5019   gboolean need_move_frame = FALSE;
5020   gboolean need_resize_client = FALSE;
5021   gboolean need_resize_frame = FALSE;
5022   int frame_size_dx;
5023   int frame_size_dy;
5024   int size_dx;
5025   int size_dy;
5026   gboolean frame_shape_changed = FALSE;
5027   gboolean is_configure_request;
5028   gboolean do_gravity_adjust;
5029   gboolean is_user_action;
5030   gboolean did_placement;
5031   gboolean configure_frame_first;
5032   gboolean use_static_gravity;
5033   /* used for the configure request, but may not be final
5034    * destination due to StaticGravity etc.
5035    */
5036   int client_move_x;
5037   int client_move_y;
5038   MetaRectangle new_rect;
5039   MetaRectangle old_rect;
5040 
5041   if (window->type != META_WINDOW_TOOLTIP)
5042     {
5043       g_return_if_fail (!window->override_redirect);
5044     }
5045 
5046   is_configure_request = (flags & META_IS_CONFIGURE_REQUEST) != 0;
5047   do_gravity_adjust = (flags & META_DO_GRAVITY_ADJUST) != 0;
5048   is_user_action = (flags & META_IS_USER_ACTION) != 0;
5049 
5050   /* The action has to be a move or a resize or both... */
5051   g_assert (flags & (META_IS_MOVE_ACTION | META_IS_RESIZE_ACTION));
5052 
5053   /* We don't need it in the idle queue anymore. */
5054   meta_window_unqueue (window, META_QUEUE_MOVE_RESIZE);
5055 
5056   meta_window_get_client_root_coords (window, &old_rect);
5057 
5058   meta_topic (META_DEBUG_GEOMETRY,
5059               "Move/resize %s to %d,%d %dx%d%s%s from %d,%d %dx%d\n",
5060               window->desc, root_x_nw, root_y_nw, w, h,
5061               is_configure_request ? " (configure request)" : "",
5062               is_user_action ? " (user move/resize)" : "",
5063               old_rect.x, old_rect.y, old_rect.width, old_rect.height);
5064 
5065   meta_frame_calc_borders (window->frame,
5066                            &borders);
5067 
5068   new_rect.x = root_x_nw;
5069   new_rect.y = root_y_nw;
5070   new_rect.width  = w;
5071   new_rect.height = h;
5072 
5073   /* If this is a resize only, the position should be ignored and
5074    * instead obtained by resizing the old rectangle according to the
5075    * relevant gravity.
5076    */
5077   if ((flags & (META_IS_MOVE_ACTION | META_IS_RESIZE_ACTION)) ==
5078       META_IS_RESIZE_ACTION)
5079     {
5080       meta_rectangle_resize_with_gravity (&old_rect,
5081                                           &new_rect,
5082                                           gravity,
5083                                           new_rect.width,
5084                                           new_rect.height);
5085 
5086       meta_topic (META_DEBUG_GEOMETRY,
5087                   "Compensated for gravity in resize action; new pos %d,%d\n",
5088                   new_rect.x, new_rect.y);
5089     }
5090   else if (is_configure_request || do_gravity_adjust)
5091     {
5092       adjust_for_gravity (window,
5093                           window->frame ? &borders : NULL,
5094                           /* configure request coords assume
5095                            * the border width existed
5096                            */
5097                           is_configure_request,
5098                           gravity,
5099                           &new_rect);
5100 
5101       meta_topic (META_DEBUG_GEOMETRY,
5102                   "Compensated for configure_request/do_gravity_adjust needing "
5103                   "weird positioning; new pos %d,%d\n",
5104                   new_rect.x, new_rect.y);
5105     }
5106 
5107   did_placement = !window->placed && window->calc_placement;
5108 
5109   meta_window_constrain (window,
5110                          window->frame ? &borders : NULL,
5111                          flags,
5112                          gravity,
5113                          &old_rect,
5114                          &new_rect);
5115 
5116   w = new_rect.width;
5117   h = new_rect.height;
5118   root_x_nw = new_rect.x;
5119   root_y_nw = new_rect.y;
5120 
5121   size_dx = w - window->rect.width;
5122   size_dy = h - window->rect.height;
5123 
5124   if (size_dx != 0 || size_dy != 0)
5125     need_resize_client = TRUE;
5126 
5127   window->rect.width = w;
5128   window->rect.height = h;
5129 
5130   if (window->frame)
5131     {
5132       int new_w, new_h;
5133 
5134       new_w = window->rect.width + borders.total.left + borders.total.right;
5135 
5136       if (window->shaded)
5137         new_h = borders.total.top;
5138       else
5139         new_h = window->rect.height + borders.total.top + borders.total.bottom;
5140 
5141       if (new_w != window->frame->rect.width ||
5142           new_h != window->frame->rect.height)
5143         {
5144           need_resize_frame = TRUE;
5145           window->frame->rect.width = new_w;
5146           window->frame->rect.height = new_h;
5147         }
5148 
5149       meta_topic (META_DEBUG_GEOMETRY,
5150                   "Calculated frame size %dx%d\n",
5151                   window->frame->rect.width,
5152                   window->frame->rect.height);
5153     }
5154 
5155 
5156   /* For nice effect, when growing the window we want to move/resize
5157    * the frame first, when shrinking the window we want to move/resize
5158    * the client first. If we grow one way and shrink the other,
5159    * see which way we're moving "more"
5160    *
5161    * Mail from Owen subject "Suggestion: Gravity and resizing from the left"
5162    * http://mail.gnome.org/archives/wm-spec-list/1999-November/msg00088.html
5163    *
5164    * An annoying fact you need to know in this code is that StaticGravity
5165    * does nothing if you _only_ resize or _only_ move the frame;
5166    * it must move _and_ resize, otherwise you get NorthWestGravity
5167    * behavior. The move and resize must actually occur, it is not
5168    * enough to set CWX | CWWidth but pass in the current size/pos.
5169    */
5170 
5171   if (window->frame)
5172     {
5173       int new_x, new_y;
5174       int frame_pos_dx, frame_pos_dy;
5175 
5176       /* Compute new frame coords */
5177       new_x = root_x_nw - borders.total.left;
5178       new_y = root_y_nw - borders.total.top;
5179 
5180       frame_pos_dx = new_x - window->frame->rect.x;
5181       frame_pos_dy = new_y - window->frame->rect.y;
5182 
5183       need_move_frame = (frame_pos_dx != 0 || frame_pos_dy != 0);
5184 
5185       window->frame->rect.x = new_x;
5186       window->frame->rect.y = new_y;
5187 
5188       /* If frame will both move and resize, then StaticGravity
5189        * on the child window will kick in and implicitly move
5190        * the child with respect to the frame. The implicit
5191        * move will keep the child in the same place with
5192        * respect to the root window. If frame only moves
5193        * or only resizes, then the child will just move along
5194        * with the frame.
5195        */
5196 
5197       /* window->rect.x, window->rect.y are relative to frame,
5198        * remember they are the server coords
5199        */
5200 
5201       new_x = borders.total.left;
5202       new_y = borders.total.top;
5203 
5204       if (need_resize_frame && need_move_frame &&
5205           static_gravity_works (window->display))
5206         {
5207           /* static gravity kicks in because frame
5208            * is both moved and resized
5209            */
5210           /* when we move the frame by frame_pos_dx, frame_pos_dy the
5211            * client will implicitly move relative to frame by the
5212            * inverse delta.
5213            *
5214            * When moving client then frame, we move the client by the
5215            * frame delta, to be canceled out by the implicit move by
5216            * the inverse frame delta, resulting in a client at new_x,
5217            * new_y.
5218            *
5219            * When moving frame then client, we move the client
5220            * by the same delta as the frame, because the client
5221            * was "left behind" by the frame - resulting in a client
5222            * at new_x, new_y.
5223            *
5224            * In both cases we need to move the client window
5225            * in all cases where we had to move the frame window.
5226            */
5227 
5228           client_move_x = new_x + frame_pos_dx;
5229           client_move_y = new_y + frame_pos_dy;
5230 
5231           if (need_move_frame)
5232             need_move_client = TRUE;
5233 
5234           use_static_gravity = TRUE;
5235         }
5236       else
5237         {
5238           client_move_x = new_x;
5239           client_move_y = new_y;
5240 
5241           if (client_move_x != window->rect.x ||
5242               client_move_y != window->rect.y)
5243             need_move_client = TRUE;
5244 
5245           use_static_gravity = FALSE;
5246         }
5247 
5248       /* This is the final target position, but not necessarily what
5249        * we pass to XConfigureWindow, due to StaticGravity implicit
5250        * movement.
5251        */
5252       window->rect.x = new_x;
5253       window->rect.y = new_y;
5254     }
5255   else
5256     {
5257       if (root_x_nw != window->rect.x ||
5258           root_y_nw != window->rect.y)
5259         need_move_client = TRUE;
5260 
5261       window->rect.x = root_x_nw;
5262       window->rect.y = root_y_nw;
5263 
5264       client_move_x = window->rect.x;
5265       client_move_y = window->rect.y;
5266 
5267       use_static_gravity = FALSE;
5268     }
5269 
5270   /* If frame extents have changed, fill in other frame fields and
5271      change frame's extents property. */
5272   if (window->frame &&
5273       (window->frame->child_x != borders.total.left ||
5274        window->frame->child_y != borders.total.top ||
5275        window->frame->right_width != borders.total.right ||
5276        window->frame->bottom_height != borders.total.bottom))
5277     {
5278       window->frame->child_x = borders.total.left;
5279       window->frame->child_y = borders.total.top;
5280       window->frame->right_width = borders.total.right;
5281       window->frame->bottom_height = borders.total.bottom;
5282 
5283       update_net_frame_extents (window);
5284     }
5285 
5286   /* See ICCCM 4.1.5 for when to send ConfigureNotify */
5287 
5288   need_configure_notify = FALSE;
5289 
5290   /* If this is a configure request and we change nothing, then we
5291    * must send configure notify.
5292    */
5293   if  (is_configure_request &&
5294        !(need_move_client || need_move_frame ||
5295          need_resize_client || need_resize_frame ||
5296          window->border_width != 0))
5297     need_configure_notify = TRUE;
5298 
5299   /* We must send configure notify if we move but don't resize, since
5300    * the client window may not get a real event
5301    */
5302   if ((need_move_client || need_move_frame) &&
5303       !(need_resize_client || need_resize_frame) &&
5304       window->type != META_WINDOW_TOOLTIP)
5305     need_configure_notify = TRUE;
5306 
5307   /* MapRequest events with a PPosition or UPosition hint with a frame
5308    * are moved by muffin without resizing; send a configure notify
5309    * in such cases.  See #322840.  (Note that window->constructing is
5310    * only true iff this call is due to a MapRequest, and when
5311    * PPosition/UPosition hints aren't set, muffin seems to send a
5312    * ConfigureNotify anyway due to the above code.)
5313    */
5314   if (window->constructing && window->frame &&
5315       ((window->size_hints.flags & PPosition) ||
5316        (window->size_hints.flags & USPosition)))
5317     need_configure_notify = TRUE;
5318 
5319   /* The rest of this function syncs our new size/pos with X as
5320    * efficiently as possible
5321    */
5322 
5323   /* Normally, we configure the frame first depending on whether
5324    * we grow the frame more than we shrink. The idea is to avoid
5325    * messing up the window contents by having a temporary situation
5326    * where the frame is smaller than the window. However, if we're
5327    * cooperating with the client to create an atomic frame upate,
5328    * and the window is redirected, then we should always update
5329    * the frame first, since updating the frame will force a new
5330    * backing pixmap to be allocated, and the old backing pixmap
5331    * will be left undisturbed for us to paint to the screen until
5332    * the client finishes redrawing.
5333    */
5334   if (window->extended_sync_request_counter)
5335     {
5336       configure_frame_first = TRUE;
5337     }
5338   else
5339     {
5340       size_dx = w - window->rect.width;
5341       size_dy = h - window->rect.height;
5342 
5343       configure_frame_first = size_dx + size_dy >= 0;
5344     }
5345 
5346   if (use_static_gravity)
5347     meta_window_set_gravity (window, StaticGravity);
5348 
5349   if (configure_frame_first && window->frame)
5350     frame_shape_changed = meta_frame_sync_to_window (window->frame,
5351                                                      gravity,
5352                                                      need_move_frame, need_resize_frame);
5353 
5354   values.border_width = 0;
5355   values.x = client_move_x;
5356   values.y = client_move_y;
5357   values.width = window->rect.width;
5358   values.height = window->rect.height;
5359 
5360   mask = 0;
5361   if (is_configure_request && window->border_width != 0)
5362     mask |= CWBorderWidth; /* must force to 0 */
5363   if (need_move_client)
5364     mask |= (CWX | CWY);
5365   if (need_resize_client)
5366     mask |= (CWWidth | CWHeight);
5367 
5368   if (mask != 0)
5369     {
5370       meta_error_trap_push (window->display);
5371 
5372 #ifdef HAVE_XSYNC
5373       if (window == window->display->grab_window &&
5374           meta_grab_op_is_resizing (window->display->grab_op) &&
5375           !window->disable_sync &&
5376           window->sync_request_counter != None &&
5377 	  window->sync_request_alarm != None &&
5378           window->sync_request_timeout_id == 0)
5379 	{
5380 	  send_sync_request (window);
5381 	}
5382 #endif
5383 
5384       XConfigureWindow (window->display->xdisplay,
5385                         window->xwindow,
5386                         mask,
5387                         &values);
5388 
5389       meta_error_trap_pop (window->display);
5390     }
5391 
5392   if (!configure_frame_first && window->frame)
5393     frame_shape_changed = meta_frame_sync_to_window (window->frame,
5394                                                      gravity,
5395                                                      need_move_frame, need_resize_frame);
5396 
5397   /* Put gravity back to be nice to lesser window managers */
5398   if (use_static_gravity)
5399     meta_window_set_gravity (window, NorthWestGravity);
5400 
5401   if (need_configure_notify)
5402     send_configure_notify (window);
5403 
5404   if (!window->placed && window->force_save_user_rect && !window->fullscreen)
5405     force_save_user_window_placement (window);
5406   else if (is_user_action)
5407     save_user_window_placement (window);
5408 
5409   if (need_move_frame || need_move_client)
5410     g_signal_emit (window, window_signals[POSITION_CHANGED], 0);
5411 
5412   if (need_resize_client)
5413     g_signal_emit (window, window_signals[SIZE_CHANGED], 0);
5414 
5415   if (need_move_frame || need_resize_frame ||
5416       need_move_client || need_resize_client ||
5417       did_placement)
5418     {
5419       int newx, newy;
5420       meta_window_get_position (window, &newx, &newy);
5421       meta_topic (META_DEBUG_GEOMETRY,
5422                   "New size/position %d,%d %dx%d (user %d,%d %dx%d)\n",
5423                   newx, newy, window->rect.width, window->rect.height,
5424                   window->user_rect.x, window->user_rect.y,
5425                   window->user_rect.width, window->user_rect.height);
5426       meta_compositor_sync_window_geometry (window->display->compositor,
5427                                             window,
5428                                             did_placement);
5429     }
5430   else
5431     {
5432       meta_topic (META_DEBUG_GEOMETRY, "Size/position not modified\n");
5433     }
5434 
5435   meta_window_refresh_resize_popup (window);
5436 
5437   meta_window_update_monitor (window);
5438 
5439   /* Invariants leaving this function are:
5440    *   a) window->rect and frame->rect reflect the actual
5441    *      server-side size/pos of window->xwindow and frame->xwindow
5442    *   b) all constraints are obeyed by window->rect and frame->rect
5443    */
5444 
5445   if (frame_shape_changed && window->frame_bounds)
5446     {
5447       cairo_region_destroy (window->frame_bounds);
5448       window->frame_bounds = NULL;
5449     }
5450 
5451   meta_window_foreach_transient (window, maybe_move_attached_dialog, NULL);
5452 
5453   meta_stack_update_window_tile_matches (window->screen->stack,
5454                                          window->screen->active_workspace);
5455 }
5456 
5457 /**
5458  * meta_window_resize:
5459  * @window: a #MetaWindow
5460  * @user_op: bool to indicate whether or not this is a user operation
5461  * @w: desired width
5462  * @h: desired height
5463  *
5464  * Resize the window to the desired size.
5465  */
5466 void
meta_window_resize(MetaWindow * window,gboolean user_op,int w,int h)5467 meta_window_resize (MetaWindow  *window,
5468                     gboolean     user_op,
5469                     int          w,
5470                     int          h)
5471 {
5472   int x, y;
5473   MetaMoveResizeFlags flags;
5474 
5475   g_return_if_fail (!window->override_redirect);
5476 
5477   meta_window_get_position (window, &x, &y);
5478 
5479   flags = (user_op ? META_IS_USER_ACTION : 0) | META_IS_RESIZE_ACTION;
5480   meta_window_move_resize_internal (window,
5481                                     flags,
5482                                     NorthWestGravity,
5483                                     x, y, w, h);
5484 }
5485 
5486 /**
5487  * meta_window_move:
5488  * @window: a #MetaWindow
5489  * @user_op: bool to indicate whether or not this is a user operation
5490  * @root_x_nw: desired x pos
5491  * @root_y_nw: desired y pos
5492  *
5493  * Moves the window to the desired location on window's assigned workspace.
5494  * NOTE: does NOT place according to the origin of the enclosing
5495  * frame/window-decoration, but according to the origin of the window,
5496  * itself.
5497  */
5498 void
meta_window_move(MetaWindow * window,gboolean user_op,int root_x_nw,int root_y_nw)5499 meta_window_move (MetaWindow  *window,
5500                   gboolean     user_op,
5501                   int          root_x_nw,
5502                   int          root_y_nw)
5503 {
5504   MetaMoveResizeFlags flags;
5505 
5506   if (window->type != META_WINDOW_TOOLTIP)
5507     {
5508       g_return_if_fail (!window->override_redirect);
5509     }
5510 
5511   flags = (user_op ? META_IS_USER_ACTION : 0) | META_IS_MOVE_ACTION;
5512 
5513   meta_window_move_resize_internal (window,
5514                                     flags,
5515                                     NorthWestGravity,
5516                                     root_x_nw, root_y_nw,
5517                                     window->rect.width,
5518                                     window->rect.height);
5519 }
5520 /**
5521  * meta_window_move_frame:
5522  * @window: a #MetaWindow
5523  * @user_op: bool to indicate whether or not this is a user operation
5524  * @root_x_nw: desired x pos
5525  * @root_y_nw: desired y pos
5526  *
5527  * Moves the window to the desired location on window's assigned
5528  * workspace, using the northwest edge of the frame as the reference,
5529  * instead of the actual window's origin, but only if a frame is present.
5530  * Otherwise, acts identically to meta_window_move().
5531  */
5532 void
meta_window_move_frame(MetaWindow * window,gboolean user_op,int root_x_nw,int root_y_nw)5533 meta_window_move_frame (MetaWindow  *window,
5534                   gboolean     user_op,
5535                   int          root_x_nw,
5536                   int          root_y_nw)
5537 {
5538   int x = root_x_nw;
5539   int y = root_y_nw;
5540 
5541   if (window->frame)
5542     {
5543       MetaFrameBorders borders;
5544 
5545       meta_frame_calc_borders (window->frame, &borders);
5546 
5547       /* root_x_nw and root_y_nw correspond to where the top of
5548        * the visible frame should be. Offset by the distance between
5549        * the origin of the window and the origin of the enclosing
5550        * window decorations.
5551        */
5552       x += window->frame->child_x - borders.invisible.left;
5553       y += window->frame->child_y - borders.invisible.top;
5554     }
5555 
5556   meta_window_move (window, user_op, x, y);
5557 }
5558 
5559 static void
meta_window_move_between_rects(MetaWindow * window,const MetaRectangle * old_area,const MetaRectangle * new_area)5560 meta_window_move_between_rects (MetaWindow  *window,
5561                                 const MetaRectangle *old_area,
5562                                 const MetaRectangle *new_area)
5563 {
5564   int rel_x, rel_y;
5565   double scale_x, scale_y;
5566 
5567   rel_x = window->user_rect.x - old_area->x;
5568   rel_y = window->user_rect.y - old_area->y;
5569   scale_x = (double)new_area->width / old_area->width;
5570   scale_y = (double)new_area->height / old_area->height;
5571 
5572   window->user_rect.x = new_area->x + rel_x * scale_x;
5573   window->user_rect.y = new_area->y + rel_y * scale_y;
5574   window->saved_rect.x = window->user_rect.x;
5575   window->saved_rect.y = window->user_rect.y;
5576 
5577   meta_window_move_resize (window, FALSE,
5578                            window->user_rect.x,
5579                            window->user_rect.y,
5580                            window->user_rect.width,
5581                            window->user_rect.height);
5582 }
5583 
5584 /**
5585  * meta_window_move_resize_frame:
5586  * @window: a #MetaWindow
5587  * @user_op: bool to indicate whether or not this is a user operation
5588  * @root_x_nw: new x
5589  * @root_y_nw: new y
5590  * @w: desired width
5591  * @h: desired height
5592  *
5593  * Resizes the window so that its outer bounds (including frame)
5594  * fit within the given rect
5595  */
5596 void
meta_window_move_resize_frame(MetaWindow * window,gboolean user_op,int root_x_nw,int root_y_nw,int w,int h)5597 meta_window_move_resize_frame (MetaWindow  *window,
5598                                gboolean     user_op,
5599                                int          root_x_nw,
5600                                int          root_y_nw,
5601                                int          w,
5602                                int          h)
5603 {
5604   MetaFrameBorders borders;
5605 
5606   meta_frame_calc_borders (window->frame, &borders);
5607   /* offset by the distance between the origin of the window
5608    * and the origin of the enclosing window decorations ( + border)
5609    */
5610   root_x_nw += borders.visible.left;
5611   root_y_nw += borders.visible.top;
5612   w -= borders.visible.left + borders.visible.right;
5613   h -= borders.visible.top + borders.visible.bottom;
5614 
5615   meta_window_move_resize (window, user_op, root_x_nw, root_y_nw, w, h);
5616 }
5617 
5618 /**
5619  * meta_window_move_to_monitor:
5620  * @window: a #MetaWindow
5621  * @monitor: desired monitor index
5622  *
5623  * Moves the window to the monitor with index @monitor, keeping
5624  * the relative position of the window's top left corner.
5625  */
5626 void
meta_window_move_to_monitor(MetaWindow * window,int monitor)5627 meta_window_move_to_monitor (MetaWindow  *window,
5628                              int          monitor)
5629 {
5630   MetaRectangle old_area, new_area;
5631 
5632   if (monitor == window->monitor->number)
5633     return;
5634 
5635   meta_window_get_work_area_for_monitor (window,
5636                                          window->monitor->number,
5637                                          &old_area);
5638   meta_window_get_work_area_for_monitor (window,
5639                                          monitor,
5640                                          &new_area);
5641 
5642   if (window->tile_mode != META_TILE_NONE)
5643     window->tile_monitor_number = monitor;
5644 
5645   meta_window_move_between_rects (window, &old_area, &new_area);
5646 }
5647 
5648 void
meta_window_move_resize(MetaWindow * window,gboolean user_op,int root_x_nw,int root_y_nw,int w,int h)5649 meta_window_move_resize (MetaWindow  *window,
5650                          gboolean     user_op,
5651                          int          root_x_nw,
5652                          int          root_y_nw,
5653                          int          w,
5654                          int          h)
5655 {
5656   MetaMoveResizeFlags flags;
5657 
5658   g_return_if_fail (!window->override_redirect);
5659 
5660   flags = (user_op ? META_IS_USER_ACTION : 0) |
5661     META_IS_MOVE_ACTION | META_IS_RESIZE_ACTION;
5662   meta_window_move_resize_internal (window,
5663                                     flags,
5664                                     NorthWestGravity,
5665                                     root_x_nw, root_y_nw,
5666                                     w, h);
5667 }
5668 
5669 LOCAL_SYMBOL void
meta_window_resize_with_gravity(MetaWindow * window,gboolean user_op,int w,int h,int gravity)5670 meta_window_resize_with_gravity (MetaWindow *window,
5671                                  gboolean     user_op,
5672                                  int          w,
5673                                  int          h,
5674                                  int          gravity)
5675 {
5676   int x, y;
5677   MetaMoveResizeFlags flags;
5678 
5679   meta_window_get_position (window, &x, &y);
5680 
5681   flags = (user_op ? META_IS_USER_ACTION : 0) | META_IS_RESIZE_ACTION;
5682   meta_window_move_resize_internal (window,
5683                                     flags,
5684                                     gravity,
5685                                     x, y, w, h);
5686 }
5687 
5688 static void
meta_window_move_resize_now(MetaWindow * window)5689 meta_window_move_resize_now (MetaWindow  *window)
5690 {
5691   /* If constraints have changed then we want to snap back to wherever
5692    * the user had the window.  We use user_rect for this reason.  See
5693    * also bug 426519 comment 3.
5694    */
5695   meta_window_move_resize (window, FALSE,
5696                            window->user_rect.x,
5697                            window->user_rect.y,
5698                            window->user_rect.width,
5699                            window->user_rect.height);
5700 }
5701 
5702 static gboolean
idle_move_resize(gpointer data)5703 idle_move_resize (gpointer data)
5704 {
5705   GSList *tmp;
5706   GSList *copy;
5707   guint queue_index = GPOINTER_TO_INT (data);
5708 
5709   meta_topic (META_DEBUG_GEOMETRY, "Clearing the move_resize queue\n");
5710 
5711   /* Work with a copy, for reentrancy. The allowed reentrancy isn't
5712    * complete; destroying a window while we're in here would result in
5713    * badness. But it's OK to queue/unqueue move_resizes.
5714    */
5715   copy = g_slist_copy (queue_pending[queue_index]);
5716   g_slist_free (queue_pending[queue_index]);
5717   queue_pending[queue_index] = NULL;
5718   queue_later[queue_index] = 0;
5719 
5720   destroying_windows_disallowed += 1;
5721 
5722   tmp = copy;
5723   while (tmp != NULL)
5724     {
5725       MetaWindow *window;
5726 
5727       window = tmp->data;
5728 
5729       /* As a side effect, sets window->move_resize_queued = FALSE */
5730       meta_window_move_resize_now (window);
5731 
5732       tmp = tmp->next;
5733     }
5734 
5735   g_slist_free (copy);
5736 
5737   destroying_windows_disallowed -= 1;
5738 
5739   return FALSE;
5740 }
5741 
5742 /**
5743  * meta_window_configure_notify: (skip)
5744  * @window: a #MetaWindow
5745  * @event: a #XConfigureEvent
5746  *
5747  * This is used to notify us of an unrequested configuration
5748  * (only applicable to override redirect windows)
5749  */
5750 void
meta_window_configure_notify(MetaWindow * window,XConfigureEvent * event)5751 meta_window_configure_notify (MetaWindow      *window,
5752                               XConfigureEvent *event)
5753 {
5754   g_assert (window->override_redirect);
5755   g_assert (window->frame == NULL);
5756 
5757   window->rect.x = event->x;
5758   window->rect.y = event->y;
5759   window->rect.width = event->width;
5760   window->rect.height = event->height;
5761   meta_window_update_monitor (window);
5762 
5763   /* Whether an override-redirect window is considered fullscreen depends
5764    * on its geometry.
5765    */
5766   if (window->override_redirect)
5767     meta_screen_queue_check_fullscreen (window->screen);
5768 
5769   if (!event->override_redirect && !event->send_event)
5770     meta_warning ("Unhandled change of windows override redirect status\n");
5771 
5772   meta_compositor_sync_window_geometry (window->display->compositor, window, FALSE);
5773 }
5774 
5775 LOCAL_SYMBOL void
meta_window_get_position(MetaWindow * window,int * x,int * y)5776 meta_window_get_position (MetaWindow  *window,
5777                           int         *x,
5778                           int         *y)
5779 {
5780   if (window->frame)
5781     {
5782       if (x)
5783         *x = window->frame->rect.x + window->frame->child_x;
5784       if (y)
5785         *y = window->frame->rect.y + window->frame->child_y;
5786     }
5787   else
5788     {
5789       if (x)
5790         *x = window->rect.x;
5791       if (y)
5792         *y = window->rect.y;
5793     }
5794 }
5795 
5796 LOCAL_SYMBOL void
meta_window_get_client_root_coords(MetaWindow * window,MetaRectangle * rect)5797 meta_window_get_client_root_coords (MetaWindow    *window,
5798                                     MetaRectangle *rect)
5799 {
5800   meta_window_get_position (window, &rect->x, &rect->y);
5801   rect->width  = window->rect.width;
5802   rect->height = window->rect.height;
5803 }
5804 
5805 LOCAL_SYMBOL void
meta_window_get_gravity_position(MetaWindow * window,int gravity,int * root_x,int * root_y)5806 meta_window_get_gravity_position (MetaWindow  *window,
5807                                   int          gravity,
5808                                   int         *root_x,
5809                                   int         *root_y)
5810 {
5811   MetaRectangle frame_extents;
5812   int w, h;
5813   int x, y;
5814 
5815   w = window->rect.width;
5816   h = window->rect.height;
5817 
5818   if (gravity == StaticGravity)
5819     {
5820       frame_extents = window->rect;
5821       if (window->frame)
5822         {
5823           frame_extents.x = window->frame->rect.x + window->frame->child_x;
5824           frame_extents.y = window->frame->rect.y + window->frame->child_y;
5825         }
5826     }
5827   else
5828     {
5829       if (window->frame == NULL)
5830         frame_extents = window->rect;
5831       else
5832         frame_extents = window->frame->rect;
5833     }
5834 
5835   x = frame_extents.x;
5836   y = frame_extents.y;
5837 
5838   switch (gravity)
5839     {
5840     case NorthGravity:
5841     case CenterGravity:
5842     case SouthGravity:
5843       /* Find center of frame. */
5844       x += frame_extents.width / 2;
5845       /* Center client window on that point. */
5846       x -= w / 2;
5847       break;
5848 
5849     case SouthEastGravity:
5850     case EastGravity:
5851     case NorthEastGravity:
5852       /* Find right edge of frame */
5853       x += frame_extents.width;
5854       /* Align left edge of client at that point. */
5855       x -= w;
5856       break;
5857     default:
5858       break;
5859     }
5860 
5861   switch (gravity)
5862     {
5863     case WestGravity:
5864     case CenterGravity:
5865     case EastGravity:
5866       /* Find center of frame. */
5867       y += frame_extents.height / 2;
5868       /* Center client window there. */
5869       y -= h / 2;
5870       break;
5871     case SouthWestGravity:
5872     case SouthGravity:
5873     case SouthEastGravity:
5874       /* Find south edge of frame */
5875       y += frame_extents.height;
5876       /* Place bottom edge of client there */
5877       y -= h;
5878       break;
5879     default:
5880       break;
5881     }
5882 
5883   if (root_x)
5884     *root_x = x;
5885   if (root_y)
5886     *root_y = y;
5887 }
5888 
5889 LOCAL_SYMBOL void
meta_window_get_geometry(MetaWindow * window,int * x,int * y,int * width,int * height)5890 meta_window_get_geometry (MetaWindow  *window,
5891                           int         *x,
5892                           int         *y,
5893                           int         *width,
5894                           int         *height)
5895 {
5896   meta_window_get_gravity_position (window,
5897                                     window->size_hints.win_gravity,
5898                                     x, y);
5899 
5900   *width = (window->rect.width - window->size_hints.base_width) /
5901     window->size_hints.width_inc;
5902   *height = (window->rect.height - window->size_hints.base_height) /
5903     window->size_hints.height_inc;
5904 }
5905 
5906 /**
5907  * meta_window_get_input_rect:
5908  * @window: a #MetaWindow
5909  * @rect: (out): pointer to an allocated #MetaRectangle
5910  *
5911  * Gets the rectangle that bounds @window that is responsive to mouse events.
5912  * This includes decorations - the visible portion of its border - and (if
5913  * present) any invisible area that we make make responsive to mouse clicks in
5914  * order to allow convenient border dragging.
5915  */
5916 void
meta_window_get_input_rect(const MetaWindow * window,MetaRectangle * rect)5917 meta_window_get_input_rect (const MetaWindow *window,
5918                             MetaRectangle    *rect)
5919 {
5920   if (window->frame)
5921     *rect = window->frame->rect;
5922   else
5923     *rect = window->rect;
5924 }
5925 
5926 /**
5927  * meta_window_get_outer_rect:
5928  * @window: a #MetaWindow
5929  * @rect: (out): pointer to an allocated #MetaRectangle
5930  *
5931  * Gets the rectangle that bounds @window that is responsive to mouse events.
5932  * This includes only what is visible; it doesn't include any extra reactive
5933  * area we add to the edges of windows.
5934  */
5935 void
meta_window_get_outer_rect(const MetaWindow * window,MetaRectangle * rect)5936 meta_window_get_outer_rect (const MetaWindow *window,
5937                             MetaRectangle    *rect)
5938 {
5939   if (window->frame)
5940     {
5941       MetaFrameBorders borders;
5942       *rect = window->frame->rect;
5943       meta_frame_calc_borders (window->frame, &borders);
5944 
5945       rect->x += borders.invisible.left;
5946       rect->y += borders.invisible.top;
5947       rect->width  -= borders.invisible.left + borders.invisible.right;
5948       rect->height -= borders.invisible.top  + borders.invisible.bottom;
5949     }
5950   else
5951     {
5952       *rect = window->rect;
5953 
5954       if (window->has_custom_frame_extents)
5955         {
5956           const GtkBorder *extents = &window->custom_frame_extents;
5957           rect->x += extents->left;
5958           rect->y += extents->top;
5959           rect->width -= extents->left + extents->right;
5960           rect->height -= extents->top + extents->bottom;
5961         }
5962     }
5963 }
5964 
5965 /**
5966  * meta_window_get_client_area_rect:
5967  * @window: a #MetaWindow
5968  * @rect: (out): pointer to a cairo rectangle
5969  *
5970  * Gets the rectangle for the boundaries of the client area, relative
5971  * to the frame. If the window is shaded, the height of the rectangle
5972  * is 0.
5973  */
5974 void
meta_window_get_client_area_rect(const MetaWindow * window,cairo_rectangle_int_t * rect)5975 meta_window_get_client_area_rect (const MetaWindow      *window,
5976                                   cairo_rectangle_int_t *rect)
5977 {
5978   if (window->frame)
5979     {
5980       rect->x = window->frame->child_x;
5981       rect->y = window->frame->child_y;
5982     }
5983   else
5984     {
5985       rect->x = 0;
5986       rect->y = 0;
5987     }
5988 
5989   rect->width = window->rect.width;
5990   if (window->shaded)
5991     rect->height = 0;
5992   else
5993     rect->height = window->rect.height;
5994 }
5995 
5996 MetaSide
meta_window_get_tile_side(MetaWindow * window)5997 meta_window_get_tile_side (MetaWindow *window)
5998 {
5999     MetaSide side;
6000     switch (window->tile_mode) {
6001         case META_TILE_LEFT:
6002             side = META_SIDE_LEFT;
6003             break;
6004         case META_TILE_ULC:
6005             side = (META_SIDE_LEFT | META_SIDE_TOP);
6006             break;
6007         case META_TILE_LLC:
6008             side = (META_SIDE_LEFT | META_SIDE_BOTTOM);
6009             break;
6010         case META_TILE_RIGHT:
6011             side = META_SIDE_RIGHT;
6012             break;
6013         case META_TILE_URC:
6014             side = (META_SIDE_RIGHT | META_SIDE_TOP);
6015             break;
6016         case META_TILE_LRC:
6017             side = (META_SIDE_RIGHT | META_SIDE_BOTTOM);
6018             break;
6019         case META_TILE_TOP:
6020             side = META_SIDE_TOP;
6021             break;
6022         case META_TILE_BOTTOM:
6023             side = META_SIDE_BOTTOM;
6024             break;
6025         default:
6026             side = META_SIDE_TOP;
6027             break;
6028     }
6029     return side;
6030 }
6031 
6032 void
meta_window_get_titlebar_rect(MetaWindow * window,MetaRectangle * rect)6033 meta_window_get_titlebar_rect (MetaWindow *window,
6034                                MetaRectangle *rect)
6035 {
6036   meta_window_get_outer_rect (window, rect);
6037 
6038   /* The returned rectangle is relative to the frame rect. */
6039   rect->x = 0;
6040   rect->y = 0;
6041 
6042   if (window->frame)
6043     {
6044       rect->height = window->frame->child_y;
6045     }
6046   else
6047     {
6048       /* Pick an arbitrary height for a titlebar. We might want to
6049        * eventually have CSD windows expose their borders to us. */
6050       rect->height = CSD_TITLEBAR_HEIGHT * meta_prefs_get_ui_scale ();
6051     }
6052 }
6053 
6054 const char*
meta_window_get_startup_id(MetaWindow * window)6055 meta_window_get_startup_id (MetaWindow *window)
6056 {
6057   if (window->startup_id == NULL)
6058     {
6059       MetaGroup *group;
6060 
6061       group = meta_window_get_group (window);
6062 
6063       if (group != NULL)
6064         return meta_group_get_startup_id (group);
6065     }
6066 
6067   return window->startup_id;
6068 }
6069 
6070 static MetaWindow*
get_modal_transient(MetaWindow * window)6071 get_modal_transient (MetaWindow *window)
6072 {
6073   GSList *windows;
6074   GSList *tmp;
6075   MetaWindow *modal_transient;
6076 
6077   /* A window can't be the transient of itself, but this is just for
6078    * convenience in the loop below; we manually fix things up at the
6079    * end if no real modal transient was found.
6080    */
6081   modal_transient = window;
6082 
6083   windows = meta_display_list_windows (window->display, META_LIST_DEFAULT);
6084   tmp = windows;
6085   while (tmp != NULL)
6086     {
6087       MetaWindow *transient = tmp->data;
6088 
6089       if (transient->xtransient_for == modal_transient->xwindow &&
6090           transient->wm_state_modal)
6091         {
6092           modal_transient = transient;
6093           tmp = windows;
6094           continue;
6095         }
6096 
6097       tmp = tmp->next;
6098     }
6099 
6100   g_slist_free (windows);
6101 
6102   if (window == modal_transient)
6103     modal_transient = NULL;
6104 
6105   return modal_transient;
6106 }
6107 
6108 /* XXX META_EFFECT_FOCUS */
6109 LOCAL_SYMBOL void
meta_window_focus(MetaWindow * window,guint32 timestamp)6110 meta_window_focus (MetaWindow  *window,
6111                    guint32      timestamp)
6112 {
6113   MetaWindow *modal_transient;
6114 
6115   g_return_if_fail (!window->override_redirect);
6116 
6117   meta_topic (META_DEBUG_FOCUS,
6118               "Setting input focus to window %s, input: %d take_focus: %d\n",
6119               window->desc, window->input, window->take_focus);
6120 
6121   if (window->display->grab_window &&
6122       window->display->grab_window->all_keys_grabbed)
6123     {
6124       meta_topic (META_DEBUG_FOCUS,
6125                   "Current focus window %s has global keygrab, not focusing window %s after all\n",
6126                   window->display->grab_window->desc, window->desc);
6127       return;
6128     }
6129 
6130   modal_transient = get_modal_transient (window);
6131   if (modal_transient != NULL &&
6132       !modal_transient->unmanaging)
6133     {
6134       meta_topic (META_DEBUG_FOCUS,
6135                   "%s has %s as a modal transient, so focusing it instead.\n",
6136                   window->desc, modal_transient->desc);
6137       if (!modal_transient->on_all_workspaces &&
6138           modal_transient->workspace != window->screen->active_workspace)
6139         meta_window_change_workspace (modal_transient,
6140                                       window->screen->active_workspace);
6141       window = modal_transient;
6142     }
6143 
6144   meta_window_flush_calc_showing (window);
6145 
6146   if ((!window->mapped || window->hidden) && !window->shaded)
6147     {
6148       meta_topic (META_DEBUG_FOCUS,
6149                   "Window %s is not showing, not focusing after all\n",
6150                   window->desc);
6151       return;
6152     }
6153 
6154   /* For output-only or shaded windows, focus the frame.
6155    * This seems to result in the client window getting key events
6156    * though, so I don't know if it's icccm-compliant.
6157    *
6158    * Still, we have to do this or keynav breaks for these windows.
6159    */
6160   if (window->frame &&
6161       (window->shaded ||
6162        !(window->input || window->take_focus)))
6163     {
6164       meta_topic (META_DEBUG_FOCUS,
6165                   "Focusing frame of %s\n", window->desc);
6166       meta_display_set_input_focus_window (window->display,
6167                                             window,
6168                                             TRUE,
6169                                             timestamp);
6170     }
6171   else
6172     {
6173       if (window->input)
6174         {
6175           meta_topic (META_DEBUG_FOCUS,
6176                       "Setting input focus on %s since input = true\n",
6177                       window->desc);
6178           meta_display_set_input_focus_window (window->display,
6179                                                window,
6180                                                FALSE,
6181                                                timestamp);
6182         }
6183 
6184       if (window->take_focus)
6185         {
6186           meta_topic (META_DEBUG_FOCUS,
6187                       "Sending WM_TAKE_FOCUS to %s since take_focus = true\n",
6188                       window->desc);
6189           meta_window_send_icccm_message (window,
6190                                           window->display->atom_WM_TAKE_FOCUS,
6191                                           timestamp);
6192           window->display->expected_focus_window = window;
6193         }
6194     }
6195 
6196   if (window->wm_state_demands_attention)
6197     meta_window_unset_demands_attention(window);
6198 }
6199 
6200 static void
meta_window_change_workspace_without_transients(MetaWindow * window,MetaWorkspace * workspace)6201 meta_window_change_workspace_without_transients (MetaWindow    *window,
6202                                                  MetaWorkspace *workspace)
6203 {
6204   int old_workspace = -1;
6205 
6206   meta_verbose ("Changing window %s to workspace %d\n",
6207                 window->desc, meta_workspace_index (workspace));
6208 
6209   if (!window->on_all_workspaces_requested)
6210     {
6211       old_workspace = meta_workspace_index (window->workspace);
6212     }
6213 
6214   /* unstick if stuck. meta_window_unstick would call
6215    * meta_window_change_workspace recursively if the window
6216    * is not in the active workspace.
6217    */
6218   if (window->on_all_workspaces_requested)
6219     meta_window_unstick (window);
6220 
6221   /* See if we're already on this space. If not, make sure we are */
6222   if (window->workspace != workspace)
6223     {
6224       meta_workspace_remove_window (window->workspace, window);
6225       meta_workspace_add_window (workspace, window);
6226       g_signal_emit (window, window_signals[WORKSPACE_CHANGED], 0,
6227                      old_workspace);
6228       g_signal_emit_by_name (window->screen, "window-workspace-changed", window, window->workspace);
6229     }
6230 }
6231 
6232 static gboolean
change_workspace_foreach(MetaWindow * window,void * data)6233 change_workspace_foreach (MetaWindow *window,
6234                           void       *data)
6235 {
6236   meta_window_change_workspace_without_transients (window, data);
6237   return TRUE;
6238 }
6239 
6240 /**
6241  * meta_window_change_workspace:
6242  * @window: a #MetaWindow
6243  * @workspace: the #MetaWorkspace where to put the window
6244  *
6245  * Moves the window to the specified workspace.
6246  */
6247 void
meta_window_change_workspace(MetaWindow * window,MetaWorkspace * workspace)6248 meta_window_change_workspace (MetaWindow    *window,
6249                               MetaWorkspace *workspace)
6250 {
6251   g_return_if_fail (!window->override_redirect);
6252 
6253   meta_window_change_workspace_without_transients (window, workspace);
6254 
6255   meta_window_foreach_transient (window, change_workspace_foreach,
6256                                  workspace);
6257   meta_window_foreach_ancestor (window, change_workspace_foreach,
6258                                 workspace);
6259 }
6260 
6261 static void
window_stick_impl(MetaWindow * window)6262 window_stick_impl (MetaWindow  *window)
6263 {
6264   meta_verbose ("Sticking window %s current on_all_workspaces = %d\n",
6265                 window->desc, window->on_all_workspaces);
6266 
6267   if (window->on_all_workspaces_requested)
6268     return;
6269 
6270   /* We don't change window->workspaces, because we revert
6271    * to that original workspace list if on_all_workspaces is
6272    * toggled back off.
6273    */
6274   int old_workspace = meta_workspace_index (window->workspace);
6275   window->on_all_workspaces_requested = TRUE;
6276   meta_window_frame_size_changed (window);
6277   meta_window_update_on_all_workspaces (window);
6278 
6279   meta_window_queue(window, META_QUEUE_CALC_SHOWING);
6280   g_signal_emit (window, window_signals[WORKSPACE_CHANGED], 0,
6281                  old_workspace);
6282 }
6283 
6284 static void
window_unstick_impl(MetaWindow * window)6285 window_unstick_impl (MetaWindow  *window)
6286 {
6287   if (!window->on_all_workspaces_requested)
6288     return;
6289 
6290   /* Revert to window->workspaces */
6291 
6292   window->on_all_workspaces_requested = FALSE;
6293   meta_window_frame_size_changed (window);
6294   meta_window_update_on_all_workspaces (window);
6295 
6296   /* We change ourselves to the active workspace, since otherwise you'd get
6297    * a weird window-vaporization effect. Once we have UI for being
6298    * on more than one workspace this should probably be add_workspace
6299    * not change_workspace.
6300    */
6301   if (window->screen->active_workspace != window->workspace)
6302     meta_window_change_workspace (window, window->screen->active_workspace);
6303 
6304   meta_window_queue(window, META_QUEUE_CALC_SHOWING);
6305   g_signal_emit (window, window_signals[WORKSPACE_CHANGED], 0, -1);
6306 }
6307 
6308 static gboolean
stick_foreach_func(MetaWindow * window,void * data)6309 stick_foreach_func (MetaWindow *window,
6310                     void       *data)
6311 {
6312   gboolean stick;
6313 
6314   stick = *(gboolean*)data;
6315   if (stick)
6316     window_stick_impl (window);
6317   else
6318     window_unstick_impl (window);
6319   return TRUE;
6320 }
6321 
6322 void
meta_window_stick(MetaWindow * window)6323 meta_window_stick (MetaWindow  *window)
6324 {
6325   gboolean stick = TRUE;
6326 
6327   g_return_if_fail (!window->override_redirect);
6328 
6329   window_stick_impl (window);
6330   meta_window_foreach_transient (window,
6331                                  stick_foreach_func,
6332                                  &stick);
6333 }
6334 
6335 void
meta_window_unstick(MetaWindow * window)6336 meta_window_unstick (MetaWindow  *window)
6337 {
6338   gboolean stick = FALSE;
6339 
6340   g_return_if_fail (!window->override_redirect);
6341 
6342   window_unstick_impl (window);
6343   meta_window_foreach_transient (window,
6344                                  stick_foreach_func,
6345                                  &stick);
6346 }
6347 
6348 LOCAL_SYMBOL unsigned long
meta_window_get_net_wm_desktop(MetaWindow * window)6349 meta_window_get_net_wm_desktop (MetaWindow *window)
6350 {
6351   if (window->on_all_workspaces)
6352     return 0xFFFFFFFF;
6353   else
6354     return meta_workspace_index (window->workspace);
6355 }
6356 
6357 static void
update_net_frame_extents(MetaWindow * window)6358 update_net_frame_extents (MetaWindow *window)
6359 {
6360   unsigned long data[4];
6361   MetaFrameBorders borders;
6362 
6363   meta_frame_calc_borders (window->frame, &borders);
6364   /* Left */
6365   data[0] = borders.visible.left;
6366   /* Right */
6367   data[1] = borders.visible.right;
6368   /* Top */
6369   data[2] = borders.visible.top;
6370   /* Bottom */
6371   data[3] = borders.visible.bottom;
6372 
6373   meta_topic (META_DEBUG_GEOMETRY,
6374               "Setting _NET_FRAME_EXTENTS on managed window 0x%lx "
6375               "to left = %lu, right = %lu, top = %lu, bottom = %lu\n",
6376               window->xwindow, data[0], data[1], data[2], data[3]);
6377 
6378   meta_error_trap_push (window->display);
6379   XChangeProperty (window->display->xdisplay, window->xwindow,
6380                    window->display->atom__NET_FRAME_EXTENTS,
6381                    XA_CARDINAL,
6382                    32, PropModeReplace, (guchar*) data, 4);
6383   meta_error_trap_pop (window->display);
6384 }
6385 
6386 static void
update_gtk_edge_constraints(MetaWindow * window)6387 update_gtk_edge_constraints (MetaWindow *window)
6388 {
6389   MetaEdgeConstraint *constraints = window->edge_constraints;
6390   unsigned long data[1];
6391 
6392   /* Edge constraints */
6393   data[0] = (constraints[0] != META_EDGE_CONSTRAINT_NONE ? 1 : 0)    << 0 |
6394             (constraints[0] != META_EDGE_CONSTRAINT_MONITOR ? 1 : 0) << 1 |
6395             (constraints[1] != META_EDGE_CONSTRAINT_NONE ? 1 : 0)    << 2 |
6396             (constraints[1] != META_EDGE_CONSTRAINT_MONITOR ? 1 : 0) << 3 |
6397             (constraints[2] != META_EDGE_CONSTRAINT_NONE ? 1 : 0)    << 4 |
6398             (constraints[2] != META_EDGE_CONSTRAINT_MONITOR ? 1 : 0) << 5 |
6399             (constraints[3] != META_EDGE_CONSTRAINT_NONE ? 1 : 0)    << 6 |
6400             (constraints[3] != META_EDGE_CONSTRAINT_MONITOR ? 1 : 0) << 7;
6401 
6402   meta_verbose ("Setting _GTK_EDGE_CONSTRAINTS to %lu\n", data[0]);
6403 
6404   meta_error_trap_push (window->display);
6405   XChangeProperty (window->display->xdisplay,
6406                    window->xwindow,
6407                    window->display->atom__GTK_EDGE_CONSTRAINTS,
6408                    XA_CARDINAL, 32, PropModeReplace,
6409                    (guchar*) data, 1);
6410   meta_error_trap_pop (window->display);
6411 }
6412 
6413 LOCAL_SYMBOL void
meta_window_set_current_workspace_hint(MetaWindow * window)6414 meta_window_set_current_workspace_hint (MetaWindow *window)
6415 {
6416   /* FIXME if on more than one workspace, we claim to be "sticky",
6417    * the WM spec doesn't say what to do here.
6418    */
6419   unsigned long data[1];
6420 
6421   if (window->workspace == NULL)
6422     {
6423       /* this happens when unmanaging windows */
6424       return;
6425     }
6426 
6427   data[0] = meta_window_get_net_wm_desktop (window);
6428 
6429   meta_verbose ("Setting _NET_WM_DESKTOP of %s to %lu\n",
6430                 window->desc, data[0]);
6431 
6432   meta_error_trap_push (window->display);
6433   XChangeProperty (window->display->xdisplay, window->xwindow,
6434                    window->display->atom__NET_WM_DESKTOP,
6435                    XA_CARDINAL,
6436                    32, PropModeReplace, (guchar*) data, 1);
6437   meta_error_trap_pop (window->display);
6438 }
6439 
6440 static gboolean
find_root_ancestor(MetaWindow * window,void * data)6441 find_root_ancestor (MetaWindow *window,
6442                     void       *data)
6443 {
6444   MetaWindow **ancestor = data;
6445 
6446   /* Overwrite the previously "most-root" ancestor with the new one found */
6447   *ancestor = window;
6448 
6449   /* We want this to continue until meta_window_foreach_ancestor quits because
6450    * there are no more valid ancestors.
6451    */
6452   return TRUE;
6453 }
6454 
6455 /**
6456  * meta_window_find_root_ancestor:
6457  * @window: a #MetaWindow
6458  *
6459  * Follow the chain of parents of @window, skipping transient windows,
6460  * and return the "root" window which has no non-transient parent.
6461  *
6462  * Returns: (transfer none): The root ancestor window
6463  */
6464 MetaWindow *
meta_window_find_root_ancestor(MetaWindow * window)6465 meta_window_find_root_ancestor (MetaWindow *window)
6466 {
6467   MetaWindow *ancestor;
6468   ancestor = window;
6469   meta_window_foreach_ancestor (window, find_root_ancestor, &ancestor);
6470   return ancestor;
6471 }
6472 
6473 void
meta_window_raise(MetaWindow * window)6474 meta_window_raise (MetaWindow  *window)
6475 {
6476   MetaWindow *ancestor;
6477 
6478   g_return_if_fail (!window->override_redirect);
6479 
6480   ancestor = meta_window_find_root_ancestor (window);
6481 
6482   meta_topic (META_DEBUG_WINDOW_OPS,
6483               "Raising window %s, ancestor of %s\n",
6484               ancestor->desc, window->desc);
6485 
6486   /* Raise the ancestor of the window (if the window has no ancestor,
6487    * then ancestor will be set to the window itself); do this because
6488    * it's weird to see windows from other apps stacked between a child
6489    * and parent window of the currently active app.  The stacking
6490    * constraints in stack.c then magically take care of raising all
6491    * the child windows appropriately.
6492    */
6493   if (window->screen->stack == ancestor->screen->stack)
6494     meta_stack_raise (window->screen->stack, ancestor);
6495   else
6496     {
6497       meta_warning (
6498                     "Either stacks aren't per screen or some window has a weird "
6499                     "transient_for hint; window->screen->stack != "
6500                     "ancestor->screen->stack.  window = %s, ancestor = %s.\n",
6501                     window->desc, ancestor->desc);
6502       /* We could raise the window here, but don't want to do that twice and
6503        * so we let the case below handle that.
6504        */
6505     }
6506 
6507   /* Okay, so stacking constraints misses one case: If a window has
6508    * two children and we want to raise one of those children, then
6509    * raising the ancestor isn't enough; we need to also raise the
6510    * correct child.  See bug 307875.
6511    */
6512   if (window != ancestor)
6513     meta_stack_raise (window->screen->stack, window);
6514 
6515   g_signal_emit (window, window_signals[RAISED], 0);
6516 }
6517 
6518 void
meta_window_lower(MetaWindow * window)6519 meta_window_lower (MetaWindow  *window)
6520 {
6521   g_return_if_fail (!window->override_redirect);
6522 
6523   meta_topic (META_DEBUG_WINDOW_OPS,
6524               "Lowering window %s\n", window->desc);
6525 
6526   meta_stack_lower (window->screen->stack, window);
6527 }
6528 
6529 LOCAL_SYMBOL void
meta_window_send_icccm_message(MetaWindow * window,Atom atom,guint32 timestamp)6530 meta_window_send_icccm_message (MetaWindow *window,
6531                                 Atom        atom,
6532                                 guint32     timestamp)
6533 {
6534   /* This comment and code are from twm, copyright
6535    * Open Group, Evans & Sutherland, etc.
6536    */
6537 
6538   /*
6539    * ICCCM Client Messages - Section 4.2.8 of the ICCCM dictates that all
6540    * client messages will have the following form:
6541    *
6542    *     event type	ClientMessage
6543    *     message type	_XA_WM_PROTOCOLS
6544    *     window		tmp->w
6545    *     format		32
6546    *     data[0]		message atom
6547    *     data[1]		time stamp
6548    */
6549 
6550     XClientMessageEvent ev;
6551 
6552     ev.type = ClientMessage;
6553     ev.window = window->xwindow;
6554     ev.message_type = window->display->atom_WM_PROTOCOLS;
6555     ev.format = 32;
6556     ev.data.l[0] = atom;
6557     ev.data.l[1] = timestamp;
6558 
6559     meta_error_trap_push (window->display);
6560     XSendEvent (window->display->xdisplay,
6561                 window->xwindow, False, 0, (XEvent*) &ev);
6562     meta_error_trap_pop (window->display);
6563 }
6564 
6565 LOCAL_SYMBOL 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)6566 meta_window_move_resize_request (MetaWindow *window,
6567                                  guint       value_mask,
6568                                  int         gravity,
6569                                  int         new_x,
6570                                  int         new_y,
6571                                  int         new_width,
6572                                  int         new_height)
6573 {
6574   int x, y, width, height;
6575   gboolean allow_position_change;
6576   gboolean in_grab_op;
6577   MetaMoveResizeFlags flags;
6578 
6579   /* We ignore configure requests while the user is moving/resizing
6580    * the window, since these represent the app sucking and fighting
6581    * the user, most likely due to a bug in the app (e.g. pfaedit
6582    * seemed to do this)
6583    *
6584    * Still have to do the ConfigureNotify and all, but pretend the
6585    * app asked for the current size/position instead of the new one.
6586    */
6587   in_grab_op = FALSE;
6588   if (window->display->grab_op != META_GRAB_OP_NONE &&
6589       window == window->display->grab_window)
6590     {
6591       switch (window->display->grab_op)
6592         {
6593         case META_GRAB_OP_MOVING:
6594         case META_GRAB_OP_RESIZING_SE:
6595         case META_GRAB_OP_RESIZING_S:
6596         case META_GRAB_OP_RESIZING_SW:
6597         case META_GRAB_OP_RESIZING_N:
6598         case META_GRAB_OP_RESIZING_NE:
6599         case META_GRAB_OP_RESIZING_NW:
6600         case META_GRAB_OP_RESIZING_W:
6601         case META_GRAB_OP_RESIZING_E:
6602           in_grab_op = TRUE;
6603           break;
6604         default:
6605           break;
6606         }
6607     }
6608 
6609   /* it's essential to use only the explicitly-set fields,
6610    * and otherwise use our current up-to-date position.
6611    *
6612    * Otherwise you get spurious position changes when the app changes
6613    * size, for example, if window->rect is not in sync with the
6614    * server-side position in effect when the configure request was
6615    * generated.
6616    */
6617   meta_window_get_gravity_position (window,
6618                                     gravity,
6619                                     &x, &y);
6620 
6621   allow_position_change = FALSE;
6622 
6623   if (meta_prefs_get_disable_workarounds ())
6624     {
6625       if (window->type == META_WINDOW_DIALOG ||
6626           window->type == META_WINDOW_MODAL_DIALOG ||
6627           window->type == META_WINDOW_SPLASHSCREEN)
6628         ; /* No position change for these */
6629       else if ((window->size_hints.flags & PPosition) ||
6630                /* USPosition is just stale if window is placed;
6631                 * no --geometry involved here.
6632                 */
6633                ((window->size_hints.flags & USPosition) &&
6634                 !window->placed))
6635         allow_position_change = TRUE;
6636     }
6637   else
6638     {
6639       allow_position_change = TRUE;
6640     }
6641 
6642   if (in_grab_op)
6643     allow_position_change = FALSE;
6644 
6645   if (allow_position_change)
6646     {
6647       if (value_mask & CWX)
6648         x = new_x;
6649       if (value_mask & CWY)
6650         y = new_y;
6651       if (value_mask & (CWX | CWY))
6652         {
6653           /* Once manually positioned, windows shouldn't be placed
6654            * by the window manager.
6655            */
6656           window->placed = TRUE;
6657         }
6658     }
6659   else
6660     {
6661       meta_topic (META_DEBUG_GEOMETRY,
6662 		  "Not allowing position change for window %s PPosition 0x%lx USPosition 0x%lx type %u\n",
6663 		  window->desc, window->size_hints.flags & PPosition,
6664 		  window->size_hints.flags & USPosition,
6665 		  window->type);
6666     }
6667 
6668   width = window->rect.width;
6669   height = window->rect.height;
6670   if (!in_grab_op)
6671     {
6672       if (value_mask & CWWidth)
6673         width = new_width;
6674 
6675       if (value_mask & CWHeight)
6676         height = new_height;
6677     }
6678 
6679   /* ICCCM 4.1.5 */
6680 
6681   /* We're ignoring the value_mask here, since sizes
6682    * not in the mask will be the current window geometry.
6683    */
6684   window->size_hints.x = x;
6685   window->size_hints.y = y;
6686   window->size_hints.width = width;
6687   window->size_hints.height = height;
6688 
6689   /* NOTE: We consider ConfigureRequests to be "user" actions in one
6690    * way, but not in another.  Explanation of the two cases are in the
6691    * next two big comments.
6692    */
6693 
6694   /* The constraints code allows user actions to move windows
6695    * offscreen, etc., and configure request actions would often send
6696    * windows offscreen when users don't want it if not constrained
6697    * (e.g. hitting a dropdown triangle in a fileselector to show more
6698    * options, which makes the window bigger).  Thus we do not set
6699    * META_IS_USER_ACTION in flags to the
6700    * meta_window_move_resize_internal() call.
6701    */
6702   flags = META_IS_CONFIGURE_REQUEST;
6703   if (value_mask & (CWX | CWY))
6704     flags |= META_IS_MOVE_ACTION;
6705   if (value_mask & (CWWidth | CWHeight))
6706     flags |= META_IS_RESIZE_ACTION;
6707 
6708   if (flags & (META_IS_MOVE_ACTION | META_IS_RESIZE_ACTION))
6709   {
6710     MetaRectangle rect, monitor_rect;
6711 
6712     rect.x = x;
6713     rect.y = y;
6714     rect.width = width;
6715     rect.height = height;
6716 
6717     /* Workaround braindead legacy apps that don't know how to
6718     * fullscreen themselves properly - don't get fooled by
6719     * windows which hide their titlebar when maximized or which are
6720     * client decorated; that's not the same as fullscreen, even
6721     * if there are no struts making the workarea smaller than
6722     * the monitor.
6723     */
6724     if (window->monitor)
6725       {
6726         meta_screen_get_monitor_geometry (window->screen, window->monitor->number, &monitor_rect);
6727 
6728         if (meta_prefs_get_force_fullscreen() &&
6729             !window->hide_titlebar_when_maximized &&
6730             (window->decorated && !meta_window_is_client_decorated (window)) &&
6731             meta_rectangle_equal (&rect, &monitor_rect) &&
6732             window->has_fullscreen_func &&
6733             !window->fullscreen)
6734           {
6735             /*
6736           meta_topic (META_DEBUG_GEOMETRY,
6737             */
6738             meta_warning (
6739                         "Treating resize request of legacy application %s as a "
6740                         "fullscreen request\n",
6741                         window->desc);
6742             meta_window_make_fullscreen_internal (window);
6743           }
6744       }
6745 
6746     meta_window_move_resize_internal (window,
6747                                       flags,
6748                                       gravity,
6749                                       x,
6750                                       y,
6751                                       width,
6752                                       height);
6753   }
6754   /* window->user_rect exists to allow "snapping-back" the window if a
6755    * new strut is set (causing the window to move) and then the strut
6756    * is later removed without the user moving the window in the
6757    * interim.  We'd like to "snap-back" to the position specified by
6758    * ConfigureRequest events (at least the constrained version of the
6759    * ConfigureRequest, since that is guaranteed to be onscreen) so we
6760    * set user_rect here.
6761    *
6762    * See also bug 426519.
6763    */
6764   save_user_window_placement (window);
6765 }
6766 
6767 static void
restack_window(MetaWindow * window,MetaWindow * sibling,int direction)6768 restack_window (MetaWindow *window,
6769                 MetaWindow *sibling,
6770                 int         direction)
6771 {
6772  switch (direction)
6773    {
6774    case Above:
6775      if (sibling)
6776        meta_window_stack_just_above (window, sibling);
6777      else
6778        meta_window_raise (window);
6779      break;
6780    case Below:
6781      if (sibling)
6782        meta_window_stack_just_below (window, sibling);
6783      else
6784        meta_window_lower (window);
6785      break;
6786    case TopIf:
6787    case BottomIf:
6788    case Opposite:
6789      break;
6790    }
6791 }
6792 
6793 LOCAL_SYMBOL gboolean
meta_window_configure_request(MetaWindow * window,XEvent * event)6794 meta_window_configure_request (MetaWindow *window,
6795                                XEvent     *event)
6796 {
6797   /* Note that x, y is the corner of the window border,
6798    * and width, height is the size of the window inside
6799    * its border, but that we always deny border requests
6800    * and give windows a border of 0. But we save the
6801    * requested border here.
6802    */
6803   if (event->xconfigurerequest.value_mask & CWBorderWidth)
6804     window->border_width = event->xconfigurerequest.border_width;
6805 
6806   meta_window_move_resize_request(window,
6807                                   event->xconfigurerequest.value_mask,
6808                                   window->size_hints.win_gravity,
6809                                   event->xconfigurerequest.x,
6810                                   event->xconfigurerequest.y,
6811                                   event->xconfigurerequest.width,
6812                                   event->xconfigurerequest.height);
6813 
6814   /* Handle stacking. We only handle raises/lowers, mostly because
6815    * stack.c really can't deal with anything else.  I guess we'll fix
6816    * that if a client turns up that really requires it. Only a very
6817    * few clients even require the raise/lower (and in fact all client
6818    * attempts to deal with stacking order are essentially broken,
6819    * since they have no idea what other clients are involved or how
6820    * the stack looks).
6821    *
6822    * I'm pretty sure no interesting client uses TopIf, BottomIf, or
6823    * Opposite anyway.
6824    */
6825   if (event->xconfigurerequest.value_mask & CWStackMode)
6826     {
6827       MetaWindow *active_window;
6828       active_window = window->display->expected_focus_window;
6829       if (meta_prefs_get_disable_workarounds ())
6830         {
6831           meta_topic (META_DEBUG_STACK,
6832                       "%s sent an xconfigure stacking request; this is "
6833                       "broken behavior and the request is being ignored.\n",
6834                       window->desc);
6835         }
6836       else if (active_window &&
6837                !meta_window_same_application (window, active_window) &&
6838                !meta_window_same_client (window, active_window) &&
6839                XSERVER_TIME_IS_BEFORE (window->net_wm_user_time,
6840                                        active_window->net_wm_user_time))
6841         {
6842           meta_topic (META_DEBUG_STACK,
6843                       "Ignoring xconfigure stacking request from %s (with "
6844                       "user_time %u); currently active application is %s (with "
6845                       "user_time %u).\n",
6846                       window->desc,
6847                       window->net_wm_user_time,
6848                       active_window->desc,
6849                       active_window->net_wm_user_time);
6850           if (event->xconfigurerequest.detail == Above)
6851             meta_window_set_demands_attention(window);
6852         }
6853       else
6854         {
6855           MetaWindow *sibling = NULL;
6856           /* Handle Above/Below with a sibling set */
6857           if (event->xconfigurerequest.above != None)
6858             {
6859               MetaDisplay *display;
6860 
6861               display = meta_window_get_display (window);
6862               sibling = meta_display_lookup_x_window (display,
6863                                                       event->xconfigurerequest.above);
6864               if (sibling == NULL)
6865                 return TRUE;
6866 
6867               meta_topic (META_DEBUG_STACK,
6868                       "xconfigure stacking request from window %s sibling %s stackmode %d\n",
6869                       window->desc, sibling->desc, event->xconfigurerequest.detail);
6870             }
6871           restack_window (window, sibling, event->xconfigurerequest.detail);
6872         }
6873     }
6874 
6875   return TRUE;
6876 }
6877 
6878 LOCAL_SYMBOL gboolean
meta_window_property_notify(MetaWindow * window,XEvent * event)6879 meta_window_property_notify (MetaWindow *window,
6880                              XEvent     *event)
6881 {
6882   return process_property_notify (window, &event->xproperty);
6883 }
6884 
6885 static void
handle_net_restack_window(MetaDisplay * display,XEvent * event)6886 handle_net_restack_window (MetaDisplay *display,
6887                            XEvent      *event)
6888 {
6889   MetaWindow *window, *sibling = NULL;
6890 
6891   /* Ignore if this does not come from a pager, see the WM spec
6892    */
6893   if (event->xclient.data.l[0] != 2)
6894     return;
6895 
6896   window = meta_display_lookup_x_window (display,
6897                                          event->xclient.window);
6898 
6899   if (window)
6900     {
6901       if (event->xclient.data.l[1])
6902         sibling = meta_display_lookup_x_window (display,
6903                                                 event->xclient.data.l[1]);
6904 
6905       restack_window (window, sibling, event->xclient.data.l[2]);
6906     }
6907 }
6908 
6909 /*
6910  * Move window to the requested workspace; append controls whether new WS
6911  * should be created if one does not exist.
6912  */
6913 void
meta_window_change_workspace_by_index(MetaWindow * window,gint space_index,gboolean append,guint32 timestamp)6914 meta_window_change_workspace_by_index (MetaWindow *window,
6915                                        gint        space_index,
6916                                        gboolean    append,
6917                                        guint32     timestamp)
6918 {
6919   MetaWorkspace *workspace;
6920   MetaScreen    *screen;
6921 
6922   g_return_if_fail (!window->override_redirect);
6923 
6924   if (space_index == -1)
6925     {
6926       meta_window_stick (window);
6927       return;
6928     }
6929 
6930   screen = window->screen;
6931 
6932   workspace =
6933     meta_screen_get_workspace_by_index (screen, space_index);
6934 
6935   if (!workspace && append)
6936     {
6937       if (timestamp == CurrentTime)
6938         timestamp = meta_display_get_current_time_roundtrip (window->display);
6939       workspace = meta_screen_append_new_workspace (screen, FALSE, timestamp);
6940     }
6941 
6942   if (workspace)
6943     {
6944       if (window->on_all_workspaces_requested)
6945         meta_window_unstick (window);
6946 
6947       meta_window_change_workspace (window, workspace);
6948     }
6949 }
6950 
6951 #define _NET_WM_MOVERESIZE_SIZE_TOPLEFT      0
6952 #define _NET_WM_MOVERESIZE_SIZE_TOP          1
6953 #define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT     2
6954 #define _NET_WM_MOVERESIZE_SIZE_RIGHT        3
6955 #define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT  4
6956 #define _NET_WM_MOVERESIZE_SIZE_BOTTOM       5
6957 #define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT   6
6958 #define _NET_WM_MOVERESIZE_SIZE_LEFT         7
6959 #define _NET_WM_MOVERESIZE_MOVE              8
6960 #define _NET_WM_MOVERESIZE_SIZE_KEYBOARD     9
6961 #define _NET_WM_MOVERESIZE_MOVE_KEYBOARD    10
6962 #define _NET_WM_MOVERESIZE_CANCEL           11
6963 
6964 LOCAL_SYMBOL gboolean
meta_window_client_message(MetaWindow * window,XEvent * event)6965 meta_window_client_message (MetaWindow *window,
6966                             XEvent     *event)
6967 {
6968   MetaDisplay *display;
6969 
6970   display = window->display;
6971 
6972   if (window->override_redirect)
6973     {
6974       /* Don't warn here: we could warn on any of the messages below,
6975        * but we might also receive other client messages that are
6976        * part of protocols we don't know anything about. So, silently
6977        * ignoring is simplest.
6978        */
6979       return FALSE;
6980     }
6981 
6982   if (event->xclient.message_type ==
6983       display->atom__NET_CLOSE_WINDOW)
6984     {
6985       guint32 timestamp;
6986 
6987       if (event->xclient.data.l[0] != 0)
6988 	timestamp = event->xclient.data.l[0];
6989       else
6990         {
6991           meta_warning ("Receiving a NET_CLOSE_WINDOW message for %s without "
6992                         "a timestamp!  This means some buggy (outdated) "
6993                         "application is on the loose!\n",
6994                         window->desc);
6995           timestamp = meta_display_get_current_time (window->display);
6996         }
6997 
6998       meta_window_delete (window, timestamp);
6999 
7000       return TRUE;
7001     }
7002   else if (event->xclient.message_type ==
7003            display->atom__NET_WM_DESKTOP)
7004     {
7005       int space;
7006       MetaWorkspace *workspace;
7007 
7008       space = event->xclient.data.l[0];
7009 
7010       meta_verbose ("Request to move %s to workspace %d\n",
7011                     window->desc, space);
7012 
7013       workspace =
7014         meta_screen_get_workspace_by_index (window->screen,
7015                                             space);
7016 
7017       if (workspace)
7018         meta_window_change_workspace (window, workspace);
7019       else if (space == (int) 0xFFFFFFFF)
7020         meta_window_stick (window);
7021       else
7022         meta_verbose ("No such workspace %d for screen\n", space);
7023 
7024       meta_verbose ("Window %s now on_all_workspaces = %d\n",
7025                     window->desc, window->on_all_workspaces);
7026 
7027       return TRUE;
7028     }
7029   else if (event->xclient.message_type ==
7030            display->atom__NET_WM_STATE)
7031     {
7032       gulong action;
7033       Atom first;
7034       Atom second;
7035 
7036       action = event->xclient.data.l[0];
7037       first = event->xclient.data.l[1];
7038       second = event->xclient.data.l[2];
7039 
7040       if (meta_is_verbose ())
7041         {
7042           char *str1;
7043           char *str2;
7044 
7045           meta_error_trap_push_with_return (display);
7046           str1 = XGetAtomName (display->xdisplay, first);
7047           if (meta_error_trap_pop_with_return (display) != Success)
7048             str1 = NULL;
7049 
7050           meta_error_trap_push_with_return (display);
7051           str2 = XGetAtomName (display->xdisplay, second);
7052           if (meta_error_trap_pop_with_return (display) != Success)
7053             str2 = NULL;
7054 
7055           meta_verbose ("Request to change _NET_WM_STATE action %lu atom1: %s atom2: %s\n",
7056                         action,
7057                         str1 ? str1 : "(unknown)",
7058                         str2 ? str2 : "(unknown)");
7059 
7060           meta_XFree (str1);
7061           meta_XFree (str2);
7062         }
7063 
7064       if (first == display->atom__NET_WM_STATE_SHADED ||
7065           second == display->atom__NET_WM_STATE_SHADED)
7066         {
7067           gboolean shade;
7068           guint32 timestamp;
7069 
7070           /* Stupid protocol has no timestamp; of course, shading
7071            * sucks anyway so who really cares that we're forced to do
7072            * a roundtrip here?
7073            */
7074           timestamp = meta_display_get_current_time_roundtrip (window->display);
7075 
7076           shade = (action == _NET_WM_STATE_ADD ||
7077                    (action == _NET_WM_STATE_TOGGLE && !window->shaded));
7078           if (shade && window->has_shade_func)
7079             meta_window_shade (window, timestamp);
7080           else
7081             meta_window_unshade (window, timestamp);
7082         }
7083 
7084       if (first == display->atom__NET_WM_STATE_FULLSCREEN ||
7085           second == display->atom__NET_WM_STATE_FULLSCREEN)
7086         {
7087           gboolean make_fullscreen;
7088 
7089           make_fullscreen = (action == _NET_WM_STATE_ADD ||
7090                              (action == _NET_WM_STATE_TOGGLE && !window->fullscreen));
7091           if (make_fullscreen && window->has_fullscreen_func)
7092             meta_window_make_fullscreen (window);
7093           else
7094             meta_window_unmake_fullscreen (window);
7095         }
7096 
7097       if (first == display->atom__NET_WM_STATE_MAXIMIZED_HORZ ||
7098           second == display->atom__NET_WM_STATE_MAXIMIZED_HORZ)
7099         {
7100           gboolean max;
7101 
7102           max = (action == _NET_WM_STATE_ADD ||
7103                  (action == _NET_WM_STATE_TOGGLE &&
7104                   !window->maximized_horizontally));
7105           if (max && window->has_maximize_func)
7106             {
7107               if (meta_prefs_get_raise_on_click ())
7108                 meta_window_raise (window);
7109               meta_window_maximize (window, META_MAXIMIZE_HORIZONTAL);
7110             }
7111           else
7112             {
7113               if (meta_prefs_get_raise_on_click ())
7114                 meta_window_raise (window);
7115               meta_window_unmaximize (window, META_MAXIMIZE_HORIZONTAL);
7116             }
7117         }
7118 
7119       if (first == display->atom__NET_WM_STATE_MAXIMIZED_VERT ||
7120           second == display->atom__NET_WM_STATE_MAXIMIZED_VERT)
7121         {
7122           gboolean max;
7123 
7124           max = (action == _NET_WM_STATE_ADD ||
7125                  (action == _NET_WM_STATE_TOGGLE &&
7126                   !window->maximized_vertically));
7127           if (max && window->has_maximize_func)
7128             {
7129               if (meta_prefs_get_raise_on_click ())
7130                 meta_window_raise (window);
7131               meta_window_maximize (window, META_MAXIMIZE_VERTICAL);
7132             }
7133           else
7134             {
7135               if (meta_prefs_get_raise_on_click ())
7136                 meta_window_raise (window);
7137               meta_window_unmaximize (window, META_MAXIMIZE_VERTICAL);
7138             }
7139         }
7140 
7141       if (first == display->atom__NET_WM_STATE_MODAL ||
7142           second == display->atom__NET_WM_STATE_MODAL)
7143         {
7144           window->wm_state_modal =
7145             (action == _NET_WM_STATE_ADD) ||
7146             (action == _NET_WM_STATE_TOGGLE && !window->wm_state_modal);
7147 
7148           recalc_window_type (window);
7149           meta_window_queue(window, META_QUEUE_MOVE_RESIZE);
7150         }
7151 
7152       if (first == display->atom__NET_WM_STATE_SKIP_PAGER ||
7153           second == display->atom__NET_WM_STATE_SKIP_PAGER)
7154         {
7155           window->wm_state_skip_pager =
7156             (action == _NET_WM_STATE_ADD) ||
7157             (action == _NET_WM_STATE_TOGGLE && !window->skip_pager);
7158 
7159           recalc_window_features (window);
7160           set_net_wm_state (window);
7161         }
7162 
7163       if (first == display->atom__NET_WM_STATE_SKIP_TASKBAR ||
7164           second == display->atom__NET_WM_STATE_SKIP_TASKBAR)
7165         {
7166           window->wm_state_skip_taskbar =
7167             (action == _NET_WM_STATE_ADD) ||
7168             (action == _NET_WM_STATE_TOGGLE && !window->skip_taskbar);
7169 
7170           recalc_window_features (window);
7171           set_net_wm_state (window);
7172         }
7173 
7174       if (first == display->atom__NET_WM_STATE_ABOVE ||
7175           second == display->atom__NET_WM_STATE_ABOVE)
7176         {
7177           meta_window_set_above(window,
7178             (action == _NET_WM_STATE_ADD) ||
7179             (action == _NET_WM_STATE_TOGGLE && !window->wm_state_above));
7180         }
7181 
7182       if (first == display->atom__NET_WM_STATE_BELOW ||
7183           second == display->atom__NET_WM_STATE_BELOW)
7184         {
7185           window->wm_state_below =
7186             (action == _NET_WM_STATE_ADD) ||
7187             (action == _NET_WM_STATE_TOGGLE && !window->wm_state_below);
7188 
7189           meta_window_update_layer (window);
7190           set_net_wm_state (window);
7191         }
7192 
7193       if (first == display->atom__NET_WM_STATE_DEMANDS_ATTENTION ||
7194           second == display->atom__NET_WM_STATE_DEMANDS_ATTENTION)
7195         {
7196           if ((action == _NET_WM_STATE_ADD) ||
7197               (action == _NET_WM_STATE_TOGGLE && !window->wm_state_demands_attention))
7198             meta_window_set_demands_attention (window);
7199           else
7200             meta_window_unset_demands_attention (window);
7201         }
7202 
7203        if (first == display->atom__NET_WM_STATE_STICKY ||
7204           second == display->atom__NET_WM_STATE_STICKY)
7205         {
7206           if ((action == _NET_WM_STATE_ADD) ||
7207               (action == _NET_WM_STATE_TOGGLE && !window->on_all_workspaces_requested))
7208             meta_window_stick (window);
7209           else
7210             meta_window_unstick (window);
7211         }
7212 
7213       return TRUE;
7214     }
7215   else if (event->xclient.message_type ==
7216            display->atom_WM_CHANGE_STATE)
7217     {
7218       meta_verbose ("WM_CHANGE_STATE client message, state: %ld\n",
7219                     event->xclient.data.l[0]);
7220       if (event->xclient.data.l[0] == IconicState)
7221         meta_window_minimize (window);
7222 
7223       return TRUE;
7224     }
7225   else if (event->xclient.message_type ==
7226            display->atom__NET_WM_MOVERESIZE)
7227     {
7228       int x_root;
7229       int y_root;
7230       int action;
7231       MetaGrabOp op;
7232       int button;
7233       guint32 timestamp;
7234 
7235       /* _NET_WM_MOVERESIZE messages are almost certainly going to come from
7236        * clients when users click on the fake "frame" that the client has,
7237        * thus we should also treat such messages as though it were a
7238        * "frame action".
7239        */
7240       gboolean const frame_action = TRUE;
7241 
7242       x_root = event->xclient.data.l[0];
7243       y_root = event->xclient.data.l[1];
7244       action = event->xclient.data.l[2];
7245       button = event->xclient.data.l[3];
7246 
7247       /* FIXME: What a braindead protocol; no timestamp?!? */
7248       timestamp = meta_display_get_current_time_roundtrip (display);
7249       meta_topic (META_DEBUG_WINDOW_OPS,
7250                   "Received _NET_WM_MOVERESIZE message on %s, %d,%d action = %d, button %d\n",
7251                   window->desc,
7252                   x_root, y_root, action, button);
7253 
7254       op = META_GRAB_OP_NONE;
7255       switch (action)
7256         {
7257         case _NET_WM_MOVERESIZE_SIZE_TOPLEFT:
7258           op = META_GRAB_OP_RESIZING_NW;
7259           break;
7260         case _NET_WM_MOVERESIZE_SIZE_TOP:
7261           op = META_GRAB_OP_RESIZING_N;
7262           break;
7263         case _NET_WM_MOVERESIZE_SIZE_TOPRIGHT:
7264           op = META_GRAB_OP_RESIZING_NE;
7265           break;
7266         case _NET_WM_MOVERESIZE_SIZE_RIGHT:
7267           op = META_GRAB_OP_RESIZING_E;
7268           break;
7269         case _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT:
7270           op = META_GRAB_OP_RESIZING_SE;
7271           break;
7272         case _NET_WM_MOVERESIZE_SIZE_BOTTOM:
7273           op = META_GRAB_OP_RESIZING_S;
7274           break;
7275         case _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT:
7276           op = META_GRAB_OP_RESIZING_SW;
7277           break;
7278         case _NET_WM_MOVERESIZE_SIZE_LEFT:
7279           op = META_GRAB_OP_RESIZING_W;
7280           break;
7281         case _NET_WM_MOVERESIZE_MOVE:
7282           op = META_GRAB_OP_MOVING;
7283           break;
7284         case _NET_WM_MOVERESIZE_SIZE_KEYBOARD:
7285           op = META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN;
7286           break;
7287         case _NET_WM_MOVERESIZE_MOVE_KEYBOARD:
7288           op = META_GRAB_OP_KEYBOARD_MOVING;
7289           break;
7290         case _NET_WM_MOVERESIZE_CANCEL:
7291           /* handled below */
7292           break;
7293         default:
7294           break;
7295         }
7296 
7297       if (action == _NET_WM_MOVERESIZE_CANCEL)
7298         {
7299           meta_display_end_grab_op (window->display, timestamp);
7300         }
7301       else if (op != META_GRAB_OP_NONE &&
7302           ((window->has_move_func && op == META_GRAB_OP_KEYBOARD_MOVING) ||
7303            (window->has_resize_func && op == META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN)))
7304         {
7305           meta_window_begin_grab_op (window, op, frame_action, timestamp);
7306         }
7307       else if (op != META_GRAB_OP_NONE &&
7308                ((window->has_move_func && op == META_GRAB_OP_MOVING) ||
7309                (window->has_resize_func &&
7310                 (op != META_GRAB_OP_MOVING &&
7311                  op != META_GRAB_OP_KEYBOARD_MOVING))))
7312         {
7313           /*
7314            * the button SHOULD already be included in the message
7315            */
7316           if (button == 0)
7317             {
7318               int x, y, query_root_x, query_root_y;
7319               Window root, child;
7320               guint mask;
7321 
7322               /* The race conditions in this _NET_WM_MOVERESIZE thing
7323                * are mind-boggling
7324                */
7325               mask = 0;
7326               meta_error_trap_push (window->display);
7327               XQueryPointer (window->display->xdisplay,
7328                              window->xwindow,
7329                              &root, &child,
7330                              &query_root_x, &query_root_y,
7331                              &x, &y,
7332                              &mask);
7333               meta_error_trap_pop (window->display);
7334 
7335               if (mask & Button1Mask)
7336                 button = 1;
7337               else if (mask & Button2Mask)
7338                 button = 2;
7339               else if (mask & Button3Mask)
7340                 button = 3;
7341               else
7342                 button = 0;
7343             }
7344 
7345           if (button != 0)
7346             {
7347               meta_topic (META_DEBUG_WINDOW_OPS,
7348                           "Beginning move/resize with button = %d\n", button);
7349               meta_display_begin_grab_op (window->display,
7350                                           window->screen,
7351                                           window,
7352                                           op,
7353                                           FALSE,
7354                                           frame_action,
7355                                           button, 0,
7356                                           timestamp,
7357                                           x_root,
7358                                           y_root);
7359             }
7360         }
7361 
7362       return TRUE;
7363     }
7364   else if (event->xclient.message_type ==
7365            display->atom__NET_MOVERESIZE_WINDOW)
7366     {
7367       int gravity;
7368       guint value_mask;
7369 
7370       gravity = (event->xclient.data.l[0] & 0xff);
7371       value_mask = (event->xclient.data.l[0] & 0xf00) >> 8;
7372       /* source = (event->xclient.data.l[0] & 0xf000) >> 12; */
7373 
7374       if (gravity == 0)
7375         gravity = window->size_hints.win_gravity;
7376 
7377       meta_window_move_resize_request(window,
7378                                       value_mask,
7379                                       gravity,
7380                                       event->xclient.data.l[1],  /* x */
7381                                       event->xclient.data.l[2],  /* y */
7382                                       event->xclient.data.l[3],  /* width */
7383                                       event->xclient.data.l[4]); /* height */
7384     }
7385   else if (event->xclient.message_type ==
7386            display->atom__NET_ACTIVE_WINDOW)
7387     {
7388       MetaClientType source_indication;
7389       guint32        timestamp;
7390 
7391       meta_verbose ("_NET_ACTIVE_WINDOW request for window '%s', activating\n",
7392                     window->desc);
7393 
7394       source_indication = event->xclient.data.l[0];
7395       timestamp = event->xclient.data.l[1];
7396 
7397       if (source_indication > META_CLIENT_TYPE_MAX_RECOGNIZED)
7398         source_indication = META_CLIENT_TYPE_UNKNOWN;
7399 
7400       if (timestamp == 0)
7401         {
7402           /* Client using older EWMH _NET_ACTIVE_WINDOW without a timestamp */
7403           meta_warning ("Buggy client sent a _NET_ACTIVE_WINDOW message with a "
7404                         "timestamp of 0 for %s\n",
7405                         window->desc);
7406           timestamp = meta_display_get_current_time (display);
7407         }
7408 
7409       window_activate (window, timestamp, source_indication, NULL);
7410       return TRUE;
7411     }
7412   else if (event->xclient.message_type ==
7413            display->atom__NET_WM_FULLSCREEN_MONITORS)
7414     {
7415       gulong top, bottom, left, right;
7416 
7417       meta_verbose ("_NET_WM_FULLSCREEN_MONITORS request for window '%s'\n",
7418                     window->desc);
7419 
7420       top = event->xclient.data.l[0];
7421       bottom = event->xclient.data.l[1];
7422       left = event->xclient.data.l[2];
7423       right = event->xclient.data.l[3];
7424       /* source_indication = event->xclient.data.l[4]; */
7425 
7426       meta_window_update_fullscreen_monitors (window, top, bottom, left, right);
7427     }
7428 
7429   else if (event->xclient.message_type ==
7430            display->atom__GTK_SHOW_WINDOW_MENU)
7431     {
7432       if (meta_window_is_client_decorated (window))
7433         {
7434           int x_root, y_root;
7435 
7436           x_root = event->xclient.data.l[1];
7437           y_root = event->xclient.data.l[2];
7438 
7439           meta_screen_hide_hud_and_preview (window->screen);
7440 
7441           if (meta_prefs_get_raise_on_click ())
7442             meta_window_raise (window);
7443           meta_window_focus (window, meta_display_get_current_time_roundtrip (display));
7444 
7445           meta_window_show_menu (window, x_root,
7446                                  y_root, GDK_BUTTON_SECONDARY,
7447                                  meta_display_get_current_time_roundtrip (display));
7448         }
7449     }
7450   else if (event->xclient.message_type ==
7451            display->atom__NET_RESTACK_WINDOW)
7452     {
7453       handle_net_restack_window (display, event);
7454     }
7455 
7456   return FALSE;
7457 }
7458 
7459 static void
meta_window_appears_focused_changed(MetaWindow * window)7460 meta_window_appears_focused_changed (MetaWindow *window)
7461 {
7462   set_net_wm_state (window);
7463   meta_window_frame_size_changed (window);
7464 
7465   g_object_notify (G_OBJECT (window), "appears-focused");
7466 
7467   if (window->frame)
7468     meta_frame_queue_draw (window->frame);
7469 }
7470 
7471 /**
7472  * meta_window_propagate_focus_appearance:
7473  * @window: the window to start propagating from
7474  * @focused: %TRUE if @window's ancestors should appear focused,
7475  *   %FALSE if they should not.
7476  *
7477  * Adjusts the value of #MetaWindow:appears-focused on @window's
7478  * ancestors (but not @window itself). If @focused is %TRUE, each of
7479  * @window's ancestors will have its %attached_focus_window field set
7480  * to the current %focus_window. If @focused if %FALSE, each of
7481  * @window's ancestors will have its %attached_focus_window field
7482  * cleared if it is currently %focus_window.
7483  */
7484 LOCAL_SYMBOL void
meta_window_propagate_focus_appearance(MetaWindow * window,gboolean focused)7485 meta_window_propagate_focus_appearance (MetaWindow *window,
7486                                         gboolean    focused)
7487 {
7488   MetaWindow *child, *parent, *focus_window;
7489 
7490   focus_window = window->display->focus_window;
7491 
7492   child = window;
7493   parent = meta_window_get_transient_for (child);
7494   while (parent && (!focused || meta_window_is_attached_dialog (child)))
7495     {
7496       gboolean child_focus_state_changed;
7497 
7498       if (focused)
7499         {
7500           if (parent->attached_focus_window == focus_window)
7501             break;
7502           child_focus_state_changed = (parent->attached_focus_window == NULL);
7503           parent->attached_focus_window = focus_window;
7504         }
7505       else
7506         {
7507           if (parent->attached_focus_window != focus_window)
7508             break;
7509           child_focus_state_changed = (parent->attached_focus_window != NULL);
7510           parent->attached_focus_window = NULL;
7511         }
7512 
7513       if (child_focus_state_changed && !parent->has_focus &&
7514           parent != window->display->expected_focus_window)
7515         {
7516           meta_window_appears_focused_changed (parent);
7517         }
7518 
7519       child = parent;
7520       parent = meta_window_get_transient_for (child);
7521     }
7522 }
7523 
7524 LOCAL_SYMBOL gboolean
meta_window_notify_focus(MetaWindow * window,XEvent * event)7525 meta_window_notify_focus (MetaWindow *window,
7526                           XEvent     *event)
7527 {
7528   /* note the event can be on either the window or the frame,
7529    * we focus the frame for shaded windows
7530    */
7531 
7532   /* The event can be FocusIn, FocusOut, or UnmapNotify.
7533    * On UnmapNotify we have to pretend it's focus out,
7534    * because we won't get a focus out if it occurs, apparently.
7535    */
7536 
7537   /* We ignore grabs, though this is questionable.
7538    * It may be better to increase the intelligence of
7539    * the focus window tracking.
7540    *
7541    * The problem is that keybindings for windows are done with
7542    * XGrabKey, which means focus_window disappears and the front of
7543    * the MRU list gets confused from what the user expects once a
7544    * keybinding is used.
7545    */
7546   meta_topic (META_DEBUG_FOCUS,
7547               "Focus %s event received on %s 0x%lx (%s) "
7548               "mode %s detail %s\n",
7549               event->type == FocusIn ? "in" :
7550               event->type == FocusOut ? "out" :
7551               event->type == UnmapNotify ? "unmap" :
7552               "???",
7553               window->desc, event->xany.window,
7554               event->xany.window == window->xwindow ?
7555               "client window" :
7556               (window->frame && event->xany.window == window->frame->xwindow) ?
7557               "frame window" :
7558               "unknown window",
7559               event->type != UnmapNotify ?
7560               meta_event_mode_to_string (event->xfocus.mode) : "n/a",
7561               event->type != UnmapNotify ?
7562               meta_event_detail_to_string (event->xfocus.detail) : "n/a");
7563 
7564   /* FIXME our pointer tracking is broken; see how
7565    * gtk+/gdk/x11/gdkevents-x11.c or XFree86/xc/programs/xterm/misc.c
7566    * handle it for the correct way.  In brief you need to track
7567    * pointer focus and regular focus, and handle EnterNotify in
7568    * PointerRoot mode with no window manager.  However as noted above,
7569    * accurate focus tracking will break things because we want to keep
7570    * windows "focused" when using keybindings on them, and also we
7571    * sometimes "focus" a window by focusing its frame or
7572    * no_focus_window; so this all needs rethinking massively.
7573    *
7574    * My suggestion is to change it so that we clearly separate
7575    * actual keyboard focus tracking using the xterm algorithm,
7576    * and muffin's "pretend" focus window, and go through all
7577    * the code and decide which one should be used in each place;
7578    * a hard bit is deciding on a policy for that.
7579    *
7580    * http://bugzilla.gnome.org/show_bug.cgi?id=90382
7581    */
7582 
7583   if ((event->type == FocusIn ||
7584        event->type == FocusOut) &&
7585       (event->xfocus.mode == NotifyGrab ||
7586        event->xfocus.mode == NotifyUngrab ||
7587        /* From WindowMaker, ignore all funky pointer root events */
7588        event->xfocus.detail > NotifyNonlinearVirtual))
7589     {
7590       meta_topic (META_DEBUG_FOCUS,
7591                   "Ignoring focus event generated by a grab or other weirdness\n");
7592       return TRUE;
7593     }
7594 
7595   if (event->type == FocusIn)
7596     {
7597       if (window->override_redirect)
7598         {
7599           window->display->focus_window = NULL;
7600           g_object_notify (G_OBJECT (window->display), "focus-window");
7601           return FALSE;
7602         }
7603 
7604       if (window != window->display->focus_window)
7605         {
7606           meta_topic (META_DEBUG_FOCUS,
7607                       "* Focus --> %s\n", window->desc);
7608           window->display->focus_window = window;
7609           window->has_focus = TRUE;
7610 
7611           /* Move to the front of the focusing workspace's MRU list.
7612            * We should only be "removing" it from the MRU list if it's
7613            * not already there.  Note that it's possible that we might
7614            * be processing this FocusIn after we've changed to a
7615            * different workspace; we should therefore update the MRU
7616            * list only if the window is actually on the active
7617            * workspace.
7618            */
7619           if (window->screen->active_workspace &&
7620               meta_window_located_on_workspace (window,
7621                                                 window->screen->active_workspace))
7622             {
7623               GList* link;
7624               link = g_list_find (window->screen->active_workspace->mru_list,
7625                                   window);
7626               g_assert (link);
7627 
7628               window->screen->active_workspace->mru_list =
7629                 g_list_remove_link (window->screen->active_workspace->mru_list,
7630                                     link);
7631               g_list_free (link);
7632 
7633               window->screen->active_workspace->mru_list =
7634                 g_list_prepend (window->screen->active_workspace->mru_list,
7635                                 window);
7636             }
7637 
7638           if (window->frame)
7639             meta_frame_queue_draw (window->frame);
7640 
7641           meta_error_trap_push (window->display);
7642           XInstallColormap (window->display->xdisplay,
7643                             window->colormap);
7644           meta_error_trap_pop (window->display);
7645 
7646 
7647           /* Ungrab click to focus button since the sync grab can interfere
7648            * with some things you might do inside the focused window, by
7649            * causing the client to get funky enter/leave events.
7650            *
7651            * The reason we usually have a passive grab on the window is
7652            * so that we can intercept clicks and raise the window in
7653            * response. For click-to-focus we don't need that since the
7654            * focused window is already raised. When raise_on_click is
7655            * FALSE we also don't need that since we don't do anything
7656            * when the window is clicked.
7657            *
7658            * There is dicussion in bugs 102209, 115072, and 461577
7659            */
7660           if (meta_prefs_get_focus_mode () == C_DESKTOP_FOCUS_MODE_CLICK ||
7661               !meta_prefs_get_raise_on_click())
7662             meta_display_ungrab_focus_window_button (window->display, window);
7663 
7664           g_signal_emit (window, window_signals[FOCUS], 0);
7665           g_object_notify (G_OBJECT (window->display), "focus-window");
7666 
7667           if (!window->attached_focus_window)
7668             meta_window_appears_focused_changed (window);
7669 
7670           meta_window_propagate_focus_appearance (window, TRUE);
7671         }
7672     }
7673   else if (event->type == FocusOut ||
7674            event->type == UnmapNotify)
7675     {
7676       if (event->type == FocusOut &&
7677           event->xfocus.detail == NotifyInferior)
7678         {
7679           /* This event means the client moved focus to a subwindow */
7680           meta_topic (META_DEBUG_FOCUS,
7681                       "Ignoring focus out on %s with NotifyInferior\n",
7682                       window->desc);
7683           return TRUE;
7684         }
7685 
7686       if (window == window->display->focus_window)
7687         {
7688           meta_topic (META_DEBUG_FOCUS,
7689                       "%s is now the previous focus window due to being focused out or unmapped\n",
7690                       window->desc);
7691 
7692           meta_topic (META_DEBUG_FOCUS,
7693                       "* Focus --> NULL (was %s)\n", window->desc);
7694 
7695           meta_window_propagate_focus_appearance (window, FALSE);
7696 
7697           window->display->focus_window = NULL;
7698           g_object_notify (G_OBJECT (window->display), "focus-window");
7699           window->has_focus = FALSE;
7700 
7701           if (!window->attached_focus_window)
7702             meta_window_appears_focused_changed (window);
7703 
7704           meta_error_trap_push (window->display);
7705           XUninstallColormap (window->display->xdisplay,
7706                               window->colormap);
7707           meta_error_trap_pop (window->display);
7708 
7709           /* Re-grab for click to focus and raise-on-click, if necessary */
7710           if (meta_prefs_get_focus_mode () == C_DESKTOP_FOCUS_MODE_CLICK ||
7711               !meta_prefs_get_raise_on_click ())
7712             meta_display_grab_focus_window_button (window->display, window);
7713        }
7714     }
7715 
7716   /* Now set _NET_ACTIVE_WINDOW hint */
7717   meta_display_update_active_window_hint (window->display);
7718 
7719   return FALSE;
7720 }
7721 
7722 static gboolean
process_property_notify(MetaWindow * window,XPropertyEvent * event)7723 process_property_notify (MetaWindow     *window,
7724                          XPropertyEvent *event)
7725 {
7726   Window xid = window->xwindow;
7727 
7728   if (meta_is_verbose ()) /* avoid looking up the name if we don't have to */
7729     {
7730       char *property_name = XGetAtomName (window->display->xdisplay,
7731                                           event->atom);
7732 
7733       meta_verbose ("Property notify on %s for %s\n",
7734                     window->desc, property_name);
7735       XFree (property_name);
7736     }
7737 
7738   if (event->atom == window->display->atom__NET_WM_USER_TIME &&
7739       window->user_time_window)
7740     {
7741         xid = window->user_time_window;
7742     }
7743 
7744   meta_window_reload_property_from_xwindow (window, xid, event->atom, FALSE);
7745 
7746   return TRUE;
7747 }
7748 
7749 static void
send_configure_notify(MetaWindow * window)7750 send_configure_notify (MetaWindow *window)
7751 {
7752   g_return_if_fail (!window->override_redirect);
7753 
7754   XEvent event;
7755 
7756   /* from twm */
7757 
7758   event.type = ConfigureNotify;
7759   event.xconfigure.display = window->display->xdisplay;
7760   event.xconfigure.event = window->xwindow;
7761   event.xconfigure.window = window->xwindow;
7762   event.xconfigure.x = window->rect.x - window->border_width;
7763   event.xconfigure.y = window->rect.y - window->border_width;
7764   if (window->frame)
7765     {
7766       if (window->withdrawn)
7767         {
7768           MetaFrameBorders borders;
7769           /* We reparent the client window and put it to the position
7770            * where the visible top-left of the frame window currently is.
7771            */
7772 
7773           meta_frame_calc_borders (window->frame, &borders);
7774 
7775           event.xconfigure.x = window->frame->rect.x + borders.invisible.left;
7776           event.xconfigure.y = window->frame->rect.y + borders.invisible.top;
7777         }
7778       else
7779         {
7780           /* Need to be in root window coordinates */
7781           event.xconfigure.x += window->frame->rect.x;
7782           event.xconfigure.y += window->frame->rect.y;
7783         }
7784     }
7785   event.xconfigure.width = window->rect.width;
7786   event.xconfigure.height = window->rect.height;
7787   event.xconfigure.border_width = window->border_width; /* requested not actual */
7788   event.xconfigure.above = None; /* FIXME */
7789   event.xconfigure.override_redirect = False;
7790 
7791   meta_topic (META_DEBUG_GEOMETRY,
7792               "Sending synthetic configure notify to %s with x: %d y: %d w: %d h: %d\n",              window->desc,
7793               event.xconfigure.x, event.xconfigure.y,
7794               event.xconfigure.width, event.xconfigure.height);
7795 
7796   meta_error_trap_push (window->display);
7797   XSendEvent (window->display->xdisplay,
7798               window->xwindow,
7799               False, StructureNotifyMask, &event);
7800   meta_error_trap_pop (window->display);
7801 }
7802 
7803 /*
7804  * meta_window_get_icon_geometry:
7805  * @window: a #MetaWindow
7806  * @rect: (out): rectangle into which to store the returned geometry.
7807  *
7808  * Gets the location of the icon corresponding to the window. The location
7809  * will be provided set by the task bar or other user interface element
7810  * displaying the icon, and is relative to the root window.
7811  *
7812  * Return value: %TRUE if the icon geometry was succesfully retrieved.
7813  */
7814 gboolean
meta_window_get_icon_geometry(MetaWindow * window,MetaRectangle * rect)7815 meta_window_get_icon_geometry (MetaWindow    *window,
7816                                MetaRectangle *rect)
7817 {
7818   g_return_val_if_fail (!window->override_redirect, FALSE);
7819 
7820   if (window->icon_geometry_set)
7821     {
7822       if (rect)
7823         *rect = window->icon_geometry;
7824 
7825       return TRUE;
7826     }
7827 
7828   return FALSE;
7829 }
7830 
7831 /**
7832  * meta_window_set_icon_geometry:
7833  * @window: a #MetaWindow
7834  * @rect: (allow-none): rectangle with the desired geometry or %NULL.
7835  *
7836  * Sets or unsets the location of the icon corresponding to the window. If
7837  * set, the location should correspond to a dock, task bar or other user
7838  * interface element displaying the icon, and is relative to the root window.
7839  */
7840 void
meta_window_set_icon_geometry(MetaWindow * window,MetaRectangle * rect)7841 meta_window_set_icon_geometry (MetaWindow    *window,
7842                                MetaRectangle *rect)
7843 {
7844   if (rect)
7845     {
7846       window->icon_geometry = *rect;
7847       window->icon_geometry_set = TRUE;
7848     }
7849   else
7850     {
7851       window->icon_geometry_set = FALSE;
7852     }
7853 }
7854 
7855 static Window
read_client_leader(MetaDisplay * display,Window xwindow)7856 read_client_leader (MetaDisplay *display,
7857                     Window       xwindow)
7858 {
7859   Window retval = None;
7860 
7861   meta_prop_get_window (display, xwindow,
7862                         display->atom_WM_CLIENT_LEADER,
7863                         &retval);
7864 
7865   return retval;
7866 }
7867 
7868 typedef struct
7869 {
7870   Window leader;
7871 } ClientLeaderData;
7872 
7873 static gboolean
find_client_leader_func(MetaWindow * ancestor,void * data)7874 find_client_leader_func (MetaWindow *ancestor,
7875                          void       *data)
7876 {
7877   ClientLeaderData *d;
7878 
7879   d = data;
7880 
7881   d->leader = read_client_leader (ancestor->display,
7882                                   ancestor->xwindow);
7883 
7884   /* keep going if no client leader found */
7885   return d->leader == None;
7886 }
7887 
7888 static void
update_sm_hints(MetaWindow * window)7889 update_sm_hints (MetaWindow *window)
7890 {
7891   Window leader;
7892 
7893   window->xclient_leader = None;
7894   window->sm_client_id = NULL;
7895 
7896   /* If not on the current window, we can get the client
7897    * leader from transient parents. If we find a client
7898    * leader, we read the SM_CLIENT_ID from it.
7899    */
7900   leader = read_client_leader (window->display, window->xwindow);
7901   if (leader == None)
7902     {
7903       ClientLeaderData d;
7904       d.leader = None;
7905       meta_window_foreach_ancestor (window, find_client_leader_func,
7906                                     &d);
7907       leader = d.leader;
7908     }
7909 
7910   if (leader != None)
7911     {
7912       char *str;
7913 
7914       window->xclient_leader = leader;
7915 
7916       if (meta_prop_get_latin1_string (window->display, leader,
7917                                        window->display->atom_SM_CLIENT_ID,
7918                                        &str))
7919         {
7920           window->sm_client_id = g_strdup (str);
7921           meta_XFree (str);
7922         }
7923     }
7924   else
7925     {
7926       meta_verbose ("Didn't find a client leader for %s\n", window->desc);
7927 
7928       if (!meta_prefs_get_disable_workarounds ())
7929         {
7930           /* Some broken apps (kdelibs fault?) set SM_CLIENT_ID on the app
7931            * instead of the client leader
7932            */
7933           char *str;
7934 
7935           str = NULL;
7936           if (meta_prop_get_latin1_string (window->display, window->xwindow,
7937                                            window->display->atom_SM_CLIENT_ID,
7938                                            &str))
7939             {
7940               if (window->sm_client_id == NULL) /* first time through */
7941                 meta_warning ("Window %s sets SM_CLIENT_ID on itself, instead of on the WM_CLIENT_LEADER window as specified in the ICCCM.\n",
7942                               window->desc);
7943 
7944               window->sm_client_id = g_strdup (str);
7945               meta_XFree (str);
7946             }
7947         }
7948     }
7949 
7950   meta_verbose ("Window %s client leader: 0x%lx SM_CLIENT_ID: '%s'\n",
7951                 window->desc, window->xclient_leader,
7952                 window->sm_client_id ? window->sm_client_id : "none");
7953 }
7954 
7955 LOCAL_SYMBOL void
meta_window_update_role(MetaWindow * window)7956 meta_window_update_role (MetaWindow *window)
7957 {
7958   char *str;
7959 
7960   g_return_if_fail (!window->override_redirect);
7961 
7962   if (window->role)
7963     free (window->role);
7964   window->role = NULL;
7965 
7966   if (meta_prop_get_latin1_string (window->display, window->xwindow,
7967                                    window->display->atom_WM_WINDOW_ROLE,
7968                                    &str))
7969     {
7970       window->role = g_strdup (str);
7971       meta_XFree (str);
7972     }
7973 
7974   meta_verbose ("Updated role of %s to '%s'\n",
7975                 window->desc, window->role ? window->role : "null");
7976 }
7977 
7978 LOCAL_SYMBOL void
meta_window_update_net_wm_type(MetaWindow * window)7979 meta_window_update_net_wm_type (MetaWindow *window)
7980 {
7981   int n_atoms;
7982   Atom *atoms;
7983   int i;
7984 
7985   window->type_atom = None;
7986   n_atoms = 0;
7987   atoms = NULL;
7988 
7989   meta_prop_get_atom_list (window->display, window->xwindow,
7990                            window->display->atom__NET_WM_WINDOW_TYPE,
7991                            &atoms, &n_atoms);
7992 
7993   i = 0;
7994   while (i < n_atoms)
7995     {
7996       /* We break as soon as we find one we recognize,
7997        * supposed to prefer those near the front of the list
7998        */
7999       if (atoms[i] == window->display->atom__NET_WM_WINDOW_TYPE_DESKTOP ||
8000           atoms[i] == window->display->atom__NET_WM_WINDOW_TYPE_DOCK ||
8001           atoms[i] == window->display->atom__NET_WM_WINDOW_TYPE_TOOLBAR ||
8002           atoms[i] == window->display->atom__NET_WM_WINDOW_TYPE_MENU ||
8003           atoms[i] == window->display->atom__NET_WM_WINDOW_TYPE_UTILITY ||
8004           atoms[i] == window->display->atom__NET_WM_WINDOW_TYPE_SPLASH ||
8005           atoms[i] == window->display->atom__NET_WM_WINDOW_TYPE_DIALOG ||
8006           atoms[i] ==
8007 	    window->display->atom__NET_WM_WINDOW_TYPE_DROPDOWN_MENU ||
8008           atoms[i] == window->display->atom__NET_WM_WINDOW_TYPE_POPUP_MENU ||
8009           atoms[i] == window->display->atom__NET_WM_WINDOW_TYPE_TOOLTIP ||
8010           atoms[i] ==
8011 	    window->display->atom__NET_WM_WINDOW_TYPE_NOTIFICATION ||
8012           atoms[i] == window->display->atom__NET_WM_WINDOW_TYPE_COMBO ||
8013           atoms[i] == window->display->atom__NET_WM_WINDOW_TYPE_DND ||
8014           atoms[i] == window->display->atom__NET_WM_WINDOW_TYPE_NORMAL)
8015         {
8016           window->type_atom = atoms[i];
8017           break;
8018         }
8019 
8020       ++i;
8021     }
8022 
8023   meta_XFree (atoms);
8024 
8025   if (meta_is_verbose ())
8026     {
8027       char *str;
8028 
8029       str = NULL;
8030       if (window->type_atom != None)
8031         {
8032           meta_error_trap_push (window->display);
8033           str = XGetAtomName (window->display->xdisplay, window->type_atom);
8034           meta_error_trap_pop (window->display);
8035         }
8036 
8037       meta_verbose ("Window %s type atom %s\n", window->desc,
8038                     str ? str : "(none)");
8039 
8040       if (str)
8041         meta_XFree (str);
8042     }
8043 
8044   meta_window_recalc_window_type (window);
8045 }
8046 
8047 void
meta_window_icon_changed(MetaWindow * window)8048 meta_window_icon_changed (MetaWindow *window)
8049 {
8050   g_clear_object (&window->icon);
8051   g_signal_emit (window, window_signals[ICON_CHANGED], 0, window);
8052 }
8053 
8054 /**
8055  * meta_window_create_icon:
8056  * @window: a #MetaWindow
8057  * @size: icon width and height
8058  *
8059  * Creates an icon for @window. This is intended to only be used for
8060  * window-backed apps.
8061  *
8062  * Return value: (transfer none): a #GdkPixbuf, or NULL.
8063  */
8064 GdkPixbuf *
meta_window_create_icon(MetaWindow * window,int size)8065 meta_window_create_icon (MetaWindow *window,
8066                          int         size)
8067 {
8068   GdkPixbuf *icon;
8069 
8070   if (window->override_redirect)
8071     return NULL;
8072 
8073   if (window->icon)
8074     return window->icon;
8075 
8076   icon = NULL;
8077 
8078   if (meta_read_icons (window->screen,
8079                        window->xwindow,
8080                        &window->icon_cache,
8081                        window->wm_hints_pixmap,
8082                        window->wm_hints_mask,
8083                        &icon,
8084                        size, size))
8085     {
8086       /* Cinnamon is handling the fallback icon case in CinnamonApp */
8087       if (icon == NULL)
8088         return NULL;
8089 
8090       if (window->icon)
8091         g_object_unref (G_OBJECT (window->icon));
8092 
8093       window->icon = icon;
8094       window->icon_size = size;
8095 
8096       return icon;
8097     }
8098 
8099   return NULL;
8100 }
8101 
8102 LOCAL_SYMBOL GList*
meta_window_get_workspaces(MetaWindow * window)8103 meta_window_get_workspaces (MetaWindow *window)
8104 {
8105   if (window->on_all_workspaces)
8106     return window->screen->workspaces;
8107   else if (window->workspace != NULL)
8108     return window->workspace->list_containing_self;
8109   else
8110     return NULL;
8111 }
8112 
8113 static void
invalidate_work_areas(MetaWindow * window)8114 invalidate_work_areas (MetaWindow *window)
8115 {
8116   GList *tmp;
8117 
8118   tmp = meta_window_get_workspaces (window);
8119 
8120   while (tmp != NULL)
8121     {
8122       meta_workspace_invalidate_work_area (tmp->data);
8123       tmp = tmp->next;
8124     }
8125 }
8126 
8127 void
meta_window_update_struts(MetaWindow * window)8128 meta_window_update_struts (MetaWindow *window)
8129 {
8130   GSList *old_struts;
8131   GSList *new_struts;
8132   GSList *old_iter, *new_iter;
8133   gulong *struts = NULL;
8134   int nitems;
8135   gboolean changed;
8136 
8137   g_return_if_fail (!window->override_redirect);
8138 
8139   meta_verbose ("Updating struts for %s\n", window->desc);
8140 
8141   old_struts = window->struts;
8142   new_struts = NULL;
8143 
8144   if (meta_prop_get_cardinal_list (window->display,
8145                                    window->xwindow,
8146                                    window->display->atom__NET_WM_STRUT_PARTIAL,
8147                                    &struts, &nitems))
8148     {
8149       if (nitems != 12)
8150         meta_verbose ("_NET_WM_STRUT_PARTIAL on %s has %d values instead "
8151                       "of 12\n",
8152                       window->desc, nitems);
8153       else
8154         {
8155           /* Pull out the strut info for each side in the hint */
8156           int i;
8157           for (i=0; i<4; i++)
8158             {
8159               MetaStrut *temp;
8160               int thickness, strut_begin, strut_end;
8161 
8162               thickness = struts[i];
8163               if (thickness == 0)
8164                 continue;
8165               strut_begin = struts[4+(i*2)];
8166               strut_end   = struts[4+(i*2)+1];
8167 
8168               temp = g_new (MetaStrut, 1);
8169               temp->side = 1 << i; /* See MetaSide def.  Matches nicely, eh? */
8170               temp->rect = window->screen->rect;
8171               switch (temp->side)
8172                 {
8173                 case META_SIDE_RIGHT:
8174                   temp->rect.x = BOX_RIGHT(temp->rect) - thickness;
8175                   /* Intentionally fall through without breaking */
8176                 case META_SIDE_LEFT:
8177                   temp->rect.width  = thickness;
8178                   temp->rect.y      = strut_begin;
8179                   temp->rect.height = strut_end - strut_begin + 1;
8180                   break;
8181                 case META_SIDE_BOTTOM:
8182                   temp->rect.y = BOX_BOTTOM(temp->rect) - thickness;
8183                   /* Intentionally fall through without breaking */
8184                 case META_SIDE_TOP:
8185                   temp->rect.height = thickness;
8186                   temp->rect.x      = strut_begin;
8187                   temp->rect.width  = strut_end - strut_begin + 1;
8188                   break;
8189                 default:
8190                   g_assert_not_reached ();
8191                 }
8192 
8193               new_struts = g_slist_prepend (new_struts, temp);
8194             }
8195 
8196           meta_verbose ("_NET_WM_STRUT_PARTIAL struts %lu %lu %lu %lu for "
8197                         "window %s\n",
8198                         struts[0], struts[1], struts[2], struts[3],
8199                         window->desc);
8200         }
8201       meta_XFree (struts);
8202     }
8203   else
8204     {
8205       meta_verbose ("No _NET_WM_STRUT property for %s\n",
8206                     window->desc);
8207     }
8208 
8209   if (!new_struts &&
8210       meta_prop_get_cardinal_list (window->display,
8211                                    window->xwindow,
8212                                    window->display->atom__NET_WM_STRUT,
8213                                    &struts, &nitems))
8214     {
8215       if (nitems != 4)
8216         meta_verbose ("_NET_WM_STRUT on %s has %d values instead of 4\n",
8217                       window->desc, nitems);
8218       else
8219         {
8220           /* Pull out the strut info for each side in the hint */
8221           int i;
8222           for (i=0; i<4; i++)
8223             {
8224               MetaStrut *temp;
8225               int thickness;
8226 
8227               thickness = struts[i];
8228               if (thickness == 0)
8229                 continue;
8230 
8231               temp = g_new (MetaStrut, 1);
8232               temp->side = 1 << i;
8233               temp->rect = window->screen->rect;
8234               switch (temp->side)
8235                 {
8236                 case META_SIDE_RIGHT:
8237                   temp->rect.x = BOX_RIGHT(temp->rect) - thickness;
8238                   /* Intentionally fall through without breaking */
8239                 case META_SIDE_LEFT:
8240                   temp->rect.width  = thickness;
8241                   break;
8242                 case META_SIDE_BOTTOM:
8243                   temp->rect.y = BOX_BOTTOM(temp->rect) - thickness;
8244                   /* Intentionally fall through without breaking */
8245                 case META_SIDE_TOP:
8246                   temp->rect.height = thickness;
8247                   break;
8248                 default:
8249                   g_assert_not_reached ();
8250                 }
8251 
8252               new_struts = g_slist_prepend (new_struts, temp);
8253             }
8254 
8255           meta_verbose ("_NET_WM_STRUT struts %lu %lu %lu %lu for window %s\n",
8256                         struts[0], struts[1], struts[2], struts[3],
8257                         window->desc);
8258         }
8259       meta_XFree (struts);
8260     }
8261   else if (!new_struts)
8262     {
8263       meta_verbose ("No _NET_WM_STRUT property for %s\n",
8264                     window->desc);
8265     }
8266 
8267   /* Determine whether old_struts and new_struts are the same */
8268   old_iter = old_struts;
8269   new_iter = new_struts;
8270   while (old_iter && new_iter)
8271     {
8272       MetaStrut *old_strut = (MetaStrut*) old_iter->data;
8273       MetaStrut *new_strut = (MetaStrut*) new_iter->data;
8274 
8275       if (old_strut->side != new_strut->side ||
8276           !meta_rectangle_equal (&old_strut->rect, &new_strut->rect))
8277         break;
8278 
8279       old_iter = old_iter->next;
8280       new_iter = new_iter->next;
8281     }
8282   changed = (old_iter != NULL || new_iter != NULL);
8283 
8284   /* Update appropriately */
8285   meta_free_gslist_and_elements (old_struts);
8286   window->struts = new_struts;
8287   if (changed)
8288     {
8289       meta_topic (META_DEBUG_WORKAREA,
8290                   "Invalidating work areas of window %s due to struts update\n",
8291                   window->desc);
8292       invalidate_work_areas (window);
8293     }
8294   else
8295     {
8296       meta_topic (META_DEBUG_WORKAREA,
8297                   "Struts on %s were unchanged\n", window->desc);
8298     }
8299 }
8300 
8301 LOCAL_SYMBOL void
meta_window_recalc_window_type(MetaWindow * window)8302 meta_window_recalc_window_type (MetaWindow *window)
8303 {
8304   recalc_window_type (window);
8305 }
8306 
8307 static void
recalc_window_type(MetaWindow * window)8308 recalc_window_type (MetaWindow *window)
8309 {
8310   MetaWindowType old_type;
8311 
8312   old_type = window->type;
8313 
8314   if (window->type_atom != None)
8315     {
8316       if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_DESKTOP)
8317         window->type = META_WINDOW_DESKTOP;
8318       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_DOCK)
8319         window->type = META_WINDOW_DOCK;
8320       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_TOOLBAR)
8321         window->type = META_WINDOW_TOOLBAR;
8322       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_MENU)
8323         window->type = META_WINDOW_MENU;
8324       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_UTILITY)
8325         window->type = META_WINDOW_UTILITY;
8326       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_SPLASH)
8327         window->type = META_WINDOW_SPLASHSCREEN;
8328       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_DIALOG)
8329         window->type = META_WINDOW_DIALOG;
8330       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_NORMAL)
8331         window->type = META_WINDOW_NORMAL;
8332       /* The below are *typically* override-redirect windows, but the spec does
8333        * not disallow using them for managed windows.
8334        */
8335       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_DROPDOWN_MENU)
8336         window->type = META_WINDOW_DROPDOWN_MENU;
8337       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_POPUP_MENU)
8338         window->type = META_WINDOW_POPUP_MENU;
8339       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_TOOLTIP)
8340         window->type = META_WINDOW_TOOLTIP;
8341       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_NOTIFICATION)
8342         window->type = META_WINDOW_NOTIFICATION;
8343       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_COMBO)
8344         window->type = META_WINDOW_COMBO;
8345       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_DND)
8346         window->type = META_WINDOW_DND;
8347       else
8348         {
8349           char *atom_name;
8350 
8351           /*
8352            * Fallback on a normal type, and print warning. Don't abort.
8353            */
8354           window->type = META_WINDOW_NORMAL;
8355 
8356           meta_error_trap_push (window->display);
8357           atom_name = XGetAtomName (window->display->xdisplay,
8358                                     window->type_atom);
8359           meta_error_trap_pop (window->display);
8360 
8361           meta_warning ("Unrecognized type atom [%s] set for %s \n",
8362                         atom_name ? atom_name : "unknown",
8363                         window->desc);
8364 
8365           if (atom_name)
8366             XFree (atom_name);
8367         }
8368     }
8369   else if (window->xtransient_for != None)
8370     {
8371       window->type = META_WINDOW_DIALOG;
8372     }
8373   else
8374     {
8375       window->type = META_WINDOW_NORMAL;
8376     }
8377 
8378   if (window->type == META_WINDOW_DIALOG &&
8379       window->wm_state_modal)
8380     window->type = META_WINDOW_MODAL_DIALOG;
8381 
8382   /* We don't want to allow override-redirect windows to have decorated-window
8383    * types since that's just confusing.
8384    */
8385   if (window->override_redirect)
8386     {
8387       switch (window->type)
8388         {
8389         /* Decorated types */
8390         case META_WINDOW_NORMAL:
8391         case META_WINDOW_DIALOG:
8392         case META_WINDOW_MODAL_DIALOG:
8393         case META_WINDOW_MENU:
8394         case META_WINDOW_UTILITY:
8395           window->type = META_WINDOW_OVERRIDE_OTHER;
8396           break;
8397         /* Undecorated types, normally not override-redirect */
8398         case META_WINDOW_DESKTOP:
8399         case META_WINDOW_DOCK:
8400         case META_WINDOW_TOOLBAR:
8401         case META_WINDOW_SPLASHSCREEN:
8402         /* Undecorated types, normally override-redirect types */
8403         case META_WINDOW_DROPDOWN_MENU:
8404         case META_WINDOW_POPUP_MENU:
8405         case META_WINDOW_TOOLTIP:
8406         case META_WINDOW_NOTIFICATION:
8407         case META_WINDOW_COMBO:
8408         case META_WINDOW_DND:
8409         /* To complete enum */
8410         case META_WINDOW_OVERRIDE_OTHER:
8411           break;
8412         }
8413     }
8414 
8415   meta_verbose ("Calculated type %u for %s, old type %u\n",
8416                 window->type, window->desc, old_type);
8417 
8418   if (old_type != window->type)
8419     {
8420       gboolean old_decorated = window->decorated;
8421       GObject  *object = G_OBJECT (window);
8422 
8423       recalc_window_features (window);
8424 
8425       if (!window->override_redirect)
8426 	set_net_wm_state (window);
8427 
8428       /* Update frame */
8429       if (window->decorated)
8430         meta_window_ensure_frame (window);
8431       else
8432         meta_window_destroy_frame (window);
8433 
8434       /* update stacking constraints */
8435       meta_window_update_layer (window);
8436 
8437       meta_window_grab_keys (window);
8438 
8439       g_object_freeze_notify (object);
8440 
8441       if (old_decorated != window->decorated)
8442         g_object_notify (object, "decorated");
8443 
8444       g_object_notify (object, "window-type");
8445 
8446       g_object_thaw_notify (object);
8447     }
8448 }
8449 
8450 void
meta_window_frame_size_changed(MetaWindow * window)8451 meta_window_frame_size_changed (MetaWindow *window)
8452 {
8453   if (window->frame)
8454     meta_frame_clear_cached_borders (window->frame);
8455 }
8456 
8457 static void
set_allowed_actions_hint(MetaWindow * window)8458 set_allowed_actions_hint (MetaWindow *window)
8459 {
8460 #define MAX_N_ACTIONS 12
8461   unsigned long data[MAX_N_ACTIONS];
8462   int i;
8463 
8464   i = 0;
8465   if (window->has_move_func)
8466     {
8467       data[i] = window->display->atom__NET_WM_ACTION_MOVE;
8468       ++i;
8469     }
8470   if (window->has_resize_func)
8471     {
8472       data[i] = window->display->atom__NET_WM_ACTION_RESIZE;
8473       ++i;
8474     }
8475   if (window->has_fullscreen_func)
8476     {
8477       data[i] = window->display->atom__NET_WM_ACTION_FULLSCREEN;
8478       ++i;
8479     }
8480   if (window->has_minimize_func)
8481     {
8482       data[i] = window->display->atom__NET_WM_ACTION_MINIMIZE;
8483       ++i;
8484     }
8485   if (window->has_shade_func)
8486     {
8487       data[i] = window->display->atom__NET_WM_ACTION_SHADE;
8488       ++i;
8489     }
8490   /* sticky according to EWMH is different from muffin's sticky;
8491    * muffin doesn't support EWMH sticky
8492    */
8493   if (window->has_maximize_func)
8494     {
8495       data[i] = window->display->atom__NET_WM_ACTION_MAXIMIZE_HORZ;
8496       ++i;
8497       data[i] = window->display->atom__NET_WM_ACTION_MAXIMIZE_VERT;
8498       ++i;
8499     }
8500   /* We always allow this */
8501   data[i] = window->display->atom__NET_WM_ACTION_CHANGE_DESKTOP;
8502   ++i;
8503   if (window->has_close_func)
8504     {
8505       data[i] = window->display->atom__NET_WM_ACTION_CLOSE;
8506       ++i;
8507     }
8508 
8509   /* I guess we always allow above/below operations */
8510   data[i] = window->display->atom__NET_WM_ACTION_ABOVE;
8511   ++i;
8512   data[i] = window->display->atom__NET_WM_ACTION_BELOW;
8513   ++i;
8514 
8515   g_assert (i <= MAX_N_ACTIONS);
8516 
8517   meta_verbose ("Setting _NET_WM_ALLOWED_ACTIONS with %d atoms\n", i);
8518 
8519   meta_error_trap_push (window->display);
8520   XChangeProperty (window->display->xdisplay, window->xwindow,
8521                    window->display->atom__NET_WM_ALLOWED_ACTIONS,
8522                    XA_ATOM,
8523                    32, PropModeReplace, (guchar*) data, i);
8524   meta_error_trap_pop (window->display);
8525 #undef MAX_N_ACTIONS
8526 }
8527 
8528 LOCAL_SYMBOL void
meta_window_recalc_features(MetaWindow * window)8529 meta_window_recalc_features (MetaWindow *window)
8530 {
8531   recalc_window_features (window);
8532 }
8533 
8534 static void
recalc_window_features(MetaWindow * window)8535 recalc_window_features (MetaWindow *window)
8536 {
8537   gboolean old_has_close_func;
8538   gboolean old_has_minimize_func;
8539   gboolean old_has_move_func;
8540   gboolean old_has_resize_func;
8541   gboolean old_has_shade_func;
8542   gboolean old_always_sticky;
8543   gboolean old_skip_taskbar;
8544 
8545   old_has_close_func = window->has_close_func;
8546   old_has_minimize_func = window->has_minimize_func;
8547   old_has_move_func = window->has_move_func;
8548   old_has_resize_func = window->has_resize_func;
8549   old_has_shade_func = window->has_shade_func;
8550   old_always_sticky = window->always_sticky;
8551   old_skip_taskbar = window->skip_taskbar;
8552 
8553   /* Use MWM hints initially */
8554   window->decorated = window->mwm_decorated;
8555   window->border_only = window->mwm_border_only;
8556   window->has_close_func = window->mwm_has_close_func;
8557   window->has_minimize_func = window->mwm_has_minimize_func;
8558   window->has_maximize_func = window->mwm_has_maximize_func;
8559   window->has_move_func = window->mwm_has_move_func;
8560 
8561   window->has_resize_func = TRUE;
8562 
8563   /* If min_size == max_size, then don't allow resize */
8564   if (window->size_hints.min_width == window->size_hints.max_width &&
8565       window->size_hints.min_height == window->size_hints.max_height)
8566     window->has_resize_func = FALSE;
8567   else if (!window->mwm_has_resize_func)
8568     {
8569       /* We ignore mwm_has_resize_func because WM_NORMAL_HINTS is the
8570        * authoritative source for that info. Some apps such as mplayer or
8571        * xine disable resize via MWM but not WM_NORMAL_HINTS, but that
8572        * leads to e.g. us not fullscreening their windows.  Apps that set
8573        * MWM but not WM_NORMAL_HINTS are basically broken. We complain
8574        * about these apps but make them work.
8575        */
8576 
8577       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",
8578                     window->desc,
8579                     window->size_hints.min_width,
8580                     window->size_hints.min_height,
8581                     window->size_hints.max_width,
8582                     window->size_hints.max_height);
8583     }
8584 
8585   window->has_shade_func = TRUE;
8586   window->has_fullscreen_func = TRUE;
8587 
8588   window->always_sticky = FALSE;
8589 
8590   /* Semantic category overrides the MWM hints */
8591   if (window->type == META_WINDOW_TOOLBAR)
8592     window->decorated = FALSE;
8593 
8594   if (meta_window_is_attached_dialog (window))
8595     window->border_only = TRUE;
8596 
8597   if (window->type == META_WINDOW_DESKTOP ||
8598       window->type == META_WINDOW_DOCK ||
8599       window->override_redirect)
8600     window->always_sticky = TRUE;
8601 
8602   if (window->override_redirect ||
8603       meta_window_get_frame_type (window) == META_FRAME_TYPE_LAST)
8604     {
8605       window->decorated = FALSE;
8606       window->has_close_func = FALSE;
8607       window->has_shade_func = FALSE;
8608 
8609       /* FIXME this keeps panels and things from using
8610        * NET_WM_MOVERESIZE; the problem is that some
8611        * panels (edge panels) have fixed possible locations,
8612        * and others ("floating panels") do not.
8613        *
8614        * Perhaps we should require edge panels to explicitly
8615        * disable movement?
8616        */
8617       window->has_move_func = FALSE;
8618       window->has_resize_func = FALSE;
8619     }
8620 
8621   if (window->type != META_WINDOW_NORMAL)
8622     {
8623       window->has_minimize_func = FALSE;
8624       window->has_maximize_func = FALSE;
8625       window->has_fullscreen_func = FALSE;
8626     }
8627 
8628   if (!window->has_resize_func)
8629     {
8630       window->has_maximize_func = FALSE;
8631 
8632       /* don't allow fullscreen if we can't resize, unless the size
8633        * is entire screen size (kind of broken, because we
8634        * actually fullscreen to monitor size not screen size)
8635        */
8636       if (window->size_hints.min_width == window->screen->rect.width &&
8637           window->size_hints.min_height == window->screen->rect.height)
8638         ; /* leave fullscreen available */
8639       else
8640         window->has_fullscreen_func = FALSE;
8641     }
8642 
8643   /* We leave fullscreen windows decorated, just push the frame outside
8644    * the screen. This avoids flickering to unparent them.
8645    *
8646    * Note that setting has_resize_func = FALSE here must come after the
8647    * above code that may disable fullscreen, because if the window
8648    * is not resizable purely due to fullscreen, we don't want to
8649    * disable fullscreen mode.
8650    */
8651   if (window->fullscreen)
8652     {
8653       window->has_shade_func = FALSE;
8654       window->has_move_func = FALSE;
8655       window->has_resize_func = FALSE;
8656       window->has_maximize_func = FALSE;
8657     }
8658 
8659   if (window->has_maximize_func)
8660     {
8661       MetaRectangle work_area;
8662       MetaFrameBorders borders;
8663       int min_frame_width, min_frame_height;
8664 
8665       meta_window_get_work_area_current_monitor (window, &work_area);
8666       meta_frame_calc_borders (window->frame, &borders);
8667 
8668       min_frame_width = window->size_hints.min_width + borders.visible.left + borders.visible.right;
8669       min_frame_height = window->size_hints.min_height + borders.visible.top + borders.visible.bottom;
8670 
8671       if (min_frame_width >= work_area.width ||
8672           min_frame_height >= work_area.height)
8673         window->has_maximize_func = FALSE;
8674     }
8675 
8676   meta_topic (META_DEBUG_WINDOW_OPS,
8677               "Window %s fullscreen = %d not resizable, maximizable = %d fullscreenable = %d min size %dx%d max size %dx%d\n",
8678               window->desc,
8679               window->fullscreen,
8680               window->has_maximize_func, window->has_fullscreen_func,
8681               window->size_hints.min_width,
8682               window->size_hints.min_height,
8683               window->size_hints.max_width,
8684               window->size_hints.max_height);
8685 
8686   /* no shading if not decorated */
8687   if (!window->decorated || window->border_only)
8688     window->has_shade_func = FALSE;
8689 
8690   window->skip_taskbar = FALSE;
8691   window->skip_pager = FALSE;
8692 
8693   if (window->wm_state_skip_taskbar)
8694     window->skip_taskbar = TRUE;
8695 
8696   if (window->wm_state_skip_pager)
8697     window->skip_pager = TRUE;
8698 
8699   switch (window->type)
8700     {
8701       /* Force skip taskbar/pager on these window types */
8702     case META_WINDOW_DESKTOP:
8703     case META_WINDOW_DOCK:
8704     case META_WINDOW_TOOLBAR:
8705     case META_WINDOW_MENU:
8706     case META_WINDOW_UTILITY:
8707     case META_WINDOW_SPLASHSCREEN:
8708     case META_WINDOW_DROPDOWN_MENU:
8709     case META_WINDOW_POPUP_MENU:
8710     case META_WINDOW_TOOLTIP:
8711     case META_WINDOW_NOTIFICATION:
8712     case META_WINDOW_COMBO:
8713     case META_WINDOW_DND:
8714     case META_WINDOW_OVERRIDE_OTHER:
8715       window->skip_taskbar = TRUE;
8716       window->skip_pager = TRUE;
8717       break;
8718 
8719     case META_WINDOW_DIALOG:
8720     case META_WINDOW_MODAL_DIALOG:
8721       /* only skip taskbar if we have a real transient parent */
8722       if (window->xtransient_for != None &&
8723           window->xtransient_for != window->screen->xroot)
8724         window->skip_taskbar = TRUE;
8725       break;
8726 
8727     case META_WINDOW_NORMAL:
8728       break;
8729     }
8730 
8731   meta_topic (META_DEBUG_WINDOW_OPS,
8732               "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",
8733               window->desc,
8734               window->decorated,
8735               window->border_only,
8736               window->has_close_func,
8737               window->has_minimize_func,
8738               window->has_maximize_func,
8739               window->has_move_func,
8740               window->has_shade_func,
8741               window->skip_taskbar,
8742               window->skip_pager);
8743 
8744   /* FIXME:
8745    * Lame workaround for recalc_window_features
8746    * being used overzealously. The fix is to
8747    * only recalc_window_features when something
8748    * has actually changed.
8749    */
8750   if (window->constructing                               ||
8751       old_has_close_func != window->has_close_func       ||
8752       old_has_minimize_func != window->has_minimize_func ||
8753       old_has_move_func != window->has_move_func         ||
8754       old_has_resize_func != window->has_resize_func     ||
8755       old_has_shade_func != window->has_shade_func       ||
8756       old_always_sticky != window->always_sticky)
8757     set_allowed_actions_hint (window);
8758 
8759   if (window->has_resize_func != old_has_resize_func)
8760     g_object_notify (G_OBJECT (window), "resizeable");
8761 
8762   meta_window_frame_size_changed (window);
8763 
8764   if (old_skip_taskbar != window->skip_taskbar)
8765     g_signal_emit_by_name (window->screen, "window-skip-taskbar-changed", window);
8766 
8767   /* FIXME perhaps should ensure if we don't have a shade func,
8768    * we aren't shaded, etc.
8769    */
8770 }
8771 
8772 static void
menu_callback(MetaWindowMenu * menu,Display * xdisplay,Window client_xwindow,guint32 timestamp,MetaMenuOp op,int workspace_index,gpointer data)8773 menu_callback (MetaWindowMenu *menu,
8774                Display        *xdisplay,
8775                Window          client_xwindow,
8776                guint32         timestamp,
8777                MetaMenuOp      op,
8778                int             workspace_index,
8779                gpointer        data)
8780 {
8781   MetaDisplay *display;
8782   MetaWindow *window;
8783   MetaWorkspace *workspace;
8784 
8785   display = meta_display_for_x_display (xdisplay);
8786   window = meta_display_lookup_x_window (display, client_xwindow);
8787   workspace = NULL;
8788 
8789   if (window != NULL) /* window can be NULL */
8790     {
8791       meta_verbose ("Menu op %u on %s\n", op, window->desc);
8792 
8793       switch (op)
8794         {
8795         case META_MENU_OP_NONE:
8796           /* nothing */
8797           break;
8798 
8799         case META_MENU_OP_DELETE:
8800           meta_window_delete (window, timestamp);
8801           break;
8802 
8803         case META_MENU_OP_MINIMIZE:
8804           meta_window_minimize (window);
8805           break;
8806 
8807         case META_MENU_OP_UNMAXIMIZE:
8808           meta_window_unmaximize (window,
8809                                   META_MAXIMIZE_HORIZONTAL |
8810                                   META_MAXIMIZE_VERTICAL);
8811           break;
8812 
8813         case META_MENU_OP_MAXIMIZE:
8814           meta_window_maximize (window,
8815                                 META_MAXIMIZE_HORIZONTAL |
8816                                 META_MAXIMIZE_VERTICAL);
8817           break;
8818 
8819         case META_MENU_OP_UNSHADE:
8820           meta_window_unshade (window, timestamp);
8821           break;
8822 
8823         case META_MENU_OP_SHADE:
8824           meta_window_shade (window, timestamp);
8825           break;
8826 
8827         case META_MENU_OP_MOVE_LEFT:
8828           workspace = meta_workspace_get_neighbor (window->screen->active_workspace,
8829                                                    META_MOTION_LEFT);
8830           break;
8831 
8832         case META_MENU_OP_MOVE_RIGHT:
8833           workspace = meta_workspace_get_neighbor (window->screen->active_workspace,
8834                                                    META_MOTION_RIGHT);
8835           break;
8836 
8837         case META_MENU_OP_MOVE_UP:
8838           workspace = meta_workspace_get_neighbor (window->screen->active_workspace,
8839                                                    META_MOTION_UP);
8840           break;
8841 
8842         case META_MENU_OP_MOVE_DOWN:
8843           workspace = meta_workspace_get_neighbor (window->screen->active_workspace,
8844                                                    META_MOTION_DOWN);
8845           break;
8846 
8847         case META_MENU_OP_WORKSPACES:
8848           workspace = meta_screen_get_workspace_by_index (window->screen,
8849                                                           workspace_index);
8850           break;
8851 
8852         case META_MENU_OP_MOVE_NEW:
8853           workspace = meta_screen_append_new_workspace (window->screen, FALSE, timestamp);
8854           GSettings *cinnamon = g_settings_new ("org.cinnamon");
8855           g_settings_set_int (cinnamon, "number-workspaces", g_list_length (window->screen->workspaces));
8856           g_object_unref (cinnamon);
8857           break;
8858 
8859         case META_MENU_OP_STICK:
8860           meta_window_stick (window);
8861           break;
8862 
8863         case META_MENU_OP_UNSTICK:
8864           meta_window_unstick (window);
8865           break;
8866 
8867         case META_MENU_OP_ABOVE:
8868         case META_MENU_OP_UNABOVE:
8869           if (window->wm_state_above == FALSE)
8870             meta_window_make_above (window);
8871           else
8872             meta_window_unmake_above (window);
8873           break;
8874 
8875         case META_MENU_OP_MOVE:
8876           meta_window_begin_grab_op (window,
8877                                      META_GRAB_OP_KEYBOARD_MOVING,
8878                                      TRUE,
8879                                      timestamp);
8880           break;
8881 
8882         case META_MENU_OP_RESIZE:
8883           if (window->tile_mode != META_TILE_NONE)
8884             {
8885               window->resize_tile_mode = window->tile_mode;
8886               window->resizing_tile_type = window->tile_type;
8887             }
8888 
8889           meta_window_begin_grab_op (window,
8890                                      META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN,
8891                                      TRUE,
8892                                      timestamp);
8893           break;
8894 
8895         case META_MENU_OP_RECOVER:
8896           meta_window_shove_titlebar_onscreen (window);
8897           break;
8898 
8899         default:
8900           meta_warning (G_STRLOC": Unknown window op\n");
8901           break;
8902         }
8903 
8904       if (workspace)
8905 	{
8906 	  meta_window_change_workspace (window,
8907 					workspace);
8908 #if 0
8909 	  meta_workspace_activate (workspace);
8910 	  meta_window_raise (window);
8911 #endif
8912 	}
8913     }
8914   else
8915     {
8916       meta_verbose ("Menu callback on nonexistent window\n");
8917     }
8918 
8919   if (display->window_menu == menu)
8920     {
8921       display->window_menu = NULL;
8922       display->window_with_menu = NULL;
8923     }
8924 
8925   meta_ui_window_menu_free (menu);
8926 }
8927 
8928 LOCAL_SYMBOL void
meta_window_show_menu(MetaWindow * window,int root_x,int root_y,int button,guint32 timestamp)8929 meta_window_show_menu (MetaWindow *window,
8930                        int         root_x,
8931                        int         root_y,
8932                        int         button,
8933                        guint32     timestamp)
8934 {
8935   MetaMenuOp ops;
8936   MetaMenuOp insensitive;
8937   MetaWindowMenu *menu;
8938   MetaWorkspaceLayout layout;
8939   int n_workspaces;
8940   // gboolean ltr;
8941 
8942   g_return_if_fail (!window->override_redirect);
8943 
8944   if (window->display->window_menu)
8945     {
8946       meta_ui_window_menu_free (window->display->window_menu);
8947       window->display->window_menu = NULL;
8948       window->display->window_with_menu = NULL;
8949     }
8950 
8951   ops = META_MENU_OP_NONE;
8952   insensitive = META_MENU_OP_NONE;
8953 
8954   //ops |= (META_MENU_OP_DELETE | META_MENU_OP_MINIMIZE | META_MENU_OP_MOVE | META_MENU_OP_RESIZE | META_MENU_OP_MOVE_NEW);
8955   ops |= (META_MENU_OP_DELETE | META_MENU_OP_MINIMIZE | META_MENU_OP_MOVE | META_MENU_OP_RESIZE);
8956 
8957   if (!meta_window_is_client_decorated (window) &&
8958       !meta_window_titlebar_is_onscreen (window) &&
8959       window->type != META_WINDOW_DOCK &&
8960       window->type != META_WINDOW_DESKTOP)
8961     ops |= META_MENU_OP_RECOVER;
8962 
8963   if (!meta_prefs_get_workspaces_only_on_primary () ||
8964       meta_window_is_on_primary_monitor (window))
8965     {
8966       n_workspaces = meta_screen_get_n_workspaces (window->screen);
8967 
8968       if (n_workspaces > 1)
8969         ops |= META_MENU_OP_WORKSPACES;
8970 
8971       meta_screen_calc_workspace_layout (window->screen,
8972                                          n_workspaces,
8973                                          meta_workspace_index ( window->screen->active_workspace),
8974                                          &layout);
8975 
8976       // if (!window->on_all_workspaces)
8977       //   {
8978       //     ltr = meta_ui_get_direction() == META_UI_DIRECTION_LTR;
8979 
8980       //     if (layout.current_col > 0)
8981       //       ops |= ltr ? META_MENU_OP_MOVE_LEFT : META_MENU_OP_MOVE_RIGHT;
8982       //     if ((layout.current_col < layout.cols - 1) &&
8983       //         (layout.current_row * layout.cols + (layout.current_col + 1) < n_workspaces))
8984       //       ops |= ltr ? META_MENU_OP_MOVE_RIGHT : META_MENU_OP_MOVE_LEFT;
8985       //     if (layout.current_row > 0)
8986       //       ops |= META_MENU_OP_MOVE_UP;
8987       //     if ((layout.current_row < layout.rows - 1) &&
8988       //         ((layout.current_row + 1) * layout.cols + layout.current_col < n_workspaces))
8989       //       ops |= META_MENU_OP_MOVE_DOWN;
8990       //   }
8991 
8992       meta_screen_free_workspace_layout (&layout);
8993 
8994       ops |= META_MENU_OP_UNSTICK;
8995       ops |= META_MENU_OP_STICK;
8996     }
8997 
8998   if (META_WINDOW_MAXIMIZED (window))
8999     ops |= META_MENU_OP_UNMAXIMIZE;
9000   else
9001     ops |= META_MENU_OP_MAXIMIZE;
9002 
9003 #if 0
9004   if (window->shaded)
9005     ops |= META_MENU_OP_UNSHADE;
9006   else
9007     ops |= META_MENU_OP_SHADE;
9008 #endif
9009 
9010   if (window->wm_state_above)
9011     ops |= META_MENU_OP_UNABOVE;
9012   else
9013     ops |= META_MENU_OP_ABOVE;
9014 
9015   if (!window->has_maximize_func)
9016     insensitive |= META_MENU_OP_UNMAXIMIZE | META_MENU_OP_MAXIMIZE;
9017 
9018   if (!window->has_minimize_func)
9019     insensitive |= META_MENU_OP_MINIMIZE;
9020 
9021   if (!window->has_close_func)
9022     insensitive |= META_MENU_OP_DELETE;
9023 
9024   if (!window->has_shade_func)
9025     insensitive |= META_MENU_OP_SHADE | META_MENU_OP_UNSHADE;
9026 
9027   if (!META_WINDOW_ALLOWS_MOVE (window))
9028     insensitive |= META_MENU_OP_MOVE;
9029 
9030   if (!META_WINDOW_ALLOWS_RESIZE (window))
9031     insensitive |= META_MENU_OP_RESIZE;
9032 
9033    if (window->always_sticky)
9034      insensitive |= META_MENU_OP_STICK | META_MENU_OP_UNSTICK | META_MENU_OP_WORKSPACES;
9035 
9036   if ((window->type == META_WINDOW_DESKTOP) ||
9037       (window->type == META_WINDOW_DOCK) ||
9038       (window->type == META_WINDOW_SPLASHSCREEN))
9039     insensitive |= META_MENU_OP_ABOVE | META_MENU_OP_UNABOVE;
9040 
9041   /* If all operations are disabled, just quit without showing the menu.
9042    * This is the case, for example, with META_WINDOW_DESKTOP windows.
9043    */
9044   if ((ops & ~insensitive) == 0)
9045     return;
9046 
9047   menu =
9048     meta_ui_window_menu_new (window->screen->ui,
9049                              window->xwindow,
9050                              ops,
9051                              insensitive,
9052                              meta_window_get_net_wm_desktop (window),
9053                              meta_screen_get_n_workspaces (window->screen),
9054                              menu_callback,
9055                              NULL);
9056 
9057   window->display->window_menu = menu;
9058   window->display->window_with_menu = window;
9059 
9060   meta_verbose ("Popping up window menu for %s\n", window->desc);
9061 
9062   meta_ui_window_menu_popup (menu, root_x, root_y, button, timestamp);
9063 }
9064 
9065 LOCAL_SYMBOL void
meta_window_shove_titlebar_onscreen(MetaWindow * window)9066 meta_window_shove_titlebar_onscreen (MetaWindow *window)
9067 {
9068   MetaRectangle  outer_rect;
9069   GList         *onscreen_region;
9070   int            horiz_amount, vert_amount;
9071   int            newx, newy;
9072 
9073   g_return_if_fail (!window->override_redirect);
9074 
9075   /* If there's no titlebar, don't bother */
9076   if (!window->frame)
9077     return;
9078 
9079   /* Get the basic info we need */
9080   meta_window_get_outer_rect (window, &outer_rect);
9081   onscreen_region = window->screen->active_workspace->screen_region;
9082 
9083   /* Extend the region (just in case the window is too big to fit on the
9084    * screen), then shove the window on screen, then return the region to
9085    * normal.
9086    */
9087   horiz_amount = outer_rect.width;
9088   vert_amount  = outer_rect.height;
9089   meta_rectangle_expand_region (onscreen_region,
9090                                 horiz_amount,
9091                                 horiz_amount,
9092                                 0,
9093                                 vert_amount);
9094   meta_rectangle_shove_into_region(onscreen_region,
9095                                    FIXED_DIRECTION_X,
9096                                    &outer_rect);
9097   meta_rectangle_expand_region (onscreen_region,
9098                                 -horiz_amount,
9099                                 -horiz_amount,
9100                                 0,
9101                                 -vert_amount);
9102 
9103   newx = outer_rect.x + window->frame->child_x;
9104   newy = outer_rect.y + window->frame->child_y;
9105   meta_window_move_resize (window,
9106                            FALSE,
9107                            newx,
9108                            newy,
9109                            window->rect.width,
9110                            window->rect.height);
9111 }
9112 
9113 LOCAL_SYMBOL gboolean
meta_window_titlebar_is_onscreen(MetaWindow * window)9114 meta_window_titlebar_is_onscreen (MetaWindow *window)
9115 {
9116   MetaRectangle  titlebar_rect;
9117   GList         *onscreen_region;
9118   gboolean       is_onscreen;
9119 
9120   const int min_height_needed  = 8;
9121   const int min_width_percent  = 0.5;
9122   const int min_width_absolute = 50;
9123 
9124   /* Titlebar can't be offscreen if there is no titlebar... */
9125   if (!window->frame)
9126     return FALSE;
9127 
9128   /* Get the rectangle corresponding to the titlebar */
9129   meta_window_get_outer_rect (window, &titlebar_rect);
9130   titlebar_rect.height = window->frame->child_y;
9131 
9132   /* Run through the spanning rectangles for the screen and see if one of
9133    * them overlaps with the titlebar sufficiently to consider it onscreen.
9134    */
9135   is_onscreen = FALSE;
9136   onscreen_region = window->screen->active_workspace->screen_region;
9137   while (onscreen_region)
9138     {
9139       MetaRectangle *spanning_rect = onscreen_region->data;
9140       MetaRectangle overlap;
9141 
9142       meta_rectangle_intersect (&titlebar_rect, spanning_rect, &overlap);
9143       if (overlap.height > MIN (titlebar_rect.height, min_height_needed) &&
9144           overlap.width  > MIN (titlebar_rect.width * min_width_percent,
9145                                 min_width_absolute))
9146         {
9147           is_onscreen = TRUE;
9148           break;
9149         }
9150 
9151       onscreen_region = onscreen_region->next;
9152     }
9153 
9154   return is_onscreen;
9155 }
9156 
9157 static double
timeval_to_ms(const GTimeVal * timeval)9158 timeval_to_ms (const GTimeVal *timeval)
9159 {
9160   return (timeval->tv_sec * G_USEC_PER_SEC + timeval->tv_usec) / 1000.0;
9161 }
9162 
9163 static double
time_diff(const GTimeVal * first,const GTimeVal * second)9164 time_diff (const GTimeVal *first,
9165 	   const GTimeVal *second)
9166 {
9167   double first_ms = timeval_to_ms (first);
9168   double second_ms = timeval_to_ms (second);
9169 
9170   return first_ms - second_ms;
9171 }
9172 
9173 static gboolean
check_moveresize_frequency(MetaWindow * window,gdouble * remaining)9174 check_moveresize_frequency (MetaWindow *window,
9175 			    gdouble    *remaining)
9176 {
9177   GTimeVal current_time;
9178   const double max_resizes_per_second = 25.0;
9179   const double ms_between_resizes = 1000.0 / max_resizes_per_second;
9180   double elapsed;
9181 
9182   g_get_current_time (&current_time);
9183 
9184 #ifdef HAVE_XSYNC
9185   /* If we are throttling via _NET_WM_SYNC_REQUEST, we don't need
9186    * an artificial timeout-based throttled */
9187   if (!window->disable_sync &&
9188       window->sync_request_alarm != None)
9189     return TRUE;
9190 #endif /* HAVE_XSYNC */
9191 
9192   elapsed = time_diff (&current_time, &window->display->grab_last_moveresize_time);
9193 
9194   if (elapsed >= 0.0 && elapsed < ms_between_resizes)
9195     {
9196       meta_topic (META_DEBUG_RESIZING,
9197                   "Delaying move/resize as only %g of %g ms elapsed\n",
9198                   elapsed, ms_between_resizes);
9199 
9200       if (remaining)
9201         *remaining = (ms_between_resizes - elapsed);
9202 
9203       return FALSE;
9204     }
9205 
9206     meta_topic (META_DEBUG_RESIZING,
9207                 " Checked moveresize freq, allowing move/resize now (%g of %g seconds elapsed)\n",
9208                 elapsed / 1000.0, 1.0 / max_resizes_per_second);
9209 
9210     return TRUE;
9211 }
9212 
9213 static gboolean
update_move_timeout(gpointer data)9214 update_move_timeout (gpointer data)
9215 {
9216   MetaWindow *window = data;
9217 
9218   update_move (window,
9219                window->display->grab_last_user_action_was_snap,
9220                window->snap_queued,
9221                window->display->grab_latest_motion_x,
9222                window->display->grab_latest_motion_y);
9223 
9224   return FALSE;
9225 }
9226 
9227 guint
meta_window_get_current_zone(MetaWindow * window,MetaRectangle monitor,MetaRectangle work_area,int x,int y,int zone_threshold)9228 meta_window_get_current_zone (MetaWindow   *window,
9229                   MetaRectangle monitor,
9230                   MetaRectangle work_area,
9231                   int           x,
9232                   int           y,
9233                   int           zone_threshold)
9234 {
9235     TileZone edge_zone = 0;
9236     guint zone = ZONE_NONE;
9237     /* First, establish edges, with top and bottom first,
9238        so they take priority in the corners                  */
9239     if (y >= monitor.y && y <= work_area.y + zone_threshold)
9240         edge_zone |= ZONE_TOP;
9241     if (y >= (work_area.y + work_area.height - zone_threshold) &&
9242              y < (monitor.y + monitor.height))
9243         edge_zone |= ZONE_BOTTOM;
9244     if (x >= monitor.x && x < (work_area.x + zone_threshold))
9245         edge_zone |= ZONE_LEFT;
9246     if (x >= (work_area.x + work_area.width - zone_threshold) &&
9247              x < (monitor.x + monitor.width))
9248         edge_zone |= ZONE_RIGHT;
9249 
9250     /* Now for each edge zone, we can figure out the specific subzone we're in */
9251 
9252     /* split the screen into a 4 x 4 grid.. the middle 2 boxes along each edge
9253        are considered a single zone though, so effectively we'll have 3 zones per
9254        edge */
9255 
9256     switch (edge_zone) {
9257         case ZONE_ULC:
9258             if (meta_window_can_tile_corner (window))
9259                 zone = ZONE_4;
9260             break;
9261         case ZONE_LLC:
9262             if (meta_window_can_tile_corner (window))
9263                 zone = ZONE_7;
9264             break;
9265         case ZONE_URC:
9266             if (meta_window_can_tile_corner (window))
9267                 zone = ZONE_5;
9268             break;
9269         case ZONE_LRC:
9270             if (meta_window_can_tile_corner (window))
9271                 zone = ZONE_6;
9272             break;
9273         case ZONE_TOP:
9274             if (meta_prefs_get_tile_maximize() || window->maybe_retile_maximize)
9275               {
9276                 if (meta_window_can_tile_maximized(window))
9277                     zone = ZONE_0;
9278               }
9279             else if (meta_window_can_tile_top_bottom (window))
9280                 zone = ZONE_0;
9281             break;
9282         case ZONE_BOTTOM:
9283             if (meta_window_can_tile_top_bottom (window))
9284                 zone = ZONE_1;
9285             break;
9286         case ZONE_LEFT:
9287             if (meta_window_can_tile_side_by_side (window))
9288                 zone = ZONE_2;
9289             break;
9290         case ZONE_RIGHT:
9291             if (meta_window_can_tile_side_by_side (window))
9292                 zone = ZONE_3;
9293             break;
9294         default:
9295             zone = ZONE_NONE;
9296             break;
9297     }
9298     return zone;
9299 }
9300 
9301 static unsigned int
get_mask_from_snap_keysym(MetaWindow * window)9302 get_mask_from_snap_keysym (MetaWindow *window)
9303 {
9304     unsigned int *pref = meta_prefs_get_snap_modifier ();
9305 
9306     return XkbKeysymToModifiers(window->display->xdisplay, pref[0]);
9307 }
9308 
9309 static inline void
get_size_limits(const MetaWindow * window,const MetaFrameBorders * borders,gboolean include_frame,MetaRectangle * min_size,MetaRectangle * max_size)9310 get_size_limits (const MetaWindow       *window,
9311                  const MetaFrameBorders *borders,
9312                        gboolean          include_frame,
9313                        MetaRectangle    *min_size,
9314                        MetaRectangle    *max_size)
9315 {
9316   /* We pack the results into MetaRectangle structs just for convienience; we
9317    * don't actually use the position of those rects.
9318    */
9319   min_size->width  = window->size_hints.min_width;
9320   min_size->height = window->size_hints.min_height;
9321   max_size->width  = window->size_hints.max_width;
9322   max_size->height = window->size_hints.max_height;
9323 
9324   if (include_frame)
9325     {
9326       int fw = borders->visible.left + borders->visible.right;
9327       int fh = borders->visible.top + borders->visible.bottom;
9328 
9329       min_size->width  += fw;
9330       min_size->height += fh;
9331       /* Do check to avoid overflow (e.g. max_size->width & max_size->height
9332        * may be set to G_MAXINT by meta_set_normal_hints()).
9333        */
9334       if (max_size->width < (G_MAXINT - fw))
9335         max_size->width += fw;
9336       else
9337         max_size->width = G_MAXINT;
9338       if (max_size->height < (G_MAXINT - fh))
9339         max_size->height += fh;
9340       else
9341         max_size->height = G_MAXINT;
9342     }
9343 }
9344 
9345 static void
update_move(MetaWindow * window,gboolean legacy_snap,gboolean snap_mode,int x,int y)9346 update_move (MetaWindow  *window,
9347              gboolean     legacy_snap,
9348              gboolean     snap_mode,
9349              int          x,
9350              int          y)
9351 {
9352   int dx, dy;
9353   int new_x, new_y;
9354   MetaRectangle old;
9355   int breakloose_threshold;
9356   MetaDisplay *display = window->display;
9357 
9358   display->grab_latest_motion_x = x;
9359   display->grab_latest_motion_y = y;
9360 
9361   dx = x - display->grab_anchor_root_x;
9362   dy = y - display->grab_anchor_root_y;
9363 
9364   new_x = display->grab_anchor_window_pos.x + dx;
9365   new_y = display->grab_anchor_window_pos.y + dy;
9366 
9367   meta_verbose ("x,y = %d,%d anchor ptr %d,%d anchor pos %d,%d dx,dy %d,%d\n",
9368                 x, y,
9369                 display->grab_anchor_root_x,
9370                 display->grab_anchor_root_y,
9371                 display->grab_anchor_window_pos.x,
9372                 display->grab_anchor_window_pos.y,
9373                 dx, dy);
9374 
9375   if (snap_mode)
9376     window->snap_queued = TRUE;
9377   else
9378     window->snap_queued = FALSE;
9379 
9380   /* Don't bother doing anything if no move has been specified.  (This
9381    * happens often, even in keyboard moving, due to the warping of the
9382    * pointer.
9383    */
9384   if (dx == 0 && dy == 0)
9385     return;
9386 
9387   /* Originally for detaching maximized windows, but we use this
9388    * for the zones at the sides of the monitor where trigger tiling
9389    * because it's about the right size
9390    */
9391 
9392   if (legacy_snap && meta_prefs_get_legacy_snap ())
9393     {
9394       /* We don't want to tile while snapping. Also, clear any previous tile
9395          request. */
9396       window->tile_mode = META_TILE_NONE;
9397       window->tile_monitor_number = -1;
9398     }
9399   else if (meta_prefs_get_edge_tiling () && !META_WINDOW_TILED_OR_SNAPPED (window))
9400     {
9401       const MetaMonitorInfo *monitor;
9402       MetaRectangle work_area;
9403 
9404       /* For side-by-side tiling we are interested in the inside vertical
9405        * edges of the work area of the monitor where the pointer is located,
9406        * and in the outside top edge for maximized tiling.
9407        *
9408        * For maximized tiling we use the outside edge instead of the
9409        * inside edge, because we don't want to force users to maximize
9410        * windows they are placing near the top of their screens.
9411        *
9412        * The "current" idea of meta_window_get_work_area_current_monitor() and
9413        * meta_screen_get_current_monitor() is slightly different: the former
9414        * refers to the monitor which contains the largest part of the window,
9415        * the latter to the one where the pointer is located.
9416        */
9417       monitor = meta_screen_get_current_monitor_info (window->screen);
9418       meta_window_get_work_area_for_monitor (window,
9419                                              monitor->number,
9420                                              &work_area);
9421 
9422       /* Check if the cursor is in a position which triggers tiling
9423        * and set tile_mode accordingly.
9424        */
9425 
9426       window->current_proximity_zone = meta_window_get_current_zone (window,
9427                                                                      monitor->rect,
9428                                                                      work_area,
9429                                                                      x,
9430                                                                      y,
9431                                                                      meta_prefs_get_tile_hud_threshold ());
9432 
9433       guint edge_zone = meta_window_get_current_zone (window,
9434                                                       monitor->rect,
9435                                                       work_area,
9436                                                       x,
9437                                                       y,
9438                                                       HUD_WIDTH * meta_prefs_get_ui_scale ());
9439 
9440       switch (edge_zone) {
9441         case ZONE_0:
9442             window->tile_mode = window->maybe_retile_maximize ? META_TILE_MAXIMIZE :
9443                 (meta_prefs_get_tile_maximize() ? META_TILE_MAXIMIZE : META_TILE_TOP);
9444             break;
9445         case ZONE_1:
9446             window->tile_mode = META_TILE_BOTTOM;
9447             break;
9448         case ZONE_2:
9449             window->tile_mode = META_TILE_LEFT;
9450             break;
9451         case ZONE_3:
9452             window->tile_mode = META_TILE_RIGHT;
9453             break;
9454         case ZONE_4:
9455             window->tile_mode = META_TILE_ULC;
9456             break;
9457         case ZONE_5:
9458             window->tile_mode = META_TILE_URC;
9459             break;
9460         case ZONE_6:
9461             window->tile_mode = META_TILE_LRC;
9462             break;
9463         case ZONE_7:
9464             window->tile_mode = META_TILE_LLC;
9465             break;
9466         default:
9467             window->tile_mode = META_TILE_NONE;
9468             break;
9469       }
9470 
9471       if (window->tile_mode != META_TILE_NONE)
9472         window->tile_monitor_number = monitor->number;
9473     }
9474 
9475   /* shake loose (unmaximize) maximized or tiled window if dragged beyond
9476    * the threshold in the Y direction. Tiled windows can also be pulled
9477    * loose via X motion.
9478    */
9479   breakloose_threshold = meta_prefs_get_resize_threshold () * 2;
9480 
9481   if (window->tile_type == META_WINDOW_TILE_TYPE_SNAPPED)
9482     breakloose_threshold *= 2;
9483   if ((META_WINDOW_MAXIMIZED (window) && ABS (dy) >= breakloose_threshold) ||
9484       (META_WINDOW_MAXIMIZED_VERTICALLY (window) && ABS (dy) >= breakloose_threshold) ||
9485       (META_WINDOW_MAXIMIZED_HORIZONTALLY (window) && ABS (dx) >= breakloose_threshold) ||
9486       (META_WINDOW_TILED_OR_SNAPPED (window) && (MAX (ABS (dx), ABS (dy)) >= breakloose_threshold)))
9487     {
9488       double prop;
9489 
9490       /* Shake loose, so that the window snaps back to maximized
9491        * when dragged near the top; do not snap back if tiling
9492        * is enabled, as top edge tiling can be used in that case
9493        */
9494       window->shaken_loose = !meta_prefs_get_edge_tiling ();
9495       if (window->saved_maximize)
9496         window->maybe_retile_maximize = TRUE;
9497       window->tile_mode = META_TILE_NONE;
9498 
9499       /* move the unmaximized window to the cursor */
9500       prop =
9501         ((double)(x - display->grab_initial_window_pos.x)) /
9502         ((double)display->grab_initial_window_pos.width);
9503 
9504       display->grab_initial_window_pos.x =
9505         x - window->saved_rect.width * prop;
9506 
9507       if (window->frame)
9508         {
9509           display->grab_initial_window_pos.y = y + (window->frame->child_y / 2);
9510           display->grab_anchor_root_x = x;
9511           display->grab_anchor_root_y = y;
9512         }
9513 
9514       window->saved_rect.x = display->grab_initial_window_pos.x;
9515       window->saved_rect.y = display->grab_initial_window_pos.y;
9516 
9517       meta_window_unmaximize (window,
9518                               META_MAXIMIZE_HORIZONTAL |
9519                               META_MAXIMIZE_VERTICAL);
9520 
9521       return;
9522     }
9523 
9524   /* remaximize window on another monitor if window has been shaken
9525    * loose or it is still maximized (then move straight)
9526    */
9527   else if ((window->shaken_loose || META_WINDOW_MAXIMIZED (window)) &&
9528            window->tile_mode != META_TILE_LEFT && window->tile_mode != META_TILE_RIGHT)
9529     {
9530       const MetaMonitorInfo *wmonitor;
9531       MetaRectangle work_area;
9532       int monitor;
9533 
9534       window->tile_mode = META_TILE_NONE;
9535       wmonitor = meta_screen_get_monitor_for_window (window->screen, window);
9536 
9537       for (monitor = 0; monitor < window->screen->n_monitor_infos; monitor++)
9538         {
9539           meta_window_get_work_area_for_monitor (window, monitor, &work_area);
9540 
9541           /* check if cursor is near the top of a monitor work area */
9542           if (x >= work_area.x &&
9543               x < (work_area.x + work_area.width) &&
9544               y >= work_area.y &&
9545               y < (work_area.y + breakloose_threshold))
9546             {
9547               /* move the saved rect if window will become maximized on an
9548                * other monitor so user isn't surprised on a later unmaximize
9549                */
9550               if (wmonitor->number != monitor)
9551                 {
9552                   window->saved_rect.x = work_area.x;
9553                   window->saved_rect.y = work_area.y;
9554 
9555                   if (window->frame)
9556                     {
9557                       window->saved_rect.x += window->frame->child_x;
9558                       window->saved_rect.y += window->frame->child_y;
9559                     }
9560 
9561                   window->user_rect.x = window->saved_rect.x;
9562                   window->user_rect.y = window->saved_rect.y;
9563 
9564                   meta_window_unmaximize (window,
9565                                           META_MAXIMIZE_HORIZONTAL |
9566                                           META_MAXIMIZE_VERTICAL);
9567                 }
9568 
9569               display->grab_initial_window_pos = work_area;
9570               display->grab_anchor_root_x = x;
9571               display->grab_anchor_root_y = y;
9572               window->shaken_loose = FALSE;
9573 
9574               meta_window_maximize (window,
9575                                     META_MAXIMIZE_HORIZONTAL |
9576                                     META_MAXIMIZE_VERTICAL);
9577 
9578               return;
9579             }
9580         }
9581     }
9582 
9583   window->mouse_on_edge = meta_window_mouse_on_edge (window, x, y);
9584 
9585   MetaRectangle min_size, max_size, target_size;
9586   gboolean hminbad = FALSE;
9587   gboolean vminbad = FALSE;
9588 
9589     {
9590       if (meta_prefs_get_edge_tiling ())
9591         {
9592           if (window->current_proximity_zone != ZONE_NONE && !window->mouse_on_edge)
9593             meta_screen_tile_hud_update (window->screen, TRUE, FALSE);
9594           else
9595             meta_screen_tile_hud_update (window->screen, TRUE, TRUE);
9596         }
9597 
9598       if (window->tile_mode == META_TILE_NONE)
9599         {
9600           meta_screen_tile_preview_hide (window->screen);
9601         }
9602       else
9603         {
9604           get_size_limits (window, NULL, FALSE, &min_size, &max_size);
9605           meta_window_get_current_tile_area (window, &target_size);
9606           MetaFrameBorders borders;
9607           meta_frame_calc_borders (window->frame, &borders);
9608           meta_window_unextend_by_frame (window, &target_size, &borders);
9609           hminbad = target_size.width < min_size.width;
9610           vminbad = target_size.height < min_size.height;
9611 
9612          /* Delay showing the tile preview slightly to make it more unlikely to
9613           * trigger it unwittingly, e.g. when shaking loose the window or moving
9614           * it to another monitor.
9615           */
9616 
9617           if (!hminbad && !vminbad)
9618             meta_screen_tile_preview_update (window->screen,
9619                                               window->tile_mode != META_TILE_NONE &&
9620                                               !window->screen->tile_preview_visible);
9621           else
9622             meta_screen_tile_preview_hide (window->screen);
9623         }
9624     }
9625 
9626   meta_window_get_client_root_coords (window, &old);
9627 
9628   /* Don't allow movement in the maximized directions or while tiled */
9629   if (window->maximized_horizontally || META_WINDOW_TILED_OR_SNAPPED (window))
9630     new_x = old.x;
9631   if (window->maximized_vertically)
9632     new_y = old.y;
9633 
9634   /* Do any edge resistance/snapping */
9635   meta_window_edge_resistance_for_move (window,
9636                                         old.x,
9637                                         old.y,
9638                                         &new_x,
9639                                         &new_y,
9640                                         update_move_timeout,
9641                                         legacy_snap && meta_prefs_get_legacy_snap (),
9642                                         FALSE);
9643 
9644   meta_window_move (window, TRUE, new_x, new_y);
9645 }
9646 
9647 /* When resizing a maximized window by using alt-middle-drag (resizing
9648  * with the grips or the menu for a maximized window is not enabled),
9649  * the user can "break" out of the maximized state. This checks for
9650  * that possibility. During such a break-out resize the user can also
9651  * return to the previous maximization state by resizing back to near
9652  * the original size.
9653  */
9654 static MetaMaximizeFlags
check_resize_unmaximize(MetaWindow * window,int dx,int dy)9655 check_resize_unmaximize(MetaWindow *window,
9656                         int         dx,
9657                         int         dy)
9658 {
9659   int threshold;
9660   MetaMaximizeFlags new_unmaximize;
9661 
9662   threshold = meta_prefs_get_resize_threshold ();
9663   new_unmaximize = 0;
9664 
9665   if (window->maximized_horizontally ||
9666       window->tile_mode != META_TILE_NONE ||
9667       (window->display->grab_resize_unmaximize & META_MAXIMIZE_HORIZONTAL) != 0)
9668     {
9669       int x_amount;
9670 
9671       /* We allow breaking out of maximization in either direction, to make
9672        * the window larger than the monitor as well as smaller than the
9673        * monitor. If we wanted to only allow resizing smaller than the
9674        * monitor, we'd use - dx for NE/E/SE and dx for SW/W/NW.
9675        */
9676       switch (window->display->grab_op)
9677         {
9678         case META_GRAB_OP_RESIZING_NE:
9679         case META_GRAB_OP_KEYBOARD_RESIZING_NE:
9680         case META_GRAB_OP_RESIZING_E:
9681         case META_GRAB_OP_KEYBOARD_RESIZING_E:
9682         case META_GRAB_OP_RESIZING_SE:
9683         case META_GRAB_OP_KEYBOARD_RESIZING_SE:
9684         case META_GRAB_OP_RESIZING_SW:
9685         case META_GRAB_OP_KEYBOARD_RESIZING_SW:
9686         case META_GRAB_OP_RESIZING_W:
9687         case META_GRAB_OP_KEYBOARD_RESIZING_W:
9688         case META_GRAB_OP_RESIZING_NW:
9689         case META_GRAB_OP_KEYBOARD_RESIZING_NW:
9690           x_amount = dx < 0 ? - dx : dx;
9691           break;
9692         default:
9693           x_amount = 0;
9694           break;
9695         }
9696 
9697       if (x_amount > threshold)
9698         new_unmaximize |= META_MAXIMIZE_HORIZONTAL;
9699     }
9700 
9701   if (window->maximized_vertically || window->tile_mode != META_TILE_NONE ||
9702       (window->display->grab_resize_unmaximize & META_MAXIMIZE_VERTICAL) != 0)
9703     {
9704       int y_amount;
9705 
9706       switch (window->display->grab_op)
9707         {
9708         case META_GRAB_OP_RESIZING_N:
9709         case META_GRAB_OP_KEYBOARD_RESIZING_N:
9710         case META_GRAB_OP_RESIZING_NE:
9711         case META_GRAB_OP_KEYBOARD_RESIZING_NE:
9712         case META_GRAB_OP_RESIZING_NW:
9713         case META_GRAB_OP_KEYBOARD_RESIZING_NW:
9714         case META_GRAB_OP_RESIZING_SE:
9715         case META_GRAB_OP_KEYBOARD_RESIZING_SE:
9716         case META_GRAB_OP_RESIZING_S:
9717         case META_GRAB_OP_KEYBOARD_RESIZING_S:
9718         case META_GRAB_OP_RESIZING_SW:
9719         case META_GRAB_OP_KEYBOARD_RESIZING_SW:
9720           y_amount = dy < 0 ? - dy : dy;
9721           break;
9722         default:
9723           y_amount = 0;
9724           break;
9725         }
9726 
9727       if (y_amount > threshold)
9728         new_unmaximize |= META_MAXIMIZE_VERTICAL;
9729     }
9730 
9731   /* Metacity doesn't have a full user interface for only horizontally or
9732    * vertically maximized, so while only unmaximizing in the direction drags
9733    * has some advantages, it will also confuse the user. So, we always
9734    * unmaximize both ways if possible.
9735    */
9736   if (new_unmaximize != 0)
9737     {
9738       new_unmaximize = 0;
9739 
9740       if (window->maximized_horizontally ||
9741           (window->display->grab_resize_unmaximize & META_MAXIMIZE_HORIZONTAL) != 0)
9742         new_unmaximize |= META_MAXIMIZE_HORIZONTAL;
9743       if ((window->tile_type == META_WINDOW_TILE_TYPE_NONE ||
9744            window->resizing_tile_type == META_WINDOW_TILE_TYPE_NONE) &&
9745            (window->maximized_vertically || (window->display->grab_resize_unmaximize & META_MAXIMIZE_VERTICAL) != 0))
9746         new_unmaximize |= META_MAXIMIZE_VERTICAL;
9747     }
9748 
9749   return new_unmaximize;
9750 }
9751 
9752 static gboolean
update_resize_timeout(gpointer data)9753 update_resize_timeout (gpointer data)
9754 {
9755   MetaWindow *window = data;
9756 
9757   update_resize (window,
9758                  window->display->grab_last_user_action_was_snap,
9759                  window->display->grab_latest_motion_x,
9760                  window->display->grab_latest_motion_y,
9761                  TRUE);
9762   return FALSE;
9763 }
9764 
9765 static void
update_resize(MetaWindow * window,gboolean snap,int x,int y,gboolean force)9766 update_resize (MetaWindow *window,
9767                gboolean    snap,
9768                int x, int y,
9769                gboolean force)
9770 {
9771   int dx, dy;
9772   int new_w, new_h;
9773   int gravity;
9774   MetaRectangle old;
9775   double remaining;
9776   MetaMaximizeFlags new_unmaximize;
9777 
9778   window->display->grab_latest_motion_x = x;
9779   window->display->grab_latest_motion_y = y;
9780 
9781   dx = x - window->display->grab_anchor_root_x;
9782   dy = y - window->display->grab_anchor_root_y;
9783 
9784   /* Don't bother doing anything if no move has been specified.  (This
9785    * happens often, even in keyboard resizing, due to the warping of the
9786    * pointer.
9787    */
9788   if (dx == 0 && dy == 0)
9789     return;
9790 
9791   /* Attached modal dialogs are special in that size
9792    * changes apply to both sides, so that the dialog
9793    * remains centered to the parent.
9794    */
9795   if (meta_window_is_attached_dialog (window))
9796     {
9797       dx *= 2;
9798       dy *= 2;
9799     }
9800 
9801   new_w = window->display->grab_anchor_window_pos.width;
9802   new_h = window->display->grab_anchor_window_pos.height;
9803 
9804   if (window->display->grab_op == META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN)
9805     {
9806       if (window->tile_mode == META_TILE_NONE)
9807         {
9808           if ((dx > 0) && (dy > 0))
9809             {
9810               window->display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_SE;
9811               meta_window_update_keyboard_resize (window, TRUE);
9812             }
9813           else if ((dx < 0) && (dy > 0))
9814             {
9815               window->display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_SW;
9816               meta_window_update_keyboard_resize (window, TRUE);
9817             }
9818           else if ((dx > 0) && (dy < 0))
9819             {
9820               window->display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_NE;
9821               meta_window_update_keyboard_resize (window, TRUE);
9822             }
9823           else if ((dx < 0) && (dy < 0))
9824             {
9825               window->display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_NW;
9826               meta_window_update_keyboard_resize (window, TRUE);
9827             }
9828           else if (dx < 0)
9829             {
9830               window->display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_W;
9831               meta_window_update_keyboard_resize (window, TRUE);
9832             }
9833           else if (dx > 0)
9834             {
9835               window->display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_E;
9836               meta_window_update_keyboard_resize (window, TRUE);
9837             }
9838           else if (dy > 0)
9839             {
9840               window->display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_S;
9841               meta_window_update_keyboard_resize (window, TRUE);
9842             }
9843           else if (dy < 0)
9844             {
9845               window->display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_N;
9846               meta_window_update_keyboard_resize (window, TRUE);
9847             }
9848         }
9849       else
9850         {
9851           switch (window->tile_mode)
9852             {
9853               case META_TILE_LEFT:
9854                 window->display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_E;
9855                 break;
9856               case META_TILE_RIGHT:
9857                 window->display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_W;
9858                 break;
9859               case META_TILE_TOP:
9860                 window->display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_S;
9861                 break;
9862               case META_TILE_BOTTOM:
9863                 window->display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_N;
9864                 break;
9865               case META_TILE_ULC:
9866                 window->display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_SE;
9867                 break;
9868               case META_TILE_LLC:
9869                 window->display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_NE;
9870                 break;
9871               case META_TILE_URC:
9872                 window->display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_SW;
9873                 break;
9874               case META_TILE_LRC:
9875                 window->display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_NW;
9876                 break;
9877               default:
9878                 break;
9879             }
9880           meta_window_update_keyboard_resize (window, TRUE);
9881         }
9882     }
9883 
9884   new_unmaximize = check_resize_unmaximize (window, dx, dy);
9885 
9886   switch (window->display->grab_op)
9887     {
9888     case META_GRAB_OP_RESIZING_SE:
9889     case META_GRAB_OP_RESIZING_NE:
9890     case META_GRAB_OP_RESIZING_E:
9891     case META_GRAB_OP_KEYBOARD_RESIZING_SE:
9892     case META_GRAB_OP_KEYBOARD_RESIZING_NE:
9893     case META_GRAB_OP_KEYBOARD_RESIZING_E:
9894       if (meta_window_resize_or_move_allowed (window, META_DIRECTION_RIGHT))
9895         new_w += dx;
9896       break;
9897 
9898     case META_GRAB_OP_RESIZING_NW:
9899     case META_GRAB_OP_RESIZING_SW:
9900     case META_GRAB_OP_RESIZING_W:
9901     case META_GRAB_OP_KEYBOARD_RESIZING_NW:
9902     case META_GRAB_OP_KEYBOARD_RESIZING_SW:
9903     case META_GRAB_OP_KEYBOARD_RESIZING_W:
9904       if (meta_window_resize_or_move_allowed (window, META_DIRECTION_LEFT))
9905         new_w -= dx;
9906       break;
9907 	default:
9908 	  break;
9909 	}
9910 
9911   switch (window->display->grab_op)
9912     {
9913     case META_GRAB_OP_RESIZING_SE:
9914     case META_GRAB_OP_RESIZING_S:
9915     case META_GRAB_OP_RESIZING_SW:
9916     case META_GRAB_OP_KEYBOARD_RESIZING_SE:
9917     case META_GRAB_OP_KEYBOARD_RESIZING_S:
9918     case META_GRAB_OP_KEYBOARD_RESIZING_SW:
9919       if (meta_window_resize_or_move_allowed (window, META_DIRECTION_DOWN))
9920         new_h += dy;
9921       break;
9922 
9923     case META_GRAB_OP_RESIZING_N:
9924     case META_GRAB_OP_RESIZING_NE:
9925     case META_GRAB_OP_RESIZING_NW:
9926     case META_GRAB_OP_KEYBOARD_RESIZING_N:
9927     case META_GRAB_OP_KEYBOARD_RESIZING_NE:
9928     case META_GRAB_OP_KEYBOARD_RESIZING_NW:
9929       if (meta_window_resize_or_move_allowed (window, META_DIRECTION_UP))
9930         new_h -= dy;
9931       break;
9932     default:
9933       break;
9934     }
9935 
9936 #ifdef HAVE_XSYNC
9937   /* If we're waiting for a request for _NET_WM_SYNC_REQUEST, we'll
9938    * resize the window when the window responds, or when we time
9939    * the response out.
9940    */
9941   if (window->sync_request_timeout_id != 0)
9942     return;
9943 #endif
9944 
9945   if (!check_moveresize_frequency (window, &remaining) && !force)
9946     {
9947       /* we are ignoring an event here, so we schedule a
9948        * compensation event when we would otherwise not ignore
9949        * an event. Otherwise we can become stuck if the user never
9950        * generates another event.
9951        */
9952       if (!window->display->grab_resize_timeout_id)
9953 	{
9954 	  window->display->grab_resize_timeout_id =
9955 	    g_timeout_add ((int)remaining, update_resize_timeout, window);
9956 	}
9957 
9958       return;
9959     }
9960 
9961   /* Remove any scheduled compensation events */
9962   if (window->display->grab_resize_timeout_id)
9963     {
9964       g_source_remove (window->display->grab_resize_timeout_id);
9965       window->display->grab_resize_timeout_id = 0;
9966     }
9967 
9968   old = window->rect;  /* Don't actually care about x,y */
9969 
9970   /* One sided resizing ought to actually be one-sided, despite the fact that
9971    * aspect ratio windows don't interact nicely with the above stuff.  So,
9972    * to avoid some nasty flicker, we enforce that.
9973    */
9974   switch (window->display->grab_op)
9975     {
9976     case META_GRAB_OP_RESIZING_S:
9977     case META_GRAB_OP_RESIZING_N:
9978     case META_GRAB_OP_KEYBOARD_RESIZING_S:
9979     case META_GRAB_OP_KEYBOARD_RESIZING_N:
9980       new_w = old.width;
9981       break;
9982 
9983     case META_GRAB_OP_RESIZING_E:
9984     case META_GRAB_OP_RESIZING_W:
9985     case META_GRAB_OP_KEYBOARD_RESIZING_E:
9986     case META_GRAB_OP_KEYBOARD_RESIZING_W:
9987       new_h = old.height;
9988       break;
9989 
9990     default:
9991       break;
9992     }
9993 
9994   /* compute gravity of client during operation */
9995   if (window->tile_mode != META_TILE_NONE)
9996     gravity = meta_resize_gravity_from_tile_mode (window->tile_mode);
9997   else
9998     gravity = meta_resize_gravity_from_grab_op (window->display->grab_op);
9999 
10000   g_assert (gravity >= 0);
10001 
10002   /* Do any edge resistance/snapping */
10003   meta_window_edge_resistance_for_resize (window,
10004                                           old.width,
10005                                           old.height,
10006                                           &new_w,
10007                                           &new_h,
10008                                           gravity,
10009                                           update_resize_timeout,
10010                                           snap,
10011                                           FALSE);
10012 
10013   if (new_unmaximize == window->display->grab_resize_unmaximize && window->tile_type == META_WINDOW_TILE_TYPE_NONE)
10014     {
10015       /* We don't need to update unless the specified width and height
10016        * are actually different from what we had before.
10017        */
10018       if (old.width != new_w || old.height != new_h)
10019         {
10020           meta_window_resize_with_gravity (window, TRUE, new_w, new_h, gravity);
10021         }
10022     }
10023   else
10024     {
10025       if ((new_unmaximize & ~window->display->grab_resize_unmaximize) != 0)
10026         {
10027           meta_window_unmaximize_with_gravity (window,
10028                                                (new_unmaximize & ~window->display->grab_resize_unmaximize),
10029                                                new_w, new_h, gravity);
10030         }
10031 
10032       if (window->tile_type != META_WINDOW_TILE_TYPE_NONE)
10033         {
10034           if (window->tile_mode != META_TILE_NONE)
10035             {
10036               window->resize_tile_mode = window->tile_mode;
10037               window->resizing_tile_type = window->tile_type;
10038             }
10039 
10040           meta_window_resize_with_gravity (window, TRUE, new_w, new_h, gravity);
10041         }
10042 
10043       if (window->resizing_tile_type == META_WINDOW_TILE_TYPE_NONE && (window->display->grab_resize_unmaximize & ~new_unmaximize))
10044         {
10045           MetaRectangle saved_rect = window->saved_rect;
10046           meta_window_maximize (window,
10047                                 (window->display->grab_resize_unmaximize & ~new_unmaximize));
10048           window->saved_rect = saved_rect;
10049         }
10050     }
10051 
10052   window->display->grab_resize_unmaximize = new_unmaximize;
10053   /* Store the latest resize time, if we actually resized. */
10054 
10055   if (window->rect.width != old.width || window->rect.height != old.height)
10056     g_get_current_time (&window->display->grab_last_moveresize_time);
10057 }
10058 
10059 typedef struct
10060 {
10061   const XEvent *current_event;
10062   int           count;
10063   guint32       last_time;
10064 } EventScannerData;
10065 
10066 static Bool
find_last_time_predicate(Display * display,XEvent * xevent,XPointer arg)10067 find_last_time_predicate (Display  *display,
10068                           XEvent   *xevent,
10069                           XPointer  arg)
10070 {
10071   EventScannerData *esd = (void*) arg;
10072 
10073   if (esd->current_event->type == xevent->type &&
10074       esd->current_event->xany.window == xevent->xany.window)
10075     {
10076       esd->count += 1;
10077       esd->last_time = xevent->xmotion.time;
10078     }
10079 
10080   return False;
10081 }
10082 
10083 static gboolean
check_use_this_motion_notify(MetaWindow * window,XEvent * event)10084 check_use_this_motion_notify (MetaWindow *window,
10085                               XEvent     *event)
10086 {
10087   EventScannerData esd;
10088   XEvent useless;
10089 
10090   /* This code is copied from Owen's GDK code. */
10091 
10092   if (window->display->grab_motion_notify_time != 0)
10093     {
10094       /* == is really the right test, but I'm all for paranoia */
10095       if (window->display->grab_motion_notify_time <=
10096           event->xmotion.time)
10097         {
10098           meta_topic (META_DEBUG_RESIZING,
10099                       "Arrived at event with time %u (waiting for %u), using it\n",
10100                       (unsigned int)event->xmotion.time,
10101                       window->display->grab_motion_notify_time);
10102           window->display->grab_motion_notify_time = 0;
10103           return TRUE;
10104         }
10105       else
10106         return FALSE; /* haven't reached the saved timestamp yet */
10107     }
10108 
10109   esd.current_event = event;
10110   esd.count = 0;
10111   esd.last_time = 0;
10112 
10113   /* "useless" isn't filled in because the predicate never returns True */
10114   XCheckIfEvent (window->display->xdisplay,
10115                  &useless,
10116                  find_last_time_predicate,
10117                  (XPointer) &esd);
10118 #ifdef WITH_VERBOSE_MODE
10119   if (esd.count > 0)
10120     meta_topic (META_DEBUG_RESIZING,
10121                 "Will skip %d motion events and use the event with time %u\n",
10122                 esd.count, (unsigned int) esd.last_time);
10123 #endif
10124   if (esd.last_time == 0)
10125     return TRUE;
10126   else
10127     {
10128       /* Save this timestamp, and ignore all motion notify
10129        * until we get to the one with this stamp.
10130        */
10131       window->display->grab_motion_notify_time = esd.last_time;
10132       return FALSE;
10133     }
10134 }
10135 
10136 static void
update_tile_mode(MetaWindow * window)10137 update_tile_mode (MetaWindow *window)
10138 {
10139   switch (window->tile_mode)
10140     {
10141       case META_TILE_LEFT:
10142       case META_TILE_RIGHT:
10143       case META_TILE_ULC:
10144       case META_TILE_LLC:
10145       case META_TILE_URC:
10146       case META_TILE_LRC:
10147       case META_TILE_TOP:
10148       case META_TILE_BOTTOM:
10149       case META_TILE_MAXIMIZE:
10150           if (!META_WINDOW_TILED_OR_SNAPPED (window))
10151               window->tile_mode = META_TILE_NONE;
10152           break;
10153     }
10154 }
10155 
10156 #ifdef HAVE_XSYNC
10157 void
meta_window_update_sync_request_counter(MetaWindow * window,gint64 new_counter_value)10158 meta_window_update_sync_request_counter (MetaWindow *window,
10159                                          gint64      new_counter_value)
10160 {
10161   gboolean needs_frame_drawn = FALSE;
10162   gboolean no_delay_frame = FALSE;
10163 
10164   if (window->extended_sync_request_counter && new_counter_value % 2 == 0)
10165     {
10166       needs_frame_drawn = TRUE;
10167       no_delay_frame = new_counter_value == window->sync_request_serial + 1;
10168     }
10169 
10170   window->sync_request_serial = new_counter_value;
10171   meta_compositor_set_updates_frozen (window->display->compositor, window,
10172                                       meta_window_updates_are_frozen (window));
10173 
10174   if (window == window->display->grab_window &&
10175       meta_grab_op_is_resizing (window->display->grab_op) &&
10176       new_counter_value >= window->sync_request_wait_serial &&
10177       (!window->extended_sync_request_counter || new_counter_value % 2 == 0) &&
10178       window->sync_request_timeout_id)
10179     {
10180       meta_topic (META_DEBUG_RESIZING,
10181                   "Alarm event received last motion x = %d y = %d\n",
10182                   window->display->grab_latest_motion_x,
10183                   window->display->grab_latest_motion_y);
10184 
10185       g_source_remove (window->sync_request_timeout_id);
10186       window->sync_request_timeout_id = 0;
10187 
10188       /* This means we are ready for another configure;
10189        * no pointer round trip here, to keep in sync */
10190       update_resize (window,
10191                      window->display->grab_last_user_action_was_snap,
10192                      window->display->grab_latest_motion_x,
10193                      window->display->grab_latest_motion_y,
10194                      TRUE);
10195     }
10196 
10197   /* If sync was previously disabled, turn it back on and hope
10198    * the application has come to its senses (maybe it was just
10199    * busy with a pagefault or a long computation).
10200    */
10201   window->disable_sync = FALSE;
10202 
10203   if (needs_frame_drawn)
10204     meta_compositor_queue_frame_drawn (window->display->compositor, window,
10205                                        no_delay_frame);
10206 }
10207 #endif /* HAVE_XSYNC */
10208 
10209 LOCAL_SYMBOL void
meta_window_handle_mouse_grab_op_event(MetaWindow * window,XEvent * event)10210 meta_window_handle_mouse_grab_op_event (MetaWindow *window,
10211                                         XEvent     *event)
10212 {
10213 #ifdef HAVE_XSYNC
10214   if (event->type == (window->display->xsync_event_base + XSyncAlarmNotify))
10215     {
10216       meta_topic (META_DEBUG_RESIZING,
10217                   "Alarm event received last motion x = %d y = %d\n",
10218                   window->display->grab_latest_motion_x,
10219                   window->display->grab_latest_motion_y);
10220 
10221       /* If sync was previously disabled, turn it back on and hope
10222        * the application has come to its senses (maybe it was just
10223        * busy with a pagefault or a long computation).
10224        */
10225       window->disable_sync = FALSE;
10226       meta_window_destroy_sync_request_alarm (window);
10227 
10228       /* This means we are ready for another configure. */
10229       switch (window->display->grab_op)
10230         {
10231         case META_GRAB_OP_RESIZING_E:
10232         case META_GRAB_OP_RESIZING_W:
10233         case META_GRAB_OP_RESIZING_S:
10234         case META_GRAB_OP_RESIZING_N:
10235         case META_GRAB_OP_RESIZING_SE:
10236         case META_GRAB_OP_RESIZING_SW:
10237         case META_GRAB_OP_RESIZING_NE:
10238         case META_GRAB_OP_RESIZING_NW:
10239         case META_GRAB_OP_KEYBOARD_RESIZING_S:
10240         case META_GRAB_OP_KEYBOARD_RESIZING_N:
10241         case META_GRAB_OP_KEYBOARD_RESIZING_W:
10242         case META_GRAB_OP_KEYBOARD_RESIZING_E:
10243         case META_GRAB_OP_KEYBOARD_RESIZING_SE:
10244         case META_GRAB_OP_KEYBOARD_RESIZING_NE:
10245         case META_GRAB_OP_KEYBOARD_RESIZING_SW:
10246         case META_GRAB_OP_KEYBOARD_RESIZING_NW:
10247           /* no pointer round trip here, to keep in sync */
10248           update_resize (window,
10249                          window->display->grab_last_user_action_was_snap,
10250                          window->display->grab_latest_motion_x,
10251                          window->display->grab_latest_motion_y,
10252                          TRUE);
10253           break;
10254 
10255         default:
10256           break;
10257         }
10258     }
10259 #endif /* HAVE_XSYNC */
10260 
10261   switch (event->type)
10262     {
10263     case ButtonRelease:
10264       meta_display_check_threshold_reached (window->display,
10265                                             event->xbutton.x_root,
10266                                             event->xbutton.y_root);
10267       /* If the user was snap moving then ignore the button release
10268        * because they may have let go of shift before releasing the
10269        * mouse button and they almost certainly do not want a
10270        * non-snapped movement to occur from the button release.
10271        */
10272       if (!window->display->grab_last_user_action_was_snap)
10273         {
10274           if (meta_grab_op_is_moving (window->display->grab_op))
10275             {
10276               if (window->tile_mode != META_TILE_NONE &&
10277                   meta_window_mouse_on_edge (window,
10278                                              event->xbutton.x_root,
10279                                              event->xbutton.y_root)) {
10280                   window->custom_snap_size = FALSE;
10281                   if (window->tile_mode == META_TILE_MAXIMIZE)
10282                     meta_window_maximize(window, META_MAXIMIZE_VERTICAL | META_MAXIMIZE_HORIZONTAL);
10283                   else
10284                     meta_window_real_tile (window, FALSE);
10285               }
10286               else if (event->xbutton.root == window->screen->xroot)
10287                   update_move (window,
10288                                event->xbutton.state & ShiftMask,
10289                                event->xbutton.state & get_mask_from_snap_keysym (window),
10290                                event->xbutton.x_root, event->xbutton.y_root);
10291               if (meta_prefs_get_edge_tiling ())
10292                   meta_screen_tile_hud_update (window->screen, FALSE, TRUE);
10293             }
10294           else if (meta_grab_op_is_resizing (window->display->grab_op))
10295             {
10296               if (event->xbutton.root == window->screen->xroot)
10297                 update_resize (window,
10298                                event->xbutton.state & ShiftMask,
10299                                event->xbutton.x_root,
10300                                event->xbutton.y_root,
10301                                TRUE);
10302             }
10303 
10304           /* If a tiled window has been dragged free with a
10305            * mouse resize without snapping back to the tiled
10306            * state, it will end up with an inconsistent tile
10307            * mode on mouse release; cleaning the mode earlier
10308            * would break the ability to snap back to the tiled
10309            * state, so we wait until mouse release.
10310            */
10311           if (window->tile_type == META_WINDOW_TILE_TYPE_NONE)
10312             update_tile_mode (window);
10313         }
10314 
10315       window->maybe_retile_maximize = FALSE;
10316       meta_display_end_grab_op (window->display, event->xbutton.time);
10317       break;
10318 
10319     case MotionNotify:
10320       meta_display_check_threshold_reached (window->display,
10321                                             event->xmotion.x_root,
10322                                             event->xmotion.y_root);
10323       if (meta_grab_op_is_moving (window->display->grab_op))
10324         {
10325           if (event->xmotion.root == window->screen->xroot)
10326             {
10327               if (check_use_this_motion_notify (window,
10328                                                 event))
10329                 update_move (window,
10330                              event->xmotion.state & ShiftMask,
10331                              event->xmotion.state & get_mask_from_snap_keysym (window),
10332                              event->xmotion.x_root,
10333                              event->xmotion.y_root);
10334             }
10335         }
10336       else if (meta_grab_op_is_resizing (window->display->grab_op))
10337         {
10338           if (event->xmotion.root == window->screen->xroot)
10339             {
10340               if (check_use_this_motion_notify (window,
10341                                                 event))
10342                 update_resize (window,
10343                                event->xmotion.state & ShiftMask,
10344                                event->xmotion.x_root,
10345                                event->xmotion.y_root,
10346                                FALSE);
10347             }
10348         }
10349       break;
10350 
10351     default:
10352       break;
10353     }
10354 }
10355 
10356 LOCAL_SYMBOL void
meta_window_handle_keyboard_grab_op_event(MetaWindow * window,XEvent * event)10357 meta_window_handle_keyboard_grab_op_event (MetaWindow *window,
10358                                            XEvent     *event)
10359 {
10360   switch (event->type)
10361     {
10362     case KeyPress:
10363     case KeyRelease:
10364       meta_display_check_threshold_reached (window->display,
10365                                             event->xmotion.x_root,
10366                                             event->xmotion.y_root);
10367       if (window->display->grab_op == META_GRAB_OP_MOVING)
10368         {
10369           if (event->xmotion.root == window->screen->xroot)
10370             {
10371               if (check_use_this_motion_notify (window,
10372                                                 event)) {
10373                 unsigned int *mod_set = meta_prefs_get_snap_modifier ();
10374                 KeySym keysym = XkbKeycodeToKeysym (window->display->xdisplay, event->xkey.keycode, 0, 0);
10375                 if (mod_set[0] != 0)
10376                 {
10377                     gboolean snap = FALSE;
10378                     if (event->type == KeyPress && (keysym == mod_set[0] ||
10379                                                     keysym == mod_set[1]))
10380                         snap = TRUE;
10381                     update_move (window,
10382                                  event->xmotion.state & ShiftMask,
10383                                  snap,
10384                                  event->xmotion.x_root,
10385                                  event->xmotion.y_root);
10386                 }
10387               }
10388             }
10389         }
10390       break;
10391     default:
10392       break;
10393     }
10394 }
10395 
10396 LOCAL_SYMBOL void
meta_window_set_gravity(MetaWindow * window,int gravity)10397 meta_window_set_gravity (MetaWindow *window,
10398                          int         gravity)
10399 {
10400   XSetWindowAttributes attrs;
10401 
10402   meta_verbose ("Setting gravity of %s to %d\n", window->desc, gravity);
10403 
10404   attrs.win_gravity = gravity;
10405 
10406   meta_error_trap_push (window->display);
10407 
10408   XChangeWindowAttributes (window->display->xdisplay,
10409                            window->xwindow,
10410                            CWWinGravity,
10411                            &attrs);
10412 
10413   meta_error_trap_pop (window->display);
10414 }
10415 
10416 static void
get_work_area_monitor(MetaWindow * window,MetaRectangle * area,int which_monitor)10417 get_work_area_monitor (MetaWindow    *window,
10418                        MetaRectangle *area,
10419                        int            which_monitor)
10420 {
10421   GList *tmp;
10422 
10423   g_assert (which_monitor >= 0);
10424 
10425   /* Initialize to the whole monitor */
10426   *area = window->screen->monitor_infos[which_monitor].rect;
10427 
10428   tmp = meta_window_get_workspaces (window);
10429   while (tmp != NULL)
10430     {
10431       MetaRectangle workspace_work_area;
10432       meta_workspace_get_work_area_for_monitor (tmp->data,
10433                                                 which_monitor,
10434                                                 &workspace_work_area);
10435       meta_rectangle_intersect (area,
10436                                 &workspace_work_area,
10437                                 area);
10438       tmp = tmp->next;
10439     }
10440 
10441   meta_topic (META_DEBUG_WORKAREA,
10442               "Window %s monitor %d has work area %d,%d %d x %d\n",
10443               window->desc, which_monitor,
10444               area->x, area->y, area->width, area->height);
10445 }
10446 
10447 LOCAL_SYMBOL void
meta_window_get_work_area_current_monitor(MetaWindow * window,MetaRectangle * area)10448 meta_window_get_work_area_current_monitor (MetaWindow    *window,
10449                                            MetaRectangle *area)
10450 {
10451   const MetaMonitorInfo *monitor = NULL;
10452   monitor = meta_screen_get_monitor_for_window (window->screen,
10453                                                 window);
10454 
10455   meta_window_get_work_area_for_monitor (window,
10456                                          monitor->number,
10457                                          area);
10458 }
10459 
10460 LOCAL_SYMBOL void
meta_window_get_work_area_for_monitor(MetaWindow * window,int which_monitor,MetaRectangle * area)10461 meta_window_get_work_area_for_monitor (MetaWindow    *window,
10462                                        int            which_monitor,
10463                                        MetaRectangle *area)
10464 {
10465   g_return_if_fail (which_monitor >= 0);
10466 
10467   get_work_area_monitor (window,
10468                          area,
10469                          which_monitor);
10470 }
10471 
10472 LOCAL_SYMBOL void
meta_window_get_work_area_all_monitors(MetaWindow * window,MetaRectangle * area)10473 meta_window_get_work_area_all_monitors (MetaWindow    *window,
10474                                         MetaRectangle *area)
10475 {
10476   GList *tmp;
10477 
10478   /* Initialize to the whole screen */
10479   *area = window->screen->rect;
10480 
10481   tmp = meta_window_get_workspaces (window);
10482   while (tmp != NULL)
10483     {
10484       MetaRectangle workspace_work_area;
10485       meta_workspace_get_work_area_all_monitors (tmp->data,
10486                                                  &workspace_work_area);
10487       meta_rectangle_intersect (area,
10488                                 &workspace_work_area,
10489                                 area);
10490       tmp = tmp->next;
10491     }
10492 
10493   meta_topic (META_DEBUG_WORKAREA,
10494               "Window %s has whole-screen work area %d,%d %d x %d\n",
10495               window->desc, area->x, area->y, area->width, area->height);
10496 }
10497 
10498 LOCAL_SYMBOL void
meta_window_get_tile_threshold_area_for_mode(MetaWindow * window,MetaRectangle work_area,MetaTileMode mode,MetaRectangle * tile_area,gint zone_width)10499 meta_window_get_tile_threshold_area_for_mode (MetaWindow    *window,
10500                                               MetaRectangle  work_area,
10501                                               MetaTileMode   mode,
10502                                               MetaRectangle *tile_area,
10503                                               gint           zone_width)
10504 {
10505   int tile_monitor_number;
10506 
10507   g_return_if_fail (mode != META_TILE_NONE);
10508 
10509   if (window != NULL) {
10510       tile_monitor_number = meta_window_get_current_tile_monitor_number(window);
10511       meta_window_get_work_area_for_monitor (window, tile_monitor_number, tile_area);
10512   } else {
10513     tile_area->x = work_area.x;
10514     tile_area->y = work_area.y;
10515     tile_area->height = work_area.height;
10516     tile_area->width = work_area.width;
10517   }
10518 
10519   switch (mode) {
10520       case META_TILE_LEFT:
10521           tile_area->width = zone_width;
10522           tile_area->y = tile_area->y + zone_width;
10523           tile_area->height = tile_area->height - (2 * zone_width);
10524           break;
10525       case META_TILE_RIGHT:
10526           tile_area->x = tile_area->width - zone_width;
10527           tile_area->width = zone_width;
10528           tile_area->y = tile_area->y + zone_width;
10529           tile_area->height = tile_area->height - (2 * zone_width);
10530           break;
10531       case META_TILE_ULC:
10532           tile_area->width = zone_width;
10533           tile_area->height = zone_width;
10534           break;
10535       case META_TILE_LLC:
10536           tile_area->y = tile_area->height - zone_width;
10537           tile_area->width = zone_width;
10538           tile_area->height = zone_width;
10539           break;
10540       case META_TILE_URC:
10541           tile_area->x = tile_area->width - zone_width;
10542           tile_area->width = zone_width;
10543           tile_area->height = zone_width;
10544           break;
10545       case META_TILE_LRC:
10546           tile_area->x = tile_area->width - zone_width;
10547           tile_area->width = zone_width;
10548           tile_area->y = tile_area->height - zone_width;
10549           tile_area->height = zone_width;
10550           break;
10551       case META_TILE_TOP:
10552       case META_TILE_MAXIMIZE:
10553           tile_area->height = zone_width;
10554           tile_area->x = tile_area->x + zone_width;
10555           tile_area->width = tile_area->width - (2 * zone_width);
10556           break;
10557       case META_TILE_BOTTOM:
10558           tile_area->y = tile_area->height - zone_width;
10559           tile_area->height = zone_width;
10560           tile_area->x = tile_area->x + zone_width;
10561           tile_area->width = tile_area->width - (2 * zone_width);
10562           break;
10563       default:
10564           break;
10565   }
10566 }
10567 
10568 int
meta_window_get_current_tile_monitor_number(MetaWindow * window)10569 meta_window_get_current_tile_monitor_number (MetaWindow *window)
10570 {
10571     int tile_monitor_number = window->tile_monitor_number;
10572 
10573     if (tile_monitor_number < 0)
10574     {
10575         meta_warning ("%s called with an invalid monitor number, using 0 instead\n", G_STRFUNC);
10576         tile_monitor_number = 0;
10577     }
10578 
10579     return tile_monitor_number;
10580 }
10581 
10582 LOCAL_SYMBOL void
meta_window_get_current_tile_area(MetaWindow * window,MetaRectangle * tile_area)10583 meta_window_get_current_tile_area (MetaWindow    *window,
10584                                    MetaRectangle *tile_area)
10585 {
10586   int tile_monitor_number;
10587 
10588   g_return_if_fail (window->tile_mode != META_TILE_NONE);
10589 
10590   tile_monitor_number = meta_window_get_current_tile_monitor_number (window);
10591 
10592   meta_window_get_work_area_for_monitor (window, tile_monitor_number, tile_area);
10593 
10594   if (window->tile_mode == META_TILE_LEFT  ||
10595       window->tile_mode == META_TILE_RIGHT)
10596     tile_area->width /= 2;
10597 
10598   if (window->tile_mode == META_TILE_RIGHT)
10599     tile_area->x += tile_area->width;
10600 
10601   if (window->tile_mode == META_TILE_ULC) {
10602     tile_area->width /= 2;
10603     tile_area->height /= 2;
10604   }
10605 
10606   if (window->tile_mode == META_TILE_LLC) {
10607     tile_area->width /= 2;
10608     tile_area->height /= 2;
10609     tile_area->y += tile_area->height;
10610   }
10611 
10612   if (window->tile_mode == META_TILE_URC) {
10613     tile_area->width /= 2;
10614     tile_area->height /= 2;
10615     tile_area->x += tile_area->width;
10616   }
10617 
10618   if (window->tile_mode == META_TILE_LRC) {
10619     tile_area->width /= 2;
10620     tile_area->height /= 2;
10621     tile_area->x += tile_area->width;
10622     tile_area->y += tile_area->height;
10623   }
10624 
10625   if (window->tile_mode == META_TILE_TOP ||
10626       window->tile_mode == META_TILE_BOTTOM)
10627     tile_area->height /= 2;
10628 
10629   if (window->tile_mode == META_TILE_BOTTOM)
10630     tile_area->y += tile_area->height;
10631 }
10632 
10633 LOCAL_SYMBOL gboolean
meta_window_same_application(MetaWindow * window,MetaWindow * other_window)10634 meta_window_same_application (MetaWindow *window,
10635                               MetaWindow *other_window)
10636 {
10637   MetaGroup *group       = meta_window_get_group (window);
10638   MetaGroup *other_group = meta_window_get_group (other_window);
10639 
10640   return
10641     group!=NULL &&
10642     other_group!=NULL &&
10643     group==other_group;
10644 }
10645 
10646 /* Generally meta_window_same_application() is a better idea
10647  * of "sameness", since it handles the case where multiple apps
10648  * want to look like the same app or the same app wants to look
10649  * like multiple apps, but in the case of workarounds for legacy
10650  * applications (which likely aren't setting the group properly
10651  * anyways), it may be desirable to check this as well.
10652  */
10653 static gboolean
meta_window_same_client(MetaWindow * window,MetaWindow * other_window)10654 meta_window_same_client (MetaWindow *window,
10655                          MetaWindow *other_window)
10656 {
10657   int resource_mask = window->display->xdisplay->resource_mask;
10658 
10659   return ((window->xwindow & ~resource_mask) ==
10660           (other_window->xwindow & ~resource_mask));
10661 }
10662 
10663 /**
10664  * meta_window_is_client_decorated:
10665  *
10666  * Check if if the window has decorations drawn by the client.
10667  * (window->decorated refers only to whether we should add decorations)
10668  */
10669 gboolean
meta_window_is_client_decorated(MetaWindow * window)10670 meta_window_is_client_decorated (MetaWindow *window)
10671 {
10672   /* Currently the implementation here is hackish -
10673    * has_custom_frame_extents() is set if _GTK_FRAME_EXTENTS is set
10674    * to any value even 0. GTK+ always sets _GTK_FRAME_EXTENTS for
10675    * client-side-decorated window, even if the value is 0 because
10676    * the window is maxized and has no invisible borders or shadows.
10677    */
10678   return window->has_custom_frame_extents;
10679 }
10680 
10681 void
meta_window_extend_by_frame(MetaWindow * window,MetaRectangle * rect,const MetaFrameBorders * borders)10682 meta_window_extend_by_frame (MetaWindow              *window,
10683                              MetaRectangle           *rect,
10684                              const MetaFrameBorders  *borders)
10685 {
10686   if (window->frame)
10687     {
10688       rect->x -= borders->visible.left;
10689       rect->y -= borders->visible.top;
10690       rect->width  += borders->visible.left + borders->visible.right;
10691       rect->height += borders->visible.top + borders->visible.bottom;
10692     }
10693   else if (meta_window_is_client_decorated (window))
10694     {
10695       const GtkBorder *extents = &window->custom_frame_extents;
10696       rect->x += extents->left;
10697       rect->y += extents->top;
10698       rect->width -= extents->left + extents->right;
10699       rect->height -= extents->top + extents->bottom;
10700     }
10701 }
10702 
10703 void
meta_window_unextend_by_frame(MetaWindow * window,MetaRectangle * rect,const MetaFrameBorders * borders)10704 meta_window_unextend_by_frame (MetaWindow              *window,
10705                                MetaRectangle           *rect,
10706                                const MetaFrameBorders  *borders)
10707 {
10708   if (window->frame)
10709     {
10710       rect->x += borders->visible.left;
10711       rect->y += borders->visible.top;
10712       rect->width  -= borders->visible.left + borders->visible.right;
10713       rect->height -= borders->visible.top + borders->visible.bottom;
10714     }
10715   else if (meta_window_is_client_decorated (window))
10716     {
10717       const GtkBorder *extents = &window->custom_frame_extents;
10718       rect->x -= extents->left;
10719       rect->y -= extents->top;
10720       rect->width += extents->left + extents->right;
10721       rect->height += extents->top + extents->bottom;
10722     }
10723 }
10724 
10725 LOCAL_SYMBOL void
meta_window_refresh_resize_popup(MetaWindow * window)10726 meta_window_refresh_resize_popup (MetaWindow *window)
10727 {
10728   if (window->display->grab_op == META_GRAB_OP_NONE)
10729     return;
10730 
10731   if (window->display->grab_window != window)
10732     return;
10733 
10734   switch (window->display->grab_op)
10735     {
10736     case META_GRAB_OP_RESIZING_SE:
10737     case META_GRAB_OP_RESIZING_S:
10738     case META_GRAB_OP_RESIZING_SW:
10739     case META_GRAB_OP_RESIZING_N:
10740     case META_GRAB_OP_RESIZING_NE:
10741     case META_GRAB_OP_RESIZING_NW:
10742     case META_GRAB_OP_RESIZING_W:
10743     case META_GRAB_OP_RESIZING_E:
10744     case META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN:
10745     case META_GRAB_OP_KEYBOARD_RESIZING_S:
10746     case META_GRAB_OP_KEYBOARD_RESIZING_N:
10747     case META_GRAB_OP_KEYBOARD_RESIZING_W:
10748     case META_GRAB_OP_KEYBOARD_RESIZING_E:
10749     case META_GRAB_OP_KEYBOARD_RESIZING_SE:
10750     case META_GRAB_OP_KEYBOARD_RESIZING_NE:
10751     case META_GRAB_OP_KEYBOARD_RESIZING_SW:
10752     case META_GRAB_OP_KEYBOARD_RESIZING_NW:
10753       break;
10754 
10755     default:
10756       /* Not resizing */
10757       return;
10758     }
10759 
10760   if (window->display->grab_resize_popup == NULL)
10761     {
10762       gint scale;
10763 
10764       scale = meta_prefs_get_ui_scale ();
10765 
10766       if (window->size_hints.width_inc > scale ||
10767           window->size_hints.height_inc > scale)
10768         {
10769           window->display->grab_resize_popup =
10770             meta_ui_resize_popup_new (window->display->xdisplay,
10771                                       window->screen->number);
10772         }
10773     }
10774 
10775   if (window->display->grab_resize_popup != NULL)
10776     {
10777       MetaRectangle rect;
10778 
10779       meta_window_get_client_root_coords (window, &rect);
10780 
10781       meta_ui_resize_popup_set (window->display->grab_resize_popup,
10782                                 rect,
10783                                 window->size_hints.base_width,
10784                                 window->size_hints.base_height,
10785                                 window->size_hints.width_inc,
10786                                 window->size_hints.height_inc);
10787 
10788       meta_ui_resize_popup_set_showing (window->display->grab_resize_popup,
10789                                         TRUE);
10790     }
10791 }
10792 
10793 /**
10794  * meta_window_foreach_transient:
10795  * @window: a #MetaWindow
10796  * @func: (scope call) (closure user_data): Called for each window which is a transient of @window (transitively)
10797  * @user_data: User data
10798  *
10799  * Call @func for every window which is either transient for @window, or is
10800  * a transient of a window which is in turn transient for @window.
10801  * The order of window enumeration is not defined.
10802  *
10803  * Iteration will stop if @func at any point returns %FALSE.
10804  */
10805 void
meta_window_foreach_transient(MetaWindow * window,MetaWindowForeachFunc func,void * user_data)10806 meta_window_foreach_transient (MetaWindow            *window,
10807                                MetaWindowForeachFunc  func,
10808                                void                  *user_data)
10809 {
10810   GSList *windows;
10811   GSList *tmp;
10812 
10813   windows = meta_display_list_windows (window->display, META_LIST_DEFAULT);
10814 
10815   tmp = windows;
10816   while (tmp != NULL)
10817     {
10818       MetaWindow *transient = tmp->data;
10819 
10820       if (meta_window_is_ancestor_of_transient (window, transient))
10821         {
10822           if (!(* func) (transient, user_data))
10823             break;
10824         }
10825 
10826       tmp = tmp->next;
10827     }
10828 
10829   g_slist_free (windows);
10830 }
10831 
10832 /**
10833  * meta_window_foreach_ancestor:
10834  * @window: a #MetaWindow
10835  * @func: (scope call) (closure user_data): Called for each window which is a transient parent of @window
10836  * @user_data: User data
10837  *
10838  * If @window is transient, call @func with the window for which it's transient,
10839  * repeatedly until either we find a non-transient window, or @func returns %FALSE.
10840  */
10841 void
meta_window_foreach_ancestor(MetaWindow * window,MetaWindowForeachFunc func,void * user_data)10842 meta_window_foreach_ancestor (MetaWindow            *window,
10843                               MetaWindowForeachFunc  func,
10844                               void                  *user_data)
10845 {
10846   MetaWindow *w;
10847 
10848   w = window;
10849   do
10850     {
10851       if (w->xtransient_for == None ||
10852           w->transient_parent_is_root_window)
10853         break;
10854 
10855       w = meta_display_lookup_x_window (w->display, w->xtransient_for);
10856     }
10857   while (w && (* func) (w, user_data));
10858 }
10859 
10860 typedef struct
10861 {
10862   MetaWindow *ancestor;
10863   gboolean found;
10864 } FindAncestorData;
10865 
10866 static gboolean
find_ancestor_func(MetaWindow * window,void * data)10867 find_ancestor_func (MetaWindow *window,
10868                     void       *data)
10869 {
10870   FindAncestorData *d = data;
10871 
10872   if (window == d->ancestor)
10873     {
10874       d->found = TRUE;
10875       return FALSE;
10876     }
10877 
10878   return TRUE;
10879 }
10880 
10881 /**
10882  * meta_window_is_ancestor_of_transient:
10883  * @window: a #MetaWindow
10884  * @transient: a #MetaWindow
10885  *
10886  * The function determines whether @window is an ancestor of @transient; it does
10887  * so by traversing the @transient's ancestors until it either locates @window
10888  * or reaches an ancestor that is not transient.
10889  *
10890  * Return Value: %TRUE if window is an ancestor of transient.
10891  */
10892 gboolean
meta_window_is_ancestor_of_transient(MetaWindow * window,MetaWindow * transient)10893 meta_window_is_ancestor_of_transient (MetaWindow *window,
10894                                       MetaWindow *transient)
10895 {
10896   FindAncestorData d;
10897 
10898   d.ancestor = window;
10899   d.found = FALSE;
10900 
10901   meta_window_foreach_ancestor (transient, find_ancestor_func, &d);
10902 
10903   return d.found;
10904 }
10905 
10906 /* Warp pointer to location appropriate for grab,
10907  * return root coordinates where pointer ended up.
10908  */
10909 static gboolean
warp_grab_pointer(MetaWindow * window,MetaGrabOp grab_op,int * x,int * y)10910 warp_grab_pointer (MetaWindow          *window,
10911                    MetaGrabOp           grab_op,
10912                    int                 *x,
10913                    int                 *y)
10914 {
10915   MetaRectangle  rect;
10916   MetaDisplay   *display;
10917 
10918   display = window->display;
10919 
10920   /* We may not have done begin_grab_op yet, i.e. may not be in a grab
10921    */
10922 
10923   meta_window_get_outer_rect (window, &rect);
10924 
10925   switch (grab_op)
10926     {
10927       case META_GRAB_OP_KEYBOARD_MOVING:
10928       case META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN:
10929         *x = rect.width / 2;
10930         *y = rect.height / 2;
10931         break;
10932 
10933       case META_GRAB_OP_KEYBOARD_RESIZING_S:
10934         *x = rect.width / 2;
10935         *y = rect.height - 1;
10936         break;
10937 
10938       case META_GRAB_OP_KEYBOARD_RESIZING_N:
10939         *x = rect.width / 2;
10940         *y = 0;
10941         break;
10942 
10943       case META_GRAB_OP_KEYBOARD_RESIZING_W:
10944         *x = 0;
10945         *y = rect.height / 2;
10946         break;
10947 
10948       case META_GRAB_OP_KEYBOARD_RESIZING_E:
10949         *x = rect.width - 1;
10950         *y = rect.height / 2;
10951         break;
10952 
10953       case META_GRAB_OP_KEYBOARD_RESIZING_SE:
10954         *x = rect.width - 1;
10955         *y = rect.height - 1;
10956         break;
10957 
10958       case META_GRAB_OP_KEYBOARD_RESIZING_NE:
10959         *x = rect.width - 1;
10960         *y = 0;
10961         break;
10962 
10963       case META_GRAB_OP_KEYBOARD_RESIZING_SW:
10964         *x = 0;
10965         *y = rect.height - 1;
10966         break;
10967 
10968       case META_GRAB_OP_KEYBOARD_RESIZING_NW:
10969         *x = 0;
10970         *y = 0;
10971         break;
10972 
10973       default:
10974         return FALSE;
10975     }
10976 
10977   *x += rect.x;
10978   *y += rect.y;
10979 
10980   /* Avoid weird bouncing at the screen edge; see bug 154706 */
10981   *x = CLAMP (*x, 0, window->screen->rect.width-1);
10982   *y = CLAMP (*y, 0, window->screen->rect.height-1);
10983 
10984   meta_error_trap_push_with_return (display);
10985 
10986   meta_topic (META_DEBUG_WINDOW_OPS,
10987               "Warping pointer to %d,%d with window at %d,%d\n",
10988               *x, *y, rect.x, rect.y);
10989 
10990   /* Need to update the grab positions so that the MotionNotify and other
10991    * events generated by the XWarpPointer() call below don't cause complete
10992    * funkiness.  See bug 124582 and bug 122670.
10993    */
10994   display->grab_anchor_root_x = *x;
10995   display->grab_anchor_root_y = *y;
10996   display->grab_latest_motion_x = *x;
10997   display->grab_latest_motion_y = *y;
10998   meta_window_get_client_root_coords (window,
10999                                       &display->grab_anchor_window_pos);
11000 
11001   XWarpPointer (display->xdisplay,
11002                 None,
11003                 window->screen->xroot,
11004                 0, 0, 0, 0,
11005                 *x, *y);
11006 
11007   if (meta_error_trap_pop_with_return (display) != Success)
11008     {
11009       meta_verbose ("Failed to warp pointer for window %s\n",
11010                     window->desc);
11011       return FALSE;
11012     }
11013 
11014   return TRUE;
11015 }
11016 
11017 LOCAL_SYMBOL void
meta_window_begin_grab_op(MetaWindow * window,MetaGrabOp op,gboolean frame_action,guint32 timestamp)11018 meta_window_begin_grab_op (MetaWindow *window,
11019                            MetaGrabOp  op,
11020                            gboolean    frame_action,
11021                            guint32     timestamp)
11022 {
11023   int x, y;
11024 
11025   warp_grab_pointer (window,
11026                      op, &x, &y);
11027 
11028   meta_display_begin_grab_op (window->display,
11029                               window->screen,
11030                               window,
11031                               op,
11032                               FALSE,
11033                               frame_action,
11034                               0 /* button */,
11035                               0,
11036                               timestamp,
11037                               x, y);
11038 }
11039 
11040 LOCAL_SYMBOL void
meta_window_update_keyboard_resize(MetaWindow * window,gboolean update_cursor)11041 meta_window_update_keyboard_resize (MetaWindow *window,
11042                                     gboolean    update_cursor)
11043 {
11044   int x, y;
11045 
11046   warp_grab_pointer (window,
11047                      window->display->grab_op,
11048                      &x, &y);
11049 
11050   if (update_cursor)
11051     {
11052       guint32 timestamp;
11053       /* FIXME: Using CurrentTime is really bad mojo */
11054       timestamp = CurrentTime;
11055       meta_display_set_grab_op_cursor (window->display,
11056                                        NULL,
11057                                        window->display->grab_op,
11058                                        TRUE,
11059                                        window->display->grab_xwindow,
11060                                        timestamp);
11061     }
11062 }
11063 
11064 LOCAL_SYMBOL void
meta_window_update_keyboard_move(MetaWindow * window)11065 meta_window_update_keyboard_move (MetaWindow *window)
11066 {
11067   int x, y;
11068 
11069   warp_grab_pointer (window,
11070                      window->display->grab_op,
11071                      &x, &y);
11072 }
11073 
11074 LOCAL_SYMBOL void
meta_window_update_layer(MetaWindow * window)11075 meta_window_update_layer (MetaWindow *window)
11076 {
11077   MetaGroup *group;
11078 
11079   meta_stack_freeze (window->screen->stack);
11080   group = meta_window_get_group (window);
11081   if (group)
11082     meta_group_update_layers (group);
11083   else
11084     meta_stack_update_layer (window->screen->stack, window);
11085   meta_stack_thaw (window->screen->stack);
11086 }
11087 
11088 /* ensure_mru_position_after ensures that window appears after
11089  * below_this_one in the active_workspace's mru_list (i.e. it treats
11090  * window as having been less recently used than below_this_one)
11091  */
11092 static void
ensure_mru_position_after(MetaWindow * window,MetaWindow * after_this_one)11093 ensure_mru_position_after (MetaWindow *window,
11094                            MetaWindow *after_this_one)
11095 {
11096   /* This is sort of slow since it runs through the entire list more
11097    * than once (especially considering the fact that we expect the
11098    * windows of interest to be the first two elements in the list),
11099    * but it doesn't matter while we're only using it on new window
11100    * map.
11101    */
11102 
11103   GList* active_mru_list;
11104   GList* window_position;
11105   GList* after_this_one_position;
11106 
11107   active_mru_list         = window->screen->active_workspace->mru_list;
11108   after_this_one_position = g_list_find (active_mru_list, after_this_one);
11109 
11110   /* after_this_one_position is NULL when we switch workspaces, but in
11111    * that case we don't need to do any MRU shuffling so we can simply
11112    * return.
11113    */
11114   if (after_this_one_position == NULL)
11115     return;
11116 
11117   window_position         = g_list_find (active_mru_list, window);
11118   if (g_list_length (window_position) > g_list_length (after_this_one_position))
11119     {
11120       window->screen->active_workspace->mru_list =
11121         g_list_delete_link (window->screen->active_workspace->mru_list,
11122                             window_position);
11123 
11124       window->screen->active_workspace->mru_list =
11125         g_list_insert_before (window->screen->active_workspace->mru_list,
11126                               after_this_one_position->next,
11127                               window);
11128     }
11129 }
11130 
11131 LOCAL_SYMBOL void
meta_window_stack_just_below(MetaWindow * window,MetaWindow * below_this_one)11132 meta_window_stack_just_below (MetaWindow *window,
11133                               MetaWindow *below_this_one)
11134 {
11135   g_return_if_fail (window         != NULL);
11136   g_return_if_fail (below_this_one != NULL);
11137 
11138   if (window->stack_position > below_this_one->stack_position)
11139     {
11140       meta_topic (META_DEBUG_STACK,
11141                   "Setting stack position of window %s to %d (making it below window %s).\n",
11142                   window->desc,
11143                   below_this_one->stack_position,
11144                   below_this_one->desc);
11145       meta_window_set_stack_position (window, below_this_one->stack_position);
11146     }
11147   else
11148     {
11149       meta_topic (META_DEBUG_STACK,
11150                   "Window %s  was already below window %s.\n",
11151                   window->desc, below_this_one->desc);
11152     }
11153 }
11154 
11155 void
meta_window_stack_just_above(MetaWindow * window,MetaWindow * above_this_one)11156 meta_window_stack_just_above (MetaWindow *window,
11157                               MetaWindow *above_this_one)
11158 {
11159   g_return_if_fail (window         != NULL);
11160   g_return_if_fail (above_this_one != NULL);
11161 
11162   if (window->stack_position < above_this_one->stack_position)
11163     {
11164       meta_topic (META_DEBUG_STACK,
11165                   "Setting stack position of window %s to %d (making it above window %s).\n",
11166                   window->desc,
11167                   above_this_one->stack_position,
11168                   above_this_one->desc);
11169       meta_window_set_stack_position (window, above_this_one->stack_position);
11170     }
11171   else
11172     {
11173       meta_topic (META_DEBUG_STACK,
11174                   "Window %s  was already above window %s.\n",
11175                   window->desc, above_this_one->desc);
11176     }
11177 }
11178 
11179 /**
11180  * meta_window_get_user_time:
11181  *
11182  * The user time represents a timestamp for the last time the user
11183  * interacted with this window.  Note this property is only available
11184  * for non-override-redirect windows.
11185  *
11186  * The property is set by Muffin initially upon window creation,
11187  * and updated thereafter on input events (key and button presses) seen by Muffin,
11188  * client updates to the _NET_WM_USER_TIME property (if later than the current time)
11189  * and when focusing the window.
11190  *
11191  * Returns: The last time the user interacted with this window.
11192  */
11193 guint32
meta_window_get_user_time(MetaWindow * window)11194 meta_window_get_user_time (MetaWindow *window)
11195 {
11196   return window->net_wm_user_time;
11197 }
11198 
11199 LOCAL_SYMBOL void
meta_window_set_user_time(MetaWindow * window,guint32 timestamp)11200 meta_window_set_user_time (MetaWindow *window,
11201                            guint32     timestamp)
11202 {
11203   /* FIXME: If Soeren's suggestion in bug 151984 is implemented, it will allow
11204    * us to sanity check the timestamp here and ensure it doesn't correspond to
11205    * a future time.
11206    */
11207 
11208   g_return_if_fail (!window->override_redirect);
11209 
11210   /* Only update the time if this timestamp is newer... */
11211   if (window->net_wm_user_time_set &&
11212       XSERVER_TIME_IS_BEFORE (timestamp, window->net_wm_user_time))
11213     {
11214       meta_topic (META_DEBUG_STARTUP,
11215                   "Window %s _NET_WM_USER_TIME not updated to %u, because it "
11216                   "is less than %u\n",
11217                   window->desc, timestamp, window->net_wm_user_time);
11218     }
11219   else
11220     {
11221       meta_topic (META_DEBUG_STARTUP,
11222                   "Window %s has _NET_WM_USER_TIME of %u\n",
11223                   window->desc, timestamp);
11224       window->net_wm_user_time_set = TRUE;
11225       window->net_wm_user_time = timestamp;
11226       if (XSERVER_TIME_IS_BEFORE (window->display->last_user_time, timestamp))
11227         window->display->last_user_time = timestamp;
11228 
11229       /* If this is a terminal, user interaction with it means the user likely
11230        * doesn't want to have focus transferred for now due to new windows.
11231        */
11232       if (meta_prefs_get_focus_new_windows () ==
11233                C_DESKTOP_FOCUS_NEW_WINDOWS_STRICT &&
11234           __window_is_terminal (window))
11235         window->display->allow_terminal_deactivation = FALSE;
11236     }
11237 
11238   g_object_notify (G_OBJECT (window), "user-time");
11239 }
11240 
11241 /**
11242  * meta_window_get_stable_sequence:
11243  * @window: A #MetaWindow
11244  *
11245  * The stable sequence number is a monotonicially increasing
11246  * unique integer assigned to each #MetaWindow upon creation.
11247  *
11248  * This number can be useful for sorting windows in a stable
11249  * fashion.
11250  *
11251  * Returns: Internal sequence number for this window
11252  */
11253 guint32
meta_window_get_stable_sequence(MetaWindow * window)11254 meta_window_get_stable_sequence (MetaWindow *window)
11255 {
11256   g_return_val_if_fail (META_IS_WINDOW (window), 0);
11257 
11258   return window->stable_sequence;
11259 }
11260 
11261 /* Sets the demands_attention hint on a window, but only
11262  * if it's at least partially obscured (see #305882).
11263  */
11264 void
meta_window_set_demands_attention(MetaWindow * window)11265 meta_window_set_demands_attention (MetaWindow *window)
11266 {
11267 
11268   GList *stack;
11269   MetaRectangle candidate_rect, other_rect;
11270   MetaWindow *other_window;
11271   gboolean obscured;
11272   MetaWorkspace *workspace;
11273 
11274   if (window->wm_state_demands_attention)
11275     return;
11276 
11277   obscured = FALSE;
11278   stack = window->screen->stack->sorted;
11279   workspace = window->screen->active_workspace;
11280 
11281   if (workspace!=window->workspace)
11282     {
11283       /* windows on other workspaces are necessarily obscured */
11284       obscured = TRUE;
11285     }
11286   else if (window->minimized)
11287     {
11288       obscured = TRUE;
11289     }
11290   else
11291     {
11292       meta_window_get_outer_rect (window, &candidate_rect);
11293 
11294       /* The stack is sorted with the top windows first. */
11295 
11296       while (stack != NULL && stack->data != window)
11297         {
11298           other_window = stack->data;
11299           stack = stack->next;
11300 
11301           if (other_window->on_all_workspaces ||
11302               window->on_all_workspaces ||
11303               other_window->workspace == window->workspace)
11304             {
11305               meta_window_get_outer_rect (other_window, &other_rect);
11306 
11307               if (meta_rectangle_overlap (&candidate_rect, &other_rect))
11308                 {
11309                   obscured = TRUE;
11310                   break;
11311                 }
11312             }
11313         }
11314     }
11315 
11316   if (obscured)
11317     {
11318       meta_topic (META_DEBUG_WINDOW_OPS,
11319                   "Marking %s as needing attention\n",
11320                   window->desc);
11321 
11322       window->wm_state_demands_attention = TRUE;
11323       set_net_wm_state (window);
11324       g_object_notify (G_OBJECT (window), "demands-attention");
11325       g_signal_emit_by_name (window->display, "window-demands-attention",
11326                              window);
11327     }
11328 #ifdef WITH_VERBOSE_MODE
11329   else
11330     {
11331       /* If the window's in full view, there's no point setting the flag. */
11332 
11333       meta_topic (META_DEBUG_WINDOW_OPS,
11334                  "Not marking %s as needing attention because "
11335                  "it's in full view\n",
11336                  window->desc);
11337     }
11338 #endif
11339 }
11340 
11341 /**
11342  * meta_window_is_demanding_attention:
11343  * @window: A #MetaWindow
11344  *
11345  * Returns true if window has the demands-attention flag set.
11346  *
11347  * Return value: %TRUE if wm_state_demands_attention is set.
11348  */
11349 gboolean
meta_window_is_demanding_attention(MetaWindow * window)11350 meta_window_is_demanding_attention (MetaWindow *window)
11351 {
11352   return window->wm_state_demands_attention;
11353 }
11354 
11355 /**
11356  * meta_window_is_urgent:
11357  * @window: A #MetaWindow
11358  *
11359  * Returns true if window has the urgent hint set.
11360  *
11361  * Return value: %TRUE if wm_hints_urgent is set.
11362  */
11363 gboolean
meta_window_is_urgent(MetaWindow * window)11364 meta_window_is_urgent (MetaWindow *window)
11365 {
11366   return window->wm_hints_urgent;
11367 }
11368 
11369 void
meta_window_unset_demands_attention(MetaWindow * window)11370 meta_window_unset_demands_attention (MetaWindow *window)
11371 {
11372   meta_topic (META_DEBUG_WINDOW_OPS,
11373       "Marking %s as not needing attention\n", window->desc);
11374 
11375   if (window->wm_state_demands_attention)
11376     {
11377       window->wm_state_demands_attention = FALSE;
11378       set_net_wm_state (window);
11379       g_object_notify (G_OBJECT (window), "demands-attention");
11380     }
11381 }
11382 
11383 /**
11384  * meta_window_get_frame: (skip)
11385  *
11386  */
11387 MetaFrame *
meta_window_get_frame(MetaWindow * window)11388 meta_window_get_frame (MetaWindow *window)
11389 {
11390   return window->frame;
11391 }
11392 
11393 /**
11394  * meta_window_appears_focused:
11395  * @window: a #MetaWindow
11396  *
11397  * Determines if the window should be drawn with a focused appearance. This is
11398  * true for focused windows but also true for windows with a focused modal
11399  * dialog attached.
11400  *
11401  * Return value: %TRUE if the window should be drawn with a focused frame
11402  */
11403 gboolean
meta_window_appears_focused(MetaWindow * window)11404 meta_window_appears_focused (MetaWindow *window)
11405 {
11406   return window->has_focus || (window->attached_focus_window != NULL);
11407 }
11408 
11409 gboolean
meta_window_has_focus(MetaWindow * window)11410 meta_window_has_focus (MetaWindow *window)
11411 {
11412   return window->has_focus;
11413 }
11414 
11415 gboolean
meta_window_is_shaded(MetaWindow * window)11416 meta_window_is_shaded (MetaWindow *window)
11417 {
11418   return window->shaded;
11419 }
11420 
11421 /**
11422  * meta_window_is_override_redirect:
11423  * @window: A #MetaWindow
11424  *
11425  * Returns if this window isn't managed by muffin; it will
11426  * control its own positioning and muffin won't draw decorations
11427  * among other things.  In X terminology this is "override redirect".
11428  */
11429 gboolean
meta_window_is_override_redirect(MetaWindow * window)11430 meta_window_is_override_redirect (MetaWindow *window)
11431 {
11432   return window->override_redirect;
11433 }
11434 
11435 /**
11436  * meta_window_is_skip_taskbar:
11437  * @window: A #MetaWindow
11438  *
11439  * Gets whether this window should be ignored by task lists.
11440  *
11441  * Return value: %TRUE if the skip bar hint is set.
11442  */
11443 gboolean
meta_window_is_skip_taskbar(MetaWindow * window)11444 meta_window_is_skip_taskbar (MetaWindow *window)
11445 {
11446   g_return_val_if_fail (META_IS_WINDOW (window), FALSE);
11447 
11448   return window->skip_taskbar;
11449 }
11450 
11451 gboolean
meta_window_is_interesting(MetaWindow * window)11452 meta_window_is_interesting (MetaWindow *window)
11453 {
11454     if (window->override_redirect
11455       || window->skip_taskbar)
11456     return FALSE;
11457 
11458   switch (window->type)
11459     {
11460       /* Definitely ignore these. */
11461       case META_WINDOW_DESKTOP:
11462       case META_WINDOW_DOCK:
11463       case META_WINDOW_SPLASHSCREEN:
11464       /* Should have already been handled by override_redirect above,
11465        * but explicitly list here so we get the "unhandled enum"
11466        * warning if in the future anything is added.*/
11467       case META_WINDOW_DROPDOWN_MENU:
11468       case META_WINDOW_POPUP_MENU:
11469       case META_WINDOW_TOOLTIP:
11470       case META_WINDOW_NOTIFICATION:
11471       case META_WINDOW_COMBO:
11472       case META_WINDOW_DND:
11473       case META_WINDOW_OVERRIDE_OTHER:
11474         return FALSE;
11475       case META_WINDOW_NORMAL:
11476       case META_WINDOW_DIALOG:
11477       case META_WINDOW_MODAL_DIALOG:
11478       case META_WINDOW_MENU:
11479       case META_WINDOW_TOOLBAR:
11480       case META_WINDOW_UTILITY:
11481         break;
11482       default:
11483         g_warning ("meta_window_is_interesting: default reached");
11484       break;
11485     }
11486 
11487   return TRUE;
11488 }
11489 
11490 /**
11491  * meta_window_get_rect:
11492  * @window: a #MetaWindow
11493  *
11494  * Gets the rectangle that bounds @window, ignoring any window decorations.
11495  *
11496  * Return value: (transfer none): the #MetaRectangle for the window
11497  */
11498 MetaRectangle *
meta_window_get_rect(MetaWindow * window)11499 meta_window_get_rect (MetaWindow *window)
11500 {
11501   return &window->rect;
11502 }
11503 
11504 /**
11505  * meta_window_get_screen:
11506  * @window: a #MetaWindow
11507  *
11508  * Gets the #MetaScreen that the window is on.
11509  *
11510  * Return value: (transfer none): the #MetaScreen for the window
11511  */
11512 MetaScreen *
meta_window_get_screen(MetaWindow * window)11513 meta_window_get_screen (MetaWindow *window)
11514 {
11515   return window->screen;
11516 }
11517 
11518 /**
11519  * meta_window_get_display:
11520  * @window: A #MetaWindow
11521  *
11522  * Returns: (transfer none): The display for @window
11523  */
11524 MetaDisplay *
meta_window_get_display(MetaWindow * window)11525 meta_window_get_display (MetaWindow *window)
11526 {
11527   return window->display;
11528 }
11529 
11530 /**
11531  * meta_window_get_xwindow:
11532  * @window: A #MetaWindow
11533  *
11534  * Returns: The Window id of the @window
11535  * note - we return unsigned long because Window
11536  * Can't be introspected (but Window *is* a ulong)
11537  *
11538  */
11539 unsigned long
meta_window_get_xwindow(MetaWindow * window)11540 meta_window_get_xwindow (MetaWindow *window)
11541 {
11542   return window->xwindow;
11543 }
11544 
11545 MetaWindowType
meta_window_get_window_type(MetaWindow * window)11546 meta_window_get_window_type (MetaWindow *window)
11547 {
11548   return window->type;
11549 }
11550 
11551 /**
11552  * meta_window_get_window_type_atom: (skip)
11553  * @window: a #MetaWindow
11554  *
11555  * Gets the X atom from the _NET_WM_WINDOW_TYPE property used by the
11556  * application to set the window type. (Note that this is constrained
11557  * to be some value that Muffin recognizes - a completely unrecognized
11558  * type atom will be returned as None.)
11559  *
11560  * Return value: the raw X atom for the window type, or None
11561  */
11562 Atom
meta_window_get_window_type_atom(MetaWindow * window)11563 meta_window_get_window_type_atom (MetaWindow *window)
11564 {
11565   return window->type_atom;
11566 }
11567 
11568 /**
11569  * meta_window_get_workspace:
11570  * @window: a #MetaWindow
11571  *
11572  * Gets the #MetaWorkspace that the window is currently displayed on.
11573  * If the window is on all workspaces, returns the currently active
11574  * workspace.
11575  *
11576  * Return value: (transfer none): the #MetaWorkspace for the window
11577  */
11578 MetaWorkspace *
meta_window_get_workspace(MetaWindow * window)11579 meta_window_get_workspace (MetaWindow *window)
11580 {
11581   if (window->on_all_workspaces)
11582     return window->screen->active_workspace;
11583 
11584   return window->workspace;
11585 }
11586 
11587 gboolean
meta_window_is_on_all_workspaces(MetaWindow * window)11588 meta_window_is_on_all_workspaces (MetaWindow *window)
11589 {
11590   return window->on_all_workspaces;
11591 }
11592 
11593 gboolean
meta_window_is_hidden(MetaWindow * window)11594 meta_window_is_hidden (MetaWindow *window)
11595 {
11596   return window->hidden;
11597 }
11598 
11599 const char *
meta_window_get_description(MetaWindow * window)11600 meta_window_get_description (MetaWindow *window)
11601 {
11602   if (!window)
11603     return NULL;
11604 
11605   return window->desc;
11606 }
11607 
11608 /**
11609  * meta_window_get_wm_class:
11610  * @window: a #MetaWindow
11611  *
11612  * Return the current value of the name part of WM_CLASS X property.
11613  */
11614 const char *
meta_window_get_wm_class(MetaWindow * window)11615 meta_window_get_wm_class (MetaWindow *window)
11616 {
11617   if (!window)
11618     return NULL;
11619 
11620   return window->res_class;
11621 }
11622 
11623 /**
11624  * meta_window_get_wm_class_instance:
11625  * @window: a #MetaWindow
11626  *
11627  * Return the current value of the instance part of WM_CLASS X property.
11628  */
11629 const char *
meta_window_get_wm_class_instance(MetaWindow * window)11630 meta_window_get_wm_class_instance (MetaWindow *window)
11631 {
11632   if (!window)
11633     return NULL;
11634 
11635   return window->res_name;
11636 }
11637 
11638 /**
11639  * meta_window_get_gtk_application_id:
11640  * @window: a #MetaWindow
11641  *
11642  * Return value: (transfer none): the application ID
11643  **/
11644 const char *
meta_window_get_gtk_application_id(MetaWindow * window)11645 meta_window_get_gtk_application_id (MetaWindow *window)
11646 {
11647   return window->gtk_application_id;
11648 }
11649 
11650 /**
11651  * meta_window_get_gtk_unique_bus_name:
11652  * @window: a #MetaWindow
11653  *
11654  * Return value: (transfer none): the unique name
11655  **/
11656 const char *
meta_window_get_gtk_unique_bus_name(MetaWindow * window)11657 meta_window_get_gtk_unique_bus_name (MetaWindow *window)
11658 {
11659   return window->gtk_unique_bus_name;
11660 }
11661 
11662 /**
11663  * meta_window_get_gtk_application_object_path:
11664  * @window: a #MetaWindow
11665  *
11666  * Return value: (transfer none): the object path
11667  **/
11668 const char *
meta_window_get_gtk_application_object_path(MetaWindow * window)11669 meta_window_get_gtk_application_object_path (MetaWindow *window)
11670 {
11671   return window->gtk_application_object_path;
11672 }
11673 
11674 /**
11675  * meta_window_get_gtk_window_object_path:
11676  * @window: a #MetaWindow
11677  *
11678  * Return value: (transfer none): the object path
11679  **/
11680 const char *
meta_window_get_gtk_window_object_path(MetaWindow * window)11681 meta_window_get_gtk_window_object_path (MetaWindow *window)
11682 {
11683   return window->gtk_window_object_path;
11684 }
11685 
11686 /**
11687  * meta_window_get_gtk_app_menu_object_path:
11688  * @window: a #MetaWindow
11689  *
11690  * Return value: (transfer none): the object path
11691  **/
11692 const char *
meta_window_get_gtk_app_menu_object_path(MetaWindow * window)11693 meta_window_get_gtk_app_menu_object_path (MetaWindow *window)
11694 {
11695   return window->gtk_app_menu_object_path;
11696 }
11697 
11698 /**
11699  * meta_window_get_gtk_menubar_object_path:
11700  * @window: a #MetaWindow
11701  *
11702  * Return value: (transfer none): the object path
11703  **/
11704 const char *
meta_window_get_gtk_menubar_object_path(MetaWindow * window)11705 meta_window_get_gtk_menubar_object_path (MetaWindow *window)
11706 {
11707   return window->gtk_menubar_object_path;
11708 }
11709 
11710 /**
11711  * meta_window_get_compositor_private:
11712  * @window: a #MetaWindow
11713  *
11714  * Gets the compositor's wrapper object for @window.
11715  *
11716  * Return value: (transfer none): the wrapper object.
11717  **/
11718 GObject *
meta_window_get_compositor_private(MetaWindow * window)11719 meta_window_get_compositor_private (MetaWindow *window)
11720 {
11721   if (!window)
11722     return NULL;
11723   return window->compositor_private;
11724 }
11725 
11726 void
meta_window_set_compositor_private(MetaWindow * window,GObject * priv)11727 meta_window_set_compositor_private (MetaWindow *window, GObject *priv)
11728 {
11729   if (!window)
11730     return;
11731   window->compositor_private = priv;
11732 }
11733 
11734 const char *
meta_window_get_role(MetaWindow * window)11735 meta_window_get_role (MetaWindow *window)
11736 {
11737   if (!window)
11738     return NULL;
11739 
11740   return window->role;
11741 }
11742 
11743 /**
11744  * meta_window_get_title:
11745  * @window: a #MetaWindow
11746  *
11747  * Returns the current title of the window.
11748  */
11749 const char *
meta_window_get_title(MetaWindow * window)11750 meta_window_get_title (MetaWindow *window)
11751 {
11752   g_return_val_if_fail (META_IS_WINDOW (window), NULL);
11753 
11754   return window->title;
11755 }
11756 
11757 MetaStackLayer
meta_window_get_layer(MetaWindow * window)11758 meta_window_get_layer (MetaWindow *window)
11759 {
11760   return window->layer;
11761 }
11762 
11763 /**
11764  * meta_window_get_transient_for:
11765  * @window: a #MetaWindow
11766  *
11767  * Returns the #MetaWindow for the window that is pointed to by the
11768  * WM_TRANSIENT_FOR hint on this window (see XGetTransientForHint()
11769  * or XSetTransientForHint()). Metacity keeps transient windows above their
11770  * parents. A typical usage of this hint is for a dialog that wants to stay
11771  * above its associated window.
11772  *
11773  * Return value: (transfer none): the window this window is transient for, or
11774  * %NULL if the WM_TRANSIENT_FOR hint is unset or does not point to a toplevel
11775  * window that Metacity knows about.
11776  */
11777 MetaWindow *
meta_window_get_transient_for(MetaWindow * window)11778 meta_window_get_transient_for (MetaWindow *window)
11779 {
11780   g_return_val_if_fail (META_IS_WINDOW (window), NULL);
11781 
11782   if (window->xtransient_for)
11783     return meta_display_lookup_x_window (window->display,
11784                                          window->xtransient_for);
11785   else
11786     return NULL;
11787 }
11788 
11789 /**
11790  * meta_window_get_transient_for_as_xid:
11791  * @window: a #MetaWindow
11792  *
11793  * Returns the XID of the window that is pointed to by the
11794  * WM_TRANSIENT_FOR hint on this window (see XGetTransientForHint()
11795  * or XSetTransientForHint()). Metacity keeps transient windows above their
11796  * parents. A typical usage of this hint is for a dialog that wants to stay
11797  * above its associated window.
11798  *
11799  * Return value: the window this window is transient for, or
11800  * None if the WM_TRANSIENT_FOR hint is unset.
11801  */
11802 Window
meta_window_get_transient_for_as_xid(MetaWindow * window)11803 meta_window_get_transient_for_as_xid (MetaWindow *window)
11804 {
11805   return window->xtransient_for;
11806 }
11807 
11808 /**
11809  * meta_window_get_pid:
11810  * @window: a #MetaWindow
11811  *
11812  * Returns pid of the process that created this window, if known (obtained from
11813  * the _NET_WM_PID property).
11814  *
11815  * Return value: the pid, or -1 if not known.
11816  */
11817 int
meta_window_get_pid(MetaWindow * window)11818 meta_window_get_pid (MetaWindow *window)
11819 {
11820   g_return_val_if_fail (META_IS_WINDOW (window), -1);
11821 
11822   return window->net_wm_pid;
11823 }
11824 
11825 /**
11826  * meta_window_get_client_pid:
11827  * @window: a #MetaWindow
11828  *
11829  * Returns the client pid of the process that created this window, if known (obtained from XCB).
11830  *
11831  * Return value: the pid, or -1 if not known.
11832  */
11833 int
meta_window_get_client_pid(MetaWindow * window)11834 meta_window_get_client_pid (MetaWindow *window)
11835 {
11836   g_return_val_if_fail (META_IS_WINDOW (window), -1);
11837 
11838   if (window->client_pid == -2) {
11839     update_client_pid (window);
11840   }
11841 
11842   return window->client_pid;
11843 }
11844 
11845 /**
11846  * meta_window_get_client_machine:
11847  * @window: a #MetaWindow
11848  *
11849  * Returns name of the client machine from which this windows was created,
11850  * if known (obtained from the WM_CLIENT_MACHINE property).
11851  *
11852  * Return value: (transfer none): the machine name, or NULL; the string is
11853  * owned by the window manager and should not be freed or modified by the
11854  * caller.
11855  */
11856 const char *
meta_window_get_client_machine(MetaWindow * window)11857 meta_window_get_client_machine (MetaWindow *window)
11858 {
11859   g_return_val_if_fail (META_IS_WINDOW (window), NULL);
11860 
11861   return window->wm_client_machine;
11862 }
11863 
11864 /**
11865  * meta_window_is_remote:
11866  * @window: a #MetaWindow
11867  *
11868  * Returns: %TRUE if this window originates from a host
11869  * different from the one running muffin.
11870  */
11871 gboolean
meta_window_is_remote(MetaWindow * window)11872 meta_window_is_remote (MetaWindow *window)
11873 {
11874   g_return_val_if_fail (META_IS_WINDOW (window), FALSE);
11875 
11876   if (window->wm_client_machine != NULL)
11877     return g_strcmp0 (window->wm_client_machine, window->display->hostname) != 0;
11878   return FALSE;
11879 }
11880 
11881 /**
11882  * meta_window_is_modal:
11883  * @window: a #MetaWindow
11884  *
11885  * Queries whether the window is in a modal state as described by the
11886  * _NET_WM_STATE protocol.
11887  *
11888  * Return value: TRUE if the window is in modal state.
11889  */
11890 gboolean
meta_window_is_modal(MetaWindow * window)11891 meta_window_is_modal (MetaWindow *window)
11892 {
11893   g_return_val_if_fail (META_IS_WINDOW (window), FALSE);
11894 
11895   return window->wm_state_modal;
11896 }
11897 
11898 /**
11899  * meta_window_get_muffin_hints:
11900  * @window: a #MetaWindow
11901  *
11902  * Gets the current value of the _MUFFIN_HINTS property.
11903  *
11904  * The purpose of the hints is to allow fine-tuning of the Window Manager and
11905  * Compositor behaviour on per-window basis, and is intended primarily for
11906  * hints that are plugin-specific.
11907  *
11908  * The property is a list of colon-separated key=value pairs. The key names for
11909  * any plugin-specific hints must be suitably namespaced to allow for shared
11910  * use; 'muffin-' key prefix is reserved for internal use, and must not be used
11911  * by plugins.
11912  *
11913  * Return value: (transfer none): the _MUFFIN_HINTS string, or %NULL if no hints
11914  * are set.
11915  */
11916 const char *
meta_window_get_muffin_hints(MetaWindow * window)11917 meta_window_get_muffin_hints (MetaWindow *window)
11918 {
11919   g_return_val_if_fail (META_IS_WINDOW (window), NULL);
11920 
11921   return window->muffin_hints;
11922 }
11923 
11924 /**
11925  * meta_window_get_frame_type:
11926  * @window: a #MetaWindow
11927  *
11928  * Gets the type of window decorations that should be used for this window.
11929  *
11930  * Return value: the frame type
11931  */
11932 MetaFrameType
meta_window_get_frame_type(MetaWindow * window)11933 meta_window_get_frame_type (MetaWindow *window)
11934 {
11935   MetaFrameType base_type = META_FRAME_TYPE_LAST;
11936 
11937   switch (window->type)
11938     {
11939     case META_WINDOW_NORMAL:
11940       base_type = META_FRAME_TYPE_NORMAL;
11941       break;
11942 
11943     case META_WINDOW_DIALOG:
11944       base_type = META_FRAME_TYPE_DIALOG;
11945       break;
11946 
11947     case META_WINDOW_MODAL_DIALOG:
11948       if (meta_window_is_attached_dialog (window))
11949         base_type = META_FRAME_TYPE_ATTACHED;
11950       else
11951         base_type = META_FRAME_TYPE_MODAL_DIALOG;
11952       break;
11953 
11954     case META_WINDOW_MENU:
11955       base_type = META_FRAME_TYPE_MENU;
11956       break;
11957 
11958     case META_WINDOW_UTILITY:
11959       base_type = META_FRAME_TYPE_UTILITY;
11960       break;
11961 
11962     case META_WINDOW_DESKTOP:
11963     case META_WINDOW_DOCK:
11964     case META_WINDOW_TOOLBAR:
11965     case META_WINDOW_SPLASHSCREEN:
11966     case META_WINDOW_DROPDOWN_MENU:
11967     case META_WINDOW_POPUP_MENU:
11968     case META_WINDOW_TOOLTIP:
11969     case META_WINDOW_NOTIFICATION:
11970     case META_WINDOW_COMBO:
11971     case META_WINDOW_DND:
11972     case META_WINDOW_OVERRIDE_OTHER:
11973       /* No frame */
11974       base_type = META_FRAME_TYPE_LAST;
11975       break;
11976     }
11977 
11978   if (base_type == META_FRAME_TYPE_LAST)
11979     {
11980       /* can't add border if undecorated */
11981       return META_FRAME_TYPE_LAST;
11982     }
11983   else if ((window->border_only && base_type != META_FRAME_TYPE_ATTACHED) ||
11984            (window->hide_titlebar_when_maximized && META_WINDOW_MAXIMIZED (window)))
11985     {
11986       /* override base frame type */
11987       return META_FRAME_TYPE_BORDER;
11988     }
11989   else
11990     {
11991       return base_type;
11992     }
11993 }
11994 
11995 /**
11996  * meta_window_get_frame_bounds:
11997  *
11998  * Gets a region representing the outer bounds of the window's frame.
11999  *
12000  * Return value: (transfer none) (allow-none): a #cairo_region_t
12001  *  holding the outer bounds of the window, or %NULL if the window
12002  *  doesn't have a frame.
12003  */
12004 cairo_region_t *
meta_window_get_frame_bounds(MetaWindow * window)12005 meta_window_get_frame_bounds (MetaWindow *window)
12006 {
12007   if (!window->frame_bounds)
12008     {
12009       if (window->frame)
12010         window->frame_bounds = meta_frame_get_frame_bounds (window->frame);
12011     }
12012 
12013   return window->frame_bounds;
12014 }
12015 
12016 /**
12017  * meta_window_is_attached_dialog:
12018  * @window: a #MetaWindow
12019  *
12020  * Tests if @window is should be attached to its parent window.
12021  * (If the "attach_modal_dialogs" option is not enabled, this will
12022  * always return %FALSE.)
12023  *
12024  * Return value: whether @window should be attached to its parent
12025  */
12026 gboolean
meta_window_is_attached_dialog(MetaWindow * window)12027 meta_window_is_attached_dialog (MetaWindow *window)
12028 {
12029   return window->attached;
12030 }
12031 
12032 /**
12033  * meta_window_get_tile_match:
12034  *
12035  * Returns the matching tiled window on the same monitory as @window. This is
12036  * the topmost tiled window in a complementary tile mode that is:
12037  *
12038  *  - on the same monitor;
12039  *  - on the same workspace;
12040  *  - spanning the remaining monitor width;
12041  *  - there is no 3rd window stacked between both tiled windows that's
12042  *    partially visible in the common edge.
12043  *
12044  * Return value: (transfer none) (allow-none): the matching tiled window or
12045  * %NULL if it doesn't exist.
12046  */
12047 MetaWindow *
meta_window_get_tile_match(MetaWindow * window)12048 meta_window_get_tile_match (MetaWindow *window)
12049 {
12050   return window->tile_match;
12051 }
12052 
12053 LOCAL_SYMBOL void
meta_window_compute_tile_match(MetaWindow * window)12054 meta_window_compute_tile_match (MetaWindow *window)
12055 {
12056   MetaWindow *match;
12057   MetaStack *stack;
12058   MetaTileMode match_tile_mode;
12059 
12060   window->tile_match = NULL;
12061 
12062   if (window->shaded || window->minimized)
12063     return;
12064 
12065   match_tile_mode = META_TILE_NONE;
12066   if (META_WINDOW_TILED_LEFT (window))
12067     match_tile_mode = META_TILE_RIGHT;
12068   else if (META_WINDOW_TILED_RIGHT (window))
12069     match_tile_mode = META_TILE_LEFT;
12070   else if (META_WINDOW_TILED_ULC (window))
12071     match_tile_mode = META_TILE_ULC;
12072   else if (META_WINDOW_TILED_LLC (window))
12073     match_tile_mode = META_TILE_LLC;
12074   else if (META_WINDOW_TILED_URC (window))
12075     match_tile_mode = META_TILE_URC;
12076   else if (META_WINDOW_TILED_LRC (window))
12077     match_tile_mode = META_TILE_LRC;
12078   else if (META_WINDOW_TILED_TOP (window))
12079     match_tile_mode = META_TILE_TOP;
12080   else if (META_WINDOW_TILED_BOTTOM (window))
12081     match_tile_mode = META_TILE_BOTTOM;
12082   else
12083     return;
12084 
12085   stack = window->screen->stack;
12086 
12087   for (match = meta_stack_get_top (stack);
12088        match;
12089        match = meta_stack_get_below (stack, match, FALSE))
12090     {
12091       if (!match->shaded &&
12092           !match->minimized &&
12093           match->tile_mode == match_tile_mode &&
12094           match->monitor == window->monitor &&
12095           meta_window_get_workspace (match) == meta_window_get_workspace (window))
12096         break;
12097     }
12098 
12099   if (match)
12100     {
12101       MetaWindow *above, *bottommost, *topmost;
12102       MetaRectangle above_rect, bottommost_rect, topmost_rect;
12103 
12104       if (meta_stack_windows_cmp (window->screen->stack, match, window) > 0)
12105         {
12106           topmost = match;
12107           bottommost = window;
12108         }
12109       else
12110         {
12111           topmost = window;
12112           bottommost = match;
12113         }
12114 
12115       meta_window_get_outer_rect (bottommost, &bottommost_rect);
12116       meta_window_get_outer_rect (topmost, &topmost_rect);
12117       /*
12118        * If there's a window stacked in between which is partially visible
12119        * behind the topmost tile we don't consider the tiles to match.
12120        */
12121       for (above = meta_stack_get_above (stack, bottommost, FALSE);
12122            above && above != topmost;
12123            above = meta_stack_get_above (stack, above, FALSE))
12124         {
12125           if (above->minimized ||
12126               above->monitor != window->monitor ||
12127               meta_window_get_workspace (above) != meta_window_get_workspace (window))
12128             continue;
12129 
12130           meta_window_get_outer_rect (above, &above_rect);
12131 
12132           if (meta_rectangle_overlap (&above_rect, &bottommost_rect) &&
12133               meta_rectangle_overlap (&above_rect, &topmost_rect))
12134             return;
12135         }
12136 
12137       window->tile_match = match;
12138     }
12139 }
12140 
12141 void
meta_window_set_tile_type(MetaWindow * window,MetaWindowTileType type)12142 meta_window_set_tile_type (MetaWindow        *window,
12143                            MetaWindowTileType type)
12144 {
12145     g_return_if_fail (META_IS_WINDOW (window));
12146 
12147     if (window->tile_type != type) {
12148         window->tile_type = type;
12149         g_object_freeze_notify (G_OBJECT (window));
12150         g_object_notify (G_OBJECT (window), "tile-type");
12151         g_object_thaw_notify (G_OBJECT (window));
12152     }
12153 }
12154 
12155 MetaWindowTileType
meta_window_get_tile_type(MetaWindow * window)12156 meta_window_get_tile_type (MetaWindow *window)
12157 {
12158     g_return_val_if_fail (META_IS_WINDOW (window), META_WINDOW_TILE_TYPE_NONE);
12159 
12160     return window->tile_type;
12161 }
12162 
12163 #define ORIGIN_CONSTANT 1
12164 #define EXTREME_CONSTANT 2
12165 #define COMMON_EDGE_PADDING 10
12166 
12167 static void
get_extra_padding_for_common_monitor_edges(MetaWindow * window,gint monitor_num,MetaRectangle cur_rect,gint * left_shift,gint * right_shift,gint * up_shift,gint * down_shift)12168 get_extra_padding_for_common_monitor_edges (MetaWindow        *window,
12169                                                   gint         monitor_num,
12170                                          MetaRectangle         cur_rect,
12171                                                   gint        *left_shift,
12172                                                   gint       *right_shift,
12173                                                   gint          *up_shift,
12174                                                   gint        *down_shift)
12175 {
12176     gint num_mons;
12177     gint i;
12178     MetaRectangle other_rect;
12179     num_mons = meta_screen_get_n_monitors (window->screen);
12180 
12181     if (num_mons == 1)
12182         return;
12183 
12184     for (i = 0; i < num_mons; i++) {
12185         if (i == monitor_num)
12186             continue;
12187 
12188         meta_screen_get_monitor_geometry (window->screen, i, &other_rect);
12189 
12190         if (BOX_CENTER_X (other_rect) < BOX_LEFT (cur_rect)) {
12191             *left_shift += COMMON_EDGE_PADDING;
12192             continue;
12193         }
12194         if (BOX_CENTER_X (other_rect) > BOX_RIGHT (cur_rect)) {
12195             *right_shift += COMMON_EDGE_PADDING;
12196             continue;
12197         }
12198         if (BOX_CENTER_Y (other_rect) < BOX_TOP (cur_rect)) {
12199             *up_shift += COMMON_EDGE_PADDING;
12200             continue;
12201         }
12202         if (BOX_CENTER_Y (other_rect) > BOX_BOTTOM (cur_rect)) {
12203             *down_shift += COMMON_EDGE_PADDING;
12204             continue;
12205         }
12206     }
12207 }
12208 
12209 gboolean
meta_window_mouse_on_edge(MetaWindow * window,gint x,gint y)12210 meta_window_mouse_on_edge (MetaWindow *window, gint x, gint y)
12211 {
12212     MetaRectangle work_area;
12213     gboolean ret = FALSE;
12214     int monitor;
12215     gint left_shift, right_shift, up_shift, down_shift;
12216     left_shift = right_shift = up_shift = down_shift = 0;
12217 
12218     monitor = meta_screen_get_current_monitor (window->screen);
12219     meta_window_get_work_area_for_monitor (window, monitor, &work_area);
12220 
12221     get_extra_padding_for_common_monitor_edges (window,
12222                                                 monitor,
12223                                                 work_area,
12224                                                 &left_shift,
12225                                                 &right_shift,
12226                                                 &up_shift,
12227                                                 &down_shift);
12228 
12229     ret = x <= BOX_LEFT (work_area) + ORIGIN_CONSTANT + left_shift ||
12230           x >= BOX_RIGHT (work_area) - EXTREME_CONSTANT - right_shift ||
12231           y <= BOX_TOP (work_area) + ORIGIN_CONSTANT  + up_shift ||
12232           y >= BOX_BOTTOM (work_area) - EXTREME_CONSTANT - down_shift;
12233 
12234     return ret;
12235 }
12236 
12237 gboolean
meta_window_can_maximize(MetaWindow * window)12238 meta_window_can_maximize (MetaWindow *window)
12239 {
12240   return window->has_maximize_func;
12241 }
12242 
12243 gboolean
meta_window_can_minimize(MetaWindow * window)12244 meta_window_can_minimize (MetaWindow *window)
12245 {
12246   return window->has_minimize_func;
12247 }
12248 
12249 gboolean
meta_window_can_shade(MetaWindow * window)12250 meta_window_can_shade (MetaWindow *window)
12251 {
12252   return window->has_shade_func;
12253 }
12254 
12255 gboolean
meta_window_can_close(MetaWindow * window)12256 meta_window_can_close (MetaWindow *window)
12257 {
12258   return window->has_close_func;
12259 }
12260 
12261 gboolean
meta_window_is_always_on_all_workspaces(MetaWindow * window)12262 meta_window_is_always_on_all_workspaces (MetaWindow *window)
12263 {
12264   return window->always_sticky;
12265 }
12266 
12267 gboolean
meta_window_is_always_on_top(MetaWindow * window)12268 meta_window_is_always_on_top (MetaWindow *window)
12269 {
12270   return window->wm_state_above;
12271 }
12272 
12273 gboolean
meta_window_can_move(MetaWindow * window)12274 meta_window_can_move (MetaWindow *window)
12275 {
12276   return META_WINDOW_ALLOWS_MOVE (window);
12277 }
12278 
12279 gboolean
meta_window_can_resize(MetaWindow * window)12280 meta_window_can_resize (MetaWindow *window)
12281 {
12282   return META_WINDOW_ALLOWS_RESIZE (window);
12283 }
12284 
12285 /**
12286  * meta_window_can_tile:
12287  * @window: a #MetaWindow
12288  * @mode: the #MetaTileMode to check for
12289  *
12290  * Tests if @window can be tiled or snapped in the supplied
12291  * tiling zone
12292  *
12293  * Return value: whether @window can be tiled
12294  */
12295 
12296 gboolean
meta_window_can_tile(MetaWindow * window,MetaTileMode mode)12297 meta_window_can_tile (MetaWindow *window, MetaTileMode mode)
12298 {
12299   g_return_val_if_fail (META_IS_WINDOW (window), FALSE);
12300 
12301   switch (mode) {
12302     case META_TILE_LEFT:
12303     case META_TILE_RIGHT:
12304         return meta_window_can_tile_side_by_side (window);
12305     case META_TILE_TOP:
12306     case META_TILE_BOTTOM:
12307         return meta_window_can_tile_top_bottom (window);
12308     case META_TILE_ULC:
12309     case META_TILE_LLC:
12310     case META_TILE_URC:
12311     case META_TILE_LRC:
12312         return meta_window_can_tile_corner (window);
12313     case META_TILE_MAXIMIZE:
12314     case META_TILE_NONE:
12315         return TRUE;
12316     default:
12317         return FALSE;
12318   }
12319 }
12320 
12321 static void
update_edge_constraints(MetaWindow * window)12322 update_edge_constraints (MetaWindow *window)
12323 {
12324   switch (window->tile_mode)
12325     {
12326     case META_TILE_NONE:
12327       window->edge_constraints[0] = META_EDGE_CONSTRAINT_NONE;
12328       window->edge_constraints[1] = META_EDGE_CONSTRAINT_NONE;
12329       window->edge_constraints[2] = META_EDGE_CONSTRAINT_NONE;
12330       window->edge_constraints[3] = META_EDGE_CONSTRAINT_NONE;
12331       break;
12332 
12333     case META_TILE_MAXIMIZE:
12334       window->edge_constraints[0] = META_EDGE_CONSTRAINT_MONITOR;
12335       window->edge_constraints[1] = META_EDGE_CONSTRAINT_MONITOR;
12336       window->edge_constraints[2] = META_EDGE_CONSTRAINT_MONITOR;
12337       window->edge_constraints[3] = META_EDGE_CONSTRAINT_MONITOR;
12338       break;
12339 
12340     case META_TILE_LEFT:
12341       window->edge_constraints[0] = META_EDGE_CONSTRAINT_MONITOR;
12342       window->edge_constraints[1] = META_EDGE_CONSTRAINT_NONE;
12343       window->edge_constraints[2] = META_EDGE_CONSTRAINT_MONITOR;
12344       window->edge_constraints[3] = META_EDGE_CONSTRAINT_MONITOR;
12345       break;
12346 
12347     case META_TILE_RIGHT:
12348       window->edge_constraints[0] = META_EDGE_CONSTRAINT_MONITOR;
12349       window->edge_constraints[1] = META_EDGE_CONSTRAINT_MONITOR;
12350       window->edge_constraints[2] = META_EDGE_CONSTRAINT_MONITOR;
12351       window->edge_constraints[3] = META_EDGE_CONSTRAINT_NONE;
12352       break;
12353 
12354     case META_TILE_TOP:
12355       window->edge_constraints[0] = META_EDGE_CONSTRAINT_MONITOR;
12356       window->edge_constraints[1] = META_EDGE_CONSTRAINT_MONITOR;
12357       window->edge_constraints[2] = META_EDGE_CONSTRAINT_MONITOR;
12358       window->edge_constraints[3] = META_EDGE_CONSTRAINT_NONE;
12359       break;
12360 
12361     case META_TILE_BOTTOM:
12362       window->edge_constraints[0] = META_EDGE_CONSTRAINT_NONE;
12363       window->edge_constraints[1] = META_EDGE_CONSTRAINT_MONITOR;
12364       window->edge_constraints[2] = META_EDGE_CONSTRAINT_MONITOR;
12365       window->edge_constraints[3] = META_EDGE_CONSTRAINT_MONITOR;
12366       break;
12367     case META_TILE_ULC:
12368       window->edge_constraints[0] = META_EDGE_CONSTRAINT_MONITOR;
12369       window->edge_constraints[1] = META_EDGE_CONSTRAINT_NONE;
12370       window->edge_constraints[2] = META_EDGE_CONSTRAINT_NONE;
12371       window->edge_constraints[3] = META_EDGE_CONSTRAINT_MONITOR;
12372       break;
12373     case META_TILE_URC:
12374       window->edge_constraints[0] = META_EDGE_CONSTRAINT_MONITOR;
12375       window->edge_constraints[1] = META_EDGE_CONSTRAINT_MONITOR;
12376       window->edge_constraints[2] = META_EDGE_CONSTRAINT_NONE;
12377       window->edge_constraints[3] = META_EDGE_CONSTRAINT_NONE;
12378       break;
12379     case META_TILE_LLC:
12380       window->edge_constraints[0] = META_EDGE_CONSTRAINT_NONE;
12381       window->edge_constraints[1] = META_EDGE_CONSTRAINT_NONE;
12382       window->edge_constraints[2] = META_EDGE_CONSTRAINT_MONITOR;
12383       window->edge_constraints[3] = META_EDGE_CONSTRAINT_MONITOR;
12384       break;
12385     case META_TILE_LRC:
12386       window->edge_constraints[0] = META_EDGE_CONSTRAINT_NONE;
12387       window->edge_constraints[1] = META_EDGE_CONSTRAINT_MONITOR;
12388       window->edge_constraints[2] = META_EDGE_CONSTRAINT_MONITOR;
12389       window->edge_constraints[3] = META_EDGE_CONSTRAINT_NONE;
12390       break;
12391     }
12392 
12393   /* h/vmaximize also modify the edge constraints */
12394   if (window->maximized_vertically)
12395     {
12396       window->edge_constraints[0] = META_EDGE_CONSTRAINT_MONITOR;
12397       window->edge_constraints[2] = META_EDGE_CONSTRAINT_MONITOR;
12398     }
12399 
12400   if (window->maximized_horizontally)
12401     {
12402       window->edge_constraints[1] = META_EDGE_CONSTRAINT_MONITOR;
12403       window->edge_constraints[3] = META_EDGE_CONSTRAINT_MONITOR;
12404     }
12405 }
12406 
12407 /**
12408  * meta_window_tile:
12409  * @window: a #MetaWindow
12410  * @mode: the #MetaTileMode to use
12411  * @snap: whether to snap the window (as opposed to simple tile)
12412  *
12413  * Tiles or snaps the window in the requested configuration
12414  *
12415  * Return value: whether or not @window was successfully tiled
12416  */
12417 
12418 gboolean
meta_window_tile(MetaWindow * window,MetaTileMode mode,gboolean snap)12419 meta_window_tile (MetaWindow *window,
12420                   MetaTileMode mode,
12421                   gboolean snap)
12422 {
12423     g_return_val_if_fail (META_IS_WINDOW (window), FALSE);
12424 
12425     if (!meta_window_can_tile (window, mode))
12426         return FALSE;
12427 
12428   if (mode != META_TILE_NONE) {
12429       window->last_tile_mode = window->tile_mode;
12430       window->snap_queued = snap;
12431       window->tile_monitor_number = window->monitor->number;
12432       window->tile_mode = mode;
12433       window->custom_snap_size = FALSE;
12434       window->saved_maximize = FALSE;
12435       /* Maximization constraints beat tiling constraints, so if the window
12436        * is maximized, tiling won't have any effect unless we unmaximize it
12437        * horizontally first; rather than calling meta_window_unmaximize(),
12438        * we just set the flag and rely on meta_window_real_tile() syncing it to
12439        * save an additional roundtrip.
12440        */
12441       meta_window_real_tile (window, TRUE);
12442   } else {
12443       window->last_tile_mode = window->tile_mode;
12444       window->tile_mode = mode;
12445       window->custom_snap_size = FALSE;
12446       meta_window_set_tile_type (window, META_WINDOW_TILE_TYPE_NONE);
12447       window->tile_monitor_number = window->saved_maximize ? window->monitor->number
12448                                                            : -1;
12449       if (window->saved_maximize)
12450         meta_window_maximize (window, META_MAXIMIZE_VERTICAL |
12451                                       META_MAXIMIZE_HORIZONTAL);
12452       else
12453         meta_window_unmaximize (window, META_MAXIMIZE_VERTICAL |
12454                                         META_MAXIMIZE_HORIZONTAL);
12455   }
12456 
12457   return TRUE;
12458 }
12459 
12460 /**
12461  * meta_window_get_icon_name:
12462  * @window: a #MetaWindow
12463  *
12464  * Returns the currently set icon name or icon path for the window.
12465  *
12466  * Note:
12467  *
12468  * This will currently only be non-NULL for programs that use XAppGtkWindow
12469  * in place of GtkWindow and use xapp_gtk_window_set_icon_name() or
12470  * set_icon_from_file().  These methods will need to be used explicitly in
12471  * C programs, but for introspection use you should not need to treat it any
12472  * differently (except for using the correct window class.)
12473  */
12474 const char *
meta_window_get_icon_name(MetaWindow * window)12475 meta_window_get_icon_name (MetaWindow *window)
12476 {
12477     g_return_val_if_fail (META_IS_WINDOW (window), NULL);
12478 
12479     return window->theme_icon_name;
12480 }
12481