window.c 333 KB
Newer Older
1 2
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */

3
/* Mutter X managed windows */
rhp's avatar
...  
rhp committed
4

5
/*
6
 * Copyright (C) 2001 Havoc Pennington, Anders Carlsson
7 8
 * Copyright (C) 2002, 2003 Red Hat, Inc.
 * Copyright (C) 2003 Rob Adams
9
 * Copyright (C) 2004-2006 Elijah Newren
10
 *
rhp's avatar
...  
rhp committed
11 12 13 14 15 16 17 18 19
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
20
 *
rhp's avatar
...  
rhp committed
21 22 23 24 25 26
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA.
 */

27
#include <config.h>
28
#include "window-private.h"
29
#include "boxes-private.h"
30
#include "edge-resistance.h"
31 32 33
#include <meta/util.h>
#include "frame.h"
#include <meta/errors.h>
34
#include "workspace-private.h"
rhp's avatar
...  
rhp committed
35
#include "stack.h"
36
#include "keybindings-private.h"
rhp's avatar
...  
rhp committed
37
#include "ui.h"
rhp's avatar
...  
rhp committed
38
#include "place.h"
rhp's avatar
...  
rhp committed
39
#include "session.h"
40
#include <meta/prefs.h>
41
#include "resizepopup.h"
42
#include "xprops.h"
43
#include <meta/group.h>
44
#include "window-props.h"
45
#include "constraints.h"
46
#include "mutter-enum-types.h"
rhp's avatar
...  
rhp committed
47

rhp's avatar
...  
rhp committed
48
#include <X11/Xatom.h>
49
#include <X11/Xlibint.h> /* For display->resource_mask */
50
#include <string.h>
51
#include <math.h>
rhp's avatar
...  
rhp committed
52

53 54 55 56
#ifdef HAVE_SHAPE
#include <X11/extensions/shape.h>
#endif

57 58
#include <X11/extensions/Xcomposite.h>

59
/* Windows that unmaximize to a size bigger than that fraction of the workarea
60 61 62
 * will be scaled down to that size (while maintaining aspect ratio).
 * Windows that cover an area greater then this size are automaximized on map.
 */
63 64
#define MAX_UNMAXIMIZED_WINDOW_AREA .8

65 66
static int destroying_windows_disallowed = 0;

rhp's avatar
...  
rhp committed
67 68

static void     update_sm_hints           (MetaWindow     *window);
69
static void     update_net_frame_extents  (MetaWindow     *window);
rhp's avatar
...  
rhp committed
70
static void     recalc_window_type        (MetaWindow     *window);
rhp's avatar
...  
rhp committed
71
static void     recalc_window_features    (MetaWindow     *window);
72
static void     invalidate_work_areas     (MetaWindow     *window);
73
static void     recalc_window_type        (MetaWindow     *window);
74 75 76
static void     set_wm_state_on_xwindow   (MetaDisplay    *display,
                                           Window          xwindow,
                                           int             state);
77
static void     set_wm_state              (MetaWindow     *window,
rhp's avatar
...  
rhp committed
78
                                           int             state);
79
static void     set_net_wm_state          (MetaWindow     *window);
80 81
static void     meta_window_set_above     (MetaWindow     *window,
                                           gboolean        new_value);
82

rhp's avatar
...  
rhp committed
83 84 85
static void     send_configure_notify     (MetaWindow     *window);
static gboolean process_property_notify   (MetaWindow     *window,
                                           XPropertyEvent *event);
86 87 88

static void     meta_window_force_placement (MetaWindow     *window);

rhp's avatar
...  
rhp committed
89 90 91
static void     meta_window_show          (MetaWindow     *window);
static void     meta_window_hide          (MetaWindow     *window);

92 93 94
static gboolean meta_window_same_client (MetaWindow *window,
                                         MetaWindow *other_window);

95
static void     meta_window_save_rect         (MetaWindow    *window);
96 97
static void     save_user_window_placement    (MetaWindow    *window);
static void     force_save_user_window_placement (MetaWindow    *window);
98

Havoc Pennington's avatar
Havoc Pennington committed
99 100 101 102 103 104 105
static void meta_window_move_resize_internal (MetaWindow         *window,
                                              MetaMoveResizeFlags flags,
                                              int                 resize_gravity,
                                              int                 root_x_nw,
                                              int                 root_y_nw,
                                              int                 w,
                                              int                 h);
rhp's avatar
...  
rhp committed
106

107 108 109
static void     ensure_mru_position_after (MetaWindow *window,
                                           MetaWindow *after_this_one);

rhp's avatar
...  
rhp committed
110

111
static void meta_window_move_resize_now (MetaWindow  *window);
rhp's avatar
...  
rhp committed
112

113
static void meta_window_unqueue (MetaWindow *window, guint queuebits);
114

115 116 117 118 119 120 121 122 123 124 125
static void     update_move           (MetaWindow   *window,
                                       gboolean      snap,
                                       int           x,
                                       int           y);
static gboolean update_move_timeout   (gpointer data);
static void     update_resize         (MetaWindow   *window,
                                       gboolean      snap,
                                       int           x,
                                       int           y,
                                       gboolean      force);
static gboolean update_resize_timeout (gpointer data);
126
static gboolean should_be_on_all_workspaces (MetaWindow *window);
127

128
static void meta_window_flush_calc_showing   (MetaWindow *window);
rhp's avatar
...  
rhp committed
129

130 131 132
static gboolean queue_calc_showing_func (MetaWindow *window,
                                         void       *data);

rhp's avatar
...  
rhp committed
133 134
static void meta_window_apply_session_info (MetaWindow                  *window,
                                            const MetaWindowSessionInfo *info);
135 136 137
static void meta_window_move_between_rects (MetaWindow          *window,
                                            const MetaRectangle *old_area,
                                            const MetaRectangle *new_area);
