diff --git a/.gitignore b/.gitignore index 715337746..04932be34 100644 --- a/.gitignore +++ b/.gitignore @@ -240,6 +240,7 @@ TAGS /tests/conform/test-script-named-object /tests/conform/test-actor-destruction /tests/conform/test-color-operators +/tests/conform/test-cogl-texture-mipmaps /tests/micro-bench/test-text-perf /tests/micro-bench/test-text /tests/micro-bench/test-picking diff --git a/NEWS b/NEWS index 5b2961e40..db795a8f8 100644 --- a/NEWS +++ b/NEWS @@ -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) =============================================================================== diff --git a/README b/README index 46674a119..5acdbd381 100644 --- a/README +++ b/README @@ -1,19 +1,25 @@ Clutter - README =============================================================================== -Clutter is an open source software library for creating fast, visually -rich and animated graphical user interfaces. +Clutter is an open source software library for creating portable, fast, +visually rich and animated graphical user interfaces. Clutter currently requires: • GLib >= 2.16.0 • Cairo >= 1.6 • 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 • JSON-GLib >= 0.8 (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: • 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: - • UProf 0.2 available from git://git.moblin.org/uprof.git + • UProf >= 0.2 -The official website is: - http://www.clutter-project.org +UProf is available from: -The Clutter blog is at - http://www.clutter-project.org/blog + git://git.moblin.org/uprof.git + +------------------------------------------------------------------------------- + +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 The official mailing list archive is: + http://lists.o-hand.com/clutter/ New bug page on Bugzilla: + http://bugzilla.o-hand.com/enter_bug.cgi?product=Clutter Clutter is licensed under the terms of the GNU Lesser General Public 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] Controls Clutter debugging level: @@ -55,7 +84,7 @@ See the INSTALL file. Info on specific Clutter options; yes: All GLib asserts, checks and support for runtime Clutter debugging notes through CLUTTER_DEBUG. This is the default - value for snapshots. + value for developers snapshots. minimum: Just GType cast checks and support for runtime Clutter @@ -65,35 +94,47 @@ See the INSTALL file. Info on specific Clutter options; no: No GLib asserts or checks and no support for runtime Clutter 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] Controls COGL debugging level (default=minimum): yes: 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: Support for COGL debugging notes through COGL_DEBUG. This is - the default for snapshots. + the default for developers snapshots. no: Disable support for COGL runtime debugging notes. This is the default for stable releases. --enable-maintainer-flags=[no/yes] - Use strict compiler flags. This defaults to 'yes' for snapshots and - to 'no' for stable releases. + Use strict compiler flags. This defaults to 'yes' for developers + snapshots and to 'no' for stable releases. --enable-gtk-doc use gtk-doc to build API documentation (default=no). Requires gtk-doc present on the target system. --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. + --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] 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 system: - Only use the system copy of JSON-GLib + Only use the system copy of JSON-GLib and warn if not found VERSIONING =============================================================================== @@ -167,22 +208,23 @@ the newly added API might still change. The micro version indicates the origin of the release: even micro numbers are only used for released archives; odd micro numbers are -only used on the SVN repository. +only used on the Git repository. HACKING =============================================================================== -If you want to hack on and improve Clutter, check the contained TODO -file for pending tasks, the HACKING file for general implementation guidelines, -HACKING.backends for backend-specific implementation issues. The CODING_STYLE -file contains the rules for writing code conformant to the style guidelines -used throughout Clutter. Remember: the coding style is mandatory; patches -not conforming to it will be rejected. +If you want to hack on and improve Clutter, check the contained TODO file for +pending tasks, the HACKING file for general implementation guidelines, and the +HACKING.backends for backend-specific implementation issues. + +The CODING_STYLE file contains the rules for writing code conformant to the +style guidelines used throughout Clutter. Remember: the coding style is +mandatory; patches not conforming to it will be rejected. 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 @@ -192,7 +234,7 @@ In the report you should include: • what system you're running Clutter on; • 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 glxinfo and xdpyinfo (if applicable); • how to reproduce the bug. @@ -235,7 +277,8 @@ RELEASE NOTES =============================================================================== 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 ------------------------------------------------------------------------------- @@ -342,7 +385,7 @@ Release Notes for Clutter 1.0 correct HLS intervals: Hue: [ 0, 360 ) - Luminance: [ 0, 1] + Luminance: [ 0, 1 ] Saturation: [ 0, 1 ] * 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: ClutterContainer::actor-added and ClutterContainer::actor-removed should be used instead. - -$LastChangedDate$ diff --git a/build/Makefile.am b/build/Makefile.am index 96b39e1ba..d8669fd29 100644 --- a/build/Makefile.am +++ b/build/Makefile.am @@ -1,3 +1,3 @@ -SUBDIRS = autotools +SUBDIRS = autotools mingw EXTRA_DIST = gen-gcov.pl diff --git a/build/mingw/Makefile.am b/build/mingw/Makefile.am new file mode 100644 index 000000000..69195f791 --- /dev/null +++ b/build/mingw/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST = README mingw-cross-compile.sh diff --git a/clutter/Makefile.am b/clutter/Makefile.am index 401b3e5e9..d8fb48fd0 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -20,6 +20,23 @@ DISTCLEANFILES = EXTRA_DIST = 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 = \ -I$(top_srcdir) \ -I$(top_srcdir)/clutter/cogl \ @@ -65,8 +82,9 @@ source_h = \ $(srcdir)/clutter-child-meta.h \ $(srcdir)/clutter-clone.h \ $(srcdir)/clutter-color.h \ - $(srcdir)/clutter-container.h \ - $(srcdir)/clutter-deprecated.h \ + $(srcdir)/clutter-container.h \ + $(srcdir)/clutter-deprecated.h \ + $(srcdir)/clutter-device-manager.h \ $(srcdir)/clutter-event.h \ $(srcdir)/clutter-feature.h \ $(srcdir)/clutter-fixed.h \ @@ -74,6 +92,7 @@ source_h = \ $(srcdir)/clutter-flow-layout.h \ $(srcdir)/clutter-frame-source.h \ $(srcdir)/clutter-group.h \ + $(srcdir)/clutter-input-device.h \ $(srcdir)/clutter-interval.h \ $(srcdir)/clutter-keysyms.h \ $(srcdir)/clutter-layout-manager.h \ @@ -137,6 +156,7 @@ source_c = \ $(srcdir)/clutter-clone.c \ $(srcdir)/clutter-color.c \ $(srcdir)/clutter-container.c \ + $(srcdir)/clutter-device-manager.c \ clutter-enum-types.c \ $(srcdir)/clutter-event.c \ $(srcdir)/clutter-feature.c \ @@ -145,6 +165,7 @@ source_c = \ $(srcdir)/clutter-flow-layout.c \ $(srcdir)/clutter-frame-source.c \ $(srcdir)/clutter-group.c \ + $(srcdir)/clutter-input-device.c \ $(srcdir)/clutter-interval.c \ $(srcdir)/clutter-layout-manager.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/$(CLUTTER_WINSYS)/libclutter-$(CLUTTER_WINSYS).la \ $(clutter_json_dep) \ - $(CLUTTER_WINSYS_BASE_LIB) + $(CLUTTER_WINSYS_BASE_LIB) \ + $(win32_resources) libclutter_@CLUTTER_WINSYS@_@CLUTTER_API_VERSION@_la_SOURCES = \ $(source_c) \ @@ -219,6 +241,7 @@ libclutter_@CLUTTER_WINSYS@_@CLUTTER_API_VERSION@_la_LDFLAGS = \ -export-dynamic \ -export-symbols-regex "^(clutter|cogl|json).*" \ -rpath $(libdir) \ + $(win32_resources_ldflag) \ $(NULL) lib_LTLIBRARIES = $(CLUTTER_WINSYS_LIB) diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index c873236b4..2419ab554 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -308,6 +308,7 @@ struct _ClutterActorPrivate guint clip_to_allocation : 1; guint enable_model_view_transform : 1; guint enable_paint_unmapped : 1; + guint has_pointer : 1; gfloat clip[4]; @@ -429,7 +430,8 @@ enum PROP_SHOW_ON_SET_PARENT, - PROP_TEXT_DIRECTION + PROP_TEXT_DIRECTION, + PROP_HAS_POINTER }; enum @@ -2581,7 +2583,7 @@ clutter_actor_set_property (GObject *object, break; case PROP_OPACITY: - clutter_actor_set_opacity (actor, g_value_get_uchar (value)); + clutter_actor_set_opacity (actor, g_value_get_uint (value)); break; case PROP_NAME: @@ -2864,7 +2866,7 @@ clutter_actor_get_property (GObject *object, break; case PROP_OPACITY: - g_value_set_uchar (value, priv->opacity); + g_value_set_uint (value, priv->opacity); break; case PROP_NAME: @@ -3031,6 +3033,10 @@ clutter_actor_get_property (GObject *object, g_value_set_enum (value, priv->text_direction); break; + case PROP_HAS_POINTER: + g_value_set_boolean (value, priv->has_pointer); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -3462,15 +3468,15 @@ clutter_actor_class_init (ClutterActorClass *klass) /** * ClutterActor:opacity: * - * Opacity of the actor, between 0 (fully transparent) and + * Opacity of an actor, between 0 (fully transparent) and * 255 (fully opaque) */ - pspec = g_param_spec_uchar ("opacity", - "Opacity", - "Opacity of actor", - 0, 255, - 255, - CLUTTER_PARAM_READWRITE); + pspec = g_param_spec_uint ("opacity", + "Opacity", + "Opacity of an actor", + 0, 255, + 255, + CLUTTER_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_OPACITY, pspec); /** @@ -3858,6 +3864,24 @@ clutter_actor_class_init (ClutterActorClass *klass) PROP_TEXT_DIRECTION, 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: * @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: * @self: a #ClutterActor @@ -9748,3 +9786,23 @@ clutter_actor_pop_internal (void) 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; +} diff --git a/clutter/clutter-actor.h b/clutter/clutter-actor.h index 974cc7ee1..acb160361 100644 --- a/clutter/clutter-actor.h +++ b/clutter/clutter-actor.h @@ -532,6 +532,7 @@ void clutter_actor_get_transformation_matrix (ClutterActor *self CoglMatrix *matrix); gboolean clutter_actor_is_in_clone_paint (ClutterActor *self); +gboolean clutter_actor_has_pointer (ClutterActor *self); void clutter_actor_set_text_direction (ClutterActor *self, ClutterTextDirection text_dir); diff --git a/clutter/clutter-animation.c b/clutter/clutter-animation.c index d943c3d4e..7139e77e6 100644 --- a/clutter/clutter-animation.c +++ b/clutter/clutter-animation.c @@ -201,7 +201,10 @@ on_actor_dispose (gpointer user_data, ClutterAnimation *self = user_data; 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); /* 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); 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); 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_weak_unref (priv->object, on_actor_dispose, self); + + CLUTTER_NOTE (ANIMATION, "Releasing the reference Animation [%p]", + animation); g_object_unref (animation); } } @@ -256,7 +266,9 @@ clutter_animation_finalize (GObject *gobject) { 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_OBJECT_CLASS (clutter_animation_parent_class)->finalize (gobject); @@ -444,7 +456,7 @@ clutter_animation_class_init (ClutterAnimationClass *klass) gobject_class->finalize = clutter_animation_finalize; /** - * ClutterAnimation:objct: + * ClutterAnimation:object: * * The #GObject to which the animation applies. * @@ -629,6 +641,7 @@ clutter_animation_validate_bind (ClutterAnimation *animation, ClutterAnimationPrivate *priv; GObjectClass *klass; GParamSpec *pspec; + GType pspec_type; priv = animation->priv; @@ -667,14 +680,17 @@ clutter_animation_validate_bind (ClutterAnimation *animation, 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 " "type '%s' is not compatible with the property value " "of type '%s'", property_name, g_type_name (argtype), - g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec))); + g_type_name (pspec_type)); return NULL; } @@ -846,6 +862,7 @@ clutter_animation_update_interval (ClutterAnimation *animation, ClutterAnimationPrivate *priv; GObjectClass *klass; GParamSpec *pspec; + GType pspec_type, int_type; g_return_if_fail (CLUTTER_IS_ANIMATION (animation)); g_return_if_fail (property_name != NULL); @@ -872,15 +889,18 @@ clutter_animation_update_interval (ClutterAnimation *animation, return; } - if (!g_value_type_compatible (G_PARAM_SPEC_VALUE_TYPE (pspec), - clutter_interval_get_value_type (interval))) + pspec_type = G_PARAM_SPEC_VALUE_TYPE (pspec); + 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 " "type '%s' is not compatible with the property value " "of type '%s'", property_name, - g_type_name (clutter_interval_get_value_type (interval)), - g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec))); + g_type_name (int_type), + g_type_name (pspec_type)); return; } @@ -906,6 +926,7 @@ clutter_animation_update (ClutterAnimation *animation, { ClutterAnimationPrivate *priv; ClutterInterval *interval; + GType int_type; g_return_val_if_fail (CLUTTER_IS_ANIMATION (animation), NULL); g_return_val_if_fail (property_name != NULL, NULL); @@ -923,14 +944,16 @@ clutter_animation_update (ClutterAnimation *animation, return NULL; } - if (!g_value_type_compatible (G_VALUE_TYPE (final), - clutter_interval_get_value_type (interval))) + int_type = 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 " "type '%s' is not compatible with the property value " "of type '%s'", property_name, - g_type_name (clutter_interval_get_value_type (interval)), + g_type_name (int_type), g_type_name (G_VALUE_TYPE (final))); return NULL; } @@ -1221,6 +1244,8 @@ clutter_animation_set_mode (ClutterAnimation *animation, g_object_freeze_notify (G_OBJECT (animation)); alpha = clutter_animation_get_alpha_internal (animation); + g_assert (CLUTTER_IS_ALPHA (alpha)); + clutter_alpha_set_mode (alpha, 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)); timeline = clutter_animation_get_timeline_internal (animation); + g_assert (CLUTTER_IS_TIMELINE (timeline)); + clutter_timeline_set_duration (timeline, msecs); 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_value_type_compatible (G_VALUE_TYPE (value), - G_VALUE_TYPE (&real_value)) && - !g_value_type_compatible (G_VALUE_TYPE (&real_value), - G_VALUE_TYPE (value))) + /* are these two types compatible (can be directly copied)? */ + if (g_value_type_compatible (G_VALUE_TYPE (value), + G_VALUE_TYPE (&real_value))) { - 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; + g_value_copy (value, &real_value); + goto done; } - 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", - G_STRLOC, - g_type_name (G_VALUE_TYPE (value)), - g_type_name (G_VALUE_TYPE (&real_value))); - g_value_unset (&real_value); - return; + if (g_value_transform (value, &real_value)) + goto done; } + + /* 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 g_value_copy (value, &real_value); +done: /* create an interval and bind it to the property, in case * it's not a fixed property, otherwise just set it */ diff --git a/clutter/clutter-device-manager.c b/clutter/clutter-device-manager.c new file mode 100644 index 000000000..9d5110f94 --- /dev/null +++ b/clutter/clutter-device-manager.c @@ -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 . + * + * Author: Emmanuele Bassi + */ + +/** + * 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 #ClutterInputDevices. + * + * 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 #ClutterInputDevices 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 #ClutterInputDevices + * 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); + } +} diff --git a/clutter/clutter-device-manager.h b/clutter/clutter-device-manager.h new file mode 100644 index 000000000..e46850e26 --- /dev/null +++ b/clutter/clutter-device-manager.h @@ -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 . + * + * Author: Emmanuele Bassi + */ + +#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __CLUTTER_DEVICE_MANAGER_H__ +#define __CLUTTER_DEVICE_MANAGER_H__ + +#include +#include + +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__ */ diff --git a/clutter/clutter-event.c b/clutter/clutter-event.c index 2f9cf178a..2492a25a3 100644 --- a/clutter/clutter-event.c +++ b/clutter/clutter-event.c @@ -787,39 +787,3 @@ clutter_get_current_event (void) 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; -} diff --git a/clutter/clutter-event.h b/clutter/clutter-event.h index 03d91549c..947536461 100644 --- a/clutter/clutter-event.h +++ b/clutter/clutter-event.h @@ -29,6 +29,7 @@ #define __CLUTTER_EVENT_H__ #include +#include #include #define CLUTTER_TYPE_EVENT (clutter_event_get_type ()) @@ -201,37 +202,6 @@ typedef struct _ClutterScrollEvent ClutterScrollEvent; typedef struct _ClutterStageStateEvent ClutterStageStateEvent; 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: * @type: event type @@ -509,9 +479,6 @@ guint32 clutter_keysym_to_unicode (guint k guint32 clutter_get_current_event_time (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 #endif /* __CLUTTER_EVENT_H__ */ diff --git a/clutter/clutter-feature.c b/clutter/clutter-feature.c index 745a4a8d1..b181c52cb 100644 --- a/clutter/clutter-feature.c +++ b/clutter/clutter-feature.c @@ -25,9 +25,16 @@ /** * 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 diff --git a/clutter/clutter-feature.h b/clutter/clutter-feature.h index 864606634..cd65edf18 100644 --- a/clutter/clutter-feature.h +++ b/clutter/clutter-feature.h @@ -25,14 +25,6 @@ #error "Only can be included directly." #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__ #define __CLUTTER_FEATURE_H__ diff --git a/clutter/clutter-frame-source.c b/clutter/clutter-frame-source.c index 4a2d65310..7ce7b7060 100644 --- a/clutter/clutter-frame-source.c +++ b/clutter/clutter-frame-source.c @@ -39,24 +39,25 @@ struct _ClutterFrameSource ClutterTimeoutInterval timeout; }; -static gboolean clutter_frame_source_prepare (GSource *source, gint *timeout); -static gboolean clutter_frame_source_check (GSource *source); -static gboolean clutter_frame_source_dispatch (GSource *source, - GSourceFunc callback, - gpointer user_data); +static gboolean clutter_frame_source_prepare (GSource *source, + gint *timeout); +static gboolean clutter_frame_source_check (GSource *source); +static gboolean clutter_frame_source_dispatch (GSource *source, + GSourceFunc callback, + gpointer user_data); static GSourceFuncs clutter_frame_source_funcs = - { - clutter_frame_source_prepare, - clutter_frame_source_check, - clutter_frame_source_dispatch, - NULL - }; +{ + clutter_frame_source_prepare, + clutter_frame_source_check, + clutter_frame_source_dispatch, + NULL +}; /** * clutter_frame_source_add_full: * @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 * @func: function to call * @data: data to pass to the function @@ -131,7 +132,8 @@ clutter_frame_source_add (guint fps, } static gboolean -clutter_frame_source_prepare (GSource *source, gint *delay) +clutter_frame_source_prepare (GSource *source, + gint *delay) { ClutterFrameSource *frame_source = (ClutterFrameSource *) source; GTimeVal current_time; diff --git a/clutter/clutter-group.c b/clutter/clutter-group.c index a0ef32c05..059931634 100644 --- a/clutter/clutter-group.c +++ b/clutter/clutter-group.c @@ -277,10 +277,12 @@ clutter_group_real_foreach (ClutterContainer *container, { ClutterGroup *group = CLUTTER_GROUP (container); ClutterGroupPrivate *priv = group->priv; - GList *l; - for (l = priv->children; l; l = l->next) - (* callback) (CLUTTER_ACTOR (l->data), user_data); + /* Using g_list_foreach instead of iterating the list manually + 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 diff --git a/clutter/clutter-input-device.c b/clutter/clutter-input-device.c new file mode 100644 index 000000000..c3109df04 --- /dev/null +++ b/clutter/clutter-input-device.c @@ -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 . + * + * Author: Emmanuele Bassi + */ + +/** + * 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; +} diff --git a/clutter/clutter-input-device.h b/clutter/clutter-input-device.h new file mode 100644 index 000000000..4b8cc9470 --- /dev/null +++ b/clutter/clutter-input-device.h @@ -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 . + * + * Author: Emmanuele Bassi + */ + +#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __CLUTTER_INPUT_DEVICE_H__ +#define __CLUTTER_INPUT_DEVICE_H__ + +#include + +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__ */ diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index ea5e19d14..1ac393ef0 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -2045,69 +2045,93 @@ event_click_count_generate (ClutterEvent *event) static guint32 previous_time = 0; static gint previous_button_number = -1; + ClutterInputDevice *device = NULL; ClutterBackend *backend; - guint double_click_time; - guint double_click_distance; + guint double_click_time; + 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_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; - previous_x = event->button.device->previous_x; - previous_y = event->button.device->previous_y; - previous_time = event->button.device->previous_time; - previous_button_number = event->button.device->previous_button_number; + click_count = device->click_count; + previous_x = device->previous_x; + previous_y = device->previous_y; + previous_time = device->previous_time; + 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_SCROLL: /* check if we are in time and within distance to increment an * 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.y - previous_y) <= double_click_distance) - && event->button.button == previous_button_number) + (ABS (event->button.y - previous_y) <= double_click_distance)) { - 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*/ { - 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; } - /* store time and position for this click for comparison with - * next event - */ + previous_x = event->button.x; + previous_y = event->button.y; previous_time = event->button.time; - previous_x = event->button.x; - previous_y = event->button.y; /* fallthrough */ case CLUTTER_BUTTON_RELEASE: - event->button.click_count=click_count; + event->button.click_count = click_count; break; + default: g_assert (NULL); } - if (event->button.device != NULL) + if (event->type == CLUTTER_BUTTON_PRESS && device != NULL) { - event->button.device->click_count = click_count; - event->button.device->previous_x = previous_x; - event->button.device->previous_y = previous_y; - event->button.device->previous_time = previous_time; - event->button.device->previous_button_number = previous_button_number; + CLUTTER_NOTE (EVENT, "Storing click count: %d (device:%d, time:%u)", + click_count, + clutter_input_device_get_device_id (device), + previous_time); + + 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 emit_event (ClutterEvent *event, gboolean is_key_event) @@ -2118,7 +2142,7 @@ emit_event (ClutterEvent *event, ClutterActor *actor; gint i = 0; - if (!event->any.source) + if (event->any.source == NULL) { CLUTTER_NOTE (EVENT, "No source set, discarding event"); return; @@ -2182,136 +2206,38 @@ static inline void emit_pointer_event (ClutterEvent *event, ClutterInputDevice *device) { - /* Using the global variable directly, since it has to be initialized - * at this point - */ - ClutterMainContext *context = ClutterCntx; + ClutterMainContext *context = _clutter_context_get_default (); - if (G_UNLIKELY (context->pointer_grab_actor != NULL && - device == 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 + if (context->pointer_grab_actor == NULL && + (device == NULL || device->pointer_grab_actor == NULL)) { /* no grab, time to capture and bubble */ 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 emit_keyboard_event (ClutterEvent *event) { - ClutterMainContext *context = ClutterCntx; + ClutterMainContext *context = _clutter_context_get_default (); - if (G_UNLIKELY (context->keyboard_grab_actor != NULL)) - clutter_actor_event (context->keyboard_grab_actor, event, FALSE); - else + if (context->keyboard_grab_actor == NULL) 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 - dev->motion_last_actor = NULL; -} - -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); + clutter_actor_event (context->keyboard_grab_actor, event, FALSE); } /** @@ -2343,7 +2269,9 @@ _clutter_process_event_details (ClutterActor *stage, ClutterMainContext *context, ClutterEvent *event) { - ClutterInputDevice *device = NULL; + ClutterInputDevice *device = NULL; + + device = clutter_event_get_device (event); switch (event->type) { @@ -2352,23 +2280,8 @@ _clutter_process_event_details (ClutterActor *stage, break; 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: - emit_pointer_event (event, event->crossing.device); + emit_pointer_event (event, device); break; case CLUTTER_DESTROY_NOTIFY: @@ -2400,8 +2313,6 @@ _clutter_process_event_details (ClutterActor *stage, break; case CLUTTER_MOTION: - device = event->motion.device; - /* Only stage gets motion events if clutter_set_motion_events is TRUE, * and the event is not a synthetic event with source set. */ @@ -2434,8 +2345,7 @@ _clutter_process_event_details (ClutterActor *stage, break; } - /* fallthrough */ - + /* fallthrough from motion */ case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_RELEASE: case CLUTTER_SCROLL: @@ -2462,18 +2372,29 @@ _clutter_process_event_details (ClutterActor *stage, x, y); event->button.source = stage; - emit_pointer_event (event, event->button.device); + event->button.click_count = 1; + emit_pointer_event (event, device); } break; } - /* Map the event to a reactive actor */ - actor = _clutter_do_pick (CLUTTER_STAGE (stage), - x, y, - CLUTTER_PICK_REACTIVE); + /* if the backend provides a device then we should + * already have everything we need to update it and + * get the actor underneath + */ + 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; - if (!actor) + if (event->any.source == NULL) break; } else @@ -2482,7 +2403,6 @@ _clutter_process_event_details (ClutterActor *stage, actor = event->any.source; } - /* FIXME: for an optimisation should check if there are * actually any reactive actors and avoid the pick all together * (signalling just the stage). Should be big help for gles. @@ -2493,33 +2413,12 @@ _clutter_process_event_details (ClutterActor *stage, x, y, actor); - /* Create, enter/leave events if needed */ - generate_enter_leave_events (event); - if (event->type != CLUTTER_MOTION) { /* Generate click count */ 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); break; } @@ -3023,9 +2922,19 @@ clutter_get_font_flags (void) /** * 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 * @@ -3034,23 +2943,11 @@ clutter_get_font_flags (void) ClutterInputDevice * clutter_get_input_device_for_id (gint id) { - GSList *item; - ClutterInputDevice *device = NULL; - ClutterMainContext *context; + ClutterDeviceManager *manager; - context = _clutter_context_get_default (); + manager = clutter_device_manager_get_default (); - for (item = context->input_devices; - item != NULL; - item = item->next) - { - device = item->data; - - if (device->id == id) - return device; - } - - return NULL; + return clutter_device_manager_get_device (manager, id); } /** diff --git a/clutter/clutter-marshal.list b/clutter/clutter-marshal.list index 7f354b11a..8296f9add 100644 --- a/clutter/clutter-marshal.list +++ b/clutter/clutter-marshal.list @@ -12,6 +12,7 @@ VOID:INT,INT,INT,INT VOID:OBJECT VOID:OBJECT,OBJECT,PARAM VOID:OBJECT,POINTER +VOID:POINTER VOID:STRING,BOOLEAN,BOOLEAN VOID:STRING,INT VOID:UINT diff --git a/clutter/clutter-master-clock.c b/clutter/clutter-master-clock.c index b61ad436c..9e54b9e1b 100644 --- a/clutter/clutter-master-clock.c +++ b/clutter/clutter-master-clock.c @@ -288,14 +288,13 @@ clutter_clock_dispatch (GSource *source, * event handling */ 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); master_clock->updated_stages = FALSE; - /* Process queued events - */ + /* Process queued events */ for (l = stages; l != NULL; l = l->next) _clutter_stage_process_queued_events (l->data); @@ -311,7 +310,7 @@ clutter_clock_dispatch (GSource *source, for (l = stages; l != NULL; l = l->next) 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); master_clock->prev_tick = master_clock->cur_tick; @@ -446,7 +445,7 @@ _clutter_master_clock_start_running (ClutterMasterClock *master_clock) void _clutter_master_clock_advance (ClutterMasterClock *master_clock) { - GSList *l, *next; + GSList *timelines, *l; CLUTTER_STATIC_TIMER (master_timeline_advance, "Master Clock", @@ -456,23 +455,38 @@ _clutter_master_clock_advance (ClutterMasterClock *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); - for (l = master_clock->timelines; l != NULL; l = next) - { - next = l->next; + /* we protect ourselves from timelines being removed during + * the advancement by other timelines by copying the list of + * 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); - - g_slist_foreach (master_clock->timelines, (GFunc) g_object_unref, NULL); } /** diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index dd687d010..79b9569cc 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -41,6 +41,7 @@ #include "pango/cogl-pango.h" #include "clutter-backend.h" +#include "clutter-device-manager.h" #include "clutter-event.h" #include "clutter-feature.h" #include "clutter-id-pool.h" @@ -52,6 +53,8 @@ G_BEGIN_DECLS +typedef struct _ClutterMainContext ClutterMainContext; + typedef enum { CLUTTER_ACTOR_UNUSED_FLAG = 0, @@ -82,21 +85,54 @@ typedef enum { struct _ClutterInputDevice { - gint id; + GObject parent_instance; + + gint id; ClutterInputDeviceType device_type; - ClutterActor *pointer_grab_actor; - ClutterActor *motion_last_actor; + gchar *device_name; - gint click_count; - gint previous_x; - gint previous_y; - guint32 previous_time; - gint previous_button_number; + /* the actor underneath the pointer */ + ClutterActor *cursor_actor; + + /* the actor that has a grab in place for the device */ + 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 { @@ -138,9 +174,6 @@ struct _ClutterMainContext PangoContext *pango_context; /* Global Pango context */ CoglPangoFontMap *font_map; /* Global font map */ - GSList *input_devices; /* For extra input devices, i.e - MultiTouch */ - ClutterEvent *current_event; guint32 last_event_time; @@ -170,21 +203,34 @@ PangoContext *_clutter_context_get_pango_context (ClutterMainContext *self); #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 */ -struct _ClutterStageManager -{ - GObject parent_instance; - - GSList *stages; -}; - void _clutter_stage_manager_add_stage (ClutterStageManager *stage_manager, ClutterStage *stage); void _clutter_stage_manager_remove_stage (ClutterStageManager *stage_manager, ClutterStage *stage); /* stage */ - void _clutter_stage_set_window (ClutterStage *stage, ClutterStageWindow *stage_window); ClutterStageWindow *_clutter_stage_get_window (ClutterStage *stage); @@ -198,6 +244,7 @@ void _clutter_stage_queue_event (ClutterStage *stage, ClutterEvent *event); gboolean _clutter_stage_has_queued_events (ClutterStage *stage); void _clutter_stage_process_queued_events (ClutterStage *stage); +void _clutter_stage_update_input_devices (ClutterStage *stage); /* vfuncs implemented by backend */ 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, gboolean enable); +void _clutter_actor_set_has_pointer (ClutterActor *self, + gboolean has_pointer); + void _clutter_run_repaint_functions (void); gint32 _clutter_backend_get_units_serial (ClutterBackend *backend); diff --git a/clutter/clutter-stage.c b/clutter/clutter-stage.c index 518d181c7..a9f9b938f 100644 --- a/clutter/clutter-stage.c +++ b/clutter/clutter-stage.c @@ -69,6 +69,7 @@ #include "clutter-id-pool.h" #include "clutter-container.h" #include "clutter-profile.h" +#include "clutter-input-device.h" #include "cogl/cogl.h" @@ -113,7 +114,8 @@ enum PROP_USER_RESIZE, PROP_USE_FOG, PROP_FOG, - PROP_USE_ALPHA + PROP_USE_ALPHA, + PROP_KEY_FOCUS }; enum @@ -454,6 +456,7 @@ _clutter_stage_queue_event (ClutterStage *stage, { ClutterStagePrivate *priv; gboolean first_event; + ClutterInputDevice *device; 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; - g_queue_push_tail (priv->event_queue, - clutter_event_copy (event)); + g_queue_push_tail (priv->event_queue, clutter_event_copy (event)); if (first_event) { ClutterMasterClock *master_clock = _clutter_master_clock_get_default (); _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 @@ -487,7 +507,7 @@ void _clutter_stage_process_queued_events (ClutterStage *stage) { ClutterStagePrivate *priv; - GList *events, *l;; + GList *events, *l; 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->length = 0; - for (l = events; l; l = l->next) + for (l = events; l != NULL; l = l->next) { ClutterEvent *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)); break; + case PROP_KEY_FOCUS: + clutter_stage_set_key_focus (stage, g_value_get_object (value)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -750,6 +774,10 @@ clutter_stage_get_property (GObject *gobject, g_value_set_boolean (value, priv->use_alpha); break; + case PROP_KEY_FOCUS: + g_value_set_object (value, priv->key_focused_actor); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; @@ -994,6 +1022,23 @@ clutter_stage_class_init (ClutterStageClass *klass) CLUTTER_PARAM_READWRITE); 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 * @stage: the stage which was fullscreened @@ -1749,6 +1794,8 @@ clutter_stage_set_key_focus (ClutterStage *stage, } else g_signal_emit_by_name (stage, "key-focus-in"); + + g_object_notify (G_OBJECT (stage), "key-focus"); } /** diff --git a/clutter/clutter-text.c b/clutter/clutter-text.c index 40c69603a..a1dd1ef00 100644 --- a/clutter/clutter-text.c +++ b/clutter/clutter-text.c @@ -260,6 +260,7 @@ clutter_text_clear_selection (ClutterText *self) if (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)); } } @@ -803,6 +804,7 @@ clutter_text_delete_selection (ClutterText *self) priv->position = start_index; priv->selection_bound = start_index; + /* Not required to be guarded by g_object_freeze/thaw_notify */ if (priv->position != old_position) g_object_notify (G_OBJECT (self), "position"); @@ -812,6 +814,21 @@ clutter_text_delete_selection (ClutterText *self) 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 clutter_text_set_text_internal (ClutterText *self, const gchar *text) @@ -857,8 +874,7 @@ clutter_text_set_text_internal (ClutterText *self, if (priv->n_bytes == 0) { - clutter_text_set_cursor_position (self, -1); - clutter_text_set_selection_bound (self, -1); + clutter_text_set_positions (self, -1, -1); } clutter_text_dirty_cache (self); @@ -1493,8 +1509,7 @@ clutter_text_button_press (ClutterActor *actor, */ if (priv->text == NULL || priv->text[0] == '\0') { - clutter_text_set_cursor_position (self, -1); - clutter_text_set_selection_bound (self, -1); + clutter_text_set_positions (self, -1, -1); return TRUE; } @@ -1519,8 +1534,7 @@ clutter_text_button_press (ClutterActor *actor, */ if (event->click_count == 1) { - clutter_text_set_cursor_position (self, offset); - clutter_text_set_selection_bound (self, offset); + clutter_text_set_positions (self, offset, offset); } else if (event->click_count == 2) { @@ -1565,8 +1579,7 @@ clutter_text_motion (ClutterActor *actor, clutter_text_set_cursor_position (self, offset); else { - clutter_text_set_cursor_position (self, offset); - clutter_text_set_selection_bound (self, offset); + clutter_text_set_positions (self, offset, offset); } return TRUE; @@ -1896,6 +1909,8 @@ clutter_text_real_move_left (ClutterText *self, len = priv->n_chars; + g_object_freeze_notify (G_OBJECT (self)); + if (pos != 0 && len != 0) { if (modifiers & CLUTTER_CONTROL_MASK) @@ -1919,6 +1934,8 @@ clutter_text_real_move_left (ClutterText *self, if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK))) clutter_text_clear_selection (self); + g_object_thaw_notify (G_OBJECT (self)); + return TRUE; } @@ -1935,6 +1952,8 @@ clutter_text_real_move_right (ClutterText *self, len = priv->n_chars; + g_object_freeze_notify (G_OBJECT (self)); + if (pos != -1 && len !=0) { if (modifiers & CLUTTER_CONTROL_MASK) @@ -1954,6 +1973,8 @@ clutter_text_real_move_right (ClutterText *self, if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK))) clutter_text_clear_selection (self); + g_object_thaw_notify (G_OBJECT (self)); + return TRUE; } @@ -1970,6 +1991,8 @@ clutter_text_real_move_up (ClutterText *self, gint index_, trailing; gint x; + g_object_freeze_notify (G_OBJECT (self)); + layout = clutter_text_get_layout (self); if (priv->position == 0) @@ -2007,6 +2030,8 @@ clutter_text_real_move_up (ClutterText *self, if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK))) clutter_text_clear_selection (self); + g_object_thaw_notify (G_OBJECT (self)); + return TRUE; } @@ -2023,6 +2048,8 @@ clutter_text_real_move_down (ClutterText *self, gint index_, trailing; gint x; + g_object_freeze_notify (G_OBJECT (self)); + layout = clutter_text_get_layout (self); if (priv->position == 0) @@ -2056,6 +2083,8 @@ clutter_text_real_move_down (ClutterText *self, if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK))) clutter_text_clear_selection (self); + g_object_thaw_notify (G_OBJECT (self)); + return TRUE; } @@ -2068,12 +2097,16 @@ clutter_text_real_line_start (ClutterText *self, ClutterTextPrivate *priv = self->priv; gint position; + g_object_freeze_notify (G_OBJECT (self)); + position = clutter_text_move_line_start (self, priv->position); clutter_text_set_cursor_position (self, position); if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK))) clutter_text_clear_selection (self); + g_object_thaw_notify (G_OBJECT (self)); + return TRUE; } @@ -2086,12 +2119,16 @@ clutter_text_real_line_end (ClutterText *self, ClutterTextPrivate *priv = self->priv; gint position; + g_object_freeze_notify (G_OBJECT (self)); + position = clutter_text_move_line_end (self, priv->position); clutter_text_set_cursor_position (self, position); if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK))) clutter_text_clear_selection (self); + g_object_thaw_notify (G_OBJECT (self)); + return TRUE; } @@ -2101,8 +2138,7 @@ clutter_text_real_select_all (ClutterText *self, guint keyval, ClutterModifierType modifiers) { - clutter_text_set_cursor_position (self, 0); - clutter_text_set_selection_bound (self, self->priv->n_chars); + clutter_text_set_positions (self, 0, self->priv->n_chars); return TRUE; } @@ -2151,15 +2187,13 @@ clutter_text_real_del_prev (ClutterText *self, { clutter_text_delete_text (self, len - 1, len); - clutter_text_set_cursor_position (self, -1); - clutter_text_set_selection_bound (self, -1); + clutter_text_set_positions (self, -1, -1); } else { clutter_text_delete_text (self, pos - 1, pos); - clutter_text_set_cursor_position (self, pos - 1); - clutter_text_set_selection_bound (self, pos - 1); + clutter_text_set_positions (self, pos - 1, pos - 1); } } @@ -3260,12 +3294,7 @@ clutter_text_set_selection (ClutterText *self, start_pos = MIN (priv->n_chars, start_pos); end_pos = MIN (priv->n_chars, end_pos); - g_object_freeze_notify (G_OBJECT (self)); - - clutter_text_set_cursor_position (self, start_pos); - clutter_text_set_selection_bound (self, end_pos); - - g_object_thaw_notify (G_OBJECT (self)); + clutter_text_set_positions (self, start_pos, end_pos); } /** @@ -4349,8 +4378,7 @@ clutter_text_insert_unichar (ClutterText *self, if (priv->position >= 0) { - clutter_text_set_cursor_position (self, priv->position + 1); - clutter_text_set_selection_bound (self, priv->position); + clutter_text_set_positions (self, priv->position + 1, priv->position); } g_string_free (new, TRUE); @@ -4397,8 +4425,9 @@ clutter_text_insert_text (ClutterText *self, if (position >= 0 && priv->position >= position) { - clutter_text_set_cursor_position (self, priv->position + g_utf8_strlen (text, -1)); - clutter_text_set_selection_bound (self, priv->position); + clutter_text_set_positions (self, + priv->position + g_utf8_strlen (text, -1), + priv->position); } g_string_free (new, TRUE); diff --git a/clutter/clutter-timeout-interval.c b/clutter/clutter-timeout-interval.c index 0998a6662..2faf4f8e9 100644 --- a/clutter/clutter-timeout-interval.c +++ b/clutter/clutter-timeout-interval.c @@ -32,7 +32,7 @@ void _clutter_timeout_interval_init (ClutterTimeoutInterval *interval, - guint fps) + guint fps) { g_get_current_time (&interval->start_time); interval->fps = fps; @@ -40,21 +40,24 @@ _clutter_timeout_interval_init (ClutterTimeoutInterval *interval, } static guint -_clutter_timeout_interval_get_ticks (const GTimeVal *current_time, +_clutter_timeout_interval_get_ticks (const GTimeVal *current_time, ClutterTimeoutInterval *interval) { 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 -_clutter_timeout_interval_prepare (const GTimeVal *current_time, +_clutter_timeout_interval_prepare (const GTimeVal *current_time, ClutterTimeoutInterval *interval, - gint *delay) + gint *delay) { - guint elapsed_time - = _clutter_timeout_interval_get_ticks (current_time, interval); - guint new_frame_num = elapsed_time * interval->fps / 1000; + guint elapsed_time, new_frame_num; + + 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 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 */ interval->start_time = *current_time; + /* Move the start time as if one whole frame has elapsed */ 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) *delay = 0; + return TRUE; } else if (new_frame_num > interval->frame_count) { if (delay) *delay = 0; + return TRUE; } else { if (delay) *delay = ((interval->frame_count + 1) * 1000 / interval->fps - - elapsed_time); + - elapsed_time); + return FALSE; } } @@ -99,10 +106,11 @@ _clutter_timeout_interval_dispatch (ClutterTimeoutInterval *interval, if ((* callback) (user_data)) { interval->frame_count++; + return TRUE; } - else - return FALSE; + + return FALSE; } gint @@ -115,12 +123,12 @@ _clutter_timeout_interval_compare_expiration (const ClutterTimeoutInterval *a, gint comparison; 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) - - (gint) ((b->frame_count + 1) * b_delay + b_difference)); + - (gint) ((b->frame_count + 1) * b_delay + b_difference)); return (comparison < 0 ? -1 - : comparison > 0 ? 1 - : 0); + : comparison > 0 ? 1 + : 0); } diff --git a/clutter/clutter-timeout-pool.c b/clutter/clutter-timeout-pool.c index d1cb48252..0f89d7afb 100644 --- a/clutter/clutter-timeout-pool.c +++ b/clutter/clutter-timeout-pool.c @@ -359,12 +359,6 @@ clutter_timeout_pool_finalize (GSource *source) * always sorted, so that the extraction of the next timeout function is * 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 * is owned by the GLib default context and will be automatically * 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 * 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 * function will be called again half the interval time after it * 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_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); } } diff --git a/clutter/clutter.h b/clutter/clutter.h index 087d93d40..fac430ae2 100644 --- a/clutter/clutter.h +++ b/clutter/clutter.h @@ -51,12 +51,14 @@ #include "clutter-clone.h" #include "clutter-color.h" #include "clutter-container.h" +#include "clutter-device-manager.h" #include "clutter-event.h" #include "clutter-feature.h" #include "clutter-fixed-layout.h" #include "clutter-flow-layout.h" #include "clutter-frame-source.h" #include "clutter-group.h" +#include "clutter-input-device.h" #include "clutter-interval.h" #include "clutter-keysyms.h" #include "clutter-layout-manager.h" diff --git a/clutter/cogl/cogl/cogl-context.h b/clutter/cogl/cogl/cogl-context.h index ce84b01cb..dcbba2c70 100644 --- a/clutter/cogl/cogl/cogl-context.h +++ b/clutter/cogl/cogl/cogl-context.h @@ -115,6 +115,12 @@ typedef struct CoglAtlas *atlas; 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; } CoglContext; diff --git a/clutter/cogl/cogl/cogl-internal.h b/clutter/cogl/cogl/cogl-internal.h index 585220a1d..f38107ead 100644 --- a/clutter/cogl/cogl/cogl-internal.h +++ b/clutter/cogl/cogl/cogl-internal.h @@ -101,6 +101,8 @@ CoglTextureUnit * _cogl_get_texture_unit (int index_); void _cogl_destroy_texture_units (void); +guint +_cogl_get_max_texture_image_units (void); void _cogl_flush_face_winding (void); diff --git a/clutter/cogl/cogl/cogl-journal.c b/clutter/cogl/cogl/cogl-journal.c index 2a0e106a5..a27eb8d75 100644 --- a/clutter/cogl/cogl/cogl-journal.c +++ b/clutter/cogl/cogl/cogl-journal.c @@ -225,30 +225,51 @@ _cogl_journal_flush_modelview_and_entries (CoglJournalEntry *batch_start, } #endif - /* DEBUGGING CODE XXX: - * This path will cause all rectangles to be drawn with a red, green - * or blue outline with no blending. This may e.g. help with debugging - * texture slicing issues or blending issues, plus it looks quite cool. + /* DEBUGGING CODE XXX: This path will cause all rectangles to be + * drawn with a coloured outline. Each batch will be rendered with + * the same color. This may e.g. help with debugging texture slicing + * issues, visually seeing what is batched and debugging blending + * issues, plus it looks quite cool. */ if (cogl_debug_flags & COGL_DEBUG_RECTANGLES) { static CoglHandle outline = COGL_INVALID_HANDLE; - static int color = 0; + guint8 color_intensity; int i; + + _COGL_GET_CONTEXT (ctxt, NO_RETVAL); + if (outline == COGL_INVALID_HANDLE) 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); - for (i = 0; i < batch_len; i++, color = (color + 1) % 3) - { - cogl_material_set_color4ub (outline, - color == 0 ? 0xff : 0x00, - color == 1 ? 0xff : 0x00, - color == 2 ? 0xff : 0x00, - 0xff); - _cogl_material_flush_gl_state (outline, NULL); - GE( glDrawArrays (GL_LINE_LOOP, 4 * i, 4) ); - } + for (i = 0; i < batch_len; i++) + GE( glDrawArrays (GL_LINE_LOOP, 4 * i + state->vertex_offset, 4) ); + + /* Go to the next color */ + do + ctxt->journal_rectangles_color = ((ctxt->journal_rectangles_color + 1) & + ((1 << 5) - 1)); + /* We don't want to use black or white */ + while ((ctxt->journal_rectangles_color & 0x07) == 0 + || (ctxt->journal_rectangles_color & 0x07) == 0x07); } 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)) cogl_get_modelview_matrix (&entry->model_view); - if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_BATCHING - || cogl_debug_flags & COGL_DEBUG_RECTANGLES)) + if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_BATCHING)) _cogl_journal_flush (); COGL_TIMER_STOP (_cogl_uprof_context, log_timer); diff --git a/clutter/cogl/cogl/cogl-material.c b/clutter/cogl/cogl/cogl-material.c index 589a753db..8f12fc81b 100644 --- a/clutter/cogl/cogl/cogl-material.c +++ b/clutter/cogl/cogl/cogl-material.c @@ -48,12 +48,6 @@ #include "../gles/cogl-gles2-wrapper.h" #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 #define glActiveTexture ctx->drv.pf_glActiveTexture #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); 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)) { @@ -1551,7 +1545,7 @@ _cogl_material_flush_layers_gl_state (CoglMaterial *material, 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; } diff --git a/clutter/cogl/cogl/cogl-types.h b/clutter/cogl/cogl/cogl-types.h index 3d5e3eb02..4ce1502f3 100644 --- a/clutter/cogl/cogl/cogl-types.h +++ b/clutter/cogl/cogl/cogl-types.h @@ -120,8 +120,8 @@ typedef struct _CoglTextureVertex CoglTextureVertex; * @COGL_PIXEL_FORMAT_RGB_565: RGB, 16 bits * @COGL_PIXEL_FORMAT_RGBA_4444: RGBA, 16 bits * @COGL_PIXEL_FORMAT_RGBA_5551: RGBA, 16 bits - * @COGL_PIXEL_FORMAT_YUV: FIXME - * @COGL_PIXEL_FORMAT_G_8: FIXME + * @COGL_PIXEL_FORMAT_YUV: Not currently supported + * @COGL_PIXEL_FORMAT_G_8: Single luminance component * @COGL_PIXEL_FORMAT_RGB_888: RGB, 24 bits * @COGL_PIXEL_FORMAT_BGR_888: BGR, 24 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_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 */ diff --git a/clutter/cogl/cogl/cogl-util.c b/clutter/cogl/cogl/cogl-util.c index 6debb5599..96fa4c9d8 100644 --- a/clutter/cogl/cogl/cogl-util.c +++ b/clutter/cogl/cogl/cogl-util.c @@ -51,9 +51,9 @@ int cogl_util_next_p2 (int a) { - int rval=1; + int rval = 1; - while(rval < a) + while (rval < a) rval <<= 1; return rval; @@ -237,8 +237,8 @@ cogl_fixed_get_type (void) g_value_register_transform_func (_cogl_fixed_type, G_TYPE_FLOAT, cogl_value_transform_fixed_float); g_value_register_transform_func (G_TYPE_FLOAT, _cogl_fixed_type, - cogl_value_transform_float_fixed); + g_value_register_transform_func (_cogl_fixed_type, G_TYPE_DOUBLE, cogl_value_transform_fixed_double); g_value_register_transform_func (G_TYPE_DOUBLE, _cogl_fixed_type, diff --git a/clutter/cogl/cogl/cogl-vertex-buffer.c b/clutter/cogl/cogl/cogl-vertex-buffer.c index 943b77776..67fd43810 100644 --- a/clutter/cogl/cogl/cogl-vertex-buffer.c +++ b/clutter/cogl/cogl/cogl-vertex-buffer.c @@ -1512,7 +1512,7 @@ enable_state_for_drawing_buffer (CoglVertexBuffer *buffer) GLuint generic_index = 0; #endif gulong enable_flags = 0; - guint max_texcoord_attrib_unit = 0; + guint max_texcoord_attrib_unit = -1; const GList *layers; guint32 fallback_layers = 0; guint32 disable_layers = ~0; diff --git a/clutter/cogl/cogl/cogl.c b/clutter/cogl/cogl/cogl.c index aa61fd590..e66f997ed 100644 --- a/clutter/cogl/cogl/cogl.c +++ b/clutter/cogl/cogl/cogl.c @@ -40,6 +40,7 @@ #include "cogl-winsys.h" #include "cogl-framebuffer-private.h" #include "cogl-matrix-private.h" +#include "cogl-journal-private.h" #if defined (HAVE_COGL_GLES2) || defined (HAVE_COGL_GLES) #include "cogl-gles2-wrapper.h" @@ -155,6 +156,16 @@ cogl_clear (const CoglColor *color, gulong 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"); } @@ -729,10 +740,11 @@ cogl_end_gl (void) } static CoglTextureUnit * -_cogl_texture_unit_new (void) +_cogl_texture_unit_new (int index_) { CoglTextureUnit *unit = g_new0 (CoglTextureUnit, 1); unit->matrix_stack = _cogl_matrix_stack_new (); + unit->index = index_; 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. */ - unit = _cogl_texture_unit_new (); + unit = _cogl_texture_unit_new (index_); /* Note: see comment after for() loop above */ ctx->texture_units = @@ -787,6 +799,36 @@ _cogl_destroy_texture_units (void) 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 cogl_push_matrix (void) { diff --git a/clutter/win32/Makefile.am b/clutter/win32/Makefile.am index 41c31d067..c9d8fcfb0 100644 --- a/clutter/win32/Makefile.am +++ b/clutter/win32/Makefile.am @@ -33,4 +33,4 @@ libclutter_win32_la_SOURCES = \ CLEANFILES = clutter-win32-$(CLUTTER_API_VERSION).pc -EXTRA_DIST = clutter-win32.pc.in +EXTRA_DIST = clutter-win32.pc.in resources.rc invisible-cursor.cur diff --git a/clutter/win32/clutter-backend-win32.c b/clutter/win32/clutter-backend-win32.c index d0dc3d0b6..ff45466c3 100644 --- a/clutter/win32/clutter-backend-win32.c +++ b/clutter/win32/clutter-backend-win32.c @@ -31,6 +31,7 @@ #include "../clutter-event.h" #include "../clutter-main.h" +#include "../clutter-input-device.h" #include "../clutter-debug.h" #include "../clutter-private.h" #include "../clutter-version.h" @@ -47,6 +48,8 @@ static ClutterBackendWin32 *backend_singleton = NULL; static gchar *clutter_vblank_name = NULL; +static HINSTANCE clutter_hinst = NULL; + gboolean clutter_backend_win32_pre_parse (ClutterBackend *backend, GError **error) @@ -62,11 +65,45 @@ clutter_backend_win32_pre_parse (ClutterBackend *backend, static void 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"); + 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); } +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[] = { { @@ -353,6 +390,7 @@ clutter_backend_win32_init (ClutterBackendWin32 *backend_win32) backend_win32->gl_context = NULL; backend_win32->no_event_retrieval = FALSE; + backend_win32->invisible_cursor = NULL; /* FIXME: get from GetSystemMetric? */ clutter_backend_set_double_click_time (backend, 250); @@ -370,3 +408,13 @@ _clutter_backend_impl_get_type (void) { 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; +} diff --git a/clutter/win32/clutter-backend-win32.h b/clutter/win32/clutter-backend-win32.h index c301f68e1..0dfb74d29 100644 --- a/clutter/win32/clutter-backend-win32.h +++ b/clutter/win32/clutter-backend-win32.h @@ -48,7 +48,12 @@ struct _ClutterBackendWin32 HGLRC gl_context; gboolean no_event_retrieval; + HCURSOR invisible_cursor; + GSource *event_source; + + ClutterInputDevice *core_pointer; + ClutterInputDevice *core_keyboard; }; struct _ClutterBackendWin32Class @@ -68,6 +73,8 @@ clutter_backend_win32_add_options (ClutterBackend *backend, ClutterFeatureFlags clutter_backend_win32_get_features (ClutterBackend *backend); +HCURSOR _clutter_backend_win32_get_invisible_cursor (ClutterBackend *backend); + G_END_DECLS #endif /* __CLUTTER_BACKEND_WIN32_H__ */ diff --git a/clutter/win32/clutter-event-win32.c b/clutter/win32/clutter-event-win32.c index 5fd75a6a6..23f1bb22a 100644 --- a/clutter/win32/clutter-event-win32.c +++ b/clutter/win32/clutter-event-win32.c @@ -201,9 +201,13 @@ get_modifier_state (WPARAM wparam) return ret; } -static void -make_button_event (const MSG *msg, ClutterEvent *event, - int button, int click_count, gboolean release) +static inline void +make_button_event (const MSG *msg, + ClutterEvent *event, + int button, + int click_count, + gboolean release, + ClutterInputDevice *device) { event->type = release ? CLUTTER_BUTTON_RELEASE : CLUTTER_BUTTON_PRESS; 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.button = button; event->button.click_count = click_count; + event->button.device = device; } /** @@ -326,11 +331,11 @@ message_translate (ClutterBackend *backend, const MSG *msg, gboolean *call_def_window_proc) { - ClutterBackendWin32 *backend_win32; - ClutterStageWin32 *stage_win32; - ClutterStage *stage; - ClutterStageWindow *impl; - gboolean res; + ClutterBackendWin32 *backend_win32; + ClutterStageWin32 *stage_win32; + ClutterStage *stage; + ClutterStageWindow *impl; + gboolean res; backend_win32 = CLUTTER_BACKEND_WIN32 (backend); @@ -429,39 +434,39 @@ message_translate (ClutterBackend *backend, break; case WM_LBUTTONDOWN: - make_button_event (msg, event, 1, 1, FALSE); + make_button_event (msg, event, 1, 1, FALSE, backend_win32->core_pointer); break; case WM_MBUTTONDOWN: - make_button_event (msg, event, 2, 1, FALSE); + make_button_event (msg, event, 2, 1, FALSE, backend_win32->core_pointer); break; case WM_RBUTTONDOWN: - make_button_event (msg, event, 3, 1, FALSE); + make_button_event (msg, event, 3, 1, FALSE, backend_win32->core_pointer); break; case WM_LBUTTONUP: - make_button_event (msg, event, 1, 1, TRUE); + make_button_event (msg, event, 1, 1, TRUE, backend_win32->core_pointer); break; case WM_MBUTTONUP: - make_button_event (msg, event, 2, 1, TRUE); + make_button_event (msg, event, 2, 1, TRUE, backend_win32->core_pointer); break; case WM_RBUTTONUP: - make_button_event (msg, event, 3, 1, TRUE); + make_button_event (msg, event, 3, 1, TRUE, backend_win32->core_pointer); break; case WM_LBUTTONDBLCLK: - make_button_event (msg, event, 1, 2, FALSE); + make_button_event (msg, event, 1, 2, FALSE, backend_win32->core_pointer); break; case WM_MBUTTONDBLCLK: - make_button_event (msg, event, 2, 2, FALSE); + make_button_event (msg, event, 2, 2, FALSE, backend_win32->core_pointer); break; case WM_RBUTTONDBLCLK: - make_button_event (msg, event, 3, 2, FALSE); + make_button_event (msg, event, 3, 2, FALSE, backend_win32->core_pointer); break; case WM_MOUSEWHEEL: @@ -469,8 +474,8 @@ message_translate (ClutterBackend *backend, event->type = CLUTTER_SCROLL; event->scroll.time = msg->time; - event->scroll.modifier_state - = get_modifier_state (LOWORD (msg->wParam)); + event->scroll.modifier_state = get_modifier_state (LOWORD (msg->wParam)); + event->scroll.device = backend_win32->core_pointer; /* conversion to window coordinates is required */ { @@ -500,7 +505,9 @@ message_translate (ClutterBackend *backend, event->motion.x = GET_X_LPARAM (msg->lParam); event->motion.y = GET_Y_LPARAM (msg->lParam); 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 */ if (!stage_win32->tracking_mouse) { @@ -511,6 +518,9 @@ message_translate (ClutterBackend *backend, tmevent.hwndTrack = stage_win32->hwnd; TrackMouseEvent (&tmevent); + /* we entered the stage */ + _clutter_input_device_set_stage (event->motion.device, stage); + stage_win32->tracking_mouse = TRUE; } break; @@ -520,6 +530,10 @@ message_translate (ClutterBackend *backend, event->crossing.time = msg->time; event->crossing.x = msg->pt.x; 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 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.modifier_state = get_key_modifier_state (key_states); event->key.hardware_keycode = scan_code; + event->key.device = backend_win32->core_keyboard; } break; @@ -616,6 +631,20 @@ message_translate (ClutterBackend *backend, } 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: /* ignore every other message */ res = FALSE; diff --git a/clutter/win32/clutter-stage-win32.c b/clutter/win32/clutter-stage-win32.c index fbd6f533d..a11d94c4e 100644 --- a/clutter/win32/clutter-stage-win32.c +++ b/clutter/win32/clutter-stage-win32.c @@ -216,17 +216,51 @@ clutter_stage_win32_set_title (ClutterStageWindow *stage_window, 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 clutter_stage_win32_set_cursor_visible (ClutterStageWindow *stage_window, gboolean cursor_visible) { ClutterStageWin32 *stage_win32 = CLUTTER_STAGE_WIN32 (stage_window); - if (stage_win32->is_cursor_visible != cursor_visible && - stage_win32->tracking_mouse) - ShowCursor (cursor_visible); + if (stage_win32->is_cursor_visible != 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 @@ -442,7 +476,7 @@ clutter_stage_win32_realize (ClutterStageWindow *stage_window) win_xpos = stage_win32->fullscreen_rect.left; win_ypos = stage_win32->fullscreen_rect.top; 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 { diff --git a/clutter/win32/clutter-stage-win32.h b/clutter/win32/clutter-stage-win32.h index 3ae9a0cd0..2778e49b6 100644 --- a/clutter/win32/clutter-stage-win32.h +++ b/clutter/win32/clutter-stage-win32.h @@ -80,6 +80,8 @@ LRESULT CALLBACK _clutter_stage_win32_window_proc (HWND hwnd, void _clutter_stage_win32_get_min_max_info (ClutterStageWin32 *stage_win32, MINMAXINFO *min_max_info); +void _clutter_stage_win32_update_cursor (ClutterStageWin32 *stage_win32); + G_END_DECLS #endif /* __CLUTTER_STAGE_H__ */ diff --git a/clutter/win32/invisible-cursor.cur b/clutter/win32/invisible-cursor.cur new file mode 100644 index 000000000..64f53b7cd Binary files /dev/null and b/clutter/win32/invisible-cursor.cur differ diff --git a/clutter/win32/resources.rc b/clutter/win32/resources.rc new file mode 100644 index 000000000..2a73874e5 --- /dev/null +++ b/clutter/win32/resources.rc @@ -0,0 +1 @@ +42 CURSOR "invisible-cursor.cur" diff --git a/clutter/x11/Makefile.am b/clutter/x11/Makefile.am index 175f65d8b..820c700f4 100644 --- a/clutter/x11/Makefile.am +++ b/clutter/x11/Makefile.am @@ -43,6 +43,8 @@ libclutter_x11_la_SOURCES = \ clutter-backend-x11.h \ clutter-backend-x11.c \ clutter-event-x11.c \ + clutter-input-device-x11.h \ + clutter-input-device-x11.c \ clutter-stage-x11.h \ clutter-stage-x11.c \ clutter-x11-enum-types.h \ diff --git a/clutter/x11/clutter-backend-x11.c b/clutter/x11/clutter-backend-x11.c index 4eaf08106..d3d59df6b 100644 --- a/clutter/x11/clutter-backend-x11.c +++ b/clutter/x11/clutter-backend-x11.c @@ -37,6 +37,7 @@ #include #include "clutter-backend-x11.h" +#include "clutter-input-device-x11.h" #include "clutter-stage-x11.h" #include "clutter-x11.h" @@ -46,31 +47,16 @@ #include #endif -#include "../clutter-event.h" -#include "../clutter-main.h" -#include "../clutter-debug.h" -#include "../clutter-private.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); -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 * the member of the ClutterBackendX11 structure if you add an * atom name here. do not change the order! @@ -109,6 +95,129 @@ static gboolean clutter_synchronise = FALSE; static int TrappedErrorCode = 0; 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 clutter_backend_x11_pre_parse (ClutterBackend *backend, GError **error) @@ -199,9 +308,8 @@ clutter_backend_x11_post_parse (ClutterBackend *backend, clutter_backend_set_resolution (backend, dpi); -#ifdef HAVE_XINPUT - _clutter_x11_register_xinput (); -#endif + /* register input devices */ + clutter_x11_register_input_devices (backend_x11); if (clutter_synchronise) 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 _clutter_x11_select_events (Window xwin) { #ifdef HAVE_XINPUT - GSList *list_it; - ClutterX11XInputDevice *device = NULL; + ClutterDeviceManager *manager; + const GSList *l; - ClutterMainContext *context; - - context = _clutter_context_get_default (); - - if (!backend_singleton) + if (G_UNLIKELY (backend_singleton == NULL)) { g_critical ("X11 backend has not been initialised"); + return; } - for (list_it = context->input_devices; - list_it != NULL; - list_it = list_it->next) - { - device = (ClutterX11XInputDevice *)list_it->data; + manager = clutter_device_manager_get_default (); - XSelectExtensionEvent (backend_singleton->xdpy, - xwin, - device->xevent_list, - device->num_events); + for (l = clutter_device_manager_peek_devices (manager); + l != NULL; + l = l->next) + { + ClutterInputDevice *device = l->data; + + _clutter_input_device_x11_select_events (device, backend_singleton, xwin); } #endif /* HAVE_XINPUT */ } @@ -884,49 +782,34 @@ _clutter_x11_select_events (Window xwin) ClutterInputDevice * _clutter_x11_get_device_for_xid (XID id) { -#ifdef HAVE_XINPUT - ClutterMainContext *context; - GSList *l; + ClutterDeviceManager *manager; - context = _clutter_context_get_default (); + manager = clutter_device_manager_get_default (); - if (!backend_singleton) - { - 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; + return clutter_device_manager_get_device (manager, (gint) id); } -/* 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 * clutter_x11_get_input_devices (void) { -#ifdef HAVE_XINPUT - ClutterMainContext *context; + ClutterDeviceManager *manager; - if (!backend_singleton) - { - g_critical ("X11 backend has not been initialised"); - return NULL; - } + manager = clutter_device_manager_get_default (); - context = _clutter_context_get_default (); - - return context->input_devices; -#else /* !HAVE_XINPUT */ - return NULL; -#endif /* HAVE_XINPUT */ + return clutter_device_manager_peek_devices (manager); } /** @@ -943,7 +826,7 @@ gboolean clutter_x11_has_xinput (void) { #ifdef HAVE_XINPUT - if (!backend_singleton) + if (backend_singleton == NULL) { g_critical ("X11 backend has not been initialised"); return FALSE; diff --git a/clutter/x11/clutter-backend-x11.h b/clutter/x11/clutter-backend-x11.h index 0958eb4cf..92a5fb8f7 100644 --- a/clutter/x11/clutter-backend-x11.h +++ b/clutter/x11/clutter-backend-x11.h @@ -76,10 +76,14 @@ struct _ClutterBackendX11 Atom atom_NET_WM_NAME; Atom atom_UTF8_STRING; + int xi_event_base; int event_types[CLUTTER_X11_XINPUT_LAST_EVENT]; gboolean have_xinput; Time last_event_time; + + ClutterInputDevice *core_pointer; + ClutterInputDevice *core_keyboard; }; struct _ClutterBackendX11Class @@ -125,12 +129,6 @@ clutter_backend_x11_get_features (ClutterBackend *backend); XVisualInfo * clutter_backend_x11_get_visual_info (ClutterBackendX11 *backend_x11); -void -_clutter_x11_register_xinput (void); - -void -_clutter_x11_unregister_xinput (void); - ClutterInputDevice * _clutter_x11_get_device_for_xid (XID id); diff --git a/clutter/x11/clutter-event-x11.c b/clutter/x11/clutter-event-x11.c index 6b3ea67ce..1a3a260fc 100644 --- a/clutter/x11/clutter-event-x11.c +++ b/clutter/x11/clutter-event-x11.c @@ -1,7 +1,8 @@ /* Clutter. * An OpenGL based 'interactive canvas' library. - * Authored By Matthew Allum - * 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 * 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 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. + * + * Authored by: + * Matthew Allum + * Emmanuele Bassi */ #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_set_can_recurse (source, TRUE); g_source_attach (source, NULL); - } 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 -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->xkey.serial = xkev->serial; @@ -410,27 +415,28 @@ event_translate (ClutterBackend *backend, ClutterEvent *event, XEvent *xevent) { - ClutterBackendX11 *backend_x11; - ClutterStageX11 *stage_x11; - ClutterStage *stage; + ClutterBackendX11 *backend_x11; + ClutterStageX11 *stage_x11; + ClutterStage *stage; ClutterStageWindow *impl; - gboolean res, not_yet_handled = FALSE; - Window xwindow, stage_xwindow; + gboolean res, not_yet_handled = FALSE; + Window xwindow, stage_xwindow; + ClutterDeviceManager *manager; + ClutterInputDevice *device; - backend_x11 = CLUTTER_BACKEND_X11 (backend); + backend_x11 = CLUTTER_BACKEND_X11 (backend); xwindow = xevent->xany.window; if (backend_x11->event_filters) { - GSList *node; - ClutterX11EventFilter *filter; + GSList *node; node = backend_x11->event_filters; while (node) { - filter = node->data; + ClutterX11EventFilter *filter = node->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 filters might be getting events for other windows, so do not - * mess them about. + /* Do further processing only on events for the stage window (the x11 + * filters might be getting events for other windows, so do not mess + * them about. */ stage = clutter_x11_get_stage_from_window (xwindow); - if (stage == NULL) - return FALSE; + return FALSE; - impl = _clutter_stage_get_window (stage); - stage_x11 = CLUTTER_STAGE_X11 (impl); - stage_xwindow = xwindow; /* clutter_x11_get_stage_window (stage); */ + manager = clutter_device_manager_get_default (); + + 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; @@ -621,6 +627,8 @@ event_translate (ClutterBackend *backend, case KeyPress: event->key.type = event->type = CLUTTER_KEY_PRESS; + event->key.device = backend_x11->core_keyboard; + translate_key_event (backend, event, xevent); 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.device = backend_x11->core_keyboard; + translate_key_event (backend, event, xevent); break; @@ -666,239 +676,287 @@ event_translate (ClutterBackend *backend, /* Input device event handling.. */ if (not_yet_handled) { - if (!clutter_x11_has_xinput ()) + device = backend_x11->core_pointer; + + /* Regular X event */ + switch (xevent->type) { - /* Regular X event */ - switch (xevent->type) + case ButtonPress: + switch (xevent->xbutton.button) { - /* KeyPress / KeyRelease should reside here if XInput - * worked properly - */ - case ButtonPress: - switch (xevent->xbutton.button) - { - case 4: /* up */ - case 5: /* down */ - case 6: /* left */ - case 7: /* right */ - event->scroll.type = event->type = CLUTTER_SCROLL; - - if (xevent->xbutton.button == 4) - event->scroll.direction = CLUTTER_SCROLL_UP; - else if (xevent->xbutton.button == 5) - event->scroll.direction = CLUTTER_SCROLL_DOWN; - else if (xevent->xbutton.button == 6) - event->scroll.direction = CLUTTER_SCROLL_LEFT; - else - event->scroll.direction = CLUTTER_SCROLL_RIGHT; - - 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); + case 4: /* up */ + case 5: /* down */ + case 6: /* left */ + case 7: /* right */ + event->scroll.type = event->type = CLUTTER_SCROLL; + + if (xevent->xbutton.button == 4) + event->scroll.direction = CLUTTER_SCROLL_UP; + else if (xevent->xbutton.button == 5) + event->scroll.direction = CLUTTER_SCROLL_DOWN; + else if (xevent->xbutton.button == 6) + event->scroll.direction = CLUTTER_SCROLL_LEFT; + else + event->scroll.direction = CLUTTER_SCROLL_RIGHT; + + 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; + event->scroll.device = device; break; - - case ButtonRelease: - /* 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; - break; - } - - event->button.type = event->type = CLUTTER_BUTTON_RELEASE; + + 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; + event->button.device = device; 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: - /* 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; + set_user_time (backend_x11, &xwindow, event->button.time); - case LeaveNotify: - 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; + res = TRUE; + break; + + case ButtonRelease: + /* 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; default: - /* ignore every other event */ - 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.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); - } - 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); + event->button.device = device; + break; } -#endif - else -#endif /* HAVE_XINPUT */ + + set_user_time (backend_x11, &xwindow, xbev->time); + + 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; + 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; } diff --git a/clutter/x11/clutter-input-device-x11.c b/clutter/x11/clutter-input-device-x11.c new file mode 100644 index 000000000..b62ca8a0c --- /dev/null +++ b/clutter/x11/clutter-input-device-x11.c @@ -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 +#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 */ +} diff --git a/clutter/x11/clutter-input-device-x11.h b/clutter/x11/clutter-input-device-x11.h new file mode 100644 index 000000000..b8d60ac46 --- /dev/null +++ b/clutter/x11/clutter-input-device-x11.h @@ -0,0 +1,25 @@ +#ifndef __CLUTTER_INPUT_DEVICE_X11_H__ +#define __CLUTTER_INPUT_DEVICE_X11_H__ + +#include +#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__ */ diff --git a/clutter/x11/clutter-x11.h b/clutter/x11/clutter-x11.h index 368a117b0..30c71ada6 100644 --- a/clutter/x11/clutter-x11.h +++ b/clutter/x11/clutter-x11.h @@ -121,14 +121,16 @@ gboolean clutter_x11_has_event_retrieval (void); ClutterStage *clutter_x11_get_stage_from_window (Window win); +#ifndef CLUTTER_DISABLE_DEPRECATED G_CONST_RETURN GSList* clutter_x11_get_input_devices (void); +#endif void clutter_x11_enable_xinput (void); gboolean clutter_x11_has_xinput (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); Time clutter_x11_get_current_event_time (void); diff --git a/configure.ac b/configure.ac index 302e6686b..359532acd 100644 --- a/configure.ac +++ b/configure.ac @@ -12,7 +12,7 @@ # - increase clutter_interface_version to the next odd number m4_define([clutter_major_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_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_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_MINOR_VERSION=clutter_minor_version @@ -157,7 +157,7 @@ AS_IF([test "x$CLUTTER_WINSYS" = "xeglnative" || COGL_DRIVER="gles" 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], [], @@ -202,7 +202,7 @@ AS_IF([test "x$CLUTTER_WINSYS" = "xeglnative" || use_gles2_wrapper=yes COGL_DRIVER="gles" 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], [], @@ -216,7 +216,7 @@ AS_IF([test "x$CLUTTER_WINSYS" = "xeglnative" || clutter_gl_header="GLES/gl.h" COGL_DRIVER="gles" 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], [], @@ -364,11 +364,18 @@ AS_CASE([$CLUTTER_WINSYS], WIN32_CFLAGS="-D_WIN32_WINNT=0x0500" WIN32_LIBS="-lopengl32 -lgdi32 -lwinmm" 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])] ) +AM_CONDITIONAL(WINSYS_WIN32, [test "x$CLUTTER_WINSYS" = "xwin32"]) + # 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])]) AC_CHECK_HEADERS([$clutter_gl_header], @@ -689,32 +696,49 @@ AS_CASE([$enable_cogl_debug], 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]) AC_ARG_ENABLE(profile, 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@:>@]), - [], + [], [enable_profile=profile_default]) AS_CASE([$enable_profile], - [yes], [ - if test "x$GCC" = "xyes"; then - PKG_CHECK_MODULES([PROFILE_DEP], [uprof-0.2]) - CLUTTER_PROFILE_CFLAGS=" -DCLUTTER_ENABLE_PROFILE -DCOGL_ENABLE_PROFILE $PROFILE_DEP_CFLAGS" - CLUTTER_PROFILE_LDFLAGS=" $PROFILE_DEP_LIBS" - if test "x$enable_debug" = "xyes"; then - CLUTTER_PROFILE_CFLAGS+=" -DUPROF_DEBUG" - fi - else - AC_MSG_ERROR([--enable-profile is currently only supported if using GCC]) - fi - ], - [no], [ - CLUTTER_PROFILE_CFLAGS="" - CLUTTER_PROFILE_LDFLAGS="" - ], - [*], [AC_MSG_ERROR([Invalid value for --enable-profile])] + [yes], + [ + AS_IF([test "x$GCC" = "xyes"], + [ + PKG_CHECK_MODULES([PROFILE_DEP], [uprof-0.2]) + CLUTTER_PROFILE_CFLAGS=" -DCLUTTER_ENABLE_PROFILE -DCOGL_ENABLE_PROFILE $PROFILE_DEP_CFLAGS" + CLUTTER_PROFILE_LDFLAGS=" $PROFILE_DEP_LIBS" + + AS_IF([test "x$enable_debug" = "xyes"], [CLUTTER_PROFILE_CFLAGS+=" -DUPROF_DEBUG"]) + ], + [ + AC_MSG_ERROR([--enable-profile is currently only supported if using GCC]) + ]) + ], + + [no], + [ + CLUTTER_PROFILE_CFLAGS="" + CLUTTER_PROFILE_LDFLAGS="" + ], + + [*], [AC_MSG_ERROR([Invalid value for --enable-profile])] ) AM_CONDITIONAL(PROFILE, test "x$enable_profile" != "xno") 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_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_LDFLAGS="-lgcov" - ] + ], + [AC_MSG_RESULT([no])] ) AM_CONDITIONAL([GCOV_ENABLED], [test "x$enable_gcov" = "xyes"]) AC_SUBST([GCOV_CFLAGS]) AC_SUBST([GCOV_LDFLAGS]) -AC_MSG_RESULT([$enable_gcov]) dnl === Enable strict compiler flags ========================================== @@ -849,11 +878,11 @@ GLIB_DEFINE_LOCALEDIR(LOCALEDIR) dnl =========================================================================== m4_ifdef([AM_SILENT_RULES], - [ + [ AM_SILENT_RULES([yes]) use_shave=no ], - [ + [ SHAVE_INIT([build/autotools], [enable]) AC_CONFIG_FILES([ build/autotools/shave @@ -868,6 +897,7 @@ AC_CONFIG_FILES([ Makefile build/Makefile build/autotools/Makefile + build/mingw/Makefile clutter/Makefile clutter/clutter-version.h clutter/clutter-json.h @@ -945,7 +975,7 @@ echo " Build Additional Documentation: ${enable_docs} (Generate PDF: ${en echo "" echo " • Extra:" 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 echo " JSON-GLib: system" diff --git a/doc/HACKING b/doc/HACKING index 9cfbccdb5..c252f50aa 100644 --- a/doc/HACKING +++ b/doc/HACKING @@ -57,7 +57,7 @@ Short explanation of the commit Longer explanation explaining exactly what's changed, whether any external or private interfaces changed, what bugs were fixed (with bug 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 === Always add a brief description of the commit to the _first_ line of diff --git a/doc/reference/clutter/Makefile.am b/doc/reference/clutter/Makefile.am index 53549c2c6..9063c4a29 100644 --- a/doc/reference/clutter/Makefile.am +++ b/doc/reference/clutter/Makefile.am @@ -63,34 +63,35 @@ CFILE_GLOB=$(top_srcdir)/clutter/*.c \ # Header files to ignore when scanning. # e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h IGNORE_HFILES=\ - clutter.h \ - clutter-bezier.h \ - clutter-debug.h \ - clutter-deprecated.h \ - clutter-enum-types.h \ - clutter-json.h \ - clutter-keysyms.h \ - clutter-keysyms-table.h \ - clutter-marshal.h \ - clutter-master-clock.h \ - clutter-model-private.h \ - clutter-private.h \ - clutter-id-pool.h \ - clutter-script-private.h \ - clutter-stage-window.h \ - clutter-timeout-interval.h \ - stamp-clutter-enum-types.h \ - stamp-clutter-marshal.h \ - cogl \ - eglnative \ - eglx \ - fruity \ - glx \ - osx \ - x11 \ - json \ - pango \ - sdl \ + clutter.h \ + clutter-bezier.h \ + clutter-debug.h \ + clutter-deprecated.h \ + clutter-enum-types.h \ + clutter-id-pool.h \ + clutter-json.h \ + clutter-keysyms.h \ + clutter-keysyms-table.h \ + clutter-marshal.h \ + clutter-master-clock.h \ + clutter-model-private.h \ + clutter-private.h \ + clutter-profile.h \ + clutter-script-private.h \ + clutter-stage-window.h \ + clutter-timeout-interval.h \ + stamp-clutter-enum-types.h \ + stamp-clutter-marshal.h \ + cogl \ + eglnative \ + eglx \ + fruity \ + glx \ + osx \ + x11 \ + json \ + pango \ + sdl \ win32 EXTRA_HFILES=\ diff --git a/doc/reference/clutter/clutter-animation-tutorial.xml b/doc/reference/clutter/clutter-animation-tutorial.xml index 306f81cf8..101e89573 100644 --- a/doc/reference/clutter/clutter-animation-tutorial.xml +++ b/doc/reference/clutter/clutter-animation-tutorial.xml @@ -298,13 +298,12 @@ main (int argc, char *argv[]) ClutterBehaviour *behave; ClutterAlpha *alpha; ClutterActor *stage, *actor; - GdkPixbuf *pixbuf; clutter_init (&argc, &argv); 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); /* 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 * 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); /* 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 milliseconds to complete and uses a linear progression. - clutter_actor_animate (actor, CLUTTER_LINEAR, 200 - "x", 200, - "y", 200, + clutter_actor_animate (actor, CLUTTER_LINEAR, 200, + "x", 200.0f, + "y", 200.0f, NULL); diff --git a/doc/reference/clutter/clutter-docs.xml.in b/doc/reference/clutter/clutter-docs.xml.in index d00ec1d5c..38fe85287 100644 --- a/doc/reference/clutter/clutter-docs.xml.in +++ b/doc/reference/clutter/clutter-docs.xml.in @@ -139,8 +139,11 @@ + + + diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index a1a7b7581..5e67cbdad 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -404,6 +404,7 @@ clutter_actor_create_pango_layout clutter_actor_is_in_clone_paint clutter_actor_set_text_direction clutter_actor_get_text_direction +clutter_actor_has_pointer ClutterActorBox @@ -874,7 +875,7 @@ clutter_param_fixed_get_type
clutter-feature -GL Features +Features ClutterFeatureFlags clutter_feature_available clutter_feature_get_all @@ -940,7 +941,6 @@ ClutterMotionEvent ClutterScrollEvent ClutterStageStateEvent ClutterCrossingEvent -ClutterInputDevice clutter_event_new clutter_event_copy clutter_event_free @@ -977,13 +977,9 @@ clutter_event_get_related clutter_event_get_scroll_direction -ClutterInputDeviceType clutter_event_get_device clutter_event_get_device_id clutter_event_get_device_type -clutter_get_input_device_for_id -clutter_input_device_get_device_id -clutter_input_device_get_device_type clutter_get_current_event_time @@ -996,6 +992,51 @@ ClutterAnyEvent clutter_event_get_type
+
+clutter-input-device +ClutterInputDevice +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 + + +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 + + +clutter_input_device_get_type +
+ +
+clutter-device-manager +ClutterDeviceManager +ClutterDeviceManager +clutter_device_manager_get_default +clutter_device_manager_list_devices +clutter_device_manager_peek_devices +clutter_device_manager_get_device + + +clutter_get_input_device_for_id + + +CLUTTER_TYPE_DEVICE_MANAGER +CLUTTER_DEVICE_MANAGER +CLUTTER_IS_DEVICE_MANAGER + + +clutter_device_manager_get_type +
+
clutter-main General diff --git a/doc/reference/clutter/clutter.types b/doc/reference/clutter/clutter.types index 9c62cb757..b954e2ad6 100644 --- a/doc/reference/clutter/clutter.types +++ b/doc/reference/clutter/clutter.types @@ -40,3 +40,5 @@ clutter_fixed_layout_get_type clutter_bin_layout_get_type clutter_flow_layout_get_type clutter_box_layout_get_type +clutter_input_device_get_type +clutter_device_manager_get_type diff --git a/doc/reference/cogl/blend-strings.xml b/doc/reference/cogl/blend-strings.xml index 9c3c8d3c9..0b37757c7 100644 --- a/doc/reference/cogl/blend-strings.xml +++ b/doc/reference/cogl/blend-strings.xml @@ -59,14 +59,13 @@ to this OpenGL code: 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_ALPHA, GL_REPLACE); glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS); glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); - glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS); - glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); - glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_RGB, GL_TEXTURE); - glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); + glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS); + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_COLOR); + glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_TEXTURE); + glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_COLOR);
diff --git a/tests/Makefile.am b/tests/Makefile.am index e31dba9da..e1d8350b8 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -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 -.PHONY: test conform +if BUILD_TESTS test conform: - $(MAKE) -C ./conform test -.PHONY: test-report full-report + ( cd ./conform && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $$? + 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 check-local: test diff --git a/tests/conform/test-cogl-blend-strings.c b/tests/conform/test-cogl-blend-strings.c index 09113bbcd..d65c809b4 100644 --- a/tests/conform/test-cogl-blend-strings.c +++ b/tests/conform/test-cogl-blend-strings.c @@ -41,9 +41,9 @@ check_pixel (GLubyte *pixel, guint32 color) g_print (" expected = %x, %x, %x, %x\n", r, g, b, a); /* FIXME - allow for hardware in-precision */ - g_assert (pixel[RED] == r); - g_assert (pixel[GREEN] == g); - g_assert (pixel[BLUE] == b); + g_assert_cmpint (pixel[RED], ==, r); + g_assert_cmpint (pixel[GREEN], ==, g); + g_assert_cmpint (pixel[BLUE], ==, b); /* FIXME * We ignore the alpha, since we don't know if our render target is diff --git a/tests/conform/test-texture-fbo.c b/tests/conform/test-texture-fbo.c index 958fc2932..3a8dbc65f 100644 --- a/tests/conform/test-texture-fbo.c +++ b/tests/conform/test-texture-fbo.c @@ -180,6 +180,7 @@ test_texture_fbo (TestConformSimpleFixture *fixture, { TestState state; guint idle_source; + gulong paint_handler; ClutterActor *actor; int ypos = 0; @@ -236,12 +237,15 @@ test_texture_fbo (TestConformSimpleFixture *fixture, * will trigger redrawing. */ 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_main (); + g_signal_handler_disconnect (state.stage, paint_handler); + g_source_remove (idle_source); /* Remove all of the actors from the stage */ diff --git a/tests/interactive/test-cogl-multitexture.c b/tests/interactive/test-cogl-multitexture.c index d52567129..fb6cee865 100644 --- a/tests/interactive/test-cogl-multitexture.c +++ b/tests/interactive/test-cogl-multitexture.c @@ -11,15 +11,22 @@ typedef struct _TestMultiLayerMaterialState { - ClutterActor *group; - CoglHandle material; - CoglHandle alpha_tex; - CoglHandle redhand_tex; - CoglHandle light_tex0; - gfloat *tex_coords; + ClutterActor *group; + CoglHandle alpha_tex; + CoglHandle redhand_tex; + gfloat *tex_coords; - CoglMatrix tex_matrix; - CoglMatrix rot_matrix; + ClutterTimeline *timeline; + + CoglHandle material0; + CoglMatrix tex_matrix0; + CoglMatrix rot_matrix0; + CoglHandle light_tex0; + + CoglHandle material1; + CoglMatrix tex_matrix1; + CoglMatrix rot_matrix1; + CoglHandle light_tex1; } TestMultiLayerMaterialState; @@ -31,10 +38,15 @@ frame_cb (ClutterTimeline *timeline, { TestMultiLayerMaterialState *state = data; - cogl_matrix_multiply (&state->tex_matrix, - &state->tex_matrix, - &state->rot_matrix); - cogl_material_set_layer_matrix (state->material, 2, &state->tex_matrix); + cogl_matrix_multiply (&state->tex_matrix0, + &state->tex_matrix0, + &state->rot_matrix0); + 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 @@ -42,18 +54,51 @@ material_rectangle_paint (ClutterActor *actor, gpointer data) { TestMultiLayerMaterialState *state = data; - cogl_set_source (state->material); - cogl_rectangle_with_multitexture_coords (0, 0, 200, 200, + cogl_push_matrix (); + + cogl_translate (150, 15, 0); + + cogl_set_source (state->material0); + cogl_rectangle_with_multitexture_coords (0, 0, 200, 213, state->tex_coords, 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 test_cogl_multitexture_main (int argc, char *argv[]) { GError *error = NULL; - ClutterTimeline *timeline; - ClutterBehaviour *r_behave; ClutterActor *stage; ClutterColor stage_color = { 0x61, 0x56, 0x56, 0xff }; TestMultiLayerMaterialState *state = g_new0 (TestMultiLayerMaterialState, 1); @@ -113,55 +158,71 @@ test_cogl_multitexture_main (int argc, char *argv[]) if (!state->light_tex0) 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); - state->material = cogl_material_new (); - cogl_material_set_layer (state->material, 0, state->alpha_tex); - cogl_material_set_layer (state->material, 1, state->redhand_tex); - cogl_material_set_layer (state->material, 2, state->light_tex0); + state->material0 = cogl_material_new (); + cogl_material_set_layer (state->material0, 0, state->alpha_tex); + cogl_material_set_layer (state->material0, 1, state->redhand_tex); + 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; - cogl_matrix_init_identity (&state->tex_matrix); - cogl_matrix_init_identity (&state->rot_matrix); + cogl_matrix_init_identity (&state->tex_matrix0); + 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_rotate (&state->rot_matrix, 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_rotate (&state->rot_matrix0, 10.0, 0, 0, 1.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_container_add_actor (CLUTTER_CONTAINER(stage), state->group); - timeline = clutter_timeline_new (7692); - clutter_timeline_set_loop (timeline, TRUE); + state->timeline = clutter_timeline_new (2812); - 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_behaviour_rotate_new (clutter_alpha_new_full (timeline, - CLUTTER_LINEAR), - CLUTTER_Y_AXIS, - CLUTTER_ROTATE_CW, - 0.0, 360.0); - - /* Apply it to our actor */ - clutter_behaviour_apply (r_behave, state->group); + clutter_actor_animate_with_timeline (state->group, + CLUTTER_LINEAR, + state->timeline, + "rotation-angle-y", 30.0, + "signal-after::completed", + animation_completed_cb, state, + NULL); /* start the timeline and thus the animations */ - clutter_timeline_start (timeline); + clutter_timeline_start (state->timeline); clutter_actor_show_all (stage); 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->redhand_tex); cogl_handle_unref (state->light_tex0); + cogl_handle_unref (state->light_tex1); g_free (state); - g_object_unref (r_behave); - return 0; } diff --git a/tests/interactive/test-devices.c b/tests/interactive/test-devices.c index 4fd2a2c44..e286a064c 100644 --- a/tests/interactive/test-devices.c +++ b/tests/interactive/test-devices.c @@ -9,7 +9,31 @@ typedef struct { } 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 stage_motion_event_cb (ClutterActor *actor, @@ -24,6 +48,11 @@ stage_motion_event_cb (ClutterActor *actor, 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) { gfloat event_x, event_y; @@ -43,6 +72,7 @@ test_devices_main (int argc, char **argv) ClutterActor *stage; TestDevicesApp *app; ClutterColor stage_color = { 0x61, 0x64, 0x8c, 0xff }; + ClutterDeviceManager *manager; const GSList *stage_devices, *l; /* force enabling X11 support */ @@ -63,10 +93,11 @@ test_devices_main (int argc, char **argv) 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) - g_error ("No extended input devices found."); + g_error ("No input devices found."); for (l = stage_devices; l != NULL; l = l->next) { @@ -74,12 +105,15 @@ test_devices_main (int argc, char **argv) ClutterInputDeviceType device_type; ClutterActor *hand = NULL; - device_type = clutter_input_device_get_device_type (device); - if (device_type == CLUTTER_POINTER_DEVICE) - { - g_print ("got a pointer device with id %d...\n", - clutter_input_device_get_device_id (device)); + g_print ("got a %s device '%s' with id %d...\n", + device_type_name (device), + clutter_input_device_get_device_name (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 G_DIR_SEPARATOR_S "redhand.png", diff --git a/tests/interactive/test-easing.c b/tests/interactive/test-easing.c index bc0b73ab6..396d636fb 100644 --- a/tests/interactive/test-easing.c +++ b/tests/interactive/test-easing.c @@ -48,6 +48,8 @@ static gboolean recenter = FALSE; static ClutterActor *main_stage = NULL; static ClutterActor *easing_mode_label = NULL; +static ClutterAnimation *last_animation = NULL; + static void on_animation_completed (ClutterAnimation *animation, ClutterActor *rectangle) @@ -113,10 +115,12 @@ on_button_press (ClutterActor *actor, "y", event->y, NULL); - if (recenter) + if (recenter && last_animation != animation) g_signal_connect_after (animation, "completed", G_CALLBACK (on_animation_completed), rectangle); + + last_animation = animation; } return TRUE; diff --git a/tests/interactive/test-events.c b/tests/interactive/test-events.c index 0cd32c62c..3655fcc58 100644 --- a/tests/interactive/test-events.c +++ b/tests/interactive/test-events.c @@ -27,6 +27,12 @@ get_event_type_name (const ClutterEvent *event) case CLUTTER_LEAVE: return "LEAVE"; + case CLUTTER_MOTION: + return "MOTION"; + + case CLUTTER_DELETE: + return "DELETE"; + default: return "EVENT"; } @@ -124,94 +130,122 @@ fill_keybuf (char *keybuf, ClutterKeyEvent *event) /* printable character, if any (ß, ∑) */ len = g_unichar_to_utf8 (event->unicode_value, utf8); utf8[len] = '\0'; - sprintf(keybuf, "'%s' ", utf8); + sprintf (keybuf, "'%s' ", utf8); /* key combination (s, S, Delete) */ - len = g_unichar_to_utf8 (clutter_keysym_to_unicode (event->keyval), - utf8); + len = g_unichar_to_utf8 (clutter_keysym_to_unicode (event->keyval), utf8); utf8[len] = '\0'; if (event->modifier_state & CLUTTER_SHIFT_MASK) strcat (keybuf, ""); + if (event->modifier_state & CLUTTER_LOCK_MASK) strcat (keybuf, ""); + if (event->modifier_state & CLUTTER_CONTROL_MASK) strcat (keybuf, ""); + if (event->modifier_state & CLUTTER_MOD1_MASK) strcat (keybuf, ""); + if (event->modifier_state & CLUTTER_MOD2_MASK) strcat (keybuf, ""); + if (event->modifier_state & CLUTTER_MOD3_MASK) strcat (keybuf, ""); + if (event->modifier_state & CLUTTER_MOD4_MASK) strcat (keybuf, ""); + if (event->modifier_state & CLUTTER_MOD5_MASK) strcat (keybuf, ""); + strcat (keybuf, utf8); } static gboolean -input_cb (ClutterActor *actor, - ClutterEvent *event, - gpointer data) +input_cb (ClutterActor *actor, + ClutterEvent *event, + gpointer data) { 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) { case CLUTTER_KEY_PRESS: 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; case CLUTTER_KEY_RELEASE: 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; case CLUTTER_MOTION: - g_print ("[%s] MOTION", source); + g_print ("[%s] MOTION", + clutter_actor_get_name (source_actor)); break; 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)) + : ""); break; 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)) + : ""); break; case CLUTTER_BUTTON_PRESS: - g_print ("[%s] BUTTON PRESS (click count:%i)", - source, event->button.click_count); + g_print ("[%s] BUTTON PRESS (button:%i, click count:%i)", + clutter_actor_get_name (source_actor), + clutter_event_get_button (event), + clutter_event_get_click_count (event)); break; case CLUTTER_BUTTON_RELEASE: - g_print ("[%s] BUTTON RELEASE (click count:%i)", - source, event->button.click_count); + g_print ("[%s] BUTTON RELEASE (button:%i, click count:%i)", + 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); - else if (clutter_event_get_source (event) == actor - && clutter_actor_get_parent (actor) == CLUTTER_ACTOR (stage)) + else if (source_actor == actor && + clutter_actor_get_parent (actor) == CLUTTER_ACTOR (stage)) clutter_stage_set_key_focus (stage, actor); break; case CLUTTER_SCROLL: - g_print ("[%s] BUTTON SCROLL (click count:%i)", - source, event->button.click_count); + g_print ("[%s] BUTTON SCROLL (direction:%s)", + clutter_actor_get_name (source_actor), + clutter_event_get_scroll_direction (event) == CLUTTER_SCROLL_UP + ? "up" + : "down"); break; case CLUTTER_STAGE_STATE: - g_print ("[%s] STAGE STATE", source); + g_print ("[%s] STAGE STATE", clutter_actor_get_name (source_actor)); break; case CLUTTER_DESTROY_NOTIFY: - g_print ("[%s] DESTROY NOTIFY", source); + g_print ("[%s] DESTROY NOTIFY", clutter_actor_get_name (source_actor)); break; case CLUTTER_CLIENT_MESSAGE: - g_print ("[%s] CLIENT MESSAGE", source); + g_print ("[%s] CLIENT MESSAGE", clutter_actor_get_name (source_actor)); break; case CLUTTER_DELETE: - g_print ("[%s] DELETE", source); + g_print ("[%s] DELETE", clutter_actor_get_name (source_actor)); break; case CLUTTER_NOTHING: return FALSE; } - if (clutter_event_get_source (event) == actor) + if (source_actor == actor) g_print (" *source*"); g_print ("\n"); @@ -234,8 +268,8 @@ test_events_main (int argc, char *argv[]) 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, "fullscreen", G_CALLBACK (stage_state_cb), "fullscreen"); g_signal_connect (stage, "unfullscreen", @@ -244,24 +278,21 @@ test_events_main (int argc, char *argv[]) G_CALLBACK (stage_state_cb), "activate"); g_signal_connect (stage, "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); + clutter_actor_set_name (focus_box, "Focus Box"); clutter_container_add (CLUTTER_CONTAINER(stage), focus_box, NULL); actor = clutter_rectangle_new_with_color (&rcol); + clutter_actor_set_name (actor, "Red Box"); clutter_actor_set_size (actor, 100, 100); clutter_actor_set_position (actor, 100, 100); - clutter_actor_set_reactive (actor, TRUE); - clutter_container_add (CLUTTER_CONTAINER (stage), actor, NULL); - g_signal_connect (actor, "event", G_CALLBACK (input_cb), "red box"); g_signal_connect (actor, "key-focus-in", G_CALLBACK (key_focus_in_cb), focus_box); - /* Toggle motion - enter/leave capture */ g_signal_connect (actor, "button-press-event", 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); actor = clutter_rectangle_new_with_color (&gcol); + clutter_actor_set_name (actor, "Green Box"); clutter_actor_set_size (actor, 100, 100); clutter_actor_set_position (actor, 250, 100); - clutter_actor_set_reactive (actor, TRUE); - clutter_container_add (CLUTTER_CONTAINER (stage), actor, NULL); - g_signal_connect (actor, "event", G_CALLBACK (input_cb), "green box"); g_signal_connect (actor, "key-focus-in", G_CALLBACK (key_focus_in_cb), focus_box); - g_signal_connect (actor, "captured-event", G_CALLBACK (capture_cb), NULL); actor = clutter_rectangle_new_with_color (&bcol); + clutter_actor_set_name (actor, "Blue Box"); clutter_actor_set_size (actor, 100, 100); clutter_actor_set_position (actor, 400, 100); - clutter_actor_set_reactive (actor, TRUE); - clutter_container_add (CLUTTER_CONTAINER(stage), actor, NULL); - g_signal_connect (actor, "event", G_CALLBACK (input_cb), "blue box"); g_signal_connect (actor, "key-focus-in", G_CALLBACK (key_focus_in_cb), focus_box); @@ -299,20 +325,19 @@ test_events_main (int argc, char *argv[]) /* non reactive */ actor = clutter_rectangle_new_with_color (&ncol); + clutter_actor_set_name (actor, "Black Box"); clutter_actor_set_size (actor, 400, 50); clutter_actor_set_position (actor, 100, 250); - clutter_container_add (CLUTTER_CONTAINER(stage), actor, NULL); - g_signal_connect (actor, "event", G_CALLBACK (input_cb), "blue box"); g_signal_connect (actor, "key-focus-in", G_CALLBACK (key_focus_in_cb), focus_box); - g_signal_connect (stage, "key-focus-in", G_CALLBACK (key_focus_in_cb), focus_box); /* non reactive group, with reactive child */ actor = clutter_rectangle_new_with_color (&ycol); + clutter_actor_set_name (actor, "Yellow Box"); clutter_actor_set_size (actor, 100, 100); clutter_actor_set_reactive (actor, TRUE);