244cacd14b
* Makefile.am: * clutter.pc.in: * clutter/Makefile.am: * clutter/clutter-backend-glx.c: * clutter/clutter-backend-glx.h: * clutter/clutter-event.c: * clutter/clutter-feature.c: * clutter/clutter-group.c: * clutter/clutter-main.c: * clutter/clutter-main.h: * clutter/clutter-private.h: * clutter/clutter-stage-glx.c: * clutter/clutter-stage-glx.h: * clutter/clutter-stage.c: * clutter/clutter-stage.h: * clutter/clutter-util.c: * clutter/clutter-util.h: * clutter/pango/pangoclutter-render.c: * configure.ac: * examples/Makefile.am: Initial work in supporting different GL backends (ie. GLX/EGL/DirectFB etc). Currently just GLX supported and now mostly self contained. * TODO: Add a note about caching glenables
899 lines
22 KiB
C
899 lines
22 KiB
C
/*
|
|
* Clutter.
|
|
*
|
|
* An OpenGL based 'interactive canvas' library.
|
|
*
|
|
* Authored By Matthew Allum <mallum@openedhand.com>
|
|
*
|
|
* Copyright (C) 2006 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.
|
|
*/
|
|
|
|
/**
|
|
* SECTION:clutter-stage
|
|
* @short_description: Top level visual element to which actors are placed.
|
|
*
|
|
* #ClutterStage is a top level 'window' on which child actors are placed
|
|
* and manipulated.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "clutter-stage.h"
|
|
#include "clutter-main.h"
|
|
#include "clutter-feature.h"
|
|
#include "clutter-color.h"
|
|
#include "clutter-util.h"
|
|
#include "clutter-marshal.h"
|
|
#include "clutter-enum-types.h"
|
|
#include "clutter-private.h"
|
|
#include "clutter-debug.h"
|
|
|
|
#include "clutter-stage-glx.h"
|
|
#include "clutter-backend-glx.h"
|
|
|
|
#include <GL/glx.h>
|
|
#include <GL/gl.h>
|
|
|
|
#include <gdk-pixbuf-xlib/gdk-pixbuf-xlib.h>
|
|
|
|
struct _ClutterStageBackend
|
|
{
|
|
XVisualInfo *xvisinfo;
|
|
Window xwin;
|
|
Pixmap xpixmap;
|
|
gint xwin_width, xwin_height; /* FIXME target_width / height */
|
|
GLXPixmap glxpixmap;
|
|
GLXContext gl_context;
|
|
gboolean is_foreign_xwin;
|
|
};
|
|
|
|
typedef struct
|
|
{
|
|
GSource source;
|
|
Display *display;
|
|
GPollFD event_poll_fd;
|
|
}
|
|
ClutterXEventSource;
|
|
|
|
typedef void (*ClutterXEventFunc) (XEvent *xev, gpointer user_data);
|
|
|
|
static gboolean
|
|
x_event_prepare (GSource *source,
|
|
gint *timeout)
|
|
{
|
|
Display *display = ((ClutterXEventSource*)source)->display;
|
|
|
|
*timeout = -1;
|
|
|
|
return XPending (display);
|
|
}
|
|
|
|
static gboolean
|
|
x_event_check (GSource *source)
|
|
{
|
|
ClutterXEventSource *display_source = (ClutterXEventSource*)source;
|
|
gboolean retval;
|
|
|
|
if (display_source->event_poll_fd.revents & G_IO_IN)
|
|
retval = XPending (display_source->display);
|
|
else
|
|
retval = FALSE;
|
|
|
|
return retval;
|
|
}
|
|
|
|
static gboolean
|
|
x_event_dispatch (GSource *source,
|
|
GSourceFunc callback,
|
|
gpointer user_data)
|
|
{
|
|
Display *display = ((ClutterXEventSource*)source)->display;
|
|
ClutterXEventFunc event_func = (ClutterXEventFunc) callback;
|
|
|
|
XEvent xev;
|
|
|
|
if (XPending (display))
|
|
{
|
|
XNextEvent (display, &xev);
|
|
|
|
if (event_func)
|
|
(*event_func) (&xev, user_data);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static const GSourceFuncs x_event_funcs = {
|
|
x_event_prepare,
|
|
x_event_check,
|
|
x_event_dispatch,
|
|
NULL
|
|
};
|
|
|
|
static void
|
|
translate_key_event (ClutterKeyEvent *event,
|
|
XEvent *xevent)
|
|
{
|
|
event->type = xevent->xany.type == KeyPress ? CLUTTER_KEY_PRESS
|
|
: CLUTTER_KEY_RELEASE;
|
|
event->time = xevent->xkey.time;
|
|
event->modifier_state = xevent->xkey.state; /* FIXME: handle modifiers */
|
|
event->hardware_keycode = xevent->xkey.keycode;
|
|
event->keyval = XKeycodeToKeysym(xevent->xkey.display,
|
|
xevent->xkey.keycode,
|
|
0 ); /* FIXME: index with modifiers */
|
|
}
|
|
|
|
static void
|
|
translate_button_event (ClutterButtonEvent *event,
|
|
XEvent *xevent)
|
|
{
|
|
/* FIXME: catch double click */
|
|
CLUTTER_NOTE (EVENT, " button event at %ix%i",
|
|
xevent->xbutton.x,
|
|
xevent->xbutton.y);
|
|
|
|
event->type = xevent->xany.type == ButtonPress ? CLUTTER_BUTTON_PRESS
|
|
: CLUTTER_BUTTON_RELEASE;
|
|
event->time = xevent->xbutton.time;
|
|
event->x = xevent->xbutton.x;
|
|
event->y = xevent->xbutton.y;
|
|
event->modifier_state = xevent->xbutton.state; /* includes button masks */
|
|
event->button = xevent->xbutton.button;
|
|
}
|
|
|
|
static void
|
|
translate_motion_event (ClutterMotionEvent *event,
|
|
XEvent *xevent)
|
|
{
|
|
event->type = CLUTTER_MOTION;
|
|
event->time = xevent->xbutton.time;
|
|
event->x = xevent->xmotion.x;
|
|
event->y = xevent->xmotion.y;
|
|
event->modifier_state = xevent->xmotion.state;
|
|
}
|
|
|
|
static void
|
|
clutter_dispatch_x_event (XEvent *xevent,
|
|
gpointer data)
|
|
{
|
|
ClutterMainContext *ctx = CLUTTER_CONTEXT ();
|
|
ClutterEvent event;
|
|
ClutterStage *stage = ctx->stage;
|
|
gboolean emit_input_event = FALSE;
|
|
|
|
switch (xevent->type)
|
|
{
|
|
case Expose:
|
|
{
|
|
XEvent foo_xev;
|
|
|
|
/* Cheap compress */
|
|
while (XCheckTypedWindowEvent(clutter_glx_display(),
|
|
xevent->xexpose.window,
|
|
Expose,
|
|
&foo_xev));
|
|
|
|
/* FIXME: need to make stage an 'actor' so can que
|
|
* a paint direct from there rather than hack here...
|
|
*/
|
|
clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
|
|
}
|
|
break;
|
|
case KeyPress:
|
|
translate_key_event ((ClutterKeyEvent *) &event, xevent);
|
|
g_signal_emit_by_name (stage, "key-press-event", &event);
|
|
emit_input_event = TRUE;
|
|
break;
|
|
case KeyRelease:
|
|
translate_key_event ((ClutterKeyEvent *) &event, xevent);
|
|
g_signal_emit_by_name (stage, "key-release-event", &event);
|
|
emit_input_event = TRUE;
|
|
break;
|
|
case ButtonPress:
|
|
translate_button_event ((ClutterButtonEvent *) &event, xevent);
|
|
g_signal_emit_by_name (stage, "button-press-event", &event);
|
|
emit_input_event = TRUE;
|
|
break;
|
|
case ButtonRelease:
|
|
translate_button_event ((ClutterButtonEvent *) &event, xevent);
|
|
g_signal_emit_by_name (stage, "button-release-event", &event);
|
|
emit_input_event = TRUE;
|
|
break;
|
|
case MotionNotify:
|
|
translate_motion_event ((ClutterMotionEvent *) &event, xevent);
|
|
g_signal_emit_by_name (stage, "motion-event", &event);
|
|
emit_input_event = TRUE;
|
|
break;
|
|
}
|
|
|
|
if (emit_input_event)
|
|
g_signal_emit_by_name (stage, "input-event", &event);
|
|
|
|
}
|
|
|
|
static void
|
|
events_init()
|
|
{
|
|
ClutterMainContext *clutter_context;
|
|
GMainContext *gmain_context;
|
|
int connection_number;
|
|
GSource *source;
|
|
ClutterXEventSource *display_source;
|
|
|
|
clutter_context = clutter_context_get_default ();
|
|
gmain_context = g_main_context_default ();
|
|
|
|
g_main_context_ref (gmain_context);
|
|
|
|
connection_number = ConnectionNumber (clutter_glx_display());
|
|
|
|
source = g_source_new ((GSourceFuncs *)&x_event_funcs,
|
|
sizeof (ClutterXEventSource));
|
|
|
|
display_source = (ClutterXEventSource *)source;
|
|
|
|
display_source->event_poll_fd.fd = connection_number;
|
|
display_source->event_poll_fd.events = G_IO_IN;
|
|
display_source->display = clutter_glx_display();
|
|
|
|
g_source_add_poll (source, &display_source->event_poll_fd);
|
|
g_source_set_can_recurse (source, TRUE);
|
|
|
|
g_source_set_callback (source,
|
|
(GSourceFunc) clutter_dispatch_x_event,
|
|
NULL /* no userdata */, NULL);
|
|
|
|
g_source_attach (source, gmain_context);
|
|
g_source_unref (source);
|
|
}
|
|
|
|
static void
|
|
sync_fullscreen (ClutterStage *stage)
|
|
{
|
|
Atom atom_WINDOW_STATE, atom_WINDOW_STATE_FULLSCREEN;
|
|
gboolean want_fullscreen;
|
|
|
|
atom_WINDOW_STATE
|
|
= XInternAtom(clutter_glx_display(), "_NET_WM_STATE", False);
|
|
atom_WINDOW_STATE_FULLSCREEN
|
|
= XInternAtom(clutter_glx_display(), "_NET_WM_STATE_FULLSCREEN",False);
|
|
|
|
g_object_get (stage, "fullscreen", &want_fullscreen, NULL);
|
|
|
|
if (want_fullscreen)
|
|
{
|
|
clutter_actor_set_size (CLUTTER_ACTOR(stage),
|
|
DisplayWidth(clutter_glx_display(),
|
|
clutter_glx_screen()),
|
|
DisplayHeight(clutter_glx_display(),
|
|
clutter_glx_screen()));
|
|
|
|
if (stage->backend->xwin != None)
|
|
XChangeProperty(clutter_glx_display(), stage->backend->xwin,
|
|
atom_WINDOW_STATE, XA_ATOM, 32,
|
|
PropModeReplace,
|
|
(unsigned char *)&atom_WINDOW_STATE_FULLSCREEN, 1);
|
|
}
|
|
else
|
|
{
|
|
if (stage->backend->xwin != None)
|
|
XDeleteProperty(clutter_glx_display(),
|
|
stage->backend->xwin, atom_WINDOW_STATE);
|
|
}
|
|
}
|
|
|
|
static void
|
|
sync_cursor (ClutterStage *stage)
|
|
{
|
|
gboolean hide_cursor;
|
|
|
|
if (stage->backend->xwin == None)
|
|
return;
|
|
|
|
g_object_get (stage, "hide-cursor", &hide_cursor, NULL);
|
|
|
|
/* FIXME: Use XFixesHideCursor */
|
|
|
|
if (hide_cursor)
|
|
{
|
|
XColor col;
|
|
Pixmap pix;
|
|
Cursor curs;
|
|
|
|
pix = XCreatePixmap (clutter_glx_display(),
|
|
stage->backend->xwin, 1, 1, 1);
|
|
memset (&col, 0, sizeof (col));
|
|
curs = XCreatePixmapCursor (clutter_glx_display(),
|
|
pix, pix, &col, &col, 1, 1);
|
|
XFreePixmap (clutter_glx_display(), pix);
|
|
XDefineCursor(clutter_glx_display(), stage->backend->xwin, curs);
|
|
}
|
|
else
|
|
{
|
|
XUndefineCursor(clutter_glx_display(), stage->backend->xwin);
|
|
}
|
|
}
|
|
|
|
/* FIXME -> CGL */
|
|
static void
|
|
frustum (GLfloat left,
|
|
GLfloat right,
|
|
GLfloat bottom,
|
|
GLfloat top,
|
|
GLfloat nearval,
|
|
GLfloat farval)
|
|
{
|
|
GLfloat x, y, a, b, c, d;
|
|
GLfloat m[16];
|
|
|
|
x = (2.0 * nearval) / (right - left);
|
|
y = (2.0 * nearval) / (top - bottom);
|
|
a = (right + left) / (right - left);
|
|
b = (top + bottom) / (top - bottom);
|
|
c = -(farval + nearval) / ( farval - nearval);
|
|
d = -(2.0 * farval * nearval) / (farval - nearval);
|
|
|
|
#define M(row,col) m[col*4+row]
|
|
M(0,0) = x; M(0,1) = 0.0F; M(0,2) = a; M(0,3) = 0.0F;
|
|
M(1,0) = 0.0F; M(1,1) = y; M(1,2) = b; M(1,3) = 0.0F;
|
|
M(2,0) = 0.0F; M(2,1) = 0.0F; M(2,2) = c; M(2,3) = d;
|
|
M(3,0) = 0.0F; M(3,1) = 0.0F; M(3,2) = -1.0F; M(3,3) = 0.0F;
|
|
#undef M
|
|
|
|
glMultMatrixf (m);
|
|
}
|
|
|
|
static void
|
|
perspective (GLfloat fovy,
|
|
GLfloat aspect,
|
|
GLfloat zNear,
|
|
GLfloat zFar)
|
|
{
|
|
GLfloat xmin, xmax, ymin, ymax;
|
|
|
|
ymax = zNear * tan (fovy * M_PI / 360.0);
|
|
ymin = -ymax;
|
|
xmin = ymin * aspect;
|
|
xmax = ymax * aspect;
|
|
|
|
frustum (xmin, xmax, ymin, ymax, zNear, zFar);
|
|
}
|
|
|
|
|
|
static void
|
|
sync_viewport (ClutterStage *stage)
|
|
{
|
|
glViewport (0, 0, stage->backend->xwin_width, stage->backend->xwin_height);
|
|
glMatrixMode (GL_PROJECTION);
|
|
glLoadIdentity ();
|
|
perspective (60.0f, 1.0f, 0.1f, 100.0f);
|
|
glMatrixMode (GL_MODELVIEW);
|
|
glLoadIdentity ();
|
|
|
|
/* Then for 2D like transform */
|
|
|
|
/* camera distance from screen, 0.5 * tan (FOV) */
|
|
#define DEFAULT_Z_CAMERA 0.866025404f
|
|
|
|
glTranslatef (-0.5f, -0.5f, -DEFAULT_Z_CAMERA);
|
|
glScalef (1.0f / stage->backend->xwin_width,
|
|
-1.0f / stage->backend->xwin_height,
|
|
1.0f / stage->backend->xwin_width);
|
|
glTranslatef (0.0f, -stage->backend->xwin_height, 0.0f);
|
|
}
|
|
|
|
static void
|
|
clutter_stage_glx_show (ClutterActor *self)
|
|
{
|
|
if (clutter_stage_glx_window (CLUTTER_STAGE(self)))
|
|
XMapWindow (clutter_glx_display(),
|
|
clutter_stage_glx_window (CLUTTER_STAGE(self)));
|
|
}
|
|
|
|
static void
|
|
clutter_stage_glx_hide (ClutterActor *self)
|
|
{
|
|
if (clutter_stage_glx_window (CLUTTER_STAGE(self)))
|
|
XUnmapWindow (clutter_glx_display(),
|
|
clutter_stage_glx_window (CLUTTER_STAGE(self)));
|
|
}
|
|
|
|
static void
|
|
clutter_stage_glx_unrealize (ClutterActor *actor)
|
|
{
|
|
ClutterStage *stage;
|
|
ClutterStagePrivate *priv;
|
|
ClutterStageBackend *backend;
|
|
gboolean want_offscreen;
|
|
|
|
stage = CLUTTER_STAGE(actor);
|
|
priv = stage->priv;
|
|
backend = stage->backend;
|
|
|
|
CLUTTER_MARK();
|
|
|
|
g_object_get (stage, "offscreen", &want_offscreen, NULL);
|
|
|
|
if (want_offscreen)
|
|
{
|
|
if (backend->glxpixmap)
|
|
{
|
|
glXDestroyGLXPixmap (clutter_glx_display(), backend->glxpixmap);
|
|
backend->glxpixmap = None;
|
|
}
|
|
|
|
if (backend->xpixmap)
|
|
{
|
|
XFreePixmap (clutter_glx_display(), backend->xpixmap);
|
|
backend->xpixmap = None;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!backend->is_foreign_xwin && backend->xwin != None)
|
|
{
|
|
XDestroyWindow (clutter_glx_display(), backend->xwin);
|
|
backend->xwin = None;
|
|
}
|
|
else
|
|
backend->xwin = None;
|
|
}
|
|
|
|
glXMakeCurrent(clutter_glx_display(), None, NULL);
|
|
if (backend->gl_context != None)
|
|
{
|
|
glXDestroyContext (clutter_glx_display(), backend->gl_context);
|
|
backend->gl_context = None;
|
|
}
|
|
}
|
|
|
|
static void
|
|
clutter_stage_glx_realize (ClutterActor *actor)
|
|
{
|
|
ClutterStage *stage;
|
|
ClutterStagePrivate *priv;
|
|
ClutterStageBackend *backend;
|
|
gboolean want_offscreen;
|
|
|
|
stage = CLUTTER_STAGE(actor);
|
|
|
|
priv = stage->priv;
|
|
backend = stage->backend;
|
|
|
|
CLUTTER_MARK();
|
|
|
|
g_object_get (stage, "offscreen", &want_offscreen, NULL);
|
|
|
|
if (want_offscreen)
|
|
{
|
|
int gl_attributes[] = {
|
|
GLX_RGBA,
|
|
GLX_RED_SIZE, 1,
|
|
GLX_GREEN_SIZE, 1,
|
|
GLX_BLUE_SIZE, 1,
|
|
0
|
|
};
|
|
|
|
if (backend->xvisinfo)
|
|
XFree(backend->xvisinfo);
|
|
|
|
backend->xvisinfo = glXChooseVisual (clutter_glx_display(),
|
|
clutter_glx_screen(),
|
|
gl_attributes);
|
|
if (!backend->xvisinfo)
|
|
{
|
|
g_critical ("Unable to find suitable GL visual.");
|
|
CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED);
|
|
return;
|
|
}
|
|
|
|
if (backend->gl_context)
|
|
glXDestroyContext (clutter_glx_display(), backend->gl_context);
|
|
|
|
backend->xpixmap = XCreatePixmap (clutter_glx_display(),
|
|
clutter_glx_root_window(),
|
|
backend->xwin_width,
|
|
backend->xwin_height,
|
|
backend->xvisinfo->depth);
|
|
|
|
backend->glxpixmap = glXCreateGLXPixmap(clutter_glx_display(),
|
|
backend->xvisinfo,
|
|
backend->xpixmap);
|
|
sync_fullscreen (stage);
|
|
|
|
/* indirect */
|
|
backend->gl_context = glXCreateContext (clutter_glx_display(),
|
|
backend->xvisinfo,
|
|
0,
|
|
False);
|
|
|
|
glXMakeCurrent(clutter_glx_display(),
|
|
backend->glxpixmap, backend->gl_context);
|
|
|
|
#if 0
|
|
/* Debug code for monitoring a off screen pixmap via window */
|
|
{
|
|
Colormap cmap;
|
|
XSetWindowAttributes swa;
|
|
|
|
cmap = XCreateColormap(clutter_glx_display(),
|
|
clutter_glx_root_window(),
|
|
backend->xvisinfo->visual, AllocNone);
|
|
|
|
/* create a window */
|
|
swa.colormap = cmap;
|
|
|
|
foo_win = XCreateWindow(clutter_glx_display(),
|
|
clutter_glx_root_window(),
|
|
0, 0,
|
|
backend->xwin_width, backend->xwin_height,
|
|
0,
|
|
backend->xvisinfo->depth,
|
|
InputOutput,
|
|
backend->xvisinfo->visual,
|
|
CWColormap, &swa);
|
|
|
|
XMapWindow(clutter_glx_display(), foo_win);
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
int gl_attributes[] =
|
|
{
|
|
GLX_RGBA,
|
|
GLX_DOUBLEBUFFER,
|
|
GLX_RED_SIZE, 1,
|
|
GLX_GREEN_SIZE, 1,
|
|
GLX_BLUE_SIZE, 1,
|
|
GLX_STENCIL_SIZE, 1,
|
|
0
|
|
};
|
|
|
|
if (backend->xvisinfo)
|
|
XFree(backend->xvisinfo);
|
|
|
|
if (backend->xvisinfo == None)
|
|
backend->xvisinfo = glXChooseVisual (clutter_glx_display(),
|
|
clutter_glx_screen(),
|
|
gl_attributes);
|
|
if (!backend->xvisinfo)
|
|
{
|
|
g_critical ("Unable to find suitable GL visual.");
|
|
CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED);
|
|
return;
|
|
}
|
|
|
|
if (backend->xwin == None)
|
|
backend->xwin = XCreateSimpleWindow(clutter_glx_display(),
|
|
clutter_glx_root_window(),
|
|
0, 0,
|
|
backend->xwin_width, backend->xwin_height,
|
|
0, 0,
|
|
WhitePixel(clutter_glx_display(),
|
|
clutter_glx_screen()));
|
|
XSelectInput(clutter_glx_display(),
|
|
backend->xwin,
|
|
StructureNotifyMask
|
|
|ExposureMask
|
|
/* FIXME: we may want to eplicity enable MotionMask */
|
|
|PointerMotionMask
|
|
|KeyPressMask
|
|
|KeyReleaseMask
|
|
|ButtonPressMask
|
|
|ButtonReleaseMask
|
|
|PropertyChangeMask);
|
|
|
|
sync_fullscreen (stage);
|
|
sync_cursor (stage);
|
|
|
|
if (backend->gl_context)
|
|
glXDestroyContext (clutter_glx_display(), backend->gl_context);
|
|
|
|
backend->gl_context = glXCreateContext (clutter_glx_display(),
|
|
backend->xvisinfo,
|
|
0,
|
|
True);
|
|
|
|
if (backend->gl_context == None)
|
|
{
|
|
g_critical ("Unable to create suitable GL context.");
|
|
CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED);
|
|
return;
|
|
}
|
|
|
|
glXMakeCurrent(clutter_glx_display(), backend->xwin, backend->gl_context);
|
|
}
|
|
|
|
CLUTTER_NOTE (GL,
|
|
"\n"
|
|
"===========================================\n"
|
|
"GL_VENDOR: %s\n"
|
|
"GL_RENDERER: %s\n"
|
|
"GL_VERSION: %s\n"
|
|
"GL_EXTENSIONS: %s\n"
|
|
"Is direct: %s\n"
|
|
"===========================================\n",
|
|
glGetString (GL_VENDOR),
|
|
glGetString (GL_RENDERER),
|
|
glGetString (GL_VERSION),
|
|
glGetString (GL_EXTENSIONS),
|
|
glXIsDirect(clutter_glx_display(), backend->gl_context) ? "yes" : "no"
|
|
);
|
|
|
|
sync_viewport (stage);
|
|
}
|
|
|
|
static void
|
|
clutter_stage_glx_paint (ClutterActor *self)
|
|
{
|
|
ClutterStage *stage = CLUTTER_STAGE(self);
|
|
ClutterColor stage_color;
|
|
static GTimer *timer = NULL;
|
|
static guint timer_n_frames = 0;
|
|
|
|
static ClutterActorClass *parent_class = NULL;
|
|
|
|
CLUTTER_NOTE (PAINT, " Redraw enter");
|
|
|
|
if (parent_class == NULL)
|
|
parent_class = g_type_class_peek_parent (CLUTTER_STAGE_GET_CLASS(stage));
|
|
|
|
if (clutter_want_fps ())
|
|
{
|
|
if (!timer)
|
|
timer = g_timer_new ();
|
|
}
|
|
|
|
clutter_stage_get_color (stage, &stage_color);
|
|
|
|
glClearColor(((float) stage_color.red / 0xff * 1.0),
|
|
((float) stage_color.green / 0xff * 1.0),
|
|
((float) stage_color.blue / 0xff * 1.0),
|
|
0.0);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
|
glDisable(GL_LIGHTING);
|
|
glDisable(GL_DEPTH_TEST);
|
|
|
|
parent_class->paint (self);
|
|
|
|
if (clutter_stage_glx_window (stage))
|
|
{
|
|
clutter_feature_wait_for_vblank ();
|
|
glXSwapBuffers(clutter_glx_display(), clutter_stage_glx_window (stage));
|
|
}
|
|
else
|
|
{
|
|
glXWaitGL();
|
|
CLUTTER_GLERR();
|
|
}
|
|
|
|
if (clutter_want_fps ())
|
|
{
|
|
timer_n_frames++;
|
|
|
|
if (g_timer_elapsed (timer, NULL) >= 1.0)
|
|
{
|
|
g_print ("*** FPS: %i ***\n", timer_n_frames);
|
|
timer_n_frames = 0;
|
|
g_timer_start (timer);
|
|
}
|
|
}
|
|
|
|
CLUTTER_NOTE (PAINT, " Redraw leave");
|
|
}
|
|
|
|
static void
|
|
clutter_stage_glx_allocate_coords (ClutterActor *self,
|
|
ClutterActorBox *box)
|
|
{
|
|
/* Do nothing, just stop group_allocate getting called */
|
|
|
|
/* TODO: sync up with any configure events from WM ?? */
|
|
return;
|
|
}
|
|
|
|
static void
|
|
clutter_stage_glx_request_coords (ClutterActor *self,
|
|
ClutterActorBox *box)
|
|
{
|
|
ClutterStage *stage;
|
|
ClutterStageBackend *backend;
|
|
gint new_width, new_height;
|
|
|
|
stage = CLUTTER_STAGE (self);
|
|
backend = stage->backend;
|
|
|
|
/* FIXME: some how have X configure_notfiys call this ?
|
|
*/
|
|
|
|
new_width = ABS(box->x2 - box->x1);
|
|
new_height = ABS(box->y2 - box->y1);
|
|
|
|
if (new_width != backend->xwin_width || new_height != backend->xwin_height)
|
|
{
|
|
backend->xwin_width = new_width;
|
|
backend->xwin_height = new_height;
|
|
|
|
if (backend->xwin != None)
|
|
XResizeWindow (clutter_glx_display(),
|
|
backend->xwin,
|
|
backend->xwin_width,
|
|
backend->xwin_height);
|
|
|
|
if (backend->xpixmap != None)
|
|
{
|
|
/* Need to recreate to resize */
|
|
clutter_actor_unrealize(self);
|
|
clutter_actor_realize(self);
|
|
}
|
|
|
|
sync_viewport (stage);
|
|
}
|
|
|
|
if (backend->xwin != None) /* Do we want to bother ? */
|
|
XMoveWindow (clutter_glx_display(),
|
|
backend->xwin,
|
|
box->x1,
|
|
box->y1);
|
|
}
|
|
|
|
static void
|
|
clutter_stage_glx_dispose (GObject *object)
|
|
{
|
|
#if 0
|
|
ClutterStage *self = CLUTTER_STAGE (object);
|
|
|
|
if (self->backend->xwin)
|
|
clutter_actor_unrealize (CLUTTER_ACTOR (self));
|
|
|
|
G_OBJECT_CLASS (clutter_stage_parent_class)->dispose (object);
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
clutter_stage_glx_finalize (GObject *object)
|
|
{
|
|
#if 0
|
|
G_OBJECT_CLASS (clutter_stage_parent_class)->finalize (object);
|
|
#endif
|
|
}
|
|
|
|
void
|
|
clutter_stage_backend_init_vtable (ClutterStageVTable *vtable)
|
|
{
|
|
vtable->show = clutter_stage_glx_show;
|
|
vtable->hide = clutter_stage_glx_hide;
|
|
vtable->realize = clutter_stage_glx_realize;
|
|
vtable->unrealize = clutter_stage_glx_unrealize;
|
|
vtable->paint = clutter_stage_glx_paint;
|
|
vtable->request_coords = clutter_stage_glx_request_coords;
|
|
vtable->allocate_coords = clutter_stage_glx_allocate_coords;
|
|
|
|
vtable->sync_fullscreen = sync_fullscreen;
|
|
vtable->sync_cursor = sync_cursor;
|
|
vtable->sync_viewport = sync_viewport;
|
|
|
|
}
|
|
|
|
ClutterStageBackend*
|
|
clutter_stage_backend_init (ClutterStage *stage)
|
|
{
|
|
ClutterStageBackend *backend;
|
|
|
|
backend = g_new0(ClutterStageBackend, 1);
|
|
|
|
backend->xwin_width = 100;
|
|
backend->xwin_height = 100;
|
|
|
|
/* Maybe better somewhere else */
|
|
events_init ();
|
|
|
|
return backend;
|
|
}
|
|
|
|
/**
|
|
* clutter_stage_glx_get_xwindow
|
|
* @stage: A #ClutterStage
|
|
*
|
|
* Get the stage's underlying x window ID.
|
|
*
|
|
* Return Value: Stage X Window XID
|
|
*
|
|
* Since: 0.3
|
|
**/
|
|
Window
|
|
clutter_stage_glx_window (ClutterStage *stage)
|
|
{
|
|
return stage->backend->xwin;
|
|
}
|
|
|
|
/**
|
|
* clutter_stage_set_xwindow_foreign
|
|
* @stage: A #ClutterStage
|
|
* @xid: A preexisting X Window ID
|
|
*
|
|
* Target the #ClutterStage to use an existing external X Window.
|
|
*
|
|
* Return Value: TRUE if foreign window valid, FALSE otherwise
|
|
*
|
|
* Since: 0.3
|
|
**/
|
|
gboolean
|
|
clutter_stage_glx_set_window_foreign (ClutterStage *stage,
|
|
Window xid)
|
|
{
|
|
/* For screensavers via XSCREENSAVER_WINDOW env var.
|
|
* Also for toolkit binding.
|
|
*/
|
|
gint x,y;
|
|
guint width, height, border, depth;
|
|
Window root_return;
|
|
Status status;
|
|
ClutterGeometry geom;
|
|
|
|
clutter_glx_trap_x_errors();
|
|
|
|
status = XGetGeometry (clutter_glx_display(),
|
|
xid,
|
|
&root_return,
|
|
&x,
|
|
&y,
|
|
&width,
|
|
&height,
|
|
&border,
|
|
&depth);
|
|
|
|
if (clutter_glx_untrap_x_errors() || !status
|
|
|| width == 0 || height == 0 || depth != stage->backend->xvisinfo->depth)
|
|
return FALSE;
|
|
|
|
clutter_actor_unrealize (CLUTTER_ACTOR(stage));
|
|
|
|
stage->backend->xwin = xid;
|
|
|
|
geom.x = x;
|
|
geom.y = y;
|
|
|
|
geom.width = stage->backend->xwin_width = width;
|
|
geom.height = stage->backend->xwin_height = height;
|
|
|
|
clutter_actor_set_geometry (CLUTTER_ACTOR(stage), &geom);
|
|
|
|
clutter_actor_realize (CLUTTER_ACTOR(stage));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* clutter_stage_glx_get_xvisual
|
|
* @stage: A #ClutterStage
|
|
*
|
|
* Get the stage's XVisualInfo.
|
|
*
|
|
* Return Value: The stage's XVisualInfo
|
|
*
|
|
* Since: 0.3
|
|
**/
|
|
const XVisualInfo*
|
|
clutter_stage_glx_get_visual (ClutterStage *stage)
|
|
{
|
|
return stage->backend->xvisinfo;
|
|
}
|