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

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

/* 
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
rhp's avatar
...  
rhp committed
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
 * 
 * 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.
 * 
 * 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 "edge-resistance.h"
rhp's avatar
...  
rhp committed
30
#include "util.h"
31
#include "frame-private.h"
rhp's avatar
...  
rhp committed
32
#include "errors.h"
rhp's avatar
...  
rhp committed
33
#include "workspace.h"
rhp's avatar
...  
rhp committed
34
#include "stack.h"
rhp's avatar
...  
rhp committed
35 36
#include "keybindings.h"
#include "ui.h"
rhp's avatar
...  
rhp committed
37
#include "place.h"
rhp's avatar
...  
rhp committed
38
#include "session.h"
39
#include "effects.h"
40
#include "prefs.h"
41
#include "resizepopup.h"
42
#include "xprops.h"
43
#include "group.h"
44
#include "window-props.h"
45
#include "constraints.h"
46
#include "compositor.h"
Adam Jackson's avatar
Adam Jackson committed
47
#include "effects.h"
rhp's avatar
...  
rhp committed
48

rhp's avatar
...  
rhp committed
49
#include <X11/Xatom.h>
50
#include <string.h>
rhp's avatar
...  
rhp committed
51

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

56 57
static int destroying_windows_disallowed = 0;

rhp's avatar
...  
rhp committed
58 59

static void     update_sm_hints           (MetaWindow     *window);
Havoc Pennington's avatar
Havoc Pennington committed
60 61
static void     update_role               (MetaWindow     *window);
static void     update_net_wm_type        (MetaWindow     *window);
62
static void     update_net_frame_extents  (MetaWindow     *window);
rhp's avatar
...  
rhp committed
63
static void     recalc_window_type        (MetaWindow     *window);
rhp's avatar
...  
rhp committed
64
static void     recalc_window_features    (MetaWindow     *window);
65
static void     invalidate_work_areas     (MetaWindow     *window);
66
static void     recalc_window_type        (MetaWindow     *window);
67
static void     set_wm_state              (MetaWindow     *window,
rhp's avatar
...  
rhp committed
68
                                           int             state);
69
static void     set_net_wm_state          (MetaWindow     *window);
70

rhp's avatar
...  
rhp committed
71 72 73 74 75 76
static void     send_configure_notify     (MetaWindow     *window);
static gboolean process_property_notify   (MetaWindow     *window,
                                           XPropertyEvent *event);
static void     meta_window_show          (MetaWindow     *window);
static void     meta_window_hide          (MetaWindow     *window);

77
static void     meta_window_save_rect         (MetaWindow    *window);
78 79
static void     save_user_window_placement    (MetaWindow    *window);
static void     force_save_user_window_placement (MetaWindow    *window);
80

Havoc Pennington's avatar
Havoc Pennington committed
81 82 83 84 85 86 87
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
88

89 90 91
static void     ensure_mru_position_after (MetaWindow *window,
                                           MetaWindow *after_this_one);

rhp's avatar
...  
rhp committed
92

93
static void meta_window_move_resize_now (MetaWindow  *window);
rhp's avatar
...  
rhp committed
94

95
static void meta_window_unqueue (MetaWindow *window, guint queuebits);
96

97 98 99 100 101 102 103 104 105 106 107 108 109
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);


110
static void meta_window_flush_calc_showing   (MetaWindow *window);
rhp's avatar
...  
rhp committed
111

112 113 114
static gboolean queue_calc_showing_func (MetaWindow *window,
                                         void       *data);

rhp's avatar
...  
rhp committed
115 116 117
static void meta_window_apply_session_info (MetaWindow                  *window,
                                            const MetaWindowSessionInfo *info);

118 119 120
static void unmaximize_window_before_freeing (MetaWindow        *window);
static void unminimize_window_and_all_transient_parents (MetaWindow *window);

121 122 123 124 125 126 127 128 129
/* Idle handlers for the three queues. The "data" parameter in each case
 * will be a GINT_TO_POINTER of the index into the queue arrays to use.
 *
 * 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);
130

131
#ifdef WITH_VERBOSE_MODE
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
static const char*
wm_state_to_string (int state)
{
  switch (state)
    {
    case NormalState:
      return "NormalState";      
    case IconicState:
      return "IconicState";
    case WithdrawnState:
      return "WithdrawnState";
    }

  return "Unknown";
}
147
#endif
148

149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
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;

172
  if (!window->screen->active_workspace->showing_desktop)
173 174 175 176 177 178 179 180
    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;
181 182
  is_desktop_or_dock_foreach (window,
                              &is_desktop_or_dock);
183 184 185 186 187 188

  meta_window_foreach_ancestor (window, is_desktop_or_dock_foreach,
                                &is_desktop_or_dock);

  if (!is_desktop_or_dock)
    {
189 190
      meta_screen_minimize_all_on_active_workspace_except (window->screen,
                                                           window);
191 192 193 194
      meta_screen_unshow_desktop (window->screen);      
    }
}

rhp's avatar
...  
rhp committed
195
MetaWindow*
196 197 198
meta_window_new (MetaDisplay *display,
                 Window       xwindow,
                 gboolean     must_be_viewable)
rhp's avatar
...  
rhp committed
199 200
{
  XWindowAttributes attrs;
Havoc Pennington's avatar
Havoc Pennington committed
201 202 203 204 205 206 207 208 209
  MetaWindow *window;
  
  meta_display_grab (display);
  meta_error_trap_push (display); /* Push a trap over all of window
                                   * creation, to reduce XSync() calls
                                   */
  
  meta_error_trap_push_with_return (display);
  
