2008-04-04 Emmanuele Bassi <ebassi@openedhand.com>

Bug #864 - Allow instantiating and subclassing of ClutterStage

	* clutter/Makefile.am: Add clutter-stage-window.[ch]

	* clutter/clutter-stage-manager.c:
	(_clutter_stage_manager_remove_stage): Do not warn if removing
	a stage we don't manage, as we might be invoked multiple times
	during a ClutterState dispose sequence.

	* clutter/clutter-actor.c:
	* clutter/clutter-backend.[ch]:
	* clutter/clutter-main.c:
	* clutter/clutter-private.h:
	* clutter/clutter-stage.[ch]: Make ClutterStage a proxy actor,
	with a private actor implementing the ClutterStageWindow
	interface for handling the per-backend realization, painting
	and unrealization, plus all the windowing system abstraction.

	* clutter/x11/clutter-event-x11.c:
	* clutter/x11/clutter-stage-x11.[ch]: Port the X11 backend
	to the new backend and stage API and semantics.

	* clutter/glx/clutter-backend-glx.c:
	* clutter/glx/clutter-stage-glx.c: Port the GLX backend to
	the new backend and stage API and semantics.

	* clutter/eglx/clutter-backend-egl.[ch]:
	* clutter/eglx/clutter-stage-egl.[ch]: Port the EGLX backend
	to the new backend and stage API and semantics (untested).

	* tests/test-multistage.c (on_button_press): Rename
	clutter_stage_create_new() to clutter_stage_new().
This commit is contained in:
Emmanuele Bassi 2008-04-04 15:02:11 +00:00
parent beb03b9750
commit f859135082
22 changed files with 1037 additions and 510 deletions

View File

@ -1,3 +1,38 @@
2008-04-04 Emmanuele Bassi <ebassi@openedhand.com>
Bug #864 - Allow instantiating and subclassing of ClutterStage
* clutter/Makefile.am: Add clutter-stage-window.[ch]
* clutter/clutter-stage-manager.c:
(_clutter_stage_manager_remove_stage): Do not warn if removing
a stage we don't manage, as we might be invoked multiple times
during a ClutterState dispose sequence.
* clutter/clutter-actor.c:
* clutter/clutter-backend.[ch]:
* clutter/clutter-main.c:
* clutter/clutter-private.h:
* clutter/clutter-stage.[ch]: Make ClutterStage a proxy actor,
with a private actor implementing the ClutterStageWindow
interface for handling the per-backend realization, painting
and unrealization, plus all the windowing system abstraction.
* clutter/x11/clutter-event-x11.c:
* clutter/x11/clutter-stage-x11.[ch]: Port the X11 backend
to the new backend and stage API and semantics.
* clutter/glx/clutter-backend-glx.c:
* clutter/glx/clutter-stage-glx.c: Port the GLX backend to
the new backend and stage API and semantics.
* clutter/eglx/clutter-backend-egl.[ch]:
* clutter/eglx/clutter-stage-egl.[ch]: Port the EGLX backend
to the new backend and stage API and semantics (untested).
* tests/test-multistage.c (on_button_press): Rename
clutter_stage_create_new() to clutter_stage_new().
2008-04-04 Neil Roberts <neil@o-hand.com> 2008-04-04 Neil Roberts <neil@o-hand.com>
Applied patch from bug #810. Applied patch from bug #810.

View File

@ -162,6 +162,7 @@ source_c = \
clutter-shader.c \ clutter-shader.c \
clutter-stage.c \ clutter-stage.c \
clutter-stage-manager.c \ clutter-stage-manager.c \
clutter-stage-window.c \
clutter-texture.c \ clutter-texture.c \
clutter-timeline.c \ clutter-timeline.c \
clutter-timeout-pool.c \ clutter-timeout-pool.c \
@ -175,6 +176,7 @@ source_h_priv = \
clutter-private.h \ clutter-private.h \
clutter-id-pool.h \ clutter-id-pool.h \
clutter-script-private.h \ clutter-script-private.h \
clutter-stage-window.h \
$(NULL) $(NULL)
libclutter_@CLUTTER_FLAVOUR@_@CLUTTER_API_VERSION@_la_LIBADD = \ libclutter_@CLUTTER_FLAVOUR@_@CLUTTER_API_VERSION@_la_LIBADD = \

View File

@ -2447,7 +2447,7 @@ clutter_actor_queue_redraw (ClutterActor *self)
/* FIXME: should we check we're visible here? */ /* FIXME: should we check we're visible here? */
if ((stage = clutter_actor_get_stage (self)) != NULL) if ((stage = clutter_actor_get_stage (self)) != NULL)
clutter_stage_queue_redraw (CLUTTER_STAGE(stage)); clutter_stage_queue_redraw (CLUTTER_STAGE (stage));
} }
/** /**

View File

@ -44,6 +44,7 @@
#include "clutter-fixed.h" #include "clutter-fixed.h"
#include "clutter-backend.h" #include "clutter-backend.h"
#include "clutter-private.h" #include "clutter-private.h"
#include "clutter-debug.h"
G_DEFINE_ABSTRACT_TYPE (ClutterBackend, clutter_backend, G_TYPE_OBJECT); G_DEFINE_ABSTRACT_TYPE (ClutterBackend, clutter_backend, G_TYPE_OBJECT);
@ -138,8 +139,9 @@ _clutter_backend_post_parse (ClutterBackend *backend,
return TRUE; return TRUE;
} }
ClutterActor* ClutterActor *
_clutter_backend_create_stage (ClutterBackend *backend, _clutter_backend_create_stage (ClutterBackend *backend,
ClutterStage *wrapper,
GError **error) GError **error)
{ {
ClutterMainContext *context; ClutterMainContext *context;
@ -147,6 +149,7 @@ _clutter_backend_create_stage (ClutterBackend *backend,
ClutterActor *stage = NULL; ClutterActor *stage = NULL;
g_return_val_if_fail (CLUTTER_IS_BACKEND (backend), FALSE); g_return_val_if_fail (CLUTTER_IS_BACKEND (backend), FALSE);
g_return_val_if_fail (CLUTTER_IS_STAGE (wrapper), FALSE);
context = clutter_context_get_default (); context = clutter_context_get_default ();
@ -155,28 +158,30 @@ _clutter_backend_create_stage (ClutterBackend *backend,
klass = CLUTTER_BACKEND_GET_CLASS (backend); klass = CLUTTER_BACKEND_GET_CLASS (backend);
if (klass->create_stage) if (klass->create_stage)
stage = klass->create_stage (backend, error); stage = klass->create_stage (backend, wrapper, error);
if (!stage) if (!stage)
return NULL; return NULL;
_clutter_stage_manager_add_stage (context->stage_manager, _clutter_stage_manager_add_stage (context->stage_manager, wrapper);
CLUTTER_STAGE(stage));
return stage; return stage;
} }
void void
_clutter_backend_redraw (ClutterBackend *backend, ClutterStage *stage) _clutter_backend_redraw (ClutterBackend *backend,
ClutterStage *stage)
{ {
ClutterBackendClass *klass; ClutterBackendClass *klass;
klass = CLUTTER_BACKEND_GET_CLASS (backend); klass = CLUTTER_BACKEND_GET_CLASS (backend);
if (G_LIKELY(klass->redraw)) if (G_LIKELY (klass->redraw))
klass->redraw (backend, stage); klass->redraw (backend, stage);
} }
void void
_clutter_backend_ensure_context (ClutterBackend *backend, ClutterStage *stage) _clutter_backend_ensure_context (ClutterBackend *backend,
ClutterStage *stage)
{ {
ClutterBackendClass *klass; ClutterBackendClass *klass;
static ClutterStage *current_context_stage = NULL; static ClutterStage *current_context_stage = NULL;
@ -184,13 +189,16 @@ _clutter_backend_ensure_context (ClutterBackend *backend, ClutterStage *stage)
g_return_if_fail (CLUTTER_IS_BACKEND (backend)); g_return_if_fail (CLUTTER_IS_BACKEND (backend));
g_return_if_fail (CLUTTER_IS_STAGE (stage)); g_return_if_fail (CLUTTER_IS_STAGE (stage));
if (stage != current_context_stage || !CLUTTER_ACTOR_IS_REALIZED(stage)) if (stage != current_context_stage || !CLUTTER_ACTOR_IS_REALIZED (stage))
{ {
if (!CLUTTER_ACTOR_IS_REALIZED(stage)) if (!CLUTTER_ACTOR_IS_REALIZED (stage))
stage = NULL; {
CLUTTER_NOTE (MULTISTAGE, "Stage is not realized");
stage = NULL;
}
klass = CLUTTER_BACKEND_GET_CLASS (backend); klass = CLUTTER_BACKEND_GET_CLASS (backend);
if (G_LIKELY(klass->ensure_context)) if (G_LIKELY (klass->ensure_context))
klass->ensure_context (backend, stage); klass->ensure_context (backend, stage);
/* FIXME: With a NULL stage and thus no active context it may make more /* FIXME: With a NULL stage and thus no active context it may make more

View File

@ -47,31 +47,33 @@ typedef struct _ClutterBackendClass ClutterBackendClass;
struct _ClutterBackend struct _ClutterBackend
{ {
/*< private >*/
GObject parent_instance; GObject parent_instance;
ClutterBackendPrivate *priv; ClutterBackendPrivate *priv;
}; };
struct _ClutterBackendClass struct _ClutterBackendClass
{ {
/*< private >*/
GObjectClass parent_class; GObjectClass parent_class;
/* vfuncs */ /* vfuncs */
gboolean (* pre_parse) (ClutterBackend *backend, gboolean (* pre_parse) (ClutterBackend *backend,
GError **error); GError **error);
gboolean (* post_parse) (ClutterBackend *backend, gboolean (* post_parse) (ClutterBackend *backend,
GError **error); GError **error);
ClutterActor *(* create_stage) (ClutterBackend *backend, ClutterActor * (* create_stage) (ClutterBackend *backend,
GError **error); ClutterStage *wrapper,
void (* init_events) (ClutterBackend *backend); GError **error);
void (* init_features) (ClutterBackend *backend); void (* init_events) (ClutterBackend *backend);
void (* add_options) (ClutterBackend *backend, void (* init_features) (ClutterBackend *backend);
GOptionGroup *group); void (* add_options) (ClutterBackend *backend,
ClutterFeatureFlags (* get_features) (ClutterBackend *backend); GOptionGroup *group);
void (* redraw) (ClutterBackend *backend, ClutterFeatureFlags (* get_features) (ClutterBackend *backend);
ClutterStage *stage); void (* redraw) (ClutterBackend *backend,
void (* ensure_context) (ClutterBackend *backend, ClutterStage *stage);
ClutterStage *stage); void (* ensure_context) (ClutterBackend *backend,
ClutterStage *stage);
}; };
GType clutter_backend_get_type (void) G_GNUC_CONST; GType clutter_backend_get_type (void) G_GNUC_CONST;

View File

