Merge remote branch 'master' into texture-debugging

Conflicts:
	clutter/cogl/cogl/cogl-context.h
This commit is contained in:
Neil Roberts 2010-02-01 13:37:19 +00:00
commit 145cc9d3df
66 changed files with 3022 additions and 1231 deletions

1
.gitignore vendored
View File

@ -240,6 +240,7 @@ TAGS
/tests/conform/test-script-named-object /tests/conform/test-script-named-object
/tests/conform/test-actor-destruction /tests/conform/test-actor-destruction
/tests/conform/test-color-operators /tests/conform/test-color-operators
/tests/conform/test-cogl-texture-mipmaps
/tests/micro-bench/test-text-perf /tests/micro-bench/test-text-perf
/tests/micro-bench/test-text /tests/micro-bench/test-text
/tests/micro-bench/test-picking /tests/micro-bench/test-picking

49
NEWS
View File

@ -1,3 +1,52 @@
Clutter 1.1.6 (18/01/2010)
===============================================================================
* List of changes since Clutter 1.1.4
o Some fixes for the Win32 backend (bug #1905).
o Profiling support via the UProf library. Configure with
--enable-profile to get a report after each Clutter application is
run.
o Improved conformance tests with coverage reports via gcov.
o ClutterTexture no longer tries to read back texture data into
g_malloc'd memory on unrealize (bug #1842).
o The CGL_* defines from cogl-defines.h have been removed. These
should not have been used by any applications, but if they were
being used then please replace them either with the Cogl enums or
with the appropriate GL_* enum if you are using GL directly.
o Added a delete-event signal to the stage.
o Fix for using cogl_rectangle with different texture coordinates
for multiple layers (bug #1937).
o Fix for using stencil and depth buffers in FBOs on Intel drivers
in Mesa (bug #1873).
o Support for subtitles in ClutterMedia.
o ClutterGLX will now use an RGB visual by default. For
applications (and toolkit integration libraries) that want to
enable the ClutterStage:use-alpha property there is a new
function: clutter_x11_set_use_argb_visual().
o Fix ClutterText to allow using Pango markup and attributes in
the same actor (bug #1940).
Many thanks to:
Alejandro Piñeiro
Damien Lespiau
Emmanuele Bassi
Gord Allot
Halton Huo
Robert Bragg
Samuel Degrande
Clutter 1.1.4 (03/01/2010) Clutter 1.1.4 (03/01/2010)
=============================================================================== ===============================================================================

105
README
View File

@ -1,19 +1,25 @@
Clutter - README Clutter - README
=============================================================================== ===============================================================================
Clutter is an open source software library for creating fast, visually Clutter is an open source software library for creating portable, fast,
rich and animated graphical user interfaces. visually rich and animated graphical user interfaces.
Clutter currently requires: Clutter currently requires:
• GLib >= 2.16.0 • GLib >= 2.16.0
• Cairo >= 1.6 • Cairo >= 1.6
• PangoCairo >= 1.20 • PangoCairo >= 1.20
• OpenGL >= 1.2, OpenGL ES 1.1 or OpenGL ES 2.0 • OpenGL >= 1.3 or 1.2+multitexturing, OpenGL ES 1.1 or OpenGL ES 2.0
• GLX, SDL, WGL or an EGL Implementation • GLX, SDL, WGL or an EGL Implementation
• JSON-GLib >= 0.8 (optional) • JSON-GLib >= 0.8 (optional)
• GDK-Pixbuf >= 2.0 (optional) • GDK-Pixbuf >= 2.0 (optional)
When running the OpenGL flavor, Clutter requires at least version 1.3
or 1.2 with the multitexturing extension. However to build Clutter
you will need the latest GL headers which can be obtained from:
http://www.khronos.org
If you are building the API reference you will also need: If you are building the API reference you will also need:
• GTK-Doc >= 1.11 • GTK-Doc >= 1.11
@ -24,30 +30,53 @@ If you are building the Introspection data you will also need:
If you want built in support for profiling Clutter you will also need: If you want built in support for profiling Clutter you will also need:
• UProf 0.2 available from git://git.moblin.org/uprof.git • UProf >= 0.2
The official website is: UProf is available from:
http://www.clutter-project.org
The Clutter blog is at git://git.moblin.org/uprof.git
http://www.clutter-project.org/blog
-------------------------------------------------------------------------------
The official Clutter website is:
http://www.clutter-project.org/
The API reference for the latest stable release and unstable developers
snapshot are available at:
http://www.clutter-project.org/docs/clutter/stable/
http://www.clutter-project.org/docs/clutter/unstable/
New releases of Clutter are available at:
http://www.clutter-project.org/sources/clutter/
The Clutter blog is at:
http://www.clutter-project.org/blog/
To subscribe to the Clutter mailing list, send an email to:
To subscribe to the Clutter mailing list, send mail to:
clutter+subscribe@o-hand.com clutter+subscribe@o-hand.com
The official mailing list archive is: The official mailing list archive is:
http://lists.o-hand.com/clutter/ http://lists.o-hand.com/clutter/
New bug page on Bugzilla: New bug page on Bugzilla:
http://bugzilla.o-hand.com/enter_bug.cgi?product=Clutter http://bugzilla.o-hand.com/enter_bug.cgi?product=Clutter
Clutter is licensed under the terms of the GNU Lesser General Public Clutter is licensed under the terms of the GNU Lesser General Public
License, version 2.1 or (at your option) later. License, version 2.1 or (at your option) later.
INSTALLATION BUILDING AND INSTALLATION
=============================================================================== ===============================================================================
See the INSTALL file. Info on specific Clutter options; See the INSTALL file.
Clutter has additional command line options for the configure script:
--enable-debug=[no/minimum/yes] --enable-debug=[no/minimum/yes]
Controls Clutter debugging level: Controls Clutter debugging level:
@ -55,7 +84,7 @@ See the INSTALL file. Info on specific Clutter options;
yes: yes:
All GLib asserts, checks and support for runtime Clutter All GLib asserts, checks and support for runtime Clutter
debugging notes through CLUTTER_DEBUG. This is the default debugging notes through CLUTTER_DEBUG. This is the default
value for snapshots. value for developers snapshots.
minimum: minimum:
Just GType cast checks and support for runtime Clutter Just GType cast checks and support for runtime Clutter
@ -65,35 +94,47 @@ See the INSTALL file. Info on specific Clutter options;
no: no:
No GLib asserts or checks and no support for runtime Clutter No GLib asserts or checks and no support for runtime Clutter
debugging notes. Only use in extreme performance and/or size debugging notes. Only use in extreme performance and/or size
optimization cases. optimization cases, though it is strongly discouraged.
--enable-cogl-debug=[no/minimum/yes] --enable-cogl-debug=[no/minimum/yes]
Controls COGL debugging level (default=minimum): Controls COGL debugging level (default=minimum):
yes: yes:
Support for COGL debugging notes through COGL_DEBUG and Support for COGL debugging notes through COGL_DEBUG and
error checking for each GL primitive. error checking for each GL primitive. This is useful mostly
to debug COGL itself.
minimum: minimum:
Support for COGL debugging notes through COGL_DEBUG. This is Support for COGL debugging notes through COGL_DEBUG. This is
the default for snapshots. the default for developers snapshots.
no: no:
Disable support for COGL runtime debugging notes. This is Disable support for COGL runtime debugging notes. This is
the default for stable releases. the default for stable releases.
--enable-maintainer-flags=[no/yes] --enable-maintainer-flags=[no/yes]
Use strict compiler flags. This defaults to 'yes' for snapshots and Use strict compiler flags. This defaults to 'yes' for developers
to 'no' for stable releases. snapshots and to 'no' for stable releases.
--enable-gtk-doc --enable-gtk-doc
use gtk-doc to build API documentation (default=no). Requires gtk-doc use gtk-doc to build API documentation (default=no). Requires gtk-doc
present on the target system. present on the target system.
--enable-docs=[no/yes] --enable-docs=[no/yes]
Build additional documentation. Requires xsltproc for DocBook Build additional documentation. Requires xsltproc for DocBook
conversion, and optionally jw for PDF generation. conversion, and optionally jw for PDF generation.
--enable-gcov=[no/yes]
Build Clutter with coverage report support, provided by gcov. This
feature only works with the GNU Compiler Suite and gcov installed.
--enable-profile=[no/yes]
Build Clutter with profiling instrumentation. Requires the GNU
C Compiler and the UProf library.
--enable-conform=[yes/no]
Build the Clutter conformance test suite.
--with-flavour=[glx/eglx/eglnative/sdl/osx/win32/fruity] --with-flavour=[glx/eglx/eglnative/sdl/osx/win32/fruity]
Select the Clutter backend: (default=glx) Select the Clutter backend: (default=glx)
@ -150,7 +191,7 @@ See the INSTALL file. Info on specific Clutter options;
and if it is available, make Clutter depend on it and if it is available, make Clutter depend on it
system: system:
Only use the system copy of JSON-GLib Only use the system copy of JSON-GLib and warn if not found
VERSIONING VERSIONING
=============================================================================== ===============================================================================
@ -167,22 +208,23 @@ the newly added API might still change.
The micro version indicates the origin of the release: even micro The micro version indicates the origin of the release: even micro
numbers are only used for released archives; odd micro numbers are numbers are only used for released archives; odd micro numbers are
only used on the SVN repository. only used on the Git repository.
HACKING HACKING
=============================================================================== ===============================================================================
If you want to hack on and improve Clutter, check the contained TODO If you want to hack on and improve Clutter, check the contained TODO file for
file for pending tasks, the HACKING file for general implementation guidelines, pending tasks, the HACKING file for general implementation guidelines, and the
HACKING.backends for backend-specific implementation issues. The CODING_STYLE HACKING.backends for backend-specific implementation issues.
file contains the rules for writing code conformant to the style guidelines
used throughout Clutter. Remember: the coding style is mandatory; patches The CODING_STYLE file contains the rules for writing code conformant to the
not conforming to it will be rejected. style guidelines used throughout Clutter. Remember: the coding style is
mandatory; patches not conforming to it will be rejected.
BUGS BUGS
=============================================================================== ===============================================================================
Bugs should be reported to the OpenedHand Bugzilla at: Bugs should be reported to the Clutter Bugzilla at:
http://bugzilla.o-hand.com/enter_bug.cgi?product=Clutter http://bugzilla.o-hand.com/enter_bug.cgi?product=Clutter
@ -192,7 +234,7 @@ In the report you should include:
• what system you're running Clutter on; • what system you're running Clutter on;
• which version of Clutter you are using; • which version of Clutter you are using;
• which version of GLib and OpenGL you are using; • which version of GLib and OpenGL (or OpenGL ES) you are using;
• which video card and which drivers you are using, including output of • which video card and which drivers you are using, including output of
glxinfo and xdpyinfo (if applicable); glxinfo and xdpyinfo (if applicable);
• how to reproduce the bug. • how to reproduce the bug.
@ -235,7 +277,8 @@ RELEASE NOTES
=============================================================================== ===============================================================================
Relevant information for developers with existing Clutter applications Relevant information for developers with existing Clutter applications
wanting to port to newer releases (See NEWS for general new feature info). wanting to port to newer releases (see NEWS for general information on new
features).
Release Notes for Clutter 1.2 Release Notes for Clutter 1.2
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
@ -342,7 +385,7 @@ Release Notes for Clutter 1.0
correct HLS intervals: correct HLS intervals:
Hue: [ 0, 360 ) Hue: [ 0, 360 )
Luminance: [ 0, 1] Luminance: [ 0, 1 ]
Saturation: [ 0, 1 ] Saturation: [ 0, 1 ]
* The ClutterFixed symbols have been completely removed: fixed-point * The ClutterFixed symbols have been completely removed: fixed-point
@ -852,5 +895,3 @@ Release Notes for Clutter 0.3
ClutterGroup::remove signals have been deprecated: ClutterGroup::remove signals have been deprecated:
ClutterContainer::actor-added and ClutterContainer::actor-removed should ClutterContainer::actor-added and ClutterContainer::actor-removed should
be used instead. be used instead.
$LastChangedDate$

View File

@ -1,3 +1,3 @@
SUBDIRS = autotools SUBDIRS = autotools mingw
EXTRA_DIST = gen-gcov.pl EXTRA_DIST = gen-gcov.pl

1
build/mingw/Makefile.am Normal file
View File

@ -0,0 +1 @@
EXTRA_DIST = README mingw-cross-compile.sh

View File

@ -20,6 +20,23 @@ DISTCLEANFILES =
EXTRA_DIST = EXTRA_DIST =
BUILT_SOURCES = BUILT_SOURCES =
if WINSYS_WIN32
# Ideally this resources stuff would go in win32/ but libtool doesn't
# seem to pass on the -Wl argument when linking a convenience library
# so we need to do it here as part of linking the dll. libtool also
# won't let you link against the .o directly because it wants you to
# link against libtool objects for dynamic libraries.
.rc.o :
$(WINDRES) -I$(srcdir)/win32 $< $@
win32/resources.o : $(srcdir)/win32/invisible-cursor.cur
win32_resources = win32/resources.o
win32_resources_ldflag = -Wl,win32/resources.o
endif # WINSYS_WIN32
INCLUDES = \ INCLUDES = \
-I$(top_srcdir) \ -I$(top_srcdir) \
-I$(top_srcdir)/clutter/cogl \ -I$(top_srcdir)/clutter/cogl \
@ -65,8 +82,9 @@ source_h = \
$(srcdir)/clutter-child-meta.h \ $(srcdir)/clutter-child-meta.h \
$(srcdir)/clutter-clone.h \ $(srcdir)/clutter-clone.h \
$(srcdir)/clutter-color.h \ $(srcdir)/clutter-color.h \
$(srcdir)/clutter-container.h \ $(srcdir)/clutter-container.h \
$(srcdir)/clutter-deprecated.h \ $(srcdir)/clutter-deprecated.h \
$(srcdir)/clutter-device-manager.h \
$(srcdir)/clutter-event.h \ $(srcdir)/clutter-event.h \
$(srcdir)/clutter-feature.h \ $(srcdir)/clutter-feature.h \
$(srcdir)/clutter-fixed.h \ $(srcdir)/clutter-fixed.h \
@ -74,6 +92,7 @@ source_h = \
$(srcdir)/clutter-flow-layout.h \ $(srcdir)/clutter-flow-layout.h \
$(srcdir)/clutter-frame-source.h \ $(srcdir)/clutter-frame-source.h \
$(srcdir)/clutter-group.h \ $(srcdir)/clutter-group.h \
$(srcdir)/clutter-input-device.h \
$(srcdir)/clutter-interval.h \ $(srcdir)/clutter-interval.h \
$(srcdir)/clutter-keysyms.h \ $(srcdir)/clutter-keysyms.h \
$(srcdir)/clutter-layout-manager.h \ $(srcdir)/clutter-layout-manager.h \
@ -137,6 +156,7 @@ source_c = \
$(srcdir)/clutter-clone.c \ $(srcdir)/clutter-clone.c \
$(srcdir)/clutter-color.c \ $(srcdir)/clutter-color.c \
$(srcdir)/clutter-container.c \ $(srcdir)/clutter-container.c \
$(srcdir)/clutter-device-manager.c \
clutter-enum-types.c \ clutter-enum-types.c \
$(srcdir)/clutter-event.c \ $(srcdir)/clutter-event.c \
$(srcdir)/clutter-feature.c \ $(srcdir)/clutter-feature.c \
@ -145,6 +165,7 @@ source_c = \
$(srcdir)/clutter-flow-layout.c \ $(srcdir)/clutter-flow-layout.c \
$(srcdir)/clutter-frame-source.c \ $(srcdir)/clutter-frame-source.c \
$(srcdir)/clutter-group.c \ $(srcdir)/clutter-group.c \
$(srcdir)/clutter-input-device.c \
$(srcdir)/clutter-interval.c \ $(srcdir)/clutter-interval.c \
$(srcdir)/clutter-layout-manager.c \ $(srcdir)/clutter-layout-manager.c \
$(srcdir)/clutter-layout-meta.c \ $(srcdir)/clutter-layout-meta.c \
@ -205,7 +226,8 @@ libclutter_@CLUTTER_WINSYS@_@CLUTTER_API_VERSION@_la_DEPENDENCIES = \
$(top_builddir)/clutter/cogl/pango/libcoglpango.la \ $(top_builddir)/clutter/cogl/pango/libcoglpango.la \
$(top_builddir)/clutter/$(CLUTTER_WINSYS)/libclutter-$(CLUTTER_WINSYS).la \ $(top_builddir)/clutter/$(CLUTTER_WINSYS)/libclutter-$(CLUTTER_WINSYS).la \
$(clutter_json_dep) \ $(clutter_json_dep) \
$(CLUTTER_WINSYS_BASE_LIB) $(CLUTTER_WINSYS_BASE_LIB) \
$(win32_resources)
libclutter_@CLUTTER_WINSYS@_@CLUTTER_API_VERSION@_la_SOURCES = \ libclutter_@CLUTTER_WINSYS@_@CLUTTER_API_VERSION@_la_SOURCES = \
$(source_c) \ $(source_c) \
@ -219,6 +241,7 @@ libclutter_@CLUTTER_WINSYS@_@CLUTTER_API_VERSION@_la_LDFLAGS = \
-export-dynamic \ -export-dynamic \
-export-symbols-regex "^(clutter|cogl|json).*" \ -export-symbols-regex "^(clutter|cogl|json).*" \
-rpath $(libdir) \ -rpath $(libdir) \
$(win32_resources_ldflag) \
$(NULL) $(NULL)
lib_LTLIBRARIES = $(CLUTTER_WINSYS_LIB) lib_LTLIBRARIES = $(CLUTTER_WINSYS_LIB)

View File

@ -308,6 +308,7 @@ struct _ClutterActorPrivate
guint clip_to_allocation : 1; guint clip_to_allocation : 1;
guint enable_model_view_transform : 1; guint enable_model_view_transform : 1;
guint enable_paint_unmapped : 1; guint enable_paint_unmapped : 1;
guint has_pointer : 1;
gfloat clip[4]; gfloat clip[4];
@ -429,7 +430,8 @@ enum
PROP_SHOW_ON_SET_PARENT, PROP_SHOW_ON_SET_PARENT,
PROP_TEXT_DIRECTION PROP_TEXT_DIRECTION,
PROP_HAS_POINTER
}; };
enum enum
@ -2581,7 +2583,7 @@ clutter_actor_set_property (GObject *object,
break; break;
case PROP_OPACITY: case PROP_OPACITY:
clutter_actor_set_opacity (actor, g_value_get_uchar (value)); clutter_actor_set_opacity (actor, g_value_get_uint (value));
break; break;
case PROP_NAME: case PROP_NAME:
@ -2864,7 +2866,7 @@ clutter_actor_get_property (GObject *object,
break; break;
case PROP_OPACITY: case PROP_OPACITY:
g_value_set_uchar (value, priv->opacity); g_value_set_uint (value, priv->opacity);
break; break;
case PROP_NAME: case PROP_NAME:
@ -3031,6 +3033,10 @@ clutter_actor_get_property (GObject *object,
g_value_set_enum (value, priv->text_direction); g_value_set_enum (value, priv->text_direction);
break; break;
case PROP_HAS_POINTER:
g_value_set_boolean (value, priv->has_pointer);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -3462,15 +3468,15 @@ clutter_actor_class_init (ClutterActorClass *klass)
/** /**
* ClutterActor:opacity: * ClutterActor:opacity:
* *
* Opacity of the actor, between 0 (fully transparent) and * Opacity of an actor, between 0 (fully transparent) and
* 255 (fully opaque) * 255 (fully opaque)
*/ */
pspec = g_param_spec_uchar ("opacity", pspec = g_param_spec_uint ("opacity",
"Opacity", "Opacity",
"Opacity of actor", "Opacity of an actor",
0, 255, 0, 255,
255, 255,
CLUTTER_PARAM_READWRITE); CLUTTER_PARAM_READWRITE);
g_object_class_install_property (object_class, PROP_OPACITY, pspec); g_object_class_install_property (object_class, PROP_OPACITY, pspec);
/** /**
@ -3858,6 +3864,24 @@ clutter_actor_class_init (ClutterActorClass *klass)
PROP_TEXT_DIRECTION, PROP_TEXT_DIRECTION,
pspec); pspec);
/**
* ClutterActor:has-pointer:
*
* Whether the actor contains the pointer of a #ClutterInputDevice
* or not.
*
* Since: 1.2
*/
pspec = g_param_spec_boolean ("has-pointer",
"Has Pointer",
"Whether the actor contains the pointer "
"of an input device",
FALSE,
CLUTTER_PARAM_READABLE);
g_object_class_install_property (object_class,
PROP_HAS_POINTER,
pspec);
/** /**
* ClutterActor::destroy: * ClutterActor::destroy:
* @actor: the object which received the signal * @actor: the object which received the signal
@ -9638,6 +9662,20 @@ clutter_actor_set_text_direction (ClutterActor *self,
} }
} }
void
_clutter_actor_set_has_pointer (ClutterActor *self,
gboolean has_pointer)
{
ClutterActorPrivate *priv = self->priv;
if (priv->has_pointer != has_pointer)
{
priv->has_pointer = has_pointer;
g_object_notify (G_OBJECT (self), "has-pointer");
}
}
/** /**
* clutter_actor_get_text_direction: * clutter_actor_get_text_direction:
* @self: a #ClutterActor * @self: a #ClutterActor
@ -9748,3 +9786,23 @@ clutter_actor_pop_internal (void)
ctx->internal_child -= 1; ctx->internal_child -= 1;
} }
/**
* clutter_actor_has_pointer:
* @self: a #ClutterActor
*
* Checks whether an actor contains the the pointer of a
* #ClutterInputDevice
*
* Return value: %TRUE if the actor contains the pointer, and
* %FALSE otherwise
*
* Since: 1.2
*/
gboolean
clutter_actor_has_pointer (ClutterActor *self)
{
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
return self->priv->has_pointer;
}

View File

@ -532,6 +532,7 @@ void clutter_actor_get_transformation_matrix (ClutterActor *self
CoglMatrix *matrix); CoglMatrix *matrix);
gboolean clutter_actor_is_in_clone_paint (ClutterActor *self); gboolean clutter_actor_is_in_clone_paint (ClutterActor *self);
gboolean clutter_actor_has_pointer (ClutterActor *self);
void clutter_actor_set_text_direction (ClutterActor *self, void clutter_actor_set_text_direction (ClutterActor *self,
ClutterTextDirection text_dir); ClutterTextDirection text_dir);

View File

@ -201,7 +201,10 @@ on_actor_dispose (gpointer user_data,
ClutterAnimation *self = user_data; ClutterAnimation *self = user_data;
if (self->priv->object == actor_pointer) if (self->priv->object == actor_pointer)
g_object_unref (self); {
CLUTTER_NOTE (ANIMATION, "Object [%p] was unref'd", actor_pointer);
g_object_unref (self);
}
} }
@ -219,6 +222,7 @@ clutter_animation_real_completed (ClutterAnimation *self)
direction = clutter_timeline_get_direction (timeline); direction = clutter_timeline_get_direction (timeline);
/* explicitly set the final state of the animation */ /* explicitly set the final state of the animation */
CLUTTER_NOTE (ANIMATION, "Set final state on object [%p]", priv->object);
g_hash_table_iter_init (&iter, priv->properties); g_hash_table_iter_init (&iter, priv->properties);
while (g_hash_table_iter_next (&iter, &key, &value)) while (g_hash_table_iter_next (&iter, &key, &value))
{ {
@ -245,8 +249,14 @@ clutter_animation_real_completed (ClutterAnimation *self)
animation = g_object_get_qdata (priv->object, quark_object_animation); animation = g_object_get_qdata (priv->object, quark_object_animation);
if (animation == self) if (animation == self)
{ {
CLUTTER_NOTE (ANIMATION, "Unsetting animation for actor [%p]",
priv->object);
g_object_set_qdata (priv->object, quark_object_animation, NULL); g_object_set_qdata (priv->object, quark_object_animation, NULL);
g_object_weak_unref (priv->object, on_actor_dispose, self); g_object_weak_unref (priv->object, on_actor_dispose, self);
CLUTTER_NOTE (ANIMATION, "Releasing the reference Animation [%p]",
animation);
g_object_unref (animation); g_object_unref (animation);
} }
} }
@ -256,7 +266,9 @@ clutter_animation_finalize (GObject *gobject)
{ {
ClutterAnimationPrivate *priv = CLUTTER_ANIMATION (gobject)->priv; ClutterAnimationPrivate *priv = CLUTTER_ANIMATION (gobject)->priv;
CLUTTER_NOTE (ANIMATION, "Destroying properties hash table"); CLUTTER_NOTE (ANIMATION,
"Destroying properties table for Animation [%p]",
gobject);
g_hash_table_destroy (priv->properties); g_hash_table_destroy (priv->properties);
G_OBJECT_CLASS (clutter_animation_parent_class)->finalize (gobject); G_OBJECT_CLASS (clutter_animation_parent_class)->finalize (gobject);
@ -444,7 +456,7 @@ clutter_animation_class_init (ClutterAnimationClass *klass)
gobject_class->finalize = clutter_animation_finalize; gobject_class->finalize = clutter_animation_finalize;
/** /**
* ClutterAnimation:objct: * ClutterAnimation:object:
* *
* The #GObject to which the animation applies. * The #GObject to which the animation applies.
* *
@ -629,6 +641,7 @@ clutter_animation_validate_bind (ClutterAnimation *animation,
ClutterAnimationPrivate *priv; ClutterAnimationPrivate *priv;
GObjectClass *klass; GObjectClass *klass;
GParamSpec *pspec; GParamSpec *pspec;
GType pspec_type;
priv = animation->priv; priv = animation->priv;
@ -667,14 +680,17 @@ clutter_animation_validate_bind (ClutterAnimation *animation,
return NULL; return NULL;
} }
if (!g_value_type_compatible (G_PARAM_SPEC_VALUE_TYPE (pspec), argtype)) pspec_type = G_PARAM_SPEC_VALUE_TYPE (pspec);
if (!g_value_type_compatible (argtype, pspec_type) ||
!g_value_type_transformable (argtype, pspec_type))
{ {
g_warning ("Cannot bind property '%s': the interval value of " g_warning ("Cannot bind property '%s': the interval value of "
"type '%s' is not compatible with the property value " "type '%s' is not compatible with the property value "
"of type '%s'", "of type '%s'",
property_name, property_name,
g_type_name (argtype), g_type_name (argtype),
g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec))); g_type_name (pspec_type));
return NULL; return NULL;
} }
@ -846,6 +862,7 @@ clutter_animation_update_interval (ClutterAnimation *animation,
ClutterAnimationPrivate *priv; ClutterAnimationPrivate *priv;
GObjectClass *klass; GObjectClass *klass;
GParamSpec *pspec; GParamSpec *pspec;
GType pspec_type, int_type;
g_return_if_fail (CLUTTER_IS_ANIMATION (animation)); g_return_if_fail (CLUTTER_IS_ANIMATION (animation));
g_return_if_fail (property_name != NULL); g_return_if_fail (property_name != NULL);
@ -872,15 +889,18 @@ clutter_animation_update_interval (ClutterAnimation *animation,
return; return;
} }
if (!g_value_type_compatible (G_PARAM_SPEC_VALUE_TYPE (pspec), pspec_type = G_PARAM_SPEC_VALUE_TYPE (pspec);
clutter_interval_get_value_type (interval))) int_type = clutter_interval_get_value_type (interval);
if (!g_value_type_compatible (int_type, pspec_type) ||
!g_value_type_transformable (int_type, pspec_type))
{ {
g_warning ("Cannot update property '%s': the interval value of " g_warning ("Cannot update property '%s': the interval value of "
"type '%s' is not compatible with the property value " "type '%s' is not compatible with the property value "
"of type '%s'", "of type '%s'",
property_name, property_name,
g_type_name (clutter_interval_get_value_type (interval)), g_type_name (int_type),
g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec))); g_type_name (pspec_type));
return; return;
} }
@ -906,6 +926,7 @@ clutter_animation_update (ClutterAnimation *animation,
{ {
ClutterAnimationPrivate *priv; ClutterAnimationPrivate *priv;
ClutterInterval *interval; ClutterInterval *interval;
GType int_type;
g_return_val_if_fail (CLUTTER_IS_ANIMATION (animation), NULL); g_return_val_if_fail (CLUTTER_IS_ANIMATION (animation), NULL);
g_return_val_if_fail (property_name != NULL, NULL); g_return_val_if_fail (property_name != NULL, NULL);
@ -923,14 +944,16 @@ clutter_animation_update (ClutterAnimation *animation,
return NULL; return NULL;
} }
if (!g_value_type_compatible (G_VALUE_TYPE (final), int_type = clutter_interval_get_value_type (interval);
clutter_interval_get_value_type (interval)))
if (!g_value_type_compatible (G_VALUE_TYPE (final), int_type) ||
!g_value_type_transformable (G_VALUE_TYPE (final), int_type))
{ {
g_warning ("Cannot update property '%s': the interval value of " g_warning ("Cannot update property '%s': the interval value of "
"type '%s' is not compatible with the property value " "type '%s' is not compatible with the property value "
"of type '%s'", "of type '%s'",
property_name, property_name,
g_type_name (clutter_interval_get_value_type (interval)), g_type_name (int_type),
g_type_name (G_VALUE_TYPE (final))); g_type_name (G_VALUE_TYPE (final)));
return NULL; return NULL;
} }
@ -1221,6 +1244,8 @@ clutter_animation_set_mode (ClutterAnimation *animation,
g_object_freeze_notify (G_OBJECT (animation)); g_object_freeze_notify (G_OBJECT (animation));
alpha = clutter_animation_get_alpha_internal (animation); alpha = clutter_animation_get_alpha_internal (animation);
g_assert (CLUTTER_IS_ALPHA (alpha));
clutter_alpha_set_mode (alpha, mode); clutter_alpha_set_mode (alpha, mode);
g_object_notify (G_OBJECT (animation), "mode"); g_object_notify (G_OBJECT (animation), "mode");
@ -1274,6 +1299,8 @@ clutter_animation_set_duration (ClutterAnimation *animation,
g_object_freeze_notify (G_OBJECT (animation)); g_object_freeze_notify (G_OBJECT (animation));
timeline = clutter_animation_get_timeline_internal (animation); timeline = clutter_animation_get_timeline_internal (animation);
g_assert (CLUTTER_IS_TIMELINE (timeline));
clutter_timeline_set_duration (timeline, msecs); clutter_timeline_set_duration (timeline, msecs);
clutter_timeline_rewind (timeline); clutter_timeline_rewind (timeline);
@ -1634,35 +1661,37 @@ clutter_animation_setup_property (ClutterAnimation *animation,
*/ */
if (!g_type_is_a (G_VALUE_TYPE (value), G_VALUE_TYPE (&real_value))) if (!g_type_is_a (G_VALUE_TYPE (value), G_VALUE_TYPE (&real_value)))
{ {
if (!g_value_type_compatible (G_VALUE_TYPE (value), /* are these two types compatible (can be directly copied)? */
G_VALUE_TYPE (&real_value)) && if (g_value_type_compatible (G_VALUE_TYPE (value),
!g_value_type_compatible (G_VALUE_TYPE (&real_value), G_VALUE_TYPE (&real_value)))
G_VALUE_TYPE (value)))
{ {
g_warning ("%s: Unable to convert from %s to %s for " g_value_copy (value, &real_value);
"the property '%s' of object %s", goto done;
G_STRLOC,
g_type_name (G_VALUE_TYPE (value)),
g_type_name (G_VALUE_TYPE (&real_value)),
property_name,
G_OBJECT_TYPE_NAME (priv->object));
g_value_unset (&real_value);
return;
} }
if (!g_value_transform (value, &real_value)) /* are these two type transformable? */
if (g_value_type_transformable (G_VALUE_TYPE (value),
G_VALUE_TYPE (&real_value)))
{ {
g_warning ("%s: Unable to transform from %s to %s", if (g_value_transform (value, &real_value))
G_STRLOC, goto done;
g_type_name (G_VALUE_TYPE (value)),
g_type_name (G_VALUE_TYPE (&real_value)));
g_value_unset (&real_value);
return;
} }
/* if not compatible and not transformable then we can't do much */
g_warning ("%s: Unable to convert from %s to %s for "
"the property '%s' of object %s",
G_STRLOC,
g_type_name (G_VALUE_TYPE (value)),
g_type_name (G_VALUE_TYPE (&real_value)),
property_name,
G_OBJECT_TYPE_NAME (priv->object));
g_value_unset (&real_value);
return;
} }
else else
g_value_copy (value, &real_value); g_value_copy (value, &real_value);
done:
/* create an interval and bind it to the property, in case /* create an interval and bind it to the property, in case
* it's not a fixed property, otherwise just set it * it's not a fixed property, otherwise just set it
*/ */

View File

@ -0,0 +1,321 @@
/*
* Clutter.
*
* An OpenGL based 'interactive canvas' library.
*
* Copyright (C) 2009 Intel Corp.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Author: Emmanuele Bassi <ebassi@linux.intel.com>
*/
/**
* SECTION:clutter-device-manager
* @short_description: Maintains the list of input devices
*
* #ClutterDeviceManager is a singleton object, owned by Clutter, which
* maintains the list of #ClutterInputDevice<!-- -->s.
*
* Depending on the backend used by Clutter it is possible to use the
* #ClutterDeviceManager::device-added and
* #ClutterDeviceManager::device-removed to monitor addition and removal
* of devices.
*
* #ClutterDeviceManager is available since Clutter 1.2
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "clutter-debug.h"
#include "clutter-device-manager.h"
#include "clutter-enum-types.h"
#include "clutter-marshal.h"
#include "clutter-private.h"
#define CLUTTER_DEVICE_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_DEVICE_MANAGER, ClutterDeviceManagerClass))
#define CLUTTER_IS_DEVICE_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_DEVICE_MANAGER))
#define CLUTTER_DEVICE_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_DEVICE_MANAGER, ClutterDeviceManagerClass))
typedef struct _ClutterDeviceManagerClass ClutterDeviceManagerClass;
struct _ClutterDeviceManagerClass
{
GObjectClass parent_instance;
};
enum
{
DEVICE_ADDED,
DEVICE_REMOVED,
LAST_SIGNAL
};
static ClutterDeviceManager *default_manager = NULL;
static guint manager_signals[LAST_SIGNAL] = { 0, };
G_DEFINE_TYPE (ClutterDeviceManager, clutter_device_manager, G_TYPE_OBJECT);
static void
clutter_device_manager_class_init (ClutterDeviceManagerClass *klass)
{
/**
* ClutterDeviceManager::device-added:
* @manager: the #ClutterDeviceManager that emitted the signal
* @device: the newly added #ClutterInputDevice
*
* The ::device-added signal is emitted each time a device has been
* added to the #ClutterDeviceManager
*
* Since: 1.2
*/
manager_signals[DEVICE_ADDED] =
g_signal_new (I_("device-added"),
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
clutter_marshal_VOID__OBJECT,
G_TYPE_NONE, 1,
CLUTTER_TYPE_INPUT_DEVICE);
/**
* ClutterDeviceManager::device-removed:
* @manager: the #ClutterDeviceManager that emitted the signal
* @device: the removed #ClutterInputDevice
*
* The ::device-removed signal is emitted each time a device has been
* removed from the #ClutterDeviceManager
*
* Since: 1.2
*/
manager_signals[DEVICE_REMOVED] =
g_signal_new (I_("device-removed"),
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
clutter_marshal_VOID__OBJECT,
G_TYPE_NONE, 1,
CLUTTER_TYPE_INPUT_DEVICE);
}
static void
clutter_device_manager_init (ClutterDeviceManager *self)
{
}
/**
* clutter_device_manager_get_default:
*
* Retrieves the device manager singleton
*
* Return value: (transfer none): the #ClutterDeviceManager singleton.
* The returned instance is owned by Clutter and it should not be
* modified or freed
*
* Since: 1.2
*/
ClutterDeviceManager *
clutter_device_manager_get_default (void)
{
if (G_UNLIKELY (default_manager == NULL))
default_manager = g_object_new (CLUTTER_TYPE_DEVICE_MANAGER, NULL);
return default_manager;
}
/**
* clutter_device_manager_list_devices:
* @device_manager: a #ClutterDeviceManager
*
* Lists all currently registered input devices
*
* Return value: (transfer container) (element-type ClutterInputDevice):
* a newly allocated list of #ClutterInputDevice objects. Use
* g_slist_free() to deallocate it when done
*
* Since: 1.2
*/
GSList *
clutter_device_manager_list_devices (ClutterDeviceManager *device_manager)
{
g_return_val_if_fail (CLUTTER_IS_DEVICE_MANAGER (device_manager), NULL);
return g_slist_copy (device_manager->devices);
}
/**
* clutter_device_manager_peek_devices:
* @device_manager: a #ClutterDeviceManager
*
* Lists all currently registered input devices
*
* Return value: (transfer none) (element-type ClutterInputDevice):
* a pointer to the internal list of #ClutterInputDevice objects. The
* returned list is owned by the #ClutterDeviceManager and should never
* be modified or freed
*
* Since: 1.2
*/
const GSList *
clutter_device_manager_peek_devices (ClutterDeviceManager *device_manager)
{
g_return_val_if_fail (CLUTTER_IS_DEVICE_MANAGER (device_manager), NULL);
return device_manager->devices;
}
/**
* clutter_device_manager_get_device:
* @device_manager: a #ClutterDeviceManager
* @device_id: the integer id of a device
*
* Retrieves the #ClutterInputDevice with the given @device_id
*
* Return value: (transfer none): a #ClutterInputDevice or %NULL. The
* returned device is owned by the #ClutterDeviceManager and should
* never be modified or freed
*
* Since: 1.2
*/
ClutterInputDevice *
clutter_device_manager_get_device (ClutterDeviceManager *device_manager,
gint device_id)
{
GSList *l;
g_return_val_if_fail (CLUTTER_IS_DEVICE_MANAGER (device_manager), NULL);
for (l = device_manager->devices; l != NULL; l = l->next)
{
ClutterInputDevice *device = l->data;
if (device->id == device_id)
return device;
}
return NULL;
}
static gint
input_device_cmp (gconstpointer a,
gconstpointer b)
{
const ClutterInputDevice *device_a = a;
const ClutterInputDevice *device_b = b;
if (device_a->id < device_b->id)
return -1;
if (device_a->id > device_b->id)
return 1;
return 0;
}
/*
* _clutter_device_manager_add_device:
* @device_manager: a #ClutterDeviceManager
* @device: a #ClutterInputDevice
*
* Adds @device to the list of #ClutterInputDevice<!-- -->s maintained
* by @device_manager
*
* The reference count of @device is not increased
*
* The #ClutterDeviceManager::device-added signal is emitted after
* adding @device to the list
*/
void
_clutter_device_manager_add_device (ClutterDeviceManager *device_manager,
ClutterInputDevice *device)
{
g_return_if_fail (CLUTTER_IS_DEVICE_MANAGER (device_manager));
device_manager->devices = g_slist_insert_sorted (device_manager->devices,
device,
input_device_cmp);
g_signal_emit (device_manager, manager_signals[DEVICE_ADDED], 0, device);
}
/*
* _clutter_device_manager_remove_device:
* @device_manager: a #ClutterDeviceManager
* @device: a #ClutterInputDevice
*
* Removes @device from the list of #ClutterInputDevice<!-- -->s
* maintained by @device_manager
*
* The reference count of @device is not decreased
*
* The #ClutterDeviceManager::device-removed signal is emitted after
* removing @device from the list
*/
void
_clutter_device_manager_remove_device (ClutterDeviceManager *device_manager,
ClutterInputDevice *device)
{
g_return_if_fail (CLUTTER_IS_DEVICE_MANAGER (device_manager));
if (g_slist_find (device_manager->devices, device) == NULL)
return;
device_manager->devices = g_slist_remove (device_manager->devices, device);
g_signal_emit (device_manager, manager_signals[DEVICE_REMOVED], 0, device);
}
/*
* _clutter_device_manager_update_devices:
* @device_manager: a #ClutterDeviceManager
*
* Updates every #ClutterInputDevice handled by @device_manager
* by performing a pick paint at the coordinates of each pointer
* device
*/
void
_clutter_device_manager_update_devices (ClutterDeviceManager *device_manager)
{
GSList *d;
/* the user disabled motion events delivery on actors; we
* don't perform any picking since the source of the events
* will always be set to be the stage
*/
if (!clutter_get_motion_events_enabled ())
return;
for (d = device_manager->devices; d != NULL; d = d->next)
{
ClutterInputDevice *device = d->data;
ClutterInputDeviceType device_type;
/* we only care about pointer devices */
device_type = clutter_input_device_get_device_type (device);
if (device_type != CLUTTER_POINTER_DEVICE)
continue;
/* out of stage */
if (device->stage == NULL)
continue;
_clutter_input_device_update (device);
}
}

View File

@ -0,0 +1,59 @@
/*
* Clutter.
*
* An OpenGL based 'interactive canvas' library.
*
* Copyright (C) 2009 Intel Corp.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Author: Emmanuele Bassi <ebassi@linux.intel.com>
*/
#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION)
#error "Only <clutter/clutter.h> can be included directly."
#endif
#ifndef __CLUTTER_DEVICE_MANAGER_H__
#define __CLUTTER_DEVICE_MANAGER_H__
#include <glib-object.h>
#include <clutter/clutter-event.h>
G_BEGIN_DECLS
#define CLUTTER_TYPE_DEVICE_MANAGER (clutter_device_manager_get_type ())
#define CLUTTER_DEVICE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_DEVICE_MANAGER, ClutterDeviceManager))
#define CLUTTER_IS_DEVICE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_DEVICE_MANAGER))
/**
* ClutterDeviceManager:
*
* The #ClutterDeviceManager structure contains only
* private data
*/
typedef struct _ClutterDeviceManager ClutterDeviceManager;
GType clutter_device_manager_get_type (void) G_GNUC_CONST;
ClutterDeviceManager *clutter_device_manager_get_default (void);
GSList * clutter_device_manager_list_devices (ClutterDeviceManager *device_manager);
const GSList * clutter_device_manager_peek_devices (ClutterDeviceManager *device_manager);
ClutterInputDevice * clutter_device_manager_get_device (ClutterDeviceManager *device_manager,
gint device_id);
G_END_DECLS
#endif /* __CLUTTER_DEVICE_MANAGER_H__ */

View File

@ -787,39 +787,3 @@ clutter_get_current_event (void)
return context->current_event; return context->current_event;
} }
/**
* clutter_input_device_get_device_type:
* @device: a #ClutterInputDevice
*
* Retrieves the type of @device
*
* Return value: the type of the device
*
* Since: 1.0
*/
ClutterInputDeviceType
clutter_input_device_get_device_type (ClutterInputDevice *device)
{
g_return_val_if_fail (device != NULL, CLUTTER_POINTER_DEVICE);
return device->device_type;
}
/**
* clutter_input_device_get_device_id:
* @device: a #ClutterInputDevice
*
* Retrieves the unique identifier of @device
*
* Return value: the identifier of the device
*
* Since: 1.0
*/
gint
clutter_input_device_get_device_id (ClutterInputDevice *device)
{
g_return_val_if_fail (device != NULL, -1);
return device->id;
}

View File

@ -29,6 +29,7 @@
#define __CLUTTER_EVENT_H__ #define __CLUTTER_EVENT_H__
#include <glib-object.h> #include <glib-object.h>
#include <clutter/clutter-input-device.h>
#include <clutter/clutter-types.h> #include <clutter/clutter-types.h>
#define CLUTTER_TYPE_EVENT (clutter_event_get_type ()) #define CLUTTER_TYPE_EVENT (clutter_event_get_type ())
@ -201,37 +202,6 @@ typedef struct _ClutterScrollEvent ClutterScrollEvent;
typedef struct _ClutterStageStateEvent ClutterStageStateEvent; typedef struct _ClutterStageStateEvent ClutterStageStateEvent;
typedef struct _ClutterCrossingEvent ClutterCrossingEvent; typedef struct _ClutterCrossingEvent ClutterCrossingEvent;
/**
* ClutterInputDevice:
*
* Generic representation of an input device. The
* actual contents of this structure depend on the
* backend used.
*/
typedef struct _ClutterInputDevice ClutterInputDevice;
/**
* ClutterInputDeviceType:
* @CLUTTER_POINTER_DEVICE: A pointer device
* @CLUTTER_KEYBOARD_DEVICE: A keyboard device
* @CLUTTER_EXTENSION_DEVICE: A generic extension device
* @CLUTTER_N_DEVICE_TYPES: The number of device types
*
* The types of input devices available.
*
* The #ClutterInputDeviceType enumeration can be extended at later
* date; not every platform supports every input device type.
*
* Since: 1.0
*/
typedef enum {
CLUTTER_POINTER_DEVICE,
CLUTTER_KEYBOARD_DEVICE,
CLUTTER_EXTENSION_DEVICE,
CLUTTER_N_DEVICE_TYPES
} ClutterInputDeviceType;
/** /**
* ClutterAnyEvent: * ClutterAnyEvent:
* @type: event type * @type: event type
@ -509,9 +479,6 @@ guint32 clutter_keysym_to_unicode (guint k
guint32 clutter_get_current_event_time (void); guint32 clutter_get_current_event_time (void);
G_CONST_RETURN ClutterEvent *clutter_get_current_event (void); G_CONST_RETURN ClutterEvent *clutter_get_current_event (void);
ClutterInputDeviceType clutter_input_device_get_device_type (ClutterInputDevice *device);
gint clutter_input_device_get_device_id (ClutterInputDevice *device);
G_END_DECLS G_END_DECLS
#endif /* __CLUTTER_EVENT_H__ */ #endif /* __CLUTTER_EVENT_H__ */

View File

@ -25,9 +25,16 @@
/** /**
* SECTION:clutter-feature * SECTION:clutter-feature
* @short_description: Query GL features at runtime * @short_description: Run-time detection of Clutter features
* *
* Functions to query available GL features ay runtime * Parts of Clutter depend on the underlying platform, including the
* capabilities of the backend used and the OpenGL features exposed through the
* Clutter and COGL API.
*
* It is possible to ask whether Clutter has support for specific features at
* run-time.
*
* See also cogl_get_features() and #CoglFeatureFlags
*/ */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H

View File

@ -25,14 +25,6 @@
#error "Only <clutter/clutter.h> can be included directly." #error "Only <clutter/clutter.h> can be included directly."
#endif #endif
/**
* SECTION:clutter-main
* @short_description: Various 'global' clutter functions.
*
* Functions to retrieve various global Clutter resources and other utility
* functions for mainloops, events and threads
*/
#ifndef __CLUTTER_FEATURE_H__ #ifndef __CLUTTER_FEATURE_H__
#define __CLUTTER_FEATURE_H__ #define __CLUTTER_FEATURE_H__

View File

@ -39,24 +39,25 @@ struct _ClutterFrameSource
ClutterTimeoutInterval timeout; ClutterTimeoutInterval timeout;
}; };
static gboolean clutter_frame_source_prepare (GSource *source, gint *timeout); static gboolean clutter_frame_source_prepare (GSource *source,
static gboolean clutter_frame_source_check (GSource *source); gint *timeout);
static gboolean clutter_frame_source_dispatch (GSource *source, static gboolean clutter_frame_source_check (GSource *source);
GSourceFunc callback, static gboolean clutter_frame_source_dispatch (GSource *source,
gpointer user_data); GSourceFunc callback,
gpointer user_data);
static GSourceFuncs clutter_frame_source_funcs = static GSourceFuncs clutter_frame_source_funcs =
{ {
clutter_frame_source_prepare, clutter_frame_source_prepare,
clutter_frame_source_check, clutter_frame_source_check,
clutter_frame_source_dispatch, clutter_frame_source_dispatch,
NULL NULL
}; };
/** /**
* clutter_frame_source_add_full: * clutter_frame_source_add_full:
* @priority: the priority of the frame source. Typically this will be in the * @priority: the priority of the frame source. Typically this will be in the
* range between #G_PRIORITY_DEFAULT and #G_PRIORITY_HIGH. * range between %G_PRIORITY_DEFAULT and %G_PRIORITY_HIGH.
* @fps: the number of times per second to call the function * @fps: the number of times per second to call the function
* @func: function to call * @func: function to call
* @data: data to pass to the function * @data: data to pass to the function
@ -131,7 +132,8 @@ clutter_frame_source_add (guint fps,
} }
static gboolean static gboolean
clutter_frame_source_prepare (GSource *source, gint *delay) clutter_frame_source_prepare (GSource *source,
gint *delay)
{ {
ClutterFrameSource *frame_source = (ClutterFrameSource *) source; ClutterFrameSource *frame_source = (ClutterFrameSource *) source;
GTimeVal current_time; GTimeVal current_time;

View File

@ -277,10 +277,12 @@ clutter_group_real_foreach (ClutterContainer *container,
{ {
ClutterGroup *group = CLUTTER_GROUP (container); ClutterGroup *group = CLUTTER_GROUP (container);
ClutterGroupPrivate *priv = group->priv; ClutterGroupPrivate *priv = group->priv;
GList *l;
for (l = priv->children; l; l = l->next) /* Using g_list_foreach instead of iterating the list manually
(* callback) (CLUTTER_ACTOR (l->data), user_data); because it has better protection against the current node being
removed. This will happen for example if someone calls
clutter_container_foreach(container, clutter_actor_destroy) */
g_list_foreach (priv->children, (GFunc) callback, user_data);
} }
static void static void