210 211 212 213 214 215 216 217 218 219 220
  if (XGetWindowAttributes (display->xdisplay,xwindow, &attrs)) 
   {
      if(meta_error_trap_pop_with_return (display, TRUE) != Success)
       {
          meta_verbose ("Failed to get attributes for window 0x%lx\n",
                        xwindow);
          meta_error_trap_pop (display, TRUE);
          meta_display_ungrab (display);
          return NULL;
       }
      window = meta_window_new_with_attrs (display, xwindow,
Havoc Pennington's avatar
Havoc Pennington committed
221
                                       must_be_viewable, &attrs);
222 223 224 225 226 227 228 229 230 231 232 233
   }
  else
   {
         meta_error_trap_pop_with_return (display, TRUE); 
         meta_verbose ("Failed to get attributes for window 0x%lx\n",
                        xwindow);
         meta_error_trap_pop (display, TRUE);
         meta_display_ungrab (display);
         return NULL;
   }
   
  
Havoc Pennington's avatar
Havoc Pennington committed
234 235 236 237 238 239 240 241 242 243 244 245 246
  meta_error_trap_pop (display, FALSE);
  meta_display_ungrab (display);

  return window;
}

MetaWindow*
meta_window_new_with_attrs (MetaDisplay       *display,
                            Window             xwindow,
                            gboolean           must_be_viewable,
                            XWindowAttributes *attrs)
{
  MetaWindow *window;
rhp's avatar
...  
rhp committed
247
  GSList *tmp;
rhp's avatar
...  
rhp committed
248
  MetaWorkspace *space;
rhp's avatar
...  
rhp committed
249
  gulong existing_wm_state;
250
  gulong event_mask;
251
  MetaMoveResizeFlags flags;
252
#define N_INITIAL_PROPS 18
253
  Atom initial_props[N_INITIAL_PROPS];
254
  int i;
255
  gboolean has_shape;
Havoc Pennington's avatar
Havoc Pennington committed
256 257

  g_assert (attrs != NULL);
258
  g_assert (N_INITIAL_PROPS == (int) G_N_ELEMENTS (initial_props));
rhp's avatar
...  
rhp committed
259
  
rhp's avatar
...  
rhp committed
260
  meta_verbose ("Attempting to manage 0x%lx\n", xwindow);
rhp's avatar
...  
rhp committed
261

262
  if (meta_display_xwindow_is_a_no_focus_window (display, xwindow))
263 264 265 266 267
    {
      meta_verbose ("Not managing no_focus_window 0x%lx\n",
                    xwindow);
      return NULL;
    }
Havoc Pennington's avatar
Havoc Pennington committed
268 269 270 271 272 273

  if (attrs->override_redirect)
    {
      meta_verbose ("Deciding not to manage override_redirect window 0x%lx\n", xwindow);
      return NULL;
    }
274
  
rhp's avatar
...  
rhp committed
275 276
  /* Grab server */
  meta_display_grab (display);
277 278 279
  meta_error_trap_push (display); /* Push a trap over all of window
                                   * creation, to reduce XSync() calls
                                   */
rhp's avatar
...  
rhp committed
280

Havoc Pennington's avatar
Havoc Pennington committed
281
  meta_verbose ("must_be_viewable = %d attrs->map_state = %d (%s)\n",
282
                must_be_viewable,
Havoc Pennington's avatar
Havoc Pennington committed
283 284
                attrs->map_state,
                (attrs->map_state == IsUnmapped) ?
285
                "IsUnmapped" :
Havoc Pennington's avatar
Havoc Pennington committed
286
                (attrs->map_state == IsViewable) ?
287
                "IsViewable" :
Havoc Pennington's avatar
Havoc Pennington committed
288
                (attrs->map_state == IsUnviewable) ?
289 290 291
                "IsUnviewable" :
                "(unknown)");
  
rhp's avatar
...  
rhp committed
292
  existing_wm_state = WithdrawnState;
Havoc Pennington's avatar
Havoc Pennington committed
293
  if (must_be_viewable && attrs->map_state != IsViewable)
rhp's avatar
...  
rhp committed
294
    {
rhp's avatar
...  
rhp committed
295 296 297
      /* Only manage if WM_STATE is IconicState or NormalState */
      gulong state;

Havoc Pennington's avatar
Havoc Pennington committed
298 299
      /* 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,
300 301
                                                   display->atom_WM_STATE,
                                                   display->atom_WM_STATE,
Havoc Pennington's avatar
Havoc Pennington committed
302
                                                   &state) &&
rhp's avatar
...  
rhp committed
303 304 305
            (state == IconicState || state == NormalState)))
        {
          meta_verbose ("Deciding not to manage unmapped or unviewable window 0x%lx\n", xwindow);
306
          meta_error_trap_pop (display, TRUE);
rhp's avatar
...  
rhp committed
307 308 309 310
          meta_display_ungrab (display);
          return NULL;
        }

rhp's avatar
...  
rhp committed
311
      existing_wm_state = state;
312 313
      meta_verbose ("WM_STATE of %lx = %s\n", xwindow,
                    wm_state_to_string (existing_wm_state));
rhp's avatar
...  
rhp committed
314 315
    }
  
316
  meta_error_trap_push_with_return (display);
rhp's avatar
...  
rhp committed
317 318 319
  
  XAddToSaveSet (display->xdisplay, xwindow);

320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344
  event_mask =
    PropertyChangeMask | EnterWindowMask | LeaveWindowMask |
    FocusChangeMask | ColormapChangeMask;

  XSelectInput (display->xdisplay, xwindow, event_mask);

  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,
345
                  "Window has_shape = %d extents %d,%d %u x %u\n",
346 347 348 349
                  has_shape, x_bounding, y_bounding,
                  w_bounding, h_bounding);
    }
#endif
rhp's avatar
...  
rhp committed
350 351

  /* Get rid of any borders */
Havoc Pennington's avatar
Havoc Pennington committed
352
  if (attrs->border_width != 0)
rhp's avatar
...  
rhp committed
353
    XSetWindowBorderWidth (display->xdisplay, xwindow, 0);
rhp's avatar
...  
rhp committed
354 355

  /* Get rid of weird gravities */
Havoc Pennington's avatar
Havoc Pennington committed
356
  if (attrs->win_gravity != NorthWestGravity)
rhp's avatar
...  
rhp committed
357 358 359 360 361 362 363 364 365 366
    {
      XSetWindowAttributes set_attrs;
      
      set_attrs.win_gravity = NorthWestGravity;
      
      XChangeWindowAttributes (display->xdisplay,
                               xwindow,
                               CWWinGravity,
                               &set_attrs);
    }
rhp's avatar
...  
rhp committed
367
  
368
  if (meta_error_trap_pop_with_return (display, FALSE) != Success)
rhp's avatar
...  
rhp committed
369 370 371
    {
      meta_verbose ("Window 0x%lx disappeared just as we tried to manage it\n",
                    xwindow);
372
      meta_error_trap_pop (display, FALSE);
rhp's avatar
...  
rhp committed
373
      meta_display_ungrab (display);
rhp's avatar
...  
rhp committed
374 375
      return NULL;
    }
rhp's avatar
...  
rhp committed
376

Havoc Pennington's avatar
Havoc Pennington committed
377
  g_assert (!attrs->override_redirect);
rhp's avatar
...  
rhp committed
378 379 380
  
  window = g_new (MetaWindow, 1);

381 382
  window->constructing = TRUE;
  
383 384 385
  window->dialog_pid = -1;
  window->dialog_pipe = -1;
  
rhp's avatar
...  
rhp committed
386
  window->xwindow = xwindow;
387
  
rhp's avatar
...  
rhp committed
388 389 390 391
  /* this is in window->screen->display, but that's too annoying to
   * type
   */
  window->display = display;
392
  window->workspace = NULL;
393 394

#ifdef HAVE_XSYNC
395 396 397 398
  window->sync_request_counter = None;
  window->sync_request_serial = 0;
  window->sync_request_time.tv_sec = 0;
  window->sync_request_time.tv_usec = 0;
399
#endif
rhp's avatar
...  
rhp committed
400 401 402 403 404
  
  window->screen = NULL;
  tmp = display->screens;
  while (tmp != NULL)
    {
405 406
      MetaScreen *scr = tmp->data;

Havoc Pennington's avatar
Havoc Pennington committed
407
      if (scr->xroot == attrs->root)
rhp's avatar
...  
rhp committed
408 409 410 411 412 413 414 415 416
        {
          window->screen = tmp->data;
          break;
        }
      
      tmp = tmp->next;
    }
  
  g_assert (window->screen);
rhp's avatar
...  
rhp committed
417

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

rhp's avatar
...  
rhp committed
420 421
  /* avoid tons of stack updates */
  meta_stack_freeze (window->screen->stack);
422 423

  window->has_shape = has_shape;
rhp's avatar
...  
rhp committed
424
  
Havoc Pennington's avatar
Havoc Pennington committed
425 426 427 428
  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
429

rhp's avatar
...  
rhp committed
430
  /* And border width, size_hints are the "request" */
Havoc Pennington's avatar
Havoc Pennington committed
431 432 433 434 435
  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;
436 437
  /* initialize the remaining size_hints as if size_hints.flags were zero */
  meta_set_normal_hints (window, NULL);
rhp's avatar
...  
rhp committed
438 439 440

  /* And this is our unmaximized size */
  window->saved_rect = window->rect;
Havoc Pennington's avatar
Havoc Pennington committed
441
  window->user_rect = window->rect;
rhp's avatar
...  
rhp committed
442
  
Havoc Pennington's avatar
Havoc Pennington committed
443 444 445
  window->depth = attrs->depth;
  window->xvisual = attrs->visual;
  window->colormap = attrs->colormap;
446
  
rhp's avatar
...  
rhp committed
447
  window->title = NULL;
rhp's avatar
...  
rhp committed
448 449
  window->icon_name = NULL;
  window->icon = NULL;
450
  window->mini_icon = NULL;
Havoc Pennington's avatar
Havoc Pennington committed
451 452 453
  meta_icon_cache_init (&window->icon_cache);
  window->wm_hints_pixmap = None;
  window->wm_hints_mask = None;
rhp's avatar
...  
rhp committed
454 455

  window->frame = NULL;
rhp's avatar
...  
rhp committed
456
  window->has_focus = FALSE;
rhp's avatar
...  
rhp committed
457

458 459 460 461
  window->maximized_horizontally = FALSE;
  window->maximized_vertically = FALSE;
  window->maximize_horizontally_after_placement = FALSE;
  window->maximize_vertically_after_placement = FALSE;
462
  window->minimize_after_placement = FALSE;
463
  window->fullscreen = FALSE;
464 465
  window->require_fully_onscreen = TRUE;
  window->require_on_single_xinerama = TRUE;
466
  window->require_titlebar_visible = TRUE;
rhp's avatar
...  
rhp committed
467
  window->on_all_workspaces = FALSE;
rhp's avatar
...  
rhp committed
468
  window->shaded = FALSE;
rhp's avatar
...  
rhp committed
469 470
  window->initially_iconic = FALSE;
  window->minimized = FALSE;
Søren Sandmann's avatar
Søren Sandmann committed
471
  window->was_minimized = FALSE;
472
  window->tab_unminimized = FALSE;
rhp's avatar
...  
rhp committed
473
  window->iconic = FALSE;
Havoc Pennington's avatar
Havoc Pennington committed
474
  window->mapped = attrs->map_state != IsUnmapped;
475 476
  /* if already mapped, no need to worry about focus-on-first-time-showing */
  window->showing_for_first_time = !window->mapped;
rhp's avatar
...  
rhp committed
477 478
  /* if already mapped we don't want to do the placement thing */
  window->placed = window->mapped;
rhp's avatar
...  
rhp committed
479
  if (window->placed)
480 481 482
    meta_topic (META_DEBUG_PLACEMENT,
                "Not placing window 0x%lx since it's already mapped\n",
                xwindow);
483
  window->denied_focus_and_not_transient = FALSE;
rhp's avatar
...  
rhp committed
484
  window->unmanaging = FALSE;
485
  window->is_in_queues = 0;
rhp's avatar
...  
rhp committed
486 487
  window->keys_grabbed = FALSE;
  window->grab_on_frame = FALSE;
rhp's avatar
...  
rhp committed
488
  window->all_keys_grabbed = FALSE;
rhp's avatar
...  
rhp committed
489
  window->withdrawn = FALSE;
rhp's avatar
...  
rhp committed
490
  window->initial_workspace_set = FALSE;
491 492
  window->initial_timestamp_set = FALSE;
  window->net_wm_user_time_set = FALSE;
493
  window->user_time_window = None;
rhp's avatar
...  
rhp committed
494
  window->calc_placement = FALSE;
495
  window->shaken_loose = FALSE;
496
  window->have_focus_click_grab = FALSE;
497
  window->disable_sync = FALSE;
rhp's avatar
...  
rhp committed
498
  
rhp's avatar
...  
rhp committed
499
  window->unmaps_pending = 0;
500
  
rhp's avatar
...  
rhp committed
501
  window->mwm_decorated = TRUE;
502
  window->mwm_border_only = FALSE;
rhp's avatar
...  
rhp committed
503 504 505 506 507
  window->mwm_has_close_func = TRUE;
  window->mwm_has_minimize_func = TRUE;
  window->mwm_has_maximize_func = TRUE;
  window->mwm_has_move_func = TRUE;
  window->mwm_has_resize_func = TRUE;
rhp's avatar
...  
rhp committed
508
  
rhp's avatar
...  
rhp committed
509 510 511 512
  window->decorated = TRUE;
  window->has_close_func = TRUE;
  window->has_minimize_func = TRUE;
  window->has_maximize_func = TRUE;
rhp's avatar
...  
rhp committed
513 514 515
  window->has_move_func = TRUE;
  window->has_resize_func = TRUE;

rhp's avatar
...  
rhp committed
516
  window->has_shade_func = TRUE;
517 518

  window->has_fullscreen_func = TRUE;
519 520

  window->always_sticky = FALSE;
rhp's avatar
...  
rhp committed
521
  
rhp's avatar
...  
rhp committed
522
  window->wm_state_modal = FALSE;
523 524
  window->skip_taskbar = FALSE;
  window->skip_pager = FALSE;
rhp's avatar
...  
rhp committed
525 526
  window->wm_state_skip_taskbar = FALSE;
  window->wm_state_skip_pager = FALSE;
Havoc Pennington's avatar
Havoc Pennington committed
527 528
  window->wm_state_above = FALSE;
  window->wm_state_below = FALSE;
529
  window->wm_state_demands_attention = FALSE;
rhp's avatar
...  
rhp committed
530
  
rhp's avatar
...  
rhp committed
531 532 533 534
  window->res_class = NULL;
  window->res_name = NULL;
  window->role = NULL;
  window->sm_client_id = NULL;
535
  window->wm_client_machine = NULL;
Havoc Pennington's avatar
Havoc Pennington committed
536 537
  window->startup_id = NULL;
  
538
  window->net_wm_pid = -1;
rhp's avatar
...  
rhp committed
539 540 541
  
  window->xtransient_for = None;
  window->xclient_leader = None;
542
  window->transient_parent_is_root_window = FALSE;
rhp's avatar
...  
rhp committed
543
  
rhp's avatar
...  
rhp committed
544 545
  window->type = META_WINDOW_NORMAL;
  window->type_atom = None;
rhp's avatar
...  
rhp committed
546

547
  window->struts = NULL;
548

549 550 551 552
  window->using_net_wm_name              = FALSE;
  window->using_net_wm_visible_name      = FALSE;
  window->using_net_wm_icon_name         = FALSE;
  window->using_net_wm_visible_icon_name = FALSE;
553 554

  window->need_reread_icon = TRUE;
555
  
556 557
  window->layer = META_LAYER_LAST; /* invalid value */
  window->stack_position = -1;
rhp's avatar
...  
rhp committed
558
  window->initial_workspace = 0; /* not used */
559
  window->initial_timestamp = 0; /* not used */
Havoc Pennington's avatar
Havoc Pennington committed
560
  
rhp's avatar
...  
rhp committed
561
  meta_display_register_x_window (display, &window->xwindow, window);
Havoc Pennington's avatar
Havoc Pennington committed
562 563 564 565 566 567 568


  /* assign the window to its group, or create a new group if needed
   */
  window->group = NULL;
  window->xgroup_leader = None;
  meta_window_compute_group (window);
569
  
570 571 572 573
  /* Fill these in the order we want them to be gotten.  we want to
   * get window name and class first so we can use them in error
   * messages and such.  However, name is modified depending on
   * wm_client_machine, so push it slightly sooner.
574
   */
575
  i = 0;
576 577
  initial_props[i++] = display->atom_WM_CLIENT_MACHINE;
  initial_props[i++] = display->atom__NET_WM_NAME;
578
  initial_props[i++] = XA_WM_CLASS;
579
  initial_props[i++] = display->atom__NET_WM_PID;
580
  initial_props[i++] = XA_WM_NAME;
581
  initial_props[i++] = display->atom__NET_WM_ICON_NAME;
582
  initial_props[i++] = XA_WM_ICON_NAME;
583 584 585
  initial_props[i++] = display->atom__NET_WM_DESKTOP;
  initial_props[i++] = display->atom__NET_STARTUP_ID;
  initial_props[i++] = display->atom__NET_WM_SYNC_REQUEST_COUNTER;
586
  initial_props[i++] = XA_WM_NORMAL_HINTS;
587
  initial_props[i++] = display->atom_WM_PROTOCOLS;
588
  initial_props[i++] = XA_WM_HINTS;
589 590 591
  initial_props[i++] = display->atom__NET_WM_USER_TIME;
  initial_props[i++] = display->atom__NET_WM_STATE;
  initial_props[i++] = display->atom__MOTIF_WM_HINTS;
592
  initial_props[i++] = XA_WM_TRANSIENT_FOR;
593
  initial_props[i++] = display->atom__NET_WM_USER_TIME_WINDOW;
594
  g_assert (N_INITIAL_PROPS == i);
595 596
  
  meta_window_reload_properties (window, initial_props, N_INITIAL_PROPS);
597
  
rhp's avatar
...  
rhp committed
598 599
  update_sm_hints (window); /* must come after transient_for */
  update_role (window);
rhp's avatar
...  
rhp committed
600
  update_net_wm_type (window);
601
  meta_window_update_icon_now (window);
602

rhp's avatar
...  
rhp committed
603 604
  if (window->initially_iconic)
    {
rhp's avatar
...  
rhp committed
605 606 607
      /* WM_HINTS said minimized */
      window->minimized = TRUE;
      meta_verbose ("Window %s asked to start out minimized\n", window->desc);
rhp's avatar
...  
rhp committed
608
    }
rhp's avatar
...  
rhp committed
609 610 611 612 613 614 615 616 617 618 619 620 621

  if (existing_wm_state == IconicState)
    {
      /* WM_STATE said minimized */
      window->minimized = TRUE;
      meta_verbose ("Window %s had preexisting WM_STATE = IconicState, minimizing\n",
                    window->desc);

      /* Assume window was previously placed, though perhaps it's
       * been iconic its whole life, we have no way of knowing.
       */
      window->placed = TRUE;
    }
Havoc Pennington's avatar
Havoc Pennington committed
622 623 624 625 626

  /* Apply any window attributes such as initial workspace
   * based on startup notification
   */
  meta_screen_apply_startup_properties (window->screen, window);
627 628 629 630 631 632 633 634 635 636 637 638 639

  /* Try to get a "launch timestamp" for the window.  If the window is
   * a transient, we'd like to be able to get a last-usage timestamp
   * from the parent window.  If the window has no parent, there isn't
   * much we can do...except record the current time so that any children
   * can use this time as a fallback.
   */
  if (!window->net_wm_user_time_set) {
    MetaWindow *parent = NULL;
    if (window->xtransient_for)
      parent = meta_display_lookup_x_window (window->display,
                                             window->xtransient_for);

640 641 642
    /* First, maybe the app was launched with startup notification using an
     * obsolete version of the spec; use that timestamp if it exists.
     */
643
    if (window->initial_timestamp_set)
644 645 646
      /* NOTE: Do NOT toggle net_wm_user_time_set to true; this is just
       * being recorded as a fallback for potential transients
       */
647 648 649 650
      window->net_wm_user_time = window->initial_timestamp;
    else if (parent != NULL)
      meta_window_set_user_time(window, parent->net_wm_user_time);
    else
651 652 653
      /* NOTE: Do NOT toggle net_wm_user_time_set to true; this is just
       * being recorded as a fallback for potential transients
       */
654 655 656
      window->net_wm_user_time =
        meta_display_get_current_time_roundtrip (window->display);
  }
rhp's avatar
...  
rhp committed
657
  
rhp's avatar
...  
rhp committed
658 659
  if (window->decorated)
    meta_window_ensure_frame (window);
rhp's avatar
...  
rhp committed
660

rhp's avatar
...  
rhp committed
661
  meta_window_grab_keys (window);
662 663 664 665 666
  if (window->type != META_WINDOW_DOCK)
    {
      meta_display_grab_window_buttons (window->display, window->xwindow);
      meta_display_grab_focus_window_button (window->display, window);
    }
667 668 669 670 671 672 673 674 675 676 677

  if (window->type == META_WINDOW_DESKTOP ||
      window->type == META_WINDOW_DOCK)
    {
      /* Change the default, but don't enforce this if the user
       * focuses the dock/desktop and unsticks it using key shortcuts.
       * Need to set this before adding to the workspaces so the MRU
       * lists will be updated.
       */
      window->on_all_workspaces = TRUE;
    }
rhp's avatar
...  
rhp committed
678
  
rhp's avatar
...  
rhp committed
679 680 681 682
  /* For the workspace, first honor hints,
   * if that fails put transients with parents,
   * otherwise put window on active space
   */
rhp's avatar
...  
rhp committed
683
  
rhp's avatar
...  
rhp committed
684 685
  if (window->initial_workspace_set)
    {
686
      if (window->initial_workspace == (int) 0xFFFFFFFF)
687
        {
688 689 690 691
          meta_topic (META_DEBUG_PLACEMENT,
                      "Window %s is initially on all spaces\n",
                      window->desc);
          
692 693 694
	  /* need to set on_all_workspaces first so that it will be
	   * added to all the MRU lists
	   */
Rob Adams's avatar
Rob Adams committed
695
          window->on_all_workspaces = TRUE;
696
          meta_workspace_add_window (window->screen->active_workspace, window);
697 698 699
        }
      else
        {
700 701 702 703
          meta_topic (META_DEBUG_PLACEMENT,
                      "Window %s is initially on space %d\n",
                      window->desc, window->initial_workspace);

704
          space =
705 706
            meta_screen_get_workspace_by_index (window->screen,
                                                window->initial_workspace);
707 708 709 710
          
          if (space)
            meta_workspace_add_window (space, window);
        }
rhp's avatar
...  
rhp committed
711 712
    }
  
713
  if (window->workspace == NULL && 
rhp's avatar
...  
rhp committed
714 715 716 717 718 719 720 721
      window->xtransient_for != None)
    {
      /* Try putting dialog on parent's workspace */
      MetaWindow *parent;

      parent = meta_display_lookup_x_window (window->display,
                                             window->xtransient_for);

722
      if (parent && parent->workspace)
rhp's avatar
...  
rhp committed
723
        {
724
          meta_topic (META_DEBUG_PLACEMENT,
725
                      "Putting window %s on same workspace as parent %s\n",
726
                      window->desc, parent->desc);
rhp's avatar
...  
rhp committed
727 728 729 730
          
          if (parent->on_all_workspaces)
            window->on_all_workspaces = TRUE;
          
731 732 733
	  /* this will implicitly add to the appropriate MRU lists
	   */
          meta_workspace_add_window (parent->workspace, window);
rhp's avatar
...  
rhp committed
734 735 736
        }
    }
  
737
  if (window->workspace == NULL)
rhp's avatar
...  
rhp committed
738
    {
739 740 741 742
      meta_topic (META_DEBUG_PLACEMENT,
                  "Putting window %s on active workspace\n",
                  window->desc);
      
rhp's avatar
...  
rhp committed
743 744 745 746
      space = window->screen->active_workspace;

      meta_workspace_add_window (space, window);
    }
747
  
748 749
  /* for the various on_all_workspaces = TRUE possible above */
  meta_window_set_current_workspace_hint (window);
750 751
  
  meta_window_update_struts (window);
752

753 754 755 756 757 758 759 760 761 762
  /* Must add window to stack before doing move/resize, since the
   * window might have fullscreen size (i.e. should have been
   * fullscreen'd; acrobat is one such braindead case; it withdraws
   * and remaps its window whenever trying to become fullscreen...)
   * and thus constraints may try to auto-fullscreen it which also
   * means restacking it.
   */
  meta_stack_add (window->screen->stack, 
                  window);

rhp's avatar
...  
rhp committed
763 764 765 766
  /* Put our state back where it should be,
   * passing TRUE for is_configure_request, ICCCM says
   * initial map is handled same as configure request
   */
767
  flags =
768
    META_IS_CONFIGURE_REQUEST | META_IS_MOVE_ACTION | META_IS_RESIZE_ACTION;
Havoc Pennington's avatar
Havoc Pennington committed
769
  meta_window_move_resize_internal (window,
770 771
                                    flags,
                                    window->size_hints.win_gravity,
rhp's avatar
...  
rhp committed
772 773 774 775 776
                                    window->size_hints.x,
                                    window->size_hints.y,
                                    window->size_hints.width,
                                    window->size_hints.height);

rhp's avatar
...  
rhp committed
777 778 779 780 781 782 783 784 785 786 787 788 789
  /* Now try applying saved stuff from the session */
  {
    const MetaWindowSessionInfo *info;

    info = meta_window_lookup_saved_state (window);

    if (info)
      {
        meta_window_apply_session_info (window, info);
        meta_window_release_saved_state (info);
      }
  }
  
790 791 792 793 794 795
  /* FIXME we have a tendency to set this then immediately
   * change it again.
   */
  set_wm_state (window, window->iconic ? IconicState : NormalState);
  set_net_wm_state (window);

rhp's avatar
...  
rhp committed
796 797
  /* Sync stack changes */
  meta_stack_thaw (window->screen->stack);
798 799

  /* disable show desktop mode unless we're a desktop component */
800
  maybe_leave_show_desktop_mode (window);
rhp's avatar
...  
rhp committed
801
  
802
  meta_window_queue (window, META_QUEUE_CALC_SHOWING);
803 804 805 806 807 808
  /* See bug 303284; a transient of the given window can already exist, in which
   * case we think it should probably be shown.
   */
  meta_window_foreach_transient (window,
                                 queue_calc_showing_func,
                                 NULL);
809 810 811 812 813 814
  /* See bug 334899; the window may have minimized ancestors
   * which need to be shown.
   *
   * However, we shouldn't unminimize windows here when opening
   * a new display because that breaks passing _NET_WM_STATE_HIDDEN
   * between window managers when replacing them; see bug 358042.
815 816 817
   *
   * And we shouldn't unminimize windows if they were initially
   * iconic.
818
   */
819
  if (!display->display_opening && !window->initially_iconic)
820
    unminimize_window_and_all_transient_parents (window);
821 822

  meta_error_trap_pop (display, FALSE); /* pop the XSync()-reducing trap */
rhp's avatar
...  
rhp committed
823
  meta_display_ungrab (display);
824 825 826
 
  window->constructing = FALSE;

rhp's avatar
...  
rhp committed
827 828 829
  return window;
}

Havoc Pennington's avatar
Havoc Pennington committed
830
/* This function should only be called from the end of meta_window_new_with_attrs () */
rhp's avatar
...  
rhp committed
831 832 833 834
static void
meta_window_apply_session_info (MetaWindow *window,
                                const MetaWindowSessionInfo *info)
{
835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860
  if (info->stack_position_set)
    {
      meta_topic (META_DEBUG_SM,
                  "Restoring stack position %d for window %s\n",
                  info->stack_position, window->desc);

      /* FIXME well, I'm not sure how to do this. */
    }

  if (info->minimized_set)
    {
      meta_topic (META_DEBUG_SM,
                  "Restoring minimized state %d for window %s\n",
                  info->minimized, window->desc);

      if (window->has_minimize_func && info->minimized)
        meta_window_minimize (window);
    }

  if (info->maximized_set)
    {
      meta_topic (META_DEBUG_SM,
                  "Restoring maximized state %d for window %s\n",
                  info->maximized, window->desc);
      
      if (window->has_maximize_func && info->maximized)
861 862 863 864
        {
          meta_window_maximize (window, 
                                META_MAXIMIZE_HORIZONTAL |
                                META_MAXIMIZE_VERTICAL);
865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881

          if (info->saved_rect_set)
            {
              meta_topic (META_DEBUG_SM,
                          "Restoring saved rect %d,%d %dx%d for window %s\n",
                          info->saved_rect.x,
                          info->saved_rect.y,
                          info->saved_rect.width,
                          info->saved_rect.height,
                          window->desc);
              
              window->saved_rect.x = info->saved_rect.x;
              window->saved_rect.y = info->saved_rect.y;
              window->saved_rect.width = info->saved_rect.width;
              window->saved_rect.height = info->saved_rect.height;
            }
	}
882 883
    }
  
rhp's avatar
...  
rhp committed
884 885 886
  if (info->on_all_workspaces_set)
    {
      window->on_all_workspaces = info->on_all_workspaces;
887 888 889
      meta_topic (META_DEBUG_SM,
                  "Restoring sticky state %d for window %s\n",
                  window->on_all_workspaces, window->desc);
rhp's avatar
...  
rhp committed
890 891 892 893 894 895 896 897 898 899 900 901 902 903 904
    }
  
  if (info->workspace_indices)
    {
      GSList *tmp;
      GSList *spaces;      

      spaces = NULL;
      
      tmp = info->workspace_indices;
      while (tmp != NULL)
        {
          MetaWorkspace *space;          

          space =
905 906
            meta_screen_get_workspace_by_index (window->screen,
                                                GPOINTER_TO_INT (tmp->data));
rhp's avatar
...  
rhp committed
907 908 909 910 911 912 913 914 915 916 917 918 919 920
          
          if (space)
            spaces = g_slist_prepend (spaces, space);
          
          tmp = tmp->next;
        }

      if (spaces)
        {
          /* This briefly breaks the invariant that we are supposed
           * to always be on some workspace. But we paranoically
           * ensured that one of the workspaces from the session was
           * indeed valid, so we know we'll go right back to one.
           */
921 922
          if (window->workspace)
            meta_workspace_remove_window (window->workspace, window);
rhp's avatar
...  
rhp committed
923

924 925 926 927 928
          /* Only restore to the first workspace if the window
           * happened to be on more than one, since we have replaces
           * window->workspaces with window->workspace
           */
          meta_workspace_add_window (spaces->data, window);              
rhp's avatar
...  
rhp committed
929

930 931 932 933
          meta_topic (META_DEBUG_SM,
                      "Restoring saved window %s to workspace %d\n",
                      window->desc,
                      meta_workspace_index (spaces->data));
rhp's avatar
...  
rhp committed
934 935 936 937
              
          g_slist_free (spaces);
        }
    }
rhp's avatar
...  
rhp committed
938 939 940 941

  if (info->geometry_set)
    {
      int x, y, w, h;
942
      MetaMoveResizeFlags flags;
rhp's avatar
...  
rhp committed
943 944 945 946 947 948 949 950 951 952 953 954 955 956
      
      window->placed = TRUE; /* don't do placement algorithms later */

      x = info->rect.x;
      y = info->rect.y;

      w = window->size_hints.base_width +
        info->rect.width * window->size_hints.width_inc;
      h = window->size_hints.base_height +
        info->rect.height * window->size_hints.height_inc;

      /* Force old gravity, ignoring anything now set */
      window->size_hints.win_gravity = info->gravity;
      
957 958 959
      meta_topic (META_DEBUG_SM,
                  "Restoring pos %d,%d size %d x %d for %s\n",
                  x, y, w, h, window->desc);
rhp's avatar
...  
rhp committed
960
      
961
      flags = META_DO_GRAVITY_ADJUST | META_IS_MOVE_ACTION | META_IS_RESIZE_ACTION;
rhp's avatar
...  
rhp committed
962
      meta_window_move_resize_internal (window,
963 964
                                        flags,
                                        window->size_hints.win_gravity,
rhp's avatar
...  
rhp committed
965 966
                                        x, y, w, h);
    }