2 * An OpenGL based 'interactive canvas' library.
3 * Authored By Matthew Allum <mallum@openedhand.com>
4 * Copyright (C) 2006-2007 OpenedHand
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.
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.
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/>.
30 #include <cogl/cogl.h>
32 #include "clutter-backend-x11.h"
33 #include "clutter-stage-x11.h"
34 #include "clutter-x11.h"
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"
49 #include <X11/extensions/Xfixes.h>
52 #define STAGE_X11_IS_MAPPED(s) ((((ClutterStageX11 *) (s))->wm_state & STAGE_X11_WITHDRAWN) == 0)
54 static ClutterStageWindowIface *clutter_stage_window_parent_iface = NULL;
56 static void clutter_stage_window_iface_init (ClutterStageWindowIface *iface);
57 static void clutter_event_translator_iface_init (ClutterEventTranslatorIface *iface);
59 static ClutterStageCogl *clutter_x11_get_stage_window_from_window (Window win);
61 static GHashTable *clutter_stages_by_xid = NULL;
63 #define clutter_stage_x11_get_type _clutter_stage_x11_get_type
65 G_DEFINE_TYPE_WITH_CODE (ClutterStageX11,
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));
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 */
78 send_wmspec_change_state (ClutterBackendX11 *backend_x11,
83 XClientMessageEvent xclient;
85 CLUTTER_NOTE (BACKEND, "%s NET_WM state", add ? "adding" : "removing");
87 memset (&xclient, 0, sizeof (xclient));
89 xclient.type = ClientMessage;
90 xclient.window = window;
91 xclient.message_type = backend_x11->atom_NET_WM_STATE;
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;
100 XSendEvent (backend_x11->xdpy,
101 DefaultRootWindow (backend_x11->xdpy),
103 SubstructureRedirectMask | SubstructureNotifyMask,
108 update_state (ClutterStageX11 *stage_x11,
109 ClutterBackendX11 *backend_x11,
115 /* FIXME: This wont work if we support more states */
116 XChangeProperty (backend_x11->xdpy,
118 backend_x11->atom_NET_WM_STATE, XA_ATOM, 32,
120 (unsigned char *) state, 1);
124 /* FIXME: This wont work if we support more states */
125 XDeleteProperty (backend_x11->xdpy,
127 backend_x11->atom_NET_WM_STATE);
132 clutter_stage_x11_fix_window_size (ClutterStageX11 *stage_x11,
136 ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11);
137 ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend);
139 if (stage_x11->xwin != None && !stage_x11->is_foreign_xwin)
141 guint min_width, min_height;
142 XSizeHints *size_hints;
145 resize = clutter_stage_get_user_resizable (stage_cogl->wrapper);
147 size_hints = XAllocSizeHints();
149 clutter_stage_get_minimum_size (stage_cogl->wrapper,
154 new_width = min_width;
157 new_height = min_height;
159 size_hints->flags = 0;
161 /* If we are going fullscreen then we don't want any
162 restrictions on the window size */
163 if (!stage_x11->fullscreening)
167 size_hints->min_width = min_width;
168 size_hints->min_height = min_height;
169 size_hints->flags = PMinSize;
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;
181 XSetWMNormalHints (backend_x11->xdpy, stage_x11->xwin, size_hints);
188 clutter_stage_x11_set_wm_protocols (ClutterStageX11 *stage_x11)
190 ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11);
191 ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend);
195 protocols[n++] = backend_x11->atom_WM_DELETE_WINDOW;
196 protocols[n++] = backend_x11->atom_NET_WM_PING;
198 XSetWMProtocols (backend_x11->xdpy, stage_x11->xwin, protocols, n);
202 clutter_stage_x11_get_geometry (ClutterStageWindow *stage_window,
203 cairo_rectangle_int_t *geometry)
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);
209 geometry->x = geometry->y = 0;
211 /* If we're fullscreen, return the size of the display. */
212 if (_clutter_stage_is_fullscreen (stage_cogl->wrapper) &&
213 stage_x11->fullscreening)
215 geometry->width = DisplayWidth (backend_x11->xdpy, backend_x11->xscreen_num);
216 geometry->height = DisplayHeight (backend_x11->xdpy, backend_x11->xscreen_num);
221 geometry->width = stage_x11->xwin_width;
222 geometry->height = stage_x11->xwin_height;
226 clutter_stage_x11_resize (ClutterStageWindow *stage_window,
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);
234 if (stage_x11->is_foreign_xwin)
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).
240 stage_x11->xwin_width = width;
241 stage_x11->xwin_height = height;
242 clutter_actor_queue_relayout (CLUTTER_ACTOR (stage_cogl->wrapper));
246 /* If we're going fullscreen, don't mess with the size */
247 if (stage_x11->fullscreening)
250 if (width == 0 || height == 0)
252 /* Should not happen, if this turns up we need to debug it and
253 * determine the cleanest way to fix.
255 g_warning ("X11 stage not allowed to have 0 width or height");
260 CLUTTER_NOTE (BACKEND, "New size received: (%d, %d)", width, height);
262 if (stage_x11->xwin != None)
264 clutter_stage_x11_fix_window_size (stage_x11, width, height);
266 if (width != stage_x11->xwin_width ||
267 height != stage_x11->xwin_height)
269 CLUTTER_NOTE (BACKEND, "%s: XResizeWindow[%x] (%d, %d)",
271 (unsigned int) stage_x11->xwin,
275 CLUTTER_SET_PRIVATE_FLAGS (stage_cogl->wrapper,
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,
291 set_wm_pid (ClutterStageX11 *stage_x11)
293 ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11);
294 ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend);
297 if (stage_x11->xwin == None || stage_x11->is_foreign_xwin)
300 /* this will take care of WM_CLIENT_MACHINE and WM_LOCALE_NAME */
301 XSetWMProperties (backend_x11->xdpy, stage_x11->xwin,
308 XChangeProperty (backend_x11->xdpy,
310 backend_x11->atom_NET_WM_PID, XA_CARDINAL, 32,
316 set_wm_title (ClutterStageX11 *stage_x11)
318 ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11);
319 ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend);
321 if (stage_x11->xwin == None || stage_x11->is_foreign_xwin)
324 if (stage_x11->title == NULL)
326 XDeleteProperty (backend_x11->xdpy,
328 backend_x11->atom_NET_WM_NAME);
332 XChangeProperty (backend_x11->xdpy,
334 backend_x11->atom_NET_WM_NAME,
335 backend_x11->atom_UTF8_STRING,
338 (unsigned char *) stage_x11->title,
339 (int) strlen (stage_x11->title));
344 set_cursor_visible (ClutterStageX11 *stage_x11)
346 ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11);
347 ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend);
349 if (stage_x11->xwin == None)
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);
356 if (stage_x11->is_cursor_visible)
358 #if 0 /* HAVE_XFIXES - seems buggy/unreliable */
359 XFixesShowCursor (backend_x11->xdpy, stage_x11->xwin);
361 XUndefineCursor (backend_x11->xdpy, stage_x11->xwin);
362 #endif /* HAVE_XFIXES */
366 #if 0 /* HAVE_XFIXES - seems buggy/unreliable, check cursor in firefox
367 * loading page after hiding.
369 XFixesHideCursor (backend_x11->xdpy, stage_x11->xwin);
375 pix = XCreatePixmap (backend_x11->xdpy, stage_x11->xwin, 1, 1, 1);
376 memset (&col, 0, sizeof (col));
377 curs = XCreatePixmapCursor (backend_x11->xdpy,
381 XFreePixmap (backend_x11->xdpy, pix);
382 XDefineCursor (backend_x11->xdpy, stage_x11->xwin, curs);
383 #endif /* HAVE_XFIXES */
388 clutter_stage_x11_unrealize (ClutterStageWindow *stage_window)
390 ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
392 if (clutter_stages_by_xid != NULL)
394 CLUTTER_NOTE (BACKEND, "Removing X11 stage 0x%x [%p]",
395 (unsigned int) stage_x11->xwin,
398 g_hash_table_remove (clutter_stages_by_xid,
399 GINT_TO_POINTER (stage_x11->xwin));
402 clutter_stage_window_parent_iface->unrealize (stage_window);
406 _clutter_stage_x11_update_foreign_event_mask (CoglOnscreen *onscreen,
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;
415 attrs.event_mask = event_mask | CLUTTER_STAGE_X11_EVENT_MASK;
417 XChangeWindowAttributes (backend_x11->xdpy,
424 clutter_stage_x11_set_fullscreen (ClutterStageWindow *stage_window,
425 gboolean is_fullscreen)
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;
433 if (stage == NULL || CLUTTER_ACTOR_IN_DESTRUCTION (stage))
436 was_fullscreen = _clutter_stage_is_fullscreen (stage);
437 is_fullscreen = !!is_fullscreen;
439 if (was_fullscreen == is_fullscreen)
442 CLUTTER_NOTE (BACKEND, "%ssetting fullscreen", is_fullscreen ? "" : "un");
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);
458 /* Set the fullscreen hint so we can retain the old size of the window. */
459 stage_x11->fullscreening = TRUE;
461 if (stage_x11->xwin != None)
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
468 if (!STAGE_X11_IS_MAPPED (stage_x11))
470 CLUTTER_NOTE (BACKEND, "Fullscreening unmapped stage");
472 update_state (stage_x11, backend_x11,
473 &backend_x11->atom_NET_WM_STATE_FULLSCREEN,
478 CLUTTER_NOTE (BACKEND, "Fullscreening mapped stage");
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);
486 send_wmspec_change_state (backend_x11, stage_x11->xwin,
487 backend_x11->atom_NET_WM_STATE_FULLSCREEN,
492 stage_x11->fullscreen_on_realize = TRUE;
496 stage_x11->fullscreening = FALSE;
498 if (stage_x11->xwin != None)
500 if (!STAGE_X11_IS_MAPPED (stage_x11))
502 CLUTTER_NOTE (BACKEND, "Un-fullscreening unmapped stage");
504 update_state (stage_x11, backend_x11,
505 &backend_x11->atom_NET_WM_STATE_FULLSCREEN,
510 CLUTTER_NOTE (BACKEND, "Un-fullscreening mapped stage");
512 send_wmspec_change_state (backend_x11,
514 backend_x11->atom_NET_WM_STATE_FULLSCREEN,
517 /* Fix the window size to restore the minimum/maximum
519 clutter_stage_x11_fix_window_size (stage_x11,
520 stage_x11->xwin_width,
521 stage_x11->xwin_height);
525 stage_x11->fullscreen_on_realize = FALSE;
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. */
535 clutter_stage_x11_realize (ClutterStageWindow *stage_window)
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;
543 gfloat width, height;
545 clutter_actor_get_size (CLUTTER_ACTOR (stage_cogl->wrapper),
548 stage_cogl->onscreen = cogl_onscreen_new (backend->cogl_context,
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;
556 if (stage_x11->xwin != None)
558 cogl_x11_onscreen_set_foreign_window_xid (stage_cogl->onscreen,
560 _clutter_stage_x11_update_foreign_event_mask,
565 /* Chain to the parent class now. ClutterStageCogl will call cogl_framebuffer_allocate,
566 which will create the X Window we need */
568 if (!(clutter_stage_window_parent_iface->realize (stage_window)))
571 if (stage_x11->xwin == None)
572 stage_x11->xwin = cogl_x11_onscreen_get_window_xid (stage_cogl->onscreen);
574 if (clutter_stages_by_xid == NULL)
575 clutter_stages_by_xid = g_hash_table_new (NULL, NULL);
577 g_hash_table_insert (clutter_stages_by_xid,
578 GINT_TO_POINTER (stage_x11->xwin),
581 set_wm_pid (stage_x11);
582 set_wm_title (stage_x11);
583 set_cursor_visible (stage_x11);
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
591 event_flags = CLUTTER_STAGE_X11_EVENT_MASK;
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.
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.
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
610 XSelectInput (backend_x11->xdpy, stage_x11->xwin, event_flags);
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
616 device_manager = clutter_device_manager_get_default ();
617 _clutter_device_manager_select_stage_events (device_manager,
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);
626 if (stage_x11->fullscreen_on_realize)
628 stage_x11->fullscreen_on_realize = FALSE;
630 clutter_stage_x11_set_fullscreen (stage_window, TRUE);
633 CLUTTER_NOTE (BACKEND, "Successfully realized stage");
639 clutter_stage_x11_set_cursor_visible (ClutterStageWindow *stage_window,
640 gboolean cursor_visible)
642 ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
644 stage_x11->is_cursor_visible = !!cursor_visible;
645 set_cursor_visible (stage_x11);
649 clutter_stage_x11_set_title (ClutterStageWindow *stage_window,
652 ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
654 g_free (stage_x11->title);
655 stage_x11->title = g_strdup (title);
656 set_wm_title (stage_x11);
660 clutter_stage_x11_set_user_resizable (ClutterStageWindow *stage_window,
661 gboolean is_resizable)
663 ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
665 clutter_stage_x11_fix_window_size (stage_x11,
666 stage_x11->xwin_width,
667 stage_x11->xwin_height);
671 update_wm_hints (ClutterStageX11 *stage_x11)
673 ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11);
674 ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend);
677 if (stage_x11->wm_state & STAGE_X11_WITHDRAWN)
680 if (stage_x11->is_foreign_xwin)
683 wm_hints.flags = StateHint | InputHint;
684 wm_hints.initial_state = NormalState;
685 wm_hints.input = stage_x11->accept_focus ? True : False;
687 XSetWMHints (backend_x11->xdpy, stage_x11->xwin, &wm_hints);
691 clutter_stage_x11_set_accept_focus (ClutterStageWindow *stage_window,
692 gboolean accept_focus)
694 ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
696 stage_x11->accept_focus = !!accept_focus;
697 update_wm_hints (stage_x11);
701 set_stage_x11_state (ClutterStageX11 *stage_x11,
702 ClutterStageX11State unset_flags,
703 ClutterStageX11State set_flags)
705 ClutterStageX11State new_stage_state, old_stage_state;
707 old_stage_state = stage_x11->wm_state;
709 new_stage_state = old_stage_state;
710 new_stage_state |= set_flags;
711 new_stage_state &= ~unset_flags;
713 if (new_stage_state == old_stage_state)
716 stage_x11->wm_state = new_stage_state;
720 clutter_stage_x11_show (ClutterStageWindow *stage_window,
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);
727 if (stage_x11->xwin != None)
729 if (do_raise && !stage_x11->is_foreign_xwin)
731 CLUTTER_NOTE (BACKEND, "Raising stage[%lu]",
732 (unsigned long) stage_x11->xwin);
733 XRaiseWindow (backend_x11->xdpy, stage_x11->xwin);
736 if (!STAGE_X11_IS_MAPPED (stage_x11))
738 CLUTTER_NOTE (BACKEND, "Mapping stage[%lu]",
739 (unsigned long) stage_x11->xwin);
741 set_stage_x11_state (stage_x11, STAGE_X11_WITHDRAWN, 0);
743 update_wm_hints (stage_x11);
745 if (stage_x11->fullscreening)
746 clutter_stage_x11_set_fullscreen (stage_window, TRUE);
748 clutter_stage_x11_set_fullscreen (stage_window, FALSE);
751 g_assert (STAGE_X11_IS_MAPPED (stage_x11));
753 clutter_actor_map (CLUTTER_ACTOR (stage_cogl->wrapper));
755 if (!stage_x11->is_foreign_xwin)
756 XMapWindow (backend_x11->xdpy, stage_x11->xwin);
761 clutter_stage_x11_hide (ClutterStageWindow *stage_window)
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);
767 if (stage_x11->xwin != None)
769 if (STAGE_X11_IS_MAPPED (stage_x11))
770 set_stage_x11_state (stage_x11, 0, STAGE_X11_WITHDRAWN);
772 g_assert (!STAGE_X11_IS_MAPPED (stage_x11));
774 clutter_actor_unmap (CLUTTER_ACTOR (stage_cogl->wrapper));
776 if (!stage_x11->is_foreign_xwin)
777 XWithdrawWindow (backend_x11->xdpy, stage_x11->xwin, 0);
782 clutter_stage_x11_can_clip_redraws (ClutterStageWindow *stage_window)
784 ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
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
790 return stage_x11->clipped_redraws_cool_off == 0;
794 clutter_stage_x11_finalize (GObject *gobject)
796 ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (gobject);
798 g_free (stage_x11->title);
800 G_OBJECT_CLASS (clutter_stage_x11_parent_class)->finalize (gobject);
804 clutter_stage_x11_dispose (GObject *gobject)
806 ClutterEventTranslator *translator = CLUTTER_EVENT_TRANSLATOR (gobject);
807 ClutterBackend *backend = CLUTTER_STAGE_COGL (gobject)->backend;
809 _clutter_backend_remove_event_translator (backend, translator);
811 G_OBJECT_CLASS (clutter_stage_x11_parent_class)->dispose (gobject);
815 clutter_stage_x11_class_init (ClutterStageX11Class *klass)
817 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
819 gobject_class->finalize = clutter_stage_x11_finalize;
820 gobject_class->dispose = clutter_stage_x11_dispose;
824 clutter_stage_x11_init (ClutterStageX11 *stage)
827 stage->xwin_width = 640;
828 stage->xwin_height = 480;
830 stage->wm_state = STAGE_X11_WITHDRAWN;
832 stage->is_foreign_xwin = FALSE;
833 stage->fullscreening = FALSE;
834 stage->is_cursor_visible = TRUE;
835 stage->accept_focus = TRUE;
841 clutter_stage_window_iface_init (ClutterStageWindowIface *iface)
843 clutter_stage_window_parent_iface = g_type_interface_peek_parent (iface);
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;
860 set_user_time (ClutterBackendX11 *backend_x11,
861 ClutterStageX11 *stage_x11,
864 if (timestamp != CLUTTER_CURRENT_TIME)
866 XChangeProperty (backend_x11->xdpy,
868 backend_x11->atom_NET_WM_USER_TIME,
871 (unsigned char *) ×tamp, 1);
876 handle_wm_protocols_event (ClutterBackendX11 *backend_x11,
877 ClutterStageX11 *stage_x11,
880 ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11);
881 Atom atom = (Atom) xevent->xclient.data.l[0];
883 if (atom == backend_x11->atom_WM_DELETE_WINDOW &&
884 xevent->xany.window == stage_x11->xwin)
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
891 CLUTTER_NOTE (EVENT, "Delete stage %s[%p], win:0x%x",
892 _clutter_actor_get_debug_name (CLUTTER_ACTOR (stage_cogl->wrapper)),
894 (unsigned int) stage_x11->xwin);
896 set_user_time (backend_x11, stage_x11, xevent->xclient.data.l[1]);
900 else if (atom == backend_x11->atom_NET_WM_PING &&
901 xevent->xany.window == stage_x11->xwin)
903 XClientMessageEvent xclient = xevent->xclient;
905 xclient.window = backend_x11->xwin_root;
906 XSendEvent (backend_x11->xdpy, xclient.window,
908 SubstructureRedirectMask | SubstructureNotifyMask,
909 (XEvent *) &xclient);
913 /* do not send any of the WM_PROTOCOLS events to the queue */
918 clipped_redraws_cool_off_cb (void *data)
920 ClutterStageX11 *stage_x11 = data;
922 stage_x11->clipped_redraws_cool_off = 0;
924 return G_SOURCE_REMOVE;
927 static ClutterTranslateReturn
928 clutter_stage_x11_translate_event (ClutterEventTranslator *translator,
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;
940 stage_cogl = clutter_x11_get_stage_window_from_window (xevent->xany.window);
941 if (stage_cogl == NULL)
942 return CLUTTER_TRANSLATE_CONTINUE;
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;
949 switch (xevent->type)
951 case ConfigureNotify:
952 if (!stage_x11->is_foreign_xwin)
954 gboolean size_changed = FALSE;
956 CLUTTER_NOTE (BACKEND, "ConfigureNotify[%x] (%d, %d)",
957 (unsigned int) stage_x11->xwin,
958 xevent->xconfigure.width,
959 xevent->xconfigure.height);
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))
966 else if ((stage_x11->xwin_width != xevent->xconfigure.width) ||
967 (stage_x11->xwin_height != xevent->xconfigure.height))
970 stage_x11->xwin_width = xevent->xconfigure.width;
971 stage_x11->xwin_height = xevent->xconfigure.height;
974 clutter_actor_set_size (CLUTTER_ACTOR (stage),
975 xevent->xconfigure.width,
976 xevent->xconfigure.height);
978 CLUTTER_UNSET_PRIVATE_FLAGS (stage_cogl->wrapper, CLUTTER_IN_RESIZE);
982 /* XXX: This is a workaround for a race condition when
983 * resizing windows while there are in-flight
984 * glXCopySubBuffer blits happening.
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.
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).
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!
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.
1014 if (stage_x11->clipped_redraws_cool_off)
1015 g_source_remove (stage_x11->clipped_redraws_cool_off);
1017 stage_x11->clipped_redraws_cool_off =
1018 clutter_threads_add_timeout (1000,
1019 clipped_redraws_cool_off_cb,
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 ().
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.
1030 * Make sure we do this only when the size has changed,
1031 * otherwise we end up relayouting on window moves.
1033 clutter_actor_queue_relayout (CLUTTER_ACTOR (stage));
1035 /* the resize process is complete, so we can ask the stage
1036 * to set up the GL viewport with the new size
1038 clutter_stage_ensure_viewport (stage);
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)
1050 gulong n_items, bytes_after;
1051 guchar *data = NULL;
1052 gboolean fullscreen_set = FALSE;
1054 clutter_x11_trap_x_errors ();
1055 XGetWindowProperty (backend_x11->xdpy, stage_xwindow,
1056 backend_x11->atom_NET_WM_STATE,
1059 &type, &format, &n_items,
1060 &bytes_after, &data);
1061 clutter_x11_untrap_x_errors ();
1063 if (type != None && data != NULL)
1065 gboolean is_fullscreen = FALSE;
1066 Atom *atoms = (Atom *) data;
1069 for (i = 0; i < n_items; i++)
1071 if (atoms[i] == backend_x11->atom_NET_WM_STATE_FULLSCREEN)
1072 fullscreen_set = TRUE;
1075 is_fullscreen = _clutter_stage_is_fullscreen (stage_cogl->wrapper);
1077 if (fullscreen_set != is_fullscreen)
1080 _clutter_stage_update_state (stage_cogl->wrapper,
1082 CLUTTER_STAGE_STATE_FULLSCREEN);
1084 _clutter_stage_update_state (stage_cogl->wrapper,
1085 CLUTTER_STAGE_STATE_FULLSCREEN,
1095 if (!_clutter_stage_is_activated (stage_cogl->wrapper))
1097 _clutter_stage_update_state (stage_cogl->wrapper,
1099 CLUTTER_STAGE_STATE_ACTIVATED);
1104 if (_clutter_stage_is_activated (stage_cogl->wrapper))
1106 _clutter_stage_update_state (stage_cogl->wrapper,
1107 CLUTTER_STAGE_STATE_ACTIVATED,
1114 XExposeEvent *expose = (XExposeEvent *) xevent;
1115 cairo_rectangle_int_t clip;
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)),
1122 (unsigned int) stage_xwindow,
1130 clip.width = expose->width;
1131 clip.height = expose->height;
1132 clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stage), &clip);
1137 CLUTTER_NOTE (EVENT,
1138 "Destroy notification received for stage %s[%p], win:0x%x",
1139 _clutter_actor_get_debug_name (CLUTTER_ACTOR (stage)),
1141 (unsigned int) stage_xwindow);
1142 event->any.type = CLUTTER_DESTROY_NOTIFY;
1143 event->any.stage = stage;
1144 res = CLUTTER_TRANSLATE_QUEUE;
1148 CLUTTER_NOTE (EVENT, "Client message for stage %s[%p], win:0x%x",
1149 _clutter_actor_get_debug_name (CLUTTER_ACTOR (stage)),
1151 (unsigned int) stage_xwindow);
1152 if (handle_wm_protocols_event (backend_x11, stage_x11, xevent))
1154 event->any.type = CLUTTER_DELETE;
1155 event->any.stage = stage;
1156 res = CLUTTER_TRANSLATE_QUEUE;
1161 CLUTTER_NOTE (EVENT, "Refresh keyboard mapping");
1162 XRefreshKeyboardMapping (&xevent->xmapping);
1163 backend_x11->keymap_serial += 1;
1164 res = CLUTTER_TRANSLATE_REMOVE;
1168 res = CLUTTER_TRANSLATE_CONTINUE;
1176 clutter_event_translator_iface_init (ClutterEventTranslatorIface *iface)
1178 iface->translate_event = clutter_stage_x11_translate_event;
1182 * clutter_x11_get_stage_window: (skip)
1183 * @stage: a #ClutterStage
1185 * Gets the stages X Window.
1187 * Return value: An XID for the stage window.
1192 clutter_x11_get_stage_window (ClutterStage *stage)
1194 ClutterStageWindow *impl;
1196 g_return_val_if_fail (CLUTTER_IS_STAGE (stage), None);
1198 impl = _clutter_stage_get_window (stage);
1199 g_assert (CLUTTER_IS_STAGE_X11 (impl));
1201 return CLUTTER_STAGE_X11 (impl)->xwin;
1204 static ClutterStageCogl *
1205 clutter_x11_get_stage_window_from_window (Window win)
1207 if (clutter_stages_by_xid == NULL)
1210 return g_hash_table_lookup (clutter_stages_by_xid,
1211 GINT_TO_POINTER (win));
1215 * clutter_x11_get_stage_from_window:
1216 * @win: an X Window ID
1218 * Gets the stage for a particular X window.
1220 * Return value: (transfer none): A #ClutterStage, or% NULL if a stage
1221 * does not exist for the window
1226 clutter_x11_get_stage_from_window (Window win)
1228 ClutterStageCogl *stage_cogl;
1230 stage_cogl = clutter_x11_get_stage_window_from_window (win);
1232 if (stage_cogl != NULL)
1233 return stage_cogl->wrapper;
1239 * clutter_x11_get_stage_visual: (skip)
1240 * @stage: a #ClutterStage
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.
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 ()
1250 * Return value: (transfer full): An XVisualInfo suitable for creating a
1251 * foreign stage. Use XFree() to free the returned value instead
1253 * Deprecated: 1.2: Use clutter_x11_get_visual_info() instead
1258 clutter_x11_get_stage_visual (ClutterStage *stage)
1260 ClutterBackend *backend = clutter_get_default_backend ();
1261 ClutterBackendX11 *backend_x11;
1263 g_return_val_if_fail (CLUTTER_IS_BACKEND_X11 (backend), NULL);
1264 backend_x11 = CLUTTER_BACKEND_X11 (backend);
1266 return _clutter_backend_x11_get_visual_info (backend_x11);
1270 ClutterStageX11 *stage_x11;
1271 cairo_rectangle_int_t geom;
1273 guint destroy_old_xwindow : 1;
1274 } ForeignWindowData;
1277 set_foreign_window_callback (ClutterActor *actor,
1280 ForeignWindowData *fwd = data;
1281 ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (fwd->stage_x11);
1282 ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend);
1284 CLUTTER_NOTE (BACKEND, "Setting foreign window (0x%x)",
1285 (unsigned int) fwd->xwindow);
1287 if (fwd->destroy_old_xwindow && fwd->stage_x11->xwin != None)
1289 CLUTTER_NOTE (BACKEND, "Destroying previous window (0x%x)",
1290 (unsigned int) fwd->xwindow);
1291 XDestroyWindow (backend_x11->xdpy, fwd->stage_x11->xwin);
1294 fwd->stage_x11->xwin = fwd->xwindow;
1295 fwd->stage_x11->is_foreign_xwin = TRUE;
1297 fwd->stage_x11->xwin_width = fwd->geom.width;
1298 fwd->stage_x11->xwin_height = fwd->geom.height;
1300 clutter_actor_set_size (actor, fwd->geom.width, fwd->geom.height);
1302 if (clutter_stages_by_xid == NULL)
1303 clutter_stages_by_xid = g_hash_table_new (NULL, NULL);
1305 g_hash_table_insert (clutter_stages_by_xid,
1306 GINT_TO_POINTER (fwd->stage_x11->xwin),
1309 /* calling this with the stage unrealized will unset the stage
1310 * from the GL context; once the stage is realized the GL context
1313 clutter_stage_ensure_current (CLUTTER_STAGE (actor));
1317 * clutter_x11_set_stage_foreign:
1318 * @stage: a #ClutterStage
1319 * @xwindow: an existing X Window id
1321 * Target the #ClutterStage to use an existing external X Window
1323 * Return value: %TRUE if foreign window is valid
1328 clutter_x11_set_stage_foreign (ClutterStage *stage,
1331 ClutterBackendX11 *backend_x11;
1332 ClutterStageX11 *stage_x11;
1333 ClutterStageCogl *stage_cogl;
1334 ClutterStageWindow *impl;
1335 ClutterActor *actor;
1337 guint width, height, border, depth;
1340 ForeignWindowData fwd;
1341 XVisualInfo *xvisinfo;
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);
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);
1352 xvisinfo = _clutter_backend_x11_get_visual_info (backend_x11);
1353 g_return_val_if_fail (xvisinfo != NULL, FALSE);
1355 clutter_x11_trap_x_errors ();
1357 status = XGetGeometry (backend_x11->xdpy, xwindow,
1364 if (clutter_x11_untrap_x_errors () || !status)
1366 g_critical ("Unable to retrieve the geometry of the foreign window: "
1367 "XGetGeometry() failed (status code: %d)", status);
1371 if (width == 0 || height == 0)
1373 g_warning ("The size of the foreign window is 0x0");
1377 if (depth != xvisinfo->depth)
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 "
1387 fwd.stage_x11 = stage_x11;
1388 fwd.xwindow = xwindow;
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;
1394 fwd.destroy_old_xwindow = FALSE;
1398 fwd.geom.width = width;
1399 fwd.geom.height = height;
1401 actor = CLUTTER_ACTOR (stage);
1403 _clutter_actor_rerealize (actor,
1404 set_foreign_window_callback,
1407 /* Queue a relayout - so the stage will be allocated the new
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().
1416 clutter_actor_queue_relayout (actor);
1422 _clutter_stage_x11_set_user_time (ClutterStageX11 *stage_x11,
1425 ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11);
1426 ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend);
1428 set_user_time (backend_x11, stage_x11, user_time);
1432 _clutter_stage_x11_get_root_coords (ClutterStageX11 *stage_x11,
1436 ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11);
1437 ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend);
1442 return_val = XTranslateCoordinates (backend_x11->xdpy,
1444 backend_x11->xwin_root,
1454 return (return_val == 0);