diff --git a/ChangeLog b/ChangeLog index 9f08e0846..d7ad4e983 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,31 @@ +2007-01-23 Matthew Allum + + * 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 * clutter/clutter-fixed.c: diff --git a/Makefile.am b/Makefile.am index 06804bec4..2741e461e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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) diff --git a/TODO b/TODO index 649334856..8a14f07a5 100644 --- a/TODO +++ b/TODO @@ -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 == diff --git a/clutter.pc.in b/clutter.pc.in index 24429656e..0399d5b1c 100644 --- a/clutter.pc.in +++ b/clutter.pc.in @@ -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 diff --git a/clutter/Makefile.am b/clutter/Makefile.am index f0a055464..6a838f7f6 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -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 \ diff --git a/clutter/clutter-backend-glx.c b/clutter/clutter-backend-glx.c new file mode 100644 index 000000000..d65a65a37 --- /dev/null +++ b/clutter/clutter-backend-glx.c @@ -0,0 +1,173 @@ +#include +#include +#include +#include +#include + +#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; +} diff --git a/clutter/clutter-backend-glx.h b/clutter/clutter-backend-glx.h new file mode 100644 index 000000000..6829a6414 --- /dev/null +++ b/clutter/clutter-backend-glx.h @@ -0,0 +1,54 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Authored By Matthew Allum + * + * 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 +#include + +#include +#include + +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 diff --git a/clutter/clutter-event.c b/clutter/clutter-event.c index e9c080014..b55562ad4 100644 --- a/clutter/clutter-event.c +++ b/clutter/clutter-event.c @@ -29,9 +29,6 @@ #include "clutter-event.h" -#include -#include - /** * clutter_event_type: * @event: a #ClutterEvent diff --git a/clutter/clutter-feature.c b/clutter/clutter-feature.c index 95005bdb6..656e97345 100644 --- a/clutter/clutter-feature.c +++ b/clutter/clutter-feature.c @@ -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)) diff --git a/clutter/clutter-group.c b/clutter/clutter-group.c index e01697764..489c7359f 100644 --- a/clutter/clutter-group.c +++ b/clutter/clutter-group.c @@ -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" diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 7921ecb05..cc53a247a 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -43,12 +43,16 @@ #include "clutter-private.h" #include "clutter-debug.h" +#ifdef CLUTTER_BACKEND_GLX +#include +#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 (); - + 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; } diff --git a/clutter/clutter-main.h b/clutter/clutter-main.h index 51f7211d8..8ffc2fec9 100644 --- a/clutter/clutter-main.h +++ b/clutter/clutter-main.h @@ -29,11 +29,6 @@ #include #include -#include - -#include -#include - 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); diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index 681a72c9c..af6e86658 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -32,12 +32,7 @@ #include #include -#include -#include -#include - -#include -#include +#include /* Togo */ #include @@ -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; diff --git a/clutter/clutter-stage-glx.c b/clutter/clutter-stage-glx.c new file mode 100644 index 000000000..1533f4309 --- /dev/null +++ b/clutter/clutter-stage-glx.c @@ -0,0 +1,898 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Authored By Matthew Allum + * + * 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 +#include + +#include + +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; +} diff --git a/clutter/clutter-stage-glx.h b/clutter/clutter-stage-glx.h new file mode 100644 index 000000000..5287cca4f --- /dev/null +++ b/clutter/clutter-stage-glx.h @@ -0,0 +1,58 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Authored By Matthew Allum + * + * 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 +#include + +#include +#include + +#include + +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 diff --git a/clutter/clutter-stage.c b/clutter/clutter-stage.c index 0637889e9..123c75874 100644 --- a/clutter/clutter-stage.c +++ b/clutter/clutter-stage.c @@ -42,14 +42,22 @@ #include "clutter-private.h" #include "clutter-debug.h" -#include -#include +#ifdef CLUTTER_BACKEND_GLX +#include +#endif + +#ifdef CLUTTER_BACKEND_EGL +#include +#endif #include /* 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; } diff --git a/clutter/clutter-stage.h b/clutter/clutter-stage.h index 94937f93e..6eed1ea53 100644 --- a/clutter/clutter-stage.h +++ b/clutter/clutter-stage.h @@ -34,10 +34,6 @@ #include -#include -#include -#include - 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 diff --git a/clutter/clutter-util.c b/clutter/clutter-util.c index 0bfb1d191..67f37b742 100644 --- a/clutter/clutter-util.c +++ b/clutter/clutter-util.c @@ -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 diff --git a/clutter/clutter-util.h b/clutter/clutter-util.h index df4cdc720..7d62d31ea 100644 --- a/clutter/clutter-util.h +++ b/clutter/clutter-util.h @@ -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); diff --git a/clutter/pango/pangoclutter-render.c b/clutter/pango/pangoclutter-render.c index e65de9ba7..595094c54 100644 --- a/clutter/pango/pangoclutter-render.c +++ b/clutter/pango/pangoclutter-render.c @@ -26,6 +26,7 @@ #include "config.h" #endif +#include #include #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 diff --git a/configure.ac b/configure.ac index fc0944164..1796de126 100644 --- a/configure.ac +++ b/configure.ac @@ -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" diff --git a/examples/Makefile.am b/examples/Makefile.am index 5021f9132..4e0cd1253 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -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)