rhp's avatar
...  
rhp committed
138

139 140 141
static void unmaximize_window_before_freeing (MetaWindow        *window);
static void unminimize_window_and_all_transient_parents (MetaWindow *window);

142 143 144
/* Idle handlers for the three queues (run with meta_later_add()). The
 * "data" parameter in each case will be a GINT_TO_POINTER of the
 * index into the queue arrays to use.
145 146 147 148 149 150 151
 *
 * TODO: Possibly there is still some code duplication among these, which we
 * need to sort out at some point.
 */
static gboolean idle_calc_showing (gpointer data);
static gboolean idle_move_resize (gpointer data);
static gboolean idle_update_icon (gpointer data);
152

Owen Taylor's avatar
Owen Taylor committed
153 154
G_DEFINE_TYPE (MetaWindow, meta_window, G_TYPE_OBJECT);

155 156 157 158 159
enum {
  PROP_0,

  PROP_TITLE,
  PROP_ICON,
160
  PROP_MINI_ICON,
161
  PROP_DECORATED,
162
  PROP_FULLSCREEN,
163 164
  PROP_MAXIMIZED_HORIZONTALLY,
  PROP_MAXIMIZED_VERTICALLY,
165
  PROP_MINIMIZED,
166
  PROP_WINDOW_TYPE,
167
  PROP_USER_TIME,
168
  PROP_DEMANDS_ATTENTION,
Tomas Frydrych's avatar
Tomas Frydrych committed
169
  PROP_URGENT,
170
  PROP_MUTTER_HINTS,
171
  PROP_APPEARS_FOCUSED,
172 173
  PROP_RESIZEABLE,
  PROP_ABOVE,
174
  PROP_WM_CLASS,
175 176 177 178 179 180
  PROP_GTK_APPLICATION_ID,
  PROP_GTK_UNIQUE_BUS_NAME,
  PROP_GTK_APPLICATION_OBJECT_PATH,
  PROP_GTK_WINDOW_OBJECT_PATH,
  PROP_GTK_APP_MENU_OBJECT_PATH,
  PROP_GTK_MENUBAR_OBJECT_PATH
181 182
};

183 184 185
enum
{
  WORKSPACE_CHANGED,
Tomas Frydrych's avatar
Tomas Frydrych committed
186
  FOCUS,
Tomas Frydrych's avatar
Tomas Frydrych committed
187
  RAISED,
188
  UNMANAGED,
189 190 191 192 193 194

  LAST_SIGNAL
};

static guint window_signals[LAST_SIGNAL] = { 0 };

195 196 197 198 199 200 201 202 203 204 205 206 207 208
static void
prefs_changed_callback (MetaPreference pref,
                        gpointer       data)
{
  MetaWindow *window = data;

  if (pref != META_PREF_WORKSPACES_ONLY_ON_PRIMARY)
    return;

  meta_window_update_on_all_workspaces (window);

  meta_window_queue (window, META_QUEUE_CALC_SHOWING);
}

Owen Taylor's avatar
Owen Taylor committed
209 210 211 212
static void
meta_window_finalize (GObject *object)
{
  MetaWindow *window = META_WINDOW (object);
213

Owen Taylor's avatar
Owen Taylor committed
214 215 216 217 218 219
  if (window->icon)
    g_object_unref (G_OBJECT (window->icon));

  if (window->mini_icon)
    g_object_unref (G_OBJECT (window->mini_icon));

220 221 222
  if (window->frame_bounds)
    cairo_region_destroy (window->frame_bounds);

Owen Taylor's avatar
Owen Taylor committed
223
  meta_icon_cache_free (&window->icon_cache);
224

Owen Taylor's avatar
Owen Taylor committed
225 226 227 228 229 230 231 232 233
  g_free (window->sm_client_id);
  g_free (window->wm_client_machine);
  g_free (window->startup_id);
  g_free (window->role);
  g_free (window->res_class);
  g_free (window->res_name);
  g_free (window->title);
  g_free (window->icon_name);
  g_free (window->desc);
234
  g_free (window->gtk_theme_variant);
235 236 237 238 239 240
  g_free (window->gtk_application_id);
  g_free (window->gtk_unique_bus_name);
  g_free (window->gtk_application_object_path);
  g_free (window->gtk_window_object_path);
  g_free (window->gtk_app_menu_object_path);
  g_free (window->gtk_menubar_object_path);
Owen Taylor's avatar
Owen Taylor committed
241 242
}

