WinRT: workaround a possible Windows bug, whereby hiding cursors, disables mouse-moved events This workaround, unfortunately, requires that apps directly link to a set of Win32-style cursor resource files (that contain a transparent cursor image). Copies of suitable resource files are in src/core/winrt/, and should be included directly in an app's MSVC project. A rough explanation of this workaround/hack, and why it's needed (and seemingly can't be done through programmatic means), is in this change's code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
diff --git a/docs/README-winrt.md b/docs/README-winrt.md
index 581406d..cccbbea 100644
--- a/docs/README-winrt.md
+++ b/docs/README-winrt.md
@@ -159,7 +159,9 @@ following, at a high-level:
the linker, and will copy SDL's .dll files to your app's final output.
4. adjust your app's build settings, at minimum, telling it where to find SDL's
header files.
-5. add a file that contains a WinRT-appropriate main function.
+5. add files that contains a WinRT-appropriate main function, along with some
+ data to make sure mouse-cursor-hiding (via SDL_ShowCursor(SDL_DISABLE) calls)
+ work properly.
6. add SDL-specific app code.
7. build and run your app.
@@ -267,33 +269,27 @@ To change these settings:
10. close the dialog, saving settings, by clicking the "OK" button
-### 5. Add a WinRT-appropriate main function to the app. ###
+### 5. Add a WinRT-appropriate main function, and a blank-cursor image, to the app. ###
-C/C++-based WinRT apps do contain a `main` function that the OS will invoke when
-the app starts launching. The parameters of WinRT main functions are different
-than those found on other platforms, Win32 included. SDL/WinRT provides a
-platform-appropriate main function that will perform these actions, setup key
-portions of the app, then invoke a classic, C/C++-style main function (that take
-in "argc" and "argv" parameters). The code for this file is contained inside
-SDL's source distribution, under `src/main/winrt/SDL_winrt_main_NonXAML.cpp`.
-You'll need to add this file, or a copy of it, to your app's project, and make
-sure it gets compiled using a Microsoft-specific set of C++ extensions called
-C++/CX.
+A few files should be included directly in your app's MSVC project, specifically:
+1. a WinRT-appropriate main function (which is different than main() functions on
+ other platforms)
+2. a Win32-style cursor resource, used by SDL_ShowCursor() to hide the mouse cursor
+ (if and when the app needs to do so). *If this cursor resource is not
+ included, mouse-position reporting may fail if and when the cursor is
+ hidden, due to possible bugs/design-oddities in Windows itself.*
-**NOTE: C++/CX compilation is currently required in at least one file of your
-app's project. This is to make sure that Visual C++'s linker builds a 'Windows
-Metadata' file (.winmd) for your app. Not doing so can lead to build errors.**
-
-To include `SDL_winrt_main_NonXAML.cpp`:
+To include these files:
1. right-click on your project (again, in Visual C++'s Solution Explorer),
navigate to "Add", then choose "Existing Item...".
-2. open `SDL_winrt_main_NonXAML.cpp`, which is found inside SDL's source
- distribution, under `src/main/winrt/`. Make sure that the open-file dialog
- closes, either by double-clicking on the file, or single-clicking on it and
- then clicking Add.
-3. right-click on the file (as listed in your project), then click on
- "Properties...".
+2. navigate to the directory containing SDL's source code, then into its
+ subdirectory, 'src/main/winrt/'. Select, then add, the following files:
+ - `SDL_winrt_main_NonXAML.cpp`
+ - `SDL2-WinRTResources.rc`
+ - `SDL2-WinRTResource_BlankCursor.cur`
+3. right-click on the file `SDL_winrt_main_NonXAML.cpp` (as listed in your
+ project), then click on "Properties...".
4. in the drop-down box next to "Configuration", choose, "All Configurations"
5. in the drop-down box next to "Platform", choose, "All Platforms"
6. in the left-hand list, click on "C/C++"
@@ -301,6 +297,11 @@ To include `SDL_winrt_main_NonXAML.cpp`:
8. click the OK button. This will close the dialog.
+**NOTE: C++/CX compilation is currently required in at least one file of your
+app's project. This is to make sure that Visual C++'s linker builds a 'Windows
+Metadata' file (.winmd) for your app. Not doing so can lead to build errors.**
+
+
### 6. Add app code and assets ###
At this point, you can add in SDL-specific source code. Be sure to include a
@@ -465,3 +466,13 @@ section.
/nodefaultlib:vccorlibd /nodefaultlib:msvcrtd vccorlibd.lib msvcrtd.lib
+
+#### Mouse-motion events fail to get sent, or SDL_GetMouseState() fails to return updated values
+
+This may be caused by a bug in Windows itself, whereby hiding the mouse
+cursor can cause mouse-position reporting to fail.
+
+SDL provides a workaround for this, but it requires that an app links to a
+set of Win32-style cursor image-resource files. A copy of suitable resource
+files can be found in `src/main/winrt/`. Adding them to an app's Visual C++
+project file should be sufficient to get the app to use them.
diff --git a/src/main/winrt/SDL2-WinRTResource_BlankCursor.cur b/src/main/winrt/SDL2-WinRTResource_BlankCursor.cur
new file mode 100644
index 0000000..c6556b8
Binary files /dev/null and b/src/main/winrt/SDL2-WinRTResource_BlankCursor.cur differ
diff --git a/src/main/winrt/SDL2-WinRTResources.rc b/src/main/winrt/SDL2-WinRTResources.rc
new file mode 100644
index 0000000..ce8549f
--- /dev/null
+++ b/src/main/winrt/SDL2-WinRTResources.rc
@@ -0,0 +1,3 @@
+#include "winres.h"
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+5000 CURSOR "SDL2-WinRTResource_BlankCursor.cur"
diff --git a/src/video/winrt/SDL_winrtmouse.cpp b/src/video/winrt/SDL_winrtmouse.cpp
index d3140a4..9997d6e 100644
--- a/src/video/winrt/SDL_winrtmouse.cpp
+++ b/src/video/winrt/SDL_winrtmouse.cpp
@@ -26,6 +26,7 @@
* Windows includes:
*/
#include <Windows.h>
+#include <windows.ui.core.h>
using namespace Windows::UI::Core;
using Windows::UI::Core::CoreCursor;
@@ -116,11 +117,69 @@ WINRT_ShowCursor(SDL_Cursor * cursor)
return 0;
}
+ CoreWindow ^ coreWindow = CoreWindow::GetForCurrentThread();
if (cursor) {
CoreCursor ^* theCursor = (CoreCursor ^*) cursor->driverdata;
- CoreWindow::GetForCurrentThread()->PointerCursor = *theCursor;
+ coreWindow->PointerCursor = *theCursor;
} else {
- CoreWindow::GetForCurrentThread()->PointerCursor = nullptr;
+ // HACK ALERT: TL;DR - Hiding the cursor in WinRT/UWP apps is weird, and
+ // a Win32-style cursor resource file must be directly included in apps,
+ // otherwise hiding the cursor will cause mouse-motion data to never be
+ // received.
+ //
+ // Here's the lengthy explanation:
+ //
+ // There are two ways to hide a cursor in WinRT/UWP apps.
+ // Both involve setting the WinRT CoreWindow's (which is somewhat analogous
+ // to a Win32 HWND) 'PointerCursor' property.
+ //
+ // The first way to hide a cursor sets PointerCursor to nullptr. This
+ // is, arguably, the easiest to implement for an app. It does have an
+ // unfortunate side-effect: it'll prevent mouse-motion events from being
+ // sent to the app (via CoreWindow).
+ //
+ // The second way to hide a cursor sets PointerCursor to a transparent
+ // cursor. This allows mouse-motion events to be sent to the app, but is
+ // more difficult to set up, as:
+ // 1. WinRT/UWP, while providing a few stock cursors, does not provide
+ // a completely transparent cursor.
+ // 2. WinRT/UWP allows apps to provide custom-built cursors, but *ONLY*
+ // if they are linked directly inside the app, via Win32-style
+ // cursor resource files. APIs to create cursors at runtime are
+ // not provided to apps, and attempting to link-to or use Win32
+ // cursor-creation APIs could cause an app to fail Windows Store
+ // certification.
+ //
+ // SDL can use either means of hiding the cursor. It provides a Win32-style
+ // set of cursor resource files in its source distribution, inside
+ // src/main/winrt/. If those files are linked to an SDL-for-WinRT/UWP app
+ // (by including them in a MSVC project, for example), SDL will attempt to
+ // use those, if and when the cursor is hidden via SDL APIs. If those
+ // files are not linked in, SDL will attempt to hide the cursor via the
+ // 'set PointerCursor to nullptr' means (which, if you recall, causes
+ // mouse-motion data to NOT be sent to the app!).
+ //
+ // Tech notes:
+ // - SDL's blank cursor resource uses a resource ID of 5000.
+ // - SDL's cursor resources consist of the following two files:
+ // - src/main/winrt/SDL2-WinRTResource_BlankCursor.cur -- cursor pixel data
+ // - src/main/winrt/SDL2-WinRTResources.rc -- declares the cursor resource, and its ID (of 5000)
+ //
+
+ const unsigned int win32CursorResourceID = 5000;
+ CoreCursor ^ blankCursor = ref new CoreCursor(CoreCursorType::Custom, win32CursorResourceID);
+
+ // Set 'PointerCursor' to 'blankCursor' in a way that shouldn't throw
+ // an exception if the app hasn't loaded that resource.
+ ABI::Windows::UI::Core::ICoreCursor * iblankCursor = reinterpret_cast<ABI::Windows::UI::Core::ICoreCursor *>(blankCursor);
+ ABI::Windows::UI::Core::ICoreWindow * icoreWindow = reinterpret_cast<ABI::Windows::UI::Core::ICoreWindow *>(coreWindow);
+ HRESULT hr = icoreWindow->put_PointerCursor(iblankCursor);
+ if (FAILED(hr)) {
+ // The app doesn't contain the cursor resource, or some other error
+ // occurred. Just use the other, but mouse-motion-preventing, means of
+ // hiding the cursor.
+ coreWindow->PointerCursor = nullptr;
+ }
}
return 0;
}