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

3
/*
4
 * Copyright (C) 2001 Havoc Pennington, Anders Carlsson
5 6
 * Copyright (C) 2002, 2003 Red Hat, Inc.
 * Copyright (C) 2003 Rob Adams
7
 * Copyright (C) 2004-2006 Elijah Newren
8
 *
rhp's avatar
...  
rhp committed
9 10 11 12 13 14 15 16 17
 * 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.
18
 *
rhp's avatar
...  
rhp committed
19
 * You should have received a copy of the GNU General Public License
20
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
rhp's avatar
...  
rhp committed
21 22
 */

23
/**
Niels De Graef's avatar
Niels De Graef committed
24
 * SECTION:meta-window
25
 * @title: MetaWindow
Niels De Graef's avatar
Niels De Graef committed
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
 * @short_description: A display-agnostic abstraction for a window.
 *
 * #MetaWindow is the core abstraction in Mutter of a window. It has the
 * properties you'd expect, such as a title, an icon, whether it's fullscreen,
 * has decorations, etc.
 *
 * Since a lot of different kinds of windows exist, each window also a
 * #MetaWindowType which denotes which kind of window we're exactly dealing
 * with. For example, one expects slightly different behaviour from a dialog
 * than a "normal" window. The type of a window can be queried with
 * meta_window_get_type().
 *
 * Common API for windows include:
 * - Minimizing: meta_window_minimize() / meta_window_unminimize()
 * - Maximizing: meta_window_maximize() / meta_window_unmaximize()
 * - Fullscreen: meta_window_make_fullscreen() / meta_window_unmake_fullscreen()
 *               / meta_window_is_fullscreen()
 *
 * Each #MetaWindow is part of either one or all #MetaWorkspace<!-- -->s of the
 * desktop. You can activate a window on a certain workspace using
 * meta_window_activate_with_workspace(), and query on which workspace it is
 * located using meta_window_located_on_workspace(). The workspace it is part
 * of can be obtained using meta_window_get_workspace().
 *
 * Each display protocol should make a subclass to be compatible with that
 * protocols' specifics, for example #MetaWindowX11 and #MetaWindowWayland.
 * This is independent of the protocol that the client uses, which is modeled
 * using the #MetaWindowClientType enum.
 *
 * To integrate within the Clutter scene graph, which deals with the actual
 * rendering, each #MetaWindow will be part of a #MetaWindowActor.
57 58
 */

Jonas Ådahl's avatar
Jonas Ådahl committed
59
#include "config.h"
rhp's avatar
...  
rhp committed
60

Jonas Ådahl's avatar
Jonas Ådahl committed
61 62 63
#include "core/window-private.h"

#include <math.h>
64
#include <stdlib.h>
65
#include <string.h>
Jonas Ådahl's avatar
Jonas Ådahl committed
66
#include <X11/Xatom.h>
rhp's avatar
...  
rhp committed
67

Jonas Ådahl's avatar
Jonas Ådahl committed
68 69
#include "backends/meta-backend-private.h"
#include "backends/meta-logical-monitor.h"
70
#include "cogl/cogl.h"
Jonas Ådahl's avatar
Jonas Ådahl committed
71 72 73 74 75 76 77 78 79 80
#include "core/boxes-private.h"
#include "core/constraints.h"
#include "core/edge-resistance.h"
#include "core/frame.h"
#include "core/keybindings-private.h"
#include "core/meta-workspace-manager-private.h"
#include "core/place.h"
#include "core/stack.h"
#include "core/util-private.h"
#include "core/workspace-private.h"
81
#include "meta/compositor-mutter.h"
Jonas Ådahl's avatar
Jonas Ådahl committed
82 83 84 85 86 87
#include "meta/group.h"
#include "meta/meta-cursor-tracker.h"
#include "meta/meta-enum-types.h"
#include "meta/meta-x11-errors.h"
#include "meta/prefs.h"
#include "ui/ui.h"
88
#include "x11/meta-x11-display-private.h"
89
#include "x11/window-props.h"
Jonas Ådahl's avatar
Jonas Ådahl committed
90
#include "x11/window-x11.h"
91 92
#include "x11/xprops.h"

93
#ifdef HAVE_WAYLAND
Jonas Ådahl's avatar
Jonas Ådahl committed
94 95
#include "wayland/meta-wayland-private.h"
#include "wayland/meta-wayland-surface.h"
96
#include "wayland/meta-window-wayland.h"
97
#include "wayland/meta-window-xwayland.h"
98
#endif
99

100
/* Windows that unmaximize to a size bigger than that fraction of the workarea
101 102 103
 * will be scaled down to that size (while maintaining aspect ratio).
 * Windows that cover an area greater then this size are automaximized on map.
 */
104 105
#define MAX_UNMAXIMIZED_WINDOW_AREA .8

106 107
#define SNAP_SECURITY_LABEL_PREFIX "snap."

108 109
static int destroying_windows_disallowed = 0;