243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
static void
meta_window_get_property(GObject         *object,
                         guint            prop_id,
                         GValue          *value,
                         GParamSpec      *pspec)
{
  MetaWindow *win = META_WINDOW (object);

  switch (prop_id)
    {
    case PROP_TITLE:
      g_value_set_string (value, win->title);
      break;
    case PROP_ICON:
      g_value_set_object (value, win->icon);
      break;
    case PROP_MINI_ICON:
      g_value_set_object (value, win->mini_icon);
261
      break;
262 263 264
    case PROP_DECORATED:
      g_value_set_boolean (value, win->decorated);
      break;
265 266 267
    case PROP_FULLSCREEN:
      g_value_set_boolean (value, win->fullscreen);
      break;
268 269 270 271 272 273
    case PROP_MAXIMIZED_HORIZONTALLY:
      g_value_set_boolean (value, win->maximized_horizontally);
      break;
    case PROP_MAXIMIZED_VERTICALLY:
      g_value_set_boolean (value, win->maximized_vertically);
      break;
274 275 276
    case PROP_MINIMIZED:
      g_value_set_boolean (value, win->minimized);
      break;
277 278 279
    case PROP_WINDOW_TYPE:
      g_value_set_enum (value, win->type);
      break;
280 281 282
    case PROP_USER_TIME:
      g_value_set_uint (value, win->net_wm_user_time);
      break;
283 284 285
    case PROP_DEMANDS_ATTENTION:
      g_value_set_boolean (value, win->wm_state_demands_attention);
      break;
286 287 288
    case PROP_URGENT:
      g_value_set_boolean (value, win->wm_hints_urgent);
      break;
Tomas Frydrych's avatar
Tomas Frydrych committed
289 290 291
    case PROP_MUTTER_HINTS:
      g_value_set_string (value, win->mutter_hints);
      break;
292 293 294
    case PROP_APPEARS_FOCUSED:
      g_value_set_boolean (value, meta_window_appears_focused (win));
      break;
295 296 297
    case PROP_WM_CLASS:
      g_value_set_string (value, win->res_class);
      break;
298 299 300 301 302 303
    case PROP_RESIZEABLE:
      g_value_set_boolean (value, win->has_resize_func);
      break;
    case PROP_ABOVE:
      g_value_set_boolean (value, win->wm_state_above);
      break;
304 305
    case PROP_GTK_APPLICATION_ID:
      g_value_set_string (value, win->gtk_application_id);
306
      break;
307 308
    case PROP_GTK_UNIQUE_BUS_NAME:
      g_value_set_string (value, win->gtk_unique_bus_name);
309
      break;
310 311 312 313 314 315 316 317 318 319 320
    case PROP_GTK_APPLICATION_OBJECT_PATH:
      g_value_set_string (value, win->gtk_application_object_path);
      break;
    case PROP_GTK_WINDOW_OBJECT_PATH:
      g_value_set_string (value, win->gtk_window_object_path);
      break;
    case PROP_GTK_APP_MENU_OBJECT_PATH:
      g_value_set_string (value, win->gtk_app_menu_object_path);
      break;
    case PROP_GTK_MENUBAR_OBJECT_PATH:
      g_value_set_string (value, win->gtk_menubar_object_path);
321
      break;
322 323
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
324
      break;
325 326 327 328 329 330 331 332 333 334 335 336 337
    }
}

static void
meta_window_set_property(GObject         *object,
                         guint            prop_id,
                         const GValue    *value,
                         GParamSpec      *pspec)
{
  switch (prop_id)
    {
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
338
      break;
339 340 341
    }
}

