2007-01-23 Matthew Allum <mallum@openedhand.com>

* 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
This commit is contained in:
Matthew Allum 2007-01-23 20:29:11 +00:00
parent 685c583d51
commit 244cacd14b
22 changed files with 1409 additions and 1140 deletions

View File

@ -1,3 +1,31 @@
2007-01-23 Matthew Allum <mallum@openedhand.com>
* 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
2007-01-23 Tomas Frydrych <tf@openedhand.com>
* clutter/clutter-fixed.c:

View File

@ -1,8 +1,8 @@
SUBDIRS=clutter doc examples
pcfiles = clutter-@CLUTTER_MAJORMINOR@.pc
pcfiles = clutter-@CLUTTER_FLAVOUR@-@CLUTTER_MAJORMINOR@.pc
%-@CLUTTER_MAJORMINOR@.pc: %.pc
%-@CLUTTER_FLAVOUR@-@CLUTTER_MAJORMINOR@.pc: %.pc
cp $< $@
pkgconfig_DATA = $(pcfiles)

2
TODO
View File

@ -19,6 +19,8 @@ Optimisations
- Display lists.
- Custom source rather than idle handler for paints ?
- General oprofiling.
- GL state cache - avoid so much expensive glenable/disable calling
during paint (Also GL does not check if a state is already set)
Other
==

View File

@ -3,9 +3,9 @@ exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include
Name: clutter-@CLUTTER_MAJORMINOR@
Name: clutter-@CLUTTER_FLAVOUR@-@CLUTTER_MAJORMINOR@
Description: Clutter library
Version: @VERSION@
Libs: -L${libdir} -lclutter-@CLUTTER_MAJORMINOR@
Cflags: -I${includedir}/clutter-@CLUTTER_MAJORMINOR@
Libs: -L${libdir} -lclutter-@CLUTTER_FLAVOUR@-@CLUTTER_MAJORMINOR@
Cflags: -I${includedir}/clutter-@CLUTTER_FLAVOUR@-@CLUTTER_MAJORMINOR@
Requires: pangoft2 glib-2.0 >= 2.8 gthread-2.0 gdk-pixbuf-2.0 gdk-pixbuf-xlib-2.0

View File

@ -32,6 +32,10 @@ source_h = \
$(srcdir)/clutter-version.h \
$(srcdir)/clutter-main.h
backend_h = \
$(srcdir)/clutter-stage-glx.h \
$(srcdir)/clutter-backend-glx.h
clutter-marshal.h: stamp-clutter-marshal.h
@true
stamp-clutter-marshal.h: clutter-marshal.list
@ -113,11 +117,18 @@ source_c = clutter-main.c \
clutter-media.c \
clutter-enum-types.c
backend_c = \
$(srcdir)/clutter-stage-glx.c \
$(srcdir)/clutter-backend-glx.c
source_h_priv = clutter-debug.h clutter-private.h
libclutter_@CLUTTER_MAJORMINOR@_la_SOURCES = $(MARSHALFILES) \
libclutter_@CLUTTER_FLAVOUR@_@CLUTTER_MAJORMINOR@_la_SOURCES = \
$(MARSHALFILES) \
$(source_c) \
$(source_h) \
$(backend_c) \
$(backend_h) \
$(source_h_priv)
INCLUDES = \
@ -130,17 +141,23 @@ INCLUDES = \
-DG_LOG_DOMAIN=\"Clutter\" \
$(GCC_FLAGS) \
$(CLUTTER_CFLAGS) \
$(CLUTTER_DEBUG_CFLAGS)
$(CLUTTER_DEBUG_CFLAGS) \
-DCLUTTER_BACKEND_GLX
lib_LTLIBRARIES = libclutter-@CLUTTER_MAJORMINOR@.la
lib_LTLIBRARIES = libclutter-@CLUTTER_FLAVOUR@-@CLUTTER_MAJORMINOR@.la
libclutter_@CLUTTER_MAJORMINOR@_la_LIBADD = \
libclutter_@CLUTTER_FLAVOUR@_@CLUTTER_MAJORMINOR@_la_LIBADD = \
@CLUTTER_LIBS@ $(top_builddir)/clutter/pango/libpangoclutter.la
libclutter_@CLUTTER_MAJORMINOR@_la_LDFLAGS = @CLUTTER_LT_LDFLAGS@
libclutter_@CLUTTER_MAJORMINOR@_la_DEPENDENCIES = \
libclutter_@CLUTTER_FLAVOUR@_@CLUTTER_MAJORMINOR@_la_LDFLAGS = \
@CLUTTER_LT_LDFLAGS@
libclutter_@CLUTTER_FLAVOUR@_@CLUTTER_MAJORMINOR@_la_DEPENDENCIES = \
$(top_builddir)/clutter/pango/libpangoclutter.la
clutterheadersdir = $(includedir)/clutter-@CLUTTER_MAJORMINOR@/clutter
clutterheadersdir = \
$(includedir)/clutter-@CLUTTER_FLAVOUR@-@CLUTTER_MAJORMINOR@/clutter
clutterheaders_HEADERS = $(source_h) \
clutter-marshal.h \
clutter-enum-types.h \

View File