110 111 112 113
/* Each window has a "stamp" which is a non-recycled 64-bit ID. They
 * start after the end of the XID space so that, for stacking
 * we can keep a guint64 that represents one or the other
 */
114
static guint64 next_window_stamp = G_GUINT64_CONSTANT(0x100000000);
rhp's avatar
...  
rhp committed
115

116
static void     invalidate_work_areas     (MetaWindow     *window);
117
static void     set_wm_state              (MetaWindow     *window);
118
static void     set_net_wm_state          (MetaWindow     *window);
119 120
static void     meta_window_set_above     (MetaWindow     *window,
                                           gboolean        new_value);
121

rhp's avatar
...  
rhp committed
122 123 124
static void     meta_window_show          (MetaWindow     *window);
static void     meta_window_hide          (MetaWindow     *window);

125 126
static void     meta_window_save_rect         (MetaWindow    *window);

127 128 129
static void     ensure_mru_position_after (MetaWindow *window,
                                           MetaWindow *after_this_one);

130
static void meta_window_move_resize_now (MetaWindow  *window);
rhp's avatar
...  
rhp committed
131

132
static void meta_window_unqueue (MetaWindow *window, guint queuebits);
133

134 135 136 137
static void     update_move           (MetaWindow              *window,
                                       MetaEdgeResistanceFlags  flags,
                                       int                      x,
                                       int                      y);
138
static gboolean update_move_timeout   (gpointer data);
139 140 141 142 143
static void     update_resize         (MetaWindow              *window,
                                       MetaEdgeResistanceFlags  flags,
                                       int                      x,
                                       int                      y,
                                       gboolean                 force);
144
static gboolean update_resize_timeout (gpointer data);
145
static gboolean should_be_on_all_workspaces (MetaWindow *window);
146

147
static void meta_window_flush_calc_showing   (MetaWindow *window);
rhp's avatar
...  
rhp committed
148

149 150 151
static gboolean queue_calc_showing_func (MetaWindow *window,
                                         void       *data);

152
static void meta_window_move_between_rects (MetaWindow          *window,
153
                                            MetaMoveResizeFlags  move_resize_flags,
154 155
                                            const MetaRectangle *old_area,
                                            const MetaRectangle *new_area);
rhp's avatar
...  
rhp committed
156

157 158 159
static void unmaximize_window_before_freeing (MetaWindow        *window);
static void unminimize_window_and_all_transient_parents (MetaWindow *window);

160 161
static void meta_window_propagate_focus_appearance (MetaWindow *window,
                                                    gboolean    focused);
162 163
static void meta_window_update_icon_now (MetaWindow *window,
                                         gboolean    force);
164

165 166 167 168
static void set_workspace_state (MetaWindow    *window,
                                 gboolean       on_all_workspaces,
                                 MetaWorkspace *workspace);

169 170
static MetaWindow * meta_window_find_tile_match (MetaWindow   *window,
                                                 MetaTileMode  mode);
171
static void update_edge_constraints (MetaWindow *window);
172