View File

@ -0,0 +1,524 @@
/*
* Clutter.
*
* An OpenGL based 'interactive canvas' library.
*
* Copyright (C) 2009 Intel Corp.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Author: Emmanuele Bassi <ebassi@linux.intel.com>
*/
/**
* SECTION:clutter-input-device
* @short_description: An input device managed by Clutter
*
* #ClutterInputDevice represents an input device known to Clutter.
*
* The #ClutterInputDevice class holds the state of the device, but
* its contents are usually defined by the Clutter backend in use.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "clutter-debug.h"
#include "clutter-enum-types.h"
#include "clutter-input-device.h"
#include "clutter-private.h"
enum
{
PROP_0,
PROP_ID,
PROP_DEVICE_TYPE,
PROP_NAME
};
G_DEFINE_TYPE (ClutterInputDevice, clutter_input_device, G_TYPE_OBJECT);
static void
clutter_input_device_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
ClutterInputDevice *self = CLUTTER_INPUT_DEVICE (gobject);
switch (prop_id)
{
case PROP_ID:
self->id = g_value_get_int (value);
break;
case PROP_DEVICE_TYPE:
self->device_type = g_value_get_enum (value);
break;
case PROP_NAME:
self->device_name = g_strdup (g_value_get_string (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
clutter_input_device_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
ClutterInputDevice *self = CLUTTER_INPUT_DEVICE (gobject);
switch (prop_id)
{
case PROP_ID:
g_value_set_int (value, self->id);
break;
case PROP_DEVICE_TYPE:
g_value_set_enum (value, self->device_type);
break;
case PROP_NAME:
g_value_set_string (value, self->device_name);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
clutter_input_device_class_init (ClutterInputDeviceClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GParamSpec *pspec;
gobject_class->set_property = clutter_input_device_set_property;
gobject_class->get_property = clutter_input_device_get_property;
/**
* ClutterInputDevice:id:
*
* The unique identifier of the device
*
* Since: 1.2
*/
pspec = g_param_spec_int ("id",
"Id",
"Unique identifier of the device",
-1, G_MAXINT,
0,
CLUTTER_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_property (gobject_class, PROP_ID, pspec);
/**
* ClutterInputDevice:name:
*
* The name of the device
*
* Since: 1.2
*/
pspec = g_param_spec_string ("name",
"Name",
"The name of the device",
NULL,
CLUTTER_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_property (gobject_class, PROP_NAME, pspec);
/**
* ClutterInputDevice:device-type:
*
* The type of the device
*
* Since: 1.2
*/
pspec = g_param_spec_enum ("device-type",
"Device Type",
"The type of the device",
CLUTTER_TYPE_INPUT_DEVICE_TYPE,
CLUTTER_POINTER_DEVICE,
CLUTTER_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_property (gobject_class, PROP_DEVICE_TYPE, pspec);
}
static void
clutter_input_device_init (ClutterInputDevice *self)
{
self->id = -1;
self->device_type = CLUTTER_POINTER_DEVICE;
self->click_count = 0;
self->current_time = self->previous_time = CLUTTER_CURRENT_TIME;
self->current_x = self->previous_x = -1;
self->current_y = self->previous_y = -1;
self->current_button_number = self->previous_button_number = -1;
self->current_state = self->previous_state = 0;
}
/*
* _clutter_input_device_set_coords:
* @device: a #ClutterInputDevice
* @x: X coordinate of the device
* @y: Y coordinate of the device
*
* Stores the last known coordinates of the device
*/
void
_clutter_input_device_set_coords (ClutterInputDevice *device,
gint x,
gint y)
{
g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device));
if (device->current_x != x)
device->current_x = x;
if (device->current_y != y)
device->current_y = y;
}
/*
* _clutter_input_device_set_state:
* @device: a #ClutterInputDevice
* @state: a bitmask of modifiers
*
* Stores the last known modifiers state of the device
*/
void
_clutter_input_device_set_state (ClutterInputDevice *device,
ClutterModifierType state)
{
g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device));
device->current_state = state;
}
/*
* _clutter_input_device_set_time:
* @device: a #ClutterInputDevice
* @time_: the time
*
* Stores the last known event time of the device
*/
void
_clutter_input_device_set_time (ClutterInputDevice *device,
guint32 time_)
{
g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device));
if (device->current_time != time_)
device->current_time = time_;
}
/*
* cursor_weak_unref:
*
* #ClutterInputDevice keeps a weak reference on the actor
* under its pointer; this function unsets the reference on
* the actor to avoid keeping around stale pointers
*/
static void
cursor_weak_unref (gpointer user_data,
GObject *object_pointer)
{
ClutterInputDevice *device = user_data;
device->cursor_actor = NULL;
}
/*
* _clutter_input_device_set_stage:
* @device: a #ClutterInputDevice
* @stage: a #ClutterStage or %NULL
*
* Stores the stage under the device
*/
void
_clutter_input_device_set_stage (ClutterInputDevice *device,
ClutterStage *stage)
{
g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device));
device->stage = stage;
/* if we left the stage then we also need to unset the
* cursor actor (and update its :has-pointer property)
*/
if (device->stage == NULL && device->cursor_actor != NULL)
{
_clutter_actor_set_has_pointer (device->cursor_actor, FALSE);
g_object_weak_unref (G_OBJECT (device->cursor_actor),
cursor_weak_unref,
device);
device->cursor_actor = NULL;
}
}
/*
* _clutter_input_device_set_actor:
* @device: a #ClutterInputDevice
* @actor: a #ClutterActor
*
* Sets the actor under the pointer coordinates of @device
*
* This function is called by _clutter_input_device_update()
* and it will:
*
* - queue a %CLUTTER_LEAVE event on the previous pointer actor
* of @device, if any
* - set to %FALSE the :has-pointer property of the previous
* pointer actor of @device, if any
* - queue a %CLUTTER_ENTER event on the new pointer actor
* - set to %TRUE the :has-pointer property of the new pointer
* actor
*/
void
_clutter_input_device_set_actor (ClutterInputDevice *device,
ClutterActor *actor)
{
ClutterActor *old_actor;
ClutterEvent cev;
g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device));
if (actor == device->cursor_actor)
return;
old_actor = device->cursor_actor;
if (old_actor != NULL)
{
cev.crossing.type = CLUTTER_LEAVE;
cev.crossing.time = device->current_time;
cev.crossing.flags = 0;
cev.crossing.stage = device->stage;
cev.crossing.source = device->cursor_actor;
cev.crossing.x = device->current_x;
cev.crossing.y = device->current_y;
cev.crossing.device = device;
cev.crossing.related = actor;
/* we need to make sure that this event is processed before
* any other event we might have queued up until now, so we
* go on and synthesize the event emission
*/
_clutter_process_event (&cev);
_clutter_actor_set_has_pointer (device->cursor_actor, FALSE);
g_object_weak_unref (G_OBJECT (device->cursor_actor),
cursor_weak_unref,
device);
device->cursor_actor = NULL;
}
if (actor != NULL)
{
cev.crossing.type = CLUTTER_ENTER;
cev.crossing.time = device->current_time;
cev.crossing.flags = 0;
cev.crossing.stage = device->stage;
cev.crossing.source = actor;
cev.crossing.x = device->current_x;
cev.crossing.y = device->current_y;
cev.crossing.device = device;
cev.crossing.related = old_actor;
/* as above: we need to make sure that this event is processed
* before any other event we might have queued up until now, so
* we go on and synthesize the event emission
*/
_clutter_process_event (&cev);
}
device->cursor_actor = actor;
if (device->cursor_actor != NULL)
{
g_object_weak_ref (G_OBJECT (device->cursor_actor),
cursor_weak_unref,
device);
_clutter_actor_set_has_pointer (device->cursor_actor, TRUE);
}
}
/**
* clutter_input_device_get_device_type:
* @device: a #ClutterInputDevice
*
* Retrieves the type of @device
*
* Return value: the type of the device
*
* Since: 1.0
*/
ClutterInputDeviceType
clutter_input_device_get_device_type (ClutterInputDevice *device)
{
g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device),
CLUTTER_POINTER_DEVICE);
return device->device_type;
}
/**
* clutter_input_device_get_device_id:
* @device: a #ClutterInputDevice
*
* Retrieves the unique identifier of @device
*
* Return value: the identifier of the device
*
* Since: 1.0
*/
gint
clutter_input_device_get_device_id (ClutterInputDevice *device)
{
g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), -1);
return device->id;
}
/**
* clutter_input_device_get_device_coords:
* @device: a #ClutterInputDevice of type %CLUTTER_POINTER_DEVICE
* @x: (out): return location for the X coordinate
* @y: (out): return location for the Y coordinate
*
* Retrieves the latest coordinates of the pointer of @device
*
* Since: 1.2
*/
void
clutter_input_device_get_device_coords (ClutterInputDevice *device,
gint *x,
gint *y)
{
g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device));
g_return_if_fail (device->device_type == CLUTTER_POINTER_DEVICE);
if (x)
*x = device->current_x;
if (y)
*y = device->current_y;
}
/*
* _clutter_input_device_update:
* @device: a #ClutterInputDevice
*
* Updates the input @device by determining the #ClutterActor underneath the
* pointer's cursor
*
* This function calls _clutter_input_device_set_actor() if needed.
*
* This function only works for #ClutterInputDevice of type
* %CLUTTER_POINTER_DEVICE.
*
* Since: 1.2
*/
ClutterActor *
_clutter_input_device_update (ClutterInputDevice *device)
{
ClutterStage *stage;
ClutterActor *new_cursor_actor;
ClutterActor *old_cursor_actor;
gint x, y;
g_return_val_if_fail (device->device_type == CLUTTER_POINTER_DEVICE, NULL);
clutter_input_device_get_device_coords (device, &x, &y);
stage = device->stage;
old_cursor_actor = device->cursor_actor;
new_cursor_actor = _clutter_do_pick (stage, x, y, CLUTTER_PICK_REACTIVE);
/* if the pick could not find an actor then we do not update the
* input device, to avoid ghost enter/leave events; the pick should
* never fail, except for bugs in the glReadPixels() implementation
* in which case this is the safest course of action anyway
*/
if (new_cursor_actor == NULL)
return NULL;
CLUTTER_NOTE (EVENT,
"Actor under cursor (device %d, at %d, %d): %s",
clutter_input_device_get_device_id (device),
x, y,
clutter_actor_get_name (new_cursor_actor) != NULL
? clutter_actor_get_name (new_cursor_actor)
: G_OBJECT_TYPE_NAME (new_cursor_actor));
/* short-circuit here */
if (new_cursor_actor == old_cursor_actor)
return old_cursor_actor;
_clutter_input_device_set_actor (device, new_cursor_actor);
return device->cursor_actor;
}
/**
* clutter_input_device_get_pointer_actor:
* @device: a #ClutterInputDevice of type %CLUTTER_POINTER_DEVICE
*
* Retrieves the #ClutterActor underneath the pointer of @device
*
* Return value: (transfer none): a pointer to the #ClutterActor or %NULL
*
* Since: 1.2
*/
ClutterActor *
clutter_input_device_get_pointer_actor (ClutterInputDevice *device)
{
g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), NULL);
g_return_val_if_fail (device->device_type == CLUTTER_POINTER_DEVICE, NULL);
return device->cursor_actor;
}
/**
* clutter_input_device_get_device_name:
* @device: a #ClutterInputDevice
*
* Retrieves the name of the @device
*
* Return value: the name of the device, or %NULL. The returned string
* is owned by the #ClutterInputDevice and should never be modified
* or freed
*
* Since: 1.2
*/
G_CONST_RETURN gchar *
clutter_input_device_get_device_name (ClutterInputDevice *device)
{
g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), NULL);
return device->device_name;
}

