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