clutter-stage-x11.c 27.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
/* Clutter.
 * An OpenGL based 'interactive canvas' library.
 * Authored By Matthew Allum  <mallum@openedhand.com>
 * Copyright (C) 2006-2007 OpenedHand
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

26 27 28 29
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

30 31 32 33
#include "clutter-backend-x11.h"
#include "clutter-stage-x11.h"
#include "clutter-x11.h"

34
#include "../clutter-stage-window.h"
35 36 37 38 39 40 41 42 43 44
#include "../clutter-main.h"
#include "../clutter-feature.h"
#include "../clutter-color.h"
#include "../clutter-util.h"
#include "../clutter-event.h"
#include "../clutter-enum-types.h"
#include "../clutter-private.h"
#include "../clutter-debug.h"
#include "../clutter-units.h"

45
#include "cogl/cogl.h"
46 47 48 49 50

#ifdef HAVE_XFIXES
#include <X11/extensions/Xfixes.h>
#endif

51 52
#define STAGE_X11_IS_MAPPED(s)  ((((ClutterStageX11 *) (s))->wm_state & STAGE_X11_WITHDRAWN) == 0)

53 54 55 56
static void clutter_stage_window_iface_init (ClutterStageWindowIface *iface);

G_DEFINE_TYPE_WITH_CODE (ClutterStageX11,
                         clutter_stage_x11,
57
                         G_TYPE_OBJECT,
58 59
                         G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_STAGE_WINDOW,
                                                clutter_stage_window_iface_init));
60 61 62 63 64 65 66

#define _NET_WM_STATE_REMOVE        0    /* remove/unset property */
#define _NET_WM_STATE_ADD           1    /* add/set property */
#define _NET_WM_STATE_TOGGLE        2    /* toggle property  */

static void
send_wmspec_change_state (ClutterBackendX11 *backend_x11,
67 68 69
                          Window             window,
                          Atom               state,
                          gboolean           add)
70 71 72
{
  XClientMessageEvent xclient;

73 74
  CLUTTER_NOTE (BACKEND, "%s NET_WM state", add ? "adding" : "removing");

75 76 77 78
  memset (&xclient, 0, sizeof (xclient));

  xclient.type         = ClientMessage;
  xclient.window       = window;
79
  xclient.message_type = backend_x11->atom_NET_WM_STATE;
80 81 82 83 84 85 86 87 88
  xclient.format       = 32;

  xclient.data.l[0] = add ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
  xclient.data.l[1] = state;
  xclient.data.l[2] = 0;
  xclient.data.l[3] = 0;
  xclient.data.l[4] = 0;

  XSendEvent (backend_x11->xdpy, 
89 90
              DefaultRootWindow(backend_x11->xdpy), 
              False,
91 92 93 94 95
              SubstructureRedirectMask|SubstructureNotifyMask,
              (XEvent *)&xclient);
}

void
96 97 98
clutter_stage_x11_fix_window_size (ClutterStageX11 *stage_x11,
                                   gint             new_width,
                                   gint             new_height)
99
{
100 101
  ClutterBackend *backend = clutter_get_default_backend ();
  ClutterBackendX11 *backend_x11;
102 103
  gboolean resize;

104 105 106
  g_return_if_fail (CLUTTER_IS_BACKEND_X11 (backend));
  backend_x11 = CLUTTER_BACKEND_X11 (backend);

107
  resize = clutter_stage_get_user_resizable (stage_x11->wrapper);
108

109
  if (stage_x11->xwin != None && !stage_x11->is_foreign_xwin)
110
    {
111
      guint min_width, min_height;
112 113 114 115
      XSizeHints *size_hints;

      size_hints = XAllocSizeHints();

116 117 118
      clutter_stage_get_minimum_size (stage_x11->wrapper,
                                      &min_width,
                                      &min_height);
119

120 121
      if (new_width <= 0)
        new_width = min_width;
122

123 124
      if (new_height <= 0)
        new_height = min_height;
125

126
      size_hints->flags = 0;
127

128 129
      /* If we are going fullscreen then we don't want any
         restrictions on the window size */
130
      if (!stage_x11->fullscreening)
131
        {
132 133 134 135 136 137 138
          if (resize)
            {
              size_hints->min_width = min_width;
              size_hints->min_height = min_height;
              size_hints->flags = PMinSize;
            }
          else
139
            {
140 141 142 143 144
              size_hints->min_width = new_width;
              size_hints->min_height = new_height;
              size_hints->max_width = new_width;
              size_hints->max_height = new_height;
              size_hints->flags = PMinSize | PMaxSize;
145
            }
146
        }
147

148
      XSetWMNormalHints (backend_x11->xdpy, stage_x11->xwin, size_hints);
149 150 151 152 153 154

      XFree(size_hints);
    }
}