View File

@ -0,0 +1,99 @@
/*
* Clutter.
*
* An OpenGL based 'interactive canvas' library.
*
* Copyright (C) 2009 Intel Corp.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Author: Emmanuele Bassi <ebassi@linux.intel.com>
*/
#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION)
#error "Only <clutter/clutter.h> can be included directly."
#endif
#ifndef __CLUTTER_INPUT_DEVICE_H__
#define __CLUTTER_INPUT_DEVICE_H__
#include <clutter/clutter-types.h>
G_BEGIN_DECLS
#define CLUTTER_TYPE_INPUT_DEVICE (clutter_input_device_get_type ())
#define CLUTTER_INPUT_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_INPUT_DEVICE, ClutterInputDevice))
#define CLUTTER_IS_INPUT_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_INPUT_DEVICE))
#define CLUTTER_INPUT_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_INPUT_DEVICE, ClutterInputDeviceClass))
#define CLUTTER_IS_INPUT_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_INPUT_DEVICE))
#define CLUTTER_INPUT_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_INPUT_DEVICE, ClutterInputDeviceClass))
/**
* ClutterInputDevice:
*
* Generic representation of an input device. The actual contents of this
* structure depend on the backend used.
*/
typedef struct _ClutterInputDevice ClutterInputDevice;
typedef struct _ClutterInputDeviceClass ClutterInputDeviceClass;
/**
* ClutterInputDeviceType:
* @CLUTTER_POINTER_DEVICE: A pointer device
* @CLUTTER_KEYBOARD_DEVICE: A keyboard device
* @CLUTTER_EXTENSION_DEVICE: A generic extension device
* @CLUTTER_N_DEVICE_TYPES: The number of device types
*
* The types of input devices available.
*
* The #ClutterInputDeviceType enumeration can be extended at later
* date; not every platform supports every input device type.
*
* Since: 1.0
*/
typedef enum {
CLUTTER_POINTER_DEVICE,
CLUTTER_KEYBOARD_DEVICE,
CLUTTER_EXTENSION_DEVICE,
CLUTTER_N_DEVICE_TYPES
} ClutterInputDeviceType;
/**
* ClutterInputDeviceClass:
*
* The #ClutterInputDeviceClass structure contains only private
* data and should not be accessed directly
*
* Since: 1.2
*/
struct _ClutterInputDeviceClass
{
/*< private >*/
GObjectClass parent_class;
};
GType clutter_input_device_get_type (void) G_GNUC_CONST;
ClutterInputDeviceType clutter_input_device_get_device_type (ClutterInputDevice *device);
gint clutter_input_device_get_device_id (ClutterInputDevice *device);
void clutter_input_device_get_device_coords (ClutterInputDevice *device,
gint *x,
gint *y);
ClutterActor * clutter_input_device_get_pointer_actor (ClutterInputDevice *device);
G_CONST_RETURN gchar * clutter_input_device_get_device_name (ClutterInputDevice *device);
G_END_DECLS
#endif /* __CLUTTER_INPUT_DEVICE_H__ */

View File

@ -2045,69 +2045,93 @@ event_click_count_generate (ClutterEvent *event)
static guint32 previous_time = 0; static guint32 previous_time = 0;
static gint previous_button_number = -1; static gint previous_button_number = -1;
ClutterInputDevice *device = NULL;
ClutterBackend *backend; ClutterBackend *backend;
guint double_click_time; guint double_click_time;
guint double_click_distance; guint double_click_distance;
backend = _clutter_context_get_default ()->backend; backend = clutter_get_default_backend ();
double_click_distance = clutter_backend_get_double_click_distance (backend); double_click_distance = clutter_backend_get_double_click_distance (backend);
double_click_time = clutter_backend_get_double_click_time (backend); double_click_time = clutter_backend_get_double_click_time (backend);
if (event->button.device != NULL) device = clutter_event_get_device (event);
if (device != NULL)
{ {
click_count = event->button.device->click_count; click_count = device->click_count;
previous_x = event->button.device->previous_x; previous_x = device->previous_x;
previous_y = event->button.device->previous_y; previous_y = device->previous_y;
previous_time = event->button.device->previous_time; previous_time = device->previous_time;
previous_button_number = event->button.device->previous_button_number; previous_button_number = device->previous_button_number;
CLUTTER_NOTE (EVENT,
"Restoring previous click count:%d (device:%d, time:%u)",
click_count,
clutter_input_device_get_device_id (device),
previous_time);
}
else
{
CLUTTER_NOTE (EVENT,
"Restoring previous click count:%d (time:%u)",
click_count,
previous_time);
} }
switch (event->type) switch (clutter_event_type (event))
{ {
case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_PRESS:
case CLUTTER_SCROLL:
/* check if we are in time and within distance to increment an /* check if we are in time and within distance to increment an
* existing click count * existing click count
*/ */
if (event->button.time < previous_time + double_click_time && if (event->button.button == previous_button_number &&
event->button.time < (previous_time + double_click_time) &&
(ABS (event->button.x - previous_x) <= double_click_distance) && (ABS (event->button.x - previous_x) <= double_click_distance) &&
(ABS (event->button.y - previous_y) <= double_click_distance) (ABS (event->button.y - previous_y) <= double_click_distance))
&& event->button.button == previous_button_number)
{ {
click_count ++; CLUTTER_NOTE (EVENT, "Increase click count (button: %d, time: %u)",
event->button.button,
event->button.time);
click_count += 1;
} }
else /* start a new click count*/ else /* start a new click count*/
{ {
click_count=1; CLUTTER_NOTE (EVENT, "Reset click count (button: %d, time: %u)",
event->button.button,
event->button.time);
click_count = 1;
previous_button_number = event->button.button; previous_button_number = event->button.button;
} }
/* store time and position for this click for comparison with previous_x = event->button.x;
* next event previous_y = event->button.y;
*/
previous_time = event->button.time; previous_time = event->button.time;
previous_x = event->button.x;
previous_y = event->button.y;
/* fallthrough */ /* fallthrough */
case CLUTTER_BUTTON_RELEASE: case CLUTTER_BUTTON_RELEASE:
event->button.click_count=click_count; event->button.click_count = click_count;
break; break;
default: default:
g_assert (NULL); g_assert (NULL);
} }
if (event->button.device != NULL) if (event->type == CLUTTER_BUTTON_PRESS && device != NULL)
{ {
event->button.device->click_count = click_count; CLUTTER_NOTE (EVENT, "Storing click count: %d (device:%d, time:%u)",
event->button.device->previous_x = previous_x; click_count,
event->button.device->previous_y = previous_y; clutter_input_device_get_device_id (device),
event->button.device->previous_time = previous_time; previous_time);
event->button.device->previous_button_number = previous_button_number;
device->click_count = click_count;
device->previous_x = previous_x;
device->previous_y = previous_y;
device->previous_time = previous_time;
device->previous_button_number = previous_button_number;
} }
} }
static inline void static inline void
emit_event (ClutterEvent *event, emit_event (ClutterEvent *event,
gboolean is_key_event) gboolean is_key_event)
@ -2118,7 +2142,7 @@ emit_event (ClutterEvent *event,
ClutterActor *actor; ClutterActor *actor;
gint i = 0; gint i = 0;
if (!event->any.source) if (event->any.source == NULL)
{ {
CLUTTER_NOTE (EVENT, "No source set, discarding event"); CLUTTER_NOTE (EVENT, "No source set, discarding event");
return; return;
@ -2182,136 +2206,38 @@ static inline void
emit_pointer_event (ClutterEvent *event, emit_pointer_event (ClutterEvent *event,
ClutterInputDevice *device) ClutterInputDevice *device)
{ {
/* Using the global variable directly, since it has to be initialized ClutterMainContext *context = _clutter_context_get_default ();
* at this point
*/
ClutterMainContext *context = ClutterCntx;
if (G_UNLIKELY (context->pointer_grab_actor != NULL && if (context->pointer_grab_actor == NULL &&
device == NULL)) (device == NULL || device->pointer_grab_actor == NULL))
{
/* global grab */
clutter_actor_event (context->pointer_grab_actor, event, FALSE);
}
else if (G_UNLIKELY (device != NULL &&
device->pointer_grab_actor != NULL))
{
/* per device grab */
clutter_actor_event (device->pointer_grab_actor, event, FALSE);
}
else
{ {
/* no grab, time to capture and bubble */ /* no grab, time to capture and bubble */
emit_event (event, FALSE); emit_event (event, FALSE);
} }
else
{
if (context->pointer_grab_actor != NULL)
{
/* global grab */
clutter_actor_event (context->pointer_grab_actor, event, FALSE);
}
else if (device != NULL && device->pointer_grab_actor != NULL)
{
/* per device grab */
clutter_actor_event (device->pointer_grab_actor, event, FALSE);
}
}
} }
static inline void static inline void
emit_keyboard_event (ClutterEvent *event) emit_keyboard_event (ClutterEvent *event)
{ {
ClutterMainContext *context = ClutterCntx; ClutterMainContext *context = _clutter_context_get_default ();
if (G_UNLIKELY (context->keyboard_grab_actor != NULL)) if (context->keyboard_grab_actor == NULL)
clutter_actor_event (context->keyboard_grab_actor, event, FALSE);
else
emit_event (event, TRUE); emit_event (event, TRUE);
}
static void
unset_motion_last_actor (ClutterActor *actor, ClutterInputDevice *dev)
{
ClutterMainContext *context = ClutterCntx;
if (dev == NULL)
context->motion_last_actor = NULL;
else else
dev->motion_last_actor = NULL; clutter_actor_event (context->keyboard_grab_actor, event, FALSE);
}
static void
set_motion_last_actor (ClutterActor *motion_current_actor,
ClutterInputDevice *device)
{
ClutterMainContext *context = ClutterCntx;
ClutterActor *last_actor = context->motion_last_actor;
if (device != NULL)
last_actor = device->motion_last_actor;
if (last_actor && last_actor != motion_current_actor)
{
g_signal_handlers_disconnect_by_func
(last_actor,
G_CALLBACK (unset_motion_last_actor),
device);
}
if (motion_current_actor && last_actor != motion_current_actor)
{
g_signal_connect (motion_current_actor, "destroy",
G_CALLBACK (unset_motion_last_actor),
device);
}
if (device != NULL)
device->motion_last_actor = motion_current_actor;
else
context->motion_last_actor = motion_current_actor;
}
static inline void
generate_enter_leave_events (ClutterEvent *event)
{
ClutterMainContext *context = ClutterCntx;
ClutterActor *motion_current_actor = event->motion.source;
ClutterActor *last_actor = context->motion_last_actor;
ClutterInputDevice *device = clutter_event_get_device (event);
if (device != NULL)
last_actor = device->motion_last_actor;
if (last_actor != motion_current_actor)
{
if (motion_current_actor)
{
ClutterEvent cev;
gfloat x, y;
cev.crossing.device = device;
clutter_event_get_coords (event, &x, &y);
if (context->motion_last_actor)
{
cev.crossing.type = CLUTTER_LEAVE;
cev.crossing.time = event->any.time;
cev.crossing.flags = 0;
cev.crossing.x = x;
cev.crossing.y = y;
cev.crossing.source = last_actor;
cev.crossing.stage = event->any.stage;
cev.crossing.related = motion_current_actor;
emit_pointer_event (&cev, device);
}
cev.crossing.type = CLUTTER_ENTER;
cev.crossing.time = event->any.time;
cev.crossing.flags = 0;
cev.crossing.x = x;
cev.crossing.y = y;
cev.crossing.source = motion_current_actor;
cev.crossing.stage = event->any.stage;
if (context->motion_last_actor)
cev.crossing.related = last_actor;
else
cev.crossing.related = NULL;
emit_pointer_event (&cev, device);
}
}
set_motion_last_actor (motion_current_actor, device);
} }
/** /**
@ -2343,7 +2269,9 @@ _clutter_process_event_details (ClutterActor *stage,
ClutterMainContext *context, ClutterMainContext *context,
ClutterEvent *event) ClutterEvent *event)
{ {
ClutterInputDevice *device = NULL; ClutterInputDevice *device = NULL;
device = clutter_event_get_device (event);
switch (event->type) switch (event->type)
{ {
@ -2352,23 +2280,8 @@ _clutter_process_event_details (ClutterActor *stage,
break; break;
case CLUTTER_LEAVE: case CLUTTER_LEAVE:
/* The source is set for generated events, not for events
* resulting from the cursor leaving the stage
*/
if (event->any.source == NULL)
{
ClutterActor *last_actor = context->motion_last_actor;
if (event->crossing.device != NULL)
last_actor = event->crossing.device->motion_last_actor;
event->any.source = last_actor;
set_motion_last_actor (NULL, event->crossing.device);
}
/* flow through */
case CLUTTER_ENTER: case CLUTTER_ENTER:
emit_pointer_event (event, event->crossing.device); emit_pointer_event (event, device);
break; break;
case CLUTTER_DESTROY_NOTIFY: case CLUTTER_DESTROY_NOTIFY:
@ -2400,8 +2313,6 @@ _clutter_process_event_details (ClutterActor *stage,
break; break;
case CLUTTER_MOTION: case CLUTTER_MOTION:
device = event->motion.device;
/* Only stage gets motion events if clutter_set_motion_events is TRUE, /* Only stage gets motion events if clutter_set_motion_events is TRUE,
* and the event is not a synthetic event with source set. * and the event is not a synthetic event with source set.
*/ */
@ -2434,8 +2345,7 @@ _clutter_process_event_details (ClutterActor *stage,
break; break;
} }
/* fallthrough */ /* fallthrough from motion */
case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_PRESS:
case CLUTTER_BUTTON_RELEASE: case CLUTTER_BUTTON_RELEASE:
case CLUTTER_SCROLL: case CLUTTER_SCROLL:
@ -2462,18 +2372,29 @@ _clutter_process_event_details (ClutterActor *stage,
x, y); x, y);
event->button.source = stage; event->button.source = stage;
emit_pointer_event (event, event->button.device); event->button.click_count = 1;
emit_pointer_event (event, device);
} }
break; break;
} }
/* Map the event to a reactive actor */ /* if the backend provides a device then we should
actor = _clutter_do_pick (CLUTTER_STAGE (stage), * already have everything we need to update it and
x, y, * get the actor underneath
CLUTTER_PICK_REACTIVE); */
if (device != NULL)
actor = _clutter_input_device_update (device);
else
{
CLUTTER_NOTE (EVENT, "No device found: picking");
actor = _clutter_do_pick (CLUTTER_STAGE (stage),
x, y,
CLUTTER_PICK_REACTIVE);
}
event->any.source = actor; event->any.source = actor;
if (!actor) if (event->any.source == NULL)
break; break;
} }
else else
@ -2482,7 +2403,6 @@ _clutter_process_event_details (ClutterActor *stage,
actor = event->any.source; actor = event->any.source;
} }
/* FIXME: for an optimisation should check if there are /* FIXME: for an optimisation should check if there are
* actually any reactive actors and avoid the pick all together * actually any reactive actors and avoid the pick all together
* (signalling just the stage). Should be big help for gles. * (signalling just the stage). Should be big help for gles.
@ -2493,33 +2413,12 @@ _clutter_process_event_details (ClutterActor *stage,
x, y, x, y,
actor); actor);
/* Create, enter/leave events if needed */
generate_enter_leave_events (event);
if (event->type != CLUTTER_MOTION) if (event->type != CLUTTER_MOTION)
{ {
/* Generate click count */ /* Generate click count */
event_click_count_generate (event); event_click_count_generate (event);
} }
if (device == NULL)
{
switch (event->type)
{
case CLUTTER_BUTTON_PRESS:
case CLUTTER_BUTTON_RELEASE:
device = event->button.device;
break;
case CLUTTER_SCROLL:
device = event->scroll.device;
break;
case CLUTTER_MOTION:
/* already handled in the MOTION case of the switch */
default:
break;
}
}
emit_pointer_event (event, device); emit_pointer_event (event, device);
break; break;
} }
@ -3023,9 +2922,19 @@ clutter_get_font_flags (void)
/** /**
* clutter_get_input_device_for_id: * clutter_get_input_device_for_id:
* @id: a device id * @id: the unique id for a device
* *
* Retrieves the #ClutterInputDevice from its id. * Retrieves the #ClutterInputDevice from its @id. This is a convenience
* wrapper for clutter_device_manager_get_device() and it is functionally
* equivalent to:
*
* |[
* ClutterDeviceManager *manager;
* ClutterInputDevice *device;
*
* manager = clutter_device_manager_get_default ();
* device = clutter_device_manager_get_device (manager, id);
* ]|
* *
* Return value: (transfer none): a #ClutterInputDevice, or %NULL * Return value: (transfer none): a #ClutterInputDevice, or %NULL
* *
@ -3034,23 +2943,11 @@ clutter_get_font_flags (void)
ClutterInputDevice * ClutterInputDevice *
clutter_get_input_device_for_id (gint id) clutter_get_input_device_for_id (gint id)
{ {
GSList *item; ClutterDeviceManager *manager;
ClutterInputDevice *device = NULL;
ClutterMainContext *context;
context = _clutter_context_get_default (); manager = clutter_device_manager_get_default ();
for (item = context->input_devices; return clutter_device_manager_get_device (manager, id);
item != NULL;
item = item->next)
{
device = item->data;
if (device->id == id)
return device;
}
return NULL;
} }
/** /**

View File

@ -12,6 +12,7 @@ VOID:INT,INT,INT,INT
VOID:OBJECT VOID:OBJECT
VOID:OBJECT,OBJECT,PARAM VOID:OBJECT,OBJECT,PARAM
VOID:OBJECT,POINTER VOID:OBJECT,POINTER
VOID:POINTER
VOID:STRING,BOOLEAN,BOOLEAN VOID:STRING,BOOLEAN,BOOLEAN
VOID:STRING,INT VOID:STRING,INT
VOID:UINT VOID:UINT

View File

@ -288,14 +288,13 @@ clutter_clock_dispatch (GSource *source,
* event handling * event handling
*/ */
stages = clutter_stage_manager_list_stages (stage_manager); stages = clutter_stage_manager_list_stages (stage_manager);
g_slist_foreach (stages, (GFunc)g_object_ref, NULL); g_slist_foreach (stages, (GFunc) g_object_ref, NULL);
CLUTTER_TIMER_START (_clutter_uprof_context, master_event_process); CLUTTER_TIMER_START (_clutter_uprof_context, master_event_process);
master_clock->updated_stages = FALSE; master_clock->updated_stages = FALSE;
/* Process queued events /* Process queued events */
*/
for (l = stages; l != NULL; l = l->next) for (l = stages; l != NULL; l = l->next)
_clutter_stage_process_queued_events (l->data); _clutter_stage_process_queued_events (l->data);
@ -311,7 +310,7 @@ clutter_clock_dispatch (GSource *source,
for (l = stages; l != NULL; l = l->next) for (l = stages; l != NULL; l = l->next)
master_clock->updated_stages |= _clutter_stage_do_update (l->data); master_clock->updated_stages |= _clutter_stage_do_update (l->data);
g_slist_foreach (stages, (GFunc)g_object_unref, NULL); g_slist_foreach (stages, (GFunc) g_object_unref, NULL);
g_slist_free (stages); g_slist_free (stages);
master_clock->prev_tick = master_clock->cur_tick; master_clock->prev_tick = master_clock->cur_tick;
@ -446,7 +445,7 @@ _clutter_master_clock_start_running (ClutterMasterClock *master_clock)
void void
_clutter_master_clock_advance (ClutterMasterClock *master_clock) _clutter_master_clock_advance (ClutterMasterClock *master_clock)
{ {
GSList *l, *next; GSList *timelines, *l;
CLUTTER_STATIC_TIMER (master_timeline_advance, CLUTTER_STATIC_TIMER (master_timeline_advance,
"Master Clock", "Master Clock",
@ -456,23 +455,38 @@ _clutter_master_clock_advance (ClutterMasterClock *master_clock)
g_return_if_fail (CLUTTER_IS_MASTER_CLOCK (master_clock)); g_return_if_fail (CLUTTER_IS_MASTER_CLOCK (master_clock));
/* we protect ourselves from timelines being removed during
* the advancement by other timelines
*/
g_slist_foreach (master_clock->timelines, (GFunc) g_object_ref, NULL);
CLUTTER_TIMER_START (_clutter_uprof_context, master_timeline_advance); CLUTTER_TIMER_START (_clutter_uprof_context, master_timeline_advance);
for (l = master_clock->timelines; l != NULL; l = next) /* we protect ourselves from timelines being removed during
{ * the advancement by other timelines by copying the list of
next = l->next; * timelines, taking a reference on them, iterating over the
* copied list and then releasing the reference.
*
* we cannot simply take a reference on the timelines and still
* use the list held by the master clock because the do_tick()
* might result in the creation of a new timeline, which gets
* added at the end of the list with no reference increase and
* thus gets disposed at the end of the iteration.
*
* this implies that a newly added timeline will not be advanced
* by this clock iteration, which is perfectly fine since we're
* in its first cycle.
*
* we also cannot steal the master clock timelines list because
* a timeline might be removed as the direct result of do_tick()
* and remove_timeline() would not find the timeline, failing
* and leaving a dangling pointer behind.
*/
timelines = g_slist_copy (master_clock->timelines);
g_slist_foreach (timelines, (GFunc) g_object_ref, NULL);
clutter_timeline_do_tick (l->data, &master_clock->cur_tick); for (l = timelines; l != NULL; l = l->next)
} clutter_timeline_do_tick (l->data, &master_clock->cur_tick);
g_slist_foreach (timelines, (GFunc) g_object_unref, NULL);
g_slist_free (timelines);
CLUTTER_TIMER_STOP (_clutter_uprof_context, master_timeline_advance); CLUTTER_TIMER_STOP (_clutter_uprof_context, master_timeline_advance);
g_slist_foreach (master_clock->timelines, (GFunc) g_object_unref, NULL);
} }
/** /**

View File

@ -41,6 +41,7 @@
#include "pango/cogl-pango.h" #include "pango/cogl-pango.h"
#include "clutter-backend.h" #include "clutter-backend.h"
#include "clutter-device-manager.h"
#include "clutter-event.h" #include "clutter-event.h"
#include "clutter-feature.h" #include "clutter-feature.h"
#include "clutter-id-pool.h" #include "clutter-id-pool.h"
@ -52,6 +53,8 @@
G_BEGIN_DECLS G_BEGIN_DECLS
typedef struct _ClutterMainContext ClutterMainContext;
typedef enum { typedef enum {
CLUTTER_ACTOR_UNUSED_FLAG = 0, CLUTTER_ACTOR_UNUSED_FLAG = 0,
@ -82,21 +85,54 @@ typedef enum {
struct _ClutterInputDevice struct _ClutterInputDevice
{ {
gint id; GObject parent_instance;
gint id;
ClutterInputDeviceType device_type; ClutterInputDeviceType device_type;
ClutterActor *pointer_grab_actor; gchar *device_name;
ClutterActor *motion_last_actor;
gint click_count; /* the actor underneath the pointer */
gint previous_x; ClutterActor *cursor_actor;
gint previous_y;
guint32 previous_time; /* the actor that has a grab in place for the device */
gint previous_button_number; ClutterActor *pointer_grab_actor;
/* the current click count */
gint click_count;
/* the stage the device is on */
ClutterStage *stage;
/* the current state */
gint current_x;
gint current_y;
guint32 current_time;
gint current_button_number;
ClutterModifierType current_state;
/* the previous state, used for click count generation */
gint previous_x;
gint previous_y;
guint32 previous_time;
gint previous_button_number;
ClutterModifierType previous_state;
}; };
typedef struct _ClutterMainContext ClutterMainContext; struct _ClutterStageManager
{
GObject parent_instance;
GSList *stages;
};
struct _ClutterDeviceManager
{
GObject parent_instance;
GSList *devices;
};
struct _ClutterMainContext struct _ClutterMainContext
{ {
@ -138,9 +174,6 @@ struct _ClutterMainContext
PangoContext *pango_context; /* Global Pango context */ PangoContext *pango_context; /* Global Pango context */
CoglPangoFontMap *font_map; /* Global font map */ CoglPangoFontMap *font_map; /* Global font map */
GSList *input_devices; /* For extra input devices, i.e
MultiTouch */
ClutterEvent *current_event; ClutterEvent *current_event;
guint32 last_event_time; guint32 last_event_time;
@ -170,21 +203,34 @@ PangoContext *_clutter_context_get_pango_context (ClutterMainContext *self);
#define I_(str) (g_intern_static_string ((str))) #define I_(str) (g_intern_static_string ((str)))
/* device manager */
void _clutter_device_manager_add_device (ClutterDeviceManager *device_manager,
ClutterInputDevice *device);
void _clutter_device_manager_remove_device (ClutterDeviceManager *device_manager,
ClutterInputDevice *device);
void _clutter_device_manager_update_devices (ClutterDeviceManager *device_manager);
/* input device */
void _clutter_input_device_set_coords (ClutterInputDevice *device,
gint x,
gint y);
void _clutter_input_device_set_state (ClutterInputDevice *device,
ClutterModifierType state);
void _clutter_input_device_set_time (ClutterInputDevice *device,
guint32 time_);
void _clutter_input_device_set_stage (ClutterInputDevice *device,
ClutterStage *stage);
void _clutter_input_device_set_actor (ClutterInputDevice *device,
ClutterActor *actor);
ClutterActor *_clutter_input_device_update (ClutterInputDevice *device);
/* stage manager */ /* stage manager */
struct _ClutterStageManager
{
GObject parent_instance;
GSList *stages;
};
void _clutter_stage_manager_add_stage (ClutterStageManager *stage_manager, void _clutter_stage_manager_add_stage (ClutterStageManager *stage_manager,
ClutterStage *stage); ClutterStage *stage);
void _clutter_stage_manager_remove_stage (ClutterStageManager *stage_manager, void _clutter_stage_manager_remove_stage (ClutterStageManager *stage_manager,
ClutterStage *stage); ClutterStage *stage);
/* stage */ /* stage */
void _clutter_stage_set_window (ClutterStage *stage, void _clutter_stage_set_window (ClutterStage *stage,
ClutterStageWindow *stage_window); ClutterStageWindow *stage_window);
ClutterStageWindow *_clutter_stage_get_window (ClutterStage *stage); ClutterStageWindow *_clutter_stage_get_window (ClutterStage *stage);
@ -198,6 +244,7 @@ void _clutter_stage_queue_event (ClutterStage *stage,
ClutterEvent *event); ClutterEvent *event);
gboolean _clutter_stage_has_queued_events (ClutterStage *stage); gboolean _clutter_stage_has_queued_events (ClutterStage *stage);
void _clutter_stage_process_queued_events (ClutterStage *stage); void _clutter_stage_process_queued_events (ClutterStage *stage);
void _clutter_stage_update_input_devices (ClutterStage *stage);
/* vfuncs implemented by backend */ /* vfuncs implemented by backend */
GType _clutter_backend_impl_get_type (void); GType _clutter_backend_impl_get_type (void);
@ -271,6 +318,9 @@ void _clutter_actor_set_enable_model_view_transform (ClutterActor *self,
void _clutter_actor_set_enable_paint_unmapped (ClutterActor *self, void _clutter_actor_set_enable_paint_unmapped (ClutterActor *self,
gboolean enable); gboolean enable);
void _clutter_actor_set_has_pointer (ClutterActor *self,
gboolean has_pointer);
void _clutter_run_repaint_functions (void); void _clutter_run_repaint_functions (void);
gint32 _clutter_backend_get_units_serial (ClutterBackend *backend); gint32 _clutter_backend_get_units_serial (ClutterBackend *backend);

View File

@ -69,6 +69,7 @@
#include "clutter-id-pool.h" #include "clutter-id-pool.h"
#include "clutter-container.h" #include "clutter-container.h"
#include "clutter-profile.h" #include "clutter-profile.h"
#include "clutter-input-device.h"
#include "cogl/cogl.h" #include "cogl/cogl.h"
@ -113,7 +114,8 @@ enum
PROP_USER_RESIZE, PROP_USER_RESIZE,
PROP_USE_FOG, PROP_USE_FOG,
PROP_FOG, PROP_FOG,
PROP_USE_ALPHA PROP_USE_ALPHA,
PROP_KEY_FOCUS
}; };
enum enum
@ -454,6 +456,7 @@ _clutter_stage_queue_event (ClutterStage *stage,
{ {
ClutterStagePrivate *priv; ClutterStagePrivate *priv;
gboolean first_event; gboolean first_event;
ClutterInputDevice *device;
g_return_if_fail (CLUTTER_IS_STAGE (stage)); g_return_if_fail (CLUTTER_IS_STAGE (stage));
@ -461,14 +464,31 @@ _clutter_stage_queue_event (ClutterStage *stage,
first_event = priv->event_queue->length == 0; first_event = priv->event_queue->length == 0;
g_queue_push_tail (priv->event_queue, g_queue_push_tail (priv->event_queue, clutter_event_copy (event));
clutter_event_copy (event));
if (first_event) if (first_event)
{ {
ClutterMasterClock *master_clock = _clutter_master_clock_get_default (); ClutterMasterClock *master_clock = _clutter_master_clock_get_default ();
_clutter_master_clock_start_running (master_clock); _clutter_master_clock_start_running (master_clock);
} }
/* if needed, update the state of the input device of the event.
* we do it here to avoid calling the same code from every backend
* event processing function
*/
device = clutter_event_get_device (event);
if (device != NULL)
{
ClutterModifierType event_state = clutter_event_get_state (event);
guint32 event_time = clutter_event_get_time (event);
gfloat event_x, event_y;
clutter_event_get_coords (event, &event_x, &event_y);
_clutter_input_device_set_coords (device, event_x, event_y);
_clutter_input_device_set_state (device, event_state);
_clutter_input_device_set_time (device, event_time);
}
} }
gboolean gboolean
@ -487,7 +507,7 @@ void
_clutter_stage_process_queued_events (ClutterStage *stage) _clutter_stage_process_queued_events (ClutterStage *stage)
{ {
ClutterStagePrivate *priv; ClutterStagePrivate *priv;
GList *events, *l;; GList *events, *l;
g_return_if_fail (CLUTTER_IS_STAGE (stage)); g_return_if_fail (CLUTTER_IS_STAGE (stage));
@ -506,7 +526,7 @@ _clutter_stage_process_queued_events (ClutterStage *stage)
priv->event_queue->tail = NULL; priv->event_queue->tail = NULL;
priv->event_queue->length = 0; priv->event_queue->length = 0;
for (l = events; l; l = l->next) for (l = events; l != NULL; l = l->next)
{ {
ClutterEvent *event; ClutterEvent *event;
ClutterEvent *next_event; ClutterEvent *next_event;
@ -694,6 +714,10 @@ clutter_stage_set_property (GObject *object,
clutter_stage_set_use_alpha (stage, g_value_get_boolean (value)); clutter_stage_set_use_alpha (stage, g_value_get_boolean (value));
break; break;
case PROP_KEY_FOCUS:
clutter_stage_set_key_focus (stage, g_value_get_object (value));
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -750,6 +774,10 @@ clutter_stage_get_property (GObject *gobject,
g_value_set_boolean (value, priv->use_alpha); g_value_set_boolean (value, priv->use_alpha);
break; break;
case PROP_KEY_FOCUS:
g_value_set_object (value, priv->key_focused_actor);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break; break;
@ -994,6 +1022,23 @@ clutter_stage_class_init (ClutterStageClass *klass)
CLUTTER_PARAM_READWRITE); CLUTTER_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_USE_ALPHA, pspec); g_object_class_install_property (gobject_class, PROP_USE_ALPHA, pspec);
/**
* ClutterStage:key-focus:
*
* The #ClutterActor that will receive key events from the underlying
* windowing system.
*
* If %NULL, the #ClutterStage will receive the events.
*
* Since: 1.2
*/
pspec = g_param_spec_object ("key-focus",
"Key Focus",
"The currently key focused actor",
CLUTTER_TYPE_ACTOR,
CLUTTER_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_KEY_FOCUS, pspec);
/** /**
* ClutterStage::fullscreen * ClutterStage::fullscreen
* @stage: the stage which was fullscreened * @stage: the stage which was fullscreened
@ -1749,6 +1794,8 @@ clutter_stage_set_key_focus (ClutterStage *stage,
} }
else else
g_signal_emit_by_name (stage, "key-focus-in"); g_signal_emit_by_name (stage, "key-focus-in");
g_object_notify (G_OBJECT (stage), "key-focus");
} }
/** /**

View File

@ -260,6 +260,7 @@ clutter_text_clear_selection (ClutterText *self)
if (priv->selection_bound != priv->position) if (priv->selection_bound != priv->position)
{ {
priv->selection_bound = priv->position; priv->selection_bound = priv->position;
g_object_notify (G_OBJECT (self), "selection-bound");
clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
} }
} }
@ -803,6 +804,7 @@ clutter_text_delete_selection (ClutterText *self)
priv->position = start_index; priv->position = start_index;
priv->selection_bound = start_index; priv->selection_bound = start_index;
/* Not required to be guarded by g_object_freeze/thaw_notify */
if (priv->position != old_position) if (priv->position != old_position)
g_object_notify (G_OBJECT (self), "position"); g_object_notify (G_OBJECT (self), "position");
@ -812,6 +814,21 @@ clutter_text_delete_selection (ClutterText *self)
return TRUE; return TRUE;
} }
/*
* Utility function to update both cursor position and selection bound
* at once
*/
static inline void
clutter_text_set_positions (ClutterText *self,
gint new_pos,
gint new_bound)
{
g_object_freeze_notify (G_OBJECT (self));
clutter_text_set_cursor_position (self, new_pos);
clutter_text_set_selection_bound (self, new_bound);
g_object_thaw_notify (G_OBJECT (self));
}
static inline void static inline void
clutter_text_set_text_internal (ClutterText *self, clutter_text_set_text_internal (ClutterText *self,
const gchar *text) const gchar *text)
@ -857,8 +874,7 @@ clutter_text_set_text_internal (ClutterText *self,
if (priv->n_bytes == 0) if (priv->n_bytes == 0)
{ {
clutter_text_set_cursor_position (self, -1); clutter_text_set_positions (self, -1, -1);
clutter_text_set_selection_bound (self, -1);
} }
clutter_text_dirty_cache (self); clutter_text_dirty_cache (self);
@ -1493,8 +1509,7 @@ clutter_text_button_press (ClutterActor *actor,
*/ */
if (priv->text == NULL || priv->text[0] == '\0') if (priv->text == NULL || priv->text[0] == '\0')
{ {
clutter_text_set_cursor_position (self, -1); clutter_text_set_positions (self, -1, -1);
clutter_text_set_selection_bound (self, -1);
return TRUE; return TRUE;
} }
@ -1519,8 +1534,7 @@ clutter_text_button_press (ClutterActor *actor,
*/ */
if (event->click_count == 1) if (event->click_count == 1)
{ {
clutter_text_set_cursor_position (self, offset); clutter_text_set_positions (self, offset, offset);
clutter_text_set_selection_bound (self, offset);
} }
else if (event->click_count == 2) else if (event->click_count == 2)
{ {
@ -1565,8 +1579,7 @@ clutter_text_motion (ClutterActor *actor,
clutter_text_set_cursor_position (self, offset); clutter_text_set_cursor_position (self, offset);
else else
{ {
clutter_text_set_cursor_position (self, offset); clutter_text_set_positions (self, offset, offset);
clutter_text_set_selection_bound (self, offset);
} }
return TRUE; return TRUE;
@ -1896,6 +1909,8 @@ clutter_text_real_move_left (ClutterText *self,
len = priv->n_chars; len = priv->n_chars;
g_object_freeze_notify (G_OBJECT (self));
if (pos != 0 && len != 0) if (pos != 0 && len != 0)
{ {
if (modifiers & CLUTTER_CONTROL_MASK) if (modifiers & CLUTTER_CONTROL_MASK)
@ -1919,6 +1934,8 @@ clutter_text_real_move_left (ClutterText *self,
if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK))) if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK)))
clutter_text_clear_selection (self); clutter_text_clear_selection (self);
g_object_thaw_notify (G_OBJECT (self));
return TRUE; return TRUE;
} }
@ -1935,6 +1952,8 @@ clutter_text_real_move_right (ClutterText *self,
len = priv->n_chars; len = priv->n_chars;
g_object_freeze_notify (G_OBJECT (self));
if (pos != -1 && len !=0) if (pos != -1 && len !=0)
{ {
if (modifiers & CLUTTER_CONTROL_MASK) if (modifiers & CLUTTER_CONTROL_MASK)
@ -1954,6 +1973,8 @@ clutter_text_real_move_right (ClutterText *self,
if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK))) if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK)))
clutter_text_clear_selection (self); clutter_text_clear_selection (self);
g_object_thaw_notify (G_OBJECT (self));
return TRUE; return TRUE;
} }
@ -1970,6 +1991,8 @@ clutter_text_real_move_up (ClutterText *self,
gint index_, trailing; gint index_, trailing;
gint x; gint x;
g_object_freeze_notify (G_OBJECT (self));
layout = clutter_text_get_layout (self); layout = clutter_text_get_layout (self);
if (priv->position == 0) if (priv->position == 0)
@ -2007,6 +2030,8 @@ clutter_text_real_move_up (ClutterText *self,
if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK))) if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK)))
clutter_text_clear_selection (self); clutter_text_clear_selection (self);
g_object_thaw_notify (G_OBJECT (self));
return TRUE; return TRUE;
} }
@ -2023,6 +2048,8 @@ clutter_text_real_move_down (ClutterText *self,
gint index_, trailing; gint index_, trailing;
gint x; gint x;
g_object_freeze_notify (G_OBJECT (self));
layout = clutter_text_get_layout (self); layout = clutter_text_get_layout (self);
if (priv->position == 0) if (priv->position == 0)
@ -2056,6 +2083,8 @@ clutter_text_real_move_down (ClutterText *self,
if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK))) if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK)))
clutter_text_clear_selection (self); clutter_text_clear_selection (self);
g_object_thaw_notify (G_OBJECT (self));
return TRUE; return TRUE;
} }
@ -2068,12 +2097,16 @@ clutter_text_real_line_start (ClutterText *self,
ClutterTextPrivate *priv = self->priv; ClutterTextPrivate *priv = self->priv;
gint position; gint position;
g_object_freeze_notify (G_OBJECT (self));
position = clutter_text_move_line_start (self, priv->position); position = clutter_text_move_line_start (self, priv->position);
clutter_text_set_cursor_position (self, position); clutter_text_set_cursor_position (self, position);
if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK))) if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK)))
clutter_text_clear_selection (self); clutter_text_clear_selection (self);
g_object_thaw_notify (G_OBJECT (self));
return TRUE; return TRUE;
} }
@ -2086,12 +2119,16 @@ clutter_text_real_line_end (ClutterText *self,
ClutterTextPrivate *priv = self->priv; ClutterTextPrivate *priv = self->priv;
gint position; gint position;
g_object_freeze_notify (G_OBJECT (self));
position = clutter_text_move_line_end (self, priv->position); position = clutter_text_move_line_end (self, priv->position);
clutter_text_set_cursor_position (self, position); clutter_text_set_cursor_position (self, position);
if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK))) if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK)))
clutter_text_clear_selection (self); clutter_text_clear_selection (self);
g_object_thaw_notify (G_OBJECT (self));
return TRUE; return TRUE;
} }
@ -2101,8 +2138,7 @@ clutter_text_real_select_all (ClutterText *self,
guint keyval, guint keyval,
ClutterModifierType modifiers) ClutterModifierType modifiers)
{ {
clutter_text_set_cursor_position (self, 0); clutter_text_set_positions (self, 0, self->priv->n_chars);
clutter_text_set_selection_bound (self, self->priv->n_chars);
return TRUE; return TRUE;
} }
@ -2151,15 +2187,13 @@ clutter_text_real_del_prev (ClutterText *self,
{ {
clutter_text_delete_text (self, len - 1, len); clutter_text_delete_text (self, len - 1, len);
clutter_text_set_cursor_position (self, -1); clutter_text_set_positions (self, -1, -1);
clutter_text_set_selection_bound (self, -1);
} }
else else
{ {
clutter_text_delete_text (self, pos - 1, pos); clutter_text_delete_text (self, pos - 1, pos);
clutter_text_set_cursor_position (self, pos - 1); clutter_text_set_positions (self, pos - 1, pos - 1);
clutter_text_set_selection_bound (self, pos - 1);
} }
} }
@ -3260,12 +3294,7 @@ clutter_text_set_selection (ClutterText *self,
start_pos = MIN (priv->n_chars, start_pos); start_pos = MIN (priv->n_chars, start_pos);
end_pos = MIN (priv->n_chars, end_pos); end_pos = MIN (priv->n_chars, end_pos);
g_object_freeze_notify (G_OBJECT (self)); clutter_text_set_positions (self, start_pos, end_pos);
clutter_text_set_cursor_position (self, start_pos);
clutter_text_set_selection_bound (self, end_pos);
g_object_thaw_notify (G_OBJECT (self));
} }
/** /**
@ -4349,8 +4378,7 @@ clutter_text_insert_unichar (ClutterText *self,
if (priv->position >= 0) if (priv->position >= 0)
{ {
clutter_text_set_cursor_position (self, priv->position + 1); clutter_text_set_positions (self, priv->position + 1, priv->position);
clutter_text_set_selection_bound (self, priv->position);
} }
g_string_free (new, TRUE); g_string_free (new, TRUE);
@ -4397,8 +4425,9 @@ clutter_text_insert_text (ClutterText *self,
if (position >= 0 && priv->position >= position) if (position >= 0 && priv->position >= position)
{ {
clutter_text_set_cursor_position (self, priv->position + g_utf8_strlen (text, -1)); clutter_text_set_positions (self,
clutter_text_set_selection_bound (self, priv->position); priv->position + g_utf8_strlen (text, -1),
priv->position);
} }
g_string_free (new, TRUE); g_string_free (new, TRUE);

View File

@ -32,7 +32,7 @@
void void
_clutter_timeout_interval_init (ClutterTimeoutInterval *interval, _clutter_timeout_interval_init (ClutterTimeoutInterval *interval,
guint fps) guint fps)
{ {
g_get_current_time (&interval->start_time); g_get_current_time (&interval->start_time);
interval->fps = fps; interval->fps = fps;
@ -40,21 +40,24 @@ _clutter_timeout_interval_init (ClutterTimeoutInterval *interval,
} }
static guint static guint
_clutter_timeout_interval_get_ticks (const GTimeVal *current_time, _clutter_timeout_interval_get_ticks (const GTimeVal *current_time,
ClutterTimeoutInterval *interval) ClutterTimeoutInterval *interval)
{ {
return ((current_time->tv_sec - interval->start_time.tv_sec) * 1000 return ((current_time->tv_sec - interval->start_time.tv_sec) * 1000
+ (current_time->tv_usec - interval->start_time.tv_usec) / 1000); + (current_time->tv_usec - interval->start_time.tv_usec) / 1000);
} }
gboolean gboolean
_clutter_timeout_interval_prepare (const GTimeVal *current_time, _clutter_timeout_interval_prepare (const GTimeVal *current_time,
ClutterTimeoutInterval *interval, ClutterTimeoutInterval *interval,
gint *delay) gint *delay)
{ {
guint elapsed_time guint elapsed_time, new_frame_num;
= _clutter_timeout_interval_get_ticks (current_time, interval);
guint new_frame_num = elapsed_time * interval->fps / 1000; elapsed_time = _clutter_timeout_interval_get_ticks (current_time,
interval);
new_frame_num = elapsed_time * interval->fps
/ 1000;
/* If time has gone backwards or the time since the last frame is /* If time has gone backwards or the time since the last frame is
greater than the two frames worth then reset the time and do a greater than the two frames worth then reset the time and do a
@ -67,6 +70,7 @@ _clutter_timeout_interval_prepare (const GTimeVal *current_time,
/* Reset the start time */ /* Reset the start time */
interval->start_time = *current_time; interval->start_time = *current_time;
/* Move the start time as if one whole frame has elapsed */ /* Move the start time as if one whole frame has elapsed */
g_time_val_add (&interval->start_time, -(gint) frame_time * 1000); g_time_val_add (&interval->start_time, -(gint) frame_time * 1000);
@ -74,19 +78,22 @@ _clutter_timeout_interval_prepare (const GTimeVal *current_time,
if (delay) if (delay)
*delay = 0; *delay = 0;
return TRUE; return TRUE;
} }
else if (new_frame_num > interval->frame_count) else if (new_frame_num > interval->frame_count)
{ {
if (delay) if (delay)
*delay = 0; *delay = 0;
return TRUE; return TRUE;
} }
else else
{ {
if (delay) if (delay)
*delay = ((interval->frame_count + 1) * 1000 / interval->fps *delay = ((interval->frame_count + 1) * 1000 / interval->fps
- elapsed_time); - elapsed_time);
return FALSE; return FALSE;
} }
} }
@ -99,10 +106,11 @@ _clutter_timeout_interval_dispatch (ClutterTimeoutInterval *interval,
if ((* callback) (user_data)) if ((* callback) (user_data))
{ {
interval->frame_count++; interval->frame_count++;
return TRUE; return TRUE;
} }
else
return FALSE; return FALSE;
} }
gint gint
@ -115,12 +123,12 @@ _clutter_timeout_interval_compare_expiration (const ClutterTimeoutInterval *a,
gint comparison; gint comparison;
b_difference = ((a->start_time.tv_sec - b->start_time.tv_sec) * 1000 b_difference = ((a->start_time.tv_sec - b->start_time.tv_sec) * 1000
+ (a->start_time.tv_usec - b->start_time.tv_usec) / 1000); + (a->start_time.tv_usec - b->start_time.tv_usec) / 1000);
comparison = ((gint) ((a->frame_count + 1) * a_delay) comparison = ((gint) ((a->frame_count + 1) * a_delay)
- (gint) ((b->frame_count + 1) * b_delay + b_difference)); - (gint) ((b->frame_count + 1) * b_delay + b_difference));
return (comparison < 0 ? -1 return (comparison < 0 ? -1
: comparison > 0 ? 1 : comparison > 0 ? 1
: 0); : 0);
} }

