Bug Summary

File:core/window.c
Warning:line 1645, column 18
Access to field 'data' results in a dereference of a null pointer (loaded from variable 'copy')

Annotated Source Code

Press '?' to see keyboard shortcuts

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