From 14a28620ae13ef041696e2e3747ec00738cfd2bd Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Fri, 15 Jan 2010 22:56:37 +0000 Subject: [PATCH] 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],