173 174 175
/* 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.
176 177 178 179 180 181 182
 *
 * 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);
183

184
G_DEFINE_ABSTRACT_TYPE (MetaWindow, meta_window, G_TYPE_OBJECT);
Owen Taylor's avatar
Owen Taylor committed
185

186 187
enum
{
188 189 190 191
  PROP_0,

  PROP_TITLE,
  PROP_ICON,
192
  PROP_MINI_ICON,
193
  PROP_DECORATED,
194
  PROP_FULLSCREEN,
195 196
  PROP_MAXIMIZED_HORIZONTALLY,
  PROP_MAXIMIZED_VERTICALLY,
197
  PROP_MINIMIZED,
198
  PROP_WINDOW_TYPE,
199
  PROP_USER_TIME,
200
  PROP_DEMANDS_ATTENTION,
Tomas Frydrych's avatar
Tomas Frydrych committed
201
  PROP_URGENT,
202
  PROP_SKIP_TASKBAR,
203
  PROP_MUTTER_HINTS,
204
  PROP_APPEARS_FOCUSED,
205 206
  PROP_RESIZEABLE,
  PROP_ABOVE,
207
  PROP_WM_CLASS,
208 209 210 211 212
  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,
213
  PROP_GTK_MENUBAR_OBJECT_PATH,
214 215
  PROP_ON_ALL_WORKSPACES,

216
  PROP_LAST,
217 218
};

219
static GParamSpec *obj_props[PROP_LAST];
220

221 222 223
enum
{
  WORKSPACE_CHANGED,
Tomas Frydrych's avatar
Tomas Frydrych committed
224
  FOCUS,
Tomas Frydrych's avatar
Tomas Frydrych committed
225
  RAISED,
226
  UNMANAGING,
227
  UNMANAGED,
228 229
  SIZE_CHANGED,
  POSITION_CHANGED,
230
  SHOWN,
231 232 233 234 235 236

  LAST_SIGNAL
};

static guint window_signals[LAST_SIGNAL] = { 0 };

237 238 239 240 241 242
static void
prefs_changed_callback (MetaPreference pref,
                        gpointer       data)
{
  MetaWindow *window = data;

243 244
  if (pref == META_PREF_WORKSPACES_ONLY_ON_PRIMARY)
    {
245
      meta_window_on_all_workspaces_changed (window);
246 247 248 249 250
    }
  else if (pref == META_PREF_ATTACH_MODAL_DIALOGS &&
           window->type == META_WINDOW_MODAL_DIALOG)
    {
      window->attached = meta_window_should_attach_to_parent (window);
Jasper St. Pierre's avatar
Jasper St. Pierre committed
251
      meta_window_recalc_features (window);
252 253
      meta_window_queue (window, META_QUEUE_MOVE_RESIZE);
    }
254 255
}

256 257 258 259 260 261 262 263 264 265 266 267 268
static void
meta_window_real_grab_op_began (MetaWindow *window,
                                MetaGrabOp  op)
{
}

static void
meta_window_real_grab_op_ended (MetaWindow *window,
                                MetaGrabOp  op)
{
  window->shaken_loose = FALSE;
}

269 270 271 272 273
static void
meta_window_real_current_workspace_changed (MetaWindow *window)
{
}

274 275 276 277 278 279
static gboolean
meta_window_real_update_struts (MetaWindow *window)
{
  return FALSE;
}

280 281 282 283 284 285 286 287 288
static void
meta_window_real_get_default_skip_hints (MetaWindow *window,
                                         gboolean   *skip_taskbar_out,
                                         gboolean   *skip_pager_out)
{
  *skip_taskbar_out = FALSE;
  *skip_pager_out = FALSE;
}

289
static gboolean
290 291 292
meta_window_real_update_icon (MetaWindow       *window,
                              cairo_surface_t **icon,
                              cairo_surface_t **mini_icon)
293 294 295 296 297 298
{
  *icon = NULL;
  *mini_icon = NULL;
  return FALSE;
}

299
static pid_t
300 301 302 303 304
meta_window_real_get_client_pid (MetaWindow *window)
{
  return 0;
}

Owen Taylor's avatar
Owen Taylor committed
305 306 307 308
static void
meta_window_finalize (GObject *object)
{
  MetaWindow *window = META_WINDOW (object);
309

Owen Taylor's avatar
Owen Taylor committed
310
  if (window->icon)
311
    cairo_surface_destroy (window->icon);
Owen Taylor's avatar
Owen Taylor committed
312 313

  if (window->mini_icon)
314
    cairo_surface_destroy (window->mini_icon);
Owen Taylor's avatar
Owen Taylor committed
315

316 317 318
  if (window->frame_bounds)
    cairo_region_destroy (window->frame_bounds);

Pavel Vasin's avatar
Pavel Vasin committed
319 320 321
  if (window->shape_region)
    cairo_region_destroy (window->shape_region);

322 323 324
  if (window->opaque_region)
    cairo_region_destroy (window->opaque_region);

325 326 327
  if (window->input_region)
    cairo_region_destroy (window->input_region);

328 329 330
  if (window->transient_for)
    g_object_unref (window->transient_for);

Owen Taylor's avatar
Owen Taylor committed
331 332 333 334 335 336 337 338
  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->desc);
339
  g_free (window->sandboxed_app_id);
340
  g_free (window->gtk_theme_variant);
341 342 343 344 345 346
  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);
347
  g_free (window->placement.rule);
348 349

  G_OBJECT_CLASS (meta_window_parent_class)->finalize (object);
Owen Taylor's avatar
Owen Taylor committed
350 351
}

352 353 354 355 356 357 358 359 360 361 362 363 364 365
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:
366
      g_value_set_pointer (value, win->icon);
367 368
      break;
    case PROP_MINI_ICON:
369
      g_value_set_pointer (value, win->mini_icon);
370
      break;
371 372 373
    case PROP_DECORATED:
      g_value_set_boolean (value, win->decorated);
      break;
374 375 376
    case PROP_FULLSCREEN:
      g_value_set_boolean (value, win->fullscreen);
      break;
377 378 379 380 381 382
    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;
383 384 385
    case PROP_MINIMIZED:
      g_value_set_boolean (value, win->minimized);
      break;
386 387 388
    case PROP_WINDOW_TYPE:
      g_value_set_enum (value, win->type);
      break;
389 390 391
    case PROP_USER_TIME:
      g_value_set_uint (value, win->net_wm_user_time);
      break;
392 393 394
    case PROP_DEMANDS_ATTENTION:
      g_value_set_boolean (value, win->wm_state_demands_attention);
      break;
395
    case PROP_URGENT:
396
      g_value_set_boolean (value, win->urgent);
397
      break;
398 399 400
    case PROP_SKIP_TASKBAR:
      g_value_set_boolean (value, win->skip_taskbar);
      break;
Tomas Frydrych's avatar
Tomas Frydrych committed
401 402 403
    case PROP_MUTTER_HINTS:
      g_value_set_string (value, win->mutter_hints);
      break;
404 405 406
    case PROP_APPEARS_FOCUSED:
      g_value_set_boolean (value, meta_window_appears_focused (win));
      break;
407 408 409
    case PROP_WM_CLASS:
      g_value_set_string (value, win->res_class);
      break;
410 411 412 413 414 415
    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;
416 417
    case PROP_GTK_APPLICATION_ID:
      g_value_set_string (value, win->gtk_application_id);
418
      break;
419 420
    case PROP_GTK_UNIQUE_BUS_NAME:
      g_value_set_string (value, win->gtk_unique_bus_name);
421
      break;
422 423 424 425 426 427 428 429 430 431 432
    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);
433
      break;
434 435 436
    case PROP_ON_ALL_WORKSPACES:
      g_value_set_boolean (value, win->on_all_workspaces);
      break;
437 438
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
439
      break;
440 441 442 443 444 445 446 447 448 449 450 451 452
    }
}

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);
453
      break;
454 455 456
    }
}

Owen Taylor's avatar
Owen Taylor committed
457 458 459 460 461 462
static void
meta_window_class_init (MetaWindowClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  object_class->finalize = meta_window_finalize;
463

464 465
  object_class->get_property = meta_window_get_property;
  object_class->set_property = meta_window_set_property;
466

467 468
  klass->grab_op_began = meta_window_real_grab_op_began;
  klass->grab_op_ended = meta_window_real_grab_op_ended;
469
  klass->current_workspace_changed = meta_window_real_current_workspace_changed;
470
  klass->update_struts = meta_window_real_update_struts;
471
  klass->get_default_skip_hints = meta_window_real_get_default_skip_hints;
472
  klass->update_icon = meta_window_real_update_icon;
473
  klass->get_client_pid = meta_window_real_get_client_pid;
474

475 476 477 478 479 480 481
  obj_props[PROP_TITLE] =
    g_param_spec_string ("title",
                         "Title",
                         "The title of the window",
                         NULL,
                         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
  obj_props[PROP_ICON] =
482 483
    g_param_spec_pointer ("icon",
                          "Icon",
Egmont Koblinger's avatar
Egmont Koblinger committed
484
                          "Normal icon, usually 96x96 pixels",
485
                          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
486
  obj_props[PROP_MINI_ICON] =
487 488
    g_param_spec_pointer ("mini-icon",
                          "Mini Icon",
Egmont Koblinger's avatar
Egmont Koblinger committed
489
                          "Mini icon, usually 16x16 pixels",
490
                          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626
  obj_props[PROP_DECORATED] =
    g_param_spec_boolean ("decorated",
                          "Decorated",
                          "Whether window is decorated",
                          TRUE,
                          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
  obj_props[PROP_FULLSCREEN] =
    g_param_spec_boolean ("fullscreen",
                          "Fullscreen",
                          "Whether window is fullscreened",
                          FALSE,
                          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
  obj_props[PROP_MAXIMIZED_HORIZONTALLY] =
    g_param_spec_boolean ("maximized-horizontally",
                          "Maximized horizontally",
                          "Whether window is maximized horizontally",
                          FALSE,
                          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
  obj_props[PROP_MAXIMIZED_VERTICALLY] =
    g_param_spec_boolean ("maximized-vertically",
                          "Maximizing vertically",
                          "Whether window is maximized vertically",
                          FALSE,
                          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
  obj_props[PROP_MINIMIZED] =
    g_param_spec_boolean ("minimized",
                          "Minimizing",
                          "Whether window is minimized",
                          FALSE,
                          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
  obj_props[PROP_WINDOW_TYPE] =
    g_param_spec_enum ("window-type",
                       "Window Type",
                       "The type of the window",
                       META_TYPE_WINDOW_TYPE,
                       META_WINDOW_NORMAL,
                       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
  obj_props[PROP_USER_TIME] =
    g_param_spec_uint ("user-time",
                       "User time",
                       "Timestamp of last user interaction",
                       0,
                       G_MAXUINT,
                       0,
                       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
  obj_props[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 | G_PARAM_STATIC_STRINGS);
  obj_props[PROP_URGENT] =
    g_param_spec_boolean ("urgent",
                          "Urgent",
                          "Whether the urgent flag of WM_HINTS is set",
                          FALSE,
                          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
  obj_props[PROP_SKIP_TASKBAR] =
    g_param_spec_boolean ("skip-taskbar",
                          "Skip taskbar",
                          "Whether the skip-taskbar flag of WM_HINTS is set",
                          FALSE,
                          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
  obj_props[PROP_MUTTER_HINTS] =
    g_param_spec_string ("mutter-hints",
                         "_MUTTER_HINTS",
                         "Contents of the _MUTTER_HINTS property of this window",
                         NULL,
                         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
  obj_props[PROP_APPEARS_FOCUSED] =
    g_param_spec_boolean ("appears-focused",
                          "Appears focused",
                          "Whether the window is drawn as being focused",
                          FALSE,
                          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
  obj_props[PROP_RESIZEABLE] =
    g_param_spec_boolean ("resizeable",
                          "Resizeable",
                          "Whether the window can be resized",
                          FALSE,
                          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
  obj_props[PROP_ABOVE] =
    g_param_spec_boolean ("above",
                          "Above",
                          "Whether the window is shown as always-on-top",
                          FALSE,
                          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
  obj_props[PROP_WM_CLASS] =
    g_param_spec_string ("wm-class",
                         "WM_CLASS",
                         "Contents of the WM_CLASS property of this window",
                         NULL,
                         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
  obj_props[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_PARAM_STATIC_STRINGS);
  obj_props[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_PARAM_STATIC_STRINGS);
  obj_props[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",
                         NULL,
                         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
  obj_props[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",
                         NULL,
                         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
  obj_props[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_PARAM_STATIC_STRINGS);
  obj_props[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",
                         NULL,
                         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
  obj_props[PROP_ON_ALL_WORKSPACES] =
    g_param_spec_boolean ("on-all-workspaces",
                          "On all workspaces",
                          "Whether the window is set to appear on all workspaces",
                          FALSE,
                          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);

627
  g_object_class_install_properties (object_class, PROP_LAST, obj_props);
628

629 630 631 632
  window_signals[WORKSPACE_CHANGED] =
    g_signal_new ("workspace-changed",
                  G_TYPE_FROM_CLASS (object_class),
                  G_SIGNAL_RUN_LAST,
633
                  0,
Jasper St. Pierre's avatar
Jasper St. Pierre committed
634
                  NULL, NULL, NULL,
635
                  G_TYPE_NONE, 0);
Tomas Frydrych's avatar
Tomas Frydrych committed
636 637 638 639 640

  window_signals[FOCUS] =
    g_signal_new ("focus",
                  G_TYPE_FROM_CLASS (object_class),
                  G_SIGNAL_RUN_LAST,
641
                  0,
Jasper St. Pierre's avatar
Jasper St. Pierre committed
642
                  NULL, NULL, NULL,
Tomas Frydrych's avatar
Tomas Frydrych committed
643
                  G_TYPE_NONE, 0);
Tomas Frydrych's avatar
Tomas Frydrych committed
644 645 646 647 648

  window_signals[RAISED] =
    g_signal_new ("raised",
                  G_TYPE_FROM_CLASS (object_class),
                  G_SIGNAL_RUN_LAST,
649
                  0,
Jasper St. Pierre's avatar
Jasper St. Pierre committed
650
                  NULL, NULL, NULL,
Tomas Frydrych's avatar
Tomas Frydrych committed
651
                  G_TYPE_NONE, 0);
652

653 654 655 656 657 658 659 660
  window_signals[UNMANAGING] =
    g_signal_new ("unmanaging",
                  G_TYPE_FROM_CLASS (object_class),
                  G_SIGNAL_RUN_LAST,
                  0,
                  NULL, NULL, NULL,
                  G_TYPE_NONE, 0);

661 662 663 664
  window_signals[UNMANAGED] =
    g_signal_new ("unmanaged",
                  G_TYPE_FROM_CLASS (object_class),
                  G_SIGNAL_RUN_LAST,
665
                  0,
Jasper St. Pierre's avatar
Jasper St. Pierre committed
666
                  NULL, NULL, NULL,
667
                  G_TYPE_NONE, 0);
668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685

  /**
   * MetaWindow::position-changed:
   * @window: a #MetaWindow
   *
   * This is emitted when the position of a window might
   * have changed. Specifically, this is emitted when the
   * position of the toplevel window has changed, or when
   * the position of the client window has changed.
   */
  window_signals[POSITION_CHANGED] =
    g_signal_new ("position-changed",
                  G_TYPE_FROM_CLASS (object_class),
                  G_SIGNAL_RUN_LAST,
                  0,
                  NULL, NULL, NULL,
                  G_TYPE_NONE, 0);