void
155
clutter_stage_x11_set_wm_protocols (ClutterStageX11 *stage_x11)
156
{
157 158
  ClutterBackend *backend = clutter_get_default_backend ();
  ClutterBackendX11 *backend_x11;
159 160 161
  Atom protocols[2];
  int n = 0;
  
162 163 164
  g_return_if_fail (CLUTTER_IS_BACKEND_X11 (backend));
  backend_x11 = CLUTTER_BACKEND_X11 (backend);

165 166
  protocols[n++] = backend_x11->atom_WM_DELETE_WINDOW;
  protocols[n++] = backend_x11->atom_NET_WM_PING;
167

168
  XSetWMProtocols (backend_x11->xdpy, stage_x11->xwin, protocols, n);
169 170 171
}

static void
172 173
clutter_stage_x11_get_geometry (ClutterStageWindow *stage_window,
                                ClutterGeometry    *geometry)
174
{
175 176
  ClutterBackend *backend = clutter_get_default_backend ();
  ClutterBackendX11 *backend_x11;
177
  ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
178

179 180 181
  g_return_if_fail (CLUTTER_IS_BACKEND_X11 (backend));
  backend_x11 = CLUTTER_BACKEND_X11 (backend);

182 183 184
  /* If we're fullscreen, return the size of the display. */
  if ((stage_x11->state & CLUTTER_STAGE_STATE_FULLSCREEN) &&
      stage_x11->fullscreening)
185
    {
186 187
      geometry->width = DisplayWidth (backend_x11->xdpy, backend_x11->xscreen_num);
      geometry->height = DisplayHeight (backend_x11->xdpy, backend_x11->xscreen_num);
188 189 190

      return;
    }
191

192 193
  geometry->width = stage_x11->xwin_width;
  geometry->height = stage_x11->xwin_height;
194 195 196
}

static void
197 198 199
clutter_stage_x11_resize (ClutterStageWindow *stage_window,
                          gint                width,
                          gint                height)
200
{
201 202
  ClutterBackend *backend = clutter_get_default_backend ();
  ClutterBackendX11 *backend_x11;
203
  ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
204
  ClutterStage *stage = stage_x11->wrapper;
205 206
  gboolean resize;

207 208
  if (stage_x11->is_foreign_xwin)
    {
209 210 211 212
      /* If this is a foreign window we won't get a ConfigureNotify,
       * so we need to manually set the size and queue a relayout on the
       * stage here (as is normally done in response to ConfigureNotify).
       */
213 214
      stage_x11->xwin_width = width;
      stage_x11->xwin_height = height;
215
      clutter_actor_queue_relayout (CLUTTER_ACTOR (stage_x11->wrapper));
216 217 218
      return;
    }

219 220 221 222
  /* If we're going fullscreen, don't mess with the size */
  if (stage_x11->fullscreening)
    return;

223
  resize = clutter_stage_get_user_resizable (stage_x11->wrapper);
224

225 226 227
  g_return_if_fail (CLUTTER_IS_BACKEND_X11 (backend));
  backend_x11 = CLUTTER_BACKEND_X11 (backend);

228
  if (width == 0 || height == 0)
229
    {
230 231 232 233
      /* Should not happen, if this turns up we need to debug it and
       * determine the cleanest way to fix.
       */
      g_warning ("X11 stage not allowed to have 0 width or height");
234 235
      width = 1;
      height = 1;
236 237
    }

238
  CLUTTER_NOTE (BACKEND, "New size received: (%d, %d)", width, height);
239

240
  if (stage_x11->xwin != None)
241
    {
242
      clutter_stage_x11_fix_window_size (stage_x11, width, height);
243

244 245
      if (width != stage_x11->xwin_width ||
          height != stage_x11->xwin_height)
246
        {
247 248 249
          CLUTTER_NOTE (BACKEND, "%s: XResizeWindow[%x] (%d, %d)",
                        G_STRLOC,
                        (unsigned int) stage_x11->xwin,
250 251
                        width,
                        height);
252

253 254 255
          CLUTTER_SET_PRIVATE_FLAGS (stage_x11->wrapper,
                                     CLUTTER_STAGE_IN_RESIZE);

256
          XResizeWindow (backend_x11->xdpy,
257
                         stage_x11->xwin,
258 259
                         width,
                         height);
260 261 262 263 264 265 266 267 268

          /* If the viewport hasn't previously been initialized then even
           * though we can't guarantee that the server will honour our request
           * we need to ensure a valid viewport is set before our first paint.
           */
          if (G_UNLIKELY (!stage_x11->viewport_initialized))
            {
              ClutterPerspective perspective;
              clutter_stage_get_perspective (stage, &perspective);
269 270
              _cogl_setup_viewport (width,
                                    height,
271 272 273 274 275
                                    perspective.fovy,
                                    perspective.aspect,
                                    perspective.z_near,
                                    perspective.z_far);
            }
276
        }
277 278 279
    }
}