@ -104,6 +104,28 @@ clutter_get_show_fps (void)
return clutter_show_fps; return clutter_show_fps;
} }
static inline void
clutter_maybe_setup_viewport (ClutterStage *stage)
{
if (CLUTTER_PRIVATE_FLAGS (stage) & CLUTTER_ACTOR_SYNC_MATRICES)
{
ClutterPerspective perspective;
guint width, height;
clutter_actor_get_size (CLUTTER_ACTOR (stage), &width, &height);
clutter_stage_get_perspectivex (stage, &perspective);
CLUTTER_NOTE (PAINT, "Setting up the viewport");
cogl_setup_viewport (width, height,
perspective.fovy,
perspective.aspect,
perspective.z_near,
perspective.z_far);
CLUTTER_UNSET_PRIVATE_FLAGS (stage, CLUTTER_ACTOR_SYNC_MATRICES);
}
}
/** /**
* clutter_redraw: * clutter_redraw:
@ -122,35 +144,21 @@ clutter_redraw (ClutterStage *stage)
CLUTTER_TIMESTAMP (SCHEDULER, "Redraw start for stage:%p", stage); CLUTTER_TIMESTAMP (SCHEDULER, "Redraw start for stage:%p", stage);
CLUTTER_NOTE (PAINT, " Redraw enter for stage:%p", stage); CLUTTER_NOTE (PAINT, " Redraw enter for stage:%p", stage);
CLUTTER_NOTE (MULTISTAGE, "redraw called for stage:%p", stage); CLUTTER_NOTE (MULTISTAGE, "Redraw called for stage:%p", stage);
_clutter_backend_ensure_context (ctx->backend, stage); _clutter_backend_ensure_context (ctx->backend, stage);
/* Setup FPS count - not currently across *all* stages rather than per */ /* Setup FPS count - not currently across *all* stages rather than per */
if (clutter_get_show_fps ()) if (G_UNLIKELY (clutter_get_show_fps ()))
{ {
if (!timer) if (!timer)
timer = g_timer_new (); timer = g_timer_new ();
} }
/* The below cant go in stage paint as base actor_paint will get /* The code below can't go in stage paint as base actor_paint
* called before the below (and break picking etc) * will get called before it (and break picking, etc)
*/ */
if (CLUTTER_PRIVATE_FLAGS (stage) & CLUTTER_ACTOR_SYNC_MATRICES) clutter_maybe_setup_viewport (stage);
{
ClutterPerspective perspective;
clutter_stage_get_perspectivex (CLUTTER_STAGE (stage), &perspective);
cogl_setup_viewport (clutter_actor_get_width (CLUTTER_ACTOR(stage)),
clutter_actor_get_height (CLUTTER_ACTOR(stage)),
perspective.fovy,
perspective.aspect,
perspective.z_near,
perspective.z_far);
CLUTTER_UNSET_PRIVATE_FLAGS (stage, CLUTTER_ACTOR_SYNC_MATRICES);
}
/* Call through to the actual backend to do the painting down from /* Call through to the actual backend to do the painting down from
* the stage. It will likely need to swap buffers, vblank sync etc * the stage. It will likely need to swap buffers, vblank sync etc
@ -159,7 +167,7 @@ clutter_redraw (ClutterStage *stage)
_clutter_backend_redraw (ctx->backend, stage); _clutter_backend_redraw (ctx->backend, stage);
/* Complete FPS info */ /* Complete FPS info */
if (clutter_get_show_fps ()) if (G_UNLIKELY (clutter_get_show_fps ()))
{ {
timer_n_frames++; timer_n_frames++;
@ -234,24 +242,8 @@ _clutter_do_pick (ClutterStage *stage,
_clutter_backend_ensure_context (context->backend, stage); _clutter_backend_ensure_context (context->backend, stage);
/* FIXME: needed for when a context switch happens - probably /* needed for when a context switch happens */
* should put into its own function somewhere.. clutter_maybe_setup_viewport (stage);
*/
if (CLUTTER_PRIVATE_FLAGS (stage) & CLUTTER_ACTOR_SYNC_MATRICES)
{
ClutterPerspective perspective;
clutter_stage_get_perspectivex (CLUTTER_STAGE (stage), &perspective);
cogl_setup_viewport (clutter_actor_get_width (CLUTTER_ACTOR(stage)),
clutter_actor_get_height (CLUTTER_ACTOR(stage)),
perspective.fovy,
perspective.aspect,
perspective.z_near,
perspective.z_far);
CLUTTER_UNSET_PRIVATE_FLAGS (stage, CLUTTER_ACTOR_SYNC_MATRICES);
}
cogl_paint_init (&white); cogl_paint_init (&white);
cogl_enable (0); cogl_enable (0);
@ -276,10 +268,10 @@ _clutter_do_pick (ClutterStage *stage,
/* glEnable (GL_DITHER); we never enabled this originally, so its /* glEnable (GL_DITHER); we never enabled this originally, so its
probably not safe to then enable it */ probably not safe to then enable it */
glReadPixels(x, viewport[3] - y -1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel); glReadPixels (x, viewport[3] - y -1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
if (pixel[0] == 0xff && pixel[1] == 0xff && pixel[2] == 0xff) if (pixel[0] == 0xff && pixel[1] == 0xff && pixel[2] == 0xff)
return CLUTTER_ACTOR (stage); return CLUTTER_ACTOR (stage);
cogl_get_bitmasks (&r, &g, &b, NULL); cogl_get_bitmasks (&r, &g, &b, NULL);
@ -988,7 +980,6 @@ clutter_init_with_args (int *argc,
GOptionContext *context; GOptionContext *context;
GOptionGroup *group; GOptionGroup *group;
gboolean res; gboolean res;
GError *stage_error;
ClutterActor *stage; ClutterActor *stage;
if (clutter_is_initialized) if (clutter_is_initialized)
@ -1018,13 +1009,13 @@ clutter_init_with_args (int *argc,
clutter_context = clutter_context_get_default (); clutter_context = clutter_context_get_default ();
stage_error = NULL; stage = clutter_stage_get_default ();
stage = _clutter_backend_create_stage (clutter_context->backend,
&stage_error);
if (!stage) if (!stage)
{ {
g_propagate_error (error, stage_error); g_set_error (error, CLUTTER_INIT_ERROR,
CLUTTER_INIT_ERROR_INTERNAL,
"Unable to create the default stage");
return CLUTTER_INIT_ERROR_INTERNAL; return CLUTTER_INIT_ERROR_INTERNAL;
} }
@ -1032,8 +1023,7 @@ clutter_init_with_args (int *argc,
_clutter_feature_init (); _clutter_feature_init ();
clutter_stage_set_title (CLUTTER_STAGE(clutter_stage_get_default()), clutter_stage_set_title (CLUTTER_STAGE (stage), g_get_prgname ());
g_get_prgname ());
return CLUTTER_INIT_SUCCESS; return CLUTTER_INIT_SUCCESS;
} }
@ -1089,7 +1079,6 @@ clutter_init (int *argc,
{ {
ClutterMainContext *context; ClutterMainContext *context;
ClutterActor *stage; ClutterActor *stage;
GError *stage_error;
if (clutter_is_initialized) if (clutter_is_initialized)
return CLUTTER_INIT_SUCCESS; return CLUTTER_INIT_SUCCESS;
@ -1115,15 +1104,10 @@ clutter_init (int *argc,
context = clutter_context_get_default (); context = clutter_context_get_default ();
/* Stage will give us a GL Context etc */ /* Stage will give us a GL Context etc */
stage_error = NULL; stage = clutter_stage_get_default ();
stage = _clutter_backend_create_stage (context->backend, &stage_error);
if (!stage) if (!stage)
{ {
CLUTTER_NOTE (MISC, "stage failed to initialise."); g_critical ("Unable to create the default stage");
g_critical (stage_error->message);
g_error_free (stage_error);
return CLUTTER_INIT_ERROR_INTERNAL; return CLUTTER_INIT_ERROR_INTERNAL;
} }
@ -1133,8 +1117,7 @@ clutter_init (int *argc,
/* finally features - will call to backend and cogl */ /* finally features - will call to backend and cogl */
_clutter_feature_init (); _clutter_feature_init ();
clutter_stage_set_title (CLUTTER_STAGE(clutter_stage_get_default()), clutter_stage_set_title (CLUTTER_STAGE (stage), g_get_prgname ());
g_get_prgname());
return CLUTTER_INIT_SUCCESS; return CLUTTER_INIT_SUCCESS;
} }

View File

@ -40,12 +40,13 @@
#include <pango/pangoft2.h> #include <pango/pangoft2.h>
#include "clutter-stage-manager.h"
#include "clutter-event.h"
#include "clutter-backend.h" #include "clutter-backend.h"
#include "clutter-stage.h" #include "clutter-event.h"
#include "clutter-feature.h" #include "clutter-feature.h"
#include "clutter-id-pool.h" #include "clutter-id-pool.h"
#include "clutter-stage-manager.h"
#include "clutter-stage-window.h"
#include "clutter-stage.h"
G_BEGIN_DECLS G_BEGIN_DECLS
@ -55,10 +56,11 @@ typedef enum {
CLUTTER_ACTOR_IN_DESTRUCTION = 1 << 0, CLUTTER_ACTOR_IN_DESTRUCTION = 1 << 0,
CLUTTER_ACTOR_IS_TOPLEVEL = 1 << 1, CLUTTER_ACTOR_IS_TOPLEVEL = 1 << 1,
CLUTTER_ACTOR_IN_REPARENT = 1 << 2, CLUTTER_ACTOR_IN_REPARENT = 1 << 2,
CLUTTER_ACTOR_SYNC_MATRICES = 1 << 3 /* Used by stage to indicate GL CLUTTER_ACTOR_SYNC_MATRICES = 1 << 3, /* Used by stage to indicate GL
* viewport / perspective etc * viewport / perspective etc
* needs (re)setting. * needs (re)setting.
*/ */
CLUTTER_ACTOR_IN_PAINT = 1 << 4 /* Used to avoid recursion */
} ClutterPrivateFlags; } ClutterPrivateFlags;
typedef enum { typedef enum {
@ -122,7 +124,6 @@ ClutterMainContext *clutter_context_get_default (void);
#define I_(str) (g_intern_static_string ((str))) #define I_(str) (g_intern_static_string ((str)))
/* stage manager */ /* stage manager */
struct _ClutterStageManager struct _ClutterStageManager
{ {
GObject parent_instance; GObject parent_instance;
@ -135,29 +136,31 @@ void _clutter_stage_manager_add_stage (ClutterStageManager *stage_manager,
void _clutter_stage_manager_remove_stage (ClutterStageManager *stage_manager, void _clutter_stage_manager_remove_stage (ClutterStageManager *stage_manager,
ClutterStage *stage); ClutterStage *stage);
/* vfuncs implemnted by backend */ /* stage */
GType _clutter_backend_impl_get_type (void); void _clutter_stage_set_window (ClutterStage *stage,
ClutterStageWindow *stage_window);
ClutterStageWindow *_clutter_stage_get_window (ClutterStage *stage);
ClutterStageWindow *_clutter_stage_get_default_window (void);
void _clutter_backend_redraw (ClutterBackend *backend, /* vfuncs implemented by backend */
ClutterStage *stage); GType _clutter_backend_impl_get_type (void);
ClutterActor* _clutter_backend_create_stage (ClutterBackend *backend, void _clutter_backend_redraw (ClutterBackend *backend,
GError **error); ClutterStage *stage);
ClutterActor *_clutter_backend_create_stage (ClutterBackend *backend,
ClutterStage *wrapper,
GError **error);
void _clutter_backend_ensure_context (ClutterBackend *backend,
ClutterStage *stage);
void _clutter_backend_redraw (ClutterBackend *backend, void _clutter_backend_add_options (ClutterBackend *backend,
ClutterStage *stage); GOptionGroup *group);
gboolean _clutter_backend_pre_parse (ClutterBackend *backend,
void _clutter_backend_add_options (ClutterBackend *backend, GError **error);
GOptionGroup *group); gboolean _clutter_backend_post_parse (ClutterBackend *backend,
gboolean _clutter_backend_pre_parse (ClutterBackend *backend, GError **error);
GError **error); void _clutter_backend_init_events (ClutterBackend *backend);
gboolean _clutter_backend_post_parse (ClutterBackend *backend,
GError **error);
void _clutter_backend_init_events (ClutterBackend *backend);
void _clutter_backend_ensure_context (ClutterBackend *backend,
ClutterStage *stage);
ClutterFeatureFlags _clutter_backend_get_features (ClutterBackend *backend); ClutterFeatureFlags _clutter_backend_get_features (ClutterBackend *backend);
@ -172,10 +175,10 @@ ClutterActor *_clutter_do_pick (ClutterStage *stage,
* a G_TYPE_BOOLEAN return value; this will stop the emission as * a G_TYPE_BOOLEAN return value; this will stop the emission as
* soon as one handler returns TRUE * soon as one handler returns TRUE
*/ */
gboolean _clutter_boolean_handled_accumulator (GSignalInvocationHint *ihint, gboolean _clutter_boolean_handled_accumulator (GSignalInvocationHint *ihint,
GValue *return_accu, GValue *return_accu,
const GValue *handler_return, const GValue *handler_return,
gpointer dummy); gpointer dummy);
G_END_DECLS G_END_DECLS

View File

@ -267,12 +267,11 @@ void
_clutter_stage_manager_remove_stage (ClutterStageManager *stage_manager, _clutter_stage_manager_remove_stage (ClutterStageManager *stage_manager,
ClutterStage *stage) ClutterStage *stage)
{ {
/* this might be called multiple times from a ::dispose, so it
* needs to just return without warning
*/
if (!g_slist_find (stage_manager->stages, stage)) if (!g_slist_find (stage_manager->stages, stage))
{ return;
g_warning ("Trying to remove an unknown stage from the list "
"of managed stages, aborting.");
return;
}
stage_manager->stages = g_slist_remove (stage_manager->stages, stage); stage_manager->stages = g_slist_remove (stage_manager->stages, stage);

View File

@ -0,0 +1,33 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <glib-object.h>
#include "clutter-actor.h"
#include "clutter-stage-window.h"
#include "clutter-private.h"
GType
clutter_stage_window_get_type (void)
{
static GType stage_window_type = 0;
if (G_UNLIKELY (stage_window_type == 0))
{
const GTypeInfo stage_window_info = {
sizeof (ClutterStageWindowIface),
NULL,
NULL,
};
stage_window_type =
g_type_register_static (G_TYPE_INTERFACE, I_("ClutterStageWindow"),
&stage_window_info, 0);
g_type_interface_add_prerequisite (stage_window_type,
CLUTTER_TYPE_ACTOR);
}
return stage_window_type;
}

View File

@ -0,0 +1,43 @@
#ifndef __CLUTTER_STAGE_WINDOW_H__
#define __CLUTTER_STAGE_WINDOW_H__
#include <clutter/clutter-actor.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
G_BEGIN_DECLS
#define CLUTTER_TYPE_STAGE_WINDOW (clutter_stage_window_get_type ())
#define CLUTTER_STAGE_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_STAGE_WINDOW, ClutterStageWindow))
#define CLUTTER_IS_STAGE_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_STAGE_WINDOW))
#define CLUTTER_STAGE_WINDOW_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), CLUTTER_TYPE_STAGE_WINDOW, ClutterStageWindowIface))
typedef struct _ClutterStageWindow ClutterStageWindow; /* dummy */
typedef struct _ClutterStageWindowIface ClutterStageWindowIface;
struct _ClutterStageWindowIface
{
GTypeInterface parent_iface;
ClutterActor *(* get_wrapper) (ClutterStageWindow *stage_window);
void (* set_title) (ClutterStageWindow *stage_window,
const gchar *title);
void (* set_fullscreen) (ClutterStageWindow *stage_window,
gboolean is_fullscreen);
void (* set_cursor_visible) (ClutterStageWindow *stage_window,
gboolean cursor_visible);
void (* set_user_resizable) (ClutterStageWindow *stage_window,
gboolean is_resizable);
GdkPixbuf * (* draw_to_pixbuf) (ClutterStageWindow *stage_window,
gint x,
gint y,
gint width,
gint height);
};
GType clutter_stage_window_get_type (void) G_GNUC_CONST;
G_END_DECLS
#endif /* __CLUTTER_STAGE_WINDOW_H__ */

View File

@ -29,6 +29,24 @@
* *
* #ClutterStage is a top level 'window' on which child actors are placed * #ClutterStage is a top level 'window' on which child actors are placed
* and manipulated. * and manipulated.
*
* Clutter creates a default stage upon initialization, which can be retrieved
* using clutter_stage_get_default(). Clutter always provides the default
* stage, unless the backend is unable to create one. The stage returned
* by clutter_stage_get_default() is guaranteed to always be the same.
*
* Backends might provide support for multiple stages. The support for this
* feature can be checked at run-time using the clutter_feature_available()
* function and the %CLUTTER_FEATURE_STAGE_MULTIPLE flag. If the backend used
* supports multiple stages, new #ClutterStage instances can be created
* using clutter_stage_new(). These stages must be managed by the developer
* using clutter_actor_destroy(), which will take care of destroying all the
* actors contained inside them.
*
* #ClutterStage is a proxy actor, wrapping the backend-specific
* implementation of the windowing system. It is possible to subclass
* #ClutterStage, as long as every overridden virtual function chains up to
* the parent class corresponding function.
*/ */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
@ -45,32 +63,36 @@
#include "clutter-private.h" #include "clutter-private.h"
#include "clutter-debug.h" #include "clutter-debug.h"
#include "clutter-stage-manager.h" #include "clutter-stage-manager.h"
#include "clutter-stage-window.h"
#include "clutter-version.h" /* For flavour */ #include "clutter-version.h" /* For flavour */
#include "clutter-id-pool.h" #include "clutter-id-pool.h"
#include "cogl.h" #include "cogl.h"
G_DEFINE_ABSTRACT_TYPE (ClutterStage, clutter_stage, CLUTTER_TYPE_GROUP); G_DEFINE_TYPE (ClutterStage, clutter_stage, CLUTTER_TYPE_GROUP);
#define CLUTTER_STAGE_GET_PRIVATE(obj) \ #define CLUTTER_STAGE_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_STAGE, ClutterStagePrivate)) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_STAGE, ClutterStagePrivate))
struct _ClutterStagePrivate struct _ClutterStagePrivate
{ {
/* the stage implementation */
ClutterActor *impl;
ClutterColor color; ClutterColor color;
ClutterPerspective perspective; ClutterPerspective perspective;
ClutterFog fog; ClutterFog fog;
gchar *title;
ClutterActor *key_focused_actor;
guint update_idle; /* repaint idler id */
guint is_fullscreen : 1; guint is_fullscreen : 1;
guint is_offscreen : 1; guint is_offscreen : 1;
guint is_cursor_visible : 1; guint is_cursor_visible : 1;
guint is_user_resizable : 1; guint is_user_resizable : 1;
guint use_fog : 1; guint use_fog : 1;
gchar *title;
ClutterActor *key_focused_actor;
guint update_idle; /* repaint idler id */
}; };
enum enum
@ -98,11 +120,37 @@ enum
static guint stage_signals[LAST_SIGNAL] = { 0, }; static guint stage_signals[LAST_SIGNAL] = { 0, };
static void
clutter_stage_request_coords (ClutterActor *self,
ClutterActorBox *box)
{
ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
g_assert (priv->impl != NULL);
CLUTTER_ACTOR_GET_CLASS (priv->impl)->request_coords (priv->impl, box);
CLUTTER_ACTOR_CLASS (clutter_stage_parent_class)->request_coords (self, box);
}
static void
clutter_stage_query_coords (ClutterActor *self,
ClutterActorBox *box)
{
ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
g_assert (priv->impl != NULL);
CLUTTER_ACTOR_GET_CLASS (priv->impl)->query_coords (priv->impl, box);
}
static void static void
clutter_stage_paint (ClutterActor *self) clutter_stage_paint (ClutterActor *self)
{ {
ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv; ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_ACTOR_IN_PAINT);
CLUTTER_NOTE (PAINT, "Initializing stage paint");
cogl_paint_init (&priv->color); cogl_paint_init (&priv->color);
if (priv->use_fog) if (priv->use_fog)
@ -113,6 +161,12 @@ clutter_stage_paint (ClutterActor *self)
priv->fog.z_far); priv->fog.z_far);
} }
CLUTTER_NOTE (PAINT, "Proxying the paint to the stage implementation");
clutter_actor_paint (priv->impl);
CLUTTER_UNSET_PRIVATE_FLAGS (self, CLUTTER_ACTOR_IN_PAINT);
/* this will take care of painting every child */
CLUTTER_ACTOR_CLASS (clutter_stage_parent_class)->paint (self); CLUTTER_ACTOR_CLASS (clutter_stage_parent_class)->paint (self);
} }
@ -128,6 +182,53 @@ clutter_stage_pick (ClutterActor *self,
CLUTTER_ACTOR_CLASS (clutter_stage_parent_class)->paint (self); CLUTTER_ACTOR_CLASS (clutter_stage_parent_class)->paint (self);
} }
static void
clutter_stage_realize (ClutterActor *self)
{
ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
/* then realize the implementation */
CLUTTER_ACTOR_GET_CLASS (priv->impl)->realize (priv->impl);
/* set the flag on the wrapper if the implementation was successful */
if (CLUTTER_ACTOR_IS_REALIZED (priv->impl))
CLUTTER_ACTOR_SET_FLAGS (self, CLUTTER_ACTOR_REALIZED);
}
static void
clutter_stage_unrealize (ClutterActor *self)
{
ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
/* unset the flag */
CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_REALIZED);
/* and then unrealize the implementation */
CLUTTER_ACTOR_GET_CLASS (priv->impl)->unrealize (priv->impl);
}
static void
clutter_stage_show (ClutterActor *self)
{
ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
g_assert (priv->impl != NULL);
clutter_actor_show (priv->impl);
CLUTTER_ACTOR_CLASS (clutter_stage_parent_class)->show (self);
}
static void
clutter_stage_hide (ClutterActor *self)
{
ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
g_assert (priv->impl != NULL);
clutter_actor_hide (priv->impl);
CLUTTER_ACTOR_CLASS (clutter_stage_parent_class)->hide (self);
}
static void static void
clutter_stage_set_property (GObject *object, clutter_stage_set_property (GObject *object,
guint prop_id, guint prop_id,
@ -248,6 +349,24 @@ clutter_stage_get_property (GObject *object,
static void static void
clutter_stage_dispose (GObject *object) clutter_stage_dispose (GObject *object)
{ {
ClutterStage *stage = CLUTTER_STAGE (object);
ClutterStagePrivate *priv = stage->priv;
ClutterStageManager *stage_manager = clutter_stage_manager_get_default ();
if (priv->update_idle)
{
g_source_remove (priv->update_idle);
priv->update_idle = 0;
}
_clutter_stage_manager_remove_stage (stage_manager, stage);
if (priv->impl)
{
CLUTTER_NOTE (MISC, "Disposing of the stage implementation");
g_object_unref (priv->impl);
priv->impl = NULL;
}
G_OBJECT_CLASS (clutter_stage_parent_class)->dispose (object); G_OBJECT_CLASS (clutter_stage_parent_class)->dispose (object);
} }
@ -255,11 +374,6 @@ clutter_stage_dispose (GObject *object)
static void static void
clutter_stage_finalize (GObject *object) clutter_stage_finalize (GObject *object)
{ {
ClutterStage *stage = CLUTTER_STAGE(object);
ClutterStageManager *stage_manager = clutter_stage_manager_get_default ();
_clutter_stage_manager_remove_stage (stage_manager, stage);
G_OBJECT_CLASS (clutter_stage_parent_class)->finalize (object); G_OBJECT_CLASS (clutter_stage_parent_class)->finalize (object);
} }
@ -275,8 +389,14 @@ clutter_stage_class_init (ClutterStageClass *klass)
gobject_class->dispose = clutter_stage_dispose; gobject_class->dispose = clutter_stage_dispose;
gobject_class->finalize = clutter_stage_finalize; gobject_class->finalize = clutter_stage_finalize;
actor_class->request_coords = clutter_stage_request_coords;
actor_class->query_coords = clutter_stage_query_coords;
actor_class->paint = clutter_stage_paint; actor_class->paint = clutter_stage_paint;
actor_class->pick = clutter_stage_pick; actor_class->pick = clutter_stage_pick;
actor_class->realize = clutter_stage_realize;
actor_class->unrealize = clutter_stage_unrealize;
actor_class->show = clutter_stage_show;
actor_class->hide = clutter_stage_hide;
/** /**
* ClutterStage:fullscreen: * ClutterStage:fullscreen:
@ -447,12 +567,28 @@ static void
clutter_stage_init (ClutterStage *self) clutter_stage_init (ClutterStage *self)
{ {
ClutterStagePrivate *priv; ClutterStagePrivate *priv;
ClutterBackend *backend;
/* a stage is a top-level object */ /* a stage is a top-level object */
CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_ACTOR_IS_TOPLEVEL); CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_ACTOR_IS_TOPLEVEL);
self->priv = priv = CLUTTER_STAGE_GET_PRIVATE (self); self->priv = priv = CLUTTER_STAGE_GET_PRIVATE (self);
CLUTTER_NOTE (BACKEND, "Creating stage from the default backend");
backend = clutter_get_default_backend ();
priv->impl = _clutter_backend_create_stage (backend, self, NULL);
if (!priv->impl)
{
g_warning ("Unable to create a new stage, falling back to the "
"default stage.");
priv->impl = CLUTTER_ACTOR (_clutter_stage_get_default_window ());
/* at this point we must have a default stage, or we're screwed */
g_assert (priv->impl != NULL);
}
else
g_object_ref_sink (priv->impl);
priv->is_offscreen = FALSE; priv->is_offscreen = FALSE;
priv->is_fullscreen = FALSE; priv->is_fullscreen = FALSE;
priv->is_user_resizable = FALSE; priv->is_user_resizable = FALSE;
@ -482,11 +618,12 @@ clutter_stage_init (ClutterStage *self)
/** /**
* clutter_stage_get_default: * clutter_stage_get_default:
* *
* Returns the main stage. #ClutterStage is a singleton, so * Returns the main stage. The default #ClutterStage is a singleton,
* the stage will be created the first time this function is * so the stage will be created the first time this function is
* called (typically, inside clutter_init()); all the subsequent * called (typically, inside clutter_init()); all the subsequent
* calls to clutter_stage_get_default() will return the same * calls to clutter_stage_get_default() will return the same instance.
* instance. *
* Clutter guarantess the existence of the default stage.
* *
* Return value: the main #ClutterStage. You should never * Return value: the main #ClutterStage. You should never
* destroy or unref the returned actor. * destroy or unref the returned actor.
@ -495,8 +632,19 @@ ClutterActor *
clutter_stage_get_default (void) clutter_stage_get_default (void)
{ {
ClutterStageManager *stage_manager = clutter_stage_manager_get_default (); ClutterStageManager *stage_manager = clutter_stage_manager_get_default ();
ClutterStage *stage;
return CLUTTER_ACTOR(clutter_stage_manager_get_default_stage(stage_manager)); stage = clutter_stage_manager_get_default_stage (stage_manager);
if (G_UNLIKELY (stage == NULL))
{
/* this will take care of automatically adding the stage
* to the stage manager and setting it as the default
*/
stage = g_object_new (CLUTTER_TYPE_STAGE, NULL);
g_object_ref_sink (stage);
}
return CLUTTER_ACTOR (stage);
} }
/** /**
@ -516,10 +664,8 @@ clutter_stage_set_color (ClutterStage *stage,
g_return_if_fail (color != NULL); g_return_if_fail (color != NULL);
priv = stage->priv; priv = stage->priv;
priv->color.red = color->red;
priv->color.green = color->green; priv->color = *color;
priv->color.blue = color->blue;
priv->color.alpha = color->alpha;
if (CLUTTER_ACTOR_IS_VISIBLE (stage)) if (CLUTTER_ACTOR_IS_VISIBLE (stage))
clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
@ -545,10 +691,7 @@ clutter_stage_get_color (ClutterStage *stage,
priv = stage->priv; priv = stage->priv;
color->red = priv->color.red; *color = priv->color;
color->green = priv->color.green;
color->blue = priv->color.blue;
color->alpha = priv->color.alpha;
} }
/** /**
@ -572,6 +715,9 @@ clutter_stage_set_perspectivex (ClutterStage *stage,
priv->perspective = *perspective; priv->perspective = *perspective;
/* this will cause the viewport to be reset; see
* clutter_maybe_setup_viewport() inside clutter-main.c
*/
CLUTTER_SET_PRIVATE_FLAGS (stage, CLUTTER_ACTOR_SYNC_MATRICES); CLUTTER_SET_PRIVATE_FLAGS (stage, CLUTTER_ACTOR_SYNC_MATRICES);
} }
@ -604,7 +750,7 @@ clutter_stage_get_perspectivex (ClutterStage *stage,
* @z_far: the distance from the viewer to the far clipping * @z_far: the distance from the viewer to the far clipping
* plane (always positive) * plane (always positive)
* *
* Set the stage perspective. * Sets the stage perspective.
* *
* Since: 0.4 * Since: 0.4
*/ */
@ -621,11 +767,14 @@ clutter_stage_set_perspective (ClutterStage *stage,
priv = stage->priv; priv = stage->priv;
priv->perspective.fovy = CLUTTER_FLOAT_TO_FIXED(fovy); priv->perspective.fovy = CLUTTER_FLOAT_TO_FIXED (fovy);
priv->perspective.aspect = CLUTTER_FLOAT_TO_FIXED(aspect); priv->perspective.aspect = CLUTTER_FLOAT_TO_FIXED (aspect);
priv->perspective.z_near = CLUTTER_FLOAT_TO_FIXED(z_near); priv->perspective.z_near = CLUTTER_FLOAT_TO_FIXED (z_near);
priv->perspective.z_far = CLUTTER_FLOAT_TO_FIXED(z_far); priv->perspective.z_far = CLUTTER_FLOAT_TO_FIXED (z_far);
/* this will cause the viewport to be reset; see
* clutter_maybe_setup_viewport() inside clutter-main.c
*/
CLUTTER_SET_PRIVATE_FLAGS (stage, CLUTTER_ACTOR_SYNC_MATRICES); CLUTTER_SET_PRIVATE_FLAGS (stage, CLUTTER_ACTOR_SYNC_MATRICES);
} }
@ -688,12 +837,17 @@ clutter_stage_fullscreen (ClutterStage *stage)
priv = stage->priv; priv = stage->priv;
if (!priv->is_fullscreen) if (!priv->is_fullscreen)
{ {
ClutterStageWindow *impl = CLUTTER_STAGE_WINDOW (priv->impl);
ClutterStageWindowIface *iface;
iface = CLUTTER_STAGE_WINDOW_GET_IFACE (impl);
/* Only set if backend implements. /* Only set if backend implements.
* Also see clutter_stage_event() for setting priv->is_fullscreen * Also see clutter_stage_event() for setting priv->is_fullscreen
* on state change event. * on state change event.
*/ */
if (CLUTTER_STAGE_GET_CLASS (stage)->set_fullscreen) if (iface->set_fullscreen)
CLUTTER_STAGE_GET_CLASS (stage)->set_fullscreen (stage, TRUE); iface->set_fullscreen (impl, TRUE);
} }
} }
@ -717,9 +871,17 @@ clutter_stage_unfullscreen (ClutterStage *stage)
priv = stage->priv; priv = stage->priv;
if (priv->is_fullscreen) if (priv->is_fullscreen)
{ {
/* Only set if backend implements */ ClutterStageWindow *impl = CLUTTER_STAGE_WINDOW (priv->impl);
if (CLUTTER_STAGE_GET_CLASS (stage)->set_fullscreen) ClutterStageWindowIface *iface;
CLUTTER_STAGE_GET_CLASS (stage)->set_fullscreen (stage, FALSE);
iface = CLUTTER_STAGE_WINDOW_GET_IFACE (impl);
/* Only set if backend implements.
* Also see clutter_stage_event() for setting priv->is_fullscreen
* on state change event.
*/
if (iface->set_fullscreen)
iface->set_fullscreen (impl, FALSE);
} }
} }
@ -744,14 +906,20 @@ clutter_stage_set_user_resizable (ClutterStage *stage,
priv = stage->priv; priv = stage->priv;
if (clutter_feature_available (CLUTTER_FEATURE_STAGE_USER_RESIZE) if (clutter_feature_available (CLUTTER_FEATURE_STAGE_USER_RESIZE)
&& priv->is_user_resizable != resizable && priv->is_user_resizable != resizable)
&& CLUTTER_STAGE_GET_CLASS (stage)->set_user_resize)
{ {
priv->is_user_resizable = resizable; ClutterStageWindow *impl = CLUTTER_STAGE_WINDOW (priv->impl);
ClutterStageWindowIface *iface;
CLUTTER_STAGE_GET_CLASS (stage)->set_user_resize (stage, resizable); iface = CLUTTER_STAGE_WINDOW_GET_IFACE (impl);
if (iface->set_user_resizable)
{
priv->is_user_resizable = resizable;
g_object_notify (G_OBJECT (stage), "user-resizable"); iface->set_user_resizable (impl, resizable);
g_object_notify (G_OBJECT (stage), "user-resizable");
}
} }
} }
@ -789,12 +957,18 @@ clutter_stage_show_cursor (ClutterStage *stage)
priv = stage->priv; priv = stage->priv;
if (!priv->is_cursor_visible) if (!priv->is_cursor_visible)
{ {
priv->is_cursor_visible = TRUE; ClutterStageWindow *impl = CLUTTER_STAGE_WINDOW (priv->impl);
ClutterStageWindowIface *iface;
if (CLUTTER_STAGE_GET_CLASS (stage)->set_cursor_visible) iface = CLUTTER_STAGE_WINDOW_GET_IFACE (impl);
CLUTTER_STAGE_GET_CLASS (stage)->set_cursor_visible (stage, TRUE); if (iface->set_cursor_visible)
{
priv->is_cursor_visible = TRUE;
g_object_notify (G_OBJECT (stage), "cursor-visible"); iface->set_cursor_visible (impl, TRUE);
g_object_notify (G_OBJECT (stage), "cursor-visible");
}
} }
} }
@ -816,12 +990,18 @@ clutter_stage_hide_cursor (ClutterStage *stage)
priv = stage->priv; priv = stage->priv;
if (priv->is_cursor_visible) if (priv->is_cursor_visible)
{ {
priv->is_cursor_visible = FALSE; ClutterStageWindow *impl = CLUTTER_STAGE_WINDOW (priv->impl);
ClutterStageWindowIface *iface;
if (CLUTTER_STAGE_GET_CLASS (stage)->set_cursor_visible) iface = CLUTTER_STAGE_WINDOW_GET_IFACE (impl);
CLUTTER_STAGE_GET_CLASS (stage)->set_cursor_visible (stage, FALSE); if (iface->set_cursor_visible)
{
priv->is_cursor_visible = TRUE;
g_object_notify (G_OBJECT (stage), "cursor-visible"); iface->set_cursor_visible (impl, FALSE);
g_object_notify (G_OBJECT (stage), "cursor-visible");
}
} }
} }
@ -837,8 +1017,9 @@ clutter_stage_hide_cursor (ClutterStage *stage)
* *
* Gets a pixel based representation of the current rendered stage. * Gets a pixel based representation of the current rendered stage.
* *
* Return value: pixel representation as a #GdkPixbuf * Return value: pixel representation as a #GdkPixbuf, or %NULL if
**/ * the backend does not support this operation
*/
GdkPixbuf * GdkPixbuf *
clutter_stage_snapshot (ClutterStage *stage, clutter_stage_snapshot (ClutterStage *stage,
gint x, gint x,
@ -846,14 +1027,17 @@ clutter_stage_snapshot (ClutterStage *stage,
gint width, gint width,
gint height) gint height)
{ {
ClutterStageClass *klass; ClutterStageWindow *impl;
ClutterStageWindowIface *iface;
g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL); g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL);
g_return_val_if_fail (x >= 0 && y >= 0, NULL); g_return_val_if_fail (x >= 0 && y >= 0, NULL);
klass = CLUTTER_STAGE_GET_CLASS (stage); impl = CLUTTER_STAGE_WINDOW (stage->priv->impl);
if (klass->draw_to_pixbuf) iface = CLUTTER_STAGE_WINDOW_GET_IFACE (impl);
return klass->draw_to_pixbuf (stage, x, y, width, height);
if (iface->draw_to_pixbuf)
return iface->draw_to_pixbuf (impl, x, y, width, height);
return NULL; return NULL;
} }
@ -883,8 +1067,9 @@ clutter_stage_get_actor_at_pos (ClutterStage *stage,
* @event: a #ClutterEvent * @event: a #ClutterEvent
* *
* This function is used to emit an event on the main stage. * This function is used to emit an event on the main stage.
*
* You should rarely need to use this function, except for * You should rarely need to use this function, except for
* synthetising events. * synthetised events.
* *
* Return value: the return value from the signal emission * Return value: the return value from the signal emission
* *
@ -960,6 +1145,7 @@ clutter_stage_set_title (ClutterStage *stage,
const gchar *title) const gchar *title)
{ {
ClutterStagePrivate *priv; ClutterStagePrivate *priv;
ClutterStageWindow *impl;
g_return_if_fail (CLUTTER_IS_STAGE (stage)); g_return_if_fail (CLUTTER_IS_STAGE (stage));
@ -968,8 +1154,8 @@ clutter_stage_set_title (ClutterStage *stage,
g_free (priv->title); g_free (priv->title);
priv->title = g_strdup (title); priv->title = g_strdup (title);
if (CLUTTER_STAGE_GET_CLASS (stage)->set_title) impl = CLUTTER_STAGE_WINDOW (priv->impl);
CLUTTER_STAGE_GET_CLASS (stage)->set_title (stage, priv->title); CLUTTER_STAGE_WINDOW_GET_IFACE (impl)->set_title (impl, priv->title);
g_object_notify (G_OBJECT (stage), "title"); g_object_notify (G_OBJECT (stage), "title");
} }
@ -1373,7 +1559,7 @@ clutter_fog_get_type (void)
} }
/** /**
* clutter_stage_create_new: * clutter_stage_new:
* *
* Creates a new, non-default stage. A non-default stage is a new * Creates a new, non-default stage. A non-default stage is a new
* top-level actor which can be used as another container. It works * top-level actor which can be used as another container. It works
@ -1388,15 +1574,13 @@ clutter_fog_get_type (void)
* *
* Return value: a new stage, or %NULL if the default backend does * Return value: a new stage, or %NULL if the default backend does
* not support multiple stages. Use clutter_actor_destroy() to * not support multiple stages. Use clutter_actor_destroy() to
* close the returned stage. * programmatically close the returned stage.
* *
* Since: 0.8 * Since: 0.8
*/ */
ClutterActor* ClutterActor *
clutter_stage_create_new (void) clutter_stage_new (void)
{ {
ClutterBackend *backend = clutter_get_default_backend ();
GError *error = NULL;
ClutterActor *retval; ClutterActor *retval;
if (!clutter_feature_available (CLUTTER_FEATURE_STAGE_MULTIPLE)) if (!clutter_feature_available (CLUTTER_FEATURE_STAGE_MULTIPLE))
@ -1407,15 +1591,11 @@ clutter_stage_create_new (void)
return NULL; return NULL;
} }
retval = _clutter_backend_create_stage (backend, &error); retval = g_object_new (CLUTTER_TYPE_STAGE, NULL);
if (error) if (retval)
{ return g_object_ref_sink (retval);
g_warning ("Unable to create a secondary stage: %s", error->message);
g_error_free (error);
retval = NULL;
}
return retval; return NULL;
} }
/** /**
@ -1431,24 +1611,23 @@ clutter_stage_create_new (void)
void void
clutter_stage_ensure_current (ClutterStage *stage) clutter_stage_ensure_current (ClutterStage *stage)
{ {
ClutterMainContext *ctx; ClutterMainContext *ctx = clutter_context_get_default ();
g_return_if_fail (CLUTTER_IS_STAGE (stage)); g_return_if_fail (CLUTTER_IS_STAGE (stage));
ctx = clutter_context_get_default ();
_clutter_backend_ensure_context (ctx->backend, stage); _clutter_backend_ensure_context (ctx->backend, stage);
} }
static gboolean static gboolean
redraw_update_idle (gpointer data) redraw_update_idle (gpointer user_data)
{ {
ClutterStage *stage = CLUTTER_STAGE(data); ClutterStage *stage = user_data;
ClutterStagePrivate *priv = stage->priv;
if (stage->priv->update_idle) if (priv->update_idle)
{ {
g_source_remove (stage->priv->update_idle); g_source_remove (priv->update_idle);
stage->priv->update_idle = 0; priv->update_idle = 0;
} }
CLUTTER_NOTE (MULTISTAGE, "redrawing via idle for stage:%p", stage); CLUTTER_NOTE (MULTISTAGE, "redrawing via idle for stage:%p", stage);
@ -1461,8 +1640,10 @@ redraw_update_idle (gpointer data)
* clutter_stage_queue_redraw: * clutter_stage_queue_redraw:
* @stage: the #ClutterStage * @stage: the #ClutterStage
* *
* Queues a redraw for the passed stage. Note applications should call * Queues a redraw for the passed stage.
* #clutter_actor_queue_redraw over this. *
* <note>Applications should call clutter_actor_queue_redraw() and not
* this function.</note>
* *
* Since: 0.8 * Since: 0.8
*/ */
@ -1483,3 +1664,32 @@ clutter_stage_queue_redraw (ClutterStage *stage)
NULL); NULL);
} }
} }
void
_clutter_stage_set_window (ClutterStage *stage,
ClutterStageWindow *stage_window)
{
g_return_if_fail (CLUTTER_IS_STAGE (stage));
g_return_if_fail (CLUTTER_IS_STAGE_WINDOW (stage_window));
if (stage->priv->impl)
g_object_unref (stage->priv->impl);
stage->priv->impl = CLUTTER_ACTOR (stage_window);
}
ClutterStageWindow *
_clutter_stage_get_window (ClutterStage *stage)
{
g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL);
return CLUTTER_STAGE_WINDOW (stage->priv->impl);
}
ClutterStageWindow *
_clutter_stage_get_default_window (void)
{
ClutterActor *stage = clutter_stage_get_default ();
return _clutter_stage_get_window (CLUTTER_STAGE (stage));
}