686 687 688 689 690 691 692 693 694 695 696 697 698 699
  /**
   * MetaWindow::shown:
   * @window: a #MetaWindow
   *
   * This is emitted after a window has been shown.
   */
  window_signals[SHOWN] =
    g_signal_new ("shown",
                  G_TYPE_FROM_CLASS (object_class),
                  G_SIGNAL_RUN_LAST,
                  0,
                  NULL, NULL, NULL,
                  G_TYPE_NONE, 0);

700 701 702 703
  /**
   * MetaWindow::size-changed:
   * @window: a #MetaWindow
   *
704
   * This is emitted when the size of a window might
705 706 707 708 709 710 711 712 713 714 715
   * have changed. Specifically, this is emitted when the
   * size of the toplevel window has changed, or when the
   * size of the client window has changed.
   */
  window_signals[SIZE_CHANGED] =
    g_signal_new ("size-changed",
                  G_TYPE_FROM_CLASS (object_class),
                  G_SIGNAL_RUN_LAST,
                  0,
                  NULL, NULL, NULL,
                  G_TYPE_NONE, 0);
Owen Taylor's avatar
Owen Taylor committed
716 717 718 719 720
}

static void
meta_window_init (MetaWindow *self)
{
721
  self->stamp = next_window_stamp++;
722
  meta_prefs_add_listener (prefs_changed_callback, self);
Owen Taylor's avatar
Owen Taylor committed
723 724
}

