From 148145ea1ad24fb1ceca3229c4cae9c7c5755f75 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 15 Jan 2010 14:47:20 +0000 Subject: [PATCH 01/79] docs: Ignore clutter-profile.h --- doc/reference/clutter/Makefile.am | 57 ++++++++++++++++--------------- 1 file changed, 29 insertions(+), 28 deletions(-) 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=\ From e019547e8c7d110332a9ba071737736a059f5b4b Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 15 Jan 2010 14:48:42 +0000 Subject: [PATCH 02/79] x11: Fix typo in clutter_x11_set_use_argb_visual() declaration The function should have a lowercase x11, not an uppercase X11 in its name. --- clutter/x11/clutter-x11.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clutter/x11/clutter-x11.h b/clutter/x11/clutter-x11.h index 368a117b0..66bea5863 100644 --- a/clutter/x11/clutter-x11.h +++ b/clutter/x11/clutter-x11.h @@ -128,7 +128,7 @@ 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); From cdfd9eb212829df0408431673bedfbe6b566fefe Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 15 Jan 2010 14:50:06 +0000 Subject: [PATCH 03/79] Add test-cogl-texture-mipmaps to the Git ignore file --- .gitignore | 1 + 1 file changed, 1 insertion(+) 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 From ea662b9ecaf1dd956f0cdc79ce04880270bf99de Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 15 Jan 2010 17:06:56 +0000 Subject: [PATCH 04/79] docs: Display the features section The features section of the API reference is built but not used, and it has some copy-and-paste errors. --- clutter/clutter-feature.c | 11 +++++++++-- clutter/clutter-feature.h | 8 -------- doc/reference/clutter/clutter-docs.xml.in | 1 + doc/reference/clutter/clutter-sections.txt | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) 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/doc/reference/clutter/clutter-docs.xml.in b/doc/reference/clutter/clutter-docs.xml.in index d00ec1d5c..921e7cb62 100644 --- a/doc/reference/clutter/clutter-docs.xml.in +++ b/doc/reference/clutter/clutter-docs.xml.in @@ -141,6 +141,7 @@ + diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index a1a7b7581..132770d63 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -874,7 +874,7 @@ clutter_param_fixed_get_type
clutter-feature -GL Features +Features ClutterFeatureFlags clutter_feature_available clutter_feature_get_all From 12a9150f5b455e5700c745de4ad1ac5f2e43631f Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 15 Jan 2010 17:32:46 +0000 Subject: [PATCH 05/79] docs: Clarify usage of UTF-8 or ASCII art in commit messages --- doc/HACKING | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 3d373c7278c47b18576edc8c8a0074654ce4a60e Mon Sep 17 00:00:00 2001 From: Samuel Degrande Date: Fri, 27 Nov 2009 16:53:50 +0100 Subject: [PATCH 06/79] win32: Fix computation of the fullscreen size during stage realization http://bugzilla.openedhand.com/show_bug.cgi?id=1905 Signed-off-by: Neil Roberts --- clutter/win32/clutter-stage-win32.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clutter/win32/clutter-stage-win32.c b/clutter/win32/clutter-stage-win32.c index fbd6f533d..9bdab86a3 100644 --- a/clutter/win32/clutter-stage-win32.c +++ b/clutter/win32/clutter-stage-win32.c @@ -442,7 +442,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 { From ce030a3fce362cf4238d361743aaf9027ceed95a Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Mon, 18 Jan 2010 12:35:05 +0000 Subject: [PATCH 07/79] clutter-group: Use g_list_foreach in clutter_group_real_foreach g_list_foreach has better protection against the current node being removed. This will happen for example if someone calls clutter_container_foreach(container, clutter_actor_destroy). This was causing valgrind errors for the conformance tests which do just that. --- clutter/clutter-group.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) 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 From ff1d9cf090aef74efa1ec04b8cd490a45b148ae4 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Mon, 18 Jan 2010 12:42:20 +0000 Subject: [PATCH 08/79] test-texture-fbo: Disconnect the paint handler for the stage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Otherwise the paint handler will still be run for the subsequent tests. This ends up writing to the ‘state’ variable which used to be on the stack so it will end up corrupting some stack variable. This was causing test-cogl-premult to fail. --- tests/conform/test-texture-fbo.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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 */ From fa51ff25d3555ce938922d7bbd64acb12140a774 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Mon, 18 Jan 2010 13:56:56 +0000 Subject: [PATCH 09/79] Add build/mingw/{README,mingw-cross-compile.sh} to the dist tarball It's quite difficult to get git working on Windows so it makes sense to put the build instructions somewhere accessible. --- build/Makefile.am | 2 +- build/mingw/Makefile.am | 1 + configure.ac | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 build/mingw/Makefile.am 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/configure.ac b/configure.ac index 302e6686b..73216d926 100644 --- a/configure.ac +++ b/configure.ac @@ -868,6 +868,7 @@ AC_CONFIG_FILES([ Makefile build/Makefile build/autotools/Makefile + build/mingw/Makefile clutter/Makefile clutter/clutter-version.h clutter/clutter-json.h From 8daff42b3bcc9d59d7c87801b3926d8ed7c105a4 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Mon, 18 Jan 2010 15:43:25 +0000 Subject: [PATCH 10/79] README: Clarify the required OpenGL version Cogl will not allow OpenGL 1.2 if it doesn't have the multitexturing extension so we should make this clear in the README. --- README | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README b/README index 46674a119..6ac539fee 100644 --- a/README +++ b/README @@ -9,11 +9,16 @@ 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 +requires the latest GL headers which can be obtained from +www.khronos.org. + If you are building the API reference you will also need: • GTK-Doc >= 1.11 From 07ff8d26f8097a00757e6e3cfcd587bce188ae0d Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Mon, 18 Jan 2010 15:44:58 +0000 Subject: [PATCH 11/79] Release Clutter 1.1.6 --- NEWS | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ configure.ac | 2 +- 2 files changed, 50 insertions(+), 1 deletion(-) 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/configure.ac b/configure.ac index 73216d926..5a984d6d0 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], [6]) m4_define([clutter_release_status], [m4_if(m4_eval(clutter_micro_version % 2), [1], [git], From 4db89759a0aa91b353ac0bb41562addfff6d76c9 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Mon, 18 Jan 2010 17:33:08 +0000 Subject: [PATCH 12/79] Post-release version bump to 1.1.7 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 5a984d6d0..ab96a92e4 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], [6]) +m4_define([clutter_micro_version], [7]) m4_define([clutter_release_status], [m4_if(m4_eval(clutter_micro_version % 2), [1], [git], From 14a28620ae13ef041696e2e3747ec00738cfd2bd Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Fri, 15 Jan 2010 22:56:37 +0000 Subject: [PATCH 13/79] win32: Use an invisible cursor when cursor-visible is FALSE The win32 backend now handles the WM_SETCURSOR message and sets a fully transparent cursor if the cursor-visible property has been cleared on the stage. The icon is stored in the library via a resource file. The instance handle for the DLL is needed to load the resource so there is now a DllMain function to grab the handle. --- clutter/Makefile.am | 21 ++++++++++++- clutter/win32/Makefile.am | 2 +- clutter/win32/clutter-backend-win32.c | 25 +++++++++++++++ clutter/win32/clutter-backend-win32.h | 4 +++ clutter/win32/clutter-event-win32.c | 14 +++++++++ clutter/win32/clutter-stage-win32.c | 42 +++++++++++++++++++++++--- clutter/win32/clutter-stage-win32.h | 2 ++ clutter/win32/invisible-cursor.cur | Bin 0 -> 86 bytes clutter/win32/resources.rc | 1 + configure.ac | 7 +++++ 10 files changed, 112 insertions(+), 6 deletions(-) create mode 100644 clutter/win32/invisible-cursor.cur create mode 100644 clutter/win32/resources.rc diff --git a/clutter/Makefile.am b/clutter/Makefile.am index 401b3e5e9..44791ee85 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 \ @@ -205,7 +222,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 +237,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/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..16bf3f976 100644 --- a/clutter/win32/clutter-backend-win32.c +++ b/clutter/win32/clutter-backend-win32.c @@ -47,6 +47,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) @@ -67,6 +69,18 @@ clutter_backend_win32_init_events (ClutterBackend *backend) _clutter_backend_win32_events_init (backend); } +HCURSOR +_clutter_backend_win32_get_invisible_cursor (ClutterBackend *backend) +{ + ClutterBackendWin32 *backend_win32 = CLUTTER_BACKEND_WIN32 (backend); + + if (backend_win32->invisible_cursor == NULL) + backend_win32->invisible_cursor = + LoadCursor (clutter_hinst, MAKEINTRESOURCE (42)); + + return backend_win32->invisible_cursor; +} + static const GOptionEntry entries[] = { { @@ -353,6 +367,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 +385,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..465917ce3 100644 --- a/clutter/win32/clutter-backend-win32.h +++ b/clutter/win32/clutter-backend-win32.h @@ -48,6 +48,8 @@ struct _ClutterBackendWin32 HGLRC gl_context; gboolean no_event_retrieval; + HCURSOR invisible_cursor; + GSource *event_source; }; @@ -68,6 +70,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..cbebe9e0b 100644 --- a/clutter/win32/clutter-event-win32.c +++ b/clutter/win32/clutter-event-win32.c @@ -616,6 +616,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 9bdab86a3..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 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 0000000000000000000000000000000000000000..64f53b7cd7ea3af6ad3c8007a85ed6a1c4ed0553 GIT binary patch literal 86 wcmZQzU}9ioU}9oo009Re76W1pAO?xB05KyFf&d4Ygb)zbNCHsie;^IQ05ntrm;e9( literal 0 HcmV?d00001 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/configure.ac b/configure.ac index ab96a92e4..9f0762403 100644 --- a/configure.ac +++ b/configure.ac @@ -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], From d34f1aa775e8a66bd0e599b32c761869b319c639 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 20 Nov 2009 15:35:40 +0000 Subject: [PATCH 14/79] Add ClutterDeviceManager The ClutterDeviceManager is a singleton object that behaves like the StageManager: it holds all input devices and notifies on addition and removal. --- clutter/Makefile.am | 6 +- clutter/clutter-device-manager.c | 146 +++++++++++++++++++++++++++++++ clutter/clutter-device-manager.h | 30 +++++++ clutter/clutter-main.c | 30 +++---- clutter/clutter-marshal.list | 1 + clutter/clutter-private.h | 43 +++++---- clutter/clutter.h | 1 + 7 files changed, 224 insertions(+), 33 deletions(-) create mode 100644 clutter/clutter-device-manager.c create mode 100644 clutter/clutter-device-manager.h diff --git a/clutter/Makefile.am b/clutter/Makefile.am index 44791ee85..c61aea192 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -82,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 \ @@ -154,6 +155,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 \ diff --git a/clutter/clutter-device-manager.c b/clutter/clutter-device-manager.c new file mode 100644 index 000000000..bfed42fb8 --- /dev/null +++ b/clutter/clutter-device-manager.c @@ -0,0 +1,146 @@ +#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) +{ + 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__POINTER, + G_TYPE_NONE, 1, + G_TYPE_POINTER); + + 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__POINTER, + G_TYPE_NONE, 1, + G_TYPE_POINTER); +} + +static void +clutter_device_manager_init (ClutterDeviceManager *self) +{ +} + +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; +} + +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); +} + +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; +} + +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; +} + +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); +} + +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); +} diff --git a/clutter/clutter-device-manager.h b/clutter/clutter-device-manager.h new file mode 100644 index 000000000..6d4893d73 --- /dev/null +++ b/clutter/clutter-device-manager.h @@ -0,0 +1,30 @@ +#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)) + +typedef struct _ClutterDeviceManager ClutterDeviceManager; + +GType clutter_stage_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-main.c b/clutter/clutter-main.c index ea5e19d14..7d69fb8a0 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -3025,7 +3025,17 @@ clutter_get_font_flags (void) * clutter_get_input_device_for_id: * @id: a device id * - * 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 +3044,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-private.h b/clutter/clutter-private.h index dd687d010..2567eaae9 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,33 @@ typedef enum { struct _ClutterInputDevice { - gint id; + gint id; ClutterInputDeviceType device_type; ClutterActor *pointer_grab_actor; ClutterActor *motion_last_actor; - gint click_count; - gint previous_x; - gint previous_y; - guint32 previous_time; - gint previous_button_number; + gint click_count; + gint previous_x; + gint previous_y; + guint32 previous_time; + gint previous_button_number; }; -typedef struct _ClutterMainContext ClutterMainContext; +struct _ClutterStageManager +{ + GObject parent_instance; + + GSList *stages; +}; + +struct _ClutterDeviceManager +{ + GObject parent_instance; + + GSList *devices; +}; struct _ClutterMainContext { @@ -170,21 +185,19 @@ 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); + /* 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); diff --git a/clutter/clutter.h b/clutter/clutter.h index 087d93d40..971910976 100644 --- a/clutter/clutter.h +++ b/clutter/clutter.h @@ -51,6 +51,7 @@ #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" From 3027d4327ae0c5d9bb7cc831f24d5ee5e8fb107f Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 20 Nov 2009 15:36:43 +0000 Subject: [PATCH 15/79] Port the X11 backend to the Device Manager Use the device manager to store the input devices. Also, provide two fallback devices when initializing the X11 backend: device 0 for the pointer and device 1 for the keyboard. --- clutter/clutter-private.h | 3 - clutter/x11/clutter-backend-x11.c | 516 +++++++++++++++--------------- clutter/x11/clutter-backend-x11.h | 6 - clutter/x11/clutter-event-x11.c | 5 +- clutter/x11/clutter-x11.h | 2 + 5 files changed, 257 insertions(+), 275 deletions(-) diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index 2567eaae9..9100ebfd7 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -153,9 +153,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; diff --git a/clutter/x11/clutter-backend-x11.c b/clutter/x11/clutter-backend-x11.c index 4eaf08106..47ba733c7 100644 --- a/clutter/x11/clutter-backend-x11.c +++ b/clutter/x11/clutter-backend-x11.c @@ -46,15 +46,17 @@ #include #endif +#include "cogl/cogl.h" + +#include "../clutter-debug.h" +#include "../clutter-device-manager.h" #include "../clutter-event.h" #include "../clutter-main.h" -#include "../clutter-debug.h" #include "../clutter-private.h" -#include "cogl/cogl.h" - G_DEFINE_TYPE (ClutterBackendX11, clutter_backend_x11, CLUTTER_TYPE_BACKEND); +/* a specific X11 input device */ struct _ClutterX11XInputDevice { ClutterInputDevice device; @@ -66,11 +68,6 @@ struct _ClutterX11XInputDevice #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 +106,222 @@ 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; + gboolean have_an_xpointer = FALSE; +#ifdef HAVE_XINPUT + XDeviceInfo *x_devices = NULL; + int res, opcode, event, error; + int i, n_devices; + GSList *devices = NULL; +#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; + } + + 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) + { + ClutterInputDevice *device; + XDevice *x_device; + int n_events = 0; + int j; + + clutter_x11_trap_x_errors (); + x_device = XOpenDevice (backend->xdpy, info->id); + if (clutter_x11_untrap_x_errors () || x_device == NULL) + { + CLUTTER_NOTE (BACKEND, "Unable to open device %li", info->id); + continue; + } + + device = g_slice_new0 (ClutterX11XInputDevice); + device->device.id = info->id; + device->device.click_count = 0; + device->device.previous_time = CLUTTER_CURRENT_TIME; + device->device.previous_x = -1; + device->device.previous_y = -1; + device->device.previous_button_number = -1; + + switch (info->use) + { + case IsXExtensionPointer: + device->device.device_type = CLUTTER_POINTER_DEVICE; + have_an_xpointer = TRUE; + break; + +#if 0 + /* XInput1 is broken for keyboards */ + case IsXExtensionKeyboard: + device->device.device_type = CLUTTER_KEYBOARD_DEVICE; + break; +#endif + + case IsXExtensionDevice: + device->device.device_type = CLUTTER_EXTENSION_DEVICE; + break; + } + + device->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 (j = 0; j < xdevice->num_classes; j++) + { + XInputClassInfo *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]); + n_events++; + + DeviceKeyRelease (xdevice, + x11b->event_types[CLUTTER_X11_XINPUT_KEY_RELEASE_EVENT], + device->xevent_list [num_events]); + n_events++; + break; +#endif + + case ButtonClass: + DeviceButtonPress (xdevice, + x11b->event_types[CLUTTER_X11_XINPUT_BUTTON_PRESS_EVENT], + device->xevent_list [num_events]); + n_events++; + + DeviceButtonRelease (xdevice, + x11b->event_types[CLUTTER_X11_XINPUT_BUTTON_RELEASE_EVENT], + device->xevent_list [num_events]); + n_events++; + break; + + case ValuatorClass: + DeviceMotionNotify (xdevice, + x11b->event_types[CLUTTER_X11_XINPUT_MOTION_NOTIFY_EVENT], + device->xevent_list [num_events]); + n_events++; + break; + } + } + + if (info->use == IsXExtensionPointer && num_events > 0) + have_an_xpointer = TRUE; + + device->num_events = n_events; + + /* add it to a temporary list; we don't add the device + * straight to the device manager because the XInput + * initialization might still fail + */ + devices = g_slist_prepend (devices, device); + } + } + + XFree (x_devices); + + devices = g_slist_reverse (devices); + + if (have_an_xpointer) + { + GSList *l; + + for (l = devices; l != NULL; l = l->next) + _clutter_device_manager_add_device (manager, l->data); + } + else + { + GSList *l; + + g_warning ("No usable XInput pointer devices found"); + + for (l = devices; l != NULL; l = l->next) + g_slice_free (ClutterX11XInputDevice, l->data); + } + + g_slist_free (devices); + + backend->have_xinput = TRUE; +#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 do not have a XInput pointer device + * + * we register two default devices, one for the pointer + * and one for the keyboard + */ + if (!have_an_xpointer) + { + ClutterInputDevice *d; + + d = g_slice_new0 (ClutterInputDevice); + d->id = 0; + d->device_type = CLUTTER_POINTER_DEVICE; + d->click_count = 0; + d->previous_time = CLUTTER_CURRENT_TIME; + d->previous_x = -1; + d->previous_y = -1; + d->previous_button_number = -1; + _clutter_device_manager_add_device (manager, d); + + d = g_slice_new0 (ClutterInputDevice); + d->id = 1; + d->device_type = CLUTTER_KEYBOARD_DEVICE; + _clutter_device_manager_add_device (manager, d); + } +} + gboolean clutter_backend_x11_pre_parse (ClutterBackend *backend, GError **error) @@ -199,9 +412,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,234 +856,27 @@ 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) + manager = clutter_device_manager_get_default (); + + for (l = clutter_device_manager_peek_devices (manager); + l != NULL; + l = l->next) { - device = (ClutterX11XInputDevice *)list_it->data; + ClutterX11XInputDevice *device = l->data; XSelectExtensionEvent (backend_singleton->xdpy, xwin, @@ -884,49 +889,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 +933,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..95cd4c3fa 100644 --- a/clutter/x11/clutter-backend-x11.h +++ b/clutter/x11/clutter-backend-x11.h @@ -125,12 +125,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..a5886aec8 100644 --- a/clutter/x11/clutter-event-x11.c +++ b/clutter/x11/clutter-event-x11.c @@ -696,8 +696,8 @@ event_translate (ClutterBackend *backend, 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; @@ -705,10 +705,9 @@ event_translate (ClutterBackend *backend, event->button.y = xevent->xbutton.y; event->button.modifier_state = xevent->xbutton.state; event->button.button = xevent->xbutton.button; - break; } - + set_user_time (backend_x11, &xwindow, event->button.time); break; diff --git a/clutter/x11/clutter-x11.h b/clutter/x11/clutter-x11.h index 66bea5863..30c71ada6 100644 --- a/clutter/x11/clutter-x11.h +++ b/clutter/x11/clutter-x11.h @@ -121,7 +121,9 @@ 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); From ca16446319b6281cf63edf3b1b6d7d4f8d41e499 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 20 Nov 2009 15:43:50 +0000 Subject: [PATCH 16/79] Add :is-default flag to InputDevice --- clutter/clutter-private.h | 2 ++ clutter/x11/clutter-backend-x11.c | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index 9100ebfd7..e2d5e2132 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -97,6 +97,8 @@ struct _ClutterInputDevice gint previous_y; guint32 previous_time; gint previous_button_number; + + guint is_default : 1; }; struct _ClutterStageManager diff --git a/clutter/x11/clutter-backend-x11.c b/clutter/x11/clutter-backend-x11.c index 47ba733c7..c67963d1e 100644 --- a/clutter/x11/clutter-backend-x11.c +++ b/clutter/x11/clutter-backend-x11.c @@ -252,7 +252,12 @@ clutter_x11_register_input_devices (ClutterBackendX11 *backend) } if (info->use == IsXExtensionPointer && num_events > 0) - have_an_xpointer = TRUE; + { + /* mark it as a default */ + device->device.is_default = TRUE; + + have_an_xpointer = TRUE; + } device->num_events = n_events; @@ -313,11 +318,13 @@ default_device: d->previous_x = -1; d->previous_y = -1; d->previous_button_number = -1; + d->is_default = TRUE; _clutter_device_manager_add_device (manager, d); d = g_slice_new0 (ClutterInputDevice); d->id = 1; d->device_type = CLUTTER_KEYBOARD_DEVICE; + d->is_default = TRUE; _clutter_device_manager_add_device (manager, d); } } From 157da20e86f88bf906f0b59bafbea171a2e5e678 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 20 Nov 2009 16:24:16 +0000 Subject: [PATCH 17/79] x11: Always assign a device to pointer and key events Even when we are not using XInput we now have fallback devices; the X11 backend should always assign the default devices when translating the X events to Clutter events. --- clutter/x11/clutter-event-x11.c | 37 +++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/clutter/x11/clutter-event-x11.c b/clutter/x11/clutter-event-x11.c index a5886aec8..af85238d7 100644 --- a/clutter/x11/clutter-event-x11.c +++ b/clutter/x11/clutter-event-x11.c @@ -410,14 +410,15 @@ 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; - backend_x11 = CLUTTER_BACKEND_X11 (backend); + backend_x11 = CLUTTER_BACKEND_X11 (backend); xwindow = xevent->xany.window; @@ -458,9 +459,11 @@ event_translate (ClutterBackend *backend, if (stage == NULL) 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 +624,8 @@ event_translate (ClutterBackend *backend, case KeyPress: event->key.type = event->type = CLUTTER_KEY_PRESS; + /* default key device if no XInput support is defined */ + event->key.device = clutter_device_manager_get_device (manager, 1); translate_key_event (backend, event, xevent); set_user_time (backend_x11, &xwindow, xevent->xkey.time); @@ -654,6 +659,8 @@ event_translate (ClutterBackend *backend, } event->key.type = event->type = CLUTTER_KEY_RELEASE; + /* default key device if no XInput support is defined */ + event->key.device = clutter_device_manager_get_device (manager, 1); translate_key_event (backend, event, xevent); break; @@ -696,6 +703,8 @@ event_translate (ClutterBackend *backend, event->scroll.x = xevent->xbutton.x; event->scroll.y = xevent->xbutton.y; event->scroll.modifier_state = xevent->xbutton.state; + event->scroll.device = + clutter_device_manager_get_device (manager, 0); break; default: @@ -705,6 +714,8 @@ event_translate (ClutterBackend *backend, event->button.y = xevent->xbutton.y; event->button.modifier_state = xevent->xbutton.state; event->button.button = xevent->xbutton.button; + event->button.device = + clutter_device_manager_get_device (manager, 0); break; } @@ -728,6 +739,8 @@ event_translate (ClutterBackend *backend, event->button.y = xevent->xbutton.y; event->button.modifier_state = xevent->xbutton.state; event->button.button = xevent->xbutton.button; + event->button.device = + clutter_device_manager_get_device (manager, 0); break; case MotionNotify: @@ -736,6 +749,8 @@ event_translate (ClutterBackend *backend, event->motion.x = xevent->xmotion.x; event->motion.y = xevent->xmotion.y; event->motion.modifier_state = xevent->xmotion.state; + event->motion.device = + clutter_device_manager_get_device (manager, 0); break; case EnterNotify: @@ -746,6 +761,8 @@ event_translate (ClutterBackend *backend, event->motion.x = xevent->xcrossing.x; event->motion.y = xevent->xcrossing.y; event->motion.modifier_state = xevent->xcrossing.state; + event->motion.device = + clutter_device_manager_get_device (manager, 0); break; case LeaveNotify: @@ -753,6 +770,8 @@ event_translate (ClutterBackend *backend, event->crossing.time = xevent->xcrossing.time; event->crossing.x = xevent->xcrossing.x; event->crossing.y = xevent->xcrossing.y; + event->motion.device = + clutter_device_manager_get_device (manager, 0); break; default: From d5331bfb5786b496cfcab925319711853db2f1d3 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 20 Nov 2009 16:37:58 +0000 Subject: [PATCH 18/79] tests: Update the devices test Use the DeviceManager API instead of the X11 specific API. --- tests/interactive/test-devices.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/interactive/test-devices.c b/tests/interactive/test-devices.c index 4fd2a2c44..62923f812 100644 --- a/tests/interactive/test-devices.c +++ b/tests/interactive/test-devices.c @@ -43,6 +43,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 +64,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) { @@ -75,7 +77,7 @@ test_devices_main (int argc, char **argv) ClutterActor *hand = NULL; device_type = clutter_input_device_get_device_type (device); - if (device_type == CLUTTER_POINTER_DEVICE) + if (device_type == CLUTTER_POINTER_DEVICE) { g_print ("got a pointer device with id %d...\n", clutter_input_device_get_device_id (device)); From d23dd9af6bf48c3c02f685505a5a34b477d4d2b3 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 23 Nov 2009 16:07:16 +0000 Subject: [PATCH 19/79] device: Make InputDevice an object and subclass it for X11 ClutterInputDevice should be a type that we can subclass per-backend to add functionality. --- clutter/Makefile.am | 2 + clutter/clutter-event.h | 35 +---- clutter/clutter-input-device.c | 175 +++++++++++++++++++++++++ clutter/clutter-input-device.h | 64 +++++++++ clutter/clutter-main.c | 34 ++--- clutter/clutter-private.h | 19 ++- clutter/clutter.h | 1 + clutter/x11/Makefile.am | 2 + clutter/x11/clutter-backend-x11.c | 141 +++++--------------- clutter/x11/clutter-input-device-x11.c | 151 +++++++++++++++++++++ clutter/x11/clutter-input-device-x11.h | 25 ++++ 11 files changed, 484 insertions(+), 165 deletions(-) create mode 100644 clutter/clutter-input-device.c create mode 100644 clutter/clutter-input-device.h create mode 100644 clutter/x11/clutter-input-device-x11.c create mode 100644 clutter/x11/clutter-input-device-x11.h diff --git a/clutter/Makefile.am b/clutter/Makefile.am index c61aea192..d8fb48fd0 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -92,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 \ @@ -164,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 \ 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-input-device.c b/clutter/clutter-input-device.c new file mode 100644 index 000000000..dae5e92e2 --- /dev/null +++ b/clutter/clutter-input-device.c @@ -0,0 +1,175 @@ +#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 +}; + +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; + + 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; + + 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; + + 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); + + 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->previous_time = CLUTTER_CURRENT_TIME; + self->previous_x = -1; + self->previous_y = -1; + self->previous_button_number = -1; + self->previous_state = 0; +} + +void +_clutter_input_device_set_coords (ClutterInputDevice *device, + gfloat x, + gfloat y) +{ + g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device)); + + device->previous_x = floorf (x) + 0.5; + device->previous_y = floorf (y) + 0.5; +} + +void +_clutter_input_device_set_state (ClutterInputDevice *device, + ClutterModifierType state) +{ + g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device)); + + device->previous_state = state; +} + +void +_clutter_input_device_set_time (ClutterInputDevice *device, + guint32 time_) +{ + g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device)); + + device->previous_time = time_; +} + +/** + * 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; +} diff --git a/clutter/clutter-input-device.h b/clutter/clutter-input-device.h new file mode 100644 index 000000000..1d36278bc --- /dev/null +++ b/clutter/clutter-input-device.h @@ -0,0 +1,64 @@ +#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; + +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); + +G_END_DECLS + +#endif /* __CLUTTER_INPUT_DEVICE_H__ */ diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 7d69fb8a0..d0438bbf2 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -2046,10 +2046,10 @@ event_click_count_generate (ClutterEvent *event) static gint previous_button_number = -1; 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); @@ -2074,11 +2074,11 @@ event_click_count_generate (ClutterEvent *event) (ABS (event->button.y - previous_y) <= double_click_distance) && event->button.button == previous_button_number) { - click_count ++; + click_count += 1; } else /* start a new click count*/ { - click_count=1; + click_count = 1; previous_button_number = event->button.button; } @@ -2086,13 +2086,14 @@ event_click_count_generate (ClutterEvent *event) * next event */ previous_time = event->button.time; - previous_x = event->button.x; - previous_y = event->button.y; + 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); } @@ -2107,7 +2108,6 @@ event_click_count_generate (ClutterEvent *event) } } - static inline void emit_event (ClutterEvent *event, gboolean is_key_event) @@ -2209,7 +2209,7 @@ emit_pointer_event (ClutterEvent *event, 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); @@ -2220,7 +2220,7 @@ emit_keyboard_event (ClutterEvent *event) static void unset_motion_last_actor (ClutterActor *actor, ClutterInputDevice *dev) { - ClutterMainContext *context = ClutterCntx; + ClutterMainContext *context = _clutter_context_get_default (); if (dev == NULL) context->motion_last_actor = NULL; @@ -2232,8 +2232,8 @@ static void set_motion_last_actor (ClutterActor *motion_current_actor, ClutterInputDevice *device) { - ClutterMainContext *context = ClutterCntx; - ClutterActor *last_actor = context->motion_last_actor; + ClutterMainContext *context = _clutter_context_get_default (); + ClutterActor *last_actor = context->motion_last_actor; if (device != NULL) last_actor = device->motion_last_actor; @@ -2262,10 +2262,10 @@ set_motion_last_actor (ClutterActor *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); + ClutterMainContext *context = _clutter_context_get_default (); + 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; diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index e2d5e2132..4f16bc33b 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -53,7 +53,7 @@ G_BEGIN_DECLS -typedef struct _ClutterMainContext ClutterMainContext; +typedef struct _ClutterMainContext ClutterMainContext; typedef enum { CLUTTER_ACTOR_UNUSED_FLAG = 0, @@ -85,6 +85,8 @@ typedef enum { struct _ClutterInputDevice { + GObject parent_instance; + gint id; ClutterInputDeviceType device_type; @@ -93,10 +95,12 @@ struct _ClutterInputDevice ClutterActor *motion_last_actor; gint click_count; - gint previous_x; - gint previous_y; + + gfloat previous_x; + gfloat previous_y; guint32 previous_time; gint previous_button_number; + ClutterModifierType previous_state; guint is_default : 1; }; @@ -190,6 +194,15 @@ void _clutter_device_manager_add_device (ClutterDeviceManager *device_manager void _clutter_device_manager_remove_device (ClutterDeviceManager *device_manager, ClutterInputDevice *device); +/* input device */ +void _clutter_input_device_set_coords (ClutterInputDevice *device, + gfloat x, + gfloat y); +void _clutter_input_device_set_state (ClutterInputDevice *device, + ClutterModifierType state); +void _clutter_input_device_set_time (ClutterInputDevice *device, + guint32 time_); + /* stage manager */ void _clutter_stage_manager_add_stage (ClutterStageManager *stage_manager, ClutterStage *stage); diff --git a/clutter/clutter.h b/clutter/clutter.h index 971910976..fac430ae2 100644 --- a/clutter/clutter.h +++ b/clutter/clutter.h @@ -58,6 +58,7 @@ #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/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 c67963d1e..d8f026f05 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" @@ -56,18 +57,6 @@ G_DEFINE_TYPE (ClutterBackendX11, clutter_backend_x11, CLUTTER_TYPE_BACKEND); -/* a specific X11 input device */ -struct _ClutterX11XInputDevice -{ - ClutterInputDevice device; - -#ifdef HAVE_XINPUT - XDevice *xdevice; - XEventClass xevent_list[5]; /* MAX 5 event types */ - int num_events; -#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! @@ -159,108 +148,43 @@ clutter_x11_register_input_devices (ClutterBackendX11 *backend) /* info->use == IsXExtensionKeyboard || XInput1 is broken */ info->use == IsXExtensionDevice) { + ClutterInputDeviceType device_type; ClutterInputDevice *device; - XDevice *x_device; - int n_events = 0; - int j; - - clutter_x11_trap_x_errors (); - x_device = XOpenDevice (backend->xdpy, info->id); - if (clutter_x11_untrap_x_errors () || x_device == NULL) - { - CLUTTER_NOTE (BACKEND, "Unable to open device %li", info->id); - continue; - } - - device = g_slice_new0 (ClutterX11XInputDevice); - device->device.id = info->id; - device->device.click_count = 0; - device->device.previous_time = CLUTTER_CURRENT_TIME; - device->device.previous_x = -1; - device->device.previous_y = -1; - device->device.previous_button_number = -1; + gint n_events = 0; switch (info->use) { case IsXExtensionPointer: - device->device.device_type = CLUTTER_POINTER_DEVICE; + device_type = CLUTTER_POINTER_DEVICE; have_an_xpointer = TRUE; break; #if 0 /* XInput1 is broken for keyboards */ case IsXExtensionKeyboard: - device->device.device_type = CLUTTER_KEYBOARD_DEVICE; + device_type = CLUTTER_KEYBOARD_DEVICE; break; #endif case IsXExtensionDevice: - device->device.device_type = CLUTTER_EXTENSION_DEVICE; + device_type = CLUTTER_EXTENSION_DEVICE; break; } - device->xdevice = x_device; + device = g_object_new (CLUTTER_TYPE_INPUT_DEVICE_X11, + "id", info->id, + "device-type", device_type, + NULL); + n_events = _clutter_input_device_x11_construct (device, backend); - 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 (j = 0; j < xdevice->num_classes; j++) - { - XInputClassInfo *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]); - n_events++; - - DeviceKeyRelease (xdevice, - x11b->event_types[CLUTTER_X11_XINPUT_KEY_RELEASE_EVENT], - device->xevent_list [num_events]); - n_events++; - break; -#endif - - case ButtonClass: - DeviceButtonPress (xdevice, - x11b->event_types[CLUTTER_X11_XINPUT_BUTTON_PRESS_EVENT], - device->xevent_list [num_events]); - n_events++; - - DeviceButtonRelease (xdevice, - x11b->event_types[CLUTTER_X11_XINPUT_BUTTON_RELEASE_EVENT], - device->xevent_list [num_events]); - n_events++; - break; - - case ValuatorClass: - DeviceMotionNotify (xdevice, - x11b->event_types[CLUTTER_X11_XINPUT_MOTION_NOTIFY_EVENT], - device->xevent_list [num_events]); - n_events++; - break; - } - } - - if (info->use == IsXExtensionPointer && num_events > 0) + if (info->use == IsXExtensionPointer && n_events > 0) { /* mark it as a default */ - device->device.is_default = TRUE; + device->is_default = TRUE; have_an_xpointer = TRUE; } - device->num_events = n_events; - /* add it to a temporary list; we don't add the device * straight to the device manager because the XInput * initialization might still fail @@ -279,20 +203,19 @@ clutter_x11_register_input_devices (ClutterBackendX11 *backend) for (l = devices; l != NULL; l = l->next) _clutter_device_manager_add_device (manager, l->data); + + backend->have_xinput = TRUE; } else { - GSList *l; - g_warning ("No usable XInput pointer devices found"); - for (l = devices; l != NULL; l = l->next) - g_slice_free (ClutterX11XInputDevice, l->data); + g_slist_foreach (devices, (GFunc) g_object_unref, NULL); + + backend->have_xinput = FALSE; } g_slist_free (devices); - - backend->have_xinput = TRUE; #endif /* HAVE_XINPUT */ default_device: @@ -310,21 +233,20 @@ default_device: { ClutterInputDevice *d; - d = g_slice_new0 (ClutterInputDevice); - d->id = 0; - d->device_type = CLUTTER_POINTER_DEVICE; - d->click_count = 0; - d->previous_time = CLUTTER_CURRENT_TIME; - d->previous_x = -1; - d->previous_y = -1; - d->previous_button_number = -1; + d = g_object_new (CLUTTER_TYPE_INPUT_DEVICE_X11, + "id", 0, + "device-type", CLUTTER_POINTER_DEVICE, + NULL); d->is_default = TRUE; + CLUTTER_NOTE (BACKEND, "Added default pointer device %d", d->id); _clutter_device_manager_add_device (manager, d); - d = g_slice_new0 (ClutterInputDevice); - d->id = 1; - d->device_type = CLUTTER_KEYBOARD_DEVICE; + d = g_object_new (CLUTTER_TYPE_INPUT_DEVICE_X11, + "id", 1, + "device-type", CLUTTER_KEYBOARD_DEVICE, + NULL); d->is_default = TRUE; + CLUTTER_NOTE (BACKEND, "Added default keyboard device %d", d->id); _clutter_device_manager_add_device (manager, d); } } @@ -883,12 +805,9 @@ _clutter_x11_select_events (Window xwin) l != NULL; l = l->next) { - ClutterX11XInputDevice *device = l->data; + ClutterInputDevice *device = l->data; - XSelectExtensionEvent (backend_singleton->xdpy, - xwin, - device->xevent_list, - device->num_events); + _clutter_input_device_x11_select_events (device, backend_singleton, xwin); } #endif /* HAVE_XINPUT */ } diff --git a/clutter/x11/clutter-input-device-x11.c b/clutter/x11/clutter-input-device-x11.c new file mode 100644 index 000000000..e0edfd1e9 --- /dev/null +++ b/clutter/x11/clutter-input-device-x11.c @@ -0,0 +1,151 @@ +#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 +}; + +G_DEFINE_TYPE (ClutterInputDeviceX11, + clutter_input_device_x11, + CLUTTER_TYPE_INPUT_DEVICE); + +static void +clutter_input_device_x11_class_init (ClutterInputDeviceX11Class *klass) +{ +} + +static void +clutter_input_device_x11_init (ClutterInputDeviceX11 *self) +{ +} + +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; + + 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]; + + switch (xclass_info->input_class) + { +#if 0 + /* XInput 1.x is broken for keyboards: */ + case KeyClass: + DeviceKeyPress (xdevice, + backend->event_types[CLUTTER_X11_XINPUT_KEY_PRESS_EVENT], + device_x11->xevent_list[n_events]); + n_events++; + + DeviceKeyRelease (xdevice, + backend->event_types[CLUTTER_X11_XINPUT_KEY_RELEASE_EVENT], + device_x11->xevent_list[n_events]); + n_events++; + break; +#endif + + 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__ */ From 1f87cac0695dc95be4ed0219752d454d9a285e50 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 24 Nov 2009 12:53:57 +0000 Subject: [PATCH 20/79] actor: Add :has-pointer property ClutterActor should be able to tell whether a pointer is within its area or not. --- clutter/clutter-actor.c | 60 ++++++++++++++++++++++++++++++++++++++- clutter/clutter-actor.h | 1 + clutter/clutter-private.h | 3 ++ 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index c873236b4..68cb38354 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 @@ -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; @@ -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-private.h b/clutter/clutter-private.h index 4f16bc33b..2a4b4eb3c 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -296,6 +296,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); From 9506510d1cf794ef530f6c4db45103efb60cca63 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 8 Jan 2010 17:51:00 +0000 Subject: [PATCH 21/79] Move all picking-related operations inside InputDevice The InputDevice objects stores pointer coordinates, state, stage and the actor under the cursor, so if the current backend provides us with one attached to the Event structure then we want the InputDevice itself to update its state and give us the ClutterActor underneath the pointer's cursor. --- clutter/clutter-device-manager.c | 24 +++++++++ clutter/clutter-event.c | 36 ------------- clutter/clutter-input-device.c | 91 ++++++++++++++++++++++++++++++-- clutter/clutter-input-device.h | 7 ++- clutter/clutter-main.c | 60 ++++++++++++--------- clutter/clutter-master-clock.c | 7 ++- clutter/clutter-private.h | 36 ++++++++----- clutter/clutter-stage.c | 28 ++++++++-- clutter/x11/clutter-event-x11.c | 36 ++++++++----- 9 files changed, 225 insertions(+), 100 deletions(-) diff --git a/clutter/clutter-device-manager.c b/clutter/clutter-device-manager.c index bfed42fb8..8b4a5b8a5 100644 --- a/clutter/clutter-device-manager.c +++ b/clutter/clutter-device-manager.c @@ -144,3 +144,27 @@ _clutter_device_manager_remove_device (ClutterDeviceManager *device_manager, device_manager->devices = g_slist_remove (device_manager->devices, device); } + +void +_clutter_device_manager_update_devices (ClutterDeviceManager *device_manager) +{ + GSList *d; + + /* perform a pick() on the stage at the coordinates of every + * input device, and store the actor currently under the pointer + */ + for (d = device_manager->devices; d != NULL; d = d->next) + { + ClutterInputDevice *device = d->data; + ClutterInputDeviceType device_type; + + device_type = clutter_input_device_get_device_type (device); + if (device_type != CLUTTER_POINTER_DEVICE) + continue; + + if (device->stage == NULL) + continue; + + _clutter_input_device_update (device); + } +} 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-input-device.c b/clutter/clutter-input-device.c index dae5e92e2..d2a298394 100644 --- a/clutter/clutter-input-device.c +++ b/clutter/clutter-input-device.c @@ -110,13 +110,13 @@ clutter_input_device_init (ClutterInputDevice *self) void _clutter_input_device_set_coords (ClutterInputDevice *device, - gfloat x, - gfloat y) + gint x, + gint y) { g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device)); - device->previous_x = floorf (x) + 0.5; - device->previous_y = floorf (y) + 0.5; + device->previous_x = x; + device->previous_y = y; } void @@ -137,6 +137,45 @@ _clutter_input_device_set_time (ClutterInputDevice *device, device->previous_time = time_; } +void +_clutter_input_device_set_stage (ClutterInputDevice *device, + ClutterStage *stage) +{ + g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device)); + + device->stage = stage; +} + +static void +cursor_weak_unref (gpointer user_data, + GObject *object_pointer) +{ + ClutterInputDevice *device = user_data; + + device->cursor_actor = NULL; +} + +void +_clutter_input_device_set_actor (ClutterInputDevice *device, + ClutterActor *actor) +{ + g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device)); + + if (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 = actor; + 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 @@ -173,3 +212,47 @@ clutter_input_device_get_device_id (ClutterInputDevice *device) return device->id; } + +void +clutter_input_device_get_device_coords (ClutterInputDevice *device, + gint *x, + gint *y) +{ + g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device)); + + if (x) + *x = device->previous_x; + + if (y) + *y = device->previous_y; +} + +ClutterActor * +_clutter_input_device_update (ClutterInputDevice *device) +{ + ClutterStage *stage; + ClutterActor *new_cursor_actor; + ClutterActor *old_cursor_actor; + gint x, y; + + 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 (new_cursor_actor == NULL) + new_cursor_actor = CLUTTER_ACTOR (stage); + + 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)); + + _clutter_input_device_set_actor (device, new_cursor_actor); + + return device->cursor_actor; +} diff --git a/clutter/clutter-input-device.h b/clutter/clutter-input-device.h index 1d36278bc..3d372bcf6 100644 --- a/clutter/clutter-input-device.h +++ b/clutter/clutter-input-device.h @@ -56,8 +56,11 @@ struct _ClutterInputDeviceClass 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); +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); G_END_DECLS diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index d0438bbf2..c177d3a0d 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -2118,7 +2118,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; @@ -2445,6 +2445,24 @@ _clutter_process_event_details (ClutterActor *stage, clutter_event_get_coords (event, &x, &y); + 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; + } + } + /* Only do a pick to find the source if source is not already set * (as it could be in a synthetic event) */ @@ -2467,13 +2485,24 @@ _clutter_process_event_details (ClutterActor *stage, 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) + { + CLUTTER_NOTE (EVENT, "No device found: picking"); + + /* Map the event to a reactive actor */ + actor = _clutter_do_pick (CLUTTER_STAGE (stage), + x, y, + CLUTTER_PICK_REACTIVE); + } + else + actor = _clutter_input_device_update (device); event->any.source = actor; - if (!actor) + if (actor == NULL) break; } else @@ -2482,7 +2511,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. @@ -2502,24 +2530,6 @@ _clutter_process_event_details (ClutterActor *stage, 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; } diff --git a/clutter/clutter-master-clock.c b/clutter/clutter-master-clock.c index b61ad436c..2f59dbff3 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; diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index 2a4b4eb3c..dbbc96f83 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -91,13 +91,16 @@ struct _ClutterInputDevice ClutterInputDeviceType device_type; + ClutterActor *cursor_actor; + ClutterActor *pointer_grab_actor; ClutterActor *motion_last_actor; gint click_count; - gfloat previous_x; - gfloat previous_y; + ClutterStage *stage; + gint previous_x; + gint previous_y; guint32 previous_time; gint previous_button_number; ClutterModifierType previous_state; @@ -189,19 +192,25 @@ 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_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, - gfloat x, - gfloat 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_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 */ void _clutter_stage_manager_add_stage (ClutterStageManager *stage_manager, @@ -223,6 +232,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); diff --git a/clutter/clutter-stage.c b/clutter/clutter-stage.c index 518d181c7..4d3d13774 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" @@ -454,6 +455,7 @@ _clutter_stage_queue_event (ClutterStage *stage, { ClutterStagePrivate *priv; gboolean first_event; + ClutterInputDevice *device; g_return_if_fail (CLUTTER_IS_STAGE (stage)); @@ -461,14 +463,32 @@ _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); + _clutter_input_device_set_stage (device, stage); + } } 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; diff --git a/clutter/x11/clutter-event-x11.c b/clutter/x11/clutter-event-x11.c index af85238d7..a87a6313d 100644 --- a/clutter/x11/clutter-event-x11.c +++ b/clutter/x11/clutter-event-x11.c @@ -624,9 +624,10 @@ event_translate (ClutterBackend *backend, case KeyPress: event->key.type = event->type = CLUTTER_KEY_PRESS; + translate_key_event (backend, event, xevent); + /* default key device if no XInput support is defined */ event->key.device = clutter_device_manager_get_device (manager, 1); - translate_key_event (backend, event, xevent); set_user_time (backend_x11, &xwindow, xevent->xkey.time); break; @@ -659,9 +660,10 @@ event_translate (ClutterBackend *backend, } event->key.type = event->type = CLUTTER_KEY_RELEASE; + translate_key_event (backend, event, xevent); + /* default key device if no XInput support is defined */ event->key.device = clutter_device_manager_get_device (manager, 1); - translate_key_event (backend, event, xevent); break; default: @@ -763,6 +765,10 @@ event_translate (ClutterBackend *backend, event->motion.modifier_state = xevent->xcrossing.state; event->motion.device = clutter_device_manager_get_device (manager, 0); + + /* we know that we are entering the stage here */ + _clutter_input_device_set_stage (event->motion.device, stage); + CLUTTER_NOTE (EVENT, "Entering the stage"); break; case LeaveNotify: @@ -770,8 +776,12 @@ event_translate (ClutterBackend *backend, event->crossing.time = xevent->xcrossing.time; event->crossing.x = xevent->xcrossing.x; event->crossing.y = xevent->xcrossing.y; - event->motion.device = + event->crossing.device = clutter_device_manager_get_device (manager, 0); + + /* we know that we are leaving the stage here */ + _clutter_input_device_set_stage (event->crossing.device, NULL); + CLUTTER_NOTE (EVENT, "Leaving the stage"); break; default: @@ -784,10 +794,16 @@ event_translate (ClutterBackend *backend, { /* XInput fun.. Needs clean up. */ #ifdef HAVE_XINPUT int *ev_types = backend_x11->event_types; + int button_press, button_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]; CLUTTER_NOTE (EVENT, "XInput event type: %d", xevent->type); - if (xevent->type == ev_types [CLUTTER_X11_XINPUT_BUTTON_PRESS_EVENT]) + if (xevent->type == button_press) { XDeviceButtonEvent *xbev = (XDeviceButtonEvent *) xevent; @@ -833,8 +849,7 @@ event_translate (ClutterBackend *backend, set_user_time (backend_x11, &xwindow, xbev->time); } - else if (xevent->type - == ev_types[CLUTTER_X11_XINPUT_BUTTON_RELEASE_EVENT]) + else if (xevent->type == button_release) { XDeviceButtonEvent *xbev = (XDeviceButtonEvent *)xevent; @@ -860,8 +875,7 @@ event_translate (ClutterBackend *backend, 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]) + else if (xevent->type == motion_notify) { XDeviceMotionEvent *xmev = (XDeviceMotionEvent *)xevent; @@ -883,8 +897,7 @@ event_translate (ClutterBackend *backend, * not generate events even when the window has focus */ - else if (xevent->type - == ev_types [CLUTTER_X11_XINPUT_KEY_PRESS_EVENT]) + else if (xevent->type == ev_types[CLUTTER_X11_XINPUT_KEY_PRESS_EVENT]) { XEvent xevent_converted; XDeviceKeyEvent *xkev = (XDeviceKeyEvent *)xevent; @@ -896,8 +909,7 @@ event_translate (ClutterBackend *backend, set_user_time (backend_x11, &xwindow, xkev->time); } - else if (xevent->type - == ev_types [CLUTTER_X11_XINPUT_KEY_RELEASE_EVENT]) + else if (xevent->type == ev_types[CLUTTER_X11_XINPUT_KEY_RELEASE_EVENT]) { XEvent xevent_converted; XDeviceKeyEvent *xkev = (XDeviceKeyEvent *)xevent; From a056ae716442e07e50ebd07cbca030776a5b3c28 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 24 Nov 2009 16:22:44 +0000 Subject: [PATCH 22/79] Add docs and licensing notices --- clutter/clutter-device-manager.c | 125 ++++++++++++++++++++++++++++++- clutter/clutter-device-manager.h | 29 +++++++ clutter/clutter-input-device.c | 109 ++++++++++++++++++++++++++- clutter/clutter-input-device.h | 31 ++++++++ 4 files changed, 288 insertions(+), 6 deletions(-) diff --git a/clutter/clutter-device-manager.c b/clutter/clutter-device-manager.c index 8b4a5b8a5..5bde10dd8 100644 --- a/clutter/clutter-device-manager.c +++ b/clutter/clutter-device-manager.c @@ -1,3 +1,36 @@ +/* + * 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. + * + * #ClutterDeviceManager is available since Clutter 1.2 + */ + #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -62,6 +95,17 @@ 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) { @@ -71,6 +115,18 @@ clutter_device_manager_get_default (void) 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) { @@ -79,6 +135,19 @@ clutter_device_manager_list_devices (ClutterDeviceManager *device_manager) 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) { @@ -87,6 +156,19 @@ clutter_device_manager_peek_devices (ClutterDeviceManager *device_manager) 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) @@ -122,6 +204,19 @@ input_device_cmp (gconstpointer a, 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) @@ -131,8 +226,23 @@ _clutter_device_manager_add_device (ClutterDeviceManager *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_remobe_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) @@ -143,25 +253,34 @@ _clutter_device_manager_remove_device (ClutterDeviceManager *device_manager, 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; - /* perform a pick() on the stage at the coordinates of every - * input device, and store the actor currently under the pointer - */ 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; diff --git a/clutter/clutter-device-manager.h b/clutter/clutter-device-manager.h index 6d4893d73..66df18932 100644 --- a/clutter/clutter-device-manager.h +++ b/clutter/clutter-device-manager.h @@ -1,3 +1,26 @@ +/* + * 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 @@ -14,6 +37,12 @@ G_BEGIN_DECLS #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_stage_manager_get_type (void) G_GNUC_CONST; diff --git a/clutter/clutter-input-device.c b/clutter/clutter-input-device.c index d2a298394..913ba13e8 100644 --- a/clutter/clutter-input-device.c +++ b/clutter/clutter-input-device.c @@ -1,3 +1,36 @@ +/* + * 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 @@ -74,6 +107,13 @@ clutter_input_device_class_init (ClutterInputDeviceClass *klass) 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", @@ -83,6 +123,13 @@ clutter_input_device_class_init (ClutterInputDeviceClass *klass) G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (gobject_class, PROP_ID, 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", @@ -108,6 +155,14 @@ clutter_input_device_init (ClutterInputDevice *self) 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, @@ -115,10 +170,20 @@ _clutter_input_device_set_coords (ClutterInputDevice *device, { g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device)); - device->previous_x = x; - device->previous_y = y; + if (device->previous_x != x) + device->previous_x = x; + + if (device->previous_y != y) + device->previous_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) @@ -128,15 +193,30 @@ _clutter_input_device_set_state (ClutterInputDevice *device, device->previous_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)); - device->previous_time = time_; + if (device->previous_time != time_) + device->previous_time = time_; } +/* + * _clutter_input_device_set_stage: + * @device: a #ClutterInputDevice + * @stage: a #ClutterStage + * + * Stores the stage under the device + */ void _clutter_input_device_set_stage (ClutterInputDevice *device, ClutterStage *stage) @@ -146,6 +226,13 @@ _clutter_input_device_set_stage (ClutterInputDevice *device, device->stage = stage; } +/* + * 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) @@ -213,12 +300,23 @@ clutter_input_device_get_device_id (ClutterInputDevice *device) 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->previous_x; @@ -241,6 +339,11 @@ _clutter_input_device_update (ClutterInputDevice *device) 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) new_cursor_actor = CLUTTER_ACTOR (stage); diff --git a/clutter/clutter-input-device.h b/clutter/clutter-input-device.h index 3d372bcf6..c0641c44f 100644 --- a/clutter/clutter-input-device.h +++ b/clutter/clutter-input-device.h @@ -1,3 +1,26 @@ +/* + * 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 @@ -48,6 +71,14 @@ typedef enum { 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 >*/ From 75f05646fa117b8096e033f9fad0ba9de1ee01ac Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 24 Nov 2009 16:31:14 +0000 Subject: [PATCH 23/79] tests: Clean up test-events The output of test-events is a bit of a mess; this patch should clean it up a little bit - at least enough for it to be useful again during visual inspection. --- tests/interactive/test-events.c | 101 +++++++++++++++++++------------- 1 file changed, 60 insertions(+), 41 deletions(-) diff --git a/tests/interactive/test-events.c b/tests/interactive/test-events.c index 0cd32c62c..35400381f 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,116 @@ 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_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_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); + clutter_actor_get_name (source_actor), + clutter_event_get_click_count (event)); break; case CLUTTER_BUTTON_RELEASE: g_print ("[%s] BUTTON RELEASE (click count:%i)", - source, event->button.click_count); + clutter_actor_get_name (source_actor), + clutter_event_get_button (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 +262,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 +272,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 +294,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 +319,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); From 687c70dffa70a3256e9f734b090cb4a088e97bb1 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 24 Nov 2009 16:33:03 +0000 Subject: [PATCH 24/79] Rework the emission of LEAVE/ENTER event pairs The LEAVE/ENTER event pairs should be queued during the InputDevice update process, when we change the actor under the device pointer. This commit cleans up the event emission code inside clutter-main.c and the logic of the event processing. --- clutter/clutter-device-manager.c | 4 +- clutter/clutter-input-device.c | 81 ++++++++++++- clutter/clutter-main.c | 192 +++++-------------------------- clutter/clutter-private.h | 2 - 4 files changed, 105 insertions(+), 174 deletions(-) diff --git a/clutter/clutter-device-manager.c b/clutter/clutter-device-manager.c index 5bde10dd8..c38cdd660 100644 --- a/clutter/clutter-device-manager.c +++ b/clutter/clutter-device-manager.c @@ -75,7 +75,7 @@ clutter_device_manager_class_init (ClutterDeviceManagerClass *klass) G_SIGNAL_RUN_LAST, 0, NULL, NULL, - clutter_marshal_VOID__POINTER, + clutter_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_POINTER); @@ -85,7 +85,7 @@ clutter_device_manager_class_init (ClutterDeviceManagerClass *klass) G_SIGNAL_RUN_LAST, 0, NULL, NULL, - clutter_marshal_VOID__POINTER, + clutter_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_POINTER); } diff --git a/clutter/clutter-input-device.c b/clutter/clutter-input-device.c index 913ba13e8..f22a9cf41 100644 --- a/clutter/clutter-input-device.c +++ b/clutter/clutter-input-device.c @@ -242,25 +242,91 @@ cursor_weak_unref (gpointer user_data, 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 (device->cursor_actor != NULL) + if (actor == device->cursor_actor) + return; + + old_actor = device->cursor_actor; + if (old_actor != NULL) { + cev.crossing.type = CLUTTER_LEAVE; + cev.crossing.time = device->previous_time; + cev.crossing.flags = 0; + cev.crossing.stage = device->stage; + cev.crossing.source = device->cursor_actor; + cev.crossing.x = device->previous_x; + cev.crossing.y = device->previous_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; - g_object_weak_ref (G_OBJECT (device->cursor_actor), - cursor_weak_unref, - device); - _clutter_actor_set_has_pointer (device->cursor_actor, TRUE); + + 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); + } } /** @@ -345,7 +411,7 @@ _clutter_input_device_update (ClutterInputDevice *device) * in which case this is the safest course of action anyway */ if (new_cursor_actor == NULL) - new_cursor_actor = CLUTTER_ACTOR (stage); + return NULL; CLUTTER_NOTE (EVENT, "Actor under cursor (device %d, at %d, %d): %s", @@ -355,6 +421,9 @@ _clutter_input_device_update (ClutterInputDevice *device) ? clutter_actor_get_name (new_cursor_actor) : G_OBJECT_TYPE_NAME (new_cursor_actor)); + if (new_cursor_actor == old_cursor_actor) + return old_cursor_actor; + _clutter_input_device_set_actor (device, new_cursor_actor); return device->cursor_actor; diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index c177d3a0d..644d20ed6 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -2182,28 +2182,27 @@ 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 @@ -2211,107 +2210,10 @@ emit_keyboard_event (ClutterEvent *event) { 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 = _clutter_context_get_default (); - - 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 = _clutter_context_get_default (); - 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 = _clutter_context_get_default (); - 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 +2245,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 +2256,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 +2289,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 +2321,7 @@ _clutter_process_event_details (ClutterActor *stage, break; } - /* fallthrough */ - + /* fallthrough from motion */ case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_RELEASE: case CLUTTER_SCROLL: @@ -2445,24 +2331,6 @@ _clutter_process_event_details (ClutterActor *stage, clutter_event_get_coords (event, &x, &y); - 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; - } - } - /* Only do a pick to find the source if source is not already set * (as it could be in a synthetic event) */ @@ -2480,7 +2348,7 @@ _clutter_process_event_details (ClutterActor *stage, x, y); event->button.source = stage; - emit_pointer_event (event, event->button.device); + emit_pointer_event (event, device); } break; } @@ -2489,20 +2357,19 @@ _clutter_process_event_details (ClutterActor *stage, * already have everything we need to update it and * get the actor underneath */ - if (device == NULL) + if (device != NULL) + actor = _clutter_input_device_update (device); + else { CLUTTER_NOTE (EVENT, "No device found: picking"); - /* Map the event to a reactive actor */ actor = _clutter_do_pick (CLUTTER_STAGE (stage), x, y, CLUTTER_PICK_REACTIVE); } - else - actor = _clutter_input_device_update (device); event->any.source = actor; - if (actor == NULL) + if (event->any.source == NULL) break; } else @@ -2521,9 +2388,6 @@ _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 */ diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index dbbc96f83..02800671c 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -92,9 +92,7 @@ struct _ClutterInputDevice ClutterInputDeviceType device_type; ClutterActor *cursor_actor; - ClutterActor *pointer_grab_actor; - ClutterActor *motion_last_actor; gint click_count; From 130286979d04128b51fa8662aa13b6bbd1867ca2 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 24 Nov 2009 17:54:02 +0000 Subject: [PATCH 25/79] Do not pick when motion event delivery is disabled The device manager does not need to update the state of the devices when the user has disabled the delivery of motion events to actors: the events will always be delivered as they are to the stage. --- clutter/clutter-device-manager.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/clutter/clutter-device-manager.c b/clutter/clutter-device-manager.c index c38cdd660..11d76a103 100644 --- a/clutter/clutter-device-manager.c +++ b/clutter/clutter-device-manager.c @@ -270,6 +270,13 @@ _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; From b3a42c3b09e256732395f7c63c4a39956d32d3da Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 7 Dec 2009 18:38:18 +0000 Subject: [PATCH 26/79] docs: Update the API reference Add the new symbols for InputDevice and DeviceManager --- clutter/clutter-device-manager.c | 2 +- clutter/clutter-device-manager.h | 2 +- clutter/clutter-main.c | 4 +- doc/reference/clutter/clutter-sections.txt | 49 ++++++++++++++++++++-- 4 files changed, 49 insertions(+), 8 deletions(-) diff --git a/clutter/clutter-device-manager.c b/clutter/clutter-device-manager.c index 11d76a103..42ce823e4 100644 --- a/clutter/clutter-device-manager.c +++ b/clutter/clutter-device-manager.c @@ -231,7 +231,7 @@ _clutter_device_manager_add_device (ClutterDeviceManager *device_manager, } /* - * _clutter_device_manager_remobe_device: + * _clutter_device_manager_remove_device: * @device_manager: a #ClutterDeviceManager * @device: a #ClutterInputDevice * diff --git a/clutter/clutter-device-manager.h b/clutter/clutter-device-manager.h index 66df18932..e46850e26 100644 --- a/clutter/clutter-device-manager.h +++ b/clutter/clutter-device-manager.h @@ -45,7 +45,7 @@ G_BEGIN_DECLS */ typedef struct _ClutterDeviceManager ClutterDeviceManager; -GType clutter_stage_manager_get_type (void) G_GNUC_CONST; +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); diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 644d20ed6..84cf2dc7a 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -2897,9 +2897,9 @@ 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. This is a convenience + * Retrieves the #ClutterInputDevice from its @id. This is a convenience * wrapper for clutter_device_manager_get_device() and it is functionally * equivalent to: * diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 132770d63..7cad61ed7 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 @@ -977,13 +978,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 +993,50 @@ ClutterAnyEvent clutter_event_get_type
+
+clutter-input-device +ClutterInputDevice +ClutterInputDeviceType +ClutterInputDevice +ClutterInputDeviceClass +clutter_get_input_device_for_id +clutter_input_device_get_device_id +clutter_input_device_get_device_type +clutter_input_device_get_device_coords + + +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 From 68b47e3ed479242a94f7a8d302a2900f7e28c840 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 7 Dec 2009 23:02:48 +0000 Subject: [PATCH 27/79] tests: Fix test-events output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • The enter/leave event line should take into account the case where the related field is set to NULL (meaning entering from off-stage and leaving the stage). • The ButtonRelease line shows the click count but uses the button; the button *and* the click count should be displayed for both ButtonPress and ButtonRelease, to verify they match. --- tests/interactive/test-events.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/tests/interactive/test-events.c b/tests/interactive/test-events.c index 35400381f..3655fcc58 100644 --- a/tests/interactive/test-events.c +++ b/tests/interactive/test-events.c @@ -193,22 +193,28 @@ input_cb (ClutterActor *actor, case CLUTTER_ENTER: g_print ("[%s] ENTER (from:%s)", clutter_actor_get_name (source_actor), - clutter_actor_get_name (clutter_event_get_related (event))); + clutter_event_get_related (event) != NULL + ? clutter_actor_get_name (clutter_event_get_related (event)) + : ""); break; case CLUTTER_LEAVE: g_print ("[%s] LEAVE (to:%s)", clutter_actor_get_name (source_actor), - clutter_actor_get_name (clutter_event_get_related (event))); + 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)", + 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)", + g_print ("[%s] BUTTON RELEASE (button:%i, click count:%i)", clutter_actor_get_name (source_actor), - clutter_event_get_button (event)); + clutter_event_get_button (event), + clutter_event_get_click_count (event)); if (source_actor == CLUTTER_ACTOR (stage)) clutter_stage_set_key_focus (stage, NULL); From cf8a06f0186e44b3437b06c56c088e8dc465402b Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 7 Dec 2009 23:05:20 +0000 Subject: [PATCH 28/79] device: Store the current state, not the previous The previous state for the device is used by the click count machinery and we should not be overwriting it at every event; instead, we should use a parallel storage for the current state coming from the windowing system. --- clutter/clutter-input-device.c | 34 +++++++++++++++++----------------- clutter/clutter-private.h | 7 +++++++ 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/clutter/clutter-input-device.c b/clutter/clutter-input-device.c index f22a9cf41..8034d5638 100644 --- a/clutter/clutter-input-device.c +++ b/clutter/clutter-input-device.c @@ -148,11 +148,11 @@ clutter_input_device_init (ClutterInputDevice *self) self->click_count = 0; - self->previous_time = CLUTTER_CURRENT_TIME; - self->previous_x = -1; - self->previous_y = -1; - self->previous_button_number = -1; - self->previous_state = 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; } /* @@ -170,11 +170,11 @@ _clutter_input_device_set_coords (ClutterInputDevice *device, { g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device)); - if (device->previous_x != x) - device->previous_x = x; + if (device->current_x != x) + device->current_x = x; - if (device->previous_y != y) - device->previous_y = y; + if (device->current_y != y) + device->current_y = y; } /* @@ -190,7 +190,7 @@ _clutter_input_device_set_state (ClutterInputDevice *device, { g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device)); - device->previous_state = state; + device->current_state = state; } /* @@ -206,8 +206,8 @@ _clutter_input_device_set_time (ClutterInputDevice *device, { g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device)); - if (device->previous_time != time_) - device->previous_time = time_; + if (device->current_time != time_) + device->current_time = time_; } /* @@ -276,12 +276,12 @@ _clutter_input_device_set_actor (ClutterInputDevice *device, if (old_actor != NULL) { cev.crossing.type = CLUTTER_LEAVE; - cev.crossing.time = device->previous_time; + 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->previous_x; - cev.crossing.y = device->previous_y; + cev.crossing.x = device->current_x; + cev.crossing.y = device->current_y; cev.crossing.device = device; cev.crossing.related = actor; @@ -385,10 +385,10 @@ clutter_input_device_get_device_coords (ClutterInputDevice *device, g_return_if_fail (device->device_type == CLUTTER_POINTER_DEVICE); if (x) - *x = device->previous_x; + *x = device->current_x; if (y) - *y = device->previous_y; + *y = device->current_y; } ClutterActor * diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index 02800671c..6508f08b3 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -97,6 +97,13 @@ struct _ClutterInputDevice gint click_count; ClutterStage *stage; + + gint current_x; + gint current_y; + guint32 current_time; + gint current_button_number; + ClutterModifierType current_state; + gint previous_x; gint previous_y; guint32 previous_time; From 0f9cfd9911c21933b34c07a9616f79022bcc01e3 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 7 Dec 2009 23:13:52 +0000 Subject: [PATCH 29/79] event: Clean up click-count detection Avoid a few indirections and direct access to the Event and InputDevice structures. --- clutter/clutter-main.c | 65 +++++++++++++++++++++++++++++------------- 1 file changed, 45 insertions(+), 20 deletions(-) diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 84cf2dc7a..b0135e280 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -2045,6 +2045,7 @@ 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; @@ -2053,41 +2054,60 @@ event_click_count_generate (ClutterEvent *event) 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)) { + 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*/ { + 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_time = event->button.time; previous_x = event->button.x; previous_y = event->button.y; + previous_time = event->button.time; /* fallthrough */ case CLUTTER_BUTTON_RELEASE: @@ -2098,13 +2118,18 @@ event_click_count_generate (ClutterEvent *event) 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; } } From e30856a54d5d1e012cd2de30e519a487fd0b4f46 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 8 Dec 2009 18:33:01 +0000 Subject: [PATCH 30/79] Whitespace and indentation fixes --- clutter/x11/clutter-event-x11.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/clutter/x11/clutter-event-x11.c b/clutter/x11/clutter-event-x11.c index a87a6313d..6a1eab79e 100644 --- a/clutter/x11/clutter-event-x11.c +++ b/clutter/x11/clutter-event-x11.c @@ -424,14 +424,13 @@ event_translate (ClutterBackend *backend, 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)) { @@ -449,15 +448,13 @@ 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; manager = clutter_device_manager_get_default (); @@ -807,7 +804,8 @@ event_translate (ClutterBackend *backend, { XDeviceButtonEvent *xbev = (XDeviceButtonEvent *) xevent; - CLUTTER_NOTE (EVENT, "XINPUT Button press event for %li at %d, %d", + CLUTTER_NOTE (EVENT, + "XINPUT Button press event for %li at %d, %d", xbev->deviceid, xbev->x, xbev->y); @@ -833,7 +831,8 @@ event_translate (ClutterBackend *backend, 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); + event->scroll.device = + _clutter_x11_get_device_for_xid (xbev->deviceid); break; default: @@ -843,7 +842,8 @@ event_translate (ClutterBackend *backend, 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); + event->button.device = + _clutter_x11_get_device_for_xid (xbev->deviceid); break; } From bddabf6d2ca100f4d91461129d4bb097c59ac666 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 8 Dec 2009 23:59:56 +0000 Subject: [PATCH 31/79] event: Scroll events do not have click count Remove the unneeded CLUTTER_SCROLL case from the click count checks. --- clutter/clutter-main.c | 1 - 1 file changed, 1 deletion(-) diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index b0135e280..d72b5dc6f 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -2080,7 +2080,6 @@ event_click_count_generate (ClutterEvent *event) 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 */ From cf287db2048eae5eaa58a9cbffd42c703f345ff5 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 9 Dec 2009 00:00:49 +0000 Subject: [PATCH 32/79] event: Off-stage button releases have a click count of 1 The ButtonRelease off-stage should not have a click count of 0 but a click count initialized to 1. --- clutter/clutter-main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index d72b5dc6f..1ac393ef0 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -2372,6 +2372,7 @@ _clutter_process_event_details (ClutterActor *stage, x, y); event->button.source = stage; + event->button.click_count = 1; emit_pointer_event (event, device); } break; From 8736b53d7cc6fd2b53649c076ef09cb69f9b37d6 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 9 Dec 2009 00:01:50 +0000 Subject: [PATCH 33/79] device: Do not overwrite the stage for an InputDevice The Stage field of an InputDevice is set by the backend, whenever the pointer enters or leaves the Stage. The Stage should not overwrite the stage field for every event it processes. --- clutter/clutter-stage.c | 1 - 1 file changed, 1 deletion(-) diff --git a/clutter/clutter-stage.c b/clutter/clutter-stage.c index 4d3d13774..3c8ea625c 100644 --- a/clutter/clutter-stage.c +++ b/clutter/clutter-stage.c @@ -487,7 +487,6 @@ _clutter_stage_queue_event (ClutterStage *stage, _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); - _clutter_input_device_set_stage (device, stage); } } From 25c6ebbb2c70af7f268036b10dcd8d4a965c34c3 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 9 Dec 2009 00:03:13 +0000 Subject: [PATCH 34/79] x11: Discard the LeaveNotify for off-stage ButtonRelease MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the user presses a button on a pointer device and then moves out the Stage X11 will emit the following events: LeaveNotify ➔ MotionNotify ... ➔ ButtonRelease ➔ LeaveNotify The second LeaveNotify differs from the first by the state field. Unfortunately, ClutterCrossingEvent doesn't have a modifier_state field like other events, so we cannot provide a way for programmatically distinguishing them from a Clutter perspective. This is also an X11-ism we might not even want to replicate on every backend with sane enter/leave semantics. For this reason we should check inside the X11 event processing if the pointer device has already left the Stage and ignore the second LeaveNotify. --- clutter/x11/clutter-event-x11.c | 58 +++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/clutter/x11/clutter-event-x11.c b/clutter/x11/clutter-event-x11.c index 6a1eab79e..549fef008 100644 --- a/clutter/x11/clutter-event-x11.c +++ b/clutter/x11/clutter-event-x11.c @@ -417,6 +417,7 @@ event_translate (ClutterBackend *backend, gboolean res, not_yet_handled = FALSE; Window xwindow, stage_xwindow; ClutterDeviceManager *manager; + ClutterInputDevice *device; backend_x11 = CLUTTER_BACKEND_X11 (backend); @@ -674,12 +675,11 @@ event_translate (ClutterBackend *backend, { if (!clutter_x11_has_xinput ()) { + device = clutter_device_manager_get_device (manager, 0); + /* Regular X event */ switch (xevent->type) { - /* KeyPress / KeyRelease should reside here if XInput - * worked properly - */ case ButtonPress: switch (xevent->xbutton.button) { @@ -702,8 +702,7 @@ event_translate (ClutterBackend *backend, event->scroll.x = xevent->xbutton.x; event->scroll.y = xevent->xbutton.y; event->scroll.modifier_state = xevent->xbutton.state; - event->scroll.device = - clutter_device_manager_get_device (manager, 0); + event->scroll.device = device; break; default: @@ -713,8 +712,7 @@ event_translate (ClutterBackend *backend, event->button.y = xevent->xbutton.y; event->button.modifier_state = xevent->xbutton.state; event->button.button = xevent->xbutton.button; - event->button.device = - clutter_device_manager_get_device (manager, 0); + event->button.device = device; break; } @@ -738,8 +736,7 @@ event_translate (ClutterBackend *backend, event->button.y = xevent->xbutton.y; event->button.modifier_state = xevent->xbutton.state; event->button.button = xevent->xbutton.button; - event->button.device = - clutter_device_manager_get_device (manager, 0); + event->button.device = device; break; case MotionNotify: @@ -748,8 +745,7 @@ event_translate (ClutterBackend *backend, event->motion.x = xevent->xmotion.x; event->motion.y = xevent->xmotion.y; event->motion.modifier_state = xevent->xmotion.state; - event->motion.device = - clutter_device_manager_get_device (manager, 0); + event->motion.device = device; break; case EnterNotify: @@ -760,25 +756,35 @@ event_translate (ClutterBackend *backend, event->motion.x = xevent->xcrossing.x; event->motion.y = xevent->xcrossing.y; event->motion.modifier_state = xevent->xcrossing.state; - event->motion.device = - clutter_device_manager_get_device (manager, 0); + event->motion.source = CLUTTER_ACTOR (stage); + event->motion.device = device; /* we know that we are entering the stage here */ - _clutter_input_device_set_stage (event->motion.device, stage); + _clutter_input_device_set_stage (device, stage); CLUTTER_NOTE (EVENT, "Entering the stage"); break; case LeaveNotify: + if (device->stage == NULL) + { + CLUTTER_NOTE (EVENT, + "Discarding LeaveNotify for ButtonRelease " + "event off-stage"); + res = FALSE; + break; + } + 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.device = - clutter_device_manager_get_device (manager, 0); + event->crossing.source = CLUTTER_ACTOR (stage); + event->crossing.device = device; /* we know that we are leaving the stage here */ - _clutter_input_device_set_stage (event->crossing.device, NULL); - CLUTTER_NOTE (EVENT, "Leaving the stage"); + _clutter_input_device_set_stage (device, NULL); + CLUTTER_NOTE (EVENT, "Leaving the stage (time:%u)", + event->crossing.time); break; default: @@ -804,6 +810,8 @@ event_translate (ClutterBackend *backend, { XDeviceButtonEvent *xbev = (XDeviceButtonEvent *) xevent; + device = _clutter_x11_get_device_for_xid (xbev->deviceid); + CLUTTER_NOTE (EVENT, "XINPUT Button press event for %li at %d, %d", xbev->deviceid, @@ -831,8 +839,7 @@ event_translate (ClutterBackend *backend, 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); + event->scroll.device = device; break; default: @@ -842,8 +849,7 @@ event_translate (ClutterBackend *backend, 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); + event->button.device = device; break; } @@ -853,6 +859,8 @@ event_translate (ClutterBackend *backend, { XDeviceButtonEvent *xbev = (XDeviceButtonEvent *)xevent; + device = _clutter_x11_get_device_for_xid (xbev->deviceid); + CLUTTER_NOTE (EVENT, "XINPUT Button release event for %li at %d, %d", xbev->deviceid, xbev->x, @@ -873,12 +881,14 @@ event_translate (ClutterBackend *backend, 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); + event->button.device = device; } else if (xevent->type == motion_notify) { XDeviceMotionEvent *xmev = (XDeviceMotionEvent *)xevent; + device = _clutter_x11_get_device_for_xid (xmev->deviceid); + CLUTTER_NOTE(EVENT, "XINPUT Motion event for %li at %d, %d", xmev->deviceid, xmev->x, @@ -889,7 +899,7 @@ event_translate (ClutterBackend *backend, 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); + event->motion.device = device; } #if 0 /* the Xinput handling of key presses/releases disabled for now since From 55e4315aa5889185a055f3ab2d5b5d4c9cae4dc4 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 12 Jan 2010 11:53:12 +0000 Subject: [PATCH 35/79] device: Add pointer actor getter ClutterInputDevice should have a getter method for retrieving the reactive actor underneath the pointer. --- clutter/clutter-input-device.c | 37 ++++++++++++++++++++++ clutter/clutter-input-device.h | 3 +- doc/reference/clutter/clutter-sections.txt | 1 + 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/clutter/clutter-input-device.c b/clutter/clutter-input-device.c index 8034d5638..2e04edbe9 100644 --- a/clutter/clutter-input-device.c +++ b/clutter/clutter-input-device.c @@ -391,6 +391,20 @@ clutter_input_device_get_device_coords (ClutterInputDevice *device, *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) { @@ -399,9 +413,12 @@ _clutter_input_device_update (ClutterInputDevice *device) 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); @@ -421,6 +438,7 @@ _clutter_input_device_update (ClutterInputDevice *device) ? 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; @@ -428,3 +446,22 @@ _clutter_input_device_update (ClutterInputDevice *device) 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; +} diff --git a/clutter/clutter-input-device.h b/clutter/clutter-input-device.h index c0641c44f..eba3fc793 100644 --- a/clutter/clutter-input-device.h +++ b/clutter/clutter-input-device.h @@ -28,7 +28,7 @@ #ifndef __CLUTTER_INPUT_DEVICE_H__ #define __CLUTTER_INPUT_DEVICE_H__ -#include +#include G_BEGIN_DECLS @@ -92,6 +92,7 @@ gint clutter_input_device_get_device_id (ClutterInputDevic void clutter_input_device_get_device_coords (ClutterInputDevice *device, gint *x, gint *y); +ClutterActor * clutter_input_device_get_pointer_actor (ClutterInputDevice *device); G_END_DECLS diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 7cad61ed7..a4cc2ff35 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -1003,6 +1003,7 @@ clutter_get_input_device_for_id clutter_input_device_get_device_id clutter_input_device_get_device_type clutter_input_device_get_device_coords +clutter_input_device_get_pointer_actor CLUTTER_TYPE_INPUT_DEVICE From 8a579838d54d2b6f552fd32d9772f6e04f178179 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 14 Jan 2010 17:14:33 +0000 Subject: [PATCH 36/79] device: Unset the cursor actor when leaving the stage When an InputDevice leaves a stage we set the stage member of InputDevice to NULL. We should also unset the cursor_actor (as the device is obviously not on an actor any more). When the device re-enters the Stage the ENTER/LEAVE event generation machinery will then be able to emit the ENTER event on the Stage. --- clutter/clutter-input-device.c | 45 ++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/clutter/clutter-input-device.c b/clutter/clutter-input-device.c index 2e04edbe9..07958e7e8 100644 --- a/clutter/clutter-input-device.c +++ b/clutter/clutter-input-device.c @@ -210,22 +210,6 @@ _clutter_input_device_set_time (ClutterInputDevice *device, device->current_time = time_; } -/* - * _clutter_input_device_set_stage: - * @device: a #ClutterInputDevice - * @stage: a #ClutterStage - * - * 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; -} - /* * cursor_weak_unref: * @@ -242,6 +226,35 @@ cursor_weak_unref (gpointer 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 From 79ad2b6a72bd91273e139e72af36d9b6db264001 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 15 Jan 2010 11:21:52 +0000 Subject: [PATCH 37/79] x11: Store core devices on the X11 Backend singleton Instead of overloading the device id of 0 and 1 we should treat the core devices as special, and have a pointer inside the X11 backend singleton structure, for fast access. --- clutter/x11/clutter-backend-x11.c | 8 +++- clutter/x11/clutter-backend-x11.h | 3 ++ clutter/x11/clutter-event-x11.c | 13 ++---- clutter/x11/clutter-input-device-x11.c | 63 ++++++++++++++++++++++++++ 4 files changed, 77 insertions(+), 10 deletions(-) diff --git a/clutter/x11/clutter-backend-x11.c b/clutter/x11/clutter-backend-x11.c index d8f026f05..08d9da934 100644 --- a/clutter/x11/clutter-backend-x11.c +++ b/clutter/x11/clutter-backend-x11.c @@ -236,18 +236,22 @@ default_device: d = g_object_new (CLUTTER_TYPE_INPUT_DEVICE_X11, "id", 0, "device-type", CLUTTER_POINTER_DEVICE, + "is-core", TRUE, NULL); d->is_default = TRUE; - CLUTTER_NOTE (BACKEND, "Added default pointer device %d", d->id); + CLUTTER_NOTE (BACKEND, "Added core pointer device %d", d->id); _clutter_device_manager_add_device (manager, d); + backend->core_pointer = d; d = g_object_new (CLUTTER_TYPE_INPUT_DEVICE_X11, "id", 1, "device-type", CLUTTER_KEYBOARD_DEVICE, + "is-core", TRUE, NULL); d->is_default = TRUE; - CLUTTER_NOTE (BACKEND, "Added default keyboard device %d", d->id); + CLUTTER_NOTE (BACKEND, "Added core keyboard device %d", d->id); _clutter_device_manager_add_device (manager, d); + backend->core_keyboard = d; } } diff --git a/clutter/x11/clutter-backend-x11.h b/clutter/x11/clutter-backend-x11.h index 95cd4c3fa..807dd3648 100644 --- a/clutter/x11/clutter-backend-x11.h +++ b/clutter/x11/clutter-backend-x11.h @@ -80,6 +80,9 @@ struct _ClutterBackendX11 gboolean have_xinput; Time last_event_time; + + ClutterInputDevice *core_pointer; + ClutterInputDevice *core_keyboard; }; struct _ClutterBackendX11Class diff --git a/clutter/x11/clutter-event-x11.c b/clutter/x11/clutter-event-x11.c index 549fef008..87a21f5dc 100644 --- a/clutter/x11/clutter-event-x11.c +++ b/clutter/x11/clutter-event-x11.c @@ -183,7 +183,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 @@ -622,10 +621,9 @@ event_translate (ClutterBackend *backend, case KeyPress: event->key.type = event->type = CLUTTER_KEY_PRESS; - translate_key_event (backend, event, xevent); + event->key.device = backend_x11->core_keyboard; - /* default key device if no XInput support is defined */ - event->key.device = clutter_device_manager_get_device (manager, 1); + translate_key_event (backend, event, xevent); set_user_time (backend_x11, &xwindow, xevent->xkey.time); break; @@ -658,10 +656,9 @@ event_translate (ClutterBackend *backend, } event->key.type = event->type = CLUTTER_KEY_RELEASE; - translate_key_event (backend, event, xevent); + event->key.device = backend_x11->core_keyboard; - /* default key device if no XInput support is defined */ - event->key.device = clutter_device_manager_get_device (manager, 1); + translate_key_event (backend, event, xevent); break; default: @@ -675,7 +672,7 @@ event_translate (ClutterBackend *backend, { if (!clutter_x11_has_xinput ()) { - device = clutter_device_manager_get_device (manager, 0); + device = backend_x11->core_pointer; /* Regular X event */ switch (xevent->type) diff --git a/clutter/x11/clutter-input-device-x11.c b/clutter/x11/clutter-input-device-x11.c index e0edfd1e9..2be771de3 100644 --- a/clutter/x11/clutter-input-device-x11.c +++ b/clutter/x11/clutter-input-device-x11.c @@ -22,20 +22,83 @@ struct _ClutterInputDeviceX11 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 From cf4e05930a1243f9b58617a4a50833d5bab705b1 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 15 Jan 2010 11:37:43 +0000 Subject: [PATCH 38/79] device: Add the :name property to InputDevice The InputDevice should have a name, possibly user readable, coming from the backend. --- clutter/clutter-input-device.c | 46 +++++++++++++++++++++++++++++++++- clutter/clutter-input-device.h | 6 ++--- clutter/clutter-private.h | 9 +++++++ 3 files changed, 57 insertions(+), 4 deletions(-) diff --git a/clutter/clutter-input-device.c b/clutter/clutter-input-device.c index 07958e7e8..c3109df04 100644 --- a/clutter/clutter-input-device.c +++ b/clutter/clutter-input-device.c @@ -45,7 +45,8 @@ enum PROP_0, PROP_ID, - PROP_DEVICE_TYPE + PROP_DEVICE_TYPE, + PROP_NAME }; G_DEFINE_TYPE (ClutterInputDevice, clutter_input_device, G_TYPE_OBJECT); @@ -68,6 +69,10 @@ clutter_input_device_set_property (GObject *gobject, 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; @@ -92,6 +97,10 @@ clutter_input_device_get_property (GObject *gobject, 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; @@ -123,6 +132,21 @@ clutter_input_device_class_init (ClutterInputDeviceClass *klass) 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: * @@ -478,3 +502,23 @@ clutter_input_device_get_pointer_actor (ClutterInputDevice *device) 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 index eba3fc793..4b8cc9470 100644 --- a/clutter/clutter-input-device.h +++ b/clutter/clutter-input-device.h @@ -42,9 +42,8 @@ G_BEGIN_DECLS /** * ClutterInputDevice: * - * Generic representation of an input device. The - * actual contents of this structure depend on the - * backend used. + * 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; @@ -93,6 +92,7 @@ void clutter_input_device_get_device_coords (ClutterInputDevic 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 diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index 6508f08b3..99862d5cb 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -91,19 +91,28 @@ struct _ClutterInputDevice ClutterInputDeviceType device_type; + gchar *device_name; + + /* 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; From 66740e8000ea92b04da663a0d2ce890271d2af2f Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 15 Jan 2010 11:38:58 +0000 Subject: [PATCH 39/79] x11: Fill out the :name property of the InputDevices For the core pointer and keyboard we assign the names ourselves; for devices coming from XI we can use the XDeviceInfo.name member. --- clutter/x11/clutter-backend-x11.c | 5 ++++- clutter/x11/clutter-event-x11.c | 9 ++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/clutter/x11/clutter-backend-x11.c b/clutter/x11/clutter-backend-x11.c index 08d9da934..523d47619 100644 --- a/clutter/x11/clutter-backend-x11.c +++ b/clutter/x11/clutter-backend-x11.c @@ -174,6 +174,7 @@ clutter_x11_register_input_devices (ClutterBackendX11 *backend) 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); @@ -226,7 +227,7 @@ default_device: * - we do not have the XInput extension * - we do not have a XInput pointer device * - * we register two default devices, one for the pointer + * we register two default core devices, one for the pointer * and one for the keyboard */ if (!have_an_xpointer) @@ -235,6 +236,7 @@ default_device: d = g_object_new (CLUTTER_TYPE_INPUT_DEVICE_X11, "id", 0, + "name", "Core Pointer", "device-type", CLUTTER_POINTER_DEVICE, "is-core", TRUE, NULL); @@ -245,6 +247,7 @@ default_device: d = g_object_new (CLUTTER_TYPE_INPUT_DEVICE_X11, "id", 1, + "name", "Core Keyboard", "device-type", CLUTTER_KEYBOARD_DEVICE, "is-core", TRUE, NULL); diff --git a/clutter/x11/clutter-event-x11.c b/clutter/x11/clutter-event-x11.c index 87a21f5dc..d6be64571 100644 --- a/clutter/x11/clutter-event-x11.c +++ b/clutter/x11/clutter-event-x11.c @@ -810,8 +810,9 @@ event_translate (ClutterBackend *backend, device = _clutter_x11_get_device_for_xid (xbev->deviceid); CLUTTER_NOTE (EVENT, - "XINPUT Button press event for %li at %d, %d", + "XI ButtonPress for %li ('%s') at %d, %d", xbev->deviceid, + device->device_name, xbev->x, xbev->y); @@ -858,8 +859,9 @@ event_translate (ClutterBackend *backend, device = _clutter_x11_get_device_for_xid (xbev->deviceid); - CLUTTER_NOTE (EVENT, "XINPUT Button release event for %li at %d, %d", + CLUTTER_NOTE (EVENT, "XI ButtonRelease for %li ('%s') at %d, %d", xbev->deviceid, + device->device_name, xbev->x, xbev->y); @@ -886,8 +888,9 @@ event_translate (ClutterBackend *backend, device = _clutter_x11_get_device_for_xid (xmev->deviceid); - CLUTTER_NOTE(EVENT, "XINPUT Motion event for %li at %d, %d", + CLUTTER_NOTE(EVENT, "XI Motion for %li ('%s') at %d, %d", xmev->deviceid, + device->device_name, xmev->x, xmev->y); From 850dd5a38fe17e82b16e19144db4ac516e349dbb Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 15 Jan 2010 11:40:01 +0000 Subject: [PATCH 40/79] tests: Print the device name, as well as its Id The test-devices interactive test should display the device name along with the id. --- tests/interactive/test-devices.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/interactive/test-devices.c b/tests/interactive/test-devices.c index 62923f812..0d9cde7ec 100644 --- a/tests/interactive/test-devices.c +++ b/tests/interactive/test-devices.c @@ -79,7 +79,8 @@ test_devices_main (int argc, char **argv) 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", + g_print ("got a pointer device '%s' with id %d...\n", + clutter_input_device_get_device_name (device), clutter_input_device_get_device_id (device)); hand = clutter_texture_new_from_file (TESTS_DATADIR From 74dbcede25c9dee5b40cb057a26263d580800e7e Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 15 Jan 2010 11:47:05 +0000 Subject: [PATCH 41/79] win32: Experimental implementation of device support Mostly lifted from the core pointer and keyboard X11 backend support. The win32 backend registers two devices (a core pointer and a core keyboard) and assigns them to the event structure when doing the translation from native events to Clutter events. Thanks to: Samuel Degrande for testing this patch. --- clutter/win32/clutter-backend-win32.c | 23 +++++++++++ clutter/win32/clutter-backend-win32.h | 3 ++ clutter/win32/clutter-event-win32.c | 55 +++++++++++++++++---------- 3 files changed, 61 insertions(+), 20 deletions(-) diff --git a/clutter/win32/clutter-backend-win32.c b/clutter/win32/clutter-backend-win32.c index 16bf3f976..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" @@ -64,8 +65,30 @@ 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); } diff --git a/clutter/win32/clutter-backend-win32.h b/clutter/win32/clutter-backend-win32.h index 465917ce3..0dfb74d29 100644 --- a/clutter/win32/clutter-backend-win32.h +++ b/clutter/win32/clutter-backend-win32.h @@ -51,6 +51,9 @@ struct _ClutterBackendWin32 HCURSOR invisible_cursor; GSource *event_source; + + ClutterInputDevice *core_pointer; + ClutterInputDevice *core_keyboard; }; struct _ClutterBackendWin32Class diff --git a/clutter/win32/clutter-event-win32.c b/clutter/win32/clutter-event-win32.c index cbebe9e0b..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; From e0b8d631593c0539c47de0158d5425391d7cdbc0 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 15 Jan 2010 11:52:27 +0000 Subject: [PATCH 42/79] device: Remove unused is_default member The is_default member of the InputDevice structure was not used anywhere. --- clutter/clutter-private.h | 2 -- clutter/x11/clutter-backend-x11.c | 11 ++--------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index 99862d5cb..79b9569cc 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -118,8 +118,6 @@ struct _ClutterInputDevice guint32 previous_time; gint previous_button_number; ClutterModifierType previous_state; - - guint is_default : 1; }; struct _ClutterStageManager diff --git a/clutter/x11/clutter-backend-x11.c b/clutter/x11/clutter-backend-x11.c index 523d47619..484e1f78e 100644 --- a/clutter/x11/clutter-backend-x11.c +++ b/clutter/x11/clutter-backend-x11.c @@ -179,12 +179,7 @@ clutter_x11_register_input_devices (ClutterBackendX11 *backend) n_events = _clutter_input_device_x11_construct (device, backend); if (info->use == IsXExtensionPointer && n_events > 0) - { - /* mark it as a default */ - device->is_default = TRUE; - - have_an_xpointer = TRUE; - } + have_an_xpointer = TRUE; /* add it to a temporary list; we don't add the device * straight to the device manager because the XInput @@ -227,7 +222,7 @@ default_device: * - we do not have the XInput extension * - we do not have a XInput pointer device * - * we register two default core devices, one for the pointer + * we register two default devices, one for the pointer * and one for the keyboard */ if (!have_an_xpointer) @@ -240,7 +235,6 @@ default_device: "device-type", CLUTTER_POINTER_DEVICE, "is-core", TRUE, NULL); - d->is_default = TRUE; CLUTTER_NOTE (BACKEND, "Added core pointer device %d", d->id); _clutter_device_manager_add_device (manager, d); backend->core_pointer = d; @@ -251,7 +245,6 @@ default_device: "device-type", CLUTTER_KEYBOARD_DEVICE, "is-core", TRUE, NULL); - d->is_default = TRUE; CLUTTER_NOTE (BACKEND, "Added core keyboard device %d", d->id); _clutter_device_manager_add_device (manager, d); backend->core_keyboard = d; From d8e167f1519499a27ee04a9a0d167b5ffed3ce8e Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 15 Jan 2010 12:22:29 +0000 Subject: [PATCH 43/79] Always register core devices Even with XInput support we should always register core devices. This allows us to handle enter and leave events correctly on the Stage and to have a working XInput 1.x support in Clutter. --- clutter/x11/clutter-backend-x11.c | 85 ++++++++------------------ clutter/x11/clutter-backend-x11.h | 1 + clutter/x11/clutter-event-x11.c | 47 +++++++++++++- clutter/x11/clutter-input-device-x11.c | 14 ++--- 4 files changed, 81 insertions(+), 66 deletions(-) diff --git a/clutter/x11/clutter-backend-x11.c b/clutter/x11/clutter-backend-x11.c index 484e1f78e..55fb65a3b 100644 --- a/clutter/x11/clutter-backend-x11.c +++ b/clutter/x11/clutter-backend-x11.c @@ -99,12 +99,11 @@ static void clutter_x11_register_input_devices (ClutterBackendX11 *backend) { ClutterDeviceManager *manager; - gboolean have_an_xpointer = FALSE; + ClutterInputDevice *device; #ifdef HAVE_XINPUT XDeviceInfo *x_devices = NULL; int res, opcode, event, error; int i, n_devices; - GSList *devices = NULL; #endif /* HAVE_XINPUT */ manager = clutter_device_manager_get_default (); @@ -126,6 +125,8 @@ clutter_x11_register_input_devices (ClutterBackendX11 *backend) goto default_device; } + backend->xi_event_base = event; + x_devices = XListInputDevices (backend->xdpy, &n_devices); if (n_devices == 0) { @@ -149,14 +150,12 @@ clutter_x11_register_input_devices (ClutterBackendX11 *backend) info->use == IsXExtensionDevice) { ClutterInputDeviceType device_type; - ClutterInputDevice *device; gint n_events = 0; switch (info->use) { case IsXExtensionPointer: device_type = CLUTTER_POINTER_DEVICE; - have_an_xpointer = TRUE; break; #if 0 @@ -178,40 +177,14 @@ clutter_x11_register_input_devices (ClutterBackendX11 *backend) NULL); n_events = _clutter_input_device_x11_construct (device, backend); - if (info->use == IsXExtensionPointer && n_events > 0) - have_an_xpointer = TRUE; + _clutter_device_manager_add_device (manager, device); - /* add it to a temporary list; we don't add the device - * straight to the device manager because the XInput - * initialization might still fail - */ - devices = g_slist_prepend (devices, device); + if (info->use == IsXExtensionPointer && n_events > 0) + backend->have_xinput = TRUE; } } XFree (x_devices); - - devices = g_slist_reverse (devices); - - if (have_an_xpointer) - { - GSList *l; - - for (l = devices; l != NULL; l = l->next) - _clutter_device_manager_add_device (manager, l->data); - - backend->have_xinput = TRUE; - } - else - { - g_warning ("No usable XInput pointer devices found"); - - g_slist_foreach (devices, (GFunc) g_object_unref, NULL); - - backend->have_xinput = FALSE; - } - - g_slist_free (devices); #endif /* HAVE_XINPUT */ default_device: @@ -220,35 +193,31 @@ default_device: * - we do not have XInput support compiled in * - we do not have XInput support enabled * - we do not have the XInput extension - * - we do not have a XInput pointer device * * we register two default devices, one for the pointer - * and one for the keyboard + * and one for the keyboard. this block must also be + * executed for the XInput support because XI does not + * cover core devices */ - if (!have_an_xpointer) - { - ClutterInputDevice *d; + 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; - d = 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 %d", d->id); - _clutter_device_manager_add_device (manager, d); - backend->core_pointer = d; - - d = 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 %d", d->id); - _clutter_device_manager_add_device (manager, d); - backend->core_keyboard = d; - } + 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 diff --git a/clutter/x11/clutter-backend-x11.h b/clutter/x11/clutter-backend-x11.h index 807dd3648..92a5fb8f7 100644 --- a/clutter/x11/clutter-backend-x11.h +++ b/clutter/x11/clutter-backend-x11.h @@ -76,6 +76,7 @@ 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; diff --git a/clutter/x11/clutter-event-x11.c b/clutter/x11/clutter-event-x11.c index d6be64571..264f61942 100644 --- a/clutter/x11/clutter-event-x11.c +++ b/clutter/x11/clutter-event-x11.c @@ -803,11 +803,54 @@ event_translate (ClutterBackend *backend, CLUTTER_NOTE (EVENT, "XInput event type: %d", xevent->type); - if (xevent->type == button_press) + if (xevent->type == EnterNotify) + { + device = backend_x11->core_pointer; + + /* 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; + + /* we know that we are entering the stage here */ + _clutter_input_device_set_stage (device, stage); + CLUTTER_NOTE (EVENT, "Entering the stage"); + } + else if (xevent->type == LeaveNotify) + { + device = backend_x11->core_pointer; + + if (device->stage == NULL) + { + CLUTTER_NOTE (EVENT, + "Discarding LeaveNotify for ButtonRelease " + "event off-stage"); + return FALSE; + } + + 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; + + /* 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); + } + else 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", @@ -858,6 +901,7 @@ event_translate (ClutterBackend *backend, 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, @@ -887,6 +931,7 @@ event_translate (ClutterBackend *backend, 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, diff --git a/clutter/x11/clutter-input-device-x11.c b/clutter/x11/clutter-input-device-x11.c index 2be771de3..94b67d2a9 100644 --- a/clutter/x11/clutter-input-device-x11.c +++ b/clutter/x11/clutter-input-device-x11.c @@ -141,14 +141,14 @@ _clutter_input_device_x11_construct (ClutterInputDevice *device, for (i = 0; i < x_device->num_classes; i++) { XInputClassInfo *xclass_info = x_device->classes + i; - int button_press, button_release, motion_notify; + int *button_press, *button_release, *motion_notify; button_press = - backend->event_types[CLUTTER_X11_XINPUT_BUTTON_PRESS_EVENT]; + &backend->event_types[CLUTTER_X11_XINPUT_BUTTON_PRESS_EVENT]; button_release = - backend->event_types[CLUTTER_X11_XINPUT_BUTTON_RELEASE_EVENT]; + &backend->event_types[CLUTTER_X11_XINPUT_BUTTON_RELEASE_EVENT]; motion_notify = - backend->event_types[CLUTTER_X11_XINPUT_MOTION_NOTIFY_EVENT]; + &backend->event_types[CLUTTER_X11_XINPUT_MOTION_NOTIFY_EVENT]; switch (xclass_info->input_class) { @@ -169,19 +169,19 @@ _clutter_input_device_x11_construct (ClutterInputDevice *device, case ButtonClass: DeviceButtonPress (x_device, - button_press, + *button_press, device_x11->xevent_list[n_events]); n_events++; DeviceButtonRelease (x_device, - button_release, + *button_release, device_x11->xevent_list[n_events]); n_events++; break; case ValuatorClass: DeviceMotionNotify (x_device, - motion_notify, + *motion_notify, device_x11->xevent_list[n_events]); n_events++; break; From 1f5a7b61b761bf8426b1229c481574c273a5964f Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 15 Jan 2010 12:24:21 +0000 Subject: [PATCH 44/79] tests: Print out the device details on motion The test-device interactive test should print out the device name and id when it detects a motion event. --- tests/interactive/test-devices.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/tests/interactive/test-devices.c b/tests/interactive/test-devices.c index 0d9cde7ec..6af0ce590 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; From 8ead5abba0ebe9c5b5c1b749bb28f7879d63aa49 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 15 Jan 2010 14:42:19 +0000 Subject: [PATCH 45/79] docs: Add clutter_input_device_get_device_name() --- doc/reference/clutter/clutter-sections.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index a4cc2ff35..b72cedb72 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -1002,6 +1002,7 @@ ClutterInputDeviceClass clutter_get_input_device_for_id 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 From 0b47acf60b46b64ae838471765d86e1c65c01cc0 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 15 Jan 2010 14:56:43 +0000 Subject: [PATCH 46/79] docs: Add sections for InputDevice and DeviceManager --- doc/reference/clutter/clutter-docs.xml.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/reference/clutter/clutter-docs.xml.in b/doc/reference/clutter/clutter-docs.xml.in index 921e7cb62..38fe85287 100644 --- a/doc/reference/clutter/clutter-docs.xml.in +++ b/doc/reference/clutter/clutter-docs.xml.in @@ -139,6 +139,8 @@ + + From 65c7ff7d05389a10eed1d881ca572082f03932fe Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 15 Jan 2010 15:29:14 +0000 Subject: [PATCH 47/79] device-manager: Fix the signals definition Add documentation for the signals, as well as using the correct type for the marshallers. --- clutter/clutter-device-manager.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/clutter/clutter-device-manager.c b/clutter/clutter-device-manager.c index 42ce823e4..4ce32e2ed 100644 --- a/clutter/clutter-device-manager.c +++ b/clutter/clutter-device-manager.c @@ -69,6 +69,16 @@ 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), @@ -77,8 +87,18 @@ clutter_device_manager_class_init (ClutterDeviceManagerClass *klass) NULL, NULL, clutter_marshal_VOID__OBJECT, G_TYPE_NONE, 1, - G_TYPE_POINTER); + 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), @@ -87,7 +107,7 @@ clutter_device_manager_class_init (ClutterDeviceManagerClass *klass) NULL, NULL, clutter_marshal_VOID__OBJECT, G_TYPE_NONE, 1, - G_TYPE_POINTER); + CLUTTER_TYPE_INPUT_DEVICE); } static void From dc39e9eff9de0797086ea1a505855724296981f7 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 15 Jan 2010 15:29:52 +0000 Subject: [PATCH 48/79] docs: Documentation fixes for DeviceManager --- clutter/clutter-device-manager.c | 7 ++++++- doc/reference/clutter/clutter-sections.txt | 2 -- doc/reference/clutter/clutter.types | 2 ++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/clutter/clutter-device-manager.c b/clutter/clutter-device-manager.c index 4ce32e2ed..9d5110f94 100644 --- a/clutter/clutter-device-manager.c +++ b/clutter/clutter-device-manager.c @@ -22,12 +22,17 @@ */ /** - * SECTION:clutter-device-manager: + * 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 */ diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index b72cedb72..5e67cbdad 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -941,7 +941,6 @@ ClutterMotionEvent ClutterScrollEvent ClutterStageStateEvent ClutterCrossingEvent -ClutterInputDevice clutter_event_new clutter_event_copy clutter_event_free @@ -999,7 +998,6 @@ clutter_event_get_type ClutterInputDeviceType ClutterInputDevice ClutterInputDeviceClass -clutter_get_input_device_for_id clutter_input_device_get_device_id clutter_input_device_get_device_type clutter_input_device_get_device_name 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 From 94f9f3bd9345d8eb1551c429615473134e3f4e8f Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 15 Jan 2010 16:28:00 +0000 Subject: [PATCH 49/79] x11: Always handle core device events before XI events The XI 1.0 layer is complementary to the X11 core devices handling; this means that core events will still be emitted for the core pointer and keyboard devices, and that secondary (floating) devices should be handled on top of that. Thus, the XI event handling code should be executed (if explicitly compiled in and enabled) if the core device events have not been parsed. Note: this is going away with XI2, which completely replaces both core and XI1 events. --- clutter/x11/clutter-event-x11.c | 515 ++++++++++++++------------------ 1 file changed, 232 insertions(+), 283 deletions(-) diff --git a/clutter/x11/clutter-event-x11.c b/clutter/x11/clutter-event-x11.c index 264f61942..6858cfd65 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 @@ -670,64 +675,38 @@ 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) { - device = backend_x11->core_pointer; - - /* Regular X event */ - switch (xevent->type) + case ButtonPress: + switch (xevent->xbutton.button) { - 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; - event->scroll.device = device; - break; + case 4: /* up */ + case 5: /* down */ + case 6: /* left */ + case 7: /* right */ + event->scroll.type = event->type = CLUTTER_SCROLL; - 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; - } + 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; - set_user_time (backend_x11, &xwindow, event->button.time); + 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; @@ -735,255 +714,225 @@ event_translate (ClutterBackend *backend, 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; - event->motion.device = device; - 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; - event->motion.source = CLUTTER_ACTOR (stage); - event->motion.device = device; + set_user_time (backend_x11, &xwindow, event->button.time); - /* we know that we are entering the stage here */ - _clutter_input_device_set_stage (device, stage); - CLUTTER_NOTE (EVENT, "Entering the stage"); - break; + res = TRUE; + break; - case LeaveNotify: - if (device->stage == NULL) - { - CLUTTER_NOTE (EVENT, - "Discarding LeaveNotify for ButtonRelease " - "event off-stage"); - res = FALSE; - 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->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; + 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; - /* 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); + 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 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]; + + 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; - int button_press, button_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]; - - CLUTTER_NOTE (EVENT, "XInput event type: %d", xevent->type); - - if (xevent->type == EnterNotify) - { - device = backend_x11->core_pointer; - - /* 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; - - /* we know that we are entering the stage here */ - _clutter_input_device_set_stage (device, stage); - CLUTTER_NOTE (EVENT, "Entering the stage"); - } - else if (xevent->type == LeaveNotify) - { - device = backend_x11->core_pointer; - - if (device->stage == NULL) - { - CLUTTER_NOTE (EVENT, - "Discarding LeaveNotify for ButtonRelease " - "event off-stage"); - return FALSE; - } - - 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; - - /* 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); - } - else 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: - 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 = device; - break; - } - - set_user_time (backend_x11, &xwindow, xbev->time); - } - 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) - { - 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 = device; - } - 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; - } -#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); + 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 +#endif /* HAVE_XINPUT */ + { + CLUTTER_NOTE (EVENT, "Uknown Event"); + res = FALSE; } } +out: return res; } From 8a4b64715486699dd8d0ea2146d0adea37d2a6c7 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 20 Jan 2010 19:40:58 +0000 Subject: [PATCH 50/79] x11: Re-enable XI1 extension keyboards The extension keyboard support in XInput 1.x is hopelessly broken. Nevertheless, it's possible to use some bits of it, as we prefer the core keyboard events to the XInput events, thus at least having proper handling for X11 key events on the Stage window. --- clutter/x11/clutter-backend-x11.c | 2 -- clutter/x11/clutter-event-x11.c | 28 ++++++++++++++++++++++++-- clutter/x11/clutter-input-device-x11.c | 21 ++++++++++++------- tests/interactive/test-devices.c | 14 +++++++------ 4 files changed, 48 insertions(+), 17 deletions(-) diff --git a/clutter/x11/clutter-backend-x11.c b/clutter/x11/clutter-backend-x11.c index 55fb65a3b..d3d59df6b 100644 --- a/clutter/x11/clutter-backend-x11.c +++ b/clutter/x11/clutter-backend-x11.c @@ -158,12 +158,10 @@ clutter_x11_register_input_devices (ClutterBackendX11 *backend) device_type = CLUTTER_POINTER_DEVICE; break; -#if 0 /* XInput1 is broken for keyboards */ case IsXExtensionKeyboard: device_type = CLUTTER_KEYBOARD_DEVICE; break; -#endif case IsXExtensionDevice: device_type = CLUTTER_EXTENSION_DEVICE; diff --git a/clutter/x11/clutter-event-x11.c b/clutter/x11/clutter-event-x11.c index 6858cfd65..1a3a260fc 100644 --- a/clutter/x11/clutter-event-x11.c +++ b/clutter/x11/clutter-event-x11.c @@ -267,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; @@ -808,11 +809,14 @@ event_translate (ClutterBackend *backend, #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); @@ -924,6 +928,26 @@ event_translate (ClutterBackend *backend, 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 */ { diff --git a/clutter/x11/clutter-input-device-x11.c b/clutter/x11/clutter-input-device-x11.c index 94b67d2a9..b62ca8a0c 100644 --- a/clutter/x11/clutter-input-device-x11.c +++ b/clutter/x11/clutter-input-device-x11.c @@ -142,6 +142,7 @@ _clutter_input_device_x11_construct (ClutterInputDevice *device, { 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]; @@ -150,22 +151,28 @@ _clutter_input_device_x11_construct (ClutterInputDevice *device, 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) { -#if 0 - /* XInput 1.x is broken for keyboards: */ + /* 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 (xdevice, - backend->event_types[CLUTTER_X11_XINPUT_KEY_PRESS_EVENT], + DeviceKeyPress (x_device, + *key_press, device_x11->xevent_list[n_events]); n_events++; - DeviceKeyRelease (xdevice, - backend->event_types[CLUTTER_X11_XINPUT_KEY_RELEASE_EVENT], + DeviceKeyRelease (x_device, + *key_release, device_x11->xevent_list[n_events]); n_events++; break; -#endif case ButtonClass: DeviceButtonPress (x_device, diff --git a/tests/interactive/test-devices.c b/tests/interactive/test-devices.c index 6af0ce590..e286a064c 100644 --- a/tests/interactive/test-devices.c +++ b/tests/interactive/test-devices.c @@ -105,13 +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 '%s' with id %d...\n", - clutter_input_device_get_device_name (device), - 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", From a545f66a5c34b66d1a31387105d114ca3edd3a46 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 21 Jan 2010 23:41:18 +0000 Subject: [PATCH 51/79] master clock: Improve the timeline advancement protection The commit 1c69c61745ed510f0b6ab16cb963ca01994cb9fc which improved the protection against timeline removals during the master clock advancement was only doing half the job - and actually broke the chaining of animations inside the ::completed signal. 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. 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. For this reason we copy the list of timelines out of the one that the Master Clock holds, take a reference on each timeline, advance them all, release the reference and free the list. --- clutter/clutter-master-clock.c | 41 +++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/clutter/clutter-master-clock.c b/clutter/clutter-master-clock.c index b61ad436c..ca4f9d95a 100644 --- a/clutter/clutter-master-clock.c +++ b/clutter/clutter-master-clock.c @@ -446,7 +446,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 +456,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); } /** From f7e7985344420caf4897777ced6f04cddb7052ee Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 21 Jan 2010 23:57:38 +0000 Subject: [PATCH 52/79] build: Add no-portability option to automake We require the GNU version of make for some of our rules, and it's been so for a while now. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 9f0762403..b7eea5360 100644 --- a/configure.ac +++ b/configure.ac @@ -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]) CLUTTER_MAJOR_VERSION=clutter_major_version CLUTTER_MINOR_VERSION=clutter_minor_version From 4695383281d4b74baa10d78253a27b2eb541eef3 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 22 Jan 2010 00:06:17 +0000 Subject: [PATCH 53/79] build: Use no-define We don't need the PACKAGE and VERSION defines in the config.h. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index b7eea5360..cc294dc83 100644 --- a/configure.ac +++ b/configure.ac @@ -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 -Wno-portability]) +AM_INIT_AUTOMAKE([1.9.6 foreign -Wno-portability no-define]) CLUTTER_MAJOR_VERSION=clutter_major_version CLUTTER_MINOR_VERSION=clutter_minor_version From 94249efff7ec46bbeaac04800d5a9a30c05dcde3 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 22 Jan 2010 21:33:28 +0000 Subject: [PATCH 54/79] animation: Check for value transformability We should not just check for compatibility, but also for the ability to transform a GValue of type A into another of type A'. Usually compatibility is enough, especially if types can be introspected beforehand; some times, though, we also need to check for transformability as a type can provide the transformation functions necessary for the operation. --- clutter/clutter-animation.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/clutter/clutter-animation.c b/clutter/clutter-animation.c index d943c3d4e..23cebc95e 100644 --- a/clutter/clutter-animation.c +++ b/clutter/clutter-animation.c @@ -629,6 +629,7 @@ clutter_animation_validate_bind (ClutterAnimation *animation, ClutterAnimationPrivate *priv; GObjectClass *klass; GParamSpec *pspec; + GType pspec_type; priv = animation->priv; @@ -667,14 +668,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 +850,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 +877,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 +914,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 +932,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; } From 7fa7c4a1b621eff0533ff70b1b6c9be001f62e2a Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 22 Jan 2010 21:36:41 +0000 Subject: [PATCH 55/79] animation: Transform if necessary The Animation code does transformation of values between type A and A' after checking for compatibility using g_value_type_compatible(). This is incorrect: compatibility means that the two types can be copied. The correct conversion should follow: if (compatible (type (A), type (A'))) copy (A, A'); else if (transformable (type (A), type (A'))) transform (A, A'); else error("Unable to trasform type A in A'"); The transformation might still fail, so we need to check for errors there as well as a fall-through case. --- clutter/clutter-animation.c | 42 +++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/clutter/clutter-animation.c b/clutter/clutter-animation.c index 23cebc95e..487fec995 100644 --- a/clutter/clutter-animation.c +++ b/clutter/clutter-animation.c @@ -1645,35 +1645,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 */ From 0788aa43b29454efed3f60160b87881a1c84f603 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 22 Jan 2010 21:41:33 +0000 Subject: [PATCH 56/79] animation: Add more debug annotations We need some better tracking of the Animation's lifetime. --- clutter/clutter-animation.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/clutter/clutter-animation.c b/clutter/clutter-animation.c index 487fec995..9d6afa190 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); From 8daa3035e563870fc614813fd6329c63542b129e Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 22 Jan 2010 21:42:10 +0000 Subject: [PATCH 57/79] docs: Fix the Animation:object property There is a typo in the Animation:object property gtk-doc declaration. --- clutter/clutter-animation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clutter/clutter-animation.c b/clutter/clutter-animation.c index 9d6afa190..7db7faafc 100644 --- a/clutter/clutter-animation.c +++ b/clutter/clutter-animation.c @@ -456,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. * From 7073e69b4e504ddbaffad4a6139e93ddbd40b9f8 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 22 Jan 2010 21:42:55 +0000 Subject: [PATCH 58/79] animation: Verify internal state Be more drastic if the internal state is broken, and assert() if the expected Alpha and Timeline instances we need are not valid. This usually implies a library bug or a massive heap corruption. --- clutter/clutter-animation.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/clutter/clutter-animation.c b/clutter/clutter-animation.c index 7db7faafc..7139e77e6 100644 --- a/clutter/clutter-animation.c +++ b/clutter/clutter-animation.c @@ -1244,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"); @@ -1297,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); From d0f7debfba4879c3df20c0067789b063e18d93ce Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 22 Jan 2010 21:44:28 +0000 Subject: [PATCH 59/79] test-easing: Do not reconnect signals multiple times The test should keep track of the last animation and avoid reconnecting signals to the same instance in case the -r argument has been passed. --- tests/interactive/test-easing.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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; From 8fc07c51a9c71aec77e4b76416ffedc72da309e2 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 21 Jan 2010 17:41:10 +0000 Subject: [PATCH 60/79] actor: Use GParamSpecUint for :opacity The :opacity property is defined using a GParamSpecUchar. This usually leads to issues with language bindings that don't have an 'unsigned char' type and that need to explicitly handle the conversion between G_TYPE_UCHAR and G_TYPE_INT or G_TYPE_UINT. The property definition already specifies an interval size of [0, 255] on the values; more importantly, GObject already implicitly transforms between G_TYPE_UCHAR and G_TYPE_UINT (the GValue transformation functions are registered at type system initialization time) so switching between a GParamSpecUchar and a GParamSpecUint should not be an ABI break. I have tested a simple program using the opacity property before and after the change and I cannot see any run-time warnings related to this issue. --- clutter/clutter-actor.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index c873236b4..dc3edfb4b 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -2581,7 +2581,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 +2864,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: @@ -3462,15 +3462,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); /** From 06d8ebb0ba2d468d492eeb94dede095a14eec909 Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Sun, 15 Nov 2009 19:54:17 +0000 Subject: [PATCH 61/79] cogl: Create CoglTextureUnit with its associated unit number The index field of CoglTextureUnit was never set, leading to the creation of units with index set to 0. When trying to retrieve a texture unit by its index (!= 0) with _cogl_get_texture_unit(), a new one was created as it could not find it back in the list of textures units: ctx->texture_units. http://bugzilla.openedhand.com/show_bug.cgi?id=1958 --- clutter/cogl/cogl/cogl.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clutter/cogl/cogl/cogl.c b/clutter/cogl/cogl/cogl.c index aa61fd590..ca76641f5 100644 --- a/clutter/cogl/cogl/cogl.c +++ b/clutter/cogl/cogl/cogl.c @@ -729,10 +729,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 +767,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 = From de31cbf4f7932f8d48583a476c5af9f6eed6adbd Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Tue, 26 Jan 2010 16:59:50 +0000 Subject: [PATCH 62/79] test-cogl-multitexture: use several materials with texture matrices A small doubt has risen about the use of CoglTextureUnit in materials: will texture matrices still work if we have several materials, each of them having at texture on the same texture unit? The answer is yes! test-cogl-multitexture has been extended to use 2 materials with about the same setup except a little difference: the texture matrices for the lightmaps rotate in opposite directions. While at it, changed the rotation behaviour by an implicit animation with a small additional bonus bling. --- tests/interactive/test-cogl-multitexture.c | 143 +++++++++++++++------ 1 file changed, 102 insertions(+), 41 deletions(-) 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; } From afb30f4013fdcb58ee35af919fdb739ab587683c Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Wed, 18 Nov 2009 01:13:11 +0000 Subject: [PATCH 63/79] tests: blend-string: use g_assert_cmpint It's very useful to see the actual number the reference value is compared too when the test fails. GTest has g_assert_cmp$type() functions for that, so make good use of them. --- tests/conform/test-cogl-blend-strings.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 From 87d19b8d182c2524a5d3c30921fe2c3d4d120606 Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Sun, 15 Nov 2009 20:17:47 +0000 Subject: [PATCH 64/79] cogl: Fix gl equivalent of blend string An example of what could be the equivalent of "RBG = REPLACE(TEXTURE) A = MODULATE(PREVIOUS,TEXTURE)" using the ARB_texture_env_combine extension was given, but it seems that a few typo were left: * remove a spurius GL_COMBINE_ALPHA * use the _ALPHA variant of SRCN and OPERANDN when setting up the alpha combiner --- doc/reference/cogl/blend-strings.xml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) 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);
From 52cb54f5fa665fdf06270fc58121617b44877639 Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Tue, 26 Jan 2010 18:47:25 +0000 Subject: [PATCH 65/79] cogl: Fix checks of the number of available texture units We were checking the number of texture units against the GL enum that is used in glGetInteger() to query that number. Let's abstract this in a little function. Took the opportunity to dig a bit on the usage of GL limits for the number of texture (image) units and document our use of them. We'll need something finer grained if we want to fully exploit texture image units with a programmable pipeline. --- clutter/cogl/cogl/cogl-internal.h | 2 ++ clutter/cogl/cogl/cogl-material.c | 10 ++-------- clutter/cogl/cogl/cogl.c | 30 ++++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 8 deletions(-) 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-material.c b/clutter/cogl/cogl/cogl-material.c index 00c376fb5..65b7258d0 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)) { @@ -1537,7 +1531,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.c b/clutter/cogl/cogl/cogl.c index ca76641f5..387eac417 100644 --- a/clutter/cogl/cogl/cogl.c +++ b/clutter/cogl/cogl/cogl.c @@ -788,6 +788,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) { From da392e24a55ad34bdc8fe9ac0841eca574f76194 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Wed, 27 Jan 2010 11:02:34 +0000 Subject: [PATCH 66/79] docs: Fix some of the examples for the animation docs In the example for clutter_actor_animate the "x" and "y" properties are floats so they need to be passed float values in the var args otherwise it will crash. There was also a missing comma. There were some other minor problems with the behaviours example which would cause it not to compile. --- doc/reference/clutter/clutter-animation-tutorial.xml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) 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); From f288eae0fc454876c2beb87b1205681a338d7f65 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Wed, 27 Jan 2010 12:06:22 +0000 Subject: [PATCH 67/79] docs: Add some notes about the CoglPixelFormat enums The pixel format enums didn't explain what order in memory the components should be so it was difficult to use them. --- clutter/cogl/cogl/cogl-types.h | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/clutter/cogl/cogl/cogl-types.h b/clutter/cogl/cogl/cogl-types.h index 30c73d4f7..826b26055 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 */ From 92a375ab47296a81d739ca29b0972c7abe6eda16 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Tue, 26 Jan 2010 13:46:27 +0000 Subject: [PATCH 68/79] cogl-vertex-buffer: Fix disabling the texture arrays from previous prim MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When setting up the state for the vertex buffer, enable_state_for_drawing_buffer tries to keep track of the highest numbered texture unit in use. It then disables any texture arrays for units that were previously enabled if they are greater than that number. However if there is no texturing in the VBO then the max used unit would be left at 0 which it would later think meant unit 0 is still in use so it wouldn't disable it. To fix this it now initialises the max used unit to -1 which it should interpret as ‘no units are in use’ so it will later disable the arrays for all units. Thanks to Jon Mayo for reporting the bug. http://bugzilla.openedhand.com/show_bug.cgi?id=1957 --- clutter/cogl/cogl/cogl-vertex-buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clutter/cogl/cogl/cogl-vertex-buffer.c b/clutter/cogl/cogl/cogl-vertex-buffer.c index 26d09ce01..5ced90abe 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; From 046a4b80476794bebdfd9005c826a64b7c8c9f81 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Fri, 22 Jan 2010 18:14:57 +0000 Subject: [PATCH 69/79] cogl: Use the colours of COGL_DEBUG=rectangles to debug batching Instead of assigning a new colour to each quad of a batch, the rectangle debugging code now assigns a new colour to each batch so that it can be used to visually see what is being batched. The colour is stored in a global variable that is reset during cogl_clear. This improves the chances that the same colour will be used for a batch in the next frames to avoid flickering. --- clutter/cogl/cogl/cogl-context.h | 6 ++++ clutter/cogl/cogl/cogl-journal.c | 54 ++++++++++++++++++++++---------- clutter/cogl/cogl/cogl.c | 11 +++++++ 3 files changed, 54 insertions(+), 17 deletions(-) diff --git a/clutter/cogl/cogl/cogl-context.h b/clutter/cogl/cogl/cogl-context.h index 717f3534e..53c233c4b 100644 --- a/clutter/cogl/cogl/cogl-context.h +++ b/clutter/cogl/cogl/cogl-context.h @@ -111,6 +111,12 @@ typedef struct CoglHandle texture_download_material; + /* 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-journal.c b/clutter/cogl/cogl/cogl-journal.c index 601d53d9a..96fe3e5c7 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.c b/clutter/cogl/cogl/cogl.c index 387eac417..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"); } From 2d5eeba5d89c129f991f367f0e80c3a152f19e52 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 27 Jan 2010 21:14:43 +0000 Subject: [PATCH 70/79] docs: Fixes for TimeoutPool and Frame sources The TimeoutPool is not used by ClutterTimeline any more, so we need to remove a sentence from its description. We also need to fix the gtk-doc syntax errors. --- clutter/clutter-frame-source.c | 25 +++++++++++++------------ clutter/clutter-timeout-pool.c | 8 +------- 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/clutter/clutter-frame-source.c b/clutter/clutter-frame-source.c index 4a2d65310..dc4e0f66f 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 diff --git a/clutter/clutter-timeout-pool.c b/clutter/clutter-timeout-pool.c index d1cb48252..216e2897c 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 From 578e83e463d4d681de27f1b4e5d64dea8161f0f7 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 27 Jan 2010 21:16:28 +0000 Subject: [PATCH 71/79] Whitespace fixes --- clutter/clutter-frame-source.c | 3 ++- clutter/clutter-timeout-interval.c | 38 ++++++++++++++++++------------ clutter/clutter-timeout-pool.c | 5 ++-- 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/clutter/clutter-frame-source.c b/clutter/clutter-frame-source.c index dc4e0f66f..7ce7b7060 100644 --- a/clutter/clutter-frame-source.c +++ b/clutter/clutter-frame-source.c @@ -132,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-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 216e2897c..0f89d7afb 100644 --- a/clutter/clutter-timeout-pool.c +++ b/clutter/clutter-timeout-pool.c @@ -472,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); } } From 7a1ebcbced45c21937c4407e28274fdb4a92af52 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 27 Jan 2010 21:26:26 +0000 Subject: [PATCH 72/79] Whitespace fixes in cogl-util --- clutter/cogl/cogl/cogl-util.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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, From 5f5f6d825f6460f532f0c0448fc403cdb95e4697 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 29 Jan 2010 09:26:47 +0000 Subject: [PATCH 73/79] build: Allow disabling the conformance test suite When building Clutter for packaging on headless boxes it's pointless to allow building the conformance test to be built (and run on 'make check'). --- configure.ac | 14 +++++++++++++- tests/Makefile.am | 25 ++++++++++++++++++++----- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/configure.ac b/configure.ac index cc294dc83..d56fdb036 100644 --- a/configure.ac +++ b/configure.ac @@ -696,6 +696,18 @@ 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@:>@], @@ -953,7 +965,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/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 From 21d21adbc45a6e0023e4a21b357fbf392b469fb2 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 29 Jan 2010 09:29:37 +0000 Subject: [PATCH 74/79] Remove tabs from configure.ac --- configure.ac | 51 ++++++++++++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/configure.ac b/configure.ac index d56fdb036..9f1831621 100644 --- a/configure.ac +++ b/configure.ac @@ -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], [], @@ -712,28 +712,33 @@ 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) @@ -868,11 +873,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 From 763fcabd8b287445d32eadb6086b0438e3962294 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 29 Jan 2010 09:29:53 +0000 Subject: [PATCH 75/79] build: Warn with --disable-conformance and --enable-gcov When building Clutter without conformance test suite we ought to warn the user that the coverage report is not going to be accurate. --- configure.ac | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 9f1831621..359532acd 100644 --- a/configure.ac +++ b/configure.ac @@ -759,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 ========================================== From 5b9259ba807f4151d08bf2ea868c2cdeed97e975 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 29 Jan 2010 09:31:13 +0000 Subject: [PATCH 76/79] docs: Update the configure switches in the README --- README | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/README b/README index 6ac539fee..aa64f3661 100644 --- a/README +++ b/README @@ -96,9 +96,20 @@ See the INSTALL file. Info on specific Clutter options; 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) From 95712f9897db7f06a6ca34661f20b388dd5a5f77 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 29 Jan 2010 10:13:57 +0000 Subject: [PATCH 77/79] docs: Update the README Clean up the grammar and some wrinkles in the text. --- README | 91 +++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 58 insertions(+), 33 deletions(-) diff --git a/README b/README index aa64f3661..5acdbd381 100644 --- a/README +++ b/README @@ -1,8 +1,8 @@ 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: @@ -15,9 +15,10 @@ Clutter currently requires: • 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 -requires the latest GL headers which can be obtained from -www.khronos.org. +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: @@ -29,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: @@ -60,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 @@ -70,26 +94,27 @@ 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 @@ -166,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 =============================================================================== @@ -183,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 @@ -208,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. @@ -251,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 ------------------------------------------------------------------------------- @@ -358,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 @@ -868,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$ From 579a9a2665b402405820585dacba137984700110 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 1 Feb 2010 11:04:59 +0000 Subject: [PATCH 78/79] stage: Add :key-focus property ClutterStage has both set_key_focus() and get_key_focus() methods, but there is no :key-focus property. This means that it is not possible to get notifications when the key-focus has changes except by connecting to both the ::key-focus-in and ::key-focus-out signals and do additional bookkeeping. http://bugzilla.openedhand.com/show_bug.cgi?id=1956 Signed-off-by: Emmanuele Bassi --- clutter/clutter-stage.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/clutter/clutter-stage.c b/clutter/clutter-stage.c index 518d181c7..38abc7480 100644 --- a/clutter/clutter-stage.c +++ b/clutter/clutter-stage.c @@ -113,7 +113,8 @@ enum PROP_USER_RESIZE, PROP_USE_FOG, PROP_FOG, - PROP_USE_ALPHA + PROP_USE_ALPHA, + PROP_KEY_FOCUS }; enum @@ -694,6 +695,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 +755,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 +1003,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 +1775,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"); } /** From ecbb7ce41a1a759e246fce07f146b8bed5e3d730 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Pi=C3=B1eiro?= Date: Mon, 25 Jan 2010 16:13:58 +0100 Subject: [PATCH 79/79] Fix problems with "position" and "selection-bound" change notification Added a "selection-bound" notify on clutter_text_clear_selection as it changes the value. Added utility function clutter_text_set_positions, in order to change both cursor position and selection bound inside a g_object_[freeze/thaw]_notify block Added g_object_[freeze/thaw]_notify in other functions that changes both cursor position and selection bound Solves http://bugzilla.openedhand.com/show_bug.cgi?id=1955 --- clutter/clutter-text.c | 77 +++++++++++++++++++++++++++++------------- 1 file changed, 53 insertions(+), 24 deletions(-) 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);