View File

@ -99,26 +99,11 @@ struct _ClutterStageClass
ClutterGroupClass parent_class; ClutterGroupClass parent_class;
/*< public >*/ /*< public >*/
/* vfuncs, not signals */ /* signals */
void (* set_fullscreen) (ClutterStage *stage, void (* fullscreen) (ClutterStage *stage);
gboolean fullscreen); void (* unfullscreen) (ClutterStage *stage);
void (* set_cursor_visible) (ClutterStage *stage, void (* activate) (ClutterStage *stage);
gboolean visible); void (* deactivate) (ClutterStage *stage);
GdkPixbuf* (* draw_to_pixbuf) (ClutterStage *stage,
gint x,
gint y,
gint width,
gint height);
void (* set_title) (ClutterStage *stage,
const gchar *title);
void (* set_user_resize) (ClutterStage *stage,
gboolean value);
/* events */
void (* fullscreen) (ClutterStage *stage);
void (* unfullscreen) (ClutterStage *stage);
void (* activate) (ClutterStage *stage);
void (* deactivate) (ClutterStage *stage);
/*< private >*/ /*< private >*/
/* padding for future expansion */ /* padding for future expansion */
@ -175,6 +160,8 @@ GType clutter_fog_get_type (void) G_GNUC_CONST;
GType clutter_stage_get_type (void) G_GNUC_CONST; GType clutter_stage_get_type (void) G_GNUC_CONST;
ClutterActor *clutter_stage_get_default (void); ClutterActor *clutter_stage_get_default (void);
ClutterActor *clutter_stage_new (void);
void clutter_stage_set_color (ClutterStage *stage, void clutter_stage_set_color (ClutterStage *stage,
const ClutterColor *color); const ClutterColor *color);
void clutter_stage_get_color (ClutterStage *stage, void clutter_stage_get_color (ClutterStage *stage,
@ -233,15 +220,12 @@ void clutter_stage_get_fogx (ClutterStage *stage,
gdouble clutter_stage_get_resolution (ClutterStage *stage); gdouble clutter_stage_get_resolution (ClutterStage *stage);
ClutterFixed clutter_stage_get_resolutionx (ClutterStage *stage); ClutterFixed clutter_stage_get_resolutionx (ClutterStage *stage);
/* New experiental calls */
void clutter_stage_set_key_focus (ClutterStage *stage, void clutter_stage_set_key_focus (ClutterStage *stage,
ClutterActor *actor); ClutterActor *actor);
ClutterActor * clutter_stage_get_key_focus (ClutterStage *stage); ClutterActor * clutter_stage_get_key_focus (ClutterStage *stage);
ClutterActor* clutter_stage_create_new (void); /* New experiental calls */
void clutter_stage_ensure_current (ClutterStage *stage); void clutter_stage_ensure_current (ClutterStage *stage);
void clutter_stage_queue_redraw (ClutterStage *stage); void clutter_stage_queue_redraw (ClutterStage *stage);
/* Commodity macro */ /* Commodity macro */

View File

@ -23,11 +23,12 @@ clutter_backend_egl_post_parse (ClutterBackend *backend,
{ {
EGLBoolean status; EGLBoolean status;
backend_egl->edpy = eglGetDisplay((NativeDisplayType)backend_x11->xdpy); backend_egl->edpy =
eglGetDisplay ((NativeDisplayType) backend_x11->xdpy);
status = eglInitialize(backend_egl->edpy, status = eglInitialize (backend_egl->edpy,
&backend_egl->egl_version_major, &backend_egl->egl_version_major,
&backend_egl->egl_version_minor); &backend_egl->egl_version_minor);
if (status != EGL_TRUE) if (status != EGL_TRUE)
{ {
@ -47,17 +48,80 @@ clutter_backend_egl_post_parse (ClutterBackend *backend,
} }
static void static void
clutter_backend_egl_redraw (ClutterBackend *backend) clutter_backend_egl_ensure_context (ClutterBackend *backend,
ClutterStage *stage)
{ {
ClutterBackendEGL *backend_egl = CLUTTER_BACKEND_EGL (backend); ClutterBackendEGL *backend_egl = CLUTTER_BACKEND_EGL (backend);
ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend);
ClutterStageEGL *stage_egl;
ClutterStageX11 *stage_x11;
stage_x11 = CLUTTER_STAGE_X11(backend_x11->stage); if (stage == NULL)
stage_egl = CLUTTER_STAGE_EGL(backend_x11->stage); {
CLUTTER_NOTE (BACKEND, "Clearing EGL context");
eglMakeCurrent (backend_egl->edpy,
EGL_NO_SURFACE,
EGL_NO_SURFACE,
EGL_NO_CONTEXT);
}
else
{
ClutterStageWindow *impl;
ClutterStageEGL *stage_egl;
ClutterStageX11 *stage_x11;
clutter_actor_paint (CLUTTER_ACTOR(stage_egl)); impl = _clutter_stage_get_window (stage);
g_assert (impl != NULL);
CLUTTER_NOTE (MULTISTAGE, "Setting context for stage of type %s [%p]",
g_type_name (G_OBJECT_TYPE (impl)),
impl);
stage_egl = CLUTTER_STAGE_EGL (impl);
stage_x11 = CLUTTER_STAGE_X11 (impl);
g_return_if_fail (backend_egl->egl_context != NULL);
/* we might get here inside the final dispose cycle, so we
* need to handle this gracefully
*/
if (stage_x11->xwin == None ||
stage_egl->egl_surface == EGL_NO_SURFACE)
{
CLUTTER_NOTE (MULTISTAGE,
"Received a stale stage, clearing all context");
eglMakeCurrent (backend_egl->edpy,
EGL_NO_SURFACE,
EGL_NO_SURFACE,
EGL_NO_CONTEXT);
}
else
eglMakeCurrent (backend_egl->edpy,
stage_egl->egl_surface,
stage_egl->egl_surface,
backend_egl->egl_context);
}
}
static void
clutter_backend_egl_redraw (ClutterBackend *backend,
ClutterStage *stage)
{
ClutterBackendEGL *backend_egl = CLUTTER_BACKEND_EGL (backend);
ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend);
ClutterStageWindow *impl;
ClutterStageEGL *stage_egl;
ClutterStageX11 *stage_x11;
impl = _clutter_stage_get_window (stage);
if (!impl)
return;
g_assert (CLUTTER_IS_STAGE_EGL (impl));
stage_x11 = CLUTTER_STAGE_X11 (impl);
stage_egl = CLUTTER_STAGE_EGL (impl);
/* this will cause the stage implementation to be painted as well */
clutter_actor_paint (CLUTTER_ACTOR (stage));
/* Why this paint is done in backend as likely GL windowing system /* Why this paint is done in backend as likely GL windowing system
* specific calls, like swapping buffers. * specific calls, like swapping buffers.
@ -88,6 +152,12 @@ clutter_backend_egl_dispose (GObject *gobject)
{ {
ClutterBackendEGL *backend_egl = CLUTTER_BACKEND_EGL (gobject); ClutterBackendEGL *backend_egl = CLUTTER_BACKEND_EGL (gobject);
if (backend_egl->egl_context)
{
eglDestroyContext (backend_egl->edpy, backend_egl->egl_context);
backend_egl->egl_context = NULL;
}
if (backend_egl->edpy) if (backend_egl->edpy)
{ {
eglTerminate (backend_egl->edpy); eglTerminate (backend_egl->edpy);
@ -133,46 +203,46 @@ clutter_backend_egl_get_features (ClutterBackend *backend)
"EGL_VENDOR: %s\n", "EGL_VENDOR: %s\n",
"EGL_VERSION: %s\n", "EGL_VERSION: %s\n",
"EGL_EXTENSIONS: %s\n", "EGL_EXTENSIONS: %s\n",
glGetString(GL_VENDOR), glGetString (GL_VENDOR),
glGetString(GL_RENDERER), glGetString (GL_RENDERER),
glGetString(GL_VERSION), glGetString (GL_VERSION),
eglQueryString(backend_egl->edpy, EGL_VENDOR), eglQueryString (backend_egl->edpy, EGL_VENDOR),
eglQueryString(backend_egl->edpy, EGL_VERSION), eglQueryString (backend_egl->edpy, EGL_VERSION),
eglQueryString(backend_egl->edpy, EGL_EXTENSIONS)); eglQueryString (backend_egl->edpy, EGL_EXTENSIONS));
/* We can actually resize too */ /* We can actually resize too */
return CLUTTER_FEATURE_STAGE_CURSOR|CLUTTER_FEATURE_STAGE_MULTIPLE; return CLUTTER_FEATURE_STAGE_CURSOR|CLUTTER_FEATURE_STAGE_MULTIPLE;
} }
static gboolean static ClutterActor *
clutter_backend_egl_init_stage (ClutterBackend *backend, clutter_backend_egl_create_stage (ClutterBackend *backend,
GError **error) ClutterStage *wrapper,
GError **error)
{ {
ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend); ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend);
if (!backend_x11->stage) CLUTTER_NOTE (BACKEND, "Creating stage of type `%s'",
{ g_type_name (CLUTTER_STAGE_TYPE));
ClutterStageX11 *stage_x11;
ClutterActor *stage;
stage = g_object_new (CLUTTER_TYPE_STAGE_EGL, NULL); stage = g_object_new (CLUTTER_STAGE_TYPE, NULL);
/* copy backend data into the stage */ /* copy backend data into the stage */
stage_x11 = CLUTTER_STAGE_X11 (stage); stage_x11 = CLUTTER_STAGE_X11 (stage);
stage_x11->xdpy = backend_x11->xdpy; stage_x11->xdpy = backend_x11->xdpy;
stage_x11->xwin_root = backend_x11->xwin_root; stage_x11->xwin_root = backend_x11->xwin_root;
stage_x11->xscreen = backend_x11->xscreen_num; stage_x11->xscreen = backend_x11->xscreen_num;
stage_x11->backend = backend_x11; stage_x11->backend = backend_x11;
stage_x11->wrapper = wrapper;
CLUTTER_NOTE (MISC, "X11 stage created (display:%p, screen:%d, root:%u)", /* set the pointer back into the wrapper */
stage_x11->xdpy, _clutter_stage_set_window (wrapper, CLUTTER_STAGE_WINDOW (stage));
stage_x11->xscreen,
(unsigned int) stage_x11->xwin_root);
g_object_set_data (G_OBJECT (stage), "clutter-backend", backend); CLUTTER_NOTE (MISC, "EGLX stage created (display:%p, screen:%d, root:%u)",
stage_x11->xdpy,
stage_x11->xscreen,
(unsigned int) stage_x11->xwin_root);
backend_x11->stage = g_object_ref_sink (stage); g_object_set_data (G_OBJECT (stage), "clutter-backend", backend);
}
clutter_actor_realize (backend_x11->stage); clutter_actor_realize (backend_x11->stage);
@ -181,10 +251,11 @@ clutter_backend_egl_init_stage (ClutterBackend *backend,
g_set_error (error, CLUTTER_INIT_ERROR, g_set_error (error, CLUTTER_INIT_ERROR,
CLUTTER_INIT_ERROR_INTERNAL, CLUTTER_INIT_ERROR_INTERNAL,
"Unable to realize the main stage"); "Unable to realize the main stage");
return FALSE; g_object_unref (stage);
return NULL;
} }
return TRUE; return stage;
} }
static void static void
@ -194,19 +265,20 @@ clutter_backend_egl_class_init (ClutterBackendEGLClass *klass)
ClutterBackendClass *backend_class = CLUTTER_BACKEND_CLASS (klass); ClutterBackendClass *backend_class = CLUTTER_BACKEND_CLASS (klass);
gobject_class->constructor = clutter_backend_egl_constructor; gobject_class->constructor = clutter_backend_egl_constructor;
gobject_class->dispose = clutter_backend_egl_dispose; gobject_class->dispose = clutter_backend_egl_dispose;
gobject_class->finalize = clutter_backend_egl_finalize; gobject_class->finalize = clutter_backend_egl_finalize;
backend_class->post_parse = clutter_backend_egl_post_parse; backend_class->post_parse = clutter_backend_egl_post_parse;
backend_class->redraw = clutter_backend_egl_redraw; backend_class->redraw = clutter_backend_egl_redraw;
backend_class->get_features = clutter_backend_egl_get_features; backend_class->get_features = clutter_backend_egl_get_features;
backend_class->init_stage = clutter_backend_egl_init_stage; backend_class->create_stage = clutter_backend_egl_create_stage;
backend_class->ensure_context = clutter_backend_egl_ensure_context;
} }
static void static void
clutter_backend_egl_init (ClutterBackendEGL *backend_egl) clutter_backend_egl_init (ClutterBackendEGL *backend_egl)
{ {
;
} }
GType GType