Owen Taylor's avatar
Owen Taylor committed
342 343 344 345 346 347
static void
meta_window_class_init (MetaWindowClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  object_class->finalize = meta_window_finalize;
348

349 350
  object_class->get_property = meta_window_get_property;
  object_class->set_property = meta_window_set_property;
351

352 353 354 355 356 357
  g_object_class_install_property (object_class,
                                   PROP_TITLE,
                                   g_param_spec_string ("title",
                                                        "Title",
                                                        "The title of the window",
                                                        NULL,
358
                                                        G_PARAM_READABLE));
359 360 361 362 363 364 365 366 367 368 369 370 371 372
  g_object_class_install_property (object_class,
                                   PROP_ICON,
                                   g_param_spec_object ("icon",
                                                        "Icon",
                                                        "32 pixel sized icon",
                                                        GDK_TYPE_PIXBUF,
                                                        G_PARAM_READABLE));

  g_object_class_install_property (object_class,
                                   PROP_MINI_ICON,
                                   g_param_spec_object ("mini-icon",
                                                        "Mini Icon",
                                                        "16 pixel sized icon",
                                                        GDK_TYPE_PIXBUF,
373
                                                        G_PARAM_READABLE));
374

375 376 377 378
  g_object_class_install_property (object_class,
                                   PROP_DECORATED,
                                   g_param_spec_boolean ("decorated",
                                                         "Decorated",
379
                                                         "Whether window is decorated",
380 381 382
                                                         TRUE,
                                                         G_PARAM_READABLE));

383 384 385 386
  g_object_class_install_property (object_class,
                                   PROP_FULLSCREEN,
                                   g_param_spec_boolean ("fullscreen",
                                                         "Fullscreen",
387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403
                                                         "Whether window is fullscreened",
                                                         FALSE,
                                                         G_PARAM_READABLE));

  g_object_class_install_property (object_class,
                                   PROP_MAXIMIZED_HORIZONTALLY,
                                   g_param_spec_boolean ("maximized-horizontally",
                                                         "Maximized horizontally",
                                                         "Whether window is maximized horizontally",
                                                         FALSE,
                                                         G_PARAM_READABLE));

  g_object_class_install_property (object_class,
                                   PROP_MAXIMIZED_VERTICALLY,
                                   g_param_spec_boolean ("maximized-vertically",
                                                         "Maximizing vertically",
                                                         "Whether window is maximized vertically",
404 405
                                                         FALSE,
                                                         G_PARAM_READABLE));
406 407 408 409 410 411 412
  g_object_class_install_property (object_class,
                                   PROP_MINIMIZED,
                                   g_param_spec_boolean ("minimized",
                                                         "Minimizing",
                                                         "Whether window is minimized",
                                                         FALSE,
                                                         G_PARAM_READABLE));
413

414 415 416 417 418
  g_object_class_install_property (object_class,
                                   PROP_WINDOW_TYPE,
                                   g_param_spec_enum ("window-type",
                                                      "Window Type",
                                                      "The type of the window",
419
                                                      META_TYPE_WINDOW_TYPE,
420 421 422
                                                      META_WINDOW_NORMAL,
                                                      G_PARAM_READABLE));

423 424 425 426 427 428 429 430 431 432
  g_object_class_install_property (object_class,
                                   PROP_USER_TIME,
                                   g_param_spec_uint ("user-time",
                                                      "User time",
                                                      "Timestamp of last user interaction",
                                                      0,
                                                      G_MAXUINT,
                                                      0,
                                                      G_PARAM_READABLE));

433 434 435 436 437 438 439 440
  g_object_class_install_property (object_class,
                                   PROP_DEMANDS_ATTENTION,
                                   g_param_spec_boolean ("demands-attention",
                                                         "Demands Attention",
                                                         "Whether the window has _NET_WM_STATE_DEMANDS_ATTENTION set",
                                                         FALSE,
                                                         G_PARAM_READABLE));

441 442 443 444 445 446 447 448
  g_object_class_install_property (object_class,
                                   PROP_URGENT,
                                   g_param_spec_boolean ("urgent",
                                                         "Urgent",
                                                         "Whether the urgent flag of WM_HINTS is set",
                                                         FALSE,
                                                         G_PARAM_READABLE));

Tomas Frydrych's avatar
Tomas Frydrych committed
449 450 451 452 453 454 455
  g_object_class_install_property (object_class,
                                   PROP_MUTTER_HINTS,
                                   g_param_spec_string ("mutter-hints",
                                                        "_MUTTER_HINTS",
                                                        "Contents of the _MUTTER_HINTS property of this window",
                                                        NULL,
                                                        G_PARAM_READABLE));
456 457 458 459 460 461 462 463
  g_object_class_install_property (object_class,
                                   PROP_APPEARS_FOCUSED,
                                   g_param_spec_boolean ("appears-focused",
                                                         "Appears focused",
                                                         "Whether the window is drawn as being focused",
                                                         FALSE,
                                                         G_PARAM_READABLE));

464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479
  g_object_class_install_property (object_class,
                                   PROP_RESIZEABLE,
                                   g_param_spec_boolean ("resizeable",
                                                         "Resizeable",
                                                         "Whether the window can be resized",
                                                         FALSE,
                                                         G_PARAM_READABLE));

  g_object_class_install_property (object_class,
                                   PROP_ABOVE,
                                   g_param_spec_boolean ("above",
                                                         "Above",
                                                         "Whether the window is shown as always-on-top",
                                                         FALSE,
                                                         G_PARAM_READABLE));

480 481 482 483 484 485 486 487
  g_object_class_install_property (object_class,
                                   PROP_WM_CLASS,
                                   g_param_spec_string ("wm-class",
                                                        "WM_CLASS",
                                                        "Contents of the WM_CLASS property of this window",
                                                        NULL,
                                                        G_PARAM_READABLE));

488
  g_object_class_install_property (object_class,
489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508
                                   PROP_GTK_APPLICATION_ID,
                                   g_param_spec_string ("gtk-application-id",
                                                        "_GTK_APPLICATION_ID",
                                                        "Contents of the _GTK_APPLICATION_ID property of this window",
                                                        NULL,
                                                        G_PARAM_READABLE));

  g_object_class_install_property (object_class,
                                   PROP_GTK_UNIQUE_BUS_NAME,
                                   g_param_spec_string ("gtk-unique-bus-name",
                                                        "_GTK_UNIQUE_BUS_NAME",
                                                        "Contents of the _GTK_UNIQUE_BUS_NAME property of this window",
                                                        NULL,
                                                        G_PARAM_READABLE));

  g_object_class_install_property (object_class,
                                   PROP_GTK_APPLICATION_OBJECT_PATH,
                                   g_param_spec_string ("gtk-application-object-path",
                                                        "_GTK_APPLICATION_OBJECT_PATH",
                                                        "Contents of the _GTK_APPLICATION_OBJECT_PATH property of this window",
509 510 511
                                                        NULL,
                                                        G_PARAM_READABLE));

512
  g_object_class_install_property (object_class,
513 514 515 516
                                   PROP_GTK_WINDOW_OBJECT_PATH,
                                   g_param_spec_string ("gtk-window-object-path",
                                                        "_GTK_WINDOW_OBJECT_PATH",
                                                        "Contents of the _GTK_WINDOW_OBJECT_PATH property of this window",
517 518 519 520
                                                        NULL,
                                                        G_PARAM_READABLE));

  g_object_class_install_property (object_class,
521 522 523 524 525 526 527 528 529 530 531 532
                                   PROP_GTK_APP_MENU_OBJECT_PATH,
                                   g_param_spec_string ("gtk-app-menu-object-path",
                                                        "_GTK_APP_MENU_OBJECT_PATH",
                                                        "Contents of the _GTK_APP_MENU_OBJECT_PATH property of this window",
                                                        NULL,
                                                        G_PARAM_READABLE));

  g_object_class_install_property (object_class,
                                   PROP_GTK_MENUBAR_OBJECT_PATH,
                                   g_param_spec_string ("gtk-menubar-object-path",
                                                        "_GTK_MENUBAR_OBJECT_PATH",
                                                        "Contents of the _GTK_MENUBAR_OBJECT_PATH property of this window",
533 534 535
                                                        NULL,
                                                        G_PARAM_READABLE));

536 537 538 539 540
  window_signals[WORKSPACE_CHANGED] =
    g_signal_new ("workspace-changed",
                  G_TYPE_FROM_CLASS (object_class),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (MetaWindowClass, workspace_changed),
Jasper St. Pierre's avatar
Jasper St. Pierre committed
541
                  NULL, NULL, NULL,
542 543
                  G_TYPE_NONE, 1,
                  G_TYPE_INT);
Tomas Frydrych's avatar
Tomas Frydrych committed
544 545 546 547 548 549

  window_signals[FOCUS] =
    g_signal_new ("focus",
                  G_TYPE_FROM_CLASS (object_class),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (MetaWindowClass, focus),
Jasper St. Pierre's avatar
Jasper St. Pierre committed
550
                  NULL, NULL, NULL,
Tomas Frydrych's avatar
Tomas Frydrych committed
551
                  G_TYPE_NONE, 0);
Tomas Frydrych's avatar
Tomas Frydrych committed
552 553 554 555 556 557

  window_signals[RAISED] =
    g_signal_new ("raised",
                  G_TYPE_FROM_CLASS (object_class),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (MetaWindowClass, raised),
Jasper St. Pierre's avatar
Jasper St. Pierre committed
558
                  NULL, NULL, NULL,
Tomas Frydrych's avatar
Tomas Frydrych committed
559
                  G_TYPE_NONE, 0);
560 561 562 563 564 565

  window_signals[UNMANAGED] =
    g_signal_new ("unmanaged",
                  G_TYPE_FROM_CLASS (object_class),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (MetaWindowClass, unmanaged),
Jasper St. Pierre's avatar
Jasper St. Pierre committed
566
                  NULL, NULL, NULL,
567
                  G_TYPE_NONE, 0);
Owen Taylor's avatar
Owen Taylor committed
568 569 570 571 572
}

