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