@ -0,0 +1,173 @@
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <GL/glx.h>
#include <GL/gl.h>
#include <glib.h>
#include "clutter-main.h"
static Display *_xdpy = NULL;
static Window _xwin_root;
static int _xscreen;
static gchar *clutter_display_name = NULL;
static int clutter_screen = 0;
static int TrappedErrorCode = 0;
static int (*old_error_handler) (Display *, XErrorEvent *);
static int
error_handler(Display *xdpy,
XErrorEvent *error)
{
TrappedErrorCode = error->error_code;
return 0;
}
/**
* clutter_util_trap_x_errors:
*
* Trap X errors so they don't cause an abort.
*/
void
clutter_glx_trap_x_errors(void)
{
TrappedErrorCode = 0;
old_error_handler = XSetErrorHandler(error_handler);
}
/**
* clutter_util_untrap_x_errors:
*
* Stop trapping X errors.
*
* Return value: 0 if there was no error, or the last X error that occurred.
*/
int
clutter_glx_untrap_x_errors(void)
{
XSetErrorHandler(old_error_handler);
return TrappedErrorCode;
}
/**
* clutter_glx_display:
*
* Retrieves the X display that Clutter is using
*
* Return value: A pointer to an X Display structure.
*/
Display*
clutter_glx_display (void)
{
return _xdpy;
}
/**
* clutter_glx_screen:
*
* Retrieves the X screen that Clutter is using.
*
* Return value: the X screen ID
*/
int
clutter_glx_screen (void)
{
return _xscreen;
}
/**
* clutter_glx_root_window:
*
* FIXME
*
* Return value: FIXME
*/
Window
clutter_glx_root_window (void)
{
return _xwin_root;
}
static GOptionEntry clutter_glx_args[] = {
{ "display", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_STRING, &clutter_display_name,
"X display to use", "DISPLAY" },
{ "screen", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_INT, &clutter_screen,
"X screen to use", "SCREEN" },
{ NULL, }
};
static gboolean
pre_parse_hook (GOptionContext *context,
GOptionGroup *group,
gpointer data,
GError **error)
{
const char *env_string;
env_string = g_getenv ("DISPLAY");
if (env_string)
{
clutter_display_name = g_strdup (env_string);
env_string = NULL;
}
return TRUE;
}
static gboolean
post_parse_hook (GOptionContext *context,
GOptionGroup *group,
gpointer data,
GError **error)
{
_xdpy = XOpenDisplay (clutter_display_name);
if (_xdpy)
{
if (clutter_screen == 0)
_xscreen = DefaultScreen (_xdpy);
else
{
Screen *xscreen;
xscreen = ScreenOfDisplay (_xdpy, clutter_screen);
_xscreen = XScreenNumberOfScreen (xscreen);
}
_xwin_root = RootWindow (_xdpy, _xscreen);
/* we don't need it anymore */
g_free (clutter_display_name);
}
else
{
g_set_error (error,
clutter_init_error_quark (),
CLUTTER_INIT_ERROR_BACKEND,
"Unable to connect to X Server DISPLAY.");
return FALSE;
}
return TRUE;
}
gboolean
clutter_backend_init (GOptionContext *context)
{
GOptionGroup *group;
group = g_option_group_new ("clutter-glx",
"Clutter GLX Options",
"Show Clutter GLX Options",
NULL,
NULL);
g_option_group_set_parse_hooks (group, pre_parse_hook, post_parse_hook);
g_option_group_add_entries (group, clutter_glx_args);
g_option_context_add_group (context, group);
return TRUE;
}

View File

@ -0,0 +1,54 @@
/*
* 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.
*/
#ifndef _HAVE_CLUTTER_GLX_H
#define _HAVE_CLUTTER_GLX_H
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <GL/glx.h>
#include <GL/gl.h>
gboolean
clutter_backend_init (GOptionContext *context) G_GNUC_INTERNAL;
void
clutter_glx_trap_x_errors(void);
int
clutter_glx_untrap_x_errors(void);
Display*
clutter_glx_display (void);
int
clutter_glx_screen (void);
Window
clutter_glx_root_window (void);
#endif

View File

@ -29,9 +29,6 @@
#include "clutter-event.h"
#include <X11/Xlib.h>
#include <X11/Xatom.h>
/**
* clutter_event_type:
* @event: a #ClutterEvent

View File

@ -249,15 +249,15 @@ clutter_feature_init (void)
__features->features_set = FALSE; /* don't rely on zero-ing */
}
if (!clutter_xdisplay ())
if (!clutter_glx_display ())
return;
if (__features->features_set)
return;
gl_extensions = (const gchar*) glGetString (GL_EXTENSIONS);
glx_extensions = glXQueryExtensionsString (clutter_xdisplay (),
clutter_xscreen ());
glx_extensions = glXQueryExtensionsString (clutter_glx_display (),
clutter_glx_screen ());
if (check_gl_extension ("GL_ARB_texture_rectangle", gl_extensions)
|| check_gl_extension ("GL_EXT_texture_rectangle", gl_extensions))

View File

@ -36,6 +36,7 @@
#include "clutter-group.h"
#include "clutter-main.h"
#include "clutter-private.h"
#include "clutter-marshal.h"
#include "clutter-enum-types.h"

View File