725 726 727 728 729 730 731 732
static gboolean
is_desktop_or_dock_foreach (MetaWindow *window,
                            void       *data)
{
  gboolean *result = data;

  *result =
    window->type == META_WINDOW_DESKTOP ||
733 734
    window->type == META_WINDOW_DOCK ||
    window->skip_from_window_list;
735 736 737 738 739 740 741 742 743 744 745 746
  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)
{
747
  MetaWorkspaceManager *workspace_manager = window->display->workspace_manager;
748 749
  gboolean is_desktop_or_dock;

750
  if (!workspace_manager->active_workspace->showing_desktop)
751 752 753 754 755 756 757 758
    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;
759 760
  is_desktop_or_dock_foreach (window,
                              &is_desktop_or_dock);
761 762 763 764 765 766

  meta_window_foreach_ancestor (window, is_desktop_or_dock_foreach,
                                &is_desktop_or_dock);

  if (!is_desktop_or_dock)
    {
767 768 769
      meta_workspace_manager_minimize_all_on_active_workspace_except (workspace_manager,
                                                                      window);
      meta_workspace_manager_unshow_desktop (workspace_manager);
770 771 772
    }
}

773 774 775 776 777 778 779 780 781 782 783 784 785
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;

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

    default:
      return FALSE;
    }