static void
meta_window_init (MetaWindow *self)
{
573
  meta_prefs_add_listener (prefs_changed_callback, self);
Owen Taylor's avatar
Owen Taylor committed
574 575
}

576
#ifdef WITH_VERBOSE_MODE
577 578 579 580 581 582
static const char*
wm_state_to_string (int state)
{
  switch (state)
    {
    case NormalState:
583
      return "NormalState";
584 585 586 587 588 589 590 591
    case IconicState:
      return "IconicState";
    case WithdrawnState:
      return "WithdrawnState";
    }

  return "Unknown";
}
592
#endif
593

594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616
static gboolean
is_desktop_or_dock_foreach (MetaWindow *window,
                            void       *data)
{
  gboolean *result = data;

  *result =
    window->type == META_WINDOW_DESKTOP ||
    window->type == META_WINDOW_DOCK;
  if (*result)
    return FALSE; /* stop as soon as we find one */
  else
    return TRUE;
}

/* window is the window that's newly mapped provoking
 * the possible change
 */
static void
maybe_leave_show_desktop_mode (MetaWindow *window)
{
  gboolean is_desktop_or_dock;

617
  if (!window->screen->active_workspace->showing_desktop)
618 619 620 621 622 623 624 625
    return;

  /* If the window is a transient for the dock or desktop, don't
   * leave show desktop mode when the window opens. That's
   * so you can e.g. hide all windows, manipulate a file on
   * the desktop via a dialog, then unshow windows again.
   */
  is_desktop_or_dock = FALSE;
626 627
  is_desktop_or_dock_foreach (window,
                              &is_desktop_or_dock);
628 629 630 631 632 633

  meta_window_foreach_ancestor (window, is_desktop_or_dock_foreach,
                                &is_desktop_or_dock);

  if (!is_desktop_or_dock)
    {
634 635
      meta_screen_minimize_all_on_active_workspace_except (window->screen,
                                                           window);
636
      meta_screen_unshow_desktop (window->screen);
637 638 639
    }
}

rhp's avatar
...  
rhp committed
640
MetaWindow*
641 642 643
meta_window_new (MetaDisplay *display,
                 Window       xwindow,
                 gboolean     must_be_viewable)
rhp's avatar
...  
rhp committed
644 645
{
  XWindowAttributes attrs;
Havoc Pennington's avatar
Havoc Pennington committed
646
  MetaWindow *window;
647

Havoc Pennington's avatar
Havoc Pennington committed
648 649 650 651
  meta_display_grab (display);
  meta_error_trap_push (display); /* Push a trap over all of window
                                   * creation, to reduce XSync() calls
                                   */
652

Havoc Pennington's avatar
Havoc Pennington committed
653
  meta_error_trap_push_with_return (display);
654 655

  if (XGetWindowAttributes (display->xdisplay,xwindow, &attrs))
656
   {
657
      if(meta_error_trap_pop_with_return (display) != Success)
658 659 660
       {
          meta_verbose ("Failed to get attributes for window 0x%lx\n",
                        xwindow);
661
          meta_error_trap_pop (display);
662 663 664 665
          meta_display_ungrab (display);
          return NULL;
       }
      window = meta_window_new_with_attrs (display, xwindow,
666 667 668
                                           must_be_viewable,
                                           META_COMP_EFFECT_CREATE,
                                           &attrs);
669 670 671
   }
  else
   {
672
         meta_error_trap_pop_with_return (display);
673 674
         meta_verbose ("Failed to get attributes for window 0x%lx\n",
                        xwindow);
675
         meta_error_trap_pop (display);
676 677 678
         meta_display_ungrab (display);
         return NULL;
   }
679 680


681
  meta_error_trap_pop (display);
Havoc Pennington's avatar
Havoc Pennington committed
682 683 684 685 686
  meta_display_ungrab (display);

  return window;
}

687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770
/* The MUTTER_WM_CLASS_FILTER environment variable is designed for
 * performance and regression testing environments where we want to do
 * tests with only a limited set of windows and ignore all other windows
 *
 * When it is set to a comma separated list of WM_CLASS class names, all
 * windows not matching the list will be ignored.
 *
 * Returns TRUE if window has been filtered out and should be ignored.
 */