View File

@ -52,7 +52,11 @@ struct _ClutterBackendEGL
/* EGL Specific */ /* EGL Specific */
EGLDisplay edpy; EGLDisplay edpy;
gint egl_version_major, egl_version_minor; EGLSurface egl_surface;
EGLContext egl_context;
gint egl_version_major;
gint egl_version_minor;
}; };

View File

@ -2,6 +2,7 @@
#include "config.h" #include "config.h"
#endif #endif
#include "clutter-backend-egl.h"
#include "clutter-stage-egl.h" #include "clutter-stage-egl.h"
#include "clutter-eglx.h" #include "clutter-eglx.h"
@ -14,8 +15,17 @@
#include "../clutter-private.h" #include "../clutter-private.h"
#include "../clutter-debug.h" #include "../clutter-debug.h"
#include "../clutter-units.h" #include "../clutter-units.h"
#include "../clutter-container.h"
#include "../clutter-stage.h"
#include "../clutter-stage-window.h"
G_DEFINE_TYPE (ClutterStageEGL, clutter_stage_egl, CLUTTER_TYPE_STAGE_X11); static void clutter_stage_window_iface_init (ClutterStageWindowIface *iface);
G_DEFINE_TYPE_WITH_CODE (ClutterStageEGL,
clutter_stage_egl,
CLUTTER_TYPE_STAGE_X11,
G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_STAGE_WINDOW,
clutter_stage_window_iface_init));
static void static void
clutter_stage_egl_unrealize (ClutterActor *actor) clutter_stage_egl_unrealize (ClutterActor *actor)
@ -26,17 +36,19 @@ clutter_stage_egl_unrealize (ClutterActor *actor)
CLUTTER_MARK(); CLUTTER_MARK();
g_object_get (actor, "offscreen", &was_offscreen, NULL); g_object_get (stage_x11->wrapper, "offscreen", &was_offscreen, NULL);
CLUTTER_ACTOR_CLASS (clutter_stage_egl_parent_class)->unrealize (actor);
clutter_x11_trap_x_errors ()
if (G_UNLIKELY (was_offscreen)) if (G_UNLIKELY (was_offscreen))
{ {
/* No support as yet for this */ /* No support as yet for this */
} }
else else
{ {
if (stage_x11->xwin != None) if (!stage_X11->is_foreign_xwin && stage_x11->xwin != None)
{ {
XDestroyWindow (stage_x11->xdpy, stage_x11->xwin); XDestroyWindow (stage_x11->xdpy, stage_x11->xwin);
stage_x11->xwin = None; stage_x11->xwin = None;
@ -46,134 +58,137 @@ clutter_stage_egl_unrealize (ClutterActor *actor)
} }
if (stage_egl->egl_surface) if (stage_egl->egl_surface)
eglDestroySurface (clutter_eglx_display(), stage_egl->egl_surface); {
stage_egl->egl_surface = NULL; eglDestroySurface (clutter_eglx_display (), stage_egl->egl_surface);
stage_egl->egl_surface = EGL_NO_SURFACE;
}
if (stage_egl->egl_context) clutter_stage_ensure_current (stage_x11->wrapper);
eglDestroyContext (clutter_eglx_display(), stage_egl->egl_context);
stage_egl->egl_context = NULL;
eglMakeCurrent (clutter_eglx_display(), /* XSync (stage_x11->xdpy, False); */
EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
stage_egl->egl_context = None; clutter_x11_untrap_x_errors ();
CLUTTER_MARK ();
} }
static void static void
clutter_stage_egl_realize (ClutterActor *actor) clutter_stage_egl_realize (ClutterActor *actor)
{ {
ClutterStageEGL *stage_egl = CLUTTER_STAGE_EGL (actor); ClutterStageEGL *stage_egl = CLUTTER_STAGE_EGL (actor);
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (actor); ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (actor);
ClutterBackendEGL *backend_egl;
EGLConfig configs[2]; EGLConfig configs[2];
EGLint config_count; EGLint config_count;
EGLBoolean status; EGLBoolean status;
gboolean is_offscreen = FALSE;
gboolean is_offscreen;
CLUTTER_NOTE (BACKEND, "Realizing main stage"); CLUTTER_NOTE (BACKEND, "Realizing main stage");
g_object_get (actor, "offscreen", &is_offscreen, NULL); g_object_get (stage_x11->wrapper, "offscreen", &is_offscreen, NULL);
backend_egl = CLUTTER_BACKEND_EGL (clutter_get_default_backend ());
if (G_LIKELY (!is_offscreen)) if (G_LIKELY (!is_offscreen))
{ {
EGLint cfg_attribs[] = { EGL_BUFFER_SIZE, EGL_DONT_CARE, EGLint cfg_attribs[] = {
EGL_RED_SIZE, 5, EGL_BUFFER_SIZE, EGL_DONT_CARE,
EGL_GREEN_SIZE, 6, EGL_RED_SIZE, 5,
EGL_BLUE_SIZE, 5, EGL_GREEN_SIZE, 6,
EGL_NONE }; EGL_BLUE_SIZE, 5,
EGL_NONE
};
status = eglGetConfigs (clutter_eglx_display(), status = eglGetConfigs (backend_egl->edpy,
configs, configs,
2, 2,
&config_count); &config_count);
if (status != EGL_TRUE) if (status != EGL_TRUE)
g_warning ("eglGetConfigs"); g_warning ("eglGetConfigs failed");
status = eglChooseConfig (clutter_eglx_display(), status = eglChooseConfig (backend_egl->edpy,
cfg_attribs, cfg_attribs,
configs, configs,
sizeof configs / sizeof configs[0], G_N_ELEMENTS (configs),
&config_count); &config_count);
if (status != EGL_TRUE) if (status != EGL_TRUE)
g_warning ("eglChooseConfig"); g_warning ("eglChooseConfig failed");
if (stage_x11->xwin == None) if (stage_x11->xwin == None)
stage_x11->xwin stage_x11->xwin =
= XCreateSimpleWindow(stage_x11->xdpy, XCreateSimpleWindow (stage_x11->xdpy,
stage_x11->xwin_root, stage_x11->xwin_root,
0, 0, 0, 0,
stage_x11->xwin_width, stage_x11->xwin_width,
stage_x11->xwin_height, stage_x11->xwin_height,
0, 0, 0, 0,
WhitePixel (stage_x11->xdpy, WhitePixel (stage_x11->xdpy,
stage_x11->xscreen)); stage_x11->xscreen));
XSelectInput(stage_x11->xdpy, XSelectInput (stage_x11->xdpy, stage_x11->xwin,
stage_x11->xwin, StructureNotifyMask
StructureNotifyMask | ExposureMask
|ExposureMask /* FIXME: we may want to eplicity enable MotionMask */
/* FIXME: we may want to eplicity enable MotionMask */ | PointerMotionMask
|PointerMotionMask | KeyPressMask
|KeyPressMask | KeyReleaseMask
|KeyReleaseMask | ButtonPressMask
|ButtonPressMask | ButtonReleaseMask
|ButtonReleaseMask | PropertyChangeMask);
|PropertyChangeMask);
if (stage_egl->egl_context) if (stage_egl->egl_surface != EGL_NO_SURFACE)
eglDestroyContext (clutter_eglx_display(), stage_egl->egl_context); {
eglDestroySurface (backend_egl->edpy, stage_egl->egl_surface);
stage_egl->egl_surface = EGL_NO_SURFACE;
}
if (stage_egl->egl_surface) stage_egl->egl_surface =
eglDestroySurface (clutter_eglx_display(), stage_egl->egl_surface); eglCreateWindowSurface (backend_egl->edpy,
configs[0],
stage_egl->egl_surface (NativeWindowType) stage_x11->xwin,
= eglCreateWindowSurface (clutter_eglx_display(), NULL);
configs[0],
(NativeWindowType)stage_x11->xwin,
NULL);
if (stage_egl->egl_surface == EGL_NO_SURFACE) if (stage_egl->egl_surface == EGL_NO_SURFACE)
g_warning ("eglCreateWindowSurface"); {
g_critical ("Unable to create an EGL surface");
stage_egl->egl_context = eglCreateContext (clutter_eglx_display(), CLUTTER_ACTOR_UNSET_FLAGS (stage_x11->wrapper, CLUTTER_ACTOR_REALIZED);
configs[0], CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED);
EGL_NO_CONTEXT, return;
NULL); }
if (stage_egl->egl_context == EGL_NO_CONTEXT) if (G_UNLIKELY (backend_egl->egl_context == None))
g_warning ("eglCreateContext"); {
CLUTTER_NOTE (GL, "Creating EGL Context");
status = eglMakeCurrent (clutter_eglx_display(), backend_egl->egl_context = eglCreateContext (backend_egl->edpy,
stage_egl->egl_surface, configs[0],
stage_egl->egl_surface, EGL_NO_CONTEXT,
stage_egl->egl_context); NULL);
if (status != EGL_TRUE) if (backend_egl->egl_context == EGL_NO_CONTEXT)
g_warning ("eglMakeCurrent"); {
g_critical ("Unable to create a suitable EGL context");
CLUTTER_ACTOR_UNSET_FLAGS (stage_x11->wrapper, CLUTTER_ACTOR_REALIZED);
CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED);
return;
}
}
CLUTTER_ACTOR_SET_FLAGS (stage_x11->wrapper, CLUTTER_ACTOR_REALIZED);
clutter_stage_ensure_current (stage_x11->wrapper);
} }
else else
{ {
g_warning("EGL Backend does not yet support offscreen rendering\n"); g_warning("EGLX Backend does not support offscreen rendering");
CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED); CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED);
return; return;
} }
CLUTTER_SET_PRIVATE_FLAGS(actor, CLUTTER_ACTOR_SYNC_MATRICES); CLUTTER_SET_PRIVATE_FLAGS (stage_x11->wrapper, CLUTTER_ACTOR_SYNC_MATRICES);
}
static GdkPixbuf*
clutter_stage_egl_draw_to_pixbuf (ClutterStage *stage,
gint x,
gint y,
gint width,
gint height)
{
g_warning ("Stage of type `%s' do not support ClutterStage::draw_to_pixbuf",
G_OBJECT_TYPE_NAME (stage));
return NULL;
} }
static void static void
@ -188,24 +203,40 @@ clutter_stage_egl_dispose (GObject *gobject)
G_OBJECT_CLASS (clutter_stage_egl_parent_class)->dispose (gobject); G_OBJECT_CLASS (clutter_stage_egl_parent_class)->dispose (gobject);
} }
static GdkPixbuf *
clutter_stage_egl_draw_to_pixbuf (ClutterStageWindow *stage_window,
gint x,
gint y,
gint width,
gint height)
{
g_warning ("Stages of type `%s' do not support "
"ClutterStageWindow::draw_to_pixbuf",
G_OBJECT_TYPE_NAME (stage));
return NULL;
}
static void
clutter_stage_window_iface_init (ClutterStageWindowIface *iface)
{
iface->draw_to_pixbuf = clutter_stage_egl_draw_to_pixbuf;
/* the rest is inherited from ClutterStageX11 */
}
static void static void
clutter_stage_egl_class_init (ClutterStageEGLClass *klass) clutter_stage_egl_class_init (ClutterStageEGLClass *klass)
{ {
GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
ClutterStageClass *stage_class = CLUTTER_STAGE_CLASS (klass);
gobject_class->dispose = clutter_stage_egl_dispose; gobject_class->dispose = clutter_stage_egl_dispose;
actor_class->realize = clutter_stage_egl_realize; actor_class->realize = clutter_stage_egl_realize;
actor_class->unrealize = clutter_stage_egl_unrealize; actor_class->unrealize = clutter_stage_egl_unrealize;
stage_class->draw_to_pixbuf = clutter_stage_egl_draw_to_pixbuf;
} }
static void static void
clutter_stage_egl_init (ClutterStageEGL *stage) clutter_stage_egl_init (ClutterStageEGL *stage)
{ {
;
} }