796 797
}

798 799 800
static gboolean
client_window_should_be_mapped (MetaWindow *window)
{
Ting-Wei Lan's avatar
Ting-Wei Lan committed
801
#ifdef HAVE_WAYLAND
802
  if (window->client_type == META_WINDOW_CLIENT_TYPE_WAYLAND &&
803
      !meta_wayland_surface_get_buffer (window->surface))
804
    return FALSE;
Ting-Wei Lan's avatar
Ting-Wei Lan committed
805
#endif
806

807 808 809 810 811 812 813 814
  return !window->shaded;
}

static void
sync_client_window_mapped (MetaWindow *window)
{
  gboolean should_be_mapped = client_window_should_be_mapped (window);

815 816
  g_return_if_fail (!window->override_redirect);

817 818 819 820 821
  if (window->mapped == should_be_mapped)
    return;

  window->mapped = should_be_mapped;

822 823
  if (window->mapped)
    META_WINDOW_GET_CLASS (window)->map (window);
824
  else
825
    META_WINDOW_GET_CLASS (window)->unmap (window);
826 827
}

828 829 830
static gboolean
meta_window_update_flatpak_id (MetaWindow *window,
                               uint32_t    pid)
831 832 833 834
{
  g_autoptr(GKeyFile) key_file = NULL;
  g_autofree char *info_filename = NULL;

835 836
  g_return_val_if_fail (pid != 0, FALSE);
  g_return_val_if_fail (window->sandboxed_app_id == NULL, FALSE);
837 838 839 840 841

  key_file = g_key_file_new ();
  info_filename = g_strdup_printf ("/proc/%u/root/.flatpak-info", pid);

  if (!g_key_file_load_from_file (key_file, info_filename, G_KEY_FILE_NONE, NULL))
842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901
    return FALSE;

  window->sandboxed_app_id = g_key_file_get_string (key_file, "Application", "name", NULL);

  return TRUE;
}

static gboolean
meta_window_update_snap_id (MetaWindow *window,
                            uint32_t    pid)
{
  g_autofree char *security_label_filename = NULL;
  g_autofree char *security_label_contents = NULL;
  gsize i, security_label_contents_size = 0;
  char *contents_start;
  char *contents_end;
  char *sandboxed_app_id;

  g_return_val_if_fail (pid != 0, FALSE);
  g_return_val_if_fail (window->sandboxed_app_id == NULL, FALSE);

  security_label_filename = g_strdup_printf ("/proc/%u/attr/current", pid);

  if (!g_file_get_contents (security_label_filename,
                            &security_label_contents,
                            &security_label_contents_size,
                            NULL))
    return FALSE;

  if (!g_str_has_prefix (security_label_contents, SNAP_SECURITY_LABEL_PREFIX))
    return FALSE;

  /* We need to translate the security profile into the desktop-id.
   * The profile is in the form of 'snap.name-space.binary-name (current)'
   * while the desktop id will be name-space_binary-name.
   */
  security_label_contents_size -= sizeof (SNAP_SECURITY_LABEL_PREFIX) - 1;
  contents_start = security_label_contents + sizeof (SNAP_SECURITY_LABEL_PREFIX) - 1;
  contents_end = strchr (contents_start, ' ');

  if (contents_end)
    security_label_contents_size = contents_end - contents_start;

  for (i = 0; i < security_label_contents_size; ++i)
    {
      if (contents_start[i] == '.')
        contents_start[i] = '_';
    }

  sandboxed_app_id = g_malloc0 (security_label_contents_size + 1);
  memcpy (sandboxed_app_id, contents_start, security_label_contents_size);

  window->sandboxed_app_id = sandboxed_app_id;

  return TRUE;
}