static gboolean
maybe_filter_window (MetaDisplay       *display,
                     Window             xwindow,
                     gboolean           must_be_viewable,
                     XWindowAttributes *attrs)
{
  static char **filter_wm_classes = NULL;
  static gboolean initialized = FALSE;
  XClassHint class_hint;
  gboolean filtered;
  Status success;
  int i;

  if (!initialized)
    {
      const char *filter_string = g_getenv ("MUTTER_WM_CLASS_FILTER");
      if (filter_string)
        filter_wm_classes = g_strsplit (filter_string, ",", -1);
      initialized = TRUE;
    }

  if (!filter_wm_classes || !filter_wm_classes[0])
    return FALSE;

  filtered = TRUE;

  meta_error_trap_push (display);
  success = XGetClassHint (display->xdisplay, xwindow, &class_hint);

  if (success)
    {
      for (i = 0; filter_wm_classes[i]; i++)
        {
          if (strcmp (class_hint.res_class, filter_wm_classes[i]) == 0)
            {
              filtered = FALSE;
              break;
            }
        }

      XFree (class_hint.res_name);
      XFree (class_hint.res_class);
    }

  if (filtered)
    {
      /* We want to try and get the window managed by the next WM that come along,
       * so we need to make sure that windows that are requested to be mapped while
       * Mutter is running (!must_be_viewable), or windows already viewable at startup
       * get a non-withdrawn WM_STATE property. Previously unmapped windows are left
       * with whatever WM_STATE property they had.
       */
      if (!must_be_viewable || attrs->map_state == IsViewable)
        {
          gulong old_state;

          if (!meta_prop_get_cardinal_with_atom_type (display, xwindow,
                                                      display->atom_WM_STATE,
                                                      display->atom_WM_STATE,
                                                      &old_state))
            old_state = WithdrawnState;

          if (old_state == WithdrawnState)
            set_wm_state_on_xwindow (display, xwindow, NormalState);
        }

      /* Make sure filtered windows are hidden from view */
      XUnmapWindow (display->xdisplay, xwindow);
    }

  meta_error_trap_pop (display);

  return filtered;
}

771 772 773 774 775 776 777 778 779 780 781 782 783
gboolean
meta_window_should_attach_to_parent (MetaWindow *window)
{
  MetaWindow *parent;

  if (!meta_prefs_get_attach_modal_dialogs () ||
      window->type != META_WINDOW_MODAL_DIALOG)
    return FALSE;

  parent = meta_window_get_transient_for (window);
  if (!parent)
    return FALSE;

784 785 786 787 788 789 790 791 792 793
  switch (parent->type)
    {
    case META_WINDOW_NORMAL:
    case META_WINDOW_DIALOG:
    case META_WINDOW_MODAL_DIALOG:
      return TRUE;

    default:
      return FALSE;
    }
794 795
}