280 281 282
static inline void
set_wm_pid (ClutterStageX11 *stage_x11)
{
283 284
  ClutterBackend *backend = clutter_get_default_backend ();
  ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend);
285 286
  long pid;

287
  if (stage_x11->xwin == None || stage_x11->is_foreign_xwin)
288 289 290
    return;

  /* this will take care of WM_CLIENT_MACHINE and WM_LOCALE_NAME */
291
  XSetWMProperties (backend_x11->xdpy, stage_x11->xwin,
292 293 294 295 296
                    NULL,
                    NULL,
                    NULL, 0,
                    NULL, NULL, NULL);

297
  pid = getpid ();
298
  XChangeProperty (backend_x11->xdpy,
299 300 301 302 303 304
                   stage_x11->xwin,
                   backend_x11->atom_NET_WM_PID, XA_CARDINAL, 32,
                   PropModeReplace,
                   (guchar *) &pid, 1);
}

305 306 307
static inline void
set_wm_title (ClutterStageX11 *stage_x11)
{
308 309 310 311 312
  ClutterBackend *backend = clutter_get_default_backend ();
  ClutterBackendX11 *backend_x11;

  g_return_if_fail (CLUTTER_IS_BACKEND_X11 (backend));
  backend_x11 = CLUTTER_BACKEND_X11 (backend);
313

314
  if (stage_x11->xwin == None || stage_x11->is_foreign_xwin)
315 316 317 318
    return;

  if (stage_x11->title == NULL)
    {
319
      XDeleteProperty (backend_x11->xdpy,
320 321 322 323 324
                       stage_x11->xwin, 
                       backend_x11->atom_NET_WM_NAME);
    }
  else
    {
325
      XChangeProperty (backend_x11->xdpy,
326
                       stage_x11->xwin, 
327 328
                       backend_x11->atom_NET_WM_NAME,
                       backend_x11->atom_UTF8_STRING,
329 330
                       8, 
                       PropModeReplace, 
331
                       (unsigned char *) stage_x11->title,
332 333 334 335 336 337 338
                       (int) strlen (stage_x11->title));
    }
}

static inline void
set_cursor_visible (ClutterStageX11 *stage_x11)
{
339 340 341 342 343 344
  ClutterBackend *backend = clutter_get_default_backend ();
  ClutterBackendX11 *backend_x11;

  g_return_if_fail (CLUTTER_IS_BACKEND_X11 (backend));
  backend_x11 = CLUTTER_BACKEND_X11 (backend);

345 346 347 348 349 350 351 352 353 354
  if (stage_x11->xwin == None)
    return;

  CLUTTER_NOTE (BACKEND, "setting cursor state ('%s') over stage window (%u)",
                stage_x11->is_cursor_visible ? "visible" : "invisible",
                (unsigned int) stage_x11->xwin);

  if (stage_x11->is_cursor_visible)
    {
#if 0 /* HAVE_XFIXES - seems buggy/unreliable */
355
      XFixesShowCursor (backend_x11->xdpy, stage_x11->xwin);
356
#else
357
      XUndefineCursor (backend_x11->xdpy, stage_x11->xwin);
358 359 360 361 362 363 364
#endif /* HAVE_XFIXES */
    }
  else
    {
#if 0 /* HAVE_XFIXES - seems buggy/unreliable, check cursor in firefox 
       *               loading page after hiding.  
      */
365
      XFixesHideCursor (backend_x11->xdpy, stage_x11->xwin);
366 367 368 369 370
#else
      XColor col;
      Pixmap pix;
      Cursor curs;

371
      pix = XCreatePixmap (backend_x11->xdpy, stage_x11->xwin, 1, 1, 1);
372
      memset (&col, 0, sizeof (col));
373
      curs = XCreatePixmapCursor (backend_x11->xdpy,
374 375 376
                                  pix, pix,
                                  &col, &col,
                                  1, 1);
377 378
      XFreePixmap (backend_x11->xdpy, pix);
      XDefineCursor (backend_x11->xdpy, stage_x11->xwin, curs);
379 380 381 382
#endif /* HAVE_XFIXES */
    }
}