View File

@ -359,12 +359,6 @@ clutter_timeout_pool_finalize (GSource *source)
* always sorted, so that the extraction of the next timeout function is * always sorted, so that the extraction of the next timeout function is
* a constant time operation. * a constant time operation.
* *
* Inside Clutter, every #ClutterTimeline share the same timeout pool, unless
* the CLUTTER_TIMELINE=no-pool environment variable is set.
*
* #ClutterTimeoutPool is part of the #ClutterTimeline implementation
* and should not be used by application developers.
*
* Return value: the newly created #ClutterTimeoutPool. The created pool * Return value: the newly created #ClutterTimeoutPool. The created pool
* is owned by the GLib default context and will be automatically * is owned by the GLib default context and will be automatically
* destroyed when the context is destroyed. It is possible to force * destroyed when the context is destroyed. It is possible to force
@ -412,7 +406,7 @@ clutter_timeout_pool_new (gint priority)
* won't be called again. If @notify is not %NULL, the @notify function * won't be called again. If @notify is not %NULL, the @notify function
* will be called. The first call to @func will be at the end of @interval. * will be called. The first call to @func will be at the end of @interval.
* *
* Since version 0.8 this will try to compensate for delays. For * Since Clutter 0.8 this will try to compensate for delays. For
* example, if @func takes half the interval time to execute then the * example, if @func takes half the interval time to execute then the
* function will be called again half the interval time after it * function will be called again half the interval time after it
* finished. Before version 0.8 it would not fire until a full * finished. Before version 0.8 it would not fire until a full
@ -478,7 +472,8 @@ clutter_timeout_pool_remove (ClutterTimeoutPool *pool,
clutter_timeout_find_by_id))) clutter_timeout_find_by_id)))
{ {
clutter_timeout_unref (l->data); clutter_timeout_unref (l->data);
pool->dispatched_timeouts
= g_list_delete_link (pool->dispatched_timeouts, l); pool->dispatched_timeouts =
g_list_delete_link (pool->dispatched_timeouts, l);
} }
} }

View File

@ -51,12 +51,14 @@
#include "clutter-clone.h" #include "clutter-clone.h"
#include "clutter-color.h" #include "clutter-color.h"
#include "clutter-container.h" #include "clutter-container.h"
#include "clutter-device-manager.h"
#include "clutter-event.h" #include "clutter-event.h"
#include "clutter-feature.h" #include "clutter-feature.h"
#include "clutter-fixed-layout.h" #include "clutter-fixed-layout.h"
#include "clutter-flow-layout.h" #include "clutter-flow-layout.h"
#include "clutter-frame-source.h" #include "clutter-frame-source.h"
#include "clutter-group.h" #include "clutter-group.h"
#include "clutter-input-device.h"
#include "clutter-interval.h" #include "clutter-interval.h"
#include "clutter-keysyms.h" #include "clutter-keysyms.h"
#include "clutter-layout-manager.h" #include "clutter-layout-manager.h"

View File

@ -115,6 +115,12 @@ typedef struct
CoglAtlas *atlas; CoglAtlas *atlas;
CoglHandle atlas_texture; CoglHandle atlas_texture;
/* This debugging variable is used to pick a colour for visually
displaying the quad batches. It needs to be global so that it can
be reset by cogl_clear. It needs to be reset to increase the
chances of getting the same colour during an animation */
guint8 journal_rectangles_color;
CoglContextDriver drv; CoglContextDriver drv;
} CoglContext; } CoglContext;

View File

@ -101,6 +101,8 @@ CoglTextureUnit *
_cogl_get_texture_unit (int index_); _cogl_get_texture_unit (int index_);
void void
_cogl_destroy_texture_units (void); _cogl_destroy_texture_units (void);
guint
_cogl_get_max_texture_image_units (void);
void void
_cogl_flush_face_winding (void); _cogl_flush_face_winding (void);

View File

@ -225,30 +225,51 @@ _cogl_journal_flush_modelview_and_entries (CoglJournalEntry *batch_start,
} }
#endif #endif
/* DEBUGGING CODE XXX: /* DEBUGGING CODE XXX: This path will cause all rectangles to be
* This path will cause all rectangles to be drawn with a red, green * drawn with a coloured outline. Each batch will be rendered with
* or blue outline with no blending. This may e.g. help with debugging * the same color. This may e.g. help with debugging texture slicing
* texture slicing issues or blending issues, plus it looks quite cool. * issues, visually seeing what is batched and debugging blending
* issues, plus it looks quite cool.
*/ */
if (cogl_debug_flags & COGL_DEBUG_RECTANGLES) if (cogl_debug_flags & COGL_DEBUG_RECTANGLES)
{ {
static CoglHandle outline = COGL_INVALID_HANDLE; static CoglHandle outline = COGL_INVALID_HANDLE;
static int color = 0; guint8 color_intensity;
int i; int i;
_COGL_GET_CONTEXT (ctxt, NO_RETVAL);
if (outline == COGL_INVALID_HANDLE) if (outline == COGL_INVALID_HANDLE)
outline = cogl_material_new (); outline = cogl_material_new ();
/* The least significant three bits represent the three
components so that the order of colours goes red, green,
yellow, blue, magenta, cyan. Black and white are skipped. The
next two bits give four scales of intensity for those colours
in the order 0xff, 0xcc, 0x99, and 0x66. This gives a total
of 24 colours. If there are more than 24 batches on the stage
then it will wrap around */
color_intensity = 0xff - 0x33 * (ctxt->journal_rectangles_color >> 3);
cogl_material_set_color4ub (outline,
(ctxt->journal_rectangles_color & 1) ?
color_intensity : 0,
(ctxt->journal_rectangles_color & 2) ?
color_intensity : 0,
(ctxt->journal_rectangles_color & 4) ?
color_intensity : 0,
0xff);
_cogl_material_flush_gl_state (outline, NULL);
cogl_enable (COGL_ENABLE_VERTEX_ARRAY); cogl_enable (COGL_ENABLE_VERTEX_ARRAY);
for (i = 0; i < batch_len; i++, color = (color + 1) % 3) for (i = 0; i < batch_len; i++)
{ GE( glDrawArrays (GL_LINE_LOOP, 4 * i + state->vertex_offset, 4) );
cogl_material_set_color4ub (outline,
color == 0 ? 0xff : 0x00, /* Go to the next color */
color == 1 ? 0xff : 0x00, do
color == 2 ? 0xff : 0x00, ctxt->journal_rectangles_color = ((ctxt->journal_rectangles_color + 1) &
0xff); ((1 << 5) - 1));
_cogl_material_flush_gl_state (outline, NULL); /* We don't want to use black or white */
GE( glDrawArrays (GL_LINE_LOOP, 4 * i, 4) ); while ((ctxt->journal_rectangles_color & 0x07) == 0
} || (ctxt->journal_rectangles_color & 0x07) == 0x07);
} }
state->vertex_offset += (4 * batch_len); state->vertex_offset += (4 * batch_len);
@ -779,8 +800,7 @@ _cogl_journal_log_quad (float x_1,
if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM)) if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM))
cogl_get_modelview_matrix (&entry->model_view); cogl_get_modelview_matrix (&entry->model_view);
if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_BATCHING if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_BATCHING))
|| cogl_debug_flags & COGL_DEBUG_RECTANGLES))
_cogl_journal_flush (); _cogl_journal_flush ();
COGL_TIMER_STOP (_cogl_uprof_context, log_timer); COGL_TIMER_STOP (_cogl_uprof_context, log_timer);

View File

