cleanup specfile for packaging
[profile/ivi/clutter.git] / clutter / x11 / clutter-stage-x11.c
1 /* Clutter.
2  * An OpenGL based 'interactive canvas' library.
3  * Authored By Matthew Allum  <mallum@openedhand.com>
4  * Copyright (C) 2006-2007 OpenedHand
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
18  *
19  *
20  */
21
22 #include "config.h"
23
24 #include <math.h>
25
26 #ifdef HAVE_UNISTD_H
27 #include <unistd.h>
28 #endif
29
30 #include <cogl/cogl.h>
31
32 #include "clutter-backend-x11.h"
33 #include "clutter-stage-x11.h"
34 #include "clutter-x11.h"
35
36 #include "clutter-actor-private.h"
37 #include "clutter-debug.h"
38 #include "clutter-device-manager-private.h"
39 #include "clutter-enum-types.h"
40 #include "clutter-event-translator.h"
41 #include "clutter-event-private.h"
42 #include "clutter-feature.h"
43 #include "clutter-main.h"
44 #include "clutter-paint-volume-private.h"
45 #include "clutter-private.h"
46 #include "clutter-stage-private.h"
47
48 #ifdef HAVE_XFIXES
49 #include <X11/extensions/Xfixes.h>
50 #endif
51
52 #define STAGE_X11_IS_MAPPED(s)  ((((ClutterStageX11 *) (s))->wm_state & STAGE_X11_WITHDRAWN) == 0)
53
54 static ClutterStageWindowIface *clutter_stage_window_parent_iface = NULL;
55
56 static void clutter_stage_window_iface_init     (ClutterStageWindowIface     *iface);
57 static void clutter_event_translator_iface_init (ClutterEventTranslatorIface *iface);
58
59 static ClutterStageCogl *clutter_x11_get_stage_window_from_window (Window win);
60
61 static GHashTable *clutter_stages_by_xid = NULL;
62
63 #define clutter_stage_x11_get_type      _clutter_stage_x11_get_type
64
65 G_DEFINE_TYPE_WITH_CODE (ClutterStageX11,
66                          clutter_stage_x11,
67                          CLUTTER_TYPE_STAGE_COGL,
68                          G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_STAGE_WINDOW,
69                                                 clutter_stage_window_iface_init)
70                          G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_EVENT_TRANSLATOR,
71                                                 clutter_event_translator_iface_init));
72
73 #define _NET_WM_STATE_REMOVE        0    /* remove/unset property */
74 #define _NET_WM_STATE_ADD           1    /* add/set property */
75 #define _NET_WM_STATE_TOGGLE        2    /* toggle property  */
76
77 static void
78 send_wmspec_change_state (ClutterBackendX11 *backend_x11,
79                           Window             window,
80                           Atom               state,
81                           gboolean           add)
82 {
83   XClientMessageEvent xclient;
84
85   CLUTTER_NOTE (BACKEND, "%s NET_WM state", add ? "adding" : "removing");
86
87   memset (&xclient, 0, sizeof (xclient));
88
89   xclient.type         = ClientMessage;
90   xclient.window       = window;
91   xclient.message_type = backend_x11->atom_NET_WM_STATE;
92   xclient.format       = 32;
93
94   xclient.data.l[0] = add ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
95   xclient.data.l[1] = state;
96   xclient.data.l[2] = 0;
97   xclient.data.l[3] = 0;
98   xclient.data.l[4] = 0;
99
100   XSendEvent (backend_x11->xdpy, 
101               DefaultRootWindow (backend_x11->xdpy),
102               False,
103               SubstructureRedirectMask | SubstructureNotifyMask,
104               (XEvent *)&xclient);
105 }
106
107 static void
108 update_state (ClutterStageX11   *stage_x11,
109               ClutterBackendX11 *backend_x11,
110               Atom              *state,
111               gboolean           add)
112 {
113   if (add)
114     {
115       /* FIXME: This wont work if we support more states */
116       XChangeProperty (backend_x11->xdpy,
117                        stage_x11->xwin,
118                        backend_x11->atom_NET_WM_STATE, XA_ATOM, 32,
119                        PropModeReplace,
120                        (unsigned char *) state, 1);
121     }
122   else
123     {
124        /* FIXME: This wont work if we support more states */
125        XDeleteProperty (backend_x11->xdpy,
126                         stage_x11->xwin,
127                         backend_x11->atom_NET_WM_STATE);
128     }
129 }
130
131 static void
132 clutter_stage_x11_fix_window_size (ClutterStageX11 *stage_x11,
133                                    gint             new_width,
134                                    gint             new_height)
135 {
136   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11);
137   ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend);
138
139   if (stage_x11->xwin != None && !stage_x11->is_foreign_xwin)
140     {
141       guint min_width, min_height;
142       XSizeHints *size_hints;
143       gboolean resize;
144
145       resize = clutter_stage_get_user_resizable (stage_cogl->wrapper);
146
147       size_hints = XAllocSizeHints();
148
149       clutter_stage_get_minimum_size (stage_cogl->wrapper,
150                                       &min_width,
151                                       &min_height);
152
153       if (new_width <= 0)
154         new_width = min_width;
155
156       if (new_height <= 0)
157         new_height = min_height;
158
159       size_hints->flags = 0;
160
161       /* If we are going fullscreen then we don't want any
162          restrictions on the window size */
163       if (!stage_x11->fullscreening)
164         {
165           if (resize)
166             {
167               size_hints->min_width = min_width;
168               size_hints->min_height = min_height;
169               size_hints->flags = PMinSize;
170             }
171           else
172             {
173               size_hints->min_width = new_width;
174               size_hints->min_height = new_height;
175               size_hints->max_width = new_width;
176               size_hints->max_height = new_height;
177               size_hints->flags = PMinSize | PMaxSize;
178             }
179         }
180
181       XSetWMNormalHints (backend_x11->xdpy, stage_x11->xwin, size_hints);
182
183       XFree(size_hints);
184     }
185 }
186
187 static void
188 clutter_stage_x11_set_wm_protocols (ClutterStageX11 *stage_x11)
189 {
190   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11);
191   ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend);
192   Atom protocols[2];
193   int n = 0;
194   
195   protocols[n++] = backend_x11->atom_WM_DELETE_WINDOW;
196   protocols[n++] = backend_x11->atom_NET_WM_PING;
197
198   XSetWMProtocols (backend_x11->xdpy, stage_x11->xwin, protocols, n);
199 }
200
201 static void
202 clutter_stage_x11_get_geometry (ClutterStageWindow    *stage_window,
203                                 cairo_rectangle_int_t *geometry)
204 {
205   ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
206   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11);
207   ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend);
208
209   geometry->x = geometry->y = 0;
210
211   /* If we're fullscreen, return the size of the display. */
212   if (_clutter_stage_is_fullscreen (stage_cogl->wrapper) &&
213       stage_x11->fullscreening)
214     {
215       geometry->width = DisplayWidth (backend_x11->xdpy, backend_x11->xscreen_num);
216       geometry->height = DisplayHeight (backend_x11->xdpy, backend_x11->xscreen_num);
217
218       return;
219     }
220
221   geometry->width = stage_x11->xwin_width;
222   geometry->height = stage_x11->xwin_height;
223 }
224
225 static void
226 clutter_stage_x11_resize (ClutterStageWindow *stage_window,
227                           gint                width,
228                           gint                height)
229 {
230   ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
231   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11);
232   ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend);
233
234   if (stage_x11->is_foreign_xwin)
235     {
236       /* If this is a foreign window we won't get a ConfigureNotify,
237        * so we need to manually set the size and queue a relayout on the
238        * stage here (as is normally done in response to ConfigureNotify).
239        */
240       stage_x11->xwin_width = width;
241       stage_x11->xwin_height = height;
242       clutter_actor_queue_relayout (CLUTTER_ACTOR (stage_cogl->wrapper));
243       return;
244     }
245
246   /* If we're going fullscreen, don't mess with the size */
247   if (stage_x11->fullscreening)
248     return;
249
250   if (width == 0 || height == 0)
251     {
252       /* Should not happen, if this turns up we need to debug it and
253        * determine the cleanest way to fix.
254        */
255       g_warning ("X11 stage not allowed to have 0 width or height");
256       width = 1;
257       height = 1;
258     }
259
260   CLUTTER_NOTE (BACKEND, "New size received: (%d, %d)", width, height);
261
262   if (stage_x11->xwin != None)
263     {
264       clutter_stage_x11_fix_window_size (stage_x11, width, height);
265
266       if (width != stage_x11->xwin_width ||
267           height != stage_x11->xwin_height)
268         {
269           CLUTTER_NOTE (BACKEND, "%s: XResizeWindow[%x] (%d, %d)",
270                         G_STRLOC,
271                         (unsigned int) stage_x11->xwin,
272                         width,
273                         height);
274
275           CLUTTER_SET_PRIVATE_FLAGS (stage_cogl->wrapper,
276                                      CLUTTER_IN_RESIZE);
277
278           /* XXX: in this case we can rely on a subsequent
279            * ConfigureNotify that will result in the stage
280            * being reallocated so we don't actively do anything
281            * to affect the stage allocation here. */
282           XResizeWindow (backend_x11->xdpy,
283                          stage_x11->xwin,
284                          width,
285                          height);
286         }
287     }
288 }
289
290 static inline void
291 set_wm_pid (ClutterStageX11 *stage_x11)
292 {
293   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11);
294   ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend);
295   long pid;
296
297   if (stage_x11->xwin == None || stage_x11->is_foreign_xwin)
298     return;
299
300   /* this will take care of WM_CLIENT_MACHINE and WM_LOCALE_NAME */
301   XSetWMProperties (backend_x11->xdpy, stage_x11->xwin,
302                     NULL,
303                     NULL,
304                     NULL, 0,
305                     NULL, NULL, NULL);
306
307   pid = getpid ();
308   XChangeProperty (backend_x11->xdpy,
309                    stage_x11->xwin,
310                    backend_x11->atom_NET_WM_PID, XA_CARDINAL, 32,
311                    PropModeReplace,
312                    (guchar *) &pid, 1);
313 }
314
315 static inline void
316 set_wm_title (ClutterStageX11 *stage_x11)
317 {
318   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11);
319   ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend);
320
321   if (stage_x11->xwin == None || stage_x11->is_foreign_xwin)
322     return;
323
324   if (stage_x11->title == NULL)
325     {
326       XDeleteProperty (backend_x11->xdpy,
327                        stage_x11->xwin, 
328                        backend_x11->atom_NET_WM_NAME);
329     }
330   else
331     {
332       XChangeProperty (backend_x11->xdpy,
333                        stage_x11->xwin, 
334                        backend_x11->atom_NET_WM_NAME,
335                        backend_x11->atom_UTF8_STRING,
336                        8, 
337                        PropModeReplace, 
338                        (unsigned char *) stage_x11->title,
339                        (int) strlen (stage_x11->title));
340     }
341 }
342
343 static inline void
344 set_cursor_visible (ClutterStageX11 *stage_x11)
345 {
346   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11);
347   ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend);
348
349   if (stage_x11->xwin == None)
350     return;
351
352   CLUTTER_NOTE (BACKEND, "setting cursor state ('%s') over stage window (%u)",
353                 stage_x11->is_cursor_visible ? "visible" : "invisible",
354                 (unsigned int) stage_x11->xwin);
355
356   if (stage_x11->is_cursor_visible)
357     {
358 #if 0 /* HAVE_XFIXES - seems buggy/unreliable */
359       XFixesShowCursor (backend_x11->xdpy, stage_x11->xwin);
360 #else
361       XUndefineCursor (backend_x11->xdpy, stage_x11->xwin);
362 #endif /* HAVE_XFIXES */
363     }
364   else
365     {
366 #if 0 /* HAVE_XFIXES - seems buggy/unreliable, check cursor in firefox 
367        *               loading page after hiding.  
368       */
369       XFixesHideCursor (backend_x11->xdpy, stage_x11->xwin);
370 #else
371       XColor col;
372       Pixmap pix;
373       Cursor curs;
374
375       pix = XCreatePixmap (backend_x11->xdpy, stage_x11->xwin, 1, 1, 1);
376       memset (&col, 0, sizeof (col));
377       curs = XCreatePixmapCursor (backend_x11->xdpy,
378                                   pix, pix,
379                                   &col, &col,
380                                   1, 1);
381       XFreePixmap (backend_x11->xdpy, pix);
382       XDefineCursor (backend_x11->xdpy, stage_x11->xwin, curs);
383 #endif /* HAVE_XFIXES */
384     }
385 }
386
387 static void
388 clutter_stage_x11_unrealize (ClutterStageWindow *stage_window)
389 {
390   ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
391
392   if (clutter_stages_by_xid != NULL)
393     {
394       CLUTTER_NOTE (BACKEND, "Removing X11 stage 0x%x [%p]",
395                     (unsigned int) stage_x11->xwin,
396                     stage_x11);
397
398       g_hash_table_remove (clutter_stages_by_xid,
399                            GINT_TO_POINTER (stage_x11->xwin));
400     }
401
402   clutter_stage_window_parent_iface->unrealize (stage_window);
403 }
404
405 void
406 _clutter_stage_x11_update_foreign_event_mask (CoglOnscreen *onscreen,
407                                               guint32 event_mask,
408                                               void *user_data)
409 {
410   ClutterStageX11 *stage_x11 = user_data;
411   ClutterStageCogl *stage_cogl = user_data;
412   ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend);
413   XSetWindowAttributes attrs;
414
415   attrs.event_mask = event_mask | CLUTTER_STAGE_X11_EVENT_MASK;
416
417   XChangeWindowAttributes (backend_x11->xdpy,
418                            stage_x11->xwin,
419                            CWEventMask,
420                            &attrs);
421 }
422
423 static void
424 clutter_stage_x11_set_fullscreen (ClutterStageWindow *stage_window,
425                                   gboolean            is_fullscreen)
426 {
427   ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
428   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11);
429   ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend);
430   ClutterStage *stage = stage_cogl->wrapper;
431   gboolean was_fullscreen;
432
433   if (stage == NULL || CLUTTER_ACTOR_IN_DESTRUCTION (stage))
434     return;
435
436   was_fullscreen = _clutter_stage_is_fullscreen (stage);
437   is_fullscreen = !!is_fullscreen;
438
439   if (was_fullscreen == is_fullscreen)
440     return;
441
442   CLUTTER_NOTE (BACKEND, "%ssetting fullscreen", is_fullscreen ? "" : "un");
443
444   if (is_fullscreen)
445     {
446 #if 0
447       int width, height;
448
449       /* FIXME: this will do the wrong thing for dual-headed
450          displays. This will return the size of the combined display
451          but Metacity (at least) will fullscreen to only one of the
452          displays. This will cause the actor to report the wrong size
453          until the ConfigureNotify for the correct size is received */
454       width  = DisplayWidth (backend_x11->xdpy, backend_x11->xscreen_num);
455       height = DisplayHeight (backend_x11->xdpy, backend_x11->xscreen_num);
456 #endif
457
458       /* Set the fullscreen hint so we can retain the old size of the window. */
459       stage_x11->fullscreening = TRUE;
460
461       if (stage_x11->xwin != None)
462         {
463           /* if the actor is not mapped we resize the stage window to match
464            * the size of the screen; this is useful for e.g. EGLX to avoid
465            * a resize when calling clutter_stage_fullscreen() before showing
466            * the stage
467            */
468           if (!STAGE_X11_IS_MAPPED (stage_x11))
469             {
470               CLUTTER_NOTE (BACKEND, "Fullscreening unmapped stage");
471
472               update_state (stage_x11, backend_x11,
473                             &backend_x11->atom_NET_WM_STATE_FULLSCREEN,
474                             TRUE);
475             }
476           else
477             {
478               CLUTTER_NOTE (BACKEND, "Fullscreening mapped stage");
479
480               /* We need to fix the window size so that it will remove
481                  the maximum and minimum window hints. Otherwise
482                  metacity will honour the restrictions and not
483                  fullscreen correctly. */
484               clutter_stage_x11_fix_window_size (stage_x11, -1, -1);
485
486               send_wmspec_change_state (backend_x11, stage_x11->xwin,
487                                         backend_x11->atom_NET_WM_STATE_FULLSCREEN,
488                                         TRUE);
489             }
490         }
491       else
492         stage_x11->fullscreen_on_realize = TRUE;
493     }
494   else
495     {
496       stage_x11->fullscreening = FALSE;
497
498       if (stage_x11->xwin != None)
499         {
500           if (!STAGE_X11_IS_MAPPED (stage_x11))
501             {
502               CLUTTER_NOTE (BACKEND, "Un-fullscreening unmapped stage");
503
504               update_state (stage_x11, backend_x11,
505                             &backend_x11->atom_NET_WM_STATE_FULLSCREEN,
506                             FALSE);
507             }
508           else
509             {
510               CLUTTER_NOTE (BACKEND, "Un-fullscreening mapped stage");
511
512               send_wmspec_change_state (backend_x11,
513                                         stage_x11->xwin,
514                                         backend_x11->atom_NET_WM_STATE_FULLSCREEN,
515                                         FALSE);
516
517               /* Fix the window size to restore the minimum/maximum
518                  restriction */
519               clutter_stage_x11_fix_window_size (stage_x11,
520                                                  stage_x11->xwin_width,
521                                                  stage_x11->xwin_height);
522             }
523         }
524       else
525         stage_x11->fullscreen_on_realize = FALSE;
526     }
527
528   /* XXX: Note we rely on the ConfigureNotify mechanism as the common
529    * mechanism to handle notifications of new X window sizes from the
530    * X server so we don't actively change the stage viewport here or
531    * queue a relayout etc. */
532 }
533
534 static gboolean
535 clutter_stage_x11_realize (ClutterStageWindow *stage_window)
536 {
537   ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
538   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
539   ClutterBackend *backend = CLUTTER_BACKEND (stage_cogl->backend);
540   ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend);
541   ClutterDeviceManager *device_manager;
542   int event_flags;
543   gfloat width, height;
544
545   clutter_actor_get_size (CLUTTER_ACTOR (stage_cogl->wrapper),
546                           &width, &height);
547
548   stage_cogl->onscreen = cogl_onscreen_new (backend->cogl_context,
549                                             width, height);
550
551   /* We just created a window of the size of the actor. No need to fix
552      the size of the stage, just update it. */
553   stage_x11->xwin_width = width;
554   stage_x11->xwin_height = height;
555
556   if (stage_x11->xwin != None)
557     {
558       cogl_x11_onscreen_set_foreign_window_xid (stage_cogl->onscreen,
559                                                 stage_x11->xwin,
560                                                 _clutter_stage_x11_update_foreign_event_mask,
561                                                 stage_x11);
562
563     }
564
565   /* Chain to the parent class now. ClutterStageCogl will call cogl_framebuffer_allocate,
566      which will create the X Window we need */
567
568   if (!(clutter_stage_window_parent_iface->realize (stage_window)))
569     return FALSE;
570
571   if (stage_x11->xwin == None)
572     stage_x11->xwin = cogl_x11_onscreen_get_window_xid (stage_cogl->onscreen);
573
574   if (clutter_stages_by_xid == NULL)
575     clutter_stages_by_xid = g_hash_table_new (NULL, NULL);
576
577   g_hash_table_insert (clutter_stages_by_xid,
578                        GINT_TO_POINTER (stage_x11->xwin),
579                        stage_x11);
580
581   set_wm_pid (stage_x11);
582   set_wm_title (stage_x11);
583   set_cursor_visible (stage_x11);
584
585
586   /* the masks for the events we want to select on a stage window;
587    * KeyPressMask and KeyReleaseMask are necessary even with XI1
588    * because key events are broken with that extension, and will
589    * be fixed by XI2
590    */
591   event_flags = CLUTTER_STAGE_X11_EVENT_MASK;
592
593   /* we unconditionally select input events even with event retrieval
594    * disabled because we need to guarantee that the Clutter internal
595    * state is maintained when calling clutter_x11_handle_event() without
596    * requiring applications or embedding toolkits to select events
597    * themselves. if we did that, we'd have to document the events to be
598    * selected, and also update applications and embedding toolkits each
599    * time we added a new mask, or a new class of events.
600    *
601    * see: http://bugzilla.clutter-project.org/show_bug.cgi?id=998
602    * for the rationale of why we did conditional selection. it is now
603    * clear that a compositor should clear out the input region, since
604    * it cannot assume a perfectly clean slate coming from us.
605    *
606    * see: http://bugzilla.clutter-project.org/show_bug.cgi?id=2228
607    * for an example of things that break if we do conditional event
608    * selection.
609    */
610   XSelectInput (backend_x11->xdpy, stage_x11->xwin, event_flags);
611
612   /* input events also depent on the actual device, so we need to
613    * use the device manager to let every device select them, using
614    * the event mask we passed to XSelectInput as the template
615    */
616   device_manager = clutter_device_manager_get_default ();
617   _clutter_device_manager_select_stage_events (device_manager,
618                                                stage_cogl->wrapper,
619                                                event_flags);
620
621   clutter_stage_x11_fix_window_size (stage_x11,
622                                      stage_x11->xwin_width,
623                                      stage_x11->xwin_height);
624   clutter_stage_x11_set_wm_protocols (stage_x11);
625
626   if (stage_x11->fullscreen_on_realize)
627     {
628       stage_x11->fullscreen_on_realize = FALSE;
629
630       clutter_stage_x11_set_fullscreen (stage_window, TRUE);
631     }
632
633   CLUTTER_NOTE (BACKEND, "Successfully realized stage");
634
635   return TRUE;
636 }
637
638 static void
639 clutter_stage_x11_set_cursor_visible (ClutterStageWindow *stage_window,
640                                       gboolean            cursor_visible)
641 {
642   ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
643
644   stage_x11->is_cursor_visible = !!cursor_visible;
645   set_cursor_visible (stage_x11);
646 }
647
648 static void
649 clutter_stage_x11_set_title (ClutterStageWindow *stage_window,
650                              const gchar        *title)
651 {
652   ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
653
654   g_free (stage_x11->title);
655   stage_x11->title = g_strdup (title);
656   set_wm_title (stage_x11);
657 }
658
659 static void
660 clutter_stage_x11_set_user_resizable (ClutterStageWindow *stage_window,
661                                       gboolean            is_resizable)
662 {
663   ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
664
665   clutter_stage_x11_fix_window_size (stage_x11,
666                                      stage_x11->xwin_width,
667                                      stage_x11->xwin_height);
668 }
669
670 static inline void
671 update_wm_hints (ClutterStageX11 *stage_x11)
672 {
673   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11);
674   ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend);
675   XWMHints wm_hints;
676
677   if (stage_x11->wm_state & STAGE_X11_WITHDRAWN)
678     return;
679
680   if (stage_x11->is_foreign_xwin)
681     return;
682
683   wm_hints.flags = StateHint | InputHint;
684   wm_hints.initial_state = NormalState;
685   wm_hints.input = stage_x11->accept_focus ? True : False;
686
687   XSetWMHints (backend_x11->xdpy, stage_x11->xwin, &wm_hints);
688 }
689
690 static void
691 clutter_stage_x11_set_accept_focus (ClutterStageWindow *stage_window,
692                                     gboolean            accept_focus)
693 {
694   ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
695
696   stage_x11->accept_focus = !!accept_focus;
697   update_wm_hints (stage_x11);
698 }
699
700 static void
701 set_stage_x11_state (ClutterStageX11      *stage_x11,
702                      ClutterStageX11State  unset_flags,
703                      ClutterStageX11State  set_flags)
704 {
705   ClutterStageX11State new_stage_state, old_stage_state;
706
707   old_stage_state = stage_x11->wm_state;
708
709   new_stage_state = old_stage_state;
710   new_stage_state |= set_flags;
711   new_stage_state &= ~unset_flags;
712
713   if (new_stage_state == old_stage_state)
714     return;
715
716   stage_x11->wm_state = new_stage_state;
717 }
718
719 static void
720 clutter_stage_x11_show (ClutterStageWindow *stage_window,
721                         gboolean            do_raise)
722 {
723   ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
724   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11);
725   ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend);
726
727   if (stage_x11->xwin != None)
728     {
729       if (do_raise && !stage_x11->is_foreign_xwin)
730         {
731           CLUTTER_NOTE (BACKEND, "Raising stage[%lu]",
732                         (unsigned long) stage_x11->xwin);
733           XRaiseWindow (backend_x11->xdpy, stage_x11->xwin);
734         }
735
736       if (!STAGE_X11_IS_MAPPED (stage_x11))
737         {
738           CLUTTER_NOTE (BACKEND, "Mapping stage[%lu]",
739                         (unsigned long) stage_x11->xwin);
740
741           set_stage_x11_state (stage_x11, STAGE_X11_WITHDRAWN, 0);
742
743           update_wm_hints (stage_x11);
744
745           if (stage_x11->fullscreening)
746             clutter_stage_x11_set_fullscreen (stage_window, TRUE);
747           else
748             clutter_stage_x11_set_fullscreen (stage_window, FALSE);
749         }
750
751       g_assert (STAGE_X11_IS_MAPPED (stage_x11));
752
753       clutter_actor_map (CLUTTER_ACTOR (stage_cogl->wrapper));
754
755       if (!stage_x11->is_foreign_xwin)
756         XMapWindow (backend_x11->xdpy, stage_x11->xwin);
757     }
758 }
759
760 static void
761 clutter_stage_x11_hide (ClutterStageWindow *stage_window)
762 {
763   ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
764   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11);
765   ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend);
766
767   if (stage_x11->xwin != None)
768     {
769       if (STAGE_X11_IS_MAPPED (stage_x11))
770         set_stage_x11_state (stage_x11, 0, STAGE_X11_WITHDRAWN);
771
772       g_assert (!STAGE_X11_IS_MAPPED (stage_x11));
773
774       clutter_actor_unmap (CLUTTER_ACTOR (stage_cogl->wrapper));
775
776       if (!stage_x11->is_foreign_xwin)
777         XWithdrawWindow (backend_x11->xdpy, stage_x11->xwin, 0);
778     }
779 }
780
781 static gboolean
782 clutter_stage_x11_can_clip_redraws (ClutterStageWindow *stage_window)
783 {
784   ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
785
786   /* while resizing a window, clipped redraws are disabled in order to
787    * avoid artefacts. see clutter-event-x11.c:event_translate for a more
788    * detailed explanation
789    */
790   return stage_x11->clipped_redraws_cool_off == 0;
791 }
792
793 static void
794 clutter_stage_x11_finalize (GObject *gobject)
795 {
796   ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (gobject);
797
798   g_free (stage_x11->title);
799
800   G_OBJECT_CLASS (clutter_stage_x11_parent_class)->finalize (gobject);
801 }
802
803 static void
804 clutter_stage_x11_dispose (GObject *gobject)
805 {
806   ClutterEventTranslator *translator = CLUTTER_EVENT_TRANSLATOR (gobject);
807   ClutterBackend *backend = CLUTTER_STAGE_COGL (gobject)->backend;
808
809   _clutter_backend_remove_event_translator (backend, translator);
810
811   G_OBJECT_CLASS (clutter_stage_x11_parent_class)->dispose (gobject);
812 }
813
814 static void
815 clutter_stage_x11_class_init (ClutterStageX11Class *klass)
816 {
817   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
818
819   gobject_class->finalize = clutter_stage_x11_finalize;
820   gobject_class->dispose = clutter_stage_x11_dispose;
821 }
822
823 static void
824 clutter_stage_x11_init (ClutterStageX11 *stage)
825 {
826   stage->xwin = None;
827   stage->xwin_width = 640;
828   stage->xwin_height = 480;
829
830   stage->wm_state = STAGE_X11_WITHDRAWN;
831
832   stage->is_foreign_xwin = FALSE;
833   stage->fullscreening = FALSE;
834   stage->is_cursor_visible = TRUE;
835   stage->accept_focus = TRUE;
836
837   stage->title = NULL;
838 }
839
840 static void
841 clutter_stage_window_iface_init (ClutterStageWindowIface *iface)
842 {
843   clutter_stage_window_parent_iface = g_type_interface_peek_parent (iface);
844
845   iface->set_title = clutter_stage_x11_set_title;
846   iface->set_fullscreen = clutter_stage_x11_set_fullscreen;
847   iface->set_cursor_visible = clutter_stage_x11_set_cursor_visible;
848   iface->set_user_resizable = clutter_stage_x11_set_user_resizable;
849   iface->set_accept_focus = clutter_stage_x11_set_accept_focus;
850   iface->show = clutter_stage_x11_show;
851   iface->hide = clutter_stage_x11_hide;
852   iface->resize = clutter_stage_x11_resize;
853   iface->get_geometry = clutter_stage_x11_get_geometry;
854   iface->realize = clutter_stage_x11_realize;
855   iface->unrealize = clutter_stage_x11_unrealize;
856   iface->can_clip_redraws = clutter_stage_x11_can_clip_redraws;
857 }
858
859 static inline void
860 set_user_time (ClutterBackendX11 *backend_x11,
861                ClutterStageX11   *stage_x11,
862                long               timestamp)
863 {
864   if (timestamp != CLUTTER_CURRENT_TIME)
865     {
866       XChangeProperty (backend_x11->xdpy,
867                        stage_x11->xwin,
868                        backend_x11->atom_NET_WM_USER_TIME,
869                        XA_CARDINAL, 32,
870                        PropModeReplace,
871                        (unsigned char *) &timestamp, 1);
872     }
873 }
874
875 static gboolean
876 handle_wm_protocols_event (ClutterBackendX11 *backend_x11,
877                            ClutterStageX11   *stage_x11,
878                            XEvent            *xevent)
879 {
880   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11);
881   Atom atom = (Atom) xevent->xclient.data.l[0];
882
883   if (atom == backend_x11->atom_WM_DELETE_WINDOW &&
884       xevent->xany.window == stage_x11->xwin)
885     {
886       /* the WM_DELETE_WINDOW is a request: we do not destroy
887        * the window right away, as it might contain vital data;
888        * we relay the event to the application and we let it
889        * handle the request
890        */
891       CLUTTER_NOTE (EVENT, "Delete stage %s[%p], win:0x%x",
892                     _clutter_actor_get_debug_name (CLUTTER_ACTOR (stage_cogl->wrapper)),
893                     stage_cogl->wrapper,
894                     (unsigned int) stage_x11->xwin);
895
896       set_user_time (backend_x11, stage_x11, xevent->xclient.data.l[1]);
897
898       return TRUE;
899     }
900   else if (atom == backend_x11->atom_NET_WM_PING &&
901            xevent->xany.window == stage_x11->xwin)
902     {
903       XClientMessageEvent xclient = xevent->xclient;
904
905       xclient.window = backend_x11->xwin_root;
906       XSendEvent (backend_x11->xdpy, xclient.window,
907                   False,
908                   SubstructureRedirectMask | SubstructureNotifyMask,
909                   (XEvent *) &xclient);
910       return FALSE;
911     }
912
913   /* do not send any of the WM_PROTOCOLS events to the queue */
914   return FALSE;
915 }
916
917 static gboolean
918 clipped_redraws_cool_off_cb (void *data)
919 {
920   ClutterStageX11 *stage_x11 = data;
921
922   stage_x11->clipped_redraws_cool_off = 0;
923
924   return G_SOURCE_REMOVE;
925 }
926
927 static ClutterTranslateReturn
928 clutter_stage_x11_translate_event (ClutterEventTranslator *translator,
929                                    gpointer                native,
930                                    ClutterEvent           *event)
931 {
932   ClutterStageX11 *stage_x11;
933   ClutterStageCogl *stage_cogl;
934   ClutterTranslateReturn res = CLUTTER_TRANSLATE_CONTINUE;
935   ClutterBackendX11 *backend_x11;
936   Window stage_xwindow;
937   XEvent *xevent = native;
938   ClutterStage *stage;
939
940   stage_cogl = clutter_x11_get_stage_window_from_window (xevent->xany.window);
941   if (stage_cogl == NULL)
942     return CLUTTER_TRANSLATE_CONTINUE;
943
944   stage = stage_cogl->wrapper;
945   stage_x11 = CLUTTER_STAGE_X11 (stage_cogl);
946   backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend);
947   stage_xwindow = stage_x11->xwin;
948
949   switch (xevent->type)
950     {
951     case ConfigureNotify:
952       if (!stage_x11->is_foreign_xwin)
953         {
954           gboolean size_changed = FALSE;
955
956           CLUTTER_NOTE (BACKEND, "ConfigureNotify[%x] (%d, %d)",
957                         (unsigned int) stage_x11->xwin,
958                         xevent->xconfigure.width,
959                         xevent->xconfigure.height);
960
961           /* When fullscreen, we'll keep the xwin_width/height
962              variables to track the old size of the window and we'll
963              assume all ConfigureNotifies constitute a size change */
964           if (_clutter_stage_is_fullscreen (stage))
965             size_changed = TRUE;
966           else if ((stage_x11->xwin_width != xevent->xconfigure.width) ||
967                    (stage_x11->xwin_height != xevent->xconfigure.height))
968             {
969               size_changed = TRUE;
970               stage_x11->xwin_width = xevent->xconfigure.width;
971               stage_x11->xwin_height = xevent->xconfigure.height;
972             }
973
974           clutter_actor_set_size (CLUTTER_ACTOR (stage),
975                                   xevent->xconfigure.width,
976                                   xevent->xconfigure.height);
977
978           CLUTTER_UNSET_PRIVATE_FLAGS (stage_cogl->wrapper, CLUTTER_IN_RESIZE);
979
980           if (size_changed)
981             {
982               /* XXX: This is a workaround for a race condition when
983                * resizing windows while there are in-flight
984                * glXCopySubBuffer blits happening.
985                *
986                * The problem stems from the fact that rectangles for the
987                * blits are described relative to the bottom left of the
988                * window and because we can't guarantee control over the X
989                * window gravity used when resizing so the gravity is
990                * typically NorthWest not SouthWest.
991                *
992                * This means if you grow a window vertically the server
993                * will make sure to place the old contents of the window
994                * at the top-left/north-west of your new larger window, but
995                * that may happen asynchronous to GLX preparing to do a
996                * blit specified relative to the bottom-left/south-west of
997                * the window (based on the old smaller window geometry).
998                *
999                * When the GLX issued blit finally happens relative to the
1000                * new bottom of your window, the destination will have
1001                * shifted relative to the top-left where all the pixels you
1002                * care about are so it will result in a nasty artefact
1003                * making resizing look very ugly!
1004                *
1005                * We can't currently fix this completely, in-part because
1006                * the window manager tends to trample any gravity we might
1007                * set.  This workaround instead simply disables blits for a
1008                * while if we are notified of any resizes happening so if
1009                * the user is resizing a window via the window manager then
1010                * they may see an artefact for one frame but then we will
1011                * fallback to redrawing the full stage until the cooling
1012                * off period is over.
1013                */
1014               if (stage_x11->clipped_redraws_cool_off)
1015                 g_source_remove (stage_x11->clipped_redraws_cool_off);
1016
1017               stage_x11->clipped_redraws_cool_off =
1018                 clutter_threads_add_timeout (1000,
1019                                              clipped_redraws_cool_off_cb,
1020                                              stage_x11);
1021
1022               /* Queue a relayout - we want glViewport to be called
1023                * with the correct values, and this is done in ClutterStage
1024                * via cogl_onscreen_clutter_backend_set_size ().
1025                *
1026                * We queue a relayout, because if this ConfigureNotify is
1027                * in response to a size we set in the application, the
1028                * set_size() call above is essentially a null-op.
1029                *
1030                * Make sure we do this only when the size has changed,
1031                * otherwise we end up relayouting on window moves.
1032                */
1033               clutter_actor_queue_relayout (CLUTTER_ACTOR (stage));
1034
1035               /* the resize process is complete, so we can ask the stage
1036                * to set up the GL viewport with the new size
1037                */
1038               clutter_stage_ensure_viewport (stage);
1039             }
1040         }
1041       break;
1042
1043     case PropertyNotify:
1044       if (xevent->xproperty.atom == backend_x11->atom_NET_WM_STATE &&
1045           xevent->xproperty.window == stage_xwindow &&
1046           !stage_x11->is_foreign_xwin)
1047         {
1048           Atom     type;
1049           gint     format;
1050           gulong   n_items, bytes_after;
1051           guchar  *data = NULL;
1052           gboolean fullscreen_set = FALSE;
1053
1054           clutter_x11_trap_x_errors ();
1055           XGetWindowProperty (backend_x11->xdpy, stage_xwindow,
1056                               backend_x11->atom_NET_WM_STATE,
1057                               0, G_MAXLONG,
1058                               False, XA_ATOM,
1059                               &type, &format, &n_items,
1060                               &bytes_after, &data);
1061           clutter_x11_untrap_x_errors ();
1062
1063           if (type != None && data != NULL)
1064             {
1065               gboolean is_fullscreen = FALSE;
1066               Atom *atoms = (Atom *) data;
1067               gulong i;
1068
1069               for (i = 0; i < n_items; i++)
1070                 {
1071                   if (atoms[i] == backend_x11->atom_NET_WM_STATE_FULLSCREEN)
1072                     fullscreen_set = TRUE;
1073                 }
1074
1075               is_fullscreen = _clutter_stage_is_fullscreen (stage_cogl->wrapper);
1076
1077               if (fullscreen_set != is_fullscreen)
1078                 {
1079                   if (fullscreen_set)
1080                     _clutter_stage_update_state (stage_cogl->wrapper,
1081                                                  0,
1082                                                  CLUTTER_STAGE_STATE_FULLSCREEN);
1083                   else
1084                     _clutter_stage_update_state (stage_cogl->wrapper,
1085                                                  CLUTTER_STAGE_STATE_FULLSCREEN,
1086                                                  0);
1087                 }
1088
1089               XFree (data);
1090             }
1091         }
1092       break;
1093
1094     case FocusIn:
1095       if (!_clutter_stage_is_activated (stage_cogl->wrapper))
1096         {
1097           _clutter_stage_update_state (stage_cogl->wrapper,
1098                                        0,
1099                                        CLUTTER_STAGE_STATE_ACTIVATED);
1100         }
1101       break;
1102
1103     case FocusOut:
1104       if (_clutter_stage_is_activated (stage_cogl->wrapper))
1105         {
1106           _clutter_stage_update_state (stage_cogl->wrapper,
1107                                        CLUTTER_STAGE_STATE_ACTIVATED,
1108                                        0);
1109         }
1110       break;
1111
1112     case Expose:
1113       {
1114         XExposeEvent *expose = (XExposeEvent *) xevent;
1115         cairo_rectangle_int_t clip;
1116
1117         CLUTTER_NOTE (EVENT,
1118                       "expose for stage: %s[%p], win:0x%x - "
1119                       "redrawing area (x: %d, y: %d, width: %d, height: %d)",
1120                       _clutter_actor_get_debug_name (CLUTTER_ACTOR (stage)),
1121                       stage,
1122                       (unsigned int) stage_xwindow,
1123                       expose->x,
1124                       expose->y,
1125                       expose->width,
1126                       expose->height);
1127
1128         clip.x = expose->x;
1129         clip.y = expose->y;
1130         clip.width = expose->width;
1131         clip.height = expose->height;
1132         clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stage), &clip);
1133       }
1134       break;
1135
1136     case DestroyNotify:
1137       CLUTTER_NOTE (EVENT,
1138                     "Destroy notification received for stage %s[%p], win:0x%x",
1139                     _clutter_actor_get_debug_name (CLUTTER_ACTOR (stage)),
1140                     stage,
1141                     (unsigned int) stage_xwindow);
1142       event->any.type = CLUTTER_DESTROY_NOTIFY;
1143       event->any.stage = stage;
1144       res = CLUTTER_TRANSLATE_QUEUE;
1145       break;
1146
1147     case ClientMessage:
1148       CLUTTER_NOTE (EVENT, "Client message for stage %s[%p], win:0x%x",
1149                     _clutter_actor_get_debug_name (CLUTTER_ACTOR (stage)),
1150                     stage,
1151                     (unsigned int) stage_xwindow);
1152       if (handle_wm_protocols_event (backend_x11, stage_x11, xevent))
1153         {
1154           event->any.type = CLUTTER_DELETE;
1155           event->any.stage = stage;
1156           res = CLUTTER_TRANSLATE_QUEUE;
1157         }
1158       break;
1159
1160     case MappingNotify:
1161       CLUTTER_NOTE (EVENT, "Refresh keyboard mapping");
1162       XRefreshKeyboardMapping (&xevent->xmapping);
1163       backend_x11->keymap_serial += 1;
1164       res = CLUTTER_TRANSLATE_REMOVE;
1165       break;
1166
1167     default:
1168       res = CLUTTER_TRANSLATE_CONTINUE;
1169       break;
1170     }
1171
1172   return res;
1173 }
1174
1175 static void
1176 clutter_event_translator_iface_init (ClutterEventTranslatorIface *iface)
1177 {
1178   iface->translate_event = clutter_stage_x11_translate_event;
1179 }
1180
1181 /**
1182  * clutter_x11_get_stage_window: (skip)
1183  * @stage: a #ClutterStage
1184  *
1185  * Gets the stages X Window.
1186  *
1187  * Return value: An XID for the stage window.
1188  *
1189  * Since: 0.4
1190  */
1191 Window
1192 clutter_x11_get_stage_window (ClutterStage *stage)
1193 {
1194   ClutterStageWindow *impl;
1195
1196   g_return_val_if_fail (CLUTTER_IS_STAGE (stage), None);
1197
1198   impl = _clutter_stage_get_window (stage);
1199   g_assert (CLUTTER_IS_STAGE_X11 (impl));
1200
1201   return CLUTTER_STAGE_X11 (impl)->xwin;
1202 }
1203
1204 static ClutterStageCogl *
1205 clutter_x11_get_stage_window_from_window (Window win)
1206 {
1207   if (clutter_stages_by_xid == NULL)
1208     return NULL;
1209
1210   return g_hash_table_lookup (clutter_stages_by_xid,
1211                               GINT_TO_POINTER (win));
1212 }
1213
1214 /**
1215  * clutter_x11_get_stage_from_window:
1216  * @win: an X Window ID
1217  *
1218  * Gets the stage for a particular X window.
1219  *
1220  * Return value: (transfer none): A #ClutterStage, or% NULL if a stage
1221  *   does not exist for the window
1222  *
1223  * Since: 0.8
1224  */
1225 ClutterStage *
1226 clutter_x11_get_stage_from_window (Window win)
1227 {
1228   ClutterStageCogl *stage_cogl;
1229
1230   stage_cogl = clutter_x11_get_stage_window_from_window (win);
1231
1232   if (stage_cogl != NULL)
1233     return stage_cogl->wrapper;
1234
1235   return NULL;
1236 }
1237
1238 /**
1239  * clutter_x11_get_stage_visual: (skip)
1240  * @stage: a #ClutterStage
1241  *
1242  * Returns an XVisualInfo suitable for creating a foreign window for the given
1243  * stage. NOTE: It doesn't do as the name may suggest, which is return the
1244  * XVisualInfo that was used to create an existing window for the given stage.
1245  *
1246  * XXX: It might be best to deprecate this function and replace with something
1247  * along the lines of clutter_backend_x11_get_foreign_visual () or perhaps
1248  * clutter_stage_x11_get_foreign_visual ()
1249  *
1250  * Return value: (transfer full): An XVisualInfo suitable for creating a
1251  *   foreign stage. Use XFree() to free the returned value instead
1252  *
1253  * Deprecated: 1.2: Use clutter_x11_get_visual_info() instead
1254  *
1255  * Since: 0.4
1256  */
1257 XVisualInfo *
1258 clutter_x11_get_stage_visual (ClutterStage *stage)
1259 {
1260   ClutterBackend *backend = clutter_get_default_backend ();
1261   ClutterBackendX11 *backend_x11;
1262
1263   g_return_val_if_fail (CLUTTER_IS_BACKEND_X11 (backend), NULL);
1264   backend_x11 = CLUTTER_BACKEND_X11 (backend);
1265
1266   return _clutter_backend_x11_get_visual_info (backend_x11);
1267 }
1268
1269 typedef struct {
1270   ClutterStageX11 *stage_x11;
1271   cairo_rectangle_int_t geom;
1272   Window xwindow;
1273   guint destroy_old_xwindow : 1;
1274 } ForeignWindowData;
1275
1276 static void
1277 set_foreign_window_callback (ClutterActor *actor,
1278                              void         *data)
1279 {
1280   ForeignWindowData *fwd = data;
1281   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (fwd->stage_x11);
1282   ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend);
1283
1284   CLUTTER_NOTE (BACKEND, "Setting foreign window (0x%x)",
1285                 (unsigned int) fwd->xwindow);
1286
1287   if (fwd->destroy_old_xwindow && fwd->stage_x11->xwin != None)
1288     {
1289       CLUTTER_NOTE (BACKEND, "Destroying previous window (0x%x)",
1290                     (unsigned int) fwd->xwindow);
1291       XDestroyWindow (backend_x11->xdpy, fwd->stage_x11->xwin);
1292     }
1293
1294   fwd->stage_x11->xwin = fwd->xwindow;
1295   fwd->stage_x11->is_foreign_xwin = TRUE;
1296
1297   fwd->stage_x11->xwin_width = fwd->geom.width;
1298   fwd->stage_x11->xwin_height = fwd->geom.height;
1299
1300   clutter_actor_set_size (actor, fwd->geom.width, fwd->geom.height);
1301
1302   if (clutter_stages_by_xid == NULL)
1303     clutter_stages_by_xid = g_hash_table_new (NULL, NULL);
1304
1305   g_hash_table_insert (clutter_stages_by_xid,
1306                        GINT_TO_POINTER (fwd->stage_x11->xwin),
1307                        fwd->stage_x11);
1308
1309   /* calling this with the stage unrealized will unset the stage
1310    * from the GL context; once the stage is realized the GL context
1311    * will be set again
1312    */
1313   clutter_stage_ensure_current (CLUTTER_STAGE (actor));
1314 }
1315
1316 /**
1317  * clutter_x11_set_stage_foreign:
1318  * @stage: a #ClutterStage
1319  * @xwindow: an existing X Window id
1320  *
1321  * Target the #ClutterStage to use an existing external X Window
1322  *
1323  * Return value: %TRUE if foreign window is valid
1324  *
1325  * Since: 0.4
1326  */
1327 gboolean
1328 clutter_x11_set_stage_foreign (ClutterStage *stage,
1329                                Window        xwindow)
1330 {
1331   ClutterBackendX11 *backend_x11;
1332   ClutterStageX11 *stage_x11;
1333   ClutterStageCogl *stage_cogl;
1334   ClutterStageWindow *impl;
1335   ClutterActor *actor;
1336   gint x, y;
1337   guint width, height, border, depth;
1338   Window root_return;
1339   Status status;
1340   ForeignWindowData fwd;
1341   XVisualInfo *xvisinfo;
1342
1343   g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
1344   g_return_val_if_fail (!CLUTTER_ACTOR_IN_DESTRUCTION (stage), FALSE);
1345   g_return_val_if_fail (xwindow != None, FALSE);
1346
1347   impl = _clutter_stage_get_window (stage);
1348   stage_x11 = CLUTTER_STAGE_X11 (impl);
1349   stage_cogl = CLUTTER_STAGE_COGL (impl);
1350   backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend);
1351
1352   xvisinfo = _clutter_backend_x11_get_visual_info (backend_x11);
1353   g_return_val_if_fail (xvisinfo != NULL, FALSE);
1354
1355   clutter_x11_trap_x_errors ();
1356
1357   status = XGetGeometry (backend_x11->xdpy, xwindow,
1358                          &root_return,
1359                          &x, &y,
1360                          &width, &height,
1361                          &border,
1362                          &depth);
1363
1364   if (clutter_x11_untrap_x_errors () || !status)
1365     {
1366       g_critical ("Unable to retrieve the geometry of the foreign window: "
1367                   "XGetGeometry() failed (status code: %d)", status);
1368       return FALSE;
1369     }
1370
1371   if (width == 0 || height == 0)
1372     {
1373       g_warning ("The size of the foreign window is 0x0");
1374       return FALSE;
1375     }
1376
1377   if (depth != xvisinfo->depth)
1378     {
1379       g_warning ("The depth of the visual of the foreign window is %d, but "
1380                  "Clutter has been initialized to require a visual depth "
1381                  "of %d",
1382                  depth,
1383                  xvisinfo->depth);
1384       return FALSE;
1385     }
1386
1387   fwd.stage_x11 = stage_x11;
1388   fwd.xwindow = xwindow;
1389
1390   /* destroy the old Window, if we have one and it's ours */
1391   if (stage_x11->xwin != None && !stage_x11->is_foreign_xwin)
1392     fwd.destroy_old_xwindow = TRUE;
1393   else
1394     fwd.destroy_old_xwindow = FALSE;
1395
1396   fwd.geom.x = x;
1397   fwd.geom.y = y;
1398   fwd.geom.width = width;
1399   fwd.geom.height = height;
1400
1401   actor = CLUTTER_ACTOR (stage);
1402
1403   _clutter_actor_rerealize (actor,
1404                             set_foreign_window_callback,
1405                             &fwd);
1406
1407   /* Queue a relayout - so the stage will be allocated the new
1408    * window size.
1409    *
1410    * Note also that when the stage gets allocated the new
1411    * window size that will result in the stage's
1412    * priv->viewport being changed, which will in turn result
1413    * in the Cogl viewport changing when _clutter_do_redraw
1414    * calls _clutter_stage_maybe_setup_viewport().
1415    */
1416   clutter_actor_queue_relayout (actor);
1417
1418   return TRUE;
1419 }
1420
1421 void
1422 _clutter_stage_x11_set_user_time (ClutterStageX11 *stage_x11,
1423                                   guint32          user_time)
1424 {
1425   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11);
1426   ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend);
1427
1428   set_user_time (backend_x11, stage_x11, user_time);
1429 }
1430
1431 gboolean
1432 _clutter_stage_x11_get_root_coords (ClutterStageX11 *stage_x11,
1433                                     gint            *root_x,
1434                                     gint            *root_y)
1435 {
1436   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11);
1437   ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend);
1438   gint return_val;
1439   Window child;
1440   gint tx, ty;
1441
1442   return_val = XTranslateCoordinates (backend_x11->xdpy,
1443                                       stage_x11->xwin,
1444                                       backend_x11->xwin_root,
1445                                       0, 0, &tx, &ty,
1446                                       &child);
1447
1448   if (root_x)
1449     *root_x = tx;
1450
1451   if (root_y)
1452     *root_y = ty;
1453
1454   return (return_val == 0);
1455 }