Havoc Pennington's avatar
Havoc Pennington committed
796 797 798 799
MetaWindow*
meta_window_new_with_attrs (MetaDisplay       *display,
                            Window             xwindow,
                            gboolean           must_be_viewable,
800
                            MetaCompEffect     effect,
Havoc Pennington's avatar
Havoc Pennington committed
801 802 803
                            XWindowAttributes *attrs)
{
  MetaWindow *window;
rhp's avatar
...  
rhp committed
804
  GSList *tmp;
rhp's avatar
...  
rhp committed
805
  MetaWorkspace *space;
rhp's avatar
...  
rhp committed
806
  gulong existing_wm_state;
807
  gulong event_mask;
808
  MetaMoveResizeFlags flags;
809
  gboolean has_shape;
810
  MetaScreen *screen;
Havoc Pennington's avatar
Havoc Pennington committed
811 812

  g_assert (attrs != NULL);
813

rhp's avatar
...  
rhp committed
814
  meta_verbose ("Attempting to manage 0x%lx\n", xwindow);
rhp's avatar
...  
rhp committed
815

816
  if (meta_display_xwindow_is_a_no_focus_window (display, xwindow))
817 818 819 820 821
    {
      meta_verbose ("Not managing no_focus_window 0x%lx\n",
                    xwindow);
      return NULL;
    }
Havoc Pennington's avatar
Havoc Pennington committed
822

823 824
  screen = NULL;
  for (tmp = display->screens; tmp != NULL; tmp = tmp->next)
Havoc Pennington's avatar
Havoc Pennington committed
825
    {
826 827 828 829 830 831 832
      MetaScreen *scr = tmp->data;

      if (scr->xroot == attrs->root)
        {
          screen = tmp->data;
          break;
        }
Havoc Pennington's avatar
Havoc Pennington committed
833
    }
834

835
  g_assert (screen);
836

837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857
  /* A black list of override redirect windows that we don't need to manage: */
  if (attrs->override_redirect &&
      (xwindow == screen->no_focus_window ||
       xwindow == screen->flash_window ||
       xwindow == screen->wm_sn_selection_window ||
       attrs->class == InputOnly ||
       /* any windows created via meta_create_offscreen_window: */
       (attrs->x == -100 && attrs->y == -100
	&& attrs->width == 1 && attrs->height == 1) ||
       xwindow == screen->wm_cm_selection_window ||
       xwindow == screen->guard_window ||
       (display->compositor &&
        xwindow == XCompositeGetOverlayWindow (display->xdisplay,
					       screen->xroot)
       )
      )
     ) {
    meta_verbose ("Not managing our own windows\n");
    return NULL;
  }

858 859 860 861 862 863
  if (maybe_filter_window (display, xwindow, must_be_viewable, attrs))
    {
      meta_verbose ("Not managing filtered window\n");
      return NULL;
    }

rhp's avatar
...  
rhp committed
864 865
  /* Grab server */
  meta_display_grab (display);
866 867 868
  meta_error_trap_push (display); /* Push a trap over all of window
                                   * creation, to reduce XSync() calls
                                   */
rhp's avatar
...  
rhp committed
869

Havoc Pennington's avatar
Havoc Pennington committed
870
  meta_verbose ("must_be_viewable = %d attrs->map_state = %d (%s)\n",
871
                must_be_viewable,
Havoc Pennington's avatar
Havoc Pennington committed
872 873
                attrs->map_state,
                (attrs->map_state == IsUnmapped) ?
874
                "IsUnmapped" :
Havoc Pennington's avatar
Havoc Pennington committed
875
                (attrs->map_state == IsViewable) ?
876
                "IsViewable" :
Havoc Pennington's avatar
Havoc Pennington committed
877
                (attrs->map_state == IsUnviewable) ?
878 879
                "IsUnviewable" :
                "(unknown)");
880

rhp's avatar
...  
rhp committed
881
  existing_wm_state = WithdrawnState;
Havoc Pennington's avatar
Havoc Pennington committed
882
  if (must_be_viewable && attrs->map_state != IsViewable)
rhp's avatar
...  
rhp committed
883
    {
rhp's avatar
...  
rhp committed
884 885 886
      /* Only manage if WM_STATE is IconicState or NormalState */
      gulong state;

Havoc Pennington's avatar
Havoc Pennington committed
887 888
      /* WM_STATE isn't a cardinal, it's type WM_STATE, but is an int */
      if (!(meta_prop_get_cardinal_with_atom_type (display, xwindow,
889 890
                                                   display->atom_WM_STATE,
                                                   display->atom_WM_STATE,
Havoc Pennington's avatar
Havoc Pennington committed
891
                                                   &state) &&
rhp's avatar
...  
rhp committed
892 893 894
            (state == IconicState || state == NormalState)))
        {
          meta_verbose ("Deciding not to manage unmapped or unviewable window 0x%lx\n", xwindow);
895
          meta_error_trap_pop (display);
rhp's avatar
...  
rhp committed
896 897 898 899
          meta_display_ungrab (display);
          return NULL;
        }

rhp's avatar
...  
rhp committed
900
      existing_wm_state = state;
901 902
      meta_verbose ("WM_STATE of %lx = %s\n", xwindow,
                    wm_state_to_string (existing_wm_state));
rhp's avatar
...  
rhp committed
903
    }
904

905
  meta_error_trap_push_with_return (display);
906

907 908 909 910 911 912 913 914 915
  /*
   * XAddToSaveSet can only be called on windows created by a different client.
   * with Mutter we want to be able to create manageable windows from within
   * the process (such as a dummy desktop window), so we do not want this
   * call failing to prevent the window from being managed -- wrap it in its
   * own error trap (we use the _with_return() version here to ensure that
   * XSync() is done on the pop, otherwise the error will not get caught).
   */
  meta_error_trap_push_with_return (display);
rhp's avatar
...  
rhp committed
916
  XAddToSaveSet (display->xdisplay, xwindow);
917
  meta_error_trap_pop_with_return (display);
rhp's avatar
...  
rhp committed
918

919 920 921
  event_mask =
    PropertyChangeMask | EnterWindowMask | LeaveWindowMask |
    FocusChangeMask | ColormapChangeMask;
922 923
  if (attrs->override_redirect)
    event_mask |= StructureNotifyMask;
924

925 926 927 928 929
  /* If the window is from this client (a menu, say) we need to augment
   * the event mask, not replace it. For windows from other clients,
   * attrs->your_event_mask will be empty at this point.
   */
  XSelectInput (display->xdisplay, xwindow, attrs->your_event_mask | event_mask);
930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949

  has_shape = FALSE;
#ifdef HAVE_SHAPE
  if (META_DISPLAY_HAS_SHAPE (display))
    {
      int x_bounding, y_bounding, x_clip, y_clip;
      unsigned w_bounding, h_bounding, w_clip, h_clip;
      int bounding_shaped, clip_shaped;

      XShapeSelectInput (display->xdisplay, xwindow, ShapeNotifyMask);

      XShapeQueryExtents (display->xdisplay, xwindow,
                          &bounding_shaped, &x_bounding, &y_bounding,
                          &w_bounding, &h_bounding,
                          &clip_shaped, &x_clip, &y_clip,
                          &w_clip, &h_clip);

      has_shape = bounding_shaped != FALSE;

      meta_topic (META_DEBUG_SHAPES,
950
                  "Window has_shape = %d extents %d,%d %u x %u\n",
951 952 953 954
                  has_shape, x_bounding, y_bounding,
                  w_bounding, h_bounding);
    }
#endif
rhp's avatar
...  
rhp committed
955 956

  /* Get rid of any borders */
Havoc Pennington's avatar
Havoc Pennington committed
957
  if (attrs->border_width != 0)
rhp's avatar
...  
rhp committed
958
    XSetWindowBorderWidth (display->xdisplay, xwindow, 0);
rhp's avatar
...  
rhp committed
959 960

  /* Get rid of weird gravities */
Havoc Pennington's avatar
Havoc Pennington committed
961
  if (attrs->win_gravity != NorthWestGravity)
rhp's avatar
...  
rhp committed
962 963
    {
      XSetWindowAttributes set_attrs;
964

rhp's avatar
...  
rhp committed
965
      set_attrs.win_gravity = NorthWestGravity;
966

rhp's avatar
...  
rhp committed
967 968 969 970 971
      XChangeWindowAttributes (display->xdisplay,
                               xwindow,
                               CWWinGravity,
                               &set_attrs);
    }
972

973
  if (meta_error_trap_pop_with_return (display) != Success)
rhp's avatar
...  
rhp committed
974 975 976
    {
      meta_verbose ("Window 0x%lx disappeared just as we tried to manage it\n",
                    xwindow);
977
      meta_error_trap_pop (display);
rhp's avatar
...  
rhp committed
978
      meta_display_ungrab (display);
rhp's avatar
...  
rhp committed
979 980
      return NULL;
    }
rhp's avatar
...  
rhp committed
981

982

Owen Taylor's avatar
Owen Taylor committed
983
  window = g_object_new (META_TYPE_WINDOW, NULL);
rhp's avatar
...  
rhp committed
984

985
  window->constructing = TRUE;
986

987
  window->dialog_pid = -1;
988

rhp's avatar
...  
rhp committed
989
  window->xwindow = xwindow;
990

rhp's avatar
...  
rhp committed
991 992 993 994
  /* this is in window->screen->display, but that's too annoying to
   * type
   */
  window->display = display;
995
  window->workspace = NULL;
996 997

#ifdef HAVE_XSYNC
998 999 1000 1001
  window->sync_request_counter = None;
  window->sync_request_serial = 0;
  window->sync_request_time.tv_sec = 0;
  window->sync_request_time.tv_usec = 0;
1002
#endif
1003

1004
  window->screen = screen;
rhp's avatar
...  
rhp committed
1005

1006
  window->desc = g_strdup_printf ("0x%lx", window->xwindow);
1007

1008 1009
  window->override_redirect = attrs->override_redirect;

rhp's avatar
...  
rhp committed
1010 1011
  /* avoid tons of stack updates */
  meta_stack_freeze (window->screen->stack);
1012 1013

  window->has_shape = has_shape;
1014

Havoc Pennington's avatar
Havoc Pennington committed
1015 1016 1017 1018
  window->rect.x = attrs->x;
  window->rect.y = attrs->y;
  window->rect.width = attrs->width;
  window->rect.height = attrs->height;
rhp's avatar
...  
rhp committed
1019

rhp's avatar
...  
rhp committed
1020
  /* And border width, size_hints are the "request" */
Havoc Pennington's avatar
Havoc Pennington committed
1021 1022 1023 1024 1025
  window->border_width = attrs->border_width;
  window->size_hints.x = attrs->x;
  window->size_hints.y = attrs->y;
  window->size_hints.width = attrs->width;
  window->size_hints.height = attrs->height;
1026 1027
  /* initialize the remaining size_hints as if size_hints.flags were zero */
  meta_set_normal_hints (window, NULL);
rhp's avatar
...  
rhp committed
1028 1029 1030

  /* And this is our unmaximized size */
  window->saved_rect = window->rect;
Havoc Pennington's avatar
Havoc Pennington committed
1031
  window->user_rect = window->rect;
1032

Havoc Pennington's avatar
Havoc Pennington committed
1033 1034 1035
  window->depth = attrs->depth;
  window->xvisual = attrs->visual;
  window->colormap = attrs->colormap;
1036

rhp's avatar
...  
rhp committed
1037
  window->title = NULL;
rhp's avatar
...  
rhp committed
1038 1039
  window->icon_name = NULL;
  window->icon = NULL;
1040
  window->mini_icon = NULL;
Havoc Pennington's avatar
Havoc Pennington committed
1041 1042 1043
  meta_icon_cache_init (&window->icon_cache);
  window->wm_hints_pixmap = None;
  window->wm_hints_mask = None;
1044
  window->wm_hints_urgent = FALSE;
rhp's avatar
...  
rhp committed
1045 1046

  window->frame = NULL;
rhp's avatar
...  
rhp committed
1047
  window->has_focus = FALSE;
1048
  window->attached_focus_window = NULL;
rhp's avatar
...  
rhp committed
1049

1050 1051 1052 1053
  window->maximized_horizontally = FALSE;
  window->maximized_vertically = FALSE;
  window->maximize_horizontally_after_placement = FALSE;
  window->maximize_vertically_after_placement = FALSE;
1054
  window->minimize_after_placement = FALSE;
1055
  window->fullscreen = FALSE;
1056
  window->fullscreen_after_placement = FALSE;
1057
  window->fullscreen_monitors[0] = -1;
1058
  window->require_fully_onscreen = TRUE;
1059
  window->require_on_single_monitor = TRUE;
1060
  window->require_titlebar_visible = TRUE;
rhp's avatar
...  
rhp committed
1061
  window->on_all_workspaces = FALSE;
1062
  window->on_all_workspaces_requested = FALSE;
1063
  window->tile_mode = META_TILE_NONE;
1064
  window->tile_monitor_number = -1;
rhp's avatar
...  
rhp committed
1065
  window->shaded = FALSE;
rhp's avatar
...  
rhp committed
1066 1067
  window->initially_iconic = FALSE;
  window->minimized = FALSE;
1068
  window->tab_unminimized = FALSE;
rhp's avatar
...  
rhp committed
1069
  window->iconic = FALSE;
Havoc Pennington's avatar
Havoc Pennington committed
1070
  window->mapped = attrs->map_state != IsUnmapped;
1071 1072 1073
  window->hidden = FALSE;
  window->visible_to_compositor = FALSE;
  window->pending_compositor_effect = effect;
1074 1075
  /* if already mapped, no need to worry about focus-on-first-time-showing */
  window->showing_for_first_time = !window->mapped;
1076 1077 1078
  /* if already mapped we don't want to do the placement thing;
   * override-redirect windows are placed by the app */
  window->placed = ((window->mapped && !window->hidden) || window->override_redirect);
rhp's avatar
...  
rhp committed
1079
  if (window->placed)
1080 1081 1082
    meta_topic (META_DEBUG_PLACEMENT,
                "Not placing window 0x%lx since it's already mapped\n",
                xwindow);
1083
  window->force_save_user_rect = TRUE;
1084
  window->denied_focus_and_not_transient = FALSE;
rhp's avatar
...  
rhp committed
1085
  window->unmanaging = FALSE;
1086
  window->is_in_queues = 0;
rhp's avatar
...  
rhp committed
1087 1088
  window->keys_grabbed = FALSE;
  window->grab_on_frame = FALSE;
rhp's avatar
...  
rhp committed
1089
  window->all_keys_grabbed = FALSE;
rhp's avatar
...  
rhp committed
1090
  window->withdrawn = FALSE;
rhp's avatar
...  
rhp committed
1091
  window->initial_workspace_set = FALSE;
1092 1093
  window->initial_timestamp_set = FALSE;
  window->net_wm_user_time_set = FALSE;
1094
  window->user_time_window = None;
1095 1096 1097 1098
  window->take_focus = FALSE;
  window->delete_window = FALSE;
  window->net_wm_ping = FALSE;
  window->input = TRUE;
rhp's avatar
...  
rhp committed
1099
  window->calc_placement = FALSE;
1100
  window->shaken_loose = FALSE;
1101
  window->have_focus_click_grab = FALSE;
1102
  window->disable_sync = FALSE;