473d0e9fc3
Bug #815 - Split up request, allocation, and paint box * clutter/clutter-actor.[ch]: Rework the size allocation, request and paint area. Now ::request_coords() is called ::allocate(), and ::query_coords() has been split into ::get_preferred_width() and ::get_preferred_height(). See the documentation and the layout test on how to implement a container and layout manager with the new API. (#915, based on a patch by Havoc Pennington, Lucas Rocha and Johan Bilien) * clutter/clutter-clone-texture.c: Port CloneTexture to the new size negotiation API; it just means forwarding the requests to the parent texture. * clutter/clutter-deprecated.h: Add deprecated and replaced API. * clutter/clutter-entry.c: Port Entry to the new size negotiation API. * clutter/clutter-group.c: Port Group to the new size negotiation API; the semantics of the Group actor do not change. * clutter/clutter-label.c: Port Label to the new size negotiation API, and vastly simplify the code. * clutter/clutter-main.[ch]: Add API for executing a relayout when needed. * clutter/clutter-private.h: Add new Stage private API. * clutter/clutter-rectangle.c: Update the get_abs_opacity() call to get_paint_opacity(). * clutter/clutter-stage.c: (clutter_stage_get_preferred_width), (clutter_stage_get_preferred_height), (clutter_stage_allocate), (clutter_stage_class_init): Port Stage to the new size negotiation API. * clutter/clutter-texture.c: Port Texture to the new size negotiation API. * clutter/clutter-types.h: Add ClutterRequestMode enumeration. * clutter/x11/clutter-stage-x11.c: Port the X11 stage implementation to the new size negotiation API. * tests/Makefile.am: Add the layout manager test case. * tests/test-opacity.c: Update. * tests/test-project.c: Update. * tests/test-layout.c: Test case for a layout manager implemented using the new size negotiation API; the layout manager handles both transformed and untransformed children.
773 lines
22 KiB
C
773 lines
22 KiB
C
/* Clutter.
|
|
* An OpenGL based 'interactive canvas' library.
|
|
* Authored By Matthew Allum <mallum@openedhand.com>
|
|
* Copyright (C) 2006-2007 OpenedHand
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "clutter-backend-x11.h"
|
|
#include "clutter-stage-x11.h"
|
|
#include "clutter-x11.h"
|
|
|
|
#include "../clutter-stage-window.h"
|
|
#include "../clutter-main.h"
|
|
#include "../clutter-feature.h"
|
|
#include "../clutter-color.h"
|
|
#include "../clutter-util.h"
|
|
#include "../clutter-event.h"
|
|
#include "../clutter-enum-types.h"
|
|
#include "../clutter-private.h"
|
|
#include "../clutter-debug.h"
|
|
#include "../clutter-units.h"
|
|
|
|
#include "cogl/cogl.h"
|
|
|
|
#ifdef HAVE_XFIXES
|
|
#include <X11/extensions/Xfixes.h>
|
|
#endif
|
|
|
|
static void clutter_stage_window_iface_init (ClutterStageWindowIface *iface);
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (ClutterStageX11,
|
|
clutter_stage_x11,
|
|
CLUTTER_TYPE_GROUP,
|
|
G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_STAGE_WINDOW,
|
|
clutter_stage_window_iface_init));
|
|
|
|
#define _NET_WM_STATE_REMOVE 0 /* remove/unset property */
|
|
#define _NET_WM_STATE_ADD 1 /* add/set property */
|
|
#define _NET_WM_STATE_TOGGLE 2 /* toggle property */
|
|
|
|
static void
|
|
send_wmspec_change_state (ClutterBackendX11 *backend_x11,
|
|
Window window,
|
|
Atom state,
|
|
gboolean add)
|
|
{
|
|
XClientMessageEvent xclient;
|
|
|
|
memset (&xclient, 0, sizeof (xclient));
|
|
|
|
xclient.type = ClientMessage;
|
|
xclient.window = window;
|
|
xclient.message_type = backend_x11->atom_NET_WM_STATE;
|
|
xclient.format = 32;
|
|
|
|
xclient.data.l[0] = add ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
|
|
xclient.data.l[1] = state;
|
|
xclient.data.l[2] = 0;
|
|
xclient.data.l[3] = 0;
|
|
xclient.data.l[4] = 0;
|
|
|
|
XSendEvent (backend_x11->xdpy,
|
|
DefaultRootWindow(backend_x11->xdpy),
|
|
False,
|
|
SubstructureRedirectMask|SubstructureNotifyMask,
|
|
(XEvent *)&xclient);
|
|
}
|
|
|
|
void
|
|
clutter_stage_x11_fix_window_size (ClutterStageX11 *stage_x11)
|
|
{
|
|
gboolean resize;
|
|
|
|
resize = clutter_stage_get_user_resizable (stage_x11->wrapper);
|
|
|
|
if (stage_x11->xwin != None && stage_x11->is_foreign_xwin == FALSE)
|
|
{
|
|
XSizeHints *size_hints;
|
|
ClutterUnit min_width, min_height;
|
|
|
|
size_hints = XAllocSizeHints();
|
|
|
|
clutter_actor_get_preferred_width (CLUTTER_ACTOR (stage_x11),
|
|
-1,
|
|
&min_width, NULL);
|
|
clutter_actor_get_preferred_height (CLUTTER_ACTOR (stage_x11),
|
|
min_width,
|
|
&min_height, NULL);
|
|
|
|
size_hints->min_width = CLUTTER_UNITS_TO_DEVICE (min_width);
|
|
size_hints->min_height = CLUTTER_UNITS_TO_DEVICE (min_height);
|
|
size_hints->flags = PMinSize;
|
|
|
|
if (!resize)
|
|
{
|
|
size_hints->max_width = size_hints->min_width;
|
|
size_hints->max_height = size_hints->min_height;
|
|
size_hints->flags |= PMaxSize;
|
|
}
|
|
|
|
XSetWMNormalHints (stage_x11->xdpy, stage_x11->xwin, size_hints);
|
|
|
|
XFree(size_hints);
|
|
}
|
|
}
|
|
|
|
static void
|
|
clutter_stage_x11_show (ClutterActor *actor)
|
|
{
|
|
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (actor);
|
|
|
|
CLUTTER_ACTOR_CLASS (clutter_stage_x11_parent_class)->show (actor);
|
|
|
|
if (stage_x11->xwin)
|
|
{
|
|
/* Fire off a redraw to avoid flicker on first map.
|
|
* Appears not to work perfectly on intel drivers at least.
|
|
*/
|
|
clutter_redraw (stage_x11->wrapper);
|
|
|
|
XSync (stage_x11->xdpy, FALSE);
|
|
XMapWindow (stage_x11->xdpy, stage_x11->xwin);
|
|
}
|
|
}
|
|
|
|
static void
|
|
clutter_stage_x11_hide (ClutterActor *actor)
|
|
{
|
|
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (actor);
|
|
|
|
if (stage_x11->xwin)
|
|
XUnmapWindow (stage_x11->xdpy, stage_x11->xwin);
|
|
}
|
|
|
|
void
|
|
clutter_stage_x11_set_wm_protocols (ClutterStageX11 *stage_x11)
|
|
{
|
|
ClutterBackendX11 *backend_x11 = stage_x11->backend;
|
|
Atom protocols[2];
|
|
int n = 0;
|
|
|
|
protocols[n++] = backend_x11->atom_WM_DELETE_WINDOW;
|
|
protocols[n++] = backend_x11->atom_NET_WM_PING;
|
|
|
|
XSetWMProtocols (stage_x11->xdpy, stage_x11->xwin, protocols, n);
|
|
}
|
|
|
|
static void
|
|
clutter_stage_x11_get_preferred_width (ClutterActor *self,
|
|
ClutterUnit for_height,
|
|
ClutterUnit *min_width_p,
|
|
ClutterUnit *natural_width_p)
|
|
{
|
|
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (self);
|
|
gboolean resize;
|
|
|
|
if (stage_x11->fullscreen_on_map)
|
|
{
|
|
int width;
|
|
|
|
width = DisplayWidth (stage_x11->xdpy, stage_x11->xscreen);
|
|
|
|
if (min_width_p)
|
|
*min_width_p = CLUTTER_UNITS_FROM_DEVICE (width);
|
|
|
|
if (natural_width_p)
|
|
*natural_width_p = CLUTTER_UNITS_FROM_DEVICE (width);
|
|
|
|
return;
|
|
}
|
|
|
|
resize = clutter_stage_get_user_resizable (stage_x11->wrapper);
|
|
|
|
if (min_width_p)
|
|
{
|
|
/* FIXME need API to set this */
|
|
if (resize)
|
|
*min_width_p = CLUTTER_UNITS_FROM_DEVICE (1);
|
|
else
|
|
*min_width_p = CLUTTER_UNITS_FROM_DEVICE (stage_x11->xwin_width);
|
|
}
|
|
|
|
if (natural_width_p)
|
|
*natural_width_p = CLUTTER_UNITS_FROM_DEVICE (stage_x11->xwin_width);
|
|
}
|
|
|
|
static void
|
|
clutter_stage_x11_get_preferred_height (ClutterActor *self,
|
|
ClutterUnit for_width,
|
|
ClutterUnit *min_height_p,
|
|
ClutterUnit *natural_height_p)
|
|
{
|
|
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (self);
|
|
gboolean resize;
|
|
|
|
if (stage_x11->fullscreen_on_map)
|
|
{
|
|
int height;
|
|
|
|
height = DisplayHeight (stage_x11->xdpy, stage_x11->xscreen);
|
|
|
|
if (min_height_p)
|
|
*min_height_p = CLUTTER_UNITS_FROM_DEVICE (height);
|
|
|
|
if (natural_height_p)
|
|
*natural_height_p = CLUTTER_UNITS_FROM_DEVICE (height);
|
|
|
|
return;
|
|
}
|
|
|
|
resize = clutter_stage_get_user_resizable (stage_x11->wrapper);
|
|
|
|
if (min_height_p)
|
|
{
|
|
if (resize)
|
|
*min_height_p = CLUTTER_UNITS_FROM_DEVICE (1); /* FIXME need API
|
|
* to set this
|
|
*/
|
|
else
|
|
*min_height_p = CLUTTER_UNITS_FROM_DEVICE (stage_x11->xwin_height);
|
|
}
|
|
|
|
if (natural_height_p)
|
|
*natural_height_p = CLUTTER_UNITS_FROM_DEVICE (stage_x11->xwin_height);
|
|
}
|
|
|
|
static void
|
|
clutter_stage_x11_allocate (ClutterActor *self,
|
|
const ClutterActorBox *box,
|
|
gboolean origin_changed)
|
|
{
|
|
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (self);
|
|
ClutterActorClass *parent_class;
|
|
gint new_width, new_height;
|
|
|
|
new_width = ABS (CLUTTER_UNITS_TO_INT (box->x2 - box->x1));
|
|
new_height = ABS (CLUTTER_UNITS_TO_INT (box->y2 - box->y1));
|
|
|
|
if (new_width == 0 || new_height == 0)
|
|
{
|
|
/* Should not happen, if this turns up we need to debug it and
|
|
* determine the cleanest way to fix.
|
|
*/
|
|
g_warning ("X11 stage not allowed to have 0 width or height");
|
|
new_width = 1;
|
|
new_height = 1;
|
|
}
|
|
|
|
if (new_width != stage_x11->xwin_width ||
|
|
new_height != stage_x11->xwin_height)
|
|
{
|
|
stage_x11->xwin_width = new_width;
|
|
stage_x11->xwin_height = new_height;
|
|
|
|
/* The 'handling_configure' flag below is used to prevent the
|
|
window from being resized again in response to a
|
|
ConfigureNotify event. Normally this will not be a problem
|
|
because the window will be resized to xwin_width and
|
|
xwin_height so the above test will prevent it from resizing
|
|
the window a second time. However if the stage is resized
|
|
multiple times without the events being processed in between
|
|
(eg, when calling g_object_set to set both width and height)
|
|
then there will be multiple ConfigureNotify events in the
|
|
queue. Handling the first event will undo the work of setting
|
|
the second property which will cause it to keep generating
|
|
events in an infinite loop. See bug #810 */
|
|
if (stage_x11->xwin != None
|
|
&& !stage_x11->is_foreign_xwin
|
|
&& !stage_x11->handling_configure)
|
|
XResizeWindow (stage_x11->xdpy,
|
|
stage_x11->xwin,
|
|
stage_x11->xwin_width,
|
|
stage_x11->xwin_height);
|
|
|
|
clutter_stage_x11_fix_window_size (stage_x11);
|
|
|
|
if (stage_x11->xpixmap != None)
|
|
{
|
|
/* Need to recreate to resize */
|
|
clutter_actor_unrealize (self);
|
|
clutter_actor_realize (self);
|
|
}
|
|
|
|
CLUTTER_SET_PRIVATE_FLAGS (CLUTTER_ACTOR (stage_x11->wrapper),
|
|
CLUTTER_ACTOR_SYNC_MATRICES);
|
|
}
|
|
|
|
/* chain up to fill in actor->priv->allocation */
|
|
parent_class = CLUTTER_ACTOR_CLASS (clutter_stage_x11_parent_class);
|
|
parent_class->allocate (self, box, origin_changed);
|
|
}
|
|
|
|
static inline void
|
|
set_wm_title (ClutterStageX11 *stage_x11)
|
|
{
|
|
ClutterBackendX11 *backend_x11 = stage_x11->backend;
|
|
|
|
if (stage_x11->xwin == None)
|
|
return;
|
|
|
|
if (stage_x11->title == NULL)
|
|
{
|
|
XDeleteProperty (stage_x11->xdpy,
|
|
stage_x11->xwin,
|
|
backend_x11->atom_NET_WM_NAME);
|
|
}
|
|
else
|
|
{
|
|
XChangeProperty (stage_x11->xdpy,
|
|
stage_x11->xwin,
|
|
backend_x11->atom_NET_WM_NAME,
|
|
backend_x11->atom_UTF8_STRING,
|
|
8,
|
|
PropModeReplace,
|
|
(unsigned char *) stage_x11->title,
|
|
(int) strlen (stage_x11->title));
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
set_cursor_visible (ClutterStageX11 *stage_x11)
|
|
{
|
|
if (stage_x11->xwin == None)
|
|
return;
|
|
|
|
CLUTTER_NOTE (BACKEND, "setting cursor state ('%s') over stage window (%u)",
|
|
stage_x11->is_cursor_visible ? "visible" : "invisible",
|
|
(unsigned int) stage_x11->xwin);
|
|
|
|
if (stage_x11->is_cursor_visible)
|
|
{
|
|
#if 0 /* HAVE_XFIXES - seems buggy/unreliable */
|
|
XFixesShowCursor (stage_x11->xdpy, stage_x11->xwin);
|
|
#else
|
|
XUndefineCursor (stage_x11->xdpy, stage_x11->xwin);
|
|
#endif /* HAVE_XFIXES */
|
|
}
|
|
else
|
|
{
|
|
#if 0 /* HAVE_XFIXES - seems buggy/unreliable, check cursor in firefox
|
|
* loading page after hiding.
|
|
*/
|
|
XFixesHideCursor (stage_x11->xdpy, stage_x11->xwin);
|
|
#else
|
|
XColor col;
|
|
Pixmap pix;
|
|
Cursor curs;
|
|
|
|
pix = XCreatePixmap (stage_x11->xdpy, stage_x11->xwin, 1, 1, 1);
|
|
memset (&col, 0, sizeof (col));
|
|
curs = XCreatePixmapCursor (stage_x11->xdpy,
|
|
pix, pix,
|
|
&col, &col,
|
|
1, 1);
|
|
XFreePixmap (stage_x11->xdpy, pix);
|
|
XDefineCursor (stage_x11->xdpy, stage_x11->xwin, curs);
|
|
#endif /* HAVE_XFIXES */
|
|
}
|
|
}
|
|
|
|
static void
|
|
clutter_stage_x11_realize (ClutterActor *actor)
|
|
{
|
|
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (actor);
|
|
|
|
set_wm_title (stage_x11);
|
|
set_cursor_visible (stage_x11);
|
|
}
|
|
|
|
static void
|
|
clutter_stage_x11_set_fullscreen (ClutterStageWindow *stage_window,
|
|
gboolean is_fullscreen)
|
|
{
|
|
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
|
|
ClutterBackendX11 *backend_x11 = stage_x11->backend;
|
|
ClutterStage *stage = stage_x11->wrapper;
|
|
static gboolean was_resizeable = FALSE;
|
|
|
|
if (!stage)
|
|
return;
|
|
|
|
if (is_fullscreen)
|
|
{
|
|
int width, height;
|
|
|
|
width = DisplayWidth (stage_x11->xdpy, stage_x11->xscreen);
|
|
height = DisplayHeight (stage_x11->xdpy, stage_x11->xscreen);
|
|
|
|
/* we force the stage to the screen size here, in order to
|
|
* get the fullscreen stage size right after the call to
|
|
* clutter_stage_fullscreen(). XXX this might break in case
|
|
* the stage is not fullscreened, but if that does not happen
|
|
* we are massively screwed anyway
|
|
*/
|
|
stage_x11->xwin_width = width;
|
|
stage_x11->xwin_height = height;
|
|
|
|
clutter_actor_set_size (CLUTTER_ACTOR (stage), width, height);
|
|
|
|
if (stage_x11->xwin != None)
|
|
{
|
|
stage_x11->fullscreen_on_map = TRUE;
|
|
|
|
/* if the actor is not mapped we resize the stage window to match
|
|
* the size of the screen; this is useful for e.g. EGLX to avoid
|
|
* a resize when calling clutter_stage_fullscreen() before showing
|
|
* the stage
|
|
*/
|
|
if (!CLUTTER_ACTOR_IS_MAPPED (stage_x11))
|
|
{
|
|
/* FIXME: This wont work if we support more states */
|
|
XChangeProperty (stage_x11->xdpy,
|
|
stage_x11->xwin,
|
|
backend_x11->atom_NET_WM_STATE, XA_ATOM, 32,
|
|
PropModeReplace,
|
|
(unsigned char *) &backend_x11->atom_NET_WM_STATE_FULLSCREEN, 1);
|
|
}
|
|
else
|
|
{
|
|
/* We need to set window user resize-able for metacity at
|
|
* at least to allow the window to fullscreen *sigh*
|
|
*/
|
|
if (clutter_stage_get_user_resizable (stage) == TRUE)
|
|
was_resizeable = TRUE;
|
|
else
|
|
clutter_stage_set_user_resizable (stage, TRUE);
|
|
|
|
send_wmspec_change_state (backend_x11, stage_x11->xwin,
|
|
backend_x11->atom_NET_WM_STATE_FULLSCREEN,
|
|
TRUE);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (stage_x11->xwin != None)
|
|
{
|
|
if (!CLUTTER_ACTOR_IS_MAPPED (stage_x11))
|
|
{
|
|
/* FIXME: This wont work if we support more states */
|
|
XDeleteProperty (stage_x11->xdpy,
|
|
stage_x11->xwin,
|
|
backend_x11->atom_NET_WM_STATE);
|
|
}
|
|
else
|
|
{
|
|
clutter_stage_set_user_resizable (stage, TRUE);
|
|
|
|
send_wmspec_change_state (backend_x11,
|
|
stage_x11->xwin,
|
|
backend_x11->atom_NET_WM_STATE_FULLSCREEN,
|
|
FALSE);
|
|
|
|
/* reset the windows state - this isn't fun - see above */
|
|
if (!was_resizeable)
|
|
clutter_stage_set_user_resizable (stage, FALSE);
|
|
|
|
was_resizeable = FALSE;
|
|
}
|
|
|
|
stage_x11->fullscreen_on_map = FALSE;
|
|
}
|
|
}
|
|
|
|
CLUTTER_SET_PRIVATE_FLAGS (stage, CLUTTER_ACTOR_SYNC_MATRICES);
|
|
}
|
|
|
|
static void
|
|
clutter_stage_x11_set_cursor_visible (ClutterStageWindow *stage_window,
|
|
gboolean cursor_visible)
|
|
{
|
|
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
|
|
|
|
stage_x11->is_cursor_visible = (cursor_visible == TRUE);
|
|
set_cursor_visible (stage_x11);
|
|
}
|
|
|
|
static void
|
|
clutter_stage_x11_set_title (ClutterStageWindow *stage_window,
|
|
const gchar *title)
|
|
{
|
|
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
|
|
|
|
g_free (stage_x11->title);
|
|
stage_x11->title = g_strdup (title);
|
|
set_wm_title (stage_x11);
|
|
}
|
|
|
|
static void
|
|
clutter_stage_x11_set_user_resizable (ClutterStageWindow *stage_window,
|
|
gboolean is_resizable)
|
|
{
|
|
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
|
|
|
|
clutter_stage_x11_fix_window_size (stage_x11);
|
|
}
|
|
|
|
static ClutterActor *
|
|
clutter_stage_x11_get_wrapper (ClutterStageWindow *stage_window)
|
|
{
|
|
return CLUTTER_ACTOR (CLUTTER_STAGE_X11 (stage_window)->wrapper);
|
|
}
|
|
|
|
static void
|
|
clutter_stage_x11_finalize (GObject *gobject)
|
|
{
|
|
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (gobject);
|
|
|
|
g_free (stage_x11->title);
|
|
|
|
G_OBJECT_CLASS (clutter_stage_x11_parent_class)->finalize (gobject);
|
|
}
|
|
|
|
static void
|
|
clutter_stage_x11_dispose (GObject *gobject)
|
|
{
|
|
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (gobject);
|
|
|
|
if (stage_x11->xwin)
|
|
clutter_actor_unrealize (CLUTTER_ACTOR (stage_x11));
|
|
|
|
G_OBJECT_CLASS (clutter_stage_x11_parent_class)->dispose (gobject);
|
|
}
|
|
|
|
static void
|
|
clutter_stage_x11_class_init (ClutterStageX11Class *klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
|
|
|
|
gobject_class->finalize = clutter_stage_x11_finalize;
|
|
gobject_class->dispose = clutter_stage_x11_dispose;
|
|
|
|
actor_class->realize = clutter_stage_x11_realize;
|
|
actor_class->show = clutter_stage_x11_show;
|
|
actor_class->hide = clutter_stage_x11_hide;
|
|
|
|
actor_class->get_preferred_width = clutter_stage_x11_get_preferred_width;
|
|
actor_class->get_preferred_height = clutter_stage_x11_get_preferred_height;
|
|
actor_class->allocate = clutter_stage_x11_allocate;
|
|
}
|
|
|
|
static void
|
|
clutter_stage_x11_init (ClutterStageX11 *stage)
|
|
{
|
|
stage->xdpy = NULL;
|
|
stage->xwin_root = None;
|
|
stage->xscreen = 0;
|
|
|
|
stage->xwin = None;
|
|
stage->xwin_width = 640;
|
|
stage->xwin_height = 480;
|
|
stage->xvisinfo = None;
|
|
|
|
stage->is_foreign_xwin = FALSE;
|
|
stage->fullscreen_on_map = FALSE;
|
|
stage->handling_configure = FALSE;
|
|
stage->is_cursor_visible = TRUE;
|
|
|
|
stage->title = NULL;
|
|
|
|
stage->wrapper = NULL;
|
|
|
|
CLUTTER_SET_PRIVATE_FLAGS (stage, CLUTTER_ACTOR_IS_TOPLEVEL);
|
|
}
|
|
|
|
static void
|
|
clutter_stage_window_iface_init (ClutterStageWindowIface *iface)
|
|
{
|
|
iface->get_wrapper = clutter_stage_x11_get_wrapper;
|
|
iface->set_title = clutter_stage_x11_set_title;
|
|
iface->set_fullscreen = clutter_stage_x11_set_fullscreen;
|
|
iface->set_cursor_visible = clutter_stage_x11_set_cursor_visible;
|
|
iface->set_user_resizable = clutter_stage_x11_set_user_resizable;
|
|
}
|
|
|
|
/**
|
|
* clutter_x11_get_stage_window:
|
|
* @stage: a #ClutterStage
|
|
*
|
|
* Gets the stages X Window.
|
|
*
|
|
* Return value: An XID for the stage window.
|
|
*
|
|
* Since: 0.4
|
|
*/
|
|
Window
|
|
clutter_x11_get_stage_window (ClutterStage *stage)
|
|
{
|
|
ClutterStageWindow *impl;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_STAGE (stage), None);
|
|
|
|
impl = _clutter_stage_get_window (stage);
|
|
g_assert (CLUTTER_IS_STAGE_X11 (impl));
|
|
|
|
return CLUTTER_STAGE_X11 (impl)->xwin;
|
|
}
|
|
|
|
/**
|
|
* clutter_x11_get_stage_from_window:
|
|
* @win: an X Window ID
|
|
*
|
|
* Gets the stage for a particular X window.
|
|
*
|
|
* Return value: The stage or NULL if a stage does not exist for the window.
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
ClutterStage *
|
|
clutter_x11_get_stage_from_window (Window win)
|
|
{
|
|
ClutterMainContext *context;
|
|
ClutterStageManager *stage_manager;
|
|
GSList *l;
|
|
|
|
context = clutter_context_get_default ();
|
|
|
|
stage_manager = context->stage_manager;
|
|
|
|
/* FIXME: use a hash here for performance resaon */
|
|
for (l = stage_manager->stages; l; l = l->next)
|
|
{
|
|
ClutterStage *stage = l->data;
|
|
ClutterStageWindow *impl;
|
|
|
|
impl = _clutter_stage_get_window (stage);
|
|
g_assert (CLUTTER_IS_STAGE_X11 (impl));
|
|
|
|
if (CLUTTER_STAGE_X11 (impl)->xwin == win)
|
|
return stage;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* clutter_x11_get_stage_visual:
|
|
* @stage: a #ClutterStage
|
|
*
|
|
* Returns the stage XVisualInfo
|
|
*
|
|
* Return value: The XVisualInfo for the stage.
|
|
*
|
|
* Since: 0.4
|
|
*/
|
|
XVisualInfo *
|
|
clutter_x11_get_stage_visual (ClutterStage *stage)
|
|
{
|
|
ClutterStageWindow *impl;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL);
|
|
|
|
impl = _clutter_stage_get_window (stage);
|
|
g_assert (CLUTTER_IS_STAGE_X11 (impl));
|
|
|
|
return CLUTTER_STAGE_X11 (impl)->xvisinfo;
|
|
}
|
|
|
|
/**
|
|
* clutter_x11_set_stage_foreign:
|
|
* @stage: a #ClutterStage
|
|
* @xwindow: an existing X Window id
|
|
*
|
|
* Target the #ClutterStage to use an existing external X Window
|
|
*
|
|
* Return value: %TRUE if foreign window is valid
|
|
*
|
|
* Since: 0.4
|
|
*/
|
|
gboolean
|
|
clutter_x11_set_stage_foreign (ClutterStage *stage,
|
|
Window xwindow)
|
|
{
|
|
ClutterStageX11 *stage_x11;
|
|
ClutterStageWindow *impl;
|
|
ClutterActor *actor;
|
|
gint x, y;
|
|
guint width, height, border, depth;
|
|
Window root_return;
|
|
Status status;
|
|
ClutterGeometry geom;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
|
|
g_return_val_if_fail (xwindow != None, FALSE);
|
|
|
|
actor = CLUTTER_ACTOR (stage);
|
|
|
|
impl = _clutter_stage_get_window (stage);
|
|
stage_x11 = CLUTTER_STAGE_X11 (impl);
|
|
|
|
clutter_x11_trap_x_errors ();
|
|
|
|
status = XGetGeometry (stage_x11->xdpy, xwindow,
|
|
&root_return,
|
|
&x, &y,
|
|
&width, &height,
|
|
&border,
|
|
&depth);
|
|
|
|
if (clutter_x11_untrap_x_errors () ||
|
|
!status ||
|
|
width == 0 || height == 0 ||
|
|
depth != stage_x11->xvisinfo->depth)
|
|
{
|
|
g_warning ("Unable to retrieve the new window geometry");
|
|
return FALSE;
|
|
}
|
|
|
|
clutter_actor_unrealize (actor);
|
|
|
|
CLUTTER_NOTE (BACKEND, "Setting foreign window (0x%x)", (int) xwindow);
|
|
|
|
stage_x11->xwin = xwindow;
|
|
stage_x11->is_foreign_xwin = TRUE;
|
|
|
|
geom.x = x;
|
|
geom.y = y;
|
|
geom.width = stage_x11->xwin_width = width;
|
|
geom.height = stage_x11->xwin_height = height;
|
|
|
|
clutter_actor_set_geometry (actor, &geom);
|
|
clutter_actor_realize (actor);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
clutter_stage_x11_map (ClutterStageX11 *stage_x11)
|
|
{
|
|
/* set the mapped flag on the implementation */
|
|
CLUTTER_ACTOR_SET_FLAGS (stage_x11, CLUTTER_ACTOR_MAPPED);
|
|
|
|
/* and on the wrapper itself */
|
|
CLUTTER_ACTOR_SET_FLAGS (stage_x11->wrapper, CLUTTER_ACTOR_MAPPED);
|
|
|
|
if (stage_x11->fullscreen_on_map)
|
|
clutter_stage_fullscreen (CLUTTER_STAGE (stage_x11->wrapper));
|
|
else
|
|
clutter_stage_unfullscreen (CLUTTER_STAGE (stage_x11->wrapper));
|
|
|
|
clutter_actor_queue_relayout (CLUTTER_ACTOR (stage_x11->wrapper));
|
|
}
|
|
|
|
void
|
|
clutter_stage_x11_unmap (ClutterStageX11 *stage_x11)
|
|
{
|
|
/* like above, unset the MAPPED stage on both the implementation and
|
|
* the wrapper
|
|
*/
|
|
CLUTTER_ACTOR_UNSET_FLAGS (stage_x11, CLUTTER_ACTOR_MAPPED);
|
|
CLUTTER_ACTOR_UNSET_FLAGS (stage_x11->wrapper, CLUTTER_ACTOR_MAPPED);
|
|
}
|
|
|