@ -43,12 +43,16 @@
#include "clutter-private.h"
#include "clutter-debug.h"
#ifdef CLUTTER_BACKEND_GLX
#include <clutter/clutter-backend-glx.h>
#endif
static ClutterMainContext *ClutterCntx = NULL;
static gboolean clutter_is_initialized = FALSE;
static gboolean clutter_show_fps = FALSE;
static gboolean clutter_fatal_warnings = FALSE;
static gchar *clutter_display_name = NULL;
static gchar *clutter_vblank_name = NULL;
static int clutter_screen = 0;
guint clutter_debug_flags = 0; /* global clutter debug flag */
@ -66,210 +70,8 @@ static const GDebugKey clutter_debug_keys[] = {
};
#endif /* CLUTTER_ENABLE_DEBUG */
typedef struct
{
GSource source;
Display *display;
GPollFD event_poll_fd;
}
ClutterXEventSource;
typedef void (*ClutterXEventFunc) (XEvent *xev, gpointer user_data);
static ClutterMainContext *ClutterCntx = NULL;
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(ctx->xdpy,
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_context->xdpy);
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_context->xdpy;
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 gboolean
gboolean
clutter_want_fps (void)
{
return clutter_show_fps;
@ -291,60 +93,8 @@ clutter_redraw (void)
{
ClutterMainContext *ctx = CLUTTER_CONTEXT();
ClutterStage *stage = ctx->stage;
ClutterColor stage_color;
static GTimer *timer = NULL;
static guint timer_n_frames = 0;
/* FIXME: Should move all this into stage...
*/
CLUTTER_NOTE (PAINT, " Redraw enter");
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);
clutter_actor_paint (CLUTTER_ACTOR (stage));
if (clutter_stage_get_xwindow (stage))
{
clutter_feature_wait_for_vblank ();
glXSwapBuffers(ctx->xdpy, clutter_stage_get_xwindow (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");
clutter_actor_paint (CLUTTER_ACTOR(stage));
}
/**
@ -444,50 +194,6 @@ clutter_threads_leave (void)
g_mutex_unlock (context->gl_lock);
}
/**
* clutter_xdisplay:
*
* Retrieves the X display that Clutter is using
*
* Return value: A pointer to an X Display structure.
*/
Display*
clutter_xdisplay (void)
{
ClutterMainContext *context = CLUTTER_CONTEXT ();
return context->xdpy;
}
/**
* clutter_xscreen:
*
* Retrieves the X screen that Clutter is using.
*
* Return value: the X screen ID
*/
int
clutter_xscreen (void)
{
ClutterMainContext *context = CLUTTER_CONTEXT ();
return context->xscreen;
}
/**
* clutter_root_xwindow:
*
* FIXME
*
* Return value: FIXME
*/
Window
clutter_root_xwindow (void)
{
ClutterMainContext *context = CLUTTER_CONTEXT ();
return context->xwin_root;
}
/**
* clutter_want_debug:
@ -572,10 +278,6 @@ clutter_arg_no_debug_cb (const char *key,
#endif /* CLUTTER_ENABLE_DEBUG */
static GOptionEntry clutter_args[] = {
{ "display", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_STRING, &clutter_display_name,
"X display to use", "DISPLAY" },
{ "screen", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_INT, &clutter_screen,
"X screen to use", "SCREEN" },
{ "clutter-show-fps", 0, 0, G_OPTION_ARG_NONE, &clutter_show_fps,
"Show frames per second", NULL },
{ "clutter-vblank", 0, 0, G_OPTION_ARG_STRING, &clutter_vblank_name,
@ -606,18 +308,6 @@ pre_parse_hook (GOptionContext *context,
if (clutter_is_initialized)
return TRUE;
#if 0
/* XXX - this shows a warning with newer releases of GLib,
* as we use GOption in order to get here, and GOption uses
* the slice allocator and other GLib stuff. so, either we
* move the thread init inside clutter_init() directly or
* we remove this call altogether, and let the applications
* deal with threading, as they are supposed to do anyway.
*/
if (!g_thread_supported ())
g_thread_init (NULL);
#endif
g_type_init ();
#ifdef CLUTTER_ENABLE_DEBUG
@ -643,13 +333,6 @@ pre_parse_hook (GOptionContext *context,
if (env_string)
clutter_show_fps = TRUE;
env_string = g_getenv ("DISPLAY");
if (env_string)
{
clutter_display_name = g_strdup (env_string);
env_string = NULL;
}
return TRUE;
}
@ -680,32 +363,6 @@ post_parse_hook (GOptionContext *context,
clutter_context->main_loops = NULL;
clutter_context->main_loop_level = 0;
/* either we got this with the DISPLAY envvar or via the
* --display command line switch; if both failed, then
* we'll fail later when we return in clutter_init()
*/
if (clutter_display_name)
clutter_context->xdpy = XOpenDisplay (clutter_display_name);
if (clutter_context->xdpy)
{
if (clutter_screen == 0)
clutter_context->xscreen = DefaultScreen (clutter_context->xdpy);
else
{
Screen *xscreen;
xscreen = ScreenOfDisplay (clutter_context->xdpy, clutter_screen);
clutter_context->xscreen = XScreenNumberOfScreen (xscreen);
}
clutter_context->xwin_root = RootWindow (clutter_context->xdpy,
clutter_context->xscreen);
/* we don't need it anymore */
g_free (clutter_display_name);
}
clutter_context->font_map = PANGO_FT2_FONT_MAP (pango_ft2_font_map_new ());
pango_ft2_font_map_set_resolution (clutter_context->font_map, 96.0, 96.0);
@ -745,34 +402,6 @@ clutter_get_option_group (void)
return group;
}
static gboolean
clutter_parse_args (int *argc,
char ***argv)
{
GOptionContext *option_context;
GOptionGroup *clutter_group;
GError *error = NULL;
if (clutter_is_initialized)
return TRUE;
option_context = g_option_context_new (NULL);
g_option_context_set_ignore_unknown_options (option_context, TRUE);
g_option_context_set_help_enabled (option_context, FALSE);
clutter_group = clutter_get_option_group ();
g_option_context_set_main_group (option_context, clutter_group);
if (!g_option_context_parse (option_context, argc, argv, &error))
{
g_warning ("%s", error->message);
g_error_free (error);
}
g_option_context_free (option_context);
return TRUE;
}
GQuark
clutter_init_error_quark (void)
{
@ -855,17 +484,11 @@ clutter_init_with_args (int *argc,
if (!g_thread_supported ())
g_thread_init (NULL);
if (!XInitThreads())
{
g_set_error (error, clutter_init_error_quark (),
CLUTTER_INIT_ERROR_THREADS,
"Unable to initialise the X threading");
return CLUTTER_INIT_ERROR_THREADS;
}
group = clutter_get_option_group ();
context = g_option_context_new (parameter_string);
clutter_backend_init (context);
g_option_context_add_group (context, group);
if (entries)
@ -881,15 +504,6 @@ clutter_init_with_args (int *argc,
return CLUTTER_INIT_ERROR_INTERNAL;
clutter_context = clutter_context_get_default ();
if (!clutter_context->xdpy)
{
g_set_error (error, clutter_init_error_quark (),
CLUTTER_INIT_ERROR_DISPLAY,
"Unable to connect to X DISPLAY. You should either "
"set the DISPLAY environment variable or use the "
"--display command line switch");
return CLUTTER_INIT_ERROR_DISPLAY;
}
stage_error = NULL;
if (!clutter_stage_init (clutter_context, &stage_error))
@ -898,18 +512,42 @@ clutter_init_with_args (int *argc,
return CLUTTER_INIT_ERROR_INTERNAL;
}
/* At least GL 1.2 is needed for CLAMP_TO_EDGE */
if (!is_gl_version_at_least_12 ())
return CLUTTER_INIT_SUCCESS;
}
static gboolean
clutter_parse_args (int *argc,
char ***argv)
{
GOptionContext *option_context;
GOptionGroup *clutter_group;
GError *error = NULL;
gboolean ret = TRUE;
if (clutter_is_initialized)
return TRUE;
option_context = g_option_context_new (NULL);
g_option_context_set_ignore_unknown_options (option_context, TRUE);
g_option_context_set_help_enabled (option_context, FALSE);
/* Initiate any command line options from the backend */
clutter_backend_init (option_context);
clutter_group = clutter_get_option_group ();
g_option_context_set_main_group (option_context, clutter_group);
if (!g_option_context_parse (option_context, argc, argv, &error))
{
g_set_error (error, clutter_init_error_quark (),
CLUTTER_INIT_ERROR_OPENGL,
"Clutter needs at least version 1.2 of OpenGL");
return CLUTTER_INIT_ERROR_OPENGL;
g_warning ("%s", error->message);
g_error_free (error);
ret = FALSE;
}
events_init ();
g_option_context_free (option_context);
return CLUTTER_INIT_SUCCESS;
return ret;
}
/**
@ -937,20 +575,10 @@ clutter_init (int *argc,
if (!g_thread_supported ())
g_thread_init (NULL);
if (!XInitThreads())
return CLUTTER_INIT_ERROR_THREADS;
clutter_parse_args (argc, argv);
if (clutter_parse_args (argc, argv) == FALSE)
return CLUTTER_INIT_ERROR_INTERNAL;
context = clutter_context_get_default ();
if (!context->xdpy)
{
g_critical ("Unable to connect to X DISPLAY. You should either "
"set the DISPLAY environment variable or use the "
"--display command line switch");
return CLUTTER_INIT_ERROR_DISPLAY;
}
stage_error = NULL;
if (!clutter_stage_init (context, &stage_error))
@ -960,14 +588,15 @@ clutter_init (int *argc,
return CLUTTER_INIT_ERROR_INTERNAL;
}
#if 0
/* FIXME: move to backend */
/* At least GL 1.2 is needed for CLAMP_TO_EDGE */
if (!is_gl_version_at_least_12 ())
{
g_critical ("Clutter needs at least version 1.2 of OpenGL");
return CLUTTER_INIT_ERROR_OPENGL;
}
events_init ();
#endif
return 1;
}

View File

@ -29,11 +29,6 @@
#include <clutter/clutter-actor.h>
#include <clutter/clutter-stage.h>
#include <X11/Xlib.h>
#include <GL/glx.h>
#include <GL/gl.h>
G_BEGIN_DECLS
#define CLUTTER_INIT_ERROR (clutter_init_error_quark ())
@ -42,7 +37,7 @@ typedef enum {
CLUTTER_INIT_SUCCESS = 1,
CLUTTER_INIT_ERROR_UNKOWN = 0,
CLUTTER_INIT_ERROR_THREADS = -1,
CLUTTER_INIT_ERROR_DISPLAY = -2,
CLUTTER_INIT_ERROR_BACKEND = -2,
CLUTTER_INIT_ERROR_INTERNAL = -3,
CLUTTER_INIT_ERROR_OPENGL = -4
} ClutterInitError;
@ -64,10 +59,8 @@ void clutter_main (void);
void clutter_main_quit (void);
gint clutter_main_level (void);
void clutter_redraw (void);
Display * clutter_xdisplay (void);
gint clutter_xscreen (void);
Window clutter_root_xwindow (void);
gboolean clutter_want_debug (void);
gboolean clutter_want_fps (void);
void clutter_threads_enter (void);
void clutter_threads_leave (void);

View File

@ -32,12 +32,7 @@
#include <unistd.h>
#include <math.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>
#include <GL/glx.h>
#include <GL/gl.h>
#include <GL/gl.h> /* Togo */
#include <glib.h>
@ -49,11 +44,6 @@ typedef struct _ClutterMainContext ClutterMainContext;
struct _ClutterMainContext
{
Display *xdpy;
Window xwin_root;
int xscreen;
GC xgc;
PangoFT2FontMap *font_map;
GMutex *gl_lock;

898
clutter/clutter-stage-glx.c Normal file
View File

@ -0,0 +1,898 @@
/*
* 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;
}

View File

@ -0,0 +1,58 @@
/*
* 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.
*/
#ifndef _HAVE_CLUTTER_STAGE_GLX_H
#define _HAVE_CLUTTER_STAGE_GLX_H
G_BEGIN_DECLS
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <GL/glx.h>
#include <GL/gl.h>
#include <clutter/clutter-stage.h>
void
clutter_stage_backend_init_vtable (ClutterStageVTable *vtable) G_GNUC_INTERNAL;
ClutterStageBackend*
clutter_stage_backend_init (ClutterStage *stage) G_GNUC_INTERNAL;
Window
clutter_stage_glx_window (ClutterStage *stage);
gboolean
clutter_stage_glx_set_window_foreign (ClutterStage *stage,
Window xid);
const XVisualInfo*
clutter_stage_glx_get_xvisual (ClutterStage *stage);
G_END_DECLS
#endif

View File

@ -42,14 +42,22 @@
#include "clutter-private.h"
#include "clutter-debug.h"
#include <GL/glx.h>
#include <GL/gl.h>
#ifdef CLUTTER_BACKEND_GLX
#include <clutter/clutter-stage-glx.h>
#endif
#ifdef CLUTTER_BACKEND_EGL
#include <clutter/clutter-stage-egl.h>
#endif
#include <gdk-pixbuf-xlib/gdk-pixbuf-xlib.h>
/* the stage is a singleton instance */
static ClutterStage *stage_singleton = NULL;
/* Backend hooks */
static ClutterStageVTable _vtable;
G_DEFINE_TYPE (ClutterStage, clutter_stage, CLUTTER_TYPE_GROUP);
#define CLUTTER_STAGE_GET_PRIVATE(obj) \
@ -57,21 +65,11 @@ G_DEFINE_TYPE (ClutterStage, clutter_stage, CLUTTER_TYPE_GROUP);
struct _ClutterStagePrivate
{
XVisualInfo *xvisinfo;
Window xwin;
Pixmap xpixmap;
gint xwin_width, xwin_height; /* FIXME target_width / height */
GLXPixmap glxpixmap;
GLXContext gl_context;
ClutterColor color;
guint want_fullscreen : 1;
guint want_offscreen : 1;
guint hide_cursor : 1;
guint is_foreign_xwin : 1;
};
enum
@ -100,511 +98,6 @@ static guint stage_signals[LAST_SIGNAL] = { 0 };
static ClutterActorClass *parent_class = NULL;
static void
sync_fullscreen (ClutterStage *stage)
{
Atom atom_WINDOW_STATE, atom_WINDOW_STATE_FULLSCREEN;
atom_WINDOW_STATE
= XInternAtom(clutter_xdisplay(), "_NET_WM_STATE", False);
atom_WINDOW_STATE_FULLSCREEN
= XInternAtom(clutter_xdisplay(), "_NET_WM_STATE_FULLSCREEN",False);
if (stage->priv->want_fullscreen)
{
clutter_actor_set_size (CLUTTER_ACTOR(stage),
DisplayWidth(clutter_xdisplay(),
clutter_xscreen()),
DisplayHeight(clutter_xdisplay(),
clutter_xscreen()));
if (stage->priv->xwin != None)
XChangeProperty(clutter_xdisplay(), stage->priv->xwin,
atom_WINDOW_STATE, XA_ATOM, 32,
PropModeReplace,
(unsigned char *)&atom_WINDOW_STATE_FULLSCREEN, 1);
}
else
{
if (stage->priv->xwin != None)
XDeleteProperty(clutter_xdisplay(),
stage->priv->xwin, atom_WINDOW_STATE);
}
}
static void
sync_cursor_visible (ClutterStage *stage)
{
if (stage->priv->xwin == None)
return;
if (stage->priv->hide_cursor)
{
XColor col;
Pixmap pix;
Cursor curs;
pix = XCreatePixmap (clutter_xdisplay(),
stage->priv->xwin, 1, 1, 1);
memset (&col, 0, sizeof (col));
curs = XCreatePixmapCursor (clutter_xdisplay(),
pix, pix, &col, &col, 1, 1);
XFreePixmap (clutter_xdisplay(), pix);
XDefineCursor(clutter_xdisplay(), stage->priv->xwin, curs);
}
else
{
XUndefineCursor(clutter_xdisplay(), stage->priv->xwin);
}
}
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_gl_viewport (ClutterStage *stage)
{
/* Set For 2D */
#if 0
glViewport (0, 0, stage->priv->xwin_width, stage->priv->xwin_height);
glMatrixMode (GL_PROJECTION);
glLoadIdentity ();
glOrtho (0, stage->priv->xwin_width, stage->priv->xwin_height, 0, -1, 1);
glMatrixMode (GL_MODELVIEW);
glLoadIdentity ();
#endif
/* For 3D */
glViewport (0, 0, stage->priv->xwin_width, stage->priv->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->priv->xwin_width,
-1.0f / stage->priv->xwin_height, 1.0f / stage->priv->xwin_width);
glTranslatef (0.0f, -stage->priv->xwin_height, 0.0f);
}
static void
clutter_stage_show (ClutterActor *self)
{
ClutterActorClass *parent_class;
parent_class = CLUTTER_ACTOR_CLASS (clutter_stage_parent_class);
if (parent_class->show)
parent_class->show (self);
if (clutter_stage_get_xwindow (CLUTTER_STAGE (self)))
XMapWindow (clutter_xdisplay (),
clutter_stage_get_xwindow (CLUTTER_STAGE (self)));
}
static void
clutter_stage_hide (ClutterActor *self)
{
ClutterActorClass *parent_class;
parent_class = CLUTTER_ACTOR_CLASS (clutter_stage_parent_class);
if (parent_class->hide)
parent_class->hide (self);
if (clutter_stage_get_xwindow (CLUTTER_STAGE (self)))
XUnmapWindow (clutter_xdisplay (),
clutter_stage_get_xwindow (CLUTTER_STAGE (self)));
}
static void
clutter_stage_unrealize (ClutterActor *actor)
{
ClutterStage *stage;
ClutterStagePrivate *priv;
stage = CLUTTER_STAGE(actor);
priv = stage->priv;
CLUTTER_MARK();
if (priv->want_offscreen)
{
if (priv->glxpixmap)
{
glXDestroyGLXPixmap (clutter_xdisplay(), priv->glxpixmap);
priv->glxpixmap = None;
}
if (priv->xpixmap)
{
XFreePixmap (clutter_xdisplay(), priv->xpixmap);
priv->xpixmap = None;
}
}
else
{
if (!priv->is_foreign_xwin && priv->xwin != None)
{
XDestroyWindow (clutter_xdisplay(), priv->xwin);
priv->xwin = None;
}
else
priv->xwin = None;
}
glXMakeCurrent(clutter_xdisplay(), None, NULL);
if (priv->gl_context != None)
{
glXDestroyContext (clutter_xdisplay(), priv->gl_context);
priv->gl_context = None;
}
}
static void
clutter_stage_realize (ClutterActor *actor)
{
ClutterStage *stage;
ClutterStagePrivate *priv;
stage = CLUTTER_STAGE(actor);
priv = stage->priv;
CLUTTER_MARK();
if (priv->want_offscreen)
{
int gl_attributes[] = {
GLX_RGBA,
GLX_RED_SIZE, 1,
GLX_GREEN_SIZE, 1,
GLX_BLUE_SIZE, 1,
0
};
if (priv->xvisinfo)
XFree(priv->xvisinfo);
priv->xvisinfo = glXChooseVisual (clutter_xdisplay(),
clutter_xscreen(),
gl_attributes);
if (!priv->xvisinfo)
{
g_critical ("Unable to find suitable GL visual.");
CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED);
return;
}
if (priv->gl_context != None)
glXDestroyContext (clutter_xdisplay(), priv->gl_context);
priv->xpixmap = XCreatePixmap (clutter_xdisplay(),
clutter_root_xwindow(),
priv->xwin_width,
priv->xwin_height,
priv->xvisinfo->depth);
priv->glxpixmap = glXCreateGLXPixmap(clutter_xdisplay(),
priv->xvisinfo,
priv->xpixmap);
sync_fullscreen (stage);
/* indirect */
priv->gl_context = glXCreateContext (clutter_xdisplay(),
priv->xvisinfo,
0,
False);
glXMakeCurrent(clutter_xdisplay(), priv->glxpixmap, priv->gl_context);
#if 0
/* Debug code for monitoring a off screen pixmap via window */
{
Colormap cmap;
XSetWindowAttributes swa;
cmap = XCreateColormap(clutter_xdisplay(),
clutter_root_xwindow(),
priv->xvisinfo->visual, AllocNone);
/* create a window */
swa.colormap = cmap;
foo_win = XCreateWindow(clutter_xdisplay(),
clutter_root_xwindow(),
0, 0,
priv->xwin_width, priv->xwin_height,
0,
priv->xvisinfo->depth,
InputOutput,
priv->xvisinfo->visual,
CWColormap, &swa);
XMapWindow(clutter_xdisplay(), 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 (priv->xvisinfo)
{
XFree(priv->xvisinfo);
priv->xvisinfo = None;
}
#if 0
/* Attempted fix at GTK 'white' textures - made no difference :( */
if (priv->is_foreign_xwin && priv->xwin != None)
{
XWindowAttributes win_attr;
XVisualInfo vis_info;
int n;
XGetWindowAttributes (clutter_xdisplay(), priv->xwin, &win_attr);
vis_info.screen = clutter_xscreen();
vis_info.visualid = XVisualIDFromVisual (win_attr.visual);
priv->xvisinfo = XGetVisualInfo (clutter_xdisplay(),
VisualScreenMask|VisualIDMask,
&vis_info, &n);
printf("made %li\n", priv->xvisinfo);
}
#endif
if (priv->xvisinfo == None)
priv->xvisinfo = glXChooseVisual (clutter_xdisplay(),
clutter_xscreen(),
gl_attributes);
if (!priv->xvisinfo)
{
g_critical ("Unable to find suitable GL visual.");
CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED);
return;
}
CLUTTER_NOTE(GL, "visual id's %li vs %li",
priv->xvisinfo->visualid,
XVisualIDFromVisual(DefaultVisual(clutter_xdisplay(),
clutter_xscreen())));
if (priv->xwin == None)
{
XSetWindowAttributes swa;
if (priv->xvisinfo->visualid
== XVisualIDFromVisual(DefaultVisual(clutter_xdisplay(),
clutter_xscreen())))
{
swa.colormap = DefaultColormap(clutter_xdisplay(),
clutter_xscreen());
}
else
{
swa.colormap = XCreateColormap(clutter_xdisplay(),
clutter_root_xwindow(),
priv->xvisinfo->visual,
AllocNone);
}
priv->xwin = XCreateWindow(clutter_xdisplay(),
clutter_root_xwindow(),
0, 0,
priv->xwin_width, priv->xwin_height,
0,
priv->xvisinfo->depth,
InputOutput,
priv->xvisinfo->visual,
CWColormap, &swa);
}
XSelectInput(clutter_xdisplay(),
priv->xwin,
StructureNotifyMask
|ExposureMask
/* FIXME: we may want to eplicity enable MotionMask */
|PointerMotionMask
|KeyPressMask
|KeyReleaseMask
|ButtonPressMask
|ButtonReleaseMask
|PropertyChangeMask);
sync_fullscreen (stage);
sync_cursor_visible (stage);
if (priv->gl_context != None)
glXDestroyContext (clutter_xdisplay(), priv->gl_context);
priv->gl_context = glXCreateContext (clutter_xdisplay(),
priv->xvisinfo,
0,
True);
if (priv->gl_context == None)
{
g_critical ("Unable to create suitable GL context.");
CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED);
return;
}
glXMakeCurrent(clutter_xdisplay(), priv->xwin, priv->gl_context);
}
CLUTTER_NOTE (GL,
"\n"
"==========================================="
"GL_VENDOR: %s\n"
"GL_RENDERER: %s\n"
"GL_VERSION: %s\n"
"GL_EXTENSIONS: %s\n"
"Is direct: %s\n"
"===========================================",
glGetString (GL_VENDOR),
glGetString (GL_RENDERER),
glGetString (GL_VERSION),
glGetString (GL_EXTENSIONS),
glXIsDirect(clutter_xdisplay(), priv->gl_context) ? "yes" : "no"
);
sync_gl_viewport (stage);
}
static void
clutter_stage_paint (ClutterActor *self)
{
parent_class->paint (self);
}
static void
clutter_stage_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_request_coords (ClutterActor *self,
ClutterActorBox *box)
{
ClutterStage *stage;
ClutterStagePrivate *priv;
gint new_width, new_height;
stage = CLUTTER_STAGE (self);
priv = stage->priv;
/* 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 != priv->xwin_width || new_height != priv->xwin_height)
{
priv->xwin_width = new_width;
priv->xwin_height = new_height;
if (priv->xwin != None)
XResizeWindow (clutter_xdisplay(),
priv->xwin,
priv->xwin_width,
priv->xwin_height);
if (priv->xpixmap)
{
/* Need to recreate to resize */
clutter_actor_unrealize(self);
clutter_actor_realize(self);
}
sync_gl_viewport (stage);
}
if (priv->xwin != None) /* Do we want to bother ? */
XMoveWindow (clutter_xdisplay(),
priv->xwin,
box->x1,
box->y1);
}
static void
clutter_stage_dispose (GObject *object)
{
ClutterStage *self = CLUTTER_STAGE (object);
if (self->priv->xwin != None)
clutter_stage_unrealize (CLUTTER_ACTOR (self));
G_OBJECT_CLASS (clutter_stage_parent_class)->dispose (object);
}
static void
clutter_stage_finalize (GObject *object)
{
G_OBJECT_CLASS (clutter_stage_parent_class)->finalize (object);
}
static void
clutter_stage_set_property (GObject *object,
guint prop_id,
@ -640,14 +133,14 @@ clutter_stage_set_property (GObject *object,
if (priv->want_fullscreen != g_value_get_boolean (value))
{
priv->want_fullscreen = g_value_get_boolean (value);
sync_fullscreen (stage);
_vtable.sync_fullscreen (stage);
}
break;
case PROP_HIDE_CURSOR:
if (priv->hide_cursor != g_value_get_boolean (value))
{
priv->hide_cursor = g_value_get_boolean (value);
sync_cursor_visible (stage);
_vtable.sync_cursor (stage);
}
break;
default:
@ -698,17 +191,22 @@ clutter_stage_class_init (ClutterStageClass *klass)
parent_class = g_type_class_peek_parent (klass);
actor_class->realize = clutter_stage_realize;
actor_class->unrealize = clutter_stage_unrealize;
actor_class->show = clutter_stage_show;
actor_class->hide = clutter_stage_hide;
actor_class->paint = clutter_stage_paint;
clutter_stage_backend_init_vtable (&_vtable);
actor_class->request_coords = clutter_stage_request_coords;
actor_class->allocate_coords = clutter_stage_allocate_coords;
actor_class->realize = _vtable.realize;
actor_class->unrealize = _vtable.unrealize;
actor_class->show = _vtable.show;
actor_class->hide = _vtable.hide;
actor_class->paint = _vtable.paint;
actor_class->request_coords = _vtable.request_coords;
actor_class->allocate_coords = _vtable.allocate_coords;
/*
gobject_class->dispose = _vtable.stage_dispose;
gobject_class->finalize = _vtable.stage_finalize;
*/
gobject_class->dispose = clutter_stage_dispose;
gobject_class->finalize = clutter_stage_finalize;
gobject_class->set_property = clutter_stage_set_property;
gobject_class->get_property = clutter_stage_get_property;
@ -867,16 +365,11 @@ clutter_stage_init (ClutterStage *self)
self->priv = priv = CLUTTER_STAGE_GET_PRIVATE (self);
self->backend = clutter_stage_backend_init (self);
priv->want_offscreen = FALSE;
priv->want_fullscreen = FALSE;
priv->hide_cursor = FALSE;
priv->is_foreign_xwin = FALSE;
priv->xwin = None;
priv->gl_context = None;
priv->xwin_width = 100;
priv->xwin_height = 100;
priv->color.red = 0xff;
priv->color.green = 0xff;
@ -928,98 +421,6 @@ clutter_stage_get_default (void)
return retval;
}
/**
* clutter_stage_get_xwindow
* @stage: A #ClutterStage
*
* Get the stage's underlying x window ID.
*
* Return Value: Stage X Window XID
**/
Window
clutter_stage_get_xwindow (ClutterStage *stage)
{
return stage->priv->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
**/
gboolean
clutter_stage_set_xwindow_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;
ClutterStagePrivate *priv;
g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
g_return_val_if_fail (xid != None, FALSE);
priv = stage->priv;
clutter_util_trap_x_errors();
status = XGetGeometry (clutter_xdisplay(),
xid,
&root_return,
&x,
&y,
&width,
&height,
&border,
&depth);
if (clutter_util_untrap_x_errors() || !status ||
width == 0 || height == 0 ||
depth != priv->xvisinfo->depth)
{
return FALSE;
}
clutter_actor_unrealize (CLUTTER_ACTOR (stage));
priv->xwin = xid;
priv->is_foreign_xwin = TRUE;
geom.x = x;
geom.y = y;
geom.width = priv->xwin_width = width;
geom.height = priv->xwin_height = height;
clutter_actor_set_geometry (CLUTTER_ACTOR (stage), &geom);
clutter_actor_realize (CLUTTER_ACTOR (stage));
return TRUE;
}
/**
* clutter_stage_get_xvisual
* @stage: A #ClutterStage
*
* Get the stage's XVisualInfo.
*
* Return Value: The stage's XVisualInfo
**/
const XVisualInfo*
clutter_stage_get_xvisual (ClutterStage *stage)
{
return stage->priv->xvisinfo;
}
/**
* clutter_stage_set_color
* @stage: A #ClutterStage
@ -1072,12 +473,14 @@ clutter_stage_get_color (ClutterStage *stage,
color->alpha = priv->color.alpha;
}
#if 0
static void
snapshot_pixbuf_free (guchar *pixels,
gpointer data)
{
g_free(pixels);
}
#endif
/**
* clutter_stage_snapshot
@ -1100,6 +503,7 @@ clutter_stage_snapshot (ClutterStage *stage,
gint width,
gint height)
{
#if 0
guchar *data;
GdkPixbuf *pixb, *fpixb;
ClutterActor *actor;
@ -1158,8 +562,56 @@ clutter_stage_snapshot (ClutterStage *stage,
return fpixb;
}
#endif
return 0;
}
/* 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);
}
/**
* clutter_stage_get_actor_at_pos:
* @stage: a #ClutterStage
@ -1220,7 +672,7 @@ clutter_stage_get_actor_at_pos (ClutterStage *stage,
buff[(hits-1) * 4 + 3]);
}
sync_gl_viewport (stage);
_vtable.sync_viewport (stage);
return found;
}

View File

@ -34,10 +34,6 @@
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <GL/glx.h>
G_BEGIN_DECLS
#define CLUTTER_TYPE_STAGE (clutter_stage_get_type())
@ -72,10 +68,14 @@ G_BEGIN_DECLS
typedef struct _ClutterStagePrivate ClutterStagePrivate;
typedef struct _ClutterStage ClutterStage;
typedef struct _ClutterStageClass ClutterStageClass;
typedef struct _ClutterStageBackend ClutterStageBackend;
typedef struct _ClutterStageVTable ClutterStageVTable;
struct _ClutterStage
{
ClutterGroup parent;
ClutterStageBackend *backend;
/*< private >*/
ClutterStagePrivate *priv;
@ -107,11 +107,27 @@ struct _ClutterStageClass
void (*_clutter_stage6) (void);
};
struct _ClutterStageVTable
{
void (* show) (ClutterActor *actor);
void (* hide) (ClutterActor *actor);
void (* realize) (ClutterActor *actor);
void (* unrealize) (ClutterActor *actor);
void (* paint) (ClutterActor *actor);
void (* request_coords) (ClutterActor *actor,
ClutterActorBox *box);
void (* allocate_coords) (ClutterActor *actor,
ClutterActorBox *box);
void (* sync_fullscreen) (ClutterStage *stage);
void (* sync_cursor) (ClutterStage *stage);
void (* sync_viewport) (ClutterStage *stage);
};
GType clutter_stage_get_type (void) G_GNUC_CONST;
ClutterActor *clutter_stage_get_default (void);
Window clutter_stage_get_xwindow (ClutterStage *stage);
gboolean clutter_stage_set_xwindow_foreign (ClutterStage *stage,
Window xid);
void clutter_stage_set_color (ClutterStage *stage,
const ClutterColor *color);
void clutter_stage_get_color (ClutterStage *stage,
@ -124,7 +140,6 @@ GdkPixbuf * clutter_stage_snapshot (ClutterStage *stage,
gint y,
gint width,
gint height);
const XVisualInfo * clutter_stage_get_xvisual (ClutterStage *stage);
G_END_DECLS

View File

@ -34,43 +34,6 @@
#include "clutter-util.h"
#include "clutter-main.h"
static int TrappedErrorCode = 0;
static int (*old_error_handler) (Display *, XErrorEvent *);
static int
error_handler(Display *xdpy,
XErrorEvent *error)
{
TrappedErrorCode = error->error_code;
return 0;
}
/**
* clutter_util_trap_x_errors:
*
* Trap X errors so they don't cause an abort.
*/
void
clutter_util_trap_x_errors(void)
{
TrappedErrorCode = 0;
old_error_handler = XSetErrorHandler(error_handler);
}
/**
* clutter_util_untrap_x_errors:
*
* Stop trapping X errors.
*
* Return value: 0 if there was no error, or the last X error that occurred.
*/
int
clutter_util_untrap_x_errors(void)
{
XSetErrorHandler(old_error_handler);
return TrappedErrorCode;
}
/**
* clutter_util_next_p2:
* @a: Value to get the next power

View File

@ -30,12 +30,6 @@
G_BEGIN_DECLS
void
clutter_util_trap_x_errors(void);
int
clutter_util_untrap_x_errors(void);
int
clutter_util_next_p2 (int a);

View File

@ -26,6 +26,7 @@
#include "config.h"
#endif
#include <GL/gl.h>
#include <math.h>
#include "pangoclutter.h"
@ -553,14 +554,15 @@ draw_begin (PangoRenderer *renderer_)
glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glEnable (GL_BLEND);
#if 0
/* How to handle in ES ? */
gl_BlendFuncSeparate (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
GL_ONE , GL_ONE_MINUS_SRC_ALPHA);
#endif
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
#if 0
glEnable (GL_ALPHA_TEST);
glAlphaFunc (GL_GREATER, 0.01f);
#endif
}
static void

View File

@ -97,6 +97,9 @@ fi
GLX_CFLAGS="$X11_CFLAGS"
CLUTTER_FLAVOUR="glx"
AC_SUBST(CLUTTER_FLAVOUR)
dnl ========================================================================
pkg_modules="pangoft2 glib-2.0 >= 2.8 gthread-2.0 gdk-pixbuf-2.0 gdk-pixbuf-xlib-2.0"

View File

@ -1,7 +1,7 @@
noinst_PROGRAMS = test super-oh behave
INCLUDES = -I$(top_srcdir)/
LDADD = $(top_builddir)/clutter/libclutter-@CLUTTER_MAJORMINOR@.la
LDADD = $(top_builddir)/clutter/libclutter-@CLUTTER_FLAVOUR@-@CLUTTER_MAJORMINOR@.la
test_SOURCES = test.c
test_CFLAGS = $(CLUTTER_CFLAGS) $(GCONF_CFLAGS)