383 384
static gboolean
clutter_stage_x11_realize (ClutterStageWindow *stage_window)
385
{
386
  ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
387

388
  set_wm_pid (stage_x11);
389 390
  set_wm_title (stage_x11);
  set_cursor_visible (stage_x11);
391 392

  return TRUE;
393 394
}

395
static void
396 397
clutter_stage_x11_set_fullscreen (ClutterStageWindow *stage_window,
                                  gboolean            is_fullscreen)
398
{
399 400
  ClutterBackend *backend = clutter_get_default_backend ();
  ClutterBackendX11 *backend_x11;
401 402
  ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
  ClutterStage *stage = stage_x11->wrapper;
403

404 405 406
  g_return_if_fail (CLUTTER_IS_BACKEND_X11 (backend));
  backend_x11 = CLUTTER_BACKEND_X11 (backend);

407
  if (stage == NULL)
408 409
    return;

410 411 412 413 414
  if (!!(stage_x11->state & CLUTTER_STAGE_STATE_FULLSCREEN) == is_fullscreen)
    return;

  CLUTTER_NOTE (BACKEND, "%ssetting fullscreen", is_fullscreen ? "" : "un");

415 416
  CLUTTER_SET_PRIVATE_FLAGS (stage, CLUTTER_ACTOR_SYNC_MATRICES);

417
  if (is_fullscreen)
418
    {
419 420
      int width, height;

421 422 423 424 425
      /* FIXME: this will do the wrong thing for dual-headed
         displays. This will return the size of the combined display
         but Metacity (at least) will fullscreen to only one of the
         displays. This will cause the actor to report the wrong size
         until the ConfigureNotify for the correct size is received */
426 427
      width  = DisplayWidth (backend_x11->xdpy, backend_x11->xscreen_num);
      height = DisplayHeight (backend_x11->xdpy, backend_x11->xscreen_num);
428

429 430
      /* Set the fullscreen hint so we can retain the old size of the window. */
      stage_x11->fullscreening = TRUE;
431

432
      if (stage_x11->xwin != None)
433
        {
434 435 436 437 438
          /* if the actor is not mapped we resize the stage window to match
           * the size of the screen; this is useful for e.g. EGLX to avoid
           * a resize when calling clutter_stage_fullscreen() before showing
           * the stage
           */
439
          if (!/*CLUTTER_ACTOR_IS_MAPPED (stage_x11->wrapper)*/ STAGE_X11_IS_MAPPED (stage_x11))
440
            {
441 442
              CLUTTER_NOTE (BACKEND, "Fullscreening unmapped stage");

443
              /* FIXME: This wont work if we support more states */
444
              XChangeProperty (backend_x11->xdpy,
445 446 447 448
                               stage_x11->xwin,
                               backend_x11->atom_NET_WM_STATE, XA_ATOM, 32,
                               PropModeReplace,
                               (unsigned char *) &backend_x11->atom_NET_WM_STATE_FULLSCREEN, 1);
449 450 451
            }
          else
            {
452 453
              CLUTTER_NOTE (BACKEND, "Fullscreening mapped stage");

454 455 456 457
              /* We need to fix the window size so that it will remove
                 the maximum and minimum window hints. Otherwise
                 metacity will honour the restrictions and not
                 fullscreen correctly. */
458
              clutter_stage_x11_fix_window_size (stage_x11, -1, -1);
459

460 461 462
              send_wmspec_change_state (backend_x11, stage_x11->xwin,
                                        backend_x11->atom_NET_WM_STATE_FULLSCREEN,
                                        TRUE);
463 464
            }
        }
465 466 467
    }
  else
    {
468
      stage_x11->fullscreening = FALSE;
469

470
      if (stage_x11->xwin != None)
471
        {
472
          if (!/*CLUTTER_ACTOR_IS_MAPPED (stage_x11->wrapper)*/ STAGE_X11_IS_MAPPED (stage_x11))
473
            {
474 475
              CLUTTER_NOTE (BACKEND, "Un-fullscreening unmapped stage");

476
              /* FIXME: This wont work if we support more states */
477
              XDeleteProperty (backend_x11->xdpy,
478 479 480 481 482
                               stage_x11->xwin, 
                               backend_x11->atom_NET_WM_STATE);
            }
          else
            {
483 484
              CLUTTER_NOTE (BACKEND, "Un-fullscreening mapped stage");

485 486 487 488
              send_wmspec_change_state (backend_x11,
                                        stage_x11->xwin,
                                        backend_x11->atom_NET_WM_STATE_FULLSCREEN,
                                        FALSE);
489

490 491
              /* Fix the window size to restore the minimum/maximum
                 restriction */
492 493 494
              clutter_stage_x11_fix_window_size (stage_x11,
                                                 stage_x11->xwin_width,
                                                 stage_x11->xwin_height);
495 496
            }
        }
497
    }
498

499 500
  clutter_stage_ensure_viewport (CLUTTER_STAGE (stage_x11->wrapper));
  clutter_actor_queue_relayout (CLUTTER_ACTOR (stage_x11->wrapper));
501 502 503
}