View File

@ -27,8 +27,7 @@ struct _ClutterStageEGL
{ {
ClutterStageX11 parent_instance; ClutterStageX11 parent_instance;
EGLSurface egl_surface; EGLSurface egl_surface;
EGLContext egl_context;
}; };
struct _ClutterStageEGLClass struct _ClutterStageEGLClass

View File

@ -173,6 +173,15 @@ clutter_backend_glx_finalize (GObject *gobject)
static void static void
clutter_backend_glx_dispose (GObject *gobject) clutter_backend_glx_dispose (GObject *gobject)
{ {
ClutterBackendGLX *backend_glx = CLUTTER_BACKEND_GLX (gobject);
ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (gobject);
if (backend_glx->gl_context)
{
glXDestroyContext (backend_x11->xdpy, backend_glx->gl_context);
backend_glx->gl_context = None;
}
G_OBJECT_CLASS (clutter_backend_glx_parent_class)->dispose (gobject); G_OBJECT_CLASS (clutter_backend_glx_parent_class)->dispose (gobject);
} }
@ -214,10 +223,13 @@ clutter_backend_glx_get_features (ClutterBackend *backend)
{ {
ClutterBackendGLX *backend_glx = CLUTTER_BACKEND_GLX (backend); ClutterBackendGLX *backend_glx = CLUTTER_BACKEND_GLX (backend);
const gchar *glx_extensions = NULL; const gchar *glx_extensions = NULL;
ClutterFeatureFlags flags = CLUTTER_FEATURE_STAGE_MULTIPLE; ClutterFeatureFlags flags;
/* this will make sure that the GL context exists and its flags = clutter_backend_x11_get_features (backend);
* bound to a drawable flags |= CLUTTER_FEATURE_STAGE_MULTIPLE;
/* this will make sure that the GL context exists and
* it's bound to a drawable
*/ */
g_assert (backend_glx->gl_context != None); g_assert (backend_glx->gl_context != None);
g_assert (glXGetCurrentDrawable () != None); g_assert (glXGetCurrentDrawable () != None);
@ -344,59 +356,88 @@ clutter_backend_glx_get_features (ClutterBackend *backend)
CLUTTER_NOTE (MISC, "backend features checked"); CLUTTER_NOTE (MISC, "backend features checked");
return flags|clutter_backend_x11_get_features (backend); return flags;
} }
static void static void
clutter_backend_glx_ensure_context (ClutterBackend *backend, clutter_backend_glx_ensure_context (ClutterBackend *backend,
ClutterStage *stage) ClutterStage *stage)
{ {
ClutterBackendGLX *backend_glx;
ClutterStageGLX *stage_glx;
ClutterStageX11 *stage_x11;
ClutterBackendX11 *backend_x11;
if (stage == NULL) if (stage == NULL)
{ {
backend_x11 = CLUTTER_BACKEND_X11(backend); ClutterBackendX11 *backend_x11;
backend_x11 = CLUTTER_BACKEND_X11 (backend);
CLUTTER_NOTE (MULTISTAGE, "Clearing all context"); CLUTTER_NOTE (MULTISTAGE, "Clearing all context");
glXMakeCurrent (backend_x11->xdpy, None, NULL); glXMakeCurrent (backend_x11->xdpy, None, NULL);
} }
else else
{ {
stage_glx = CLUTTER_STAGE_GLX(stage); ClutterBackendGLX *backend_glx;
stage_x11 = CLUTTER_STAGE_X11(stage); ClutterStageGLX *stage_glx;
backend_glx = CLUTTER_BACKEND_GLX(backend); ClutterStageX11 *stage_x11;
ClutterStageWindow *impl;
impl = _clutter_stage_get_window (stage);
g_assert (impl != NULL);
CLUTTER_NOTE (MULTISTAGE, "Setting context for stage of type %s [%p]",
g_type_name (G_OBJECT_TYPE (impl)),
impl);
stage_glx = CLUTTER_STAGE_GLX (impl);
stage_x11 = CLUTTER_STAGE_X11 (impl);
backend_glx = CLUTTER_BACKEND_GLX (backend);
g_return_if_fail (stage_x11->xwin != None);
g_return_if_fail (backend_glx->gl_context != None); g_return_if_fail (backend_glx->gl_context != None);
CLUTTER_NOTE (MULTISTAGE, "setting context for stage:%p", stage ); /* we might get here inside the final dispose cycle, so we
* need to handle this gracefully
*/
if (stage_x11->xwin == None)
{
ClutterBackendX11 *backend_x11;
glXMakeCurrent (stage_x11->xdpy, backend_x11 = CLUTTER_BACKEND_X11 (backend);
stage_x11->xwin, CLUTTER_NOTE (MULTISTAGE,
backend_glx->gl_context); "Received a stale stage, clearing all context");
glXMakeCurrent (backend_x11->xdpy, None, NULL);
}
else
glXMakeCurrent (stage_x11->xdpy,
stage_x11->xwin,
backend_glx->gl_context);
} }
} }
static void static void
clutter_backend_glx_redraw (ClutterBackend *backend, ClutterStage *stage) clutter_backend_glx_redraw (ClutterBackend *backend,
ClutterStage *stage)
{ {
ClutterStageGLX *stage_glx; ClutterStageGLX *stage_glx;
ClutterStageX11 *stage_x11; ClutterStageX11 *stage_x11;
ClutterStageWindow *impl;
stage_x11 = CLUTTER_STAGE_X11(stage); impl = _clutter_stage_get_window (stage);
stage_glx = CLUTTER_STAGE_GLX(stage); if (!impl)
return;
clutter_actor_paint (CLUTTER_ACTOR (stage_glx)); g_assert (CLUTTER_IS_STAGE_GLX (impl));
stage_x11 = CLUTTER_STAGE_X11 (impl);
stage_glx = CLUTTER_STAGE_GLX (impl);
/* this will cause the stage implementation to be painted */
clutter_actor_paint (CLUTTER_ACTOR (stage));
/* Why this paint is done in backend as likely GL windowing system /* Why this paint is done in backend as likely GL windowing system
* specific calls, like swapping buffers. * specific calls, like swapping buffers.
*/ */
if (stage_x11->xwin) if (stage_x11->xwin)
{ {
clutter_backend_glx_wait_for_vblank (CLUTTER_BACKEND_GLX(backend)); clutter_backend_glx_wait_for_vblank (CLUTTER_BACKEND_GLX (backend));
glXSwapBuffers (stage_x11->xdpy, stage_x11->xwin); glXSwapBuffers (stage_x11->xdpy, stage_x11->xwin);
} }
else else
@ -409,12 +450,16 @@ clutter_backend_glx_redraw (ClutterBackend *backend, ClutterStage *stage)
static ClutterActor* static ClutterActor*
clutter_backend_glx_create_stage (ClutterBackend *backend, clutter_backend_glx_create_stage (ClutterBackend *backend,
ClutterStage *wrapper,
GError **error) GError **error)
{ {
ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend); ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend);
ClutterStageX11 *stage_x11; ClutterStageX11 *stage_x11;
ClutterActor *stage; ClutterActor *stage;
CLUTTER_NOTE (BACKEND, "Creating stage of type `%s'",
g_type_name (CLUTTER_STAGE_TYPE));
stage = g_object_new (CLUTTER_STAGE_TYPE, NULL); stage = g_object_new (CLUTTER_STAGE_TYPE, NULL);
/* copy backend data into the stage */ /* copy backend data into the stage */
@ -423,8 +468,12 @@ clutter_backend_glx_create_stage (ClutterBackend *backend,
stage_x11->xwin_root = backend_x11->xwin_root; stage_x11->xwin_root = backend_x11->xwin_root;
stage_x11->xscreen = backend_x11->xscreen_num; stage_x11->xscreen = backend_x11->xscreen_num;
stage_x11->backend = backend_x11; stage_x11->backend = backend_x11;
stage_x11->wrapper = wrapper;
CLUTTER_NOTE (MISC, "X11 stage created (display:%p, screen:%d, root:%u)", /* set the pointer back into the wrapper */
_clutter_stage_set_window (wrapper, CLUTTER_STAGE_WINDOW (stage));
CLUTTER_NOTE (BACKEND, "GLX stage created (display:%p, screen:%d, root:%u)",
stage_x11->xdpy, stage_x11->xdpy,
stage_x11->xscreen, stage_x11->xscreen,
(unsigned int) stage_x11->xwin_root); (unsigned int) stage_x11->xwin_root);
@ -432,6 +481,10 @@ clutter_backend_glx_create_stage (ClutterBackend *backend,
/* needed ? */ /* needed ? */
g_object_set_data (G_OBJECT (stage), "clutter-backend", backend); g_object_set_data (G_OBJECT (stage), "clutter-backend", backend);
/* FIXME - is this needed? we should call realize inside the clutter
* init sequence for the default stage, and let the usual realization
* sequence be used for any other stage
*/
clutter_actor_realize (stage); clutter_actor_realize (stage);
if (!CLUTTER_ACTOR_IS_REALIZED (stage)) if (!CLUTTER_ACTOR_IS_REALIZED (stage))
@ -445,7 +498,6 @@ clutter_backend_glx_create_stage (ClutterBackend *backend,
return stage; return stage;
} }
static void static void
clutter_backend_glx_class_init (ClutterBackendGLXClass *klass) clutter_backend_glx_class_init (ClutterBackendGLXClass *klass)
{ {
@ -456,19 +508,19 @@ clutter_backend_glx_class_init (ClutterBackendGLXClass *klass)
gobject_class->dispose = clutter_backend_glx_dispose; gobject_class->dispose = clutter_backend_glx_dispose;
gobject_class->finalize = clutter_backend_glx_finalize; gobject_class->finalize = clutter_backend_glx_finalize;
backend_class->pre_parse = clutter_backend_glx_pre_parse; backend_class->pre_parse = clutter_backend_glx_pre_parse;
backend_class->post_parse = clutter_backend_glx_post_parse; backend_class->post_parse = clutter_backend_glx_post_parse;
backend_class->create_stage = clutter_backend_glx_create_stage; backend_class->create_stage = clutter_backend_glx_create_stage;
backend_class->add_options = clutter_backend_glx_add_options; backend_class->add_options = clutter_backend_glx_add_options;
backend_class->get_features = clutter_backend_glx_get_features; backend_class->get_features = clutter_backend_glx_get_features;
backend_class->redraw = clutter_backend_glx_redraw; backend_class->redraw = clutter_backend_glx_redraw;
backend_class->ensure_context = clutter_backend_glx_ensure_context; backend_class->ensure_context = clutter_backend_glx_ensure_context;
} }
static void static void
clutter_backend_glx_init (ClutterBackendGLX *backend_glx) clutter_backend_glx_init (ClutterBackendGLX *backend_glx)
{ {
;
} }
/* every backend must implement this function */ /* every backend must implement this function */

View File

@ -40,6 +40,7 @@
#include "../clutter-group.h" #include "../clutter-group.h"
#include "../clutter-container.h" #include "../clutter-container.h"
#include "../clutter-stage.h" #include "../clutter-stage.h"
#include "../clutter-stage-window.h"
#include "cogl.h" #include "cogl.h"
@ -48,21 +49,25 @@
#include <gdk-pixbuf-xlib/gdk-pixbuf-xlib.h> #include <gdk-pixbuf-xlib/gdk-pixbuf-xlib.h>
G_DEFINE_TYPE (ClutterStageGLX, clutter_stage_glx, CLUTTER_TYPE_STAGE_X11); static void clutter_stage_window_iface_init (ClutterStageWindowIface *iface);
G_DEFINE_TYPE_WITH_CODE (ClutterStageGLX,
clutter_stage_glx,
CLUTTER_TYPE_STAGE_X11,
G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_STAGE_WINDOW,
clutter_stage_window_iface_init));
static void static void
clutter_stage_glx_unrealize (ClutterActor *actor) clutter_stage_glx_unrealize (ClutterActor *actor)
{ {
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (actor); ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (actor);
ClutterStageGLX *stage_glx = CLUTTER_STAGE_GLX (actor); ClutterStageGLX *stage_glx = CLUTTER_STAGE_GLX (actor);
/* Note unrealize should free up any backend stage related resources */
gboolean was_offscreen; gboolean was_offscreen;
/* Note unrealize should free up any backend stage related resources */
CLUTTER_MARK(); CLUTTER_MARK();
g_object_get (actor, "offscreen", &was_offscreen, NULL); g_object_get (stage_x11->wrapper, "offscreen", &was_offscreen, NULL);
/* Chain up so all children get unrealized, needed to move texture data /* Chain up so all children get unrealized, needed to move texture data
* across contexts * across contexts
@ -100,7 +105,7 @@ clutter_stage_glx_unrealize (ClutterActor *actor)
} }
/* As unrealised the context will now get cleared */ /* As unrealised the context will now get cleared */
clutter_stage_ensure_current (CLUTTER_STAGE(stage_glx)); clutter_stage_ensure_current (stage_x11->wrapper);
XSync (stage_x11->xdpy, False); XSync (stage_x11->xdpy, False);
@ -119,9 +124,9 @@ clutter_stage_glx_realize (ClutterActor *actor)
CLUTTER_NOTE (MISC, "Realizing main stage"); CLUTTER_NOTE (MISC, "Realizing main stage");
g_object_get (actor, "offscreen", &is_offscreen, NULL); g_object_get (stage_x11->wrapper, "offscreen", &is_offscreen, NULL);
backend_glx = CLUTTER_BACKEND_GLX(clutter_get_default_backend()); backend_glx = CLUTTER_BACKEND_GLX (clutter_get_default_backend ());
if (G_LIKELY (!is_offscreen)) if (G_LIKELY (!is_offscreen))
{ {
@ -137,8 +142,10 @@ clutter_stage_glx_realize (ClutterActor *actor)
}; };
if (stage_x11->xvisinfo) if (stage_x11->xvisinfo)
XFree (stage_x11->xvisinfo); {
stage_x11->xvisinfo = NULL; XFree (stage_x11->xvisinfo);
stage_x11->xvisinfo = None;
}
/* The following check seems strange */ /* The following check seems strange */
if (stage_x11->xvisinfo == None) if (stage_x11->xvisinfo == None)
@ -148,6 +155,8 @@ clutter_stage_glx_realize (ClutterActor *actor)
if (!stage_x11->xvisinfo) if (!stage_x11->xvisinfo)
{ {
g_critical ("Unable to find suitable GL visual."); g_critical ("Unable to find suitable GL visual.");
CLUTTER_ACTOR_UNSET_FLAGS (stage_x11->wrapper, CLUTTER_ACTOR_REALIZED);
CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED); CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED);
return; return;
} }
@ -169,15 +178,15 @@ clutter_stage_glx_realize (ClutterActor *actor)
AllocNone); AllocNone);
mask = CWBackPixel | CWBorderPixel | CWColormap; mask = CWBackPixel | CWBorderPixel | CWColormap;
stage_x11->xwin = XCreateWindow (stage_x11->xdpy, stage_x11->xwin = XCreateWindow (stage_x11->xdpy,
stage_x11->xwin_root, stage_x11->xwin_root,
0, 0, 0, 0,
stage_x11->xwin_width, stage_x11->xwin_width,
stage_x11->xwin_height, stage_x11->xwin_height,
0, 0,
stage_x11->xvisinfo->depth, stage_x11->xvisinfo->depth,
InputOutput, InputOutput,
stage_x11->xvisinfo->visual, stage_x11->xvisinfo->visual,
mask, &xattr); mask, &xattr);
} }
CLUTTER_NOTE (MISC, "XSelectInput"); CLUTTER_NOTE (MISC, "XSelectInput");
@ -193,7 +202,6 @@ clutter_stage_glx_realize (ClutterActor *actor)
/* no user resize.. */ /* no user resize.. */
clutter_stage_x11_fix_window_size (stage_x11); clutter_stage_x11_fix_window_size (stage_x11);
clutter_stage_x11_set_wm_protocols (stage_x11); clutter_stage_x11_set_wm_protocols (stage_x11);
if (backend_glx->gl_context == None) if (backend_glx->gl_context == None)
@ -208,6 +216,7 @@ clutter_stage_glx_realize (ClutterActor *actor)
{ {
g_critical ("Unable to create suitable GL context."); g_critical ("Unable to create suitable GL context.");
CLUTTER_ACTOR_UNSET_FLAGS (stage_x11->wrapper, CLUTTER_ACTOR_REALIZED);
CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED); CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED);
return; return;
@ -215,8 +224,8 @@ clutter_stage_glx_realize (ClutterActor *actor)
} }
CLUTTER_NOTE (GL, "glXMakeCurrent"); CLUTTER_NOTE (GL, "glXMakeCurrent");
CLUTTER_ACTOR_SET_FLAGS (stage_x11->wrapper, CLUTTER_ACTOR_REALIZED);
clutter_stage_ensure_current (CLUTTER_STAGE(stage_glx)); clutter_stage_ensure_current (stage_x11->wrapper);
} }
else else
{ {
@ -276,6 +285,7 @@ clutter_stage_glx_realize (ClutterActor *actor)
{ {
g_critical ("Unable to create suitable GL context."); g_critical ("Unable to create suitable GL context.");
CLUTTER_ACTOR_UNSET_FLAGS (stage_x11->wrapper, CLUTTER_ACTOR_REALIZED);
CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED); CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED);
return; return;
@ -285,7 +295,7 @@ clutter_stage_glx_realize (ClutterActor *actor)
clutter_x11_trap_x_errors (); clutter_x11_trap_x_errors ();
/* below will call glxMakeCurrent */ /* below will call glxMakeCurrent */
clutter_stage_ensure_current (CLUTTER_STAGE(stage_glx)); clutter_stage_ensure_current (stage_x11->wrapper);
if (clutter_x11_untrap_x_errors ()) if (clutter_x11_untrap_x_errors ())
{ {
@ -295,14 +305,13 @@ clutter_stage_glx_realize (ClutterActor *actor)
} }
/* Make sure the viewport gets set up correctly */ /* Make sure the viewport gets set up correctly */
CLUTTER_SET_PRIVATE_FLAGS (actor, CLUTTER_ACTOR_SYNC_MATRICES); CLUTTER_SET_PRIVATE_FLAGS (stage_x11->wrapper, CLUTTER_ACTOR_SYNC_MATRICES);
return; return;
fail: fail:
/* For one reason or another we cant realize the stage.. */ /* For one reason or another we cant realize the stage.. */
CLUTTER_ACTOR_UNSET_FLAGS (stage_x11->wrapper, CLUTTER_ACTOR_REALIZED);
CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED); CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED);
return;
} }
static void static void
@ -312,12 +321,12 @@ snapshot_pixbuf_free (guchar *pixels,
g_free (pixels); g_free (pixels);
} }
static GdkPixbuf* static GdkPixbuf *
clutter_stage_glx_draw_to_pixbuf (ClutterStage *stage, clutter_stage_glx_draw_to_pixbuf (ClutterStageWindow *stage_window,
gint x, gint x,
gint y, gint y,
gint width, gint width,
gint height) gint height)
{ {
guchar *data; guchar *data;
GdkPixbuf *pixb; GdkPixbuf *pixb;
@ -326,9 +335,9 @@ clutter_stage_glx_draw_to_pixbuf (ClutterStage *stage,
ClutterStageX11 *stage_x11; ClutterStageX11 *stage_x11;
gboolean is_offscreen = FALSE; gboolean is_offscreen = FALSE;
stage_glx = CLUTTER_STAGE_GLX (stage); stage_glx = CLUTTER_STAGE_GLX (stage_window);
stage_x11 = CLUTTER_STAGE_X11 (stage); stage_x11 = CLUTTER_STAGE_X11 (stage_window);
actor = CLUTTER_ACTOR (stage); actor = CLUTTER_ACTOR (stage_window);
if (width < 0) if (width < 0)
width = clutter_actor_get_width (actor); width = clutter_actor_get_width (actor);
@ -336,7 +345,7 @@ clutter_stage_glx_draw_to_pixbuf (ClutterStage *stage,
if (height < 0) if (height < 0)
height = clutter_actor_get_height (actor); height = clutter_actor_get_height (actor);
g_object_get (stage, "offscreen", &is_offscreen, NULL); g_object_get (stage_x11->wrapper, "offscreen", &is_offscreen, NULL);
if (G_UNLIKELY (is_offscreen)) if (G_UNLIKELY (is_offscreen))
{ {
@ -401,17 +410,22 @@ clutter_stage_glx_class_init (ClutterStageGLXClass *klass)
{ {
GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
ClutterStageClass *stage_class = CLUTTER_STAGE_CLASS (klass);
gobject_class->dispose = clutter_stage_glx_dispose; gobject_class->dispose = clutter_stage_glx_dispose;
actor_class->realize = clutter_stage_glx_realize; actor_class->realize = clutter_stage_glx_realize;
actor_class->unrealize = clutter_stage_glx_unrealize; actor_class->unrealize = clutter_stage_glx_unrealize;
stage_class->draw_to_pixbuf = clutter_stage_glx_draw_to_pixbuf;
} }
static void static void
clutter_stage_glx_init (ClutterStageGLX *stage) clutter_stage_glx_init (ClutterStageGLX *stage)
{ {
; }
static void
clutter_stage_window_iface_init (ClutterStageWindowIface *iface)
{
iface->draw_to_pixbuf = clutter_stage_glx_draw_to_pixbuf;
/* the rest is inherited from ClutterStageX11 */
} }

View File

@ -328,11 +328,12 @@ event_translate (ClutterBackend *backend,
ClutterEvent *event, ClutterEvent *event,
XEvent *xevent) XEvent *xevent)
{ {
ClutterBackendX11 *backend_x11; ClutterBackendX11 *backend_x11;
ClutterStageX11 *stage_x11; ClutterStageX11 *stage_x11;
ClutterStage *stage; ClutterStage *stage;
gboolean res; ClutterStageWindow *impl;
Window xwindow, stage_xwindow; gboolean res;
Window xwindow, stage_xwindow;
backend_x11 = CLUTTER_BACKEND_X11 (backend); backend_x11 = CLUTTER_BACKEND_X11 (backend);
@ -375,7 +376,8 @@ event_translate (ClutterBackend *backend,
if (stage == NULL) if (stage == NULL)
return FALSE; return FALSE;
stage_x11 = CLUTTER_STAGE_X11 (stage); impl = _clutter_stage_get_window (stage);
stage_x11 = CLUTTER_STAGE_X11 (impl);
stage_xwindow = xwindow; /* clutter_x11_get_stage_window (stage); */ stage_xwindow = xwindow; /* clutter_x11_get_stage_window (stage); */
event->any.stage = stage; event->any.stage = stage;

View File

@ -27,6 +27,7 @@
#include "clutter-stage-x11.h" #include "clutter-stage-x11.h"
#include "clutter-x11.h" #include "clutter-x11.h"
#include "../clutter-stage-window.h"
#include "../clutter-main.h" #include "../clutter-main.h"
#include "../clutter-feature.h" #include "../clutter-feature.h"
#include "../clutter-color.h" #include "../clutter-color.h"
@ -45,7 +46,13 @@
#include <gdk-pixbuf-xlib/gdk-pixbuf-xlib.h> #include <gdk-pixbuf-xlib/gdk-pixbuf-xlib.h>
G_DEFINE_TYPE (ClutterStageX11, clutter_stage_x11, CLUTTER_TYPE_STAGE); static void clutter_stage_window_iface_init (ClutterStageWindowIface *iface);
G_DEFINE_TYPE_WITH_CODE (ClutterStageX11,
clutter_stage_x11,
CLUTTER_TYPE_GROUP,
G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_STAGE_WINDOW,
clutter_stage_window_iface_init));
#define _NET_WM_STATE_REMOVE 0 /* remove/unset property */ #define _NET_WM_STATE_REMOVE 0 /* remove/unset property */
#define _NET_WM_STATE_ADD 1 /* add/set property */ #define _NET_WM_STATE_ADD 1 /* add/set property */
@ -84,7 +91,7 @@ clutter_stage_x11_fix_window_size (ClutterStageX11 *stage_x11)
{ {
gboolean resize; gboolean resize;
resize = clutter_stage_get_user_resizable (CLUTTER_STAGE (stage_x11)); resize = clutter_stage_get_user_resizable (stage_x11->wrapper);
if (stage_x11->xwin != None && stage_x11->is_foreign_xwin == FALSE) if (stage_x11->xwin != None && stage_x11->is_foreign_xwin == FALSE)
{ {
@ -117,7 +124,7 @@ clutter_stage_x11_show (ClutterActor *actor)
/* Fire off a redraw to avoid flicker on first map. /* Fire off a redraw to avoid flicker on first map.
* Appears not to work perfectly on intel drivers at least. * Appears not to work perfectly on intel drivers at least.
*/ */
clutter_redraw(CLUTTER_STAGE(actor)); clutter_redraw (stage_x11->wrapper);
XSync (stage_x11->xdpy, FALSE); XSync (stage_x11->xdpy, FALSE);
XMapWindow (stage_x11->xdpy, stage_x11->xwin); XMapWindow (stage_x11->xdpy, stage_x11->xwin);
@ -153,8 +160,8 @@ clutter_stage_x11_set_wm_protocols (ClutterStageX11 *stage_x11)
} }
static void static void
clutter_stage_x11_query_coords (ClutterActor *self, clutter_stage_x11_query_coords (ClutterActor *self,
ClutterActorBox *box) ClutterActorBox *box)
{ {
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (self); ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (self);
@ -164,8 +171,8 @@ clutter_stage_x11_query_coords (ClutterActor *self,
} }
static void static void
clutter_stage_x11_request_coords (ClutterActor *self, clutter_stage_x11_request_coords (ClutterActor *self,
ClutterActorBox *box) ClutterActorBox *box)
{ {
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (self); ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (self);
gint new_width, new_height; gint new_width, new_height;
@ -208,7 +215,8 @@ clutter_stage_x11_request_coords (ClutterActor *self,
clutter_actor_realize (self); clutter_actor_realize (self);
} }
CLUTTER_SET_PRIVATE_FLAGS(self, CLUTTER_ACTOR_SYNC_MATRICES); CLUTTER_SET_PRIVATE_FLAGS (CLUTTER_ACTOR (stage_x11->wrapper),
CLUTTER_ACTOR_SYNC_MATRICES);
} }
if (stage_x11->xwin != None if (stage_x11->xwin != None
@ -224,15 +232,18 @@ clutter_stage_x11_request_coords (ClutterActor *self,
} }
static void static void
clutter_stage_x11_set_fullscreen (ClutterStage *stage, clutter_stage_x11_set_fullscreen (ClutterStageWindow *stage_window,
gboolean fullscreen) gboolean is_fullscreen)
{ {
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage); ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
ClutterBackendX11 *backend_x11 = stage_x11->backend; ClutterBackendX11 *backend_x11 = stage_x11->backend;
ClutterStage *stage = stage_x11->wrapper;
static gboolean was_resizeable = FALSE; static gboolean was_resizeable = FALSE;
if (fullscreen) if (!stage)
return;
if (is_fullscreen)
{ {
if (stage_x11->xwin != None) if (stage_x11->xwin != None)
{ {
@ -311,19 +322,19 @@ clutter_stage_x11_set_fullscreen (ClutterStage *stage,
} }
static void static void
clutter_stage_x11_set_cursor_visible (ClutterStage *stage, clutter_stage_x11_set_cursor_visible (ClutterStageWindow *stage_window,
gboolean show_cursor) gboolean cursor_visible)
{ {
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage); ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
if (stage_x11->xwin == None) if (stage_x11->xwin == None)
return; return;
CLUTTER_NOTE (BACKEND, "setting cursor state ('%s') over stage window (%u)", CLUTTER_NOTE (BACKEND, "setting cursor state ('%s') over stage window (%u)",
show_cursor ? "visible" : "invisible", cursor_visible ? "visible" : "invisible",
(unsigned int) stage_x11->xwin); (unsigned int) stage_x11->xwin);
if (show_cursor) if (cursor_visible)
{ {
#if 0 /* HAVE_XFIXES - seems buggy/unreliable */ #if 0 /* HAVE_XFIXES - seems buggy/unreliable */
XFixesShowCursor (stage_x11->xdpy, stage_x11->xwin); XFixesShowCursor (stage_x11->xdpy, stage_x11->xwin);
@ -355,10 +366,10 @@ clutter_stage_x11_set_cursor_visible (ClutterStage *stage,
} }
static void static void
clutter_stage_x11_set_title (ClutterStage *stage, clutter_stage_x11_set_title (ClutterStageWindow *stage_window,
const gchar *title) const gchar *title)
{ {
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage); ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
ClutterBackendX11 *backend_x11 = stage_x11->backend; ClutterBackendX11 *backend_x11 = stage_x11->backend;
if (stage_x11->xwin == None) if (stage_x11->xwin == None)
@ -384,14 +395,20 @@ clutter_stage_x11_set_title (ClutterStage *stage,
} }
static void static void
clutter_stage_x11_set_user_resize (ClutterStage *stage, clutter_stage_x11_set_user_resizable (ClutterStageWindow *stage_window,
gboolean value) gboolean is_resizable)
{ {
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage); ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
clutter_stage_x11_fix_window_size (stage_x11); clutter_stage_x11_fix_window_size (stage_x11);
} }
static ClutterActor *
clutter_stage_x11_get_wrapper (ClutterStageWindow *stage_window)
{
return CLUTTER_ACTOR (CLUTTER_STAGE_X11 (stage_window)->wrapper);
}
static void static void
clutter_stage_x11_dispose (GObject *gobject) clutter_stage_x11_dispose (GObject *gobject)
{ {
@ -408,7 +425,6 @@ clutter_stage_x11_class_init (ClutterStageX11Class *klass)
{ {
GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
ClutterStageClass *stage_class = CLUTTER_STAGE_CLASS (klass);
gobject_class->dispose = clutter_stage_x11_dispose; gobject_class->dispose = clutter_stage_x11_dispose;
@ -416,11 +432,6 @@ clutter_stage_x11_class_init (ClutterStageX11Class *klass)
actor_class->hide = clutter_stage_x11_hide; actor_class->hide = clutter_stage_x11_hide;
actor_class->request_coords = clutter_stage_x11_request_coords; actor_class->request_coords = clutter_stage_x11_request_coords;
actor_class->query_coords = clutter_stage_x11_query_coords; actor_class->query_coords = clutter_stage_x11_query_coords;
stage_class->set_fullscreen = clutter_stage_x11_set_fullscreen;
stage_class->set_cursor_visible = clutter_stage_x11_set_cursor_visible;
stage_class->set_title = clutter_stage_x11_set_title;
stage_class->set_user_resize = clutter_stage_x11_set_user_resize;
} }
static void static void
@ -439,7 +450,22 @@ clutter_stage_x11_init (ClutterStageX11 *stage)
stage->fullscreen_on_map = FALSE; stage->fullscreen_on_map = FALSE;
stage->handling_configure = FALSE; stage->handling_configure = FALSE;
stage->wrapper = NULL;
CLUTTER_SET_PRIVATE_FLAGS (stage, CLUTTER_ACTOR_IS_TOPLEVEL);
#if 0
CLUTTER_SET_PRIVATE_FLAGS (stage, CLUTTER_ACTOR_SYNC_MATRICES); CLUTTER_SET_PRIVATE_FLAGS (stage, CLUTTER_ACTOR_SYNC_MATRICES);
#endif
}
static void
clutter_stage_window_iface_init (ClutterStageWindowIface *iface)
{
iface->get_wrapper = clutter_stage_x11_get_wrapper;
iface->set_title = clutter_stage_x11_set_title;
iface->set_fullscreen = clutter_stage_x11_set_fullscreen;
iface->set_cursor_visible = clutter_stage_x11_set_cursor_visible;
iface->set_user_resizable = clutter_stage_x11_set_user_resizable;
} }
/** /**
@ -455,9 +481,14 @@ clutter_stage_x11_init (ClutterStageX11 *stage)
Window Window
clutter_x11_get_stage_window (ClutterStage *stage) clutter_x11_get_stage_window (ClutterStage *stage)
{ {
g_return_val_if_fail (CLUTTER_IS_STAGE_X11 (stage), None); ClutterStageWindow *impl;
return CLUTTER_STAGE_X11 (stage)->xwin; g_return_val_if_fail (CLUTTER_IS_STAGE (stage), None);
impl = _clutter_stage_get_window (stage);
g_assert (CLUTTER_IS_STAGE_X11 (impl));
return CLUTTER_STAGE_X11 (impl)->xwin;
} }
/** /**
@ -470,7 +501,7 @@ clutter_x11_get_stage_window (ClutterStage *stage)
* *
* Since: 0.8 * Since: 0.8
*/ */
ClutterStage* ClutterStage *
clutter_x11_get_stage_from_window (Window win) clutter_x11_get_stage_from_window (Window win)
{ {
ClutterMainContext *context; ClutterMainContext *context;
@ -484,10 +515,14 @@ clutter_x11_get_stage_from_window (Window win)
/* FIXME: use a hash here for performance resaon */ /* FIXME: use a hash here for performance resaon */
for (l = stage_manager->stages; l; l = l->next) for (l = stage_manager->stages; l; l = l->next)
{ {
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (l->data); ClutterStage *stage = l->data;
ClutterStageWindow *impl;
if (stage_x11->xwin == win) impl = _clutter_stage_get_window (stage);
return CLUTTER_STAGE(stage_x11); g_assert (CLUTTER_IS_STAGE_X11 (impl));
if (CLUTTER_STAGE_X11 (impl)->xwin == win)
return stage;
} }
return NULL; return NULL;
@ -506,9 +541,14 @@ clutter_x11_get_stage_from_window (Window win)
XVisualInfo * XVisualInfo *
clutter_x11_get_stage_visual (ClutterStage *stage) clutter_x11_get_stage_visual (ClutterStage *stage)
{ {
g_return_val_if_fail (CLUTTER_IS_STAGE_X11 (stage), NULL); ClutterStageWindow *impl;
return CLUTTER_STAGE_X11 (stage)->xvisinfo; g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL);
impl = _clutter_stage_get_window (stage);
g_assert (CLUTTER_IS_STAGE_X11 (impl));
return CLUTTER_STAGE_X11 (impl)->xvisinfo;
} }
/** /**
@ -527,6 +567,7 @@ clutter_x11_set_stage_foreign (ClutterStage *stage,
Window xwindow) Window xwindow)
{ {
ClutterStageX11 *stage_x11; ClutterStageX11 *stage_x11;
ClutterStageWindow *impl;
ClutterActor *actor; ClutterActor *actor;
gint x, y; gint x, y;
guint width, height, border, depth; guint width, height, border, depth;
@ -534,11 +575,12 @@ clutter_x11_set_stage_foreign (ClutterStage *stage,
Status status; Status status;
ClutterGeometry geom; ClutterGeometry geom;
g_return_val_if_fail (CLUTTER_IS_STAGE_X11 (stage), FALSE); g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
g_return_val_if_fail (xwindow != None, FALSE); g_return_val_if_fail (xwindow != None, FALSE);
stage_x11 = CLUTTER_STAGE_X11 (stage); impl = _clutter_stage_get_window (stage);
actor = CLUTTER_ACTOR (stage); stage_x11 = CLUTTER_STAGE_X11 (impl);
actor = CLUTTER_ACTOR (impl);
clutter_x11_trap_x_errors (); clutter_x11_trap_x_errors ();
@ -577,19 +619,27 @@ clutter_x11_set_stage_foreign (ClutterStage *stage,
void void
clutter_stage_x11_map (ClutterStageX11 *stage_x11) clutter_stage_x11_map (ClutterStageX11 *stage_x11)
{ {
/* set the mapped flag on the implementation */
CLUTTER_ACTOR_SET_FLAGS (stage_x11, CLUTTER_ACTOR_MAPPED); CLUTTER_ACTOR_SET_FLAGS (stage_x11, CLUTTER_ACTOR_MAPPED);
if (stage_x11->fullscreen_on_map) /* and on the wrapper itself */
clutter_stage_fullscreen (CLUTTER_STAGE (stage_x11)); CLUTTER_ACTOR_SET_FLAGS (stage_x11->wrapper, CLUTTER_ACTOR_MAPPED);
else
clutter_stage_unfullscreen (CLUTTER_STAGE (stage_x11));
clutter_actor_queue_redraw (CLUTTER_ACTOR (stage_x11)); if (stage_x11->fullscreen_on_map)
clutter_stage_fullscreen (CLUTTER_STAGE (stage_x11->wrapper));
else
clutter_stage_unfullscreen (CLUTTER_STAGE (stage_x11->wrapper));
clutter_actor_queue_redraw (CLUTTER_ACTOR (stage_x11->wrapper));
} }
void void
clutter_stage_x11_unmap (ClutterStageX11 *stage_x11) clutter_stage_x11_unmap (ClutterStageX11 *stage_x11)
{ {
/* like above, unset the MAPPED stage on both the implementation and
* the wrapper
*/
CLUTTER_ACTOR_UNSET_FLAGS (stage_x11, CLUTTER_ACTOR_MAPPED); CLUTTER_ACTOR_UNSET_FLAGS (stage_x11, CLUTTER_ACTOR_MAPPED);
CLUTTER_ACTOR_UNSET_FLAGS (stage_x11->wrapper, CLUTTER_ACTOR_MAPPED);
} }

View File

@ -22,7 +22,7 @@
#ifndef __CLUTTER_STAGE_X11_H__ #ifndef __CLUTTER_STAGE_X11_H__
#define __CLUTTER_STAGE_X11_H__ #define __CLUTTER_STAGE_X11_H__
#include <glib-object.h> #include <clutter/clutter-group.h>
#include <clutter/clutter-stage.h> #include <clutter/clutter-stage.h>
#include <X11/Xlib.h> #include <X11/Xlib.h>
#include <X11/Xatom.h> #include <X11/Xatom.h>
@ -43,7 +43,7 @@ typedef struct _ClutterStageX11Class ClutterStageX11Class;
struct _ClutterStageX11 struct _ClutterStageX11
{ {
ClutterStage parent_instance; ClutterGroup parent_instance;
guint is_foreign_xwin : 1; guint is_foreign_xwin : 1;
guint fullscreen_on_map : 1; guint fullscreen_on_map : 1;
@ -60,11 +60,13 @@ struct _ClutterStageX11
ClutterBackendX11 *backend; ClutterBackendX11 *backend;
ClutterStageState state; ClutterStageState state;
ClutterStage *wrapper;
}; };
struct _ClutterStageX11Class struct _ClutterStageX11Class
{ {
ClutterStageClass parent_class; ClutterGroupClass parent_class;
}; };
GType clutter_stage_x11_get_type (void) G_GNUC_CONST; GType clutter_stage_x11_get_type (void) G_GNUC_CONST;
@ -72,9 +74,8 @@ GType clutter_stage_x11_get_type (void) G_GNUC_CONST;
/* Private to subclasses */ /* Private to subclasses */
void clutter_stage_x11_fix_window_size (ClutterStageX11 *stage_x11); void clutter_stage_x11_fix_window_size (ClutterStageX11 *stage_x11);
void clutter_stage_x11_set_wm_protocols (ClutterStageX11 *stage_x11); void clutter_stage_x11_set_wm_protocols (ClutterStageX11 *stage_x11);
void clutter_stage_x11_map (ClutterStageX11 *stage_x11);
void clutter_stage_x11_map (ClutterStageX11 *stage_x11); void clutter_stage_x11_unmap (ClutterStageX11 *stage_x11);
void clutter_stage_x11_unmap (ClutterStageX11 *stage_x11);
G_END_DECLS G_END_DECLS

View File

@ -26,7 +26,7 @@ on_button_press (ClutterActor *actor,
ClutterAlpha *alpha; ClutterAlpha *alpha;
ClutterBehaviour *r_behave; ClutterBehaviour *r_behave;
new_stage = clutter_stage_create_new (); new_stage = clutter_stage_new ();
/* FIXME: below should really be automatic */ /* FIXME: below should really be automatic */
/* clutter_stage_ensure_cogl_context (CLUTTER_STAGE(new_stage)); */ /* clutter_stage_ensure_cogl_context (CLUTTER_STAGE(new_stage)); */