update to 1.10.4
[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 void
535 _clutter_stage_x11_events_device_changed (ClutterStageX11 *stage_x11,
536                                           ClutterInputDevice *device,
537                                           ClutterDeviceManager *device_manager)
538 {
539   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11);
540   int event_flags = 0;
541
542   if (clutter_input_device_get_device_mode (device) == CLUTTER_INPUT_MODE_FLOATING)
543     event_flags = CLUTTER_STAGE_X11_EVENT_MASK;
544
545   _clutter_device_manager_select_stage_events (device_manager,
546                                                stage_cogl->wrapper,
547                                                event_flags);
548 }
549
550 static void
551 stage_events_device_added (ClutterDeviceManager *device_manager,
552                            ClutterInputDevice *device,
553                            gpointer user_data)
554 {
555   ClutterStageWindow *stage_window = user_data;
556   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
557   int event_flags = CLUTTER_STAGE_X11_EVENT_MASK;
558
559   if (clutter_input_device_get_device_mode (device) == CLUTTER_INPUT_MODE_FLOATING)
560     _clutter_device_manager_select_stage_events (device_manager,
561                                                  stage_cogl->wrapper,
562                                                  event_flags);
563 }
564
565 static gboolean
566 clutter_stage_x11_realize (ClutterStageWindow *stage_window)
567 {
568   ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
569   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
570   ClutterBackend *backend = CLUTTER_BACKEND (stage_cogl->backend);
571   ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend);
572   ClutterDeviceManager *device_manager;
573   int event_flags;
574   gfloat width, height;
575
576   clutter_actor_get_size (CLUTTER_ACTOR (stage_cogl->wrapper),
577                           &width, &height);
578
579   stage_cogl->onscreen = cogl_onscreen_new (backend->cogl_context,
580                                             width, height);
581
582   /* We just created a window of the size of the actor. No need to fix
583      the size of the stage, just update it. */
584   stage_x11->xwin_width = width;
585   stage_x11->xwin_height = height;
586
587   if (stage_x11->xwin != None)
588     {
589       cogl_x11_onscreen_set_foreign_window_xid (stage_cogl->onscreen,
590                                                 stage_x11->xwin,
591                                                 _clutter_stage_x11_update_foreign_event_mask,
592                                                 stage_x11);
593
594     }
595
596   /* Chain to the parent class now. ClutterStageCogl will call cogl_framebuffer_allocate,
597      which will create the X Window we need */
598
599   if (!(clutter_stage_window_parent_iface->realize (stage_window)))
600     return FALSE;
601
602   if (stage_x11->xwin == None)
603     stage_x11->xwin = cogl_x11_onscreen_get_window_xid (stage_cogl->onscreen);
604
605   if (clutter_stages_by_xid == NULL)
606     clutter_stages_by_xid = g_hash_table_new (NULL, NULL);
607
608   g_hash_table_insert (clutter_stages_by_xid,
609                        GINT_TO_POINTER (stage_x11->xwin),
610                        stage_x11);
611
612   set_wm_pid (stage_x11);
613   set_wm_title (stage_x11);
614   set_cursor_visible (stage_x11);
615
616
617   /* the masks for the events we want to select on a stage window;
618    * KeyPressMask and KeyReleaseMask are necessary even with XI1
619    * because key events are broken with that extension, and will
620    * be fixed by XI2
621    */
622   event_flags = CLUTTER_STAGE_X11_EVENT_MASK;
623
624   /* we unconditionally select input events even with event retrieval
625    * disabled because we need to guarantee that the Clutter internal
626    * state is maintained when calling clutter_x11_handle_event() without
627    * requiring applications or embedding toolkits to select events
628    * themselves. if we did that, we'd have to document the events to be
629    * selected, and also update applications and embedding toolkits each
630    * time we added a new mask, or a new class of events.
631    *
632    * see: http://bugzilla.clutter-project.org/show_bug.cgi?id=998
633    * for the rationale of why we did conditional selection. it is now
634    * clear that a compositor should clear out the input region, since
635    * it cannot assume a perfectly clean slate coming from us.
636    *
637    * see: http://bugzilla.clutter-project.org/show_bug.cgi?id=2228
638    * for an example of things that break if we do conditional event
639    * selection.
640    */
641   XSelectInput (backend_x11->xdpy, stage_x11->xwin, event_flags);
642
643   /* input events also depent on the actual device, so we need to
644    * use the device manager to let every device select them, using
645    * the event mask we passed to XSelectInput as the template
646    */
647   device_manager = clutter_device_manager_get_default ();
648   _clutter_device_manager_select_stage_events (device_manager,
649                                                stage_cogl->wrapper,
650                                                event_flags);
651
652   g_signal_connect (device_manager, "device-added",
653                     G_CALLBACK (stage_events_device_added), stage_window);
654
655   clutter_stage_x11_fix_window_size (stage_x11,
656                                      stage_x11->xwin_width,
657                                      stage_x11->xwin_height);
658   clutter_stage_x11_set_wm_protocols (stage_x11);
659
660   if (stage_x11->fullscreen_on_realize)
661     {
662       stage_x11->fullscreen_on_realize = FALSE;
663
664       clutter_stage_x11_set_fullscreen (stage_window, TRUE);
665     }
666
667   CLUTTER_NOTE (BACKEND, "Successfully realized stage");
668
669   return TRUE;
670 }
671
672 static void
673 clutter_stage_x11_set_cursor_visible (ClutterStageWindow *stage_window,
674                                       gboolean            cursor_visible)
675 {
676   ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
677
678   stage_x11->is_cursor_visible = !!cursor_visible;
679   set_cursor_visible (stage_x11);
680 }
681
682 static void
683 clutter_stage_x11_set_title (ClutterStageWindow *stage_window,
684                              const gchar        *title)
685 {
686   ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
687
688   g_free (stage_x11->title);
689   stage_x11->title = g_strdup (title);
690   set_wm_title (stage_x11);
691 }
692
693 static void
694 clutter_stage_x11_set_user_resizable (ClutterStageWindow *stage_window,
695                                       gboolean            is_resizable)
696 {
697   ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
698
699   clutter_stage_x11_fix_window_size (stage_x11,
700                                      stage_x11->xwin_width,
701                                      stage_x11->xwin_height);
702 }
703
704 static inline void
705 update_wm_hints (ClutterStageX11 *stage_x11)
706 {
707   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11);
708   ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend);
709   XWMHints wm_hints;
710
711   if (stage_x11->wm_state & STAGE_X11_WITHDRAWN)
712     return;
713
714   if (stage_x11->is_foreign_xwin)
715     return;
716
717   wm_hints.flags = StateHint | InputHint;
718   wm_hints.initial_state = NormalState;
719   wm_hints.input = stage_x11->accept_focus ? True : False;
720
721   XSetWMHints (backend_x11->xdpy, stage_x11->xwin, &wm_hints);
722 }
723
724 static void
725 clutter_stage_x11_set_accept_focus (ClutterStageWindow *stage_window,
726                                     gboolean            accept_focus)
727 {
728   ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
729
730   stage_x11->accept_focus = !!accept_focus;
731   update_wm_hints (stage_x11);
732 }
733
734 static void
735 set_stage_x11_state (ClutterStageX11      *stage_x11,
736                      ClutterStageX11State  unset_flags,
737                      ClutterStageX11State  set_flags)
738 {
739   ClutterStageX11State new_stage_state, old_stage_state;
740
741   old_stage_state = stage_x11->wm_state;
742
743   new_stage_state = old_stage_state;
744   new_stage_state |= set_flags;
745   new_stage_state &= ~unset_flags;
746
747   if (new_stage_state == old_stage_state)
748     return;
749
750   stage_x11->wm_state = new_stage_state;
751 }
752
753 static void
754 clutter_stage_x11_show (ClutterStageWindow *stage_window,
755                         gboolean            do_raise)
756 {
757   ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
758   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11);
759   ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend);
760
761   if (stage_x11->xwin != None)
762     {
763       if (do_raise && !stage_x11->is_foreign_xwin)
764         {
765           CLUTTER_NOTE (BACKEND, "Raising stage[%lu]",
766                         (unsigned long) stage_x11->xwin);
767           XRaiseWindow (backend_x11->xdpy, stage_x11->xwin);
768         }
769
770       if (!STAGE_X11_IS_MAPPED (stage_x11))
771         {
772           CLUTTER_NOTE (BACKEND, "Mapping stage[%lu]",
773                         (unsigned long) stage_x11->xwin);
774
775           set_stage_x11_state (stage_x11, STAGE_X11_WITHDRAWN, 0);
776
777           update_wm_hints (stage_x11);
778
779           if (stage_x11->fullscreening)
780             clutter_stage_x11_set_fullscreen (stage_window, TRUE);
781           else
782             clutter_stage_x11_set_fullscreen (stage_window, FALSE);
783         }
784
785       g_assert (STAGE_X11_IS_MAPPED (stage_x11));
786
787       clutter_actor_map (CLUTTER_ACTOR (stage_cogl->wrapper));
788
789       if (!stage_x11->is_foreign_xwin)
790         XMapWindow (backend_x11->xdpy, stage_x11->xwin);
791     }
792 }
793
794 static void
795 clutter_stage_x11_hide (ClutterStageWindow *stage_window)
796 {
797   ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
798   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11);
799   ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend);
800
801   if (stage_x11->xwin != None)
802     {
803       if (STAGE_X11_IS_MAPPED (stage_x11))
804         set_stage_x11_state (stage_x11, 0, STAGE_X11_WITHDRAWN);
805
806       g_assert (!STAGE_X11_IS_MAPPED (stage_x11));
807
808       clutter_actor_unmap (CLUTTER_ACTOR (stage_cogl->wrapper));
809
810       if (!stage_x11->is_foreign_xwin)
811         XWithdrawWindow (backend_x11->xdpy, stage_x11->xwin, 0);
812     }
813 }
814
815 static gboolean
816 clutter_stage_x11_can_clip_redraws (ClutterStageWindow *stage_window)
817 {
818   ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
819
820   /* while resizing a window, clipped redraws are disabled in order to
821    * avoid artefacts. see clutter-event-x11.c:event_translate for a more
822    * detailed explanation
823    */
824   return stage_x11->clipped_redraws_cool_off == 0;
825 }
826
827 static void
828 clutter_stage_x11_finalize (GObject *gobject)
829 {
830   ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (gobject);
831
832   g_free (stage_x11->title);
833
834   G_OBJECT_CLASS (clutter_stage_x11_parent_class)->finalize (gobject);
835 }
836
837 static void
838 clutter_stage_x11_dispose (GObject *gobject)
839 {
840   ClutterEventTranslator *translator = CLUTTER_EVENT_TRANSLATOR (gobject);
841   ClutterBackend *backend = CLUTTER_STAGE_COGL (gobject)->backend;
842
843   _clutter_backend_remove_event_translator (backend, translator);
844
845   G_OBJECT_CLASS (clutter_stage_x11_parent_class)->dispose (gobject);
846 }
847
848 static void
849 clutter_stage_x11_class_init (ClutterStageX11Class *klass)
850 {
851   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
852
853   gobject_class->finalize = clutter_stage_x11_finalize;
854   gobject_class->dispose = clutter_stage_x11_dispose;
855 }
856
857 static void
858 clutter_stage_x11_init (ClutterStageX11 *stage)
859 {
860   stage->xwin = None;
861   stage->xwin_width = 640;
862   stage->xwin_height = 480;
863
864   stage->wm_state = STAGE_X11_WITHDRAWN;
865
866   stage->is_foreign_xwin = FALSE;
867   stage->fullscreening = FALSE;
868   stage->is_cursor_visible = TRUE;
869   stage->accept_focus = TRUE;
870
871   stage->title = NULL;
872 }
873
874 static void
875 clutter_stage_window_iface_init (ClutterStageWindowIface *iface)
876 {
877   clutter_stage_window_parent_iface = g_type_interface_peek_parent (iface);
878
879   iface->set_title = clutter_stage_x11_set_title;
880   iface->set_fullscreen = clutter_stage_x11_set_fullscreen;
881   iface->set_cursor_visible = clutter_stage_x11_set_cursor_visible;
882   iface->set_user_resizable = clutter_stage_x11_set_user_resizable;
883   iface->set_accept_focus = clutter_stage_x11_set_accept_focus;
884   iface->show = clutter_stage_x11_show;
885   iface->hide = clutter_stage_x11_hide;
886   iface->resize = clutter_stage_x11_resize;
887   iface->get_geometry = clutter_stage_x11_get_geometry;
888   iface->realize = clutter_stage_x11_realize;
889   iface->unrealize = clutter_stage_x11_unrealize;
890   iface->can_clip_redraws = clutter_stage_x11_can_clip_redraws;
891 }
892
893 static inline void
894 set_user_time (ClutterBackendX11 *backend_x11,
895                ClutterStageX11   *stage_x11,
896                long               timestamp)
897 {
898   if (timestamp != CLUTTER_CURRENT_TIME)
899     {
900       XChangeProperty (backend_x11->xdpy,
901                        stage_x11->xwin,
902                        backend_x11->atom_NET_WM_USER_TIME,
903                        XA_CARDINAL, 32,
904                        PropModeReplace,
905                        (unsigned char *) &timestamp, 1);
906     }
907 }
908
909 static gboolean
910 handle_wm_protocols_event (ClutterBackendX11 *backend_x11,
911                            ClutterStageX11   *stage_x11,
912                            XEvent            *xevent)
913 {
914   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11);
915   Atom atom = (Atom) xevent->xclient.data.l[0];
916
917   if (atom == backend_x11->atom_WM_DELETE_WINDOW &&
918       xevent->xany.window == stage_x11->xwin)
919     {
920       /* the WM_DELETE_WINDOW is a request: we do not destroy
921        * the window right away, as it might contain vital data;
922        * we relay the event to the application and we let it
923        * handle the request
924        */
925       CLUTTER_NOTE (EVENT, "Delete stage %s[%p], win:0x%x",
926                     _clutter_actor_get_debug_name (CLUTTER_ACTOR (stage_cogl->wrapper)),
927                     stage_cogl->wrapper,
928                     (unsigned int) stage_x11->xwin);
929
930       set_user_time (backend_x11, stage_x11, xevent->xclient.data.l[1]);
931
932       return TRUE;
933     }
934   else if (atom == backend_x11->atom_NET_WM_PING &&
935            xevent->xany.window == stage_x11->xwin)
936     {
937       XClientMessageEvent xclient = xevent->xclient;
938
939       xclient.window = backend_x11->xwin_root;
940       XSendEvent (backend_x11->xdpy, xclient.window,
941                   False,
942                   SubstructureRedirectMask | SubstructureNotifyMask,
943                   (XEvent *) &xclient);
944       return FALSE;
945     }
946
947   /* do not send any of the WM_PROTOCOLS events to the queue */
948   return FALSE;
949 }
950
951 static gboolean
952 clipped_redraws_cool_off_cb (void *data)
953 {
954   ClutterStageX11 *stage_x11 = data;
955
956   stage_x11->clipped_redraws_cool_off = 0;
957
958   return G_SOURCE_REMOVE;
959 }
960
961 static ClutterTranslateReturn
962 clutter_stage_x11_translate_event (ClutterEventTranslator *translator,
963                                    gpointer                native,
964                                    ClutterEvent           *event)
965 {
966   ClutterStageX11 *stage_x11;
967   ClutterStageCogl *stage_cogl;
968   ClutterTranslateReturn res = CLUTTER_TRANSLATE_CONTINUE;
969   ClutterBackendX11 *backend_x11;
970   Window stage_xwindow;
971   XEvent *xevent = native;
972   ClutterStage *stage;
973
974   stage_cogl = clutter_x11_get_stage_window_from_window (xevent->xany.window);
975   if (stage_cogl == NULL)
976     return CLUTTER_TRANSLATE_CONTINUE;
977
978   stage = stage_cogl->wrapper;
979   stage_x11 = CLUTTER_STAGE_X11 (stage_cogl);
980   backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend);
981   stage_xwindow = stage_x11->xwin;
982
983   switch (xevent->type)
984     {
985     case ConfigureNotify:
986       if (!stage_x11->is_foreign_xwin)
987         {
988           gboolean size_changed = FALSE;
989
990           CLUTTER_NOTE (BACKEND, "ConfigureNotify[%x] (%d, %d)",
991                         (unsigned int) stage_x11->xwin,
992                         xevent->xconfigure.width,
993                         xevent->xconfigure.height);
994
995           /* When fullscreen, we'll keep the xwin_width/height
996              variables to track the old size of the window and we'll
997              assume all ConfigureNotifies constitute a size change */
998           if (_clutter_stage_is_fullscreen (stage))
999             size_changed = TRUE;
1000           else if ((stage_x11->xwin_width != xevent->xconfigure.width) ||
1001                    (stage_x11->xwin_height != xevent->xconfigure.height))
1002             {
1003               size_changed = TRUE;
1004               stage_x11->xwin_width = xevent->xconfigure.width;
1005               stage_x11->xwin_height = xevent->xconfigure.height;
1006             }
1007
1008           clutter_actor_set_size (CLUTTER_ACTOR (stage),
1009                                   xevent->xconfigure.width,
1010                                   xevent->xconfigure.height);
1011
1012           CLUTTER_UNSET_PRIVATE_FLAGS (stage_cogl->wrapper, CLUTTER_IN_RESIZE);
1013
1014           if (size_changed)
1015             {
1016               /* XXX: This is a workaround for a race condition when
1017                * resizing windows while there are in-flight
1018                * glXCopySubBuffer blits happening.
1019                *
1020                * The problem stems from the fact that rectangles for the
1021                * blits are described relative to the bottom left of the
1022                * window and because we can't guarantee control over the X
1023                * window gravity used when resizing so the gravity is
1024                * typically NorthWest not SouthWest.
1025                *
1026                * This means if you grow a window vertically the server
1027                * will make sure to place the old contents of the window
1028                * at the top-left/north-west of your new larger window, but
1029                * that may happen asynchronous to GLX preparing to do a
1030                * blit specified relative to the bottom-left/south-west of
1031                * the window (based on the old smaller window geometry).
1032                *
1033                * When the GLX issued blit finally happens relative to the
1034                * new bottom of your window, the destination will have
1035                * shifted relative to the top-left where all the pixels you
1036                * care about are so it will result in a nasty artefact
1037                * making resizing look very ugly!
1038                *
1039                * We can't currently fix this completely, in-part because
1040                * the window manager tends to trample any gravity we might
1041                * set.  This workaround instead simply disables blits for a
1042                * while if we are notified of any resizes happening so if
1043                * the user is resizing a window via the window manager then
1044                * they may see an artefact for one frame but then we will
1045                * fallback to redrawing the full stage until the cooling
1046                * off period is over.
1047                */
1048               if (stage_x11->clipped_redraws_cool_off)
1049                 g_source_remove (stage_x11->clipped_redraws_cool_off);
1050
1051               stage_x11->clipped_redraws_cool_off =
1052                 clutter_threads_add_timeout (1000,
1053                                              clipped_redraws_cool_off_cb,
1054                                              stage_x11);
1055
1056               /* Queue a relayout - we want glViewport to be called
1057                * with the correct values, and this is done in ClutterStage
1058                * via cogl_onscreen_clutter_backend_set_size ().
1059                *
1060                * We queue a relayout, because if this ConfigureNotify is
1061                * in response to a size we set in the application, the
1062                * set_size() call above is essentially a null-op.
1063                *
1064                * Make sure we do this only when the size has changed,
1065                * otherwise we end up relayouting on window moves.
1066                */
1067               clutter_actor_queue_relayout (CLUTTER_ACTOR (stage));
1068
1069               /* the resize process is complete, so we can ask the stage
1070                * to set up the GL viewport with the new size
1071                */
1072               clutter_stage_ensure_viewport (stage);
1073             }
1074         }
1075       break;
1076
1077     case PropertyNotify:
1078       if (xevent->xproperty.atom == backend_x11->atom_NET_WM_STATE &&
1079           xevent->xproperty.window == stage_xwindow &&
1080           !stage_x11->is_foreign_xwin)
1081         {
1082           Atom     type;
1083           gint     format;
1084           gulong   n_items, bytes_after;
1085           guchar  *data = NULL;
1086           gboolean fullscreen_set = FALSE;
1087
1088           clutter_x11_trap_x_errors ();
1089           XGetWindowProperty (backend_x11->xdpy, stage_xwindow,
1090                               backend_x11->atom_NET_WM_STATE,
1091                               0, G_MAXLONG,
1092                               False, XA_ATOM,
1093                               &type, &format, &n_items,
1094                               &bytes_after, &data);
1095           clutter_x11_untrap_x_errors ();
1096
1097           if (type != None && data != NULL)
1098             {
1099               gboolean is_fullscreen = FALSE;
1100               Atom *atoms = (Atom *) data;
1101               gulong i;
1102
1103               for (i = 0; i < n_items; i++)
1104                 {
1105                   if (atoms[i] == backend_x11->atom_NET_WM_STATE_FULLSCREEN)
1106                     fullscreen_set = TRUE;
1107                 }
1108
1109               is_fullscreen = _clutter_stage_is_fullscreen (stage_cogl->wrapper);
1110
1111               if (fullscreen_set != is_fullscreen)
1112                 {
1113                   if (fullscreen_set)
1114                     _clutter_stage_update_state (stage_cogl->wrapper,
1115                                                  0,
1116                                                  CLUTTER_STAGE_STATE_FULLSCREEN);
1117                   else
1118                     _clutter_stage_update_state (stage_cogl->wrapper,
1119                                                  CLUTTER_STAGE_STATE_FULLSCREEN,
1120                                                  0);
1121                 }
1122
1123               XFree (data);
1124             }
1125         }
1126       break;
1127
1128     case FocusIn:
1129       if (!_clutter_stage_is_activated (stage_cogl->wrapper))
1130         {
1131           _clutter_stage_update_state (stage_cogl->wrapper,
1132                                        0,
1133                                        CLUTTER_STAGE_STATE_ACTIVATED);
1134         }
1135       break;
1136
1137     case FocusOut:
1138       if (_clutter_stage_is_activated (stage_cogl->wrapper))
1139         {
1140           _clutter_stage_update_state (stage_cogl->wrapper,
1141                                        CLUTTER_STAGE_STATE_ACTIVATED,
1142                                        0);
1143         }
1144       break;
1145
1146     case Expose:
1147       {
1148         XExposeEvent *expose = (XExposeEvent *) xevent;
1149         cairo_rectangle_int_t clip;
1150
1151         CLUTTER_NOTE (EVENT,
1152                       "expose for stage: %s[%p], win:0x%x - "
1153                       "redrawing area (x: %d, y: %d, width: %d, height: %d)",
1154                       _clutter_actor_get_debug_name (CLUTTER_ACTOR (stage)),
1155                       stage,
1156                       (unsigned int) stage_xwindow,
1157                       expose->x,
1158                       expose->y,
1159                       expose->width,
1160                       expose->height);
1161
1162         clip.x = expose->x;
1163         clip.y = expose->y;
1164         clip.width = expose->width;
1165         clip.height = expose->height;
1166         clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stage), &clip);
1167       }
1168       break;
1169
1170     case DestroyNotify:
1171       CLUTTER_NOTE (EVENT,
1172                     "Destroy notification received for stage %s[%p], win:0x%x",
1173                     _clutter_actor_get_debug_name (CLUTTER_ACTOR (stage)),
1174                     stage,
1175                     (unsigned int) stage_xwindow);
1176       event->any.type = CLUTTER_DESTROY_NOTIFY;
1177       event->any.stage = stage;
1178       res = CLUTTER_TRANSLATE_QUEUE;
1179       break;
1180
1181     case ClientMessage:
1182       CLUTTER_NOTE (EVENT, "Client message for stage %s[%p], win:0x%x",
1183                     _clutter_actor_get_debug_name (CLUTTER_ACTOR (stage)),
1184                     stage,
1185                     (unsigned int) stage_xwindow);
1186       if (handle_wm_protocols_event (backend_x11, stage_x11, xevent))
1187         {
1188           event->any.type = CLUTTER_DELETE;
1189           event->any.stage = stage;
1190           res = CLUTTER_TRANSLATE_QUEUE;
1191         }
1192       break;
1193
1194     case MappingNotify:
1195       CLUTTER_NOTE (EVENT, "Refresh keyboard mapping");
1196       XRefreshKeyboardMapping (&xevent->xmapping);
1197       backend_x11->keymap_serial += 1;
1198       res = CLUTTER_TRANSLATE_REMOVE;
1199       break;
1200
1201     default:
1202       res = CLUTTER_TRANSLATE_CONTINUE;
1203       break;
1204     }
1205
1206   return res;
1207 }
1208
1209 static void
1210 clutter_event_translator_iface_init (ClutterEventTranslatorIface *iface)
1211 {
1212   iface->translate_event = clutter_stage_x11_translate_event;
1213 }
1214
1215 /**
1216  * clutter_x11_get_stage_window: (skip)
1217  * @stage: a #ClutterStage
1218  *
1219  * Gets the stages X Window.
1220  *
1221  * Return value: An XID for the stage window.
1222  *
1223  * Since: 0.4
1224  */
1225 Window
1226 clutter_x11_get_stage_window (ClutterStage *stage)
1227 {
1228   ClutterStageWindow *impl;
1229
1230   g_return_val_if_fail (CLUTTER_IS_STAGE (stage), None);
1231
1232   impl = _clutter_stage_get_window (stage);
1233   g_assert (CLUTTER_IS_STAGE_X11 (impl));
1234
1235   return CLUTTER_STAGE_X11 (impl)->xwin;
1236 }
1237
1238 static ClutterStageCogl *
1239 clutter_x11_get_stage_window_from_window (Window win)
1240 {
1241   if (clutter_stages_by_xid == NULL)
1242     return NULL;
1243
1244   return g_hash_table_lookup (clutter_stages_by_xid,
1245                               GINT_TO_POINTER (win));
1246 }
1247
1248 /**
1249  * clutter_x11_get_stage_from_window:
1250  * @win: an X Window ID
1251  *
1252  * Gets the stage for a particular X window.
1253  *
1254  * Return value: (transfer none): A #ClutterStage, or% NULL if a stage
1255  *   does not exist for the window
1256  *
1257  * Since: 0.8
1258  */
1259 ClutterStage *
1260 clutter_x11_get_stage_from_window (Window win)
1261 {
1262   ClutterStageCogl *stage_cogl;
1263
1264   stage_cogl = clutter_x11_get_stage_window_from_window (win);
1265
1266   if (stage_cogl != NULL)
1267     return stage_cogl->wrapper;
1268
1269   return NULL;
1270 }
1271
1272 /**
1273  * clutter_x11_get_stage_visual: (skip)
1274  * @stage: a #ClutterStage
1275  *
1276  * Returns an XVisualInfo suitable for creating a foreign window for the given
1277  * stage. NOTE: It doesn't do as the name may suggest, which is return the
1278  * XVisualInfo that was used to create an existing window for the given stage.
1279  *
1280  * XXX: It might be best to deprecate this function and replace with something
1281  * along the lines of clutter_backend_x11_get_foreign_visual () or perhaps
1282  * clutter_stage_x11_get_foreign_visual ()
1283  *
1284  * Return value: (transfer full): An XVisualInfo suitable for creating a
1285  *   foreign stage. Use XFree() to free the returned value instead
1286  *
1287  * Deprecated: 1.2: Use clutter_x11_get_visual_info() instead
1288  *
1289  * Since: 0.4
1290  */
1291 XVisualInfo *
1292 clutter_x11_get_stage_visual (ClutterStage *stage)
1293 {
1294   ClutterBackend *backend = clutter_get_default_backend ();
1295   ClutterBackendX11 *backend_x11;
1296
1297   g_return_val_if_fail (CLUTTER_IS_BACKEND_X11 (backend), NULL);
1298   backend_x11 = CLUTTER_BACKEND_X11 (backend);
1299
1300   return _clutter_backend_x11_get_visual_info (backend_x11);
1301 }
1302
1303 typedef struct {
1304   ClutterStageX11 *stage_x11;
1305   cairo_rectangle_int_t geom;
1306   Window xwindow;
1307   guint destroy_old_xwindow : 1;
1308 } ForeignWindowData;
1309
1310 static void
1311 set_foreign_window_callback (ClutterActor *actor,
1312                              void         *data)
1313 {
1314   ForeignWindowData *fwd = data;
1315   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (fwd->stage_x11);
1316   ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend);
1317
1318   CLUTTER_NOTE (BACKEND, "Setting foreign window (0x%x)",
1319                 (unsigned int) fwd->xwindow);
1320
1321   if (fwd->destroy_old_xwindow && fwd->stage_x11->xwin != None)
1322     {
1323       CLUTTER_NOTE (BACKEND, "Destroying previous window (0x%x)",
1324                     (unsigned int) fwd->xwindow);
1325       XDestroyWindow (backend_x11->xdpy, fwd->stage_x11->xwin);
1326     }
1327
1328   fwd->stage_x11->xwin = fwd->xwindow;
1329   fwd->stage_x11->is_foreign_xwin = TRUE;
1330
1331   fwd->stage_x11->xwin_width = fwd->geom.width;
1332   fwd->stage_x11->xwin_height = fwd->geom.height;
1333
1334   clutter_actor_set_size (actor, fwd->geom.width, fwd->geom.height);
1335
1336   if (clutter_stages_by_xid == NULL)
1337     clutter_stages_by_xid = g_hash_table_new (NULL, NULL);
1338
1339   g_hash_table_insert (clutter_stages_by_xid,
1340                        GINT_TO_POINTER (fwd->stage_x11->xwin),
1341                        fwd->stage_x11);
1342
1343   /* calling this with the stage unrealized will unset the stage
1344    * from the GL context; once the stage is realized the GL context
1345    * will be set again
1346    */
1347   clutter_stage_ensure_current (CLUTTER_STAGE (actor));
1348 }
1349
1350 /**
1351  * clutter_x11_set_stage_foreign:
1352  * @stage: a #ClutterStage
1353  * @xwindow: an existing X Window id
1354  *
1355  * Target the #ClutterStage to use an existing external X Window
1356  *
1357  * Return value: %TRUE if foreign window is valid
1358  *
1359  * Since: 0.4
1360  */
1361 gboolean
1362 clutter_x11_set_stage_foreign (ClutterStage *stage,
1363                                Window        xwindow)
1364 {
1365   ClutterBackendX11 *backend_x11;
1366   ClutterStageX11 *stage_x11;
1367   ClutterStageCogl *stage_cogl;
1368   ClutterStageWindow *impl;
1369   ClutterActor *actor;
1370   gint x, y;
1371   guint width, height, border, depth;
1372   Window root_return;
1373   Status status;
1374   ForeignWindowData fwd;
1375   XVisualInfo *xvisinfo;
1376
1377   g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
1378   g_return_val_if_fail (!CLUTTER_ACTOR_IN_DESTRUCTION (stage), FALSE);
1379   g_return_val_if_fail (xwindow != None, FALSE);
1380
1381   impl = _clutter_stage_get_window (stage);
1382   stage_x11 = CLUTTER_STAGE_X11 (impl);
1383   stage_cogl = CLUTTER_STAGE_COGL (impl);
1384   backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend);
1385
1386   xvisinfo = _clutter_backend_x11_get_visual_info (backend_x11);
1387   g_return_val_if_fail (xvisinfo != NULL, FALSE);
1388
1389   clutter_x11_trap_x_errors ();
1390
1391   status = XGetGeometry (backend_x11->xdpy, xwindow,
1392                          &root_return,
1393                          &x, &y,
1394                          &width, &height,
1395                          &border,
1396                          &depth);
1397
1398   if (clutter_x11_untrap_x_errors () || !status)
1399     {
1400       g_critical ("Unable to retrieve the geometry of the foreign window: "
1401                   "XGetGeometry() failed (status code: %d)", status);
1402       return FALSE;
1403     }
1404
1405   if (width == 0 || height == 0)
1406     {
1407       g_warning ("The size of the foreign window is 0x0");
1408       return FALSE;
1409     }
1410
1411   if (depth != xvisinfo->depth)
1412     {
1413       g_warning ("The depth of the visual of the foreign window is %d, but "
1414                  "Clutter has been initialized to require a visual depth "
1415                  "of %d",
1416                  depth,
1417                  xvisinfo->depth);
1418       return FALSE;
1419     }
1420
1421   fwd.stage_x11 = stage_x11;
1422   fwd.xwindow = xwindow;
1423
1424   /* destroy the old Window, if we have one and it's ours */
1425   if (stage_x11->xwin != None && !stage_x11->is_foreign_xwin)
1426     fwd.destroy_old_xwindow = TRUE;
1427   else
1428     fwd.destroy_old_xwindow = FALSE;
1429
1430   fwd.geom.x = x;
1431   fwd.geom.y = y;
1432   fwd.geom.width = width;
1433   fwd.geom.height = height;
1434
1435   actor = CLUTTER_ACTOR (stage);
1436
1437   _clutter_actor_rerealize (actor,
1438                             set_foreign_window_callback,
1439                             &fwd);
1440
1441   /* Queue a relayout - so the stage will be allocated the new
1442    * window size.
1443    *
1444    * Note also that when the stage gets allocated the new
1445    * window size that will result in the stage's
1446    * priv->viewport being changed, which will in turn result
1447    * in the Cogl viewport changing when _clutter_do_redraw
1448    * calls _clutter_stage_maybe_setup_viewport().
1449    */
1450   clutter_actor_queue_relayout (actor);
1451
1452   return TRUE;
1453 }
1454
1455 void
1456 _clutter_stage_x11_set_user_time (ClutterStageX11 *stage_x11,
1457                                   guint32          user_time)
1458 {
1459   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11);
1460   ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend);
1461
1462   set_user_time (backend_x11, stage_x11, user_time);
1463 }
1464
1465 gboolean
1466 _clutter_stage_x11_get_root_coords (ClutterStageX11 *stage_x11,
1467                                     gint            *root_x,
1468                                     gint            *root_y)
1469 {
1470   ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11);
1471   ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend);
1472   gint return_val;
1473   Window child;
1474   gint tx, ty;
1475
1476   return_val = XTranslateCoordinates (backend_x11->xdpy,
1477                                       stage_x11->xwin,
1478                                       backend_x11->xwin_root,
1479                                       0, 0, &tx, &ty,
1480                                       &child);
1481
1482   if (root_x)
1483     *root_x = tx;
1484
1485   if (root_y)
1486     *root_y = ty;
1487
1488   return (return_val == 0);
1489 }