static void
504 505
clutter_stage_x11_set_cursor_visible (ClutterStageWindow *stage_window,
                                      gboolean            cursor_visible)
506
{
507
  ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
508

509 510
  stage_x11->is_cursor_visible = (cursor_visible == TRUE);
  set_cursor_visible (stage_x11);
511 512 513
}

static void
514 515
clutter_stage_x11_set_title (ClutterStageWindow *stage_window,
                             const gchar        *title)
516
{
517
  ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
518

519 520 521
  g_free (stage_x11->title);
  stage_x11->title = g_strdup (title);
  set_wm_title (stage_x11);
522 523 524
}

static void
525 526
clutter_stage_x11_set_user_resizable (ClutterStageWindow *stage_window,
                                      gboolean            is_resizable)
527
{
528
  ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
529

530 531 532
  clutter_stage_x11_fix_window_size (stage_x11,
                                     stage_x11->xwin_width,
                                     stage_x11->xwin_height);
533 534 535 536 537
}

static void
update_wm_hints (ClutterStageX11 *stage_x11)
{
538
  ClutterBackend *backend;
539
  ClutterBackendX11 *backend_x11;
540 541 542 543 544
  XWMHints wm_hints;

  if (stage_x11->wm_state & STAGE_X11_WITHDRAWN)
    return;

545 546 547 548 549 550 551 552
  if (stage_x11->is_foreign_xwin)
    return;

  backend = clutter_get_default_backend ();

  g_return_if_fail (CLUTTER_IS_BACKEND_X11 (backend));
  backend_x11 = CLUTTER_BACKEND_X11 (backend);

553 554 555
  wm_hints.flags = StateHint;
  wm_hints.initial_state = NormalState;

556
  XSetWMHints (backend_x11->xdpy, stage_x11->xwin, &wm_hints);
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
}

static void
set_stage_state (ClutterStageX11      *stage_x11,
                 ClutterStageX11State  unset_flags,
                 ClutterStageX11State  set_flags)
{
  ClutterStageX11State new_stage_state, old_stage_state;

  old_stage_state = stage_x11->wm_state;

  new_stage_state = old_stage_state;
  new_stage_state |= set_flags;
  new_stage_state &= ~unset_flags;

  if (new_stage_state == old_stage_state)
    return;

  stage_x11->wm_state = new_stage_state;
}

static void
clutter_stage_x11_show (ClutterStageWindow *stage_window,
                        gboolean            do_raise)
{
582 583
  ClutterBackend *backend = clutter_get_default_backend ();
  ClutterBackendX11 *backend_x11;
584 585
  ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);