static void
meta_window_update_sandboxed_app_id (MetaWindow *window)
{
902
  pid_t pid;
903 904 905

  g_clear_pointer (&window->sandboxed_app_id, g_free);

906
  pid = meta_window_get_pid (window);
907

908
  if (pid < 1)
909 910
    return;

911 912 913 914 915
  if (meta_window_update_flatpak_id (window, pid))
    return;

  if (meta_window_update_snap_id (window, pid))
    return;
916 917
}

918 919 920 921 922
static void
meta_window_update_desc (MetaWindow *window)
{
  g_clear_pointer (&window->desc, g_free);

923
  if (window->client_type == META_WINDOW_CLIENT_TYPE_X11)
924
    window->desc = g_strdup_printf ("0x%lx", window->xwindow);
925
  else
926 927 928
    {
      guint64 small_stamp = window->stamp - G_GUINT64_CONSTANT(0x100000000);

929
      window->desc = g_strdup_printf ("W%" G_GUINT64_FORMAT , small_stamp);
930
    }
931 932
}

933
static void
934 935
meta_window_main_monitor_changed (MetaWindow               *window,
                                  const MetaLogicalMonitor *old)
936 937 938 939
{
  META_WINDOW_GET_CLASS (window)->main_monitor_changed (window, old);

  if (old)
940
    g_signal_emit_by_name (window->display, "window-left-monitor",
941 942
                           old->number, window);
  if (window->monitor)
943
    g_signal_emit_by_name (window->display, "window-entered-monitor",
944 945 946
                           window->monitor->number, window);
}

947 948 949 950 951 952 953 954 955 956 957 958 959
MetaLogicalMonitor *
meta_window_calculate_main_logical_monitor (MetaWindow *window)
{
  MetaBackend *backend = meta_get_backend ();
  MetaMonitorManager *monitor_manager =
    meta_backend_get_monitor_manager (backend);
  MetaRectangle window_rect;

  meta_window_get_frame_rect (window, &window_rect);
  return meta_monitor_manager_get_logical_monitor_from_rect (monitor_manager,
                                                             &window_rect);
}

960 961 962 963 964 965 966 967 968
static void
meta_window_manage (MetaWindow *window)
{
  COGL_TRACE_BEGIN_SCOPED (MetaWindowManage,
                           "Window (manage)");

  META_WINDOW_GET_CLASS (window)->manage (window);
}

969 970 971 972 973 974 975 976
MetaWindow *
_meta_window_shared_new (MetaDisplay         *display,
                         MetaWindowClientType client_type,
                         MetaWaylandSurface  *surface,
                         Window               xwindow,
                         gulong               existing_wm_state,
                         MetaCompEffect       effect,
                         XWindowAttributes   *attrs)
