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