3607a470aa
* clutter/Makefile.am: * clutter/eglx/Makefile.am: * clutter/eglx/clutter-backend-egl.c: * clutter/eglx/clutter-backend-egl.h: * clutter/eglx/clutter-eglx.h: * clutter/eglx/clutter-event-egl.c: * clutter/eglx/clutter-stage-egl.c: * clutter/eglx/clutter-stage-egl.h: * clutter/glx/Makefile.am: * clutter/glx/clutter-backend-glx.c: * clutter/glx/clutter-backend-glx.h: * clutter/glx/clutter-event-glx.c: * clutter/glx/clutter-glx.h: * clutter/glx/clutter-stage-glx.c: * clutter/glx/clutter-stage-glx.h: * clutter/x11/Makefile.am: * clutter/x11/clutter-backend-x11-private.h: * clutter/x11/clutter-backend-x11.c: * clutter/x11/clutter-backend-x11.h: * clutter/x11/clutter-event-x11.c: * clutter/x11/clutter-stage-x11.c: * clutter/x11/clutter-stage-x11.h: * clutter/x11/clutter-x11.h: Create a new X11 backend class of which EGL and GLX 'real' backends then subclass. Effectively shares all X11 code between both backends avoids code duplication and brings many missing features to EGL X backend. Requires some cleanup and testing. (#518) * clutter/cogl/gles/cogl.c: (cogl_color): Add define to use color4ub only if configure finds it. If not fall back to old code. * configure.ac: Drop support for vincent checks. Drop sdles backend. Specifically check for color4ub call.
525 lines
14 KiB
C
525 lines
14 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-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.h"
|
|
|
|
#ifdef HAVE_XFIXES
|
|
#include <X11/extensions/Xfixes.h>
|
|
#endif
|
|
|
|
#include <gdk-pixbuf-xlib/gdk-pixbuf-xlib.h>
|
|
|
|
G_DEFINE_TYPE (ClutterStageX11, clutter_stage_x11, CLUTTER_TYPE_STAGE);
|
|
|
|
#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_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 (CLUTTER_STAGE (stage_x11));
|
|
|
|
if (stage_x11->xwin != None && stage_x11->is_foreign_xwin == FALSE)
|
|
{
|
|
XSizeHints *size_hints;
|
|
|
|
size_hints = XAllocSizeHints();
|
|
|
|
if (!resize)
|
|
{
|
|
size_hints->max_width
|
|
= size_hints->min_width = stage_x11->xwin_width;
|
|
size_hints->max_height
|
|
= size_hints->min_height = stage_x11->xwin_height;
|
|
size_hints->flags = PMinSize|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);
|
|
|
|
/* Chain up to set mapped flags */
|
|
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();
|
|
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 (Display *xdisplay,
|
|
Window xwindow)
|
|
{
|
|
Atom protocols[2];
|
|
int n = 0;
|
|
|
|
protocols[n++] = XInternAtom (xdisplay, "WM_DELETE_WINDOW", False);
|
|
protocols[n++] = XInternAtom (xdisplay, "_NET_WM_PING", False);
|
|
|
|
XSetWMProtocols (xdisplay, xwindow, protocols, n);
|
|
}
|
|
|
|
static void
|
|
clutter_stage_x11_query_coords (ClutterActor *self,
|
|
ClutterActorBox *box)
|
|
{
|
|
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (self);
|
|
|
|
box->x1 = box->y1 = 0;
|
|
box->x2 = box->x1 + CLUTTER_UNITS_FROM_INT (stage_x11->xwin_width);
|
|
box->y2 = box->y1 + CLUTTER_UNITS_FROM_INT (stage_x11->xwin_height);
|
|
}
|
|
|
|
static void
|
|
clutter_stage_x11_request_coords (ClutterActor *self,
|
|
ClutterActorBox *box)
|
|
{
|
|
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (self);
|
|
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 != stage_x11->xwin_width ||
|
|
new_height != stage_x11->xwin_height)
|
|
{
|
|
stage_x11->xwin_width = new_width;
|
|
stage_x11->xwin_height = new_height;
|
|
|
|
if (stage_x11->xwin != None)
|
|
{
|
|
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(self, CLUTTER_ACTOR_SYNC_MATRICES);
|
|
}
|
|
|
|
if (stage_x11->xwin != None) /* Do we want to bother ? */
|
|
XMoveWindow (stage_x11->xdpy,
|
|
stage_x11->xwin,
|
|
CLUTTER_UNITS_TO_INT (box->x1),
|
|
CLUTTER_UNITS_TO_INT (box->y1));
|
|
}
|
|
|
|
static void
|
|
clutter_stage_x11_set_fullscreen (ClutterStage *stage,
|
|
gboolean fullscreen)
|
|
{
|
|
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage);
|
|
ClutterBackendX11 *backend_x11 = stage_x11->backend;
|
|
|
|
static gboolean was_resizeable = FALSE;
|
|
|
|
if (fullscreen)
|
|
{
|
|
if (stage_x11->xwin != None)
|
|
{
|
|
if (!CLUTTER_ACTOR_IS_MAPPED(CLUTTER_ACTOR (stage_x11)))
|
|
{
|
|
gint width, height;
|
|
|
|
width = DisplayWidth (stage_x11->xdpy, stage_x11->xscreen);
|
|
height = DisplayHeight (stage_x11->xdpy, stage_x11->xscreen);
|
|
|
|
clutter_actor_set_size (CLUTTER_ACTOR (stage_x11),
|
|
width, height);
|
|
/* FIXME: This wont work if we support more states */
|
|
XChangeProperty
|
|
(stage_x11->xdpy,
|
|
stage_x11->xwin,
|
|
backend_x11->atom_WM_STATE, XA_ATOM, 32,
|
|
PropModeReplace,
|
|
(unsigned char *)&backend_x11->atom_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_WM_STATE_FULLSCREEN,
|
|
TRUE);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (stage_x11->xwin != None)
|
|
{
|
|
if (!CLUTTER_ACTOR_IS_MAPPED(CLUTTER_ACTOR (stage_x11)))
|
|
{
|
|
/* FIXME: This wont work if we support more states */
|
|
XDeleteProperty (stage_x11->xdpy,
|
|
stage_x11->xwin,
|
|
backend_x11->atom_WM_STATE);
|
|
}
|
|
else
|
|
{
|
|
clutter_stage_set_user_resizable (stage, TRUE);
|
|
|
|
send_wmspec_change_state(backend_x11,
|
|
stage_x11->xwin,
|
|
backend_x11->atom_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;
|
|
}
|
|
}
|
|
}
|
|
|
|
CLUTTER_SET_PRIVATE_FLAGS(stage, CLUTTER_ACTOR_SYNC_MATRICES);
|
|
}
|
|
|
|
static void
|
|
clutter_stage_x11_set_cursor_visible (ClutterStage *stage,
|
|
gboolean show_cursor)
|
|
{
|
|
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage);
|
|
|
|
if (stage_x11->xwin == None)
|
|
return;
|
|
|
|
CLUTTER_NOTE (BACKEND, "setting cursor state ('%s') over stage window (%u)",
|
|
show_cursor ? "visible" : "invisible",
|
|
(unsigned int) stage_x11->xwin);
|
|
|
|
if (show_cursor)
|
|
{
|
|
#if 0 /* HAVE_XFIXES - borked on fiesty at least so disabled until further
|
|
* investigation.
|
|
*/
|
|
XFixesShowCursor (stage_x11->xdpy, stage_x11->xwin);
|
|
#else
|
|
XUndefineCursor (stage_x11->xdpy, stage_x11->xwin);
|
|
#endif /* HAVE_XFIXES */
|
|
}
|
|
else
|
|
{
|
|
#if 0 /* HAVE_XFIXES - borked */
|
|
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_set_title (ClutterStage *stage,
|
|
const gchar *title)
|
|
{
|
|
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage);
|
|
Atom atom_NET_WM_NAME, atom_UTF8_STRING;
|
|
|
|
if (stage_x11->xwin == None)
|
|
return;
|
|
|
|
/* FIXME: pre create these to avoid too many round trips */
|
|
atom_NET_WM_NAME = XInternAtom (stage_x11->xdpy, "_NET_WM_NAME", False);
|
|
atom_UTF8_STRING = XInternAtom (stage_x11->xdpy, "UTF8_STRING", False);
|
|
|
|
if (title == NULL)
|
|
{
|
|
XDeleteProperty (stage_x11->xdpy,
|
|
stage_x11->xwin,
|
|
atom_NET_WM_NAME);
|
|
}
|
|
else
|
|
{
|
|
XChangeProperty (stage_x11->xdpy,
|
|
stage_x11->xwin,
|
|
atom_NET_WM_NAME,
|
|
atom_UTF8_STRING,
|
|
8,
|
|
PropModeReplace,
|
|
(unsigned char*)title,
|
|
(int)strlen(title));
|
|
}
|
|
}
|
|
|
|
static void
|
|
clutter_stage_x11_set_user_resize (ClutterStage *stage,
|
|
gboolean value)
|
|
{
|
|
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage);
|
|
|
|
clutter_stage_x11_fix_window_size (stage_x11);
|
|
}
|
|
|
|
static void
|
|
clutter_stage_x11_set_offscreen (ClutterStage *stage,
|
|
gboolean offscreen)
|
|
{
|
|
/* Do nothing ? */
|
|
}
|
|
|
|
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);
|
|
ClutterStageClass *stage_class = CLUTTER_STAGE_CLASS (klass);
|
|
|
|
gobject_class->dispose = clutter_stage_x11_dispose;
|
|
|
|
actor_class->show = clutter_stage_x11_show;
|
|
actor_class->hide = clutter_stage_x11_hide;
|
|
actor_class->request_coords = clutter_stage_x11_request_coords;
|
|
actor_class->query_coords = clutter_stage_x11_query_coords;
|
|
|
|
stage_class->set_fullscreen = clutter_stage_x11_set_fullscreen;
|
|
stage_class->set_cursor_visible = clutter_stage_x11_set_cursor_visible;
|
|
stage_class->set_offscreen = clutter_stage_x11_set_offscreen;
|
|
stage_class->set_title = clutter_stage_x11_set_title;
|
|
stage_class->set_user_resize = clutter_stage_x11_set_user_resize;
|
|
}
|
|
|
|
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;
|
|
|
|
CLUTTER_SET_PRIVATE_FLAGS(stage, CLUTTER_ACTOR_SYNC_MATRICES);
|
|
}
|
|
|
|
/**
|
|
* 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)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_STAGE_X11 (stage), None);
|
|
|
|
return CLUTTER_STAGE_X11 (stage)->xwin;
|
|
}
|
|
|
|
/**
|
|
* 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)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_STAGE_X11 (stage), NULL);
|
|
|
|
return CLUTTER_STAGE_X11 (stage)->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;
|
|
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_X11 (stage), FALSE);
|
|
g_return_val_if_fail (xwindow != None, FALSE);
|
|
|
|
stage_x11 = CLUTTER_STAGE_X11 (stage);
|
|
actor = CLUTTER_ACTOR (stage);
|
|
|
|
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)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
clutter_actor_unrealize (actor);
|
|
|
|
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;
|
|
}
|