586 587 588
  g_return_if_fail (CLUTTER_IS_BACKEND_X11 (backend));
  backend_x11 = CLUTTER_BACKEND_X11 (backend);

589 590
  if (stage_x11->xwin != None)
    {
591
      if (do_raise && !stage_x11->is_foreign_xwin)
592 593 594
        {
          CLUTTER_NOTE (BACKEND, "Raising stage[%lu]",
                        (unsigned long) stage_x11->xwin);
595
          XRaiseWindow (backend_x11->xdpy, stage_x11->xwin);
596
        }
597 598 599

      if (!STAGE_X11_IS_MAPPED (stage_x11))
        {
600 601 602
          CLUTTER_NOTE (BACKEND, "Mapping stage[%lu]",
                        (unsigned long) stage_x11->xwin);

603
          set_stage_state (stage_x11, STAGE_X11_WITHDRAWN, 0);
604 605

          update_wm_hints (stage_x11);
606 607 608 609 610

          if (stage_x11->fullscreening)
            clutter_stage_x11_set_fullscreen (stage_window, TRUE);
          else
            clutter_stage_x11_set_fullscreen (stage_window, FALSE);
611 612 613 614 615 616
        }

      g_assert (STAGE_X11_IS_MAPPED (stage_x11));

      clutter_actor_map (CLUTTER_ACTOR (stage_x11->wrapper));

617 618
      if (!stage_x11->is_foreign_xwin)
        XMapWindow (backend_x11->xdpy, stage_x11->xwin);
619 620 621 622 623 624
    }
}

static void
clutter_stage_x11_hide (ClutterStageWindow *stage_window)
{
625 626
  ClutterBackend *backend = clutter_get_default_backend ();
  ClutterBackendX11 *backend_x11;
627 628
  ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);

629 630 631
  g_return_if_fail (CLUTTER_IS_BACKEND_X11 (backend));
  backend_x11 = CLUTTER_BACKEND_X11 (backend);

632 633 634 635 636 637 638 639 640
  if (stage_x11->xwin != None)
    {
      if (STAGE_X11_IS_MAPPED (stage_x11))
        set_stage_state (stage_x11, 0, STAGE_X11_WITHDRAWN);

      g_assert (!STAGE_X11_IS_MAPPED (stage_x11));

      clutter_actor_unmap (CLUTTER_ACTOR (stage_x11->wrapper));

641 642
      if (!stage_x11->is_foreign_xwin)
        XWithdrawWindow (backend_x11->xdpy, stage_x11->xwin, 0);
643
    }
644 645
}

646 647 648 649 650 651
static ClutterActor *
clutter_stage_x11_get_wrapper (ClutterStageWindow *stage_window)
{
  return CLUTTER_ACTOR (CLUTTER_STAGE_X11 (stage_window)->wrapper);
}

652 653 654 655 656 657 658 659 660 661
static void
clutter_stage_x11_finalize (GObject *gobject)
{
  ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (gobject);

  g_free (stage_x11->title);

  G_OBJECT_CLASS (clutter_stage_x11_parent_class)->finalize (gobject);
}

662 663 664 665 666 667 668 669 670 671 672
static void
clutter_stage_x11_dispose (GObject *gobject)
{
  G_OBJECT_CLASS (clutter_stage_x11_parent_class)->dispose (gobject);
}

static void
clutter_stage_x11_class_init (ClutterStageX11Class *klass)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);

673
  gobject_class->finalize = clutter_stage_x11_finalize;
674 675 676 677 678 679 680
  gobject_class->dispose = clutter_stage_x11_dispose;
}

static void
clutter_stage_x11_init (ClutterStageX11 *stage)
{
  stage->xwin = None;
681 682
  stage->xwin_width = 640;
  stage->xwin_height = 480;
683

684 685
  stage->wm_state = STAGE_X11_WITHDRAWN;

686
  stage->is_foreign_xwin = FALSE;
687
  stage->fullscreening = FALSE;
688
  stage->is_cursor_visible = TRUE;
689
  stage->viewport_initialized = FALSE;
690 691

  stage->title = NULL;
692

693 694 695 696 697 698 699 700 701 702 703 704 705
  stage->wrapper = NULL;

  CLUTTER_SET_PRIVATE_FLAGS (stage, CLUTTER_ACTOR_IS_TOPLEVEL);
}

