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