Havoc Pennington's avatar
Havoc Pennington committed
977
{
978
  MetaWorkspaceManager *workspace_manager = display->workspace_manager;
Havoc Pennington's avatar
Havoc Pennington committed
979 980
  MetaWindow *window;

981 982 983
  COGL_TRACE_BEGIN_SCOPED (MetaWindowSharedNew,
                           "Window (new)");

Havoc Pennington's avatar
Havoc Pennington committed
984
  g_assert (attrs != NULL);
985

986
  meta_verbose ("attrs->map_state = %d (%s)",
Havoc Pennington's avatar
Havoc Pennington committed
987 988
                attrs->map_state,
                (attrs->map_state == IsUnmapped) ?
989
                "IsUnmapped" :
Havoc Pennington's avatar
Havoc Pennington committed
990
                (attrs->map_state == IsViewable) ?
991
                "IsViewable" :
Havoc Pennington's avatar
Havoc Pennington committed
992
                (attrs->map_state == IsUnviewable) ?
993 994
                "IsUnviewable" :
                "(unknown)");
995

996
  if (client_type == META_WINDOW_CLIENT_TYPE_X11 && !meta_is_wayland_compositor ())
997
    window = g_object_new (META_TYPE_WINDOW_X11, NULL);
998
#ifdef HAVE_WAYLAND
999 1000
  else if (client_type == META_WINDOW_CLIENT_TYPE_X11)
    window = g_object_new (META_TYPE_WINDOW_XWAYLAND, NULL);
1001
  else if (client_type == META_WINDOW_CLIENT_TYPE_WAYLAND)
1002
    window = g_object_new (META_TYPE_WINDOW_WAYLAND, NULL);
1003 1004 1005
#endif
  else
    g_assert_not_reached ();
rhp's avatar
...  
rhp committed
1006

1007
  window->constructing = TRUE;
1008

1009 1010
  window->client_type = client_type;
  window->surface = surface;
rhp's avatar
...  
rhp committed
1011
  window->xwindow = xwindow;
1012

rhp's avatar
...  
rhp committed
1013
  window->display = display;
1014 1015
  meta_display_register_stamp (window->display, &window->stamp, window);

1016
  window->workspace = NULL;
1017

1018 1019
  window->sync_request_counter = None;
  window->sync_request_serial = 0;
1020
  window->sync_request_timeout_id = 0;
1021
  window->sync_request_alarm = None;
1022

1023
  meta_window_update_sandboxed_app_id (window);
1024
  meta_window_update_desc (window);
1025

1026 1027
  window->override_redirect = attrs->override_redirect;

rhp's avatar
...  
rhp committed
1028
  /* avoid tons of stack updates */
1029
  meta_stack_freeze (window->display->stack);
1030

Havoc Pennington's avatar
Havoc Pennington committed
1031 1032 1033 1034
  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
1035

1036
  /* size_hints are the "request" */
Havoc Pennington's avatar
Havoc Pennington committed
1037 1038 1039 1040
  window->size_hints.x = attrs->x;
  window->size_hints.y = attrs->y;
  window->size_hints.width = attrs->width;
  window->size_hints.height = attrs->height;
1041 1042
  /* initialize the remaining size_hints as if size_hints.flags were zero */
  meta_set_normal_hints (window, NULL);
rhp's avatar
...  
rhp committed
1043 1044

  /* And this is our unmaximized size */
1045
  window->saved_rect = window->rect;
1046
  window->unconstrained_rect = window->rect;
1047

Havoc Pennington's avatar
Havoc Pennington committed
1048 1049
  window->depth = attrs->depth;
  window->xvisual = attrs->visual;
1050

rhp's avatar
...  
rhp committed
1051
  window->title = NULL;
rhp's avatar
...  
rhp committed
1052
  window->icon = NULL;
1053
  window->mini_icon = NULL;
rhp's avatar
...  
rhp committed
1054 1055

  window->frame = NULL;
rhp's avatar
...  
rhp committed
1056
  window->has_focus = FALSE;
1057
  window->attached_focus_window = NULL;
rhp's avatar
...  
rhp committed
1058

1059 1060 1061 1062
  window->maximized_horizontally = FALSE;
  window->maximized_vertically = FALSE;
  window->maximize_horizontally_after_placement = FALSE;
  window->maximize_vertically_after_placement = FALSE;
1063
  window->minimize_after_placement = FALSE;
1064
  window->fullscreen = FALSE;
1065
  window->require_fully_onscreen = TRUE;
1066
  window->require_on_single_monitor = TRUE;
1067
  window->require_titlebar_visible = TRUE;
rhp's avatar
...  
rhp committed
1068
  window->on_all_workspaces = FALSE;
1069
  window->on_all_workspaces_requested = FALSE;
1070 1071
  window->tile_mode = META_TILE_NONE;
  window->tile_monitor_number = -1;
1072
  window->tile_hfraction = -1.;
rhp's avatar
...  
rhp committed
1073
  window->shaded = FALSE;
rhp's avatar
...  
rhp committed
1074 1075
  window->initially_iconic = FALSE;
  window->minimized = FALSE;
1076
  window->tab_unminimized = FALSE;
rhp's avatar
...  
rhp committed
1077
  window->iconic = FALSE;
Havoc Pennington's avatar
Havoc Pennington committed
1078
  window->mapped = attrs->map_state != IsUnmapped;
1079
  window->known_to_compositor = FALSE;
1080 1081
  window->visible_to_compositor = FALSE;
  window->pending_compositor_effect = effect;
1082 1083
  /* if already mapped, no need to worry about focus-on-first-time-showing */
  window->showing_for_first_time = !window->mapped;
1084 1085 1086
  /* 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);
1087
  window->denied_focus_and_not_transient = FALSE;
rhp's avatar
...  
rhp committed
1088
  window->unmanaging = FALSE;
1089
  window->is_in_queues = 0;
rhp's avatar
...  
rhp committed
1090 1091
  window->keys_grabbed = FALSE;
  window->grab_on_frame = FALSE;
rhp's avatar
...  
rhp committed
1092
  window->all_keys_grabbed = FALSE;
rhp's avatar
...  
rhp committed
1093
  window->withdrawn = FALSE;
rhp's avatar
...  
rhp committed
1094
  window->initial_workspace_set = FALSE;
1095 1096
  window->initial_timestamp_set = FALSE;
  window->net_wm_user_time_set = FALSE;
1097
  window->user_time_window = None;
1098
  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;
1103

rhp's avatar
...  
rhp committed
1104
  window->unmaps_pending = 0;
1105
  window->reparents_pending = 0;
1106

rhp's avatar
...  
rhp committed
1107
  window->mwm_decorated = TRUE;