static void
clutter_stage_window_iface_init (ClutterStageWindowIface *iface)
{
  iface->get_wrapper = clutter_stage_x11_get_wrapper;
  iface->set_title = clutter_stage_x11_set_title;
  iface->set_fullscreen = clutter_stage_x11_set_fullscreen;
  iface->set_cursor_visible = clutter_stage_x11_set_cursor_visible;
  iface->set_user_resizable = clutter_stage_x11_set_user_resizable;
706 707
  iface->show = clutter_stage_x11_show;
  iface->hide = clutter_stage_x11_hide;
708 709 710
  iface->resize = clutter_stage_x11_resize;
  iface->get_geometry = clutter_stage_x11_get_geometry;
  iface->realize = clutter_stage_x11_realize;
711 712 713 714 715 716
}

/**
 * clutter_x11_get_stage_window:
 * @stage: a #ClutterStage
 *
717
 * Gets the stages X Window.
718 719 720 721 722 723 724 725
 *
 * Return value: An XID for the stage window.
 *
 * Since: 0.4
 */
Window
clutter_x11_get_stage_window (ClutterStage *stage)
{
726 727 728
  ClutterStageWindow *impl;

  g_return_val_if_fail (CLUTTER_IS_STAGE (stage), None);
729

730 731 732 733
  impl = _clutter_stage_get_window (stage);
  g_assert (CLUTTER_IS_STAGE_X11 (impl));

  return CLUTTER_STAGE_X11 (impl)->xwin;
734 735
}

736 737 738 739 740 741 742 743 744 745
/**
 * clutter_x11_get_stage_from_window:
 * @win: an X Window ID
 *
 * Gets the stage for a particular X window.  
 *
 * Return value: The stage or NULL if a stage does not exist for the window.
 *
 * Since: 0.8
 */
746
ClutterStage *
747 748 749
clutter_x11_get_stage_from_window (Window win)
{
  ClutterStageManager *stage_manager;
750
  const GSList        *stages, *s;
751

752 753
  stage_manager = clutter_stage_manager_get_default ();
  stages = clutter_stage_manager_peek_stages (stage_manager);
754

755 756
  /* XXX: might use a hash here for performance resaon */
  for (s = stages; s != NULL; s = s->next)
757
    {
758
      ClutterStage *stage = s->data;
759 760 761 762
      ClutterStageWindow *impl;

      impl = _clutter_stage_get_window (stage);
      g_assert (CLUTTER_IS_STAGE_X11 (impl));
763

764 765
      if (CLUTTER_STAGE_X11 (impl)->xwin == win)
        return stage;
766 767 768 769 770
    }

  return NULL;
}

771 772 773 774
/**
 * clutter_x11_get_stage_visual:
 * @stage: a #ClutterStage
 *
775 776 777 778 779 780 781
 * Returns an XVisualInfo suitable for creating a foreign window for the given
 * stage. NOTE: It doesn't do as the name may suggest, which is return the
 * XVisualInfo that was used to create an existing window for the given stage.
 *
 * XXX: It might be best to deprecate this function and replace with something
 * along the lines of clutter_backend_x11_get_foreign_visual () or perhaps
 * clutter_stage_x11_get_foreign_visual ()
782
 *
783 784 785 786
 * Return value: An XVisualInfo suitable for creating a foreign stage. Use
 *   XFree() to free the returned value instead
 *
 * Deprecated: 1.2: Use clutter_x11_get_visual_info() instead
787 788 789 790 791 792
 *
 * Since: 0.4
 */
XVisualInfo *
clutter_x11_get_stage_visual (ClutterStage *stage)
{
793 794
  ClutterBackend *backend = clutter_get_default_backend ();
  ClutterBackendX11 *backend_x11;
795

796 797
  g_return_val_if_fail (CLUTTER_IS_BACKEND_X11 (backend), NULL);
  backend_x11 = CLUTTER_BACKEND_X11 (backend);
798

799
  return clutter_backend_x11_get_visual_info (backend_x11);
800 801
}

802 803 804 805
typedef struct {
  ClutterStageX11 *stage_x11;
  ClutterGeometry geom;
  Window xwindow;
806
  guint destroy_old_xwindow : 1;
807 808 809 810 811 812 813
} ForeignWindowData;