@ -48,12 +48,6 @@
#include "../gles/cogl-gles2-wrapper.h" #include "../gles/cogl-gles2-wrapper.h"
#endif #endif
#ifdef HAVE_COGL_GLES
#define COGL_MATERIAL_MAX_TEXTURE_UNITS GL_MAX_TEXTURE_UNITS
#else
#define COGL_MATERIAL_MAX_TEXTURE_UNITS GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS
#endif
#ifdef HAVE_COGL_GL #ifdef HAVE_COGL_GL
#define glActiveTexture ctx->drv.pf_glActiveTexture #define glActiveTexture ctx->drv.pf_glActiveTexture
#define glClientActiveTexture ctx->drv.pf_glClientActiveTexture #define glClientActiveTexture ctx->drv.pf_glClientActiveTexture
@ -828,7 +822,7 @@ cogl_material_set_layer (CoglHandle material_handle,
_cogl_material_pre_change_notify (material, FALSE, NULL); _cogl_material_pre_change_notify (material, FALSE, NULL);
material->n_layers = g_list_length (material->layers); material->n_layers = g_list_length (material->layers);
if (material->n_layers >= COGL_MATERIAL_MAX_TEXTURE_UNITS) if (material->n_layers >= _cogl_get_max_texture_image_units ())
{ {
if (!(material->flags & COGL_MATERIAL_FLAG_SHOWN_SAMPLER_WARNING)) if (!(material->flags & COGL_MATERIAL_FLAG_SHOWN_SAMPLER_WARNING))
{ {
@ -1551,7 +1545,7 @@ _cogl_material_flush_layers_gl_state (CoglMaterial *material,
layer->flags &= ~COGL_MATERIAL_LAYER_FLAG_DIRTY; layer->flags &= ~COGL_MATERIAL_LAYER_FLAG_DIRTY;
if ((i+1) >= COGL_MATERIAL_MAX_TEXTURE_UNITS) if ((i+1) >= _cogl_get_max_texture_image_units ())
break; break;
} }

View File

@ -120,8 +120,8 @@ typedef struct _CoglTextureVertex CoglTextureVertex;
* @COGL_PIXEL_FORMAT_RGB_565: RGB, 16 bits * @COGL_PIXEL_FORMAT_RGB_565: RGB, 16 bits
* @COGL_PIXEL_FORMAT_RGBA_4444: RGBA, 16 bits * @COGL_PIXEL_FORMAT_RGBA_4444: RGBA, 16 bits
* @COGL_PIXEL_FORMAT_RGBA_5551: RGBA, 16 bits * @COGL_PIXEL_FORMAT_RGBA_5551: RGBA, 16 bits
* @COGL_PIXEL_FORMAT_YUV: FIXME * @COGL_PIXEL_FORMAT_YUV: Not currently supported
* @COGL_PIXEL_FORMAT_G_8: FIXME * @COGL_PIXEL_FORMAT_G_8: Single luminance component
* @COGL_PIXEL_FORMAT_RGB_888: RGB, 24 bits * @COGL_PIXEL_FORMAT_RGB_888: RGB, 24 bits
* @COGL_PIXEL_FORMAT_BGR_888: BGR, 24 bits * @COGL_PIXEL_FORMAT_BGR_888: BGR, 24 bits
* @COGL_PIXEL_FORMAT_RGBA_8888: RGBA, 32 bits * @COGL_PIXEL_FORMAT_RGBA_8888: RGBA, 32 bits
@ -135,7 +135,23 @@ typedef struct _CoglTextureVertex CoglTextureVertex;
* @COGL_PIXEL_FORMAT_RGBA_4444_PRE: Premultiplied RGBA, 16 bits * @COGL_PIXEL_FORMAT_RGBA_4444_PRE: Premultiplied RGBA, 16 bits
* @COGL_PIXEL_FORMAT_RGBA_5551_PRE: Premultiplied RGBA, 16 bits * @COGL_PIXEL_FORMAT_RGBA_5551_PRE: Premultiplied RGBA, 16 bits
* *
* Pixel formats used by COGL. * Pixel formats used by COGL. For the formats with a byte per
* component, the order of the components specify the order in
* increasing memory addresses. So for example
* %COGL_PIXEL_FORMAT_RGB_888 would have the red component in the
* lowest address, green in the next address and blue after that
* regardless of the endinanness of the system.
*
* For the 16-bit formats the component order specifies the order
* within a 16-bit number from most significant bit to least
* significant. So for %COGL_PIXEL_FORMAT_RGB_565, the red component
* would be in bits 11-15, the green component would be in 6-11 and
* the blue component would be in 1-5. Therefore the order in memory
* depends on the endianness of the system.
*
* When uploading a texture %COGL_PIXEL_FORMAT_ANY can be used as the
* internal format. Cogl will try to pick the best format to use
* internally and convert the texture data if necessary.
* *
* Since: 0.8 * Since: 0.8
*/ */

View File

@ -51,9 +51,9 @@
int int
cogl_util_next_p2 (int a) cogl_util_next_p2 (int a)
{ {
int rval=1; int rval = 1;
while(rval < a) while (rval < a)
rval <<= 1; rval <<= 1;
return rval; return rval;
@ -237,8 +237,8 @@ cogl_fixed_get_type (void)
g_value_register_transform_func (_cogl_fixed_type, G_TYPE_FLOAT, g_value_register_transform_func (_cogl_fixed_type, G_TYPE_FLOAT,
cogl_value_transform_fixed_float); cogl_value_transform_fixed_float);
g_value_register_transform_func (G_TYPE_FLOAT, _cogl_fixed_type, g_value_register_transform_func (G_TYPE_FLOAT, _cogl_fixed_type,
cogl_value_transform_float_fixed); cogl_value_transform_float_fixed);
g_value_register_transform_func (_cogl_fixed_type, G_TYPE_DOUBLE, g_value_register_transform_func (_cogl_fixed_type, G_TYPE_DOUBLE,
cogl_value_transform_fixed_double); cogl_value_transform_fixed_double);
g_value_register_transform_func (G_TYPE_DOUBLE, _cogl_fixed_type, g_value_register_transform_func (G_TYPE_DOUBLE, _cogl_fixed_type,

View File

@ -1512,7 +1512,7 @@ enable_state_for_drawing_buffer (CoglVertexBuffer *buffer)
GLuint generic_index = 0; GLuint generic_index = 0;
#endif #endif
gulong enable_flags = 0; gulong enable_flags = 0;
guint max_texcoord_attrib_unit = 0; guint max_texcoord_attrib_unit = -1;
const GList *layers; const GList *layers;
guint32 fallback_layers = 0; guint32 fallback_layers = 0;
guint32 disable_layers = ~0; guint32 disable_layers = ~0;

View File

@ -40,6 +40,7 @@
#include "cogl-winsys.h" #include "cogl-winsys.h"
#include "cogl-framebuffer-private.h" #include "cogl-framebuffer-private.h"
#include "cogl-matrix-private.h" #include "cogl-matrix-private.h"
#include "cogl-journal-private.h"
#if defined (HAVE_COGL_GLES2) || defined (HAVE_COGL_GLES) #if defined (HAVE_COGL_GLES2) || defined (HAVE_COGL_GLES)
#include "cogl-gles2-wrapper.h" #include "cogl-gles2-wrapper.h"
@ -155,6 +156,16 @@ cogl_clear (const CoglColor *color, gulong buffers)
glClear (gl_buffers); glClear (gl_buffers);
/* This is a debugging variable used to visually display the quad
batches from the journal. It is reset here to increase the
chances of getting the same colours for each frame during an
animation */
if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_RECTANGLES))
{
_COGL_GET_CONTEXT (ctxt, NO_RETVAL);
ctxt->journal_rectangles_color = 1;
}
COGL_NOTE (DRAW, "Clear end"); COGL_NOTE (DRAW, "Clear end");
} }
@ -729,10 +740,11 @@ cogl_end_gl (void)
} }
static CoglTextureUnit * static CoglTextureUnit *
_cogl_texture_unit_new (void) _cogl_texture_unit_new (int index_)
{ {
CoglTextureUnit *unit = g_new0 (CoglTextureUnit, 1); CoglTextureUnit *unit = g_new0 (CoglTextureUnit, 1);
unit->matrix_stack = _cogl_matrix_stack_new (); unit->matrix_stack = _cogl_matrix_stack_new ();
unit->index = index_;
return unit; return unit;
} }
@ -766,7 +778,7 @@ _cogl_get_texture_unit (int index_)
/* NB: if we now insert a new layer before l, that will maintain order. /* NB: if we now insert a new layer before l, that will maintain order.
*/ */
unit = _cogl_texture_unit_new (); unit = _cogl_texture_unit_new (index_);
/* Note: see comment after for() loop above */ /* Note: see comment after for() loop above */
ctx->texture_units = ctx->texture_units =
@ -787,6 +799,36 @@ _cogl_destroy_texture_units (void)
g_list_free (ctx->texture_units); g_list_free (ctx->texture_units);
} }
/*
* This is more complicated than that, another pass needs to be done when
* cogl have a neat way of saying if we are using the fixed function pipeline
* or not (for the GL case):
* MAX_TEXTURE_UNITS: fixed function pipeline, a texture unit has both a
* sampler and a set of texture coordinates
* MAX_TEXTURE_IMAGE_UNITS: number of samplers one can use from a fragment
* program/shader (ARBfp1.0 asm/GLSL)
* MAX_VERTEX_TEXTURE_UNITS: number of samplers one can use from a vertex
* program/shader (can be 0)
* MAX_COMBINED_TEXTURE_IMAGE_UNITS: Maximum samplers one can use, counting both
* the vertex and fragment shaders
*
* If both the vertex shader and the fragment processing stage access the same
* texture image unit, then that counts as using two texture image units
* against the latter limit: http://www.opengl.org/sdk/docs/man/xhtml/glGet.xml
*
* Note that, for now, we use GL_MAX_TEXTURE_UNITS as we are exposing the
* fixed function pipeline.
*/
guint
_cogl_get_max_texture_image_units (void)
{
GLint nb_texture_image_units;
GE( glGetIntegerv(GL_MAX_TEXTURE_UNITS, &nb_texture_image_units) );
return nb_texture_image_units;
}
void void
cogl_push_matrix (void) cogl_push_matrix (void)
{ {

View File

@ -33,4 +33,4 @@ libclutter_win32_la_SOURCES = \
CLEANFILES = clutter-win32-$(CLUTTER_API_VERSION).pc CLEANFILES = clutter-win32-$(CLUTTER_API_VERSION).pc
EXTRA_DIST = clutter-win32.pc.in EXTRA_DIST = clutter-win32.pc.in resources.rc invisible-cursor.cur

View File

@ -31,6 +31,7 @@
#include "../clutter-event.h" #include "../clutter-event.h"
#include "../clutter-main.h" #include "../clutter-main.h"
#include "../clutter-input-device.h"
#include "../clutter-debug.h" #include "../clutter-debug.h"
#include "../clutter-private.h" #include "../clutter-private.h"
#include "../clutter-version.h" #include "../clutter-version.h"
@ -47,6 +48,8 @@ static ClutterBackendWin32 *backend_singleton = NULL;
static gchar *clutter_vblank_name = NULL; static gchar *clutter_vblank_name = NULL;
static HINSTANCE clutter_hinst = NULL;
gboolean gboolean
clutter_backend_win32_pre_parse (ClutterBackend *backend, clutter_backend_win32_pre_parse (ClutterBackend *backend,
GError **error) GError **error)
@ -62,11 +65,45 @@ clutter_backend_win32_pre_parse (ClutterBackend *backend,
static void static void
clutter_backend_win32_init_events (ClutterBackend *backend) clutter_backend_win32_init_events (ClutterBackend *backend)
{ {
ClutterBackendWin32 *backend_win32 = CLUTTER_BACKEND_WIN32 (backend);
ClutterDeviceManager *manager;
ClutterInputDevice *device;
CLUTTER_NOTE (EVENT, "initialising the event loop"); CLUTTER_NOTE (EVENT, "initialising the event loop");
manager = clutter_device_manager_get_default ();
device = g_object_new (CLUTTER_TYPE_INPUT_DEVICE,
"id", 0,
"name", "Core Pointer",
"device-type", CLUTTER_POINTER_DEVICE,
NULL);
_clutter_device_manager_add_device (manager, device);
backend_win32->core_pointer = device;
device = g_object_new (CLUTTER_TYPE_INPUT_DEVICE,
"id", 1,
"name", "Core Keyboard",
"device-type", CLUTTER_KEYBOARD_DEVICE,
NULL);
_clutter_device_manager_add_device (manager, device);
backend_win32->core_keyboard = device;
_clutter_backend_win32_events_init (backend); _clutter_backend_win32_events_init (backend);
} }
HCURSOR
_clutter_backend_win32_get_invisible_cursor (ClutterBackend *backend)
{
ClutterBackendWin32 *backend_win32 = CLUTTER_BACKEND_WIN32 (backend);
if (backend_win32->invisible_cursor == NULL)
backend_win32->invisible_cursor =
LoadCursor (clutter_hinst, MAKEINTRESOURCE (42));
return backend_win32->invisible_cursor;
}
static const GOptionEntry entries[] = static const GOptionEntry entries[] =
{ {
{ {
@ -353,6 +390,7 @@ clutter_backend_win32_init (ClutterBackendWin32 *backend_win32)
backend_win32->gl_context = NULL; backend_win32->gl_context = NULL;
backend_win32->no_event_retrieval = FALSE; backend_win32->no_event_retrieval = FALSE;
backend_win32->invisible_cursor = NULL;
/* FIXME: get from GetSystemMetric? */ /* FIXME: get from GetSystemMetric? */
clutter_backend_set_double_click_time (backend, 250); clutter_backend_set_double_click_time (backend, 250);
@ -370,3 +408,13 @@ _clutter_backend_impl_get_type (void)
{ {
return clutter_backend_win32_get_type (); return clutter_backend_win32_get_type ();
} }
BOOL WINAPI
DllMain (HINSTANCE hinst, DWORD reason, LPVOID reserved)
{
if (reason == DLL_PROCESS_ATTACH)
/* Store the module handle so that we can use it to load resources */
clutter_hinst = hinst;
return TRUE;
}

View File

@ -48,7 +48,12 @@ struct _ClutterBackendWin32
HGLRC gl_context; HGLRC gl_context;
gboolean no_event_retrieval; gboolean no_event_retrieval;
HCURSOR invisible_cursor;
GSource *event_source; GSource *event_source;
ClutterInputDevice *core_pointer;
ClutterInputDevice *core_keyboard;
}; };
struct _ClutterBackendWin32Class struct _ClutterBackendWin32Class
@ -68,6 +73,8 @@ clutter_backend_win32_add_options (ClutterBackend *backend,
ClutterFeatureFlags ClutterFeatureFlags
clutter_backend_win32_get_features (ClutterBackend *backend); clutter_backend_win32_get_features (ClutterBackend *backend);
HCURSOR _clutter_backend_win32_get_invisible_cursor (ClutterBackend *backend);
G_END_DECLS G_END_DECLS
#endif /* __CLUTTER_BACKEND_WIN32_H__ */ #endif /* __CLUTTER_BACKEND_WIN32_H__ */

View File

@ -201,9 +201,13 @@ get_modifier_state (WPARAM wparam)
return ret; return ret;
} }
static void static inline void
make_button_event (const MSG *msg, ClutterEvent *event, make_button_event (const MSG *msg,
int button, int click_count, gboolean release) ClutterEvent *event,
int button,
int click_count,
gboolean release,
ClutterInputDevice *device)
{ {
event->type = release ? CLUTTER_BUTTON_RELEASE : CLUTTER_BUTTON_PRESS; event->type = release ? CLUTTER_BUTTON_RELEASE : CLUTTER_BUTTON_PRESS;
event->button.time = msg->time; event->button.time = msg->time;
@ -212,6 +216,7 @@ make_button_event (const MSG *msg, ClutterEvent *event,
event->button.modifier_state = get_modifier_state (msg->wParam); event->button.modifier_state = get_modifier_state (msg->wParam);
event->button.button = button; event->button.button = button;
event->button.click_count = click_count; event->button.click_count = click_count;
event->button.device = device;
} }
/** /**
@ -326,11 +331,11 @@ message_translate (ClutterBackend *backend,
const MSG *msg, const MSG *msg,
gboolean *call_def_window_proc) gboolean *call_def_window_proc)
{ {
ClutterBackendWin32 *backend_win32; ClutterBackendWin32 *backend_win32;
ClutterStageWin32 *stage_win32; ClutterStageWin32 *stage_win32;
ClutterStage *stage; ClutterStage *stage;
ClutterStageWindow *impl; ClutterStageWindow *impl;
gboolean res; gboolean res;
backend_win32 = CLUTTER_BACKEND_WIN32 (backend); backend_win32 = CLUTTER_BACKEND_WIN32 (backend);
@ -429,39 +434,39 @@ message_translate (ClutterBackend *backend,
break; break;
case WM_LBUTTONDOWN: case WM_LBUTTONDOWN:
make_button_event (msg, event, 1, 1, FALSE); make_button_event (msg, event, 1, 1, FALSE, backend_win32->core_pointer);
break; break;
case WM_MBUTTONDOWN: case WM_MBUTTONDOWN:
make_button_event (msg, event, 2, 1, FALSE); make_button_event (msg, event, 2, 1, FALSE, backend_win32->core_pointer);
break; break;
case WM_RBUTTONDOWN: case WM_RBUTTONDOWN:
make_button_event (msg, event, 3, 1, FALSE); make_button_event (msg, event, 3, 1, FALSE, backend_win32->core_pointer);
break; break;
case WM_LBUTTONUP: case WM_LBUTTONUP:
make_button_event (msg, event, 1, 1, TRUE); make_button_event (msg, event, 1, 1, TRUE, backend_win32->core_pointer);
break; break;
case WM_MBUTTONUP: case WM_MBUTTONUP:
make_button_event (msg, event, 2, 1, TRUE); make_button_event (msg, event, 2, 1, TRUE, backend_win32->core_pointer);
break; break;
case WM_RBUTTONUP: case WM_RBUTTONUP:
make_button_event (msg, event, 3, 1, TRUE); make_button_event (msg, event, 3, 1, TRUE, backend_win32->core_pointer);
break; break;
case WM_LBUTTONDBLCLK: case WM_LBUTTONDBLCLK:
make_button_event (msg, event, 1, 2, FALSE); make_button_event (msg, event, 1, 2, FALSE, backend_win32->core_pointer);
break; break;
case WM_MBUTTONDBLCLK: case WM_MBUTTONDBLCLK:
make_button_event (msg, event, 2, 2, FALSE); make_button_event (msg, event, 2, 2, FALSE, backend_win32->core_pointer);
break; break;
case WM_RBUTTONDBLCLK: case WM_RBUTTONDBLCLK:
make_button_event (msg, event, 3, 2, FALSE); make_button_event (msg, event, 3, 2, FALSE, backend_win32->core_pointer);
break; break;
case WM_MOUSEWHEEL: case WM_MOUSEWHEEL:
@ -469,8 +474,8 @@ message_translate (ClutterBackend *backend,
event->type = CLUTTER_SCROLL; event->type = CLUTTER_SCROLL;
event->scroll.time = msg->time; event->scroll.time = msg->time;
event->scroll.modifier_state event->scroll.modifier_state = get_modifier_state (LOWORD (msg->wParam));
= get_modifier_state (LOWORD (msg->wParam)); event->scroll.device = backend_win32->core_pointer;
/* conversion to window coordinates is required */ /* conversion to window coordinates is required */
{ {
@ -500,7 +505,9 @@ message_translate (ClutterBackend *backend,
event->motion.x = GET_X_LPARAM (msg->lParam); event->motion.x = GET_X_LPARAM (msg->lParam);
event->motion.y = GET_Y_LPARAM (msg->lParam); event->motion.y = GET_Y_LPARAM (msg->lParam);
event->motion.modifier_state = get_modifier_state (msg->wParam); event->motion.modifier_state = get_modifier_state (msg->wParam);
/* We need to start tracking when the mouse leaves the stage if event->motion.device = backend_win32->core_pointer;
/* We need to start tracking when the mouse enters the stage if
we're not already */ we're not already */
if (!stage_win32->tracking_mouse) if (!stage_win32->tracking_mouse)
{ {
@ -511,6 +518,9 @@ message_translate (ClutterBackend *backend,
tmevent.hwndTrack = stage_win32->hwnd; tmevent.hwndTrack = stage_win32->hwnd;
TrackMouseEvent (&tmevent); TrackMouseEvent (&tmevent);
/* we entered the stage */
_clutter_input_device_set_stage (event->motion.device, stage);
stage_win32->tracking_mouse = TRUE; stage_win32->tracking_mouse = TRUE;
} }
break; break;
@ -520,6 +530,10 @@ message_translate (ClutterBackend *backend,
event->crossing.time = msg->time; event->crossing.time = msg->time;
event->crossing.x = msg->pt.x; event->crossing.x = msg->pt.x;
event->crossing.y = msg->pt.y; event->crossing.y = msg->pt.y;
event->crossing.device = backend_win32->core_pointer;
/* we left the stage */
_clutter_input_device_set_stage (event->crossing.device, NULL);
/* When we get a leave message the mouse tracking is /* When we get a leave message the mouse tracking is
automatically cancelled so we'll need to start it again when automatically cancelled so we'll need to start it again when
@ -604,6 +618,7 @@ message_translate (ClutterBackend *backend,
event->key.time = msg->time; event->key.time = msg->time;
event->key.modifier_state = get_key_modifier_state (key_states); event->key.modifier_state = get_key_modifier_state (key_states);
event->key.hardware_keycode = scan_code; event->key.hardware_keycode = scan_code;
event->key.device = backend_win32->core_keyboard;
} }
break; break;
@ -616,6 +631,20 @@ message_translate (ClutterBackend *backend,
} }
break; break;
case WM_SETCURSOR:
/* If the cursor is in the window's client area and the stage's
cursor should be invisible then we'll set a blank cursor
instead */
if (LOWORD (msg->lParam) == HTCLIENT && !stage_win32->is_cursor_visible)
{
if (call_def_window_proc)
*call_def_window_proc = FALSE;
_clutter_stage_win32_update_cursor (stage_win32);
}
res = FALSE;
break;
default: default:
/* ignore every other message */ /* ignore every other message */
res = FALSE; res = FALSE;

View File

@ -216,17 +216,51 @@ clutter_stage_win32_set_title (ClutterStageWindow *stage_window,
SetWindowTextW (stage_win32->hwnd, stage_win32->wtitle); SetWindowTextW (stage_win32->hwnd, stage_win32->wtitle);
} }
void
_clutter_stage_win32_update_cursor (ClutterStageWin32 *stage_win32)
{
HCURSOR cursor;
if (stage_win32->is_cursor_visible)
cursor = (HCURSOR) GetClassLongPtrW (stage_win32->hwnd, GCL_HCURSOR);
else
{
ClutterBackend *backend = clutter_get_default_backend ();
/* The documentation implies that we can just use
SetCursor(NULL) to get rid of the cursor but apparently this
doesn't work very well so instead we create an invisible
cursor */
cursor = _clutter_backend_win32_get_invisible_cursor (backend);
}
SetCursor (cursor);
}
static void static void
clutter_stage_win32_set_cursor_visible (ClutterStageWindow *stage_window, clutter_stage_win32_set_cursor_visible (ClutterStageWindow *stage_window,
gboolean cursor_visible) gboolean cursor_visible)
{ {
ClutterStageWin32 *stage_win32 = CLUTTER_STAGE_WIN32 (stage_window); ClutterStageWin32 *stage_win32 = CLUTTER_STAGE_WIN32 (stage_window);
if (stage_win32->is_cursor_visible != cursor_visible && if (stage_win32->is_cursor_visible != cursor_visible)
stage_win32->tracking_mouse) {
ShowCursor (cursor_visible); POINT cursor_pos;
RECT client_rect;
stage_win32->is_cursor_visible = cursor_visible; stage_win32->is_cursor_visible = cursor_visible;
/* If the cursor is already over the client area of the window
then we need to update it immediately */
GetCursorPos (&cursor_pos);
if (WindowFromPoint (cursor_pos) == stage_win32->hwnd &&
ScreenToClient (stage_win32->hwnd, &cursor_pos) &&
GetClientRect (stage_win32->hwnd, &client_rect) &&
cursor_pos.x >= client_rect.left &&
cursor_pos.y >= client_rect.top &&
cursor_pos.x < client_rect.right &&
cursor_pos.y < client_rect.bottom)
_clutter_stage_win32_update_cursor (stage_win32);
}
} }
static LONG static LONG
@ -442,7 +476,7 @@ clutter_stage_win32_realize (ClutterStageWindow *stage_window)
win_xpos = stage_win32->fullscreen_rect.left; win_xpos = stage_win32->fullscreen_rect.left;
win_ypos = stage_win32->fullscreen_rect.top; win_ypos = stage_win32->fullscreen_rect.top;
win_width = stage_win32->fullscreen_rect.right - win_xpos; win_width = stage_win32->fullscreen_rect.right - win_xpos;
win_height = stage_win32->fullscreen_rect.left - win_ypos; win_height = stage_win32->fullscreen_rect.bottom - win_ypos;
} }
else else
{ {

View File

@ -80,6 +80,8 @@ LRESULT CALLBACK _clutter_stage_win32_window_proc (HWND hwnd,
void _clutter_stage_win32_get_min_max_info (ClutterStageWin32 *stage_win32, void _clutter_stage_win32_get_min_max_info (ClutterStageWin32 *stage_win32,
MINMAXINFO *min_max_info); MINMAXINFO *min_max_info);
void _clutter_stage_win32_update_cursor (ClutterStageWin32 *stage_win32);
G_END_DECLS G_END_DECLS
#endif /* __CLUTTER_STAGE_H__ */ #endif /* __CLUTTER_STAGE_H__ */

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 B

View File

@ -0,0 +1 @@
42 CURSOR "invisible-cursor.cur"

View File

@ -43,6 +43,8 @@ libclutter_x11_la_SOURCES = \
clutter-backend-x11.h \ clutter-backend-x11.h \
clutter-backend-x11.c \ clutter-backend-x11.c \
clutter-event-x11.c \ clutter-event-x11.c \
clutter-input-device-x11.h \
clutter-input-device-x11.c \
clutter-stage-x11.h \ clutter-stage-x11.h \
clutter-stage-x11.c \ clutter-stage-x11.c \
clutter-x11-enum-types.h \ clutter-x11-enum-types.h \

View File

@ -37,6 +37,7 @@
#include <errno.h> #include <errno.h>
#include "clutter-backend-x11.h" #include "clutter-backend-x11.h"
#include "clutter-input-device-x11.h"
#include "clutter-stage-x11.h" #include "clutter-stage-x11.h"
#include "clutter-x11.h" #include "clutter-x11.h"
@ -46,31 +47,16 @@
#include <X11/extensions/XInput.h> #include <X11/extensions/XInput.h>
#endif #endif
#include "../clutter-event.h"
#include "../clutter-main.h"
#include "../clutter-debug.h"
#include "../clutter-private.h"
#include "cogl/cogl.h" #include "cogl/cogl.h"
#include "../clutter-debug.h"
#include "../clutter-device-manager.h"
#include "../clutter-event.h"
#include "../clutter-main.h"
#include "../clutter-private.h"
G_DEFINE_TYPE (ClutterBackendX11, clutter_backend_x11, CLUTTER_TYPE_BACKEND); G_DEFINE_TYPE (ClutterBackendX11, clutter_backend_x11, CLUTTER_TYPE_BACKEND);
struct _ClutterX11XInputDevice
{
ClutterInputDevice device;
#ifdef HAVE_XINPUT
XDevice *xdevice;
XEventClass xevent_list[5]; /* MAX 5 event types */
int num_events;
#endif
};
#ifdef HAVE_XINPUT
void _clutter_x11_register_xinput ();
#endif
/* atoms; remember to add the code that assigns the atom value to /* atoms; remember to add the code that assigns the atom value to
* the member of the ClutterBackendX11 structure if you add an * the member of the ClutterBackendX11 structure if you add an
* atom name here. do not change the order! * atom name here. do not change the order!
@ -109,6 +95,129 @@ static gboolean clutter_synchronise = FALSE;
static int TrappedErrorCode = 0; static int TrappedErrorCode = 0;
static int (* old_error_handler) (Display *, XErrorEvent *); static int (* old_error_handler) (Display *, XErrorEvent *);
static void
clutter_x11_register_input_devices (ClutterBackendX11 *backend)
{
ClutterDeviceManager *manager;
ClutterInputDevice *device;
#ifdef HAVE_XINPUT
XDeviceInfo *x_devices = NULL;
int res, opcode, event, error;
int i, n_devices;
#endif /* HAVE_XINPUT */
manager = clutter_device_manager_get_default ();
if (!clutter_enable_xinput)
{
CLUTTER_NOTE (BACKEND, "XInput support not enabled");
goto default_device;
}
#ifdef HAVE_XINPUT
res = XQueryExtension (backend->xdpy, "XInputExtension",
&opcode,
&event,
&error);
if (!res)
{
CLUTTER_NOTE (BACKEND, "No XInput extension available");
goto default_device;
}
backend->xi_event_base = event;
x_devices = XListInputDevices (backend->xdpy, &n_devices);
if (n_devices == 0)
{
CLUTTER_NOTE (BACKEND, "No XInput devices found");
goto default_device;
}
for (i = 0; i < n_devices; i++)
{
XDeviceInfo *info = x_devices + i;
CLUTTER_NOTE (BACKEND,
"Considering device %li with type %d, %d of %d",
info->id,
info->use,
i, n_devices);
/* we only want 'raw' devices, not virtual ones */
if (info->use == IsXExtensionPointer ||
/* info->use == IsXExtensionKeyboard || XInput1 is broken */
info->use == IsXExtensionDevice)
{
ClutterInputDeviceType device_type;
gint n_events = 0;
switch (info->use)
{
case IsXExtensionPointer:
device_type = CLUTTER_POINTER_DEVICE;
break;
/* XInput1 is broken for keyboards */
case IsXExtensionKeyboard:
device_type = CLUTTER_KEYBOARD_DEVICE;
break;
case IsXExtensionDevice:
device_type = CLUTTER_EXTENSION_DEVICE;
break;
}
device = g_object_new (CLUTTER_TYPE_INPUT_DEVICE_X11,
"id", info->id,
"device-type", device_type,
"name", info->name,
NULL);
n_events = _clutter_input_device_x11_construct (device, backend);
_clutter_device_manager_add_device (manager, device);
if (info->use == IsXExtensionPointer && n_events > 0)
backend->have_xinput = TRUE;
}
}
XFree (x_devices);
#endif /* HAVE_XINPUT */
default_device:
/* fallback code in case:
*
* - we do not have XInput support compiled in
* - we do not have XInput support enabled
* - we do not have the XInput extension
*
* we register two default devices, one for the pointer
* and one for the keyboard. this block must also be
* executed for the XInput support because XI does not
* cover core devices
*/
device = g_object_new (CLUTTER_TYPE_INPUT_DEVICE_X11,
"id", 0,
"name", "Core Pointer",
"device-type", CLUTTER_POINTER_DEVICE,
"is-core", TRUE,
NULL);
CLUTTER_NOTE (BACKEND, "Added core pointer device");
_clutter_device_manager_add_device (manager, device);
backend->core_pointer = device;
device = g_object_new (CLUTTER_TYPE_INPUT_DEVICE_X11,
"id", 1,
"name", "Core Keyboard",
"device-type", CLUTTER_KEYBOARD_DEVICE,
"is-core", TRUE,
NULL);
CLUTTER_NOTE (BACKEND, "Added core keyboard device");
_clutter_device_manager_add_device (manager, device);
backend->core_keyboard = device;
}
gboolean gboolean
clutter_backend_x11_pre_parse (ClutterBackend *backend, clutter_backend_x11_pre_parse (ClutterBackend *backend,
GError **error) GError **error)
@ -199,9 +308,8 @@ clutter_backend_x11_post_parse (ClutterBackend *backend,
clutter_backend_set_resolution (backend, dpi); clutter_backend_set_resolution (backend, dpi);
#ifdef HAVE_XINPUT /* register input devices */
_clutter_x11_register_xinput (); clutter_x11_register_input_devices (backend_x11);
#endif
if (clutter_synchronise) if (clutter_synchronise)
XSynchronize (backend_x11->xdpy, True); XSynchronize (backend_x11->xdpy, True);
@ -644,239 +752,29 @@ clutter_x11_remove_filter (ClutterX11FilterFunc func,
} }
} }
void
_clutter_x11_register_xinput ()
{
#ifdef HAVE_XINPUT
XDeviceInfo *xdevices = NULL;
XDeviceInfo *info = NULL;
XDevice *xdevice = NULL;
XInputClassInfo *xclass_info = NULL;
gint opcode, event, error;
gint res;
gint num_devices = 0;
gint num_events = 0;
gint i = 0, j = 0;
gboolean have_an_xpointer = FALSE;
ClutterBackendX11 *x11b;
ClutterX11XInputDevice *device = NULL;
ClutterMainContext *context;
GSList *input_devices = NULL;
if (!backend_singleton)
{
g_critical ("X11 backend has not been initialised");
return;
}
if (!clutter_enable_xinput)
{
CLUTTER_NOTE (BACKEND, "Not enabling XInput");
return;
}
context = _clutter_context_get_default ();
backend_singleton->have_xinput = FALSE;
/* is the XInput extension available? */
res = XQueryExtension (backend_singleton->xdpy, "XInputExtension",
&opcode, &event,
&error);
if (!res)
{
CLUTTER_NOTE (BACKEND, "X Input extension not available");
return;
}
x11b = backend_singleton;
xdevices = XListInputDevices (x11b->xdpy, &num_devices);
CLUTTER_NOTE (BACKEND, "%d XINPUT devices found", num_devices);
if (num_devices == 0)
return;
for (i = 0; i < num_devices; i++)
{
info = xdevices + i;
num_events = 0;
CLUTTER_NOTE (BACKEND, "Considering %li with type %d",
info->id,
info->use);
/* Only want 'raw' devices themselves not virtual ones */
if (info->use == IsXExtensionPointer ||
/*info->use == IsXExtensionKeyboard || XInput 1.x is broken */
info->use == IsXExtensionDevice)
{
clutter_x11_trap_x_errors ();
xdevice = XOpenDevice (backend_singleton->xdpy, info->id);
if (clutter_x11_untrap_x_errors () || xdevice == NULL)
continue;
/* Create the appropriate Clutter device */
device = g_slice_new0 (ClutterX11XInputDevice);
device->device.id = info->id;
switch (info->use)
{
case IsXExtensionPointer:
device->device.device_type = CLUTTER_POINTER_DEVICE;
have_an_xpointer = TRUE;
break;
#if 0
/* XInput 1.x is broken for keyboards: */
case IsXExtensionKeyboard:
device->device.type = CLUTTER_KEYBOARD_DEVICE;
break;
#endif
case IsXExtensionDevice:
device->device.device_type = CLUTTER_EXTENSION_DEVICE;
break;
}
/* FIXME: some kind of general device_init() call should do below */
device->device.click_count = 0;
device->device.previous_time = 0;
device->device.previous_x = -1;
device->device.previous_y = -1;
device->device.previous_button_number = -1;
device->num_events = 0;
device->xdevice = xdevice;
CLUTTER_NOTE (BACKEND, "Registering XINPUT device with XID: %li",
xdevice->device_id);
/* We must go through all the classes supported by this device and
* register the appropriate events we want. Each class only appears
* once. We need to store the types with the stage since they are
* created dynamically by the server. They are not device specific.
*/
for (j = 0; j < xdevice->num_classes; j++)
{
xclass_info = xdevice->classes + j;
switch (xclass_info->input_class)
{
#if 0
/* XInput 1.x is broken for keyboards: */
case KeyClass:
DeviceKeyPress (xdevice,
x11b->event_types[CLUTTER_X11_XINPUT_KEY_PRESS_EVENT],
device->xevent_list [num_events]);
num_events++;
DeviceKeyRelease (xdevice,
x11b->event_types[CLUTTER_X11_XINPUT_KEY_RELEASE_EVENT],
device->xevent_list [num_events]);
num_events++;
break;
#endif
case ButtonClass:
DeviceButtonPress (xdevice,
x11b->event_types[CLUTTER_X11_XINPUT_BUTTON_PRESS_EVENT],
device->xevent_list [num_events]);
num_events++;
DeviceButtonRelease (xdevice,
x11b->event_types[CLUTTER_X11_XINPUT_BUTTON_RELEASE_EVENT],
device->xevent_list [num_events]);
num_events++;
break;
case ValuatorClass:
DeviceMotionNotify (xdevice,
x11b->event_types[CLUTTER_X11_XINPUT_MOTION_NOTIFY_EVENT],
device->xevent_list [num_events]);
num_events++;
break;
}
}
if (info->use == IsXExtensionPointer && num_events > 0)
have_an_xpointer = TRUE;
device->num_events = num_events;
input_devices = g_slist_prepend (input_devices, device);
}
}
XFree (xdevices);
if (!have_an_xpointer)
{
GSList *l;
/* Something is likely wrong with Xinput setup so we basically
* abort here and fall back to lofi regular xinput.
*/
g_warning ("No usuable XInput pointing devices found");
for (l = input_devices; l != NULL; l = l->next)
g_slice_free (ClutterX11XInputDevice, l->data);
g_slist_free (input_devices);
context->input_devices = NULL;
return;
}
/* store the list of input devices */
context->input_devices = g_slist_reverse (input_devices);
/* why yes, we are awesome */
backend_singleton->have_xinput = TRUE;
#endif /* HAVE_XINPUT */
}
void
_clutter_x11_unregister_xinput ()
{
}
void void
_clutter_x11_select_events (Window xwin) _clutter_x11_select_events (Window xwin)
{ {
#ifdef HAVE_XINPUT #ifdef HAVE_XINPUT
GSList *list_it; ClutterDeviceManager *manager;
ClutterX11XInputDevice *device = NULL; const GSList *l;
ClutterMainContext *context; if (G_UNLIKELY (backend_singleton == NULL))
context = _clutter_context_get_default ();
if (!backend_singleton)
{ {
g_critical ("X11 backend has not been initialised"); g_critical ("X11 backend has not been initialised");
return; return;
} }
for (list_it = context->input_devices; manager = clutter_device_manager_get_default ();
list_it != NULL;
list_it = list_it->next)
{
device = (ClutterX11XInputDevice *)list_it->data;
XSelectExtensionEvent (backend_singleton->xdpy, for (l = clutter_device_manager_peek_devices (manager);
xwin, l != NULL;
device->xevent_list, l = l->next)
device->num_events); {
ClutterInputDevice *device = l->data;
_clutter_input_device_x11_select_events (device, backend_singleton, xwin);
} }
#endif /* HAVE_XINPUT */ #endif /* HAVE_XINPUT */
} }
@ -884,49 +782,34 @@ _clutter_x11_select_events (Window xwin)
ClutterInputDevice * ClutterInputDevice *
_clutter_x11_get_device_for_xid (XID id) _clutter_x11_get_device_for_xid (XID id)
{ {
#ifdef HAVE_XINPUT ClutterDeviceManager *manager;
ClutterMainContext *context;
GSList *l;
context = _clutter_context_get_default (); manager = clutter_device_manager_get_default ();
if (!backend_singleton) return clutter_device_manager_get_device (manager, (gint) id);
{
g_critical ("X11 backend has not been initialised");
return NULL;
}
for (l = context->input_devices; l != NULL; l = l->next)
{
ClutterX11XInputDevice *device = l->data;
if (device->xdevice->device_id == id)
return (ClutterInputDevice *) device;
}
#endif /* HAVE_XINPUT */
return NULL;
} }
/* FIXME: This nasty little func needs moving elsewhere.. */ /**
* clutter_x11_get_input_devices:
*
* Retrieves a pointer to the list of input devices
*
* Deprecated: 1.2: Use clutter_device_manager_peek_devices() instead
*
* Since: 0.8
*
* Return value: a pointer to the internal list of input devices; the
* returned list is owned by Clutter and should not be modified or
* freed
*/
G_CONST_RETURN GSList * G_CONST_RETURN GSList *
clutter_x11_get_input_devices (void) clutter_x11_get_input_devices (void)
{ {
#ifdef HAVE_XINPUT ClutterDeviceManager *manager;
ClutterMainContext *context;
if (!backend_singleton) manager = clutter_device_manager_get_default ();
{
g_critical ("X11 backend has not been initialised");
return NULL;
}
context = _clutter_context_get_default (); return clutter_device_manager_peek_devices (manager);
return context->input_devices;
#else /* !HAVE_XINPUT */
return NULL;
#endif /* HAVE_XINPUT */
} }
/** /**
@ -943,7 +826,7 @@ gboolean
clutter_x11_has_xinput (void) clutter_x11_has_xinput (void)
{ {
#ifdef HAVE_XINPUT #ifdef HAVE_XINPUT
if (!backend_singleton) if (backend_singleton == NULL)
{ {
g_critical ("X11 backend has not been initialised"); g_critical ("X11 backend has not been initialised");
return FALSE; return FALSE;

View File

@ -76,10 +76,14 @@ struct _ClutterBackendX11
Atom atom_NET_WM_NAME; Atom atom_NET_WM_NAME;
Atom atom_UTF8_STRING; Atom atom_UTF8_STRING;
int xi_event_base;
int event_types[CLUTTER_X11_XINPUT_LAST_EVENT]; int event_types[CLUTTER_X11_XINPUT_LAST_EVENT];
gboolean have_xinput; gboolean have_xinput;
Time last_event_time; Time last_event_time;
ClutterInputDevice *core_pointer;
ClutterInputDevice *core_keyboard;
}; };
struct _ClutterBackendX11Class struct _ClutterBackendX11Class
@ -125,12 +129,6 @@ clutter_backend_x11_get_features (ClutterBackend *backend);
XVisualInfo * XVisualInfo *
clutter_backend_x11_get_visual_info (ClutterBackendX11 *backend_x11); clutter_backend_x11_get_visual_info (ClutterBackendX11 *backend_x11);
void
_clutter_x11_register_xinput (void);
void
_clutter_x11_unregister_xinput (void);
ClutterInputDevice * ClutterInputDevice *
_clutter_x11_get_device_for_xid (XID id); _clutter_x11_get_device_for_xid (XID id);

View File

@ -1,7 +1,8 @@
/* Clutter. /* Clutter.
* An OpenGL based 'interactive canvas' library. * An OpenGL based 'interactive canvas' library.
* Authored By Matthew Allum <mallum@openedhand.com> *
* Copyright (C) 2006-2007 OpenedHand * Copyright (C) 2006, 2007, 2008 OpenedHand Ltd
* Copyright (C) 2009, 2010 Intel Corp.
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@ -17,6 +18,10 @@
* License along with this library; if not, write to the * License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA. * Boston, MA 02111-1307, USA.
*
* Authored by:
* Matthew Allum <mallum@openedhand.com>
* Emmanuele Bassi <ebassi@linux.intel.com>
*/ */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
@ -183,7 +188,6 @@ _clutter_backend_x11_events_init (ClutterBackend *backend)
g_source_add_poll (source, &event_source->event_poll_fd); g_source_add_poll (source, &event_source->event_poll_fd);
g_source_set_can_recurse (source, TRUE); g_source_set_can_recurse (source, TRUE);
g_source_attach (source, NULL); g_source_attach (source, NULL);
} }
void void
@ -263,9 +267,10 @@ set_user_time (ClutterBackendX11 *backend_x11,
} }
} }
#if 0 /* See XInput keyboard comment below HAVE_XINPUT */ #ifdef HAVE_XINPUT
static void static void
convert_xdevicekey_to_xkey (XDeviceKeyEvent *xkev, XEvent *xevent) convert_xdevicekey_to_xkey (XDeviceKeyEvent *xkev,
XEvent *xevent)
{ {
xevent->xany.type = xevent->xkey.type = xkev->type; xevent->xany.type = xevent->xkey.type = xkev->type;
xevent->xkey.serial = xkev->serial; xevent->xkey.serial = xkev->serial;
@ -410,27 +415,28 @@ 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;
ClutterStageWindow *impl; ClutterStageWindow *impl;
gboolean res, not_yet_handled = FALSE; gboolean res, not_yet_handled = FALSE;
Window xwindow, stage_xwindow; Window xwindow, stage_xwindow;
ClutterDeviceManager *manager;
ClutterInputDevice *device;
backend_x11 = CLUTTER_BACKEND_X11 (backend); backend_x11 = CLUTTER_BACKEND_X11 (backend);
xwindow = xevent->xany.window; xwindow = xevent->xany.window;
if (backend_x11->event_filters) if (backend_x11->event_filters)
{ {
GSList *node; GSList *node;
ClutterX11EventFilter *filter;
node = backend_x11->event_filters; node = backend_x11->event_filters;
while (node) while (node)
{ {
filter = node->data; ClutterX11EventFilter *filter = node->data;
switch (filter->func (xevent, event, filter->data)) switch (filter->func (xevent, event, filter->data))
{ {
@ -448,19 +454,19 @@ event_translate (ClutterBackend *backend,
} }
} }
/* /* Do further processing only on events for the stage window (the x11
* Do further processing only on events for the stage window * filters might be getting events for other windows, so do not mess
* (the x11 filters might be getting events for other windows, so do not * them about.
* mess them about.
*/ */
stage = clutter_x11_get_stage_from_window (xwindow); stage = clutter_x11_get_stage_from_window (xwindow);
if (stage == NULL) if (stage == NULL)
return FALSE; return FALSE;
impl = _clutter_stage_get_window (stage); manager = clutter_device_manager_get_default ();
stage_x11 = CLUTTER_STAGE_X11 (impl);
stage_xwindow = xwindow; /* clutter_x11_get_stage_window (stage); */ impl = _clutter_stage_get_window (stage);
stage_x11 = CLUTTER_STAGE_X11 (impl);
stage_xwindow = xwindow; /* clutter_x11_get_stage_window (stage); */
event->any.stage = stage; event->any.stage = stage;
@ -621,6 +627,8 @@ event_translate (ClutterBackend *backend,
case KeyPress: case KeyPress:
event->key.type = event->type = CLUTTER_KEY_PRESS; event->key.type = event->type = CLUTTER_KEY_PRESS;
event->key.device = backend_x11->core_keyboard;
translate_key_event (backend, event, xevent); translate_key_event (backend, event, xevent);
set_user_time (backend_x11, &xwindow, xevent->xkey.time); set_user_time (backend_x11, &xwindow, xevent->xkey.time);
@ -654,6 +662,8 @@ event_translate (ClutterBackend *backend,
} }
event->key.type = event->type = CLUTTER_KEY_RELEASE; event->key.type = event->type = CLUTTER_KEY_RELEASE;
event->key.device = backend_x11->core_keyboard;
translate_key_event (backend, event, xevent); translate_key_event (backend, event, xevent);
break; break;
@ -666,239 +676,287 @@ event_translate (ClutterBackend *backend,
/* Input device event handling.. */ /* Input device event handling.. */
if (not_yet_handled) if (not_yet_handled)
{ {
if (!clutter_x11_has_xinput ()) device = backend_x11->core_pointer;
/* Regular X event */
switch (xevent->type)
{ {
/* Regular X event */ case ButtonPress:
switch (xevent->type) switch (xevent->xbutton.button)
{ {
/* KeyPress / KeyRelease should reside here if XInput case 4: /* up */
* worked properly case 5: /* down */
*/ case 6: /* left */
case ButtonPress: case 7: /* right */
switch (xevent->xbutton.button) event->scroll.type = event->type = CLUTTER_SCROLL;
{
case 4: /* up */ if (xevent->xbutton.button == 4)
case 5: /* down */ event->scroll.direction = CLUTTER_SCROLL_UP;
case 6: /* left */ else if (xevent->xbutton.button == 5)
case 7: /* right */ event->scroll.direction = CLUTTER_SCROLL_DOWN;
event->scroll.type = event->type = CLUTTER_SCROLL; else if (xevent->xbutton.button == 6)
event->scroll.direction = CLUTTER_SCROLL_LEFT;
if (xevent->xbutton.button == 4) else
event->scroll.direction = CLUTTER_SCROLL_UP; event->scroll.direction = CLUTTER_SCROLL_RIGHT;
else if (xevent->xbutton.button == 5)
event->scroll.direction = CLUTTER_SCROLL_DOWN; event->scroll.time = xevent->xbutton.time;
else if (xevent->xbutton.button == 6) event->scroll.x = xevent->xbutton.x;
event->scroll.direction = CLUTTER_SCROLL_LEFT; event->scroll.y = xevent->xbutton.y;
else event->scroll.modifier_state = xevent->xbutton.state;
event->scroll.direction = CLUTTER_SCROLL_RIGHT; event->scroll.device = device;
event->scroll.time = xevent->xbutton.time;
event->scroll.x = xevent->xbutton.x;
event->scroll.y = xevent->xbutton.y;
event->scroll.modifier_state = xevent->xbutton.state;
break;
default:
event->button.type = event->type = CLUTTER_BUTTON_PRESS;
event->button.time = xevent->xbutton.time;
event->button.x = xevent->xbutton.x;
event->button.y = xevent->xbutton.y;
event->button.modifier_state = xevent->xbutton.state;
event->button.button = xevent->xbutton.button;
break;
}
set_user_time (backend_x11, &xwindow, event->button.time);
break; break;
case ButtonRelease: default:
/* scroll events don't have a corresponding release */ event->button.type = event->type = CLUTTER_BUTTON_PRESS;
if (xevent->xbutton.button == 4 ||
xevent->xbutton.button == 5 ||
xevent->xbutton.button == 6 ||
xevent->xbutton.button == 7)
{
res = FALSE;
break;
}
event->button.type = event->type = CLUTTER_BUTTON_RELEASE;
event->button.time = xevent->xbutton.time; event->button.time = xevent->xbutton.time;
event->button.x = xevent->xbutton.x; event->button.x = xevent->xbutton.x;
event->button.y = xevent->xbutton.y; event->button.y = xevent->xbutton.y;
event->button.modifier_state = xevent->xbutton.state; event->button.modifier_state = xevent->xbutton.state;
event->button.button = xevent->xbutton.button; event->button.button = xevent->xbutton.button;
event->button.device = device;
break; break;
}
case MotionNotify:
event->motion.type = event->type = CLUTTER_MOTION;
event->motion.time = xevent->xmotion.time;
event->motion.x = xevent->xmotion.x;
event->motion.y = xevent->xmotion.y;
event->motion.modifier_state = xevent->xmotion.state;
break;
case EnterNotify: set_user_time (backend_x11, &xwindow, event->button.time);
/* Convert enter notifies to motion events because X
doesn't emit the corresponding motion notify */
event->motion.type = event->type = CLUTTER_MOTION;
event->motion.time = xevent->xcrossing.time;
event->motion.x = xevent->xcrossing.x;
event->motion.y = xevent->xcrossing.y;
event->motion.modifier_state = xevent->xcrossing.state;
break;
case LeaveNotify: res = TRUE;
event->crossing.type = event->type = CLUTTER_LEAVE; break;
event->crossing.time = xevent->xcrossing.time;
event->crossing.x = xevent->xcrossing.x; case ButtonRelease:
event->crossing.y = xevent->xcrossing.y; /* scroll events don't have a corresponding release */
if (xevent->xbutton.button == 4 ||
xevent->xbutton.button == 5 ||
xevent->xbutton.button == 6 ||
xevent->xbutton.button == 7)
{
res = FALSE;
goto out;
}
event->button.type = event->type = CLUTTER_BUTTON_RELEASE;
event->button.time = xevent->xbutton.time;
event->button.x = xevent->xbutton.x;
event->button.y = xevent->xbutton.y;
event->button.modifier_state = xevent->xbutton.state;
event->button.button = xevent->xbutton.button;
event->button.device = device;
res = TRUE;
break;
case MotionNotify:
event->motion.type = event->type = CLUTTER_MOTION;
event->motion.time = xevent->xmotion.time;
event->motion.x = xevent->xmotion.x;
event->motion.y = xevent->xmotion.y;
event->motion.modifier_state = xevent->xmotion.state;
event->motion.device = device;
res = TRUE;
break;
case EnterNotify:
/* we know that we are entering the stage here */
_clutter_input_device_set_stage (device, stage);
CLUTTER_NOTE (EVENT, "Entering the stage");
/* Convert enter notifies to motion events because X
doesn't emit the corresponding motion notify */
event->motion.type = event->type = CLUTTER_MOTION;
event->motion.time = xevent->xcrossing.time;
event->motion.x = xevent->xcrossing.x;
event->motion.y = xevent->xcrossing.y;
event->motion.modifier_state = xevent->xcrossing.state;
event->motion.source = CLUTTER_ACTOR (stage);
event->motion.device = device;
res = TRUE;
break;
case LeaveNotify:
if (device->stage == NULL)
{
CLUTTER_NOTE (EVENT,
"Discarding LeaveNotify for ButtonRelease "
"event off-stage");
res = FALSE;
goto out;
}
/* we know that we are leaving the stage here */
_clutter_input_device_set_stage (device, NULL);
CLUTTER_NOTE (EVENT, "Leaving the stage (time:%u)",
event->crossing.time);
event->crossing.type = event->type = CLUTTER_LEAVE;
event->crossing.time = xevent->xcrossing.time;
event->crossing.x = xevent->xcrossing.x;
event->crossing.y = xevent->xcrossing.y;
event->crossing.source = CLUTTER_ACTOR (stage);
event->crossing.device = device;
res = TRUE;
break;
default:
res = FALSE;
break;
}
}
/* XInput fun...*/
if (!res && clutter_x11_has_xinput ())
{
#ifdef HAVE_XINPUT
int *ev_types = backend_x11->event_types;
int button_press, button_release;
int key_press, key_release;
int motion_notify;
button_press = ev_types[CLUTTER_X11_XINPUT_BUTTON_PRESS_EVENT];
button_release = ev_types[CLUTTER_X11_XINPUT_BUTTON_RELEASE_EVENT];
motion_notify = ev_types[CLUTTER_X11_XINPUT_MOTION_NOTIFY_EVENT];
key_press = ev_types[CLUTTER_X11_XINPUT_KEY_PRESS_EVENT];
key_release = ev_types[CLUTTER_X11_XINPUT_KEY_RELEASE_EVENT];
CLUTTER_NOTE (EVENT, "XInput event type: %d", xevent->type);
if (xevent->type == button_press)
{
XDeviceButtonEvent *xbev = (XDeviceButtonEvent *) xevent;
device = _clutter_x11_get_device_for_xid (xbev->deviceid);
_clutter_input_device_set_stage (device, stage);
CLUTTER_NOTE (EVENT,
"XI ButtonPress for %li ('%s') at %d, %d",
xbev->deviceid,
device->device_name,
xbev->x,
xbev->y);
switch (xbev->button)
{
case 4:
case 5:
case 6:
case 7:
event->scroll.type = event->type = CLUTTER_SCROLL;
if (xbev->button == 4)
event->scroll.direction = CLUTTER_SCROLL_UP;
else if (xbev->button == 5)
event->scroll.direction = CLUTTER_SCROLL_DOWN;
else if (xbev->button == 6)
event->scroll.direction = CLUTTER_SCROLL_LEFT;
else
event->scroll.direction = CLUTTER_SCROLL_RIGHT;
event->scroll.time = xbev->time;
event->scroll.x = xbev->x;
event->scroll.y = xbev->y;
event->scroll.modifier_state = xbev->state;
event->scroll.device = device;
break; break;
default: default:
/* ignore every other event */ event->button.type = event->type = CLUTTER_BUTTON_PRESS;
res = FALSE;
break;
}
}
else
{ /* XInput fun.. Needs clean up. */
#ifdef HAVE_XINPUT
int *ev_types = backend_x11->event_types;
CLUTTER_NOTE (EVENT, "XInput event type: %d", xevent->type);
if (xevent->type == ev_types [CLUTTER_X11_XINPUT_BUTTON_PRESS_EVENT])
{
XDeviceButtonEvent *xbev = (XDeviceButtonEvent *) xevent;
CLUTTER_NOTE (EVENT, "XINPUT Button press event for %li at %d, %d",
xbev->deviceid,
xbev->x,
xbev->y);
switch (xbev->button)
{
case 4:
case 5:
case 6:
case 7:
event->scroll.type = event->type = CLUTTER_SCROLL;
if (xbev->button == 4)
event->scroll.direction = CLUTTER_SCROLL_UP;
else if (xbev->button == 5)
event->scroll.direction = CLUTTER_SCROLL_DOWN;
else if (xbev->button == 6)
event->scroll.direction = CLUTTER_SCROLL_LEFT;
else
event->scroll.direction = CLUTTER_SCROLL_RIGHT;
event->scroll.time = xbev->time;
event->scroll.x = xbev->x;
event->scroll.y = xbev->y;
event->scroll.modifier_state = xbev->state;
event->scroll.device = _clutter_x11_get_device_for_xid (xbev->deviceid);
break;
default:
event->button.type = event->type = CLUTTER_BUTTON_PRESS;
event->button.time = xbev->time;
event->button.x = xbev->x;
event->button.y = xbev->y;
event->button.modifier_state = xbev->state;
event->button.button = xbev->button;
event->button.device = _clutter_x11_get_device_for_xid (xbev->deviceid);
break;
}
set_user_time (backend_x11, &xwindow, xbev->time);
}
else if (xevent->type
== ev_types[CLUTTER_X11_XINPUT_BUTTON_RELEASE_EVENT])
{
XDeviceButtonEvent *xbev = (XDeviceButtonEvent *)xevent;
CLUTTER_NOTE (EVENT, "XINPUT Button release event for %li at %d, %d",
xbev->deviceid,
xbev->x,
xbev->y);
/* scroll events don't have a corresponding release */
if (xbev->button == 4 ||
xbev->button == 5 ||
xbev->button == 6 ||
xbev->button == 7)
{
return FALSE;
}
event->button.type = event->type = CLUTTER_BUTTON_RELEASE;
event->button.time = xbev->time; event->button.time = xbev->time;
event->button.x = xbev->x; event->button.x = xbev->x;
event->button.y = xbev->y; event->button.y = xbev->y;
event->button.modifier_state = xbev->state; event->button.modifier_state = xbev->state;
event->button.button = xbev->button; event->button.button = xbev->button;
event->button.device = _clutter_x11_get_device_for_xid (xbev->deviceid); event->button.device = device;
} break;
else if (xevent->type
== ev_types [CLUTTER_X11_XINPUT_MOTION_NOTIFY_EVENT])
{
XDeviceMotionEvent *xmev = (XDeviceMotionEvent *)xevent;
CLUTTER_NOTE(EVENT, "XINPUT Motion event for %li at %d, %d",
xmev->deviceid,
xmev->x,
xmev->y);
event->motion.type = event->type = CLUTTER_MOTION;
event->motion.time = xmev->time;
event->motion.x = xmev->x;
event->motion.y = xmev->y;
event->motion.modifier_state = xmev->state;
event->motion.device = _clutter_x11_get_device_for_xid (xmev->deviceid);
}
#if 0
/* the Xinput handling of key presses/releases disabled for now since
* it makes keyrepeat, and key presses and releases outside the window
* not generate events even when the window has focus
*/
else if (xevent->type
== ev_types [CLUTTER_X11_XINPUT_KEY_PRESS_EVENT])
{
XEvent xevent_converted;
XDeviceKeyEvent *xkev = (XDeviceKeyEvent *)xevent;
convert_xdevicekey_to_xkey (xkev, &xevent_converted);
event->key.type = event->type = CLUTTER_KEY_PRESS;
translate_key_event (backend, event, &xevent_converted);
set_user_time (backend_x11, &xwindow, xkev->time);
}
else if (xevent->type
== ev_types [CLUTTER_X11_XINPUT_KEY_RELEASE_EVENT])
{
XEvent xevent_converted;
XDeviceKeyEvent *xkev = (XDeviceKeyEvent *)xevent;
convert_xdevicekey_to_xkey (xkev, &xevent_converted);
event->key.type = event->type = CLUTTER_KEY_RELEASE;
translate_key_event (backend, event, &xevent_converted);
} }
#endif
else set_user_time (backend_x11, &xwindow, xbev->time);
#endif /* HAVE_XINPUT */
res = TRUE;
}
else if (xevent->type == button_release)
{
XDeviceButtonEvent *xbev = (XDeviceButtonEvent *)xevent;
device = _clutter_x11_get_device_for_xid (xbev->deviceid);
_clutter_input_device_set_stage (device, stage);
CLUTTER_NOTE (EVENT, "XI ButtonRelease for %li ('%s') at %d, %d",
xbev->deviceid,
device->device_name,
xbev->x,
xbev->y);
/* scroll events don't have a corresponding release */
if (xbev->button == 4 ||
xbev->button == 5 ||
xbev->button == 6 ||
xbev->button == 7)
{ {
CLUTTER_NOTE (EVENT, "Uknown Event");
res = FALSE; res = FALSE;
goto out;
} }
event->button.type = event->type = CLUTTER_BUTTON_RELEASE;
event->button.time = xbev->time;
event->button.x = xbev->x;
event->button.y = xbev->y;
event->button.modifier_state = xbev->state;
event->button.button = xbev->button;
event->button.device = device;
res = TRUE;
}
else if (xevent->type == motion_notify)
{
XDeviceMotionEvent *xmev = (XDeviceMotionEvent *)xevent;
device = _clutter_x11_get_device_for_xid (xmev->deviceid);
_clutter_input_device_set_stage (device, stage);
CLUTTER_NOTE (EVENT, "XI Motion for %li ('%s') at %d, %d",
xmev->deviceid,
device->device_name,
xmev->x,
xmev->y);
event->motion.type = event->type = CLUTTER_MOTION;
event->motion.time = xmev->time;
event->motion.x = xmev->x;
event->motion.y = xmev->y;
event->motion.modifier_state = xmev->state;
event->motion.device = device;
res = TRUE;
}
else if (xevent->type == key_press || xevent->type == key_release)
{
/* the XInput 1.x handling of key presses/releases is broken:
* it makes key repeat, key presses and releases outside the
* window not generate events even when the window has focus
*/
XDeviceKeyEvent *xkev = (XDeviceKeyEvent *) xevent;
XEvent xevent_converted;
convert_xdevicekey_to_xkey (xkev, &xevent_converted);
event->key.type = event->type = (xevent->type == key_press)
? CLUTTER_KEY_PRESS
: CLUTTER_KEY_RELEASE;
translate_key_event (backend, event, &xevent_converted);
if (xevent->type == key_press)
set_user_time (backend_x11, &xwindow, xkev->time);
}
else
#endif /* HAVE_XINPUT */
{
CLUTTER_NOTE (EVENT, "Uknown Event");
res = FALSE;
} }
} }
out:
return res; return res;
} }

View File

@ -0,0 +1,221 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "clutter-input-device-x11.h"
#include "../clutter-debug.h"
#include "../clutter-private.h"
#ifdef HAVE_XINPUT
#include <X11/extensions/XInput.h>
#endif
typedef struct _ClutterInputDeviceClass ClutterInputDeviceX11Class;
/* a specific X11 input device */
struct _ClutterInputDeviceX11
{
ClutterInputDevice device;
#ifdef HAVE_XINPUT
XDevice *xdevice;
XEventClass xevent_list[5]; /* MAX 5 event types */
int num_events;
#endif
guint is_core : 1;
};
enum
{
PROP_0,
PROP_IS_CORE
};
G_DEFINE_TYPE (ClutterInputDeviceX11,
clutter_input_device_x11,
CLUTTER_TYPE_INPUT_DEVICE);
static void
clutter_input_device_x11_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
ClutterInputDeviceX11 *self = CLUTTER_INPUT_DEVICE_X11 (gobject);
switch (prop_id)
{
case PROP_IS_CORE:
self->is_core = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
clutter_input_device_x11_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
ClutterInputDeviceX11 *self = CLUTTER_INPUT_DEVICE_X11 (gobject);
switch (prop_id)
{
case PROP_IS_CORE:
g_value_set_boolean (value, self->is_core);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
clutter_input_device_x11_class_init (ClutterInputDeviceX11Class *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GParamSpec *pspec;
gobject_class->set_property = clutter_input_device_x11_set_property;
gobject_class->get_property = clutter_input_device_x11_get_property;
pspec = g_param_spec_boolean ("is-core",
"Is Core",
"Whether the device is a core one",
FALSE,
CLUTTER_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_property (gobject_class, PROP_IS_CORE, pspec);
}
static void
clutter_input_device_x11_init (ClutterInputDeviceX11 *self)
{
self->is_core = FALSE;
}
gint
_clutter_input_device_x11_construct (ClutterInputDevice *device,
ClutterBackendX11 *backend)
{
int n_events = 0;
#ifdef HAVE_XINPUT
ClutterInputDeviceX11 *device_x11;
XDevice *x_device = NULL;
gint device_id;
int i;
device_x11 = CLUTTER_INPUT_DEVICE_X11 (device);
device_id = clutter_input_device_get_device_id (device);
clutter_x11_trap_x_errors ();
/* retrieve the X11 device */
x_device = XOpenDevice (backend->xdpy, device_id);
if (clutter_x11_untrap_x_errors () || x_device == NULL)
{
CLUTTER_NOTE (BACKEND, "Unable to open device %i", device_id);
return 0;
}
device_x11->xdevice = x_device;
CLUTTER_NOTE (BACKEND,
"Registering XINPUT device with XID: %li",
x_device->device_id);
/* We must go through all the classes supported by this device and
* register the appropriate events we want. Each class only appears
* once. We need to store the types with the stage since they are
* created dynamically by the server. They are not device specific.
*/
for (i = 0; i < x_device->num_classes; i++)
{
XInputClassInfo *xclass_info = x_device->classes + i;
int *button_press, *button_release, *motion_notify;
int *key_press, *key_release;
button_press =
&backend->event_types[CLUTTER_X11_XINPUT_BUTTON_PRESS_EVENT];
button_release =
&backend->event_types[CLUTTER_X11_XINPUT_BUTTON_RELEASE_EVENT];
motion_notify =
&backend->event_types[CLUTTER_X11_XINPUT_MOTION_NOTIFY_EVENT];
key_press =
&backend->event_types[CLUTTER_X11_XINPUT_KEY_PRESS_EVENT];
key_release =
&backend->event_types[CLUTTER_X11_XINPUT_KEY_RELEASE_EVENT];
switch (xclass_info->input_class)
{
/* event though XInput 1.x is broken for keyboard-like devices
* it might still be useful to track them down; the core keyboard
* will handle the right events anyway
*/
case KeyClass:
DeviceKeyPress (x_device,
*key_press,
device_x11->xevent_list[n_events]);
n_events++;
DeviceKeyRelease (x_device,
*key_release,
device_x11->xevent_list[n_events]);
n_events++;
break;
case ButtonClass:
DeviceButtonPress (x_device,
*button_press,
device_x11->xevent_list[n_events]);
n_events++;
DeviceButtonRelease (x_device,
*button_release,
device_x11->xevent_list[n_events]);
n_events++;
break;
case ValuatorClass:
DeviceMotionNotify (x_device,
*motion_notify,
device_x11->xevent_list[n_events]);
n_events++;
break;
}
}
device_x11->num_events = n_events;
#endif /* HAVE_XINPUT */
return n_events;
}
void
_clutter_input_device_x11_select_events (ClutterInputDevice *device,
ClutterBackendX11 *backend_x11,
Window xwin)
{
#if HAVE_XINPUT
ClutterInputDeviceX11 *device_x11;
device_x11 = CLUTTER_INPUT_DEVICE_X11 (device);
if (device_x11->xdevice == None || device_x11->num_events == 0)
return;
XSelectExtensionEvent (backend_x11->xdpy, xwin,
device_x11->xevent_list,
device_x11->num_events);
#endif /* HAVE_XINPUT */
}

View File

@ -0,0 +1,25 @@
#ifndef __CLUTTER_INPUT_DEVICE_X11_H__
#define __CLUTTER_INPUT_DEVICE_X11_H__
#include <clutter/clutter-input-device.h>
#include "clutter-backend-x11.h"
G_BEGIN_DECLS
#define CLUTTER_TYPE_INPUT_DEVICE_X11 (clutter_input_device_x11_get_type ())
#define CLUTTER_INPUT_DEVICE_X11(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_INPUT_DEVICE_X11, ClutterInputDeviceX11))
#define CLUTTER_IS_INPUT_DEVICE_X11(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_INPUT_DEVICE_X11))
typedef struct _ClutterInputDeviceX11 ClutterInputDeviceX11;
GType clutter_input_device_x11_get_type (void) G_GNUC_CONST;
gint _clutter_input_device_x11_construct (ClutterInputDevice *device,
ClutterBackendX11 *backend);
void _clutter_input_device_x11_select_events (ClutterInputDevice *device,
ClutterBackendX11 *backend,
Window xwin);
G_END_DECLS
#endif /* __CLUTTER_INPUT_DEVICE_X11_H__ */

View File

@ -121,14 +121,16 @@ gboolean clutter_x11_has_event_retrieval (void);
ClutterStage *clutter_x11_get_stage_from_window (Window win); ClutterStage *clutter_x11_get_stage_from_window (Window win);
#ifndef CLUTTER_DISABLE_DEPRECATED
G_CONST_RETURN GSList* clutter_x11_get_input_devices (void); G_CONST_RETURN GSList* clutter_x11_get_input_devices (void);
#endif
void clutter_x11_enable_xinput (void); void clutter_x11_enable_xinput (void);
gboolean clutter_x11_has_xinput (void); gboolean clutter_x11_has_xinput (void);
gboolean clutter_x11_has_composite_extension (void); gboolean clutter_x11_has_composite_extension (void);
void clutter_X11_set_use_argb_visual (gboolean use_argb); void clutter_x11_set_use_argb_visual (gboolean use_argb);
gboolean clutter_x11_get_use_argb_visual (void); gboolean clutter_x11_get_use_argb_visual (void);
Time clutter_x11_get_current_event_time (void); Time clutter_x11_get_current_event_time (void);

View File

@ -12,7 +12,7 @@
# - increase clutter_interface_version to the next odd number # - increase clutter_interface_version to the next odd number
m4_define([clutter_major_version], [1]) m4_define([clutter_major_version], [1])
m4_define([clutter_minor_version], [1]) m4_define([clutter_minor_version], [1])
m4_define([clutter_micro_version], [5]) m4_define([clutter_micro_version], [7])
m4_define([clutter_release_status], m4_define([clutter_release_status],
[m4_if(m4_eval(clutter_micro_version % 2), [1], [git], [m4_if(m4_eval(clutter_micro_version % 2), [1], [git],
@ -56,7 +56,7 @@ AC_CONFIG_HEADERS([config.h])
AC_CONFIG_AUX_DIR([build]) AC_CONFIG_AUX_DIR([build])
AC_CONFIG_MACRO_DIR([build/autotools]) AC_CONFIG_MACRO_DIR([build/autotools])
AM_INIT_AUTOMAKE([1.9.6 foreign]) AM_INIT_AUTOMAKE([1.9.6 foreign -Wno-portability no-define])
CLUTTER_MAJOR_VERSION=clutter_major_version CLUTTER_MAJOR_VERSION=clutter_major_version
CLUTTER_MINOR_VERSION=clutter_minor_version CLUTTER_MINOR_VERSION=clutter_minor_version
@ -157,7 +157,7 @@ AS_IF([test "x$CLUTTER_WINSYS" = "xeglnative" ||
COGL_DRIVER="gles" COGL_DRIVER="gles"
AC_DEFINE([HAVE_COGL_GLES], 1, [Have GL/ES for rendering]) AC_DEFINE([HAVE_COGL_GLES], 1, [Have GL/ES for rendering])
AC_SUBST(COGL_GLES_VERSION, [COGL_HAS_GLES1]) AC_SUBST(COGL_GLES_VERSION, [COGL_HAS_GLES1])
AC_CHECK_HEADERS([GLES/egl.h], AC_CHECK_HEADERS([GLES/egl.h],
[], [],
@ -202,7 +202,7 @@ AS_IF([test "x$CLUTTER_WINSYS" = "xeglnative" ||
use_gles2_wrapper=yes use_gles2_wrapper=yes
COGL_DRIVER="gles" COGL_DRIVER="gles"
AC_DEFINE([HAVE_COGL_GLES2], 1, [Have GL/ES for rendering]) AC_DEFINE([HAVE_COGL_GLES2], 1, [Have GL/ES for rendering])
AC_SUBST(COGL_GLES_VERSION, [COGL_HAS_GLES2]) AC_SUBST(COGL_GLES_VERSION, [COGL_HAS_GLES2])
AC_CHECK_HEADERS([EGL/egl.h], AC_CHECK_HEADERS([EGL/egl.h],
[], [],
@ -216,7 +216,7 @@ AS_IF([test "x$CLUTTER_WINSYS" = "xeglnative" ||
clutter_gl_header="GLES/gl.h" clutter_gl_header="GLES/gl.h"
COGL_DRIVER="gles" COGL_DRIVER="gles"
AC_DEFINE([HAVE_COGL_GLES], 1, [Have GL/ES for rendering]) AC_DEFINE([HAVE_COGL_GLES], 1, [Have GL/ES for rendering])
AC_SUBST(COGL_GLES_VERSION, [COGL_HAS_GLES1]) AC_SUBST(COGL_GLES_VERSION, [COGL_HAS_GLES1])
AC_CHECK_HEADERS([GLES/egl.h], AC_CHECK_HEADERS([GLES/egl.h],
[], [],
@ -364,11 +364,18 @@ AS_CASE([$CLUTTER_WINSYS],
WIN32_CFLAGS="-D_WIN32_WINNT=0x0500" WIN32_CFLAGS="-D_WIN32_WINNT=0x0500"
WIN32_LIBS="-lopengl32 -lgdi32 -lwinmm" WIN32_LIBS="-lopengl32 -lgdi32 -lwinmm"
CLUTTER_LT_LDFLAGS="$CLUTTER_LT_LDFLAGS -no-undefined" CLUTTER_LT_LDFLAGS="$CLUTTER_LT_LDFLAGS -no-undefined"
AC_CHECK_TOOL(WINDRES, windres, no)
if test "$WINDRES" = no; then
AC_MSG_ERROR([*** windres is required])
fi
], ],
[AC_MSG_ERROR([Invalid backend for Clutter: use glx, sdl, osx, win32, eglx, eglnative or fruity])] [AC_MSG_ERROR([Invalid backend for Clutter: use glx, sdl, osx, win32, eglx, eglnative or fruity])]
) )
AM_CONDITIONAL(WINSYS_WIN32, [test "x$CLUTTER_WINSYS" = "xwin32"])
# at this point we must have a GL header to check # at this point we must have a GL header to check
AS_IF([test "x$clutter_gl_header" = "x"], [AC_MSG_ERROR([Internal error: no GL header set])]) AS_IF([test "x$clutter_gl_header" = "x"], [AC_MSG_ERROR([Internal error: no GL header set])])
AC_CHECK_HEADERS([$clutter_gl_header], AC_CHECK_HEADERS([$clutter_gl_header],
@ -689,32 +696,49 @@ AS_CASE([$enable_cogl_debug],
AC_SUBST(COGL_DEBUG_CFLAGS) AC_SUBST(COGL_DEBUG_CFLAGS)
dnl === Conformance test suite ================================================
AC_ARG_ENABLE([conformance],
[AC_HELP_STRING([--enable-conformance=@<:@no/yes@:>@],
[Build conformance test suite @<:@default=yes@:>@])],
[],
[enable_conformance=yes])
AM_CONDITIONAL([BUILD_TESTS], [test "x$enable_conformance" = "xyes"])
dnl === Profiling =============================================================
m4_define([profile_default], [no]) m4_define([profile_default], [no])
AC_ARG_ENABLE(profile, AC_ARG_ENABLE(profile,
AC_HELP_STRING([--enable-profile=@<:@no/yes@:>@], AC_HELP_STRING([--enable-profile=@<:@no/yes@:>@],
[Turn on profiling support. yes; All profiling probe points are compiled in and may be runtime enabled. no; No profiling support will built into clutter. @<:@default=no@:>@]), [Turn on profiling support. yes; All profiling probe points are compiled in and may be runtime enabled. no; No profiling support will built into clutter. @<:@default=no@:>@]),
[], [],
[enable_profile=profile_default]) [enable_profile=profile_default])
AS_CASE([$enable_profile], AS_CASE([$enable_profile],
[yes], [ [yes],
if test "x$GCC" = "xyes"; then [
PKG_CHECK_MODULES([PROFILE_DEP], [uprof-0.2]) AS_IF([test "x$GCC" = "xyes"],
CLUTTER_PROFILE_CFLAGS=" -DCLUTTER_ENABLE_PROFILE -DCOGL_ENABLE_PROFILE $PROFILE_DEP_CFLAGS" [
CLUTTER_PROFILE_LDFLAGS=" $PROFILE_DEP_LIBS" PKG_CHECK_MODULES([PROFILE_DEP], [uprof-0.2])
if test "x$enable_debug" = "xyes"; then CLUTTER_PROFILE_CFLAGS=" -DCLUTTER_ENABLE_PROFILE -DCOGL_ENABLE_PROFILE $PROFILE_DEP_CFLAGS"
CLUTTER_PROFILE_CFLAGS+=" -DUPROF_DEBUG" CLUTTER_PROFILE_LDFLAGS=" $PROFILE_DEP_LIBS"
fi
else AS_IF([test "x$enable_debug" = "xyes"], [CLUTTER_PROFILE_CFLAGS+=" -DUPROF_DEBUG"])
AC_MSG_ERROR([--enable-profile is currently only supported if using GCC]) ],
fi [
], AC_MSG_ERROR([--enable-profile is currently only supported if using GCC])
[no], [ ])
CLUTTER_PROFILE_CFLAGS="" ],
CLUTTER_PROFILE_LDFLAGS=""
], [no],
[*], [AC_MSG_ERROR([Invalid value for --enable-profile])] [
CLUTTER_PROFILE_CFLAGS=""
CLUTTER_PROFILE_LDFLAGS=""
],
[*], [AC_MSG_ERROR([Invalid value for --enable-profile])]
) )
AM_CONDITIONAL(PROFILE, test "x$enable_profile" != "xno") AM_CONDITIONAL(PROFILE, test "x$enable_profile" != "xno")
AC_SUBST(CLUTTER_PROFILE_CFLAGS) AC_SUBST(CLUTTER_PROFILE_CFLAGS)
@ -735,15 +759,20 @@ and gcov)])],
AS_IF([test "x$enable_gcov" = "xyes" && test "x$GCC" = "xyes"], AS_IF([test "x$enable_gcov" = "xyes" && test "x$GCC" = "xyes"],
[ [
AS_IF([test "x$enable_conformance" = "xno"],
[AC_MSG_WARN([Conformance test suite is disabled, the coverage report will be incomplete])],
[AC_MSG_RESULT([yes])]
)
GCOV_CFLAGS="-g -O0 -fprofile-arcs -ftest-coverage" GCOV_CFLAGS="-g -O0 -fprofile-arcs -ftest-coverage"
GCOV_LDFLAGS="-lgcov" GCOV_LDFLAGS="-lgcov"
] ],
[AC_MSG_RESULT([no])]
) )
AM_CONDITIONAL([GCOV_ENABLED], [test "x$enable_gcov" = "xyes"]) AM_CONDITIONAL([GCOV_ENABLED], [test "x$enable_gcov" = "xyes"])
AC_SUBST([GCOV_CFLAGS]) AC_SUBST([GCOV_CFLAGS])
AC_SUBST([GCOV_LDFLAGS]) AC_SUBST([GCOV_LDFLAGS])
AC_MSG_RESULT([$enable_gcov])
dnl === Enable strict compiler flags ========================================== dnl === Enable strict compiler flags ==========================================
@ -849,11 +878,11 @@ GLIB_DEFINE_LOCALEDIR(LOCALEDIR)
dnl =========================================================================== dnl ===========================================================================
m4_ifdef([AM_SILENT_RULES], m4_ifdef([AM_SILENT_RULES],
[ [
AM_SILENT_RULES([yes]) AM_SILENT_RULES([yes])
use_shave=no use_shave=no
], ],
[ [
SHAVE_INIT([build/autotools], [enable]) SHAVE_INIT([build/autotools], [enable])
AC_CONFIG_FILES([ AC_CONFIG_FILES([
build/autotools/shave build/autotools/shave
@ -868,6 +897,7 @@ AC_CONFIG_FILES([
Makefile Makefile
build/Makefile build/Makefile
build/autotools/Makefile build/autotools/Makefile
build/mingw/Makefile
clutter/Makefile clutter/Makefile
clutter/clutter-version.h clutter/clutter-version.h
clutter/clutter-json.h clutter/clutter-json.h
@ -945,7 +975,7 @@ echo " Build Additional Documentation: ${enable_docs} (Generate PDF: ${en
echo "" echo ""
echo " • Extra:" echo " • Extra:"
echo " Build introspection data: ${enable_introspection}" echo " Build introspection data: ${enable_introspection}"
echo " Build test suite: ${enable_glibtest}" echo " Build conformance test suite: ${enable_conformance}"
if test "x$have_json" = "xyes"; then if test "x$have_json" = "xyes"; then
echo " JSON-GLib: system" echo " JSON-GLib: system"

View File

@ -57,7 +57,7 @@ Short explanation of the commit
Longer explanation explaining exactly what's changed, whether any Longer explanation explaining exactly what's changed, whether any
external or private interfaces changed, what bugs were fixed (with bug external or private interfaces changed, what bugs were fixed (with bug
tracker reference if applicable) and so forth. Be concise but not too tracker reference if applicable) and so forth. Be concise but not too
brief. brief. Don't be afraid of using UTF-8, or even ASCII art.
=== end example commit === === end example commit ===
Always add a brief description of the commit to the _first_ line of Always add a brief description of the commit to the _first_ line of

View File

@ -63,34 +63,35 @@ CFILE_GLOB=$(top_srcdir)/clutter/*.c \
# Header files to ignore when scanning. # Header files to ignore when scanning.
# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h # e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h
IGNORE_HFILES=\ IGNORE_HFILES=\
clutter.h \ clutter.h \
clutter-bezier.h \ clutter-bezier.h \
clutter-debug.h \ clutter-debug.h \
clutter-deprecated.h \ clutter-deprecated.h \
clutter-enum-types.h \ clutter-enum-types.h \
clutter-json.h \ clutter-id-pool.h \
clutter-keysyms.h \ clutter-json.h \
clutter-keysyms-table.h \ clutter-keysyms.h \
clutter-marshal.h \ clutter-keysyms-table.h \
clutter-master-clock.h \ clutter-marshal.h \
clutter-model-private.h \ clutter-master-clock.h \
clutter-private.h \ clutter-model-private.h \
clutter-id-pool.h \ clutter-private.h \
clutter-script-private.h \ clutter-profile.h \
clutter-stage-window.h \ clutter-script-private.h \
clutter-timeout-interval.h \ clutter-stage-window.h \
stamp-clutter-enum-types.h \ clutter-timeout-interval.h \
stamp-clutter-marshal.h \ stamp-clutter-enum-types.h \
cogl \ stamp-clutter-marshal.h \
eglnative \ cogl \
eglx \ eglnative \
fruity \ eglx \
glx \ fruity \
osx \ glx \
x11 \ osx \
json \ x11 \
pango \ json \
sdl \ pango \
sdl \
win32 win32
EXTRA_HFILES=\ EXTRA_HFILES=\

View File

@ -298,13 +298,12 @@ main (int argc, char *argv[])
ClutterBehaviour *behave; ClutterBehaviour *behave;
ClutterAlpha *alpha; ClutterAlpha *alpha;
ClutterActor *stage, *actor; ClutterActor *stage, *actor;
GdkPixbuf *pixbuf;
clutter_init (&amp;argc, &amp;argv); clutter_init (&amp;argc, &amp;argv);
stage = clutter_stage_get_default (); stage = clutter_stage_get_default ();
actor = clutter_texture_new_from_file ("ohpowers.png, NULL); actor = clutter_texture_new_from_file ("ohpowers.png", NULL);
clutter_container_add_actor (CLUTTER_CONTAINER (stage), actor); clutter_container_add_actor (CLUTTER_CONTAINER (stage), actor);
/* set up the animation to be 4 seconds long */ /* set up the animation to be 4 seconds long */
@ -315,7 +314,7 @@ main (int argc, char *argv[])
* alpha will take a reference on the timeline so we can safely * alpha will take a reference on the timeline so we can safely
* release the reference we hold * release the reference we hold
*/ */
alpha = clutter_alpha_new_full (timeline, CLUTTER_EASE_SINE_IN_OUT); alpha = clutter_alpha_new_full (timeline, CLUTTER_EASE_IN_OUT_SINE);
g_object_unref (timeline); g_object_unref (timeline);
/* the behaviour will own the alpha by sinking its floating /* the behaviour will own the alpha by sinking its floating
@ -385,9 +384,9 @@ main (int argc, char *argv[])
position and a new set of coordinates. The animation takes 200 position and a new set of coordinates. The animation takes 200
milliseconds to complete and uses a linear progression.</para> milliseconds to complete and uses a linear progression.</para>
<programlisting> <programlisting>
clutter_actor_animate (actor, CLUTTER_LINEAR, 200 clutter_actor_animate (actor, CLUTTER_LINEAR, 200,
"x", 200, "x", 200.0f,
"y", 200, "y", 200.0f,
NULL); NULL);
</programlisting> </programlisting>
</example> </example>

View File

@ -139,8 +139,11 @@
<xi:include href="xml/clutter-main.xml"/> <xi:include href="xml/clutter-main.xml"/>
<xi:include href="xml/clutter-shader.xml"/> <xi:include href="xml/clutter-shader.xml"/>
<xi:include href="xml/clutter-stage-manager.xml"/> <xi:include href="xml/clutter-stage-manager.xml"/>
<xi:include href="xml/clutter-input-device.xml"/>
<xi:include href="xml/clutter-device-manager.xml"/>
<xi:include href="xml/clutter-units.xml"/> <xi:include href="xml/clutter-units.xml"/>
<xi:include href="xml/clutter-util.xml"/> <xi:include href="xml/clutter-util.xml"/>
<xi:include href="xml/clutter-feature.xml"/>
<xi:include href="xml/clutter-version.xml"/> <xi:include href="xml/clutter-version.xml"/>
</chapter> </chapter>

View File

@ -404,6 +404,7 @@ clutter_actor_create_pango_layout
clutter_actor_is_in_clone_paint clutter_actor_is_in_clone_paint
clutter_actor_set_text_direction clutter_actor_set_text_direction
clutter_actor_get_text_direction clutter_actor_get_text_direction
clutter_actor_has_pointer
<SUBSECTION> <SUBSECTION>
ClutterActorBox ClutterActorBox
@ -874,7 +875,7 @@ clutter_param_fixed_get_type
<SECTION> <SECTION>
<FILE>clutter-feature</FILE> <FILE>clutter-feature</FILE>
<TITLE>GL Features</TITLE> <TITLE>Features</TITLE>
ClutterFeatureFlags ClutterFeatureFlags
clutter_feature_available clutter_feature_available
clutter_feature_get_all clutter_feature_get_all
@ -940,7 +941,6 @@ ClutterMotionEvent
ClutterScrollEvent ClutterScrollEvent
ClutterStageStateEvent ClutterStageStateEvent
ClutterCrossingEvent ClutterCrossingEvent
ClutterInputDevice
clutter_event_new clutter_event_new
clutter_event_copy clutter_event_copy
clutter_event_free clutter_event_free
@ -977,13 +977,9 @@ clutter_event_get_related
clutter_event_get_scroll_direction clutter_event_get_scroll_direction
<SUBSECTION> <SUBSECTION>
ClutterInputDeviceType
clutter_event_get_device clutter_event_get_device
clutter_event_get_device_id clutter_event_get_device_id
clutter_event_get_device_type clutter_event_get_device_type
clutter_get_input_device_for_id
clutter_input_device_get_device_id
clutter_input_device_get_device_type
<SUBSECTION> <SUBSECTION>
clutter_get_current_event_time clutter_get_current_event_time
@ -996,6 +992,51 @@ ClutterAnyEvent
clutter_event_get_type clutter_event_get_type
</SECTION> </SECTION>
<SECTION>
<FILE>clutter-input-device</FILE>
<TITLE>ClutterInputDevice</TITLE>
ClutterInputDeviceType
ClutterInputDevice
ClutterInputDeviceClass
clutter_input_device_get_device_id
clutter_input_device_get_device_type
clutter_input_device_get_device_name
clutter_input_device_get_device_coords
clutter_input_device_get_pointer_actor
<SUBSECTION Standard>
CLUTTER_TYPE_INPUT_DEVICE
CLUTTER_INPUT_DEVICE
CLUTTER_INPUT_DEVICE_CLASS
CLUTTER_IS_INPUT_DEVICE
CLUTTER_IS_INPUT_DEVICE_CLASS
CLUTTER_INPUT_DEVICE_GET_CLASS
<SUBSECTION Private>
clutter_input_device_get_type
</SECTION>
<SECTION>
<FILE>clutter-device-manager</FILE>
<TITLE>ClutterDeviceManager</TITLE>
ClutterDeviceManager
clutter_device_manager_get_default
clutter_device_manager_list_devices
clutter_device_manager_peek_devices
clutter_device_manager_get_device
<SUBSECTION>
clutter_get_input_device_for_id
<SUBSECTION Standard>
CLUTTER_TYPE_DEVICE_MANAGER
CLUTTER_DEVICE_MANAGER
CLUTTER_IS_DEVICE_MANAGER
<SUBSECTION Private>
clutter_device_manager_get_type
</SECTION>
<SECTION> <SECTION>
<FILE>clutter-main</FILE> <FILE>clutter-main</FILE>
<TITLE>General</TITLE> <TITLE>General</TITLE>

View File

@ -40,3 +40,5 @@ clutter_fixed_layout_get_type
clutter_bin_layout_get_type clutter_bin_layout_get_type
clutter_flow_layout_get_type clutter_flow_layout_get_type
clutter_box_layout_get_type clutter_box_layout_get_type
clutter_input_device_get_type
clutter_device_manager_get_type

View File

@ -59,14 +59,13 @@ to this OpenGL code:
<programlisting> <programlisting>
glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE); glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS); glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS); glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS);
glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_COLOR);
glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_RGB, GL_TEXTURE); glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_TEXTURE);
glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_COLOR);
</programlisting> </programlisting>
</section> </section>

View File

@ -1,13 +1,28 @@
SUBDIRS = data conform interactive micro-bench tools SUBDIRS = data interactive micro-bench tools
if BUILD_TESTS
SUBDIRS += conform
endif
DIST_SUBDIRS = data conform interactive micro-bench tools
EXTRA_DIST = README EXTRA_DIST = README
.PHONY: test conform if BUILD_TESTS
test conform: test conform:
$(MAKE) -C ./conform test ( cd ./conform && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $$?
.PHONY: test-report full-report
test-report full-report: test-report full-report:
$(MAKE) -C ./conform $(@) ( cd ./conform && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $$?
else
test conform:
@true
test-report full-report:
@true
endif # BUILD_TESTS
.PHONY: test conform test-report full-report
# run make test as part of make check # run make test as part of make check
check-local: test check-local: test

View File

@ -41,9 +41,9 @@ check_pixel (GLubyte *pixel, guint32 color)
g_print (" expected = %x, %x, %x, %x\n", g_print (" expected = %x, %x, %x, %x\n",
r, g, b, a); r, g, b, a);
/* FIXME - allow for hardware in-precision */ /* FIXME - allow for hardware in-precision */
g_assert (pixel[RED] == r); g_assert_cmpint (pixel[RED], ==, r);
g_assert (pixel[GREEN] == g); g_assert_cmpint (pixel[GREEN], ==, g);
g_assert (pixel[BLUE] == b); g_assert_cmpint (pixel[BLUE], ==, b);
/* FIXME /* FIXME
* We ignore the alpha, since we don't know if our render target is * We ignore the alpha, since we don't know if our render target is

View File

@ -180,6 +180,7 @@ test_texture_fbo (TestConformSimpleFixture *fixture,
{ {
TestState state; TestState state;
guint idle_source; guint idle_source;
gulong paint_handler;
ClutterActor *actor; ClutterActor *actor;
int ypos = 0; int ypos = 0;
@ -236,12 +237,15 @@ test_texture_fbo (TestConformSimpleFixture *fixture,
* will trigger redrawing. */ * will trigger redrawing. */
idle_source = g_idle_add (queue_redraw, state.stage); idle_source = g_idle_add (queue_redraw, state.stage);
g_signal_connect_after (state.stage, "paint", G_CALLBACK (on_paint), &state); paint_handler = g_signal_connect_after (state.stage, "paint",
G_CALLBACK (on_paint), &state);
clutter_actor_show_all (state.stage); clutter_actor_show_all (state.stage);
clutter_main (); clutter_main ();
g_signal_handler_disconnect (state.stage, paint_handler);
g_source_remove (idle_source); g_source_remove (idle_source);
/* Remove all of the actors from the stage */ /* Remove all of the actors from the stage */

View File

@ -11,15 +11,22 @@
typedef struct _TestMultiLayerMaterialState typedef struct _TestMultiLayerMaterialState
{ {
ClutterActor *group; ClutterActor *group;
CoglHandle material; CoglHandle alpha_tex;
CoglHandle alpha_tex; CoglHandle redhand_tex;
CoglHandle redhand_tex; gfloat *tex_coords;
CoglHandle light_tex0;
gfloat *tex_coords;
CoglMatrix tex_matrix; ClutterTimeline *timeline;
CoglMatrix rot_matrix;
CoglHandle material0;
CoglMatrix tex_matrix0;
CoglMatrix rot_matrix0;
CoglHandle light_tex0;
CoglHandle material1;
CoglMatrix tex_matrix1;
CoglMatrix rot_matrix1;
CoglHandle light_tex1;
} TestMultiLayerMaterialState; } TestMultiLayerMaterialState;
@ -31,10 +38,15 @@ frame_cb (ClutterTimeline *timeline,
{ {
TestMultiLayerMaterialState *state = data; TestMultiLayerMaterialState *state = data;
cogl_matrix_multiply (&state->tex_matrix, cogl_matrix_multiply (&state->tex_matrix0,
&state->tex_matrix, &state->tex_matrix0,
&state->rot_matrix); &state->rot_matrix0);
cogl_material_set_layer_matrix (state->material, 2, &state->tex_matrix); cogl_material_set_layer_matrix (state->material0, 2, &state->tex_matrix0);
cogl_matrix_multiply (&state->tex_matrix1,
&state->tex_matrix1,
&state->rot_matrix1);
cogl_material_set_layer_matrix (state->material1, 2, &state->tex_matrix1);
} }
static void static void
@ -42,18 +54,51 @@ material_rectangle_paint (ClutterActor *actor, gpointer data)
{ {
TestMultiLayerMaterialState *state = data; TestMultiLayerMaterialState *state = data;
cogl_set_source (state->material); cogl_push_matrix ();
cogl_rectangle_with_multitexture_coords (0, 0, 200, 200,
cogl_translate (150, 15, 0);
cogl_set_source (state->material0);
cogl_rectangle_with_multitexture_coords (0, 0, 200, 213,
state->tex_coords, state->tex_coords,
12); 12);
cogl_translate (-300, -30, 0);
cogl_set_source (state->material1);
cogl_rectangle_with_multitexture_coords (0, 0, 200, 213,
state->tex_coords,
12);
cogl_pop_matrix ();
}
static void
animation_completed_cb (ClutterAnimation *animation,
TestMultiLayerMaterialState *state)
{
static gboolean go_back = FALSE;
gdouble new_rotation_y;
if (go_back)
new_rotation_y = 30;
else
new_rotation_y = -30;
go_back = !go_back;
clutter_actor_animate_with_timeline (state->group,
CLUTTER_LINEAR,
state->timeline,
"rotation-angle-y", new_rotation_y,
"signal-after::completed",
animation_completed_cb, state,
NULL);
} }
G_MODULE_EXPORT int G_MODULE_EXPORT int
test_cogl_multitexture_main (int argc, char *argv[]) test_cogl_multitexture_main (int argc, char *argv[])
{ {
GError *error = NULL; GError *error = NULL;
ClutterTimeline *timeline;
ClutterBehaviour *r_behave;
ClutterActor *stage; ClutterActor *stage;
ClutterColor stage_color = { 0x61, 0x56, 0x56, 0xff }; ClutterColor stage_color = { 0x61, 0x56, 0x56, 0xff };
TestMultiLayerMaterialState *state = g_new0 (TestMultiLayerMaterialState, 1); TestMultiLayerMaterialState *state = g_new0 (TestMultiLayerMaterialState, 1);
@ -113,55 +158,71 @@ test_cogl_multitexture_main (int argc, char *argv[])
if (!state->light_tex0) if (!state->light_tex0)
g_critical ("Failed to load light0.png: %s", error->message); g_critical ("Failed to load light0.png: %s", error->message);
state->light_tex1 =
cogl_texture_new_from_file (files[2],
COGL_TEXTURE_NO_SLICING,
COGL_PIXEL_FORMAT_ANY,
&error);
if (!state->light_tex1)
g_critical ("Failed to load light0.png: %s", error->message);
g_strfreev (files); g_strfreev (files);
state->material = cogl_material_new (); state->material0 = cogl_material_new ();
cogl_material_set_layer (state->material, 0, state->alpha_tex); cogl_material_set_layer (state->material0, 0, state->alpha_tex);
cogl_material_set_layer (state->material, 1, state->redhand_tex); cogl_material_set_layer (state->material0, 1, state->redhand_tex);
cogl_material_set_layer (state->material, 2, state->light_tex0); cogl_material_set_layer (state->material0, 2, state->light_tex0);
state->material1 = cogl_material_new ();
cogl_material_set_layer (state->material1, 0, state->alpha_tex);
cogl_material_set_layer (state->material1, 1, state->redhand_tex);
cogl_material_set_layer (state->material1, 2, state->light_tex1);
state->tex_coords = tex_coords; state->tex_coords = tex_coords;
cogl_matrix_init_identity (&state->tex_matrix); cogl_matrix_init_identity (&state->tex_matrix0);
cogl_matrix_init_identity (&state->rot_matrix); cogl_matrix_init_identity (&state->tex_matrix1);
cogl_matrix_init_identity (&state->rot_matrix0);
cogl_matrix_init_identity (&state->rot_matrix1);
cogl_matrix_translate (&state->rot_matrix, 0.5, 0.5, 0); cogl_matrix_translate (&state->rot_matrix0, 0.5, 0.5, 0);
cogl_matrix_rotate (&state->rot_matrix, 10.0, 0, 0, 1.0); cogl_matrix_rotate (&state->rot_matrix0, 10.0, 0, 0, 1.0);
cogl_matrix_translate (&state->rot_matrix, -0.5, -0.5, 0); cogl_matrix_translate (&state->rot_matrix0, -0.5, -0.5, 0);
cogl_matrix_translate (&state->rot_matrix1, 0.5, 0.5, 0);
cogl_matrix_rotate (&state->rot_matrix1, -10.0, 0, 0, 1.0);
cogl_matrix_translate (&state->rot_matrix1, -0.5, -0.5, 0);
clutter_actor_set_anchor_point (state->group, 86, 125); clutter_actor_set_anchor_point (state->group, 86, 125);
clutter_container_add_actor (CLUTTER_CONTAINER(stage), clutter_container_add_actor (CLUTTER_CONTAINER(stage),
state->group); state->group);
timeline = clutter_timeline_new (7692); state->timeline = clutter_timeline_new (2812);
clutter_timeline_set_loop (timeline, TRUE);
g_signal_connect (timeline, "new-frame", G_CALLBACK (frame_cb), state); g_signal_connect (state->timeline, "new-frame", G_CALLBACK (frame_cb), state);
r_behave = clutter_actor_animate_with_timeline (state->group,
clutter_behaviour_rotate_new (clutter_alpha_new_full (timeline, CLUTTER_LINEAR,
CLUTTER_LINEAR), state->timeline,
CLUTTER_Y_AXIS, "rotation-angle-y", 30.0,
CLUTTER_ROTATE_CW, "signal-after::completed",
0.0, 360.0); animation_completed_cb, state,
NULL);
/* Apply it to our actor */
clutter_behaviour_apply (r_behave, state->group);
/* start the timeline and thus the animations */ /* start the timeline and thus the animations */
clutter_timeline_start (timeline); clutter_timeline_start (state->timeline);
clutter_actor_show_all (stage); clutter_actor_show_all (stage);
clutter_main(); clutter_main();
cogl_handle_unref (state->material); cogl_handle_unref (state->material1);
cogl_handle_unref (state->material0);
cogl_handle_unref (state->alpha_tex); cogl_handle_unref (state->alpha_tex);
cogl_handle_unref (state->redhand_tex); cogl_handle_unref (state->redhand_tex);
cogl_handle_unref (state->light_tex0); cogl_handle_unref (state->light_tex0);
cogl_handle_unref (state->light_tex1);
g_free (state); g_free (state);
g_object_unref (r_behave);
return 0; return 0;
} }

View File

@ -9,7 +9,31 @@ typedef struct {
} TestDevicesApp; } TestDevicesApp;
static const gchar *
device_type_name (ClutterInputDevice *device)
{
ClutterInputDeviceType d_type;
d_type = clutter_input_device_get_device_type (device);
switch (d_type)
{
case CLUTTER_POINTER_DEVICE:
return "Pointer";
case CLUTTER_KEYBOARD_DEVICE:
return "Keyboard";
case CLUTTER_EXTENSION_DEVICE:
return "Extension";
default:
return "Unknown";
}
g_warn_if_reached ();
return NULL;
}
static gboolean static gboolean
stage_motion_event_cb (ClutterActor *actor, stage_motion_event_cb (ClutterActor *actor,
@ -24,6 +48,11 @@ stage_motion_event_cb (ClutterActor *actor,
hand = g_hash_table_lookup (app->devices, device); hand = g_hash_table_lookup (app->devices, device);
g_print ("Device: '%s' (id:%d, type:%s)\n",
clutter_input_device_get_device_name (device),
clutter_input_device_get_device_id (device),
device_type_name (device));
if (hand != NULL) if (hand != NULL)
{ {
gfloat event_x, event_y; gfloat event_x, event_y;
@ -43,6 +72,7 @@ test_devices_main (int argc, char **argv)
ClutterActor *stage; ClutterActor *stage;
TestDevicesApp *app; TestDevicesApp *app;
ClutterColor stage_color = { 0x61, 0x64, 0x8c, 0xff }; ClutterColor stage_color = { 0x61, 0x64, 0x8c, 0xff };
ClutterDeviceManager *manager;
const GSList *stage_devices, *l; const GSList *stage_devices, *l;
/* force enabling X11 support */ /* force enabling X11 support */
@ -63,10 +93,11 @@ test_devices_main (int argc, char **argv)
clutter_actor_show_all (stage); clutter_actor_show_all (stage);
stage_devices = clutter_x11_get_input_devices (); manager = clutter_device_manager_get_default ();
stage_devices = clutter_device_manager_peek_devices (manager);
if (stage_devices == NULL) if (stage_devices == NULL)
g_error ("No extended input devices found."); g_error ("No input devices found.");
for (l = stage_devices; l != NULL; l = l->next) for (l = stage_devices; l != NULL; l = l->next)
{ {
@ -74,12 +105,15 @@ test_devices_main (int argc, char **argv)
ClutterInputDeviceType device_type; ClutterInputDeviceType device_type;
ClutterActor *hand = NULL; ClutterActor *hand = NULL;
device_type = clutter_input_device_get_device_type (device); g_print ("got a %s device '%s' with id %d...\n",
if (device_type == CLUTTER_POINTER_DEVICE) device_type_name (device),
{ clutter_input_device_get_device_name (device),
g_print ("got a pointer device with id %d...\n", clutter_input_device_get_device_id (device));
clutter_input_device_get_device_id (device));
device_type = clutter_input_device_get_device_type (device);
if (device_type == CLUTTER_POINTER_DEVICE ||
device_type == CLUTTER_EXTENSION_DEVICE)
{
hand = clutter_texture_new_from_file (TESTS_DATADIR hand = clutter_texture_new_from_file (TESTS_DATADIR
G_DIR_SEPARATOR_S G_DIR_SEPARATOR_S
"redhand.png", "redhand.png",

View File

@ -48,6 +48,8 @@ static gboolean recenter = FALSE;
static ClutterActor *main_stage = NULL; static ClutterActor *main_stage = NULL;
static ClutterActor *easing_mode_label = NULL; static ClutterActor *easing_mode_label = NULL;
static ClutterAnimation *last_animation = NULL;
static void static void
on_animation_completed (ClutterAnimation *animation, on_animation_completed (ClutterAnimation *animation,
ClutterActor *rectangle) ClutterActor *rectangle)
@ -113,10 +115,12 @@ on_button_press (ClutterActor *actor,
"y", event->y, "y", event->y,
NULL); NULL);
if (recenter) if (recenter && last_animation != animation)
g_signal_connect_after (animation, "completed", g_signal_connect_after (animation, "completed",
G_CALLBACK (on_animation_completed), G_CALLBACK (on_animation_completed),
rectangle); rectangle);
last_animation = animation;
} }
return TRUE; return TRUE;

View File

@ -27,6 +27,12 @@ get_event_type_name (const ClutterEvent *event)
case CLUTTER_LEAVE: case CLUTTER_LEAVE:
return "LEAVE"; return "LEAVE";
case CLUTTER_MOTION:
return "MOTION";
case CLUTTER_DELETE:
return "DELETE";
default: default:
return "EVENT"; return "EVENT";
} }
@ -124,94 +130,122 @@ fill_keybuf (char *keybuf, ClutterKeyEvent *event)
/* printable character, if any (ß, ∑) */ /* printable character, if any (ß, ∑) */
len = g_unichar_to_utf8 (event->unicode_value, utf8); len = g_unichar_to_utf8 (event->unicode_value, utf8);
utf8[len] = '\0'; utf8[len] = '\0';
sprintf(keybuf, "'%s' ", utf8); sprintf (keybuf, "'%s' ", utf8);
/* key combination (<Mod1>s, <Shift><Mod1>S, <Ctrl><Mod1>Delete) */ /* key combination (<Mod1>s, <Shift><Mod1>S, <Ctrl><Mod1>Delete) */
len = g_unichar_to_utf8 (clutter_keysym_to_unicode (event->keyval), len = g_unichar_to_utf8 (clutter_keysym_to_unicode (event->keyval), utf8);
utf8);
utf8[len] = '\0'; utf8[len] = '\0';
if (event->modifier_state & CLUTTER_SHIFT_MASK) if (event->modifier_state & CLUTTER_SHIFT_MASK)
strcat (keybuf, "<Shift>"); strcat (keybuf, "<Shift>");
if (event->modifier_state & CLUTTER_LOCK_MASK) if (event->modifier_state & CLUTTER_LOCK_MASK)
strcat (keybuf, "<Lock>"); strcat (keybuf, "<Lock>");
if (event->modifier_state & CLUTTER_CONTROL_MASK) if (event->modifier_state & CLUTTER_CONTROL_MASK)
strcat (keybuf, "<Control>"); strcat (keybuf, "<Control>");
if (event->modifier_state & CLUTTER_MOD1_MASK) if (event->modifier_state & CLUTTER_MOD1_MASK)
strcat (keybuf, "<Mod1>"); strcat (keybuf, "<Mod1>");
if (event->modifier_state & CLUTTER_MOD2_MASK) if (event->modifier_state & CLUTTER_MOD2_MASK)
strcat (keybuf, "<Mod2>"); strcat (keybuf, "<Mod2>");
if (event->modifier_state & CLUTTER_MOD3_MASK) if (event->modifier_state & CLUTTER_MOD3_MASK)
strcat (keybuf, "<Mod3>"); strcat (keybuf, "<Mod3>");
if (event->modifier_state & CLUTTER_MOD4_MASK) if (event->modifier_state & CLUTTER_MOD4_MASK)
strcat (keybuf, "<Mod4>"); strcat (keybuf, "<Mod4>");
if (event->modifier_state & CLUTTER_MOD5_MASK) if (event->modifier_state & CLUTTER_MOD5_MASK)
strcat (keybuf, "<Mod5>"); strcat (keybuf, "<Mod5>");
strcat (keybuf, utf8); strcat (keybuf, utf8);
} }
static gboolean static gboolean
input_cb (ClutterActor *actor, input_cb (ClutterActor *actor,
ClutterEvent *event, ClutterEvent *event,
gpointer data) gpointer data)
{ {
ClutterStage *stage = CLUTTER_STAGE (clutter_stage_get_default ()); ClutterStage *stage = CLUTTER_STAGE (clutter_stage_get_default ());
gchar keybuf[128], *source = (gchar*)data; ClutterActor *source_actor = clutter_event_get_source (event);
gchar keybuf[128];
switch (event->type) switch (event->type)
{ {
case CLUTTER_KEY_PRESS: case CLUTTER_KEY_PRESS:
fill_keybuf (keybuf, &event->key); fill_keybuf (keybuf, &event->key);
printf ("[%s] KEY PRESS %s", source, keybuf); printf ("[%s] KEY PRESS %s",
clutter_actor_get_name (source_actor),
keybuf);
break; break;
case CLUTTER_KEY_RELEASE: case CLUTTER_KEY_RELEASE:
fill_keybuf (keybuf, &event->key); fill_keybuf (keybuf, &event->key);
printf ("[%s] KEY RELEASE %s", source, keybuf); printf ("[%s] KEY RELEASE %s",
clutter_actor_get_name (source_actor),
keybuf);
break; break;
case CLUTTER_MOTION: case CLUTTER_MOTION:
g_print ("[%s] MOTION", source); g_print ("[%s] MOTION",
clutter_actor_get_name (source_actor));
break; break;
case CLUTTER_ENTER: case CLUTTER_ENTER:
g_print ("[%s] ENTER", source); g_print ("[%s] ENTER (from:%s)",
clutter_actor_get_name (source_actor),
clutter_event_get_related (event) != NULL
? clutter_actor_get_name (clutter_event_get_related (event))
: "<out of stage>");
break; break;
case CLUTTER_LEAVE: case CLUTTER_LEAVE:
g_print ("[%s] LEAVE", source); g_print ("[%s] LEAVE (to:%s)",
clutter_actor_get_name (source_actor),
clutter_event_get_related (event) != NULL
? clutter_actor_get_name (clutter_event_get_related (event))
: "<out of stage>");
break; break;
case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_PRESS:
g_print ("[%s] BUTTON PRESS (click count:%i)", g_print ("[%s] BUTTON PRESS (button:%i, click count:%i)",
source, event->button.click_count); clutter_actor_get_name (source_actor),
clutter_event_get_button (event),
clutter_event_get_click_count (event));
break; break;
case CLUTTER_BUTTON_RELEASE: case CLUTTER_BUTTON_RELEASE:
g_print ("[%s] BUTTON RELEASE (click count:%i)", g_print ("[%s] BUTTON RELEASE (button:%i, click count:%i)",
source, event->button.click_count); clutter_actor_get_name (source_actor),
clutter_event_get_button (event),
clutter_event_get_click_count (event));
if (clutter_event_get_source (event) == CLUTTER_ACTOR (stage)) if (source_actor == CLUTTER_ACTOR (stage))
clutter_stage_set_key_focus (stage, NULL); clutter_stage_set_key_focus (stage, NULL);
else if (clutter_event_get_source (event) == actor else if (source_actor == actor &&
&& clutter_actor_get_parent (actor) == CLUTTER_ACTOR (stage)) clutter_actor_get_parent (actor) == CLUTTER_ACTOR (stage))
clutter_stage_set_key_focus (stage, actor); clutter_stage_set_key_focus (stage, actor);
break; break;
case CLUTTER_SCROLL: case CLUTTER_SCROLL:
g_print ("[%s] BUTTON SCROLL (click count:%i)", g_print ("[%s] BUTTON SCROLL (direction:%s)",
source, event->button.click_count); clutter_actor_get_name (source_actor),
clutter_event_get_scroll_direction (event) == CLUTTER_SCROLL_UP
? "up"
: "down");
break; break;
case CLUTTER_STAGE_STATE: case CLUTTER_STAGE_STATE:
g_print ("[%s] STAGE STATE", source); g_print ("[%s] STAGE STATE", clutter_actor_get_name (source_actor));
break; break;
case CLUTTER_DESTROY_NOTIFY: case CLUTTER_DESTROY_NOTIFY:
g_print ("[%s] DESTROY NOTIFY", source); g_print ("[%s] DESTROY NOTIFY", clutter_actor_get_name (source_actor));
break; break;
case CLUTTER_CLIENT_MESSAGE: case CLUTTER_CLIENT_MESSAGE:
g_print ("[%s] CLIENT MESSAGE", source); g_print ("[%s] CLIENT MESSAGE", clutter_actor_get_name (source_actor));
break; break;
case CLUTTER_DELETE: case CLUTTER_DELETE:
g_print ("[%s] DELETE", source); g_print ("[%s] DELETE", clutter_actor_get_name (source_actor));
break; break;
case CLUTTER_NOTHING: case CLUTTER_NOTHING:
return FALSE; return FALSE;
} }
if (clutter_event_get_source (event) == actor) if (source_actor == actor)
g_print (" *source*"); g_print (" *source*");
g_print ("\n"); g_print ("\n");
@ -234,8 +268,8 @@ test_events_main (int argc, char *argv[])
stage = clutter_stage_get_default (); stage = clutter_stage_get_default ();
clutter_actor_set_name (stage, "Stage");
g_signal_connect (stage, "event", G_CALLBACK (input_cb), "stage"); g_signal_connect (stage, "event", G_CALLBACK (input_cb), "stage");
g_signal_connect (stage, "fullscreen", g_signal_connect (stage, "fullscreen",
G_CALLBACK (stage_state_cb), "fullscreen"); G_CALLBACK (stage_state_cb), "fullscreen");
g_signal_connect (stage, "unfullscreen", g_signal_connect (stage, "unfullscreen",
@ -244,24 +278,21 @@ test_events_main (int argc, char *argv[])
G_CALLBACK (stage_state_cb), "activate"); G_CALLBACK (stage_state_cb), "activate");
g_signal_connect (stage, "deactivate", g_signal_connect (stage, "deactivate",
G_CALLBACK (stage_state_cb), "deactivate"); G_CALLBACK (stage_state_cb), "deactivate");
/*g_signal_connect (stage, "captured-event", G_CALLBACK (capture_cb), NULL);*/
g_signal_connect (stage, "captured-event", G_CALLBACK (capture_cb), NULL);
focus_box = clutter_rectangle_new_with_color (&ncol); focus_box = clutter_rectangle_new_with_color (&ncol);
clutter_actor_set_name (focus_box, "Focus Box");
clutter_container_add (CLUTTER_CONTAINER(stage), focus_box, NULL); clutter_container_add (CLUTTER_CONTAINER(stage), focus_box, NULL);
actor = clutter_rectangle_new_with_color (&rcol); actor = clutter_rectangle_new_with_color (&rcol);
clutter_actor_set_name (actor, "Red Box");
clutter_actor_set_size (actor, 100, 100); clutter_actor_set_size (actor, 100, 100);
clutter_actor_set_position (actor, 100, 100); clutter_actor_set_position (actor, 100, 100);
clutter_actor_set_reactive (actor, TRUE); clutter_actor_set_reactive (actor, TRUE);
clutter_container_add (CLUTTER_CONTAINER (stage), actor, NULL); clutter_container_add (CLUTTER_CONTAINER (stage), actor, NULL);
g_signal_connect (actor, "event", G_CALLBACK (input_cb), "red box"); g_signal_connect (actor, "event", G_CALLBACK (input_cb), "red box");
g_signal_connect (actor, "key-focus-in", G_CALLBACK (key_focus_in_cb), g_signal_connect (actor, "key-focus-in", G_CALLBACK (key_focus_in_cb),
focus_box); focus_box);
/* Toggle motion - enter/leave capture */ /* Toggle motion - enter/leave capture */
g_signal_connect (actor, "button-press-event", g_signal_connect (actor, "button-press-event",
G_CALLBACK (red_button_cb), NULL); G_CALLBACK (red_button_cb), NULL);
@ -269,27 +300,22 @@ test_events_main (int argc, char *argv[])
clutter_stage_set_key_focus (CLUTTER_STAGE (stage), actor); clutter_stage_set_key_focus (CLUTTER_STAGE (stage), actor);
actor = clutter_rectangle_new_with_color (&gcol); actor = clutter_rectangle_new_with_color (&gcol);
clutter_actor_set_name (actor, "Green Box");
clutter_actor_set_size (actor, 100, 100); clutter_actor_set_size (actor, 100, 100);
clutter_actor_set_position (actor, 250, 100); clutter_actor_set_position (actor, 250, 100);
clutter_actor_set_reactive (actor, TRUE); clutter_actor_set_reactive (actor, TRUE);
clutter_container_add (CLUTTER_CONTAINER (stage), actor, NULL); clutter_container_add (CLUTTER_CONTAINER (stage), actor, NULL);
g_signal_connect (actor, "event", G_CALLBACK (input_cb), "green box"); g_signal_connect (actor, "event", G_CALLBACK (input_cb), "green box");
g_signal_connect (actor, "key-focus-in", G_CALLBACK (key_focus_in_cb), g_signal_connect (actor, "key-focus-in", G_CALLBACK (key_focus_in_cb),
focus_box); focus_box);
g_signal_connect (actor, "captured-event", G_CALLBACK (capture_cb), NULL); g_signal_connect (actor, "captured-event", G_CALLBACK (capture_cb), NULL);
actor = clutter_rectangle_new_with_color (&bcol); actor = clutter_rectangle_new_with_color (&bcol);
clutter_actor_set_name (actor, "Blue Box");
clutter_actor_set_size (actor, 100, 100); clutter_actor_set_size (actor, 100, 100);
clutter_actor_set_position (actor, 400, 100); clutter_actor_set_position (actor, 400, 100);
clutter_actor_set_reactive (actor, TRUE); clutter_actor_set_reactive (actor, TRUE);
clutter_container_add (CLUTTER_CONTAINER(stage), actor, NULL); clutter_container_add (CLUTTER_CONTAINER(stage), actor, NULL);
g_signal_connect (actor, "event", G_CALLBACK (input_cb), "blue box"); g_signal_connect (actor, "event", G_CALLBACK (input_cb), "blue box");
g_signal_connect (actor, "key-focus-in", G_CALLBACK (key_focus_in_cb), g_signal_connect (actor, "key-focus-in", G_CALLBACK (key_focus_in_cb),
focus_box); focus_box);
@ -299,20 +325,19 @@ test_events_main (int argc, char *argv[])
/* non reactive */ /* non reactive */
actor = clutter_rectangle_new_with_color (&ncol); actor = clutter_rectangle_new_with_color (&ncol);
clutter_actor_set_name (actor, "Black Box");
clutter_actor_set_size (actor, 400, 50); clutter_actor_set_size (actor, 400, 50);
clutter_actor_set_position (actor, 100, 250); clutter_actor_set_position (actor, 100, 250);
clutter_container_add (CLUTTER_CONTAINER(stage), actor, NULL); clutter_container_add (CLUTTER_CONTAINER(stage), actor, NULL);
g_signal_connect (actor, "event", G_CALLBACK (input_cb), "blue box"); g_signal_connect (actor, "event", G_CALLBACK (input_cb), "blue box");
g_signal_connect (actor, "key-focus-in", G_CALLBACK (key_focus_in_cb), g_signal_connect (actor, "key-focus-in", G_CALLBACK (key_focus_in_cb),
focus_box); focus_box);
g_signal_connect (stage, "key-focus-in", G_CALLBACK (key_focus_in_cb), g_signal_connect (stage, "key-focus-in", G_CALLBACK (key_focus_in_cb),
focus_box); focus_box);
/* non reactive group, with reactive child */ /* non reactive group, with reactive child */
actor = clutter_rectangle_new_with_color (&ycol); actor = clutter_rectangle_new_with_color (&ycol);
clutter_actor_set_name (actor, "Yellow Box");
clutter_actor_set_size (actor, 100, 100); clutter_actor_set_size (actor, 100, 100);
clutter_actor_set_reactive (actor, TRUE); clutter_actor_set_reactive (actor, TRUE);