static void
set_foreign_window_callback (ClutterActor *actor,
                             void         *data)
{
  ForeignWindowData *fwd = data;
814 815
  ClutterBackend *backend = clutter_get_default_backend ();
  ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend);
816

817 818
  CLUTTER_NOTE (BACKEND, "Setting foreign window (0x%x)",
                (unsigned int) fwd->xwindow);
819

820 821 822 823
  if (fwd->destroy_old_xwindow && fwd->stage_x11->xwin != None)
    {
      CLUTTER_NOTE (BACKEND, "Destroying previous window (0x%x)",
                    (unsigned int) fwd->xwindow);
824
      XDestroyWindow (backend_x11->xdpy, fwd->stage_x11->xwin);
825
    }
826

827 828 829 830 831 832 833
  fwd->stage_x11->xwin = fwd->xwindow;
  fwd->stage_x11->is_foreign_xwin = TRUE;

  fwd->stage_x11->xwin_width = fwd->geom.width;
  fwd->stage_x11->xwin_height = fwd->geom.height;

  clutter_actor_set_geometry (actor, &fwd->geom);
834 835 836 837 838 839

  /* calling this with the stage unrealized will unset the stage
   * from the GL context; once the stage is realized the GL context
   * will be set again
   */
  clutter_stage_ensure_current (CLUTTER_STAGE (actor));
840 841
}

842 843 844 845 846 847 848 849 850 851 852 853 854 855 856
/**
 * clutter_x11_set_stage_foreign:
 * @stage: a #ClutterStage
 * @xwindow: an existing X Window id
 *
 * Target the #ClutterStage to use an existing external X Window
 *
 * Return value: %TRUE if foreign window is valid
 *
 * Since: 0.4
 */
gboolean
clutter_x11_set_stage_foreign (ClutterStage *stage,
                               Window        xwindow)
{
857 858
  ClutterBackend *backend = clutter_get_default_backend ();
  ClutterBackendX11 *backend_x11;
859
  ClutterStageX11 *stage_x11;
860
  ClutterStageWindow *impl;
861 862 863 864 865
  ClutterActor *actor;
  gint x, y;
  guint width, height, border, depth;
  Window root_return;
  Status status;
866
  ForeignWindowData fwd;
867 868 869 870
  XVisualInfo *xvisinfo;

  g_return_val_if_fail (CLUTTER_IS_BACKEND_X11 (backend), FALSE);
  backend_x11 = CLUTTER_BACKEND_X11 (backend);
871

872
  g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
873 874
  g_return_val_if_fail (xwindow != None, FALSE);

875 876
  actor = CLUTTER_ACTOR (stage);

877 878
  impl = _clutter_stage_get_window (stage);
  stage_x11 = CLUTTER_STAGE_X11 (impl);
879

880 881
  xvisinfo = clutter_backend_x11_get_visual_info (backend_x11);

882 883
  clutter_x11_trap_x_errors ();

884
  status = XGetGeometry (backend_x11->xdpy, xwindow,
885 886 887 888 889
                         &root_return,
                         &x, &y,
                         &width, &height,
                         &border,
                         &depth);
890

891 892 893
  if (clutter_x11_untrap_x_errors () ||
      !status ||
      width == 0 || height == 0 ||
894
      depth != xvisinfo->depth)
895
    {
896
      g_warning ("Unable to retrieve the new window geometry");
897 898 899
      return FALSE;
    }

900 901
  fwd.stage_x11 = stage_x11;
  fwd.xwindow = xwindow;
902 903 904 905 906 907 908

  /* destroy the old Window, if we have one and it's ours */
  if (stage_x11->xwin != None && !stage_x11->is_foreign_xwin)
    fwd.destroy_old_xwindow = TRUE;
  else
    fwd.destroy_old_xwindow = FALSE;

909 910 911 912
  fwd.geom.x = x;
  fwd.geom.y = y;
  fwd.geom.width = width;
  fwd.geom.height = height;
913

914 915 916
  _clutter_actor_rerealize (actor,
                            set_foreign_window_callback,
                            &fwd);
917

918
  clutter_stage_ensure_viewport (stage);
919

920 921
  return TRUE;
}