From d8dd4b9bf90fc5f3b5f6cdfc744cd58cfaa44e80 Mon Sep 17 00:00:00 2001 From: Mario Liebisch Date: Tue, 25 Oct 2022 10:19:15 +0200 Subject: [PATCH] Simplified the Windows touch implementation --- src/SFML/Window/Win32/InputImpl.cpp | 12 +- src/SFML/Window/Win32/WindowImplWin32.cpp | 335 ++++++++-------------- src/SFML/Window/Win32/WindowImplWin32.hpp | 2 +- 3 files changed, 123 insertions(+), 226 deletions(-) diff --git a/src/SFML/Window/Win32/InputImpl.cpp b/src/SFML/Window/Win32/InputImpl.cpp index 3b1aa3e5a..e37ef8e40 100644 --- a/src/SFML/Window/Win32/InputImpl.cpp +++ b/src/SFML/Window/Win32/InputImpl.cpp @@ -226,7 +226,7 @@ void InputImpl::setMousePosition(const Vector2i& position) //////////////////////////////////////////////////////////// void InputImpl::setMousePosition(const Vector2i& position, const WindowBase& relativeTo) { - WindowHandle handle = relativeTo.getSystemHandle(); + const WindowHandle handle = relativeTo.getSystemHandle(); if (handle) { POINT point = {position.x, position.y}; @@ -253,13 +253,13 @@ Vector2i InputImpl::getTouchPosition(unsigned int finger) //////////////////////////////////////////////////////////// Vector2i InputImpl::getTouchPosition(unsigned int finger, const WindowBase& relativeTo) { - WindowHandle handle = relativeTo.getSystemHandle(); - Vector2i pos; - + const WindowHandle handle = relativeTo.getSystemHandle(); + Vector2i pos; + if (handle && WindowImplWin32::isTouchDown(finger)) { - pos = WindowImplWin32::getTouchPosition(finger); - POINT point = { pos.x, pos.y }; + pos = WindowImplWin32::getTouchPosition(finger); + POINT point = {pos.x, pos.y}; ScreenToClient(handle, &point); pos.x = point.x; pos.y = point.y; diff --git a/src/SFML/Window/Win32/WindowImplWin32.cpp b/src/SFML/Window/Win32/WindowImplWin32.cpp index 3726bbe8c..a993edc19 100644 --- a/src/SFML/Window/Win32/WindowImplWin32.cpp +++ b/src/SFML/Window/Win32/WindowImplWin32.cpp @@ -56,68 +56,28 @@ namespace { +constexpr int maxTouchPoints = 10; // Current Windows API limitation + +// Define constants for tablet API to avoid pulling in an extra Tablet PC header +const char* tabletServiceProperty = "MicrosoftTabletPenServiceProperty"; +const unsigned long long tabletDisablePressAndHold = 0x00000001ULL; + unsigned int windowCount = 0; // Windows owned by SFML unsigned int handleCount = 0; // All window handles const wchar_t* className = L"SFML_Window"; sf::priv::WindowImplWin32* fullscreenWindow = nullptr; -HINSTANCE user32Dll = nullptr; -DWORD touchIDs[10]; -POINT touches[10]; +DWORD touchIDs[maxTouchPoints]; +POINT touches[maxTouchPoints]; -#if WINVER < 0x0601 -// Define touch API that's available for more recent versions of Windows -#define WM_TOUCH 0x0240 - -DECLARE_HANDLE(HTOUCHINPUT); - -typedef struct tagTOUCHINPUT -{ - LONG x; - LONG y; - HANDLE hSource; - DWORD dwID; - DWORD dwFlags; - DWORD dwMask; - DWORD dwTime; - ULONG_PTR dwExtraInfo; - DWORD cxContact; - DWORD cyContact; -} TOUCHINPUT, *PTOUCHINPUT; - -typedef TOUCHINPUT const * PCTOUCHINPUT; - -#define TOUCH_COORD_TO_PIXEL(l) ((l) / 100) - -#define TOUCHEVENTF_MOVE 0x0001 -#define TOUCHEVENTF_DOWN 0x0002 -#define TOUCHEVENTF_UP 0x0004 -#define TOUCHEVENTF_INRANGE 0x0008 -#define TOUCHEVENTF_PRIMARY 0x0010 -#define TOUCHEVENTF_NOCOALESCE 0x0020 -#define TOUCHEVENTF_PEN 0x0040 -#define TOUCHEVENTF_PALM 0x0080 - -typedef BOOL(WINAPI* RegisterTouchWindowFuncType)(HWND, ULONG); -typedef BOOL(WINAPI* CloseTouchInputHandleFuncType)(HTOUCHINPUT); -typedef BOOL(WINAPI* GetTouchInputInfoFuncType)(HTOUCHINPUT, UINT, PTOUCHINPUT, int); - -RegisterTouchWindowFuncType RegisterTouchWindow = nullptr; -CloseTouchInputHandleFuncType CloseTouchInputHandle = nullptr; -GetTouchInputInfoFuncType GetTouchInputInfo = nullptr; -bool touchEnabled = false; -#else -static const bool touchEnabled = true; -#endif - // Convert a hardware dependent ID to a 0 based index we can use int getTouchID(DWORD id) { - for (int i = 0; i < 10; ++i) + for (int i = 0; i < maxTouchPoints; ++i) { if (touchIDs[i] == id) return i; } - for (int i = 0; i < 10; ++i) + for (int i = 0; i < maxTouchPoints; ++i) { if (touchIDs[i] == -1) { @@ -132,7 +92,7 @@ int getTouchID(DWORD id) std::string getErrorString(DWORD error) { PTCHAR buffer; - + if (FormatMessage(FORMAT_MESSAGE_MAX_WIDTH_MASK | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, nullptr, error, @@ -141,8 +101,8 @@ std::string getErrorString(DWORD error) 0, nullptr) == 0) return "Unknown error."; - - sf::String message = buffer; + + const sf::String message = buffer; LocalFree(buffer); return message.toAnsiString(); } @@ -151,52 +111,17 @@ const GUID GUID_DEVINTERFACE_HID = {0x4d1e55b2, 0xf16f, 0x11cf, {0x88, 0xcb, 0x0 void setProcessDpiAware() { - // Try SetProcessDpiAwareness first - HINSTANCE shCoreDll = LoadLibrary(L"Shcore.dll"); - - if (shCoreDll) +#if 0 + // Windows 8.1+ + if (SetProcessDpiAwareness(PROCESS_SYSTEM_DPI_AWARE) == E_INVALIDARG) { - enum ProcessDpiAwareness - { - ProcessDpiUnaware = 0, - ProcessSystemDpiAware = 1, - ProcessPerMonitorDpiAware = 2 - }; - - using SetProcessDpiAwarenessFuncType = HRESULT(WINAPI*)(ProcessDpiAwareness); - auto SetProcessDpiAwarenessFunc = reinterpret_cast( - reinterpret_cast(GetProcAddress(shCoreDll, "SetProcessDpiAwareness"))); - - if (SetProcessDpiAwarenessFunc) - { - // We only check for E_INVALIDARG because we would get - // E_ACCESSDENIED if the DPI was already set previously - // and S_OK means the call was successful - if (SetProcessDpiAwarenessFunc(ProcessSystemDpiAware) == E_INVALIDARG) - { - sf::err() << "Failed to set process DPI awareness" << std::endl; - } - else - { - FreeLibrary(shCoreDll); - return; - } - } - - FreeLibrary(shCoreDll); + sf::err() << "Failed to set process DPI awareness" << std::endl; } +#endif - if (user32Dll) + if (!SetProcessDPIAware()) { - using SetProcessDPIAwareFuncType = BOOL(WINAPI*)(); - auto SetProcessDPIAwareFunc = reinterpret_cast( - reinterpret_cast(GetProcAddress(user32Dll, "SetProcessDPIAware"))); - - if (SetProcessDPIAwareFunc) - { - if (!SetProcessDPIAwareFunc()) - sf::err() << "Failed to set process DPI awareness" << std::endl; - } + sf::err() << "Failed to set process DPI awareness" << std::endl; } } } // namespace @@ -221,21 +146,16 @@ m_fullscreen(false), m_cursorGrabbed(false) { // If we're the first window handle - if (handleCount == 0) - { - // Ensure User32.dll is loaded - if (!user32Dll) - user32Dll = LoadLibraryA("User32.dll"); - - // Set that this process is DPI aware and can handle DPI scaling - setProcessDpiAware(); - } if (m_handle) { - // If we're the first window handle, we only need to poll for joysticks when WM_DEVICECHANGE message is received + // If we're the first window handle, set DPI Awareness + // plus we only need to poll for joysticks when WM_DEVICECHANGE message is received if (handleCount == 0) + { + setProcessDpiAware(); JoystickImpl::setLazyUpdates(true); + } ++handleCount; @@ -265,26 +185,20 @@ m_fullscreen((style & Style::Fullscreen) != 0), m_cursorGrabbed(m_fullscreen) { // If we're the first window handle + // Set that this process is DPI aware and can handle DPI scaling if (handleCount == 0) - { - // Ensure User32.dll is loaded - if (!user32Dll) - user32Dll = LoadLibraryA("User32.dll"); - - // Set that this process is DPI aware and can handle DPI scaling setProcessDpiAware(); - } // Register the window class at first call if (windowCount == 0) registerWindowClass(); // Compute position and size - HDC screenDC = GetDC(nullptr); - int left = (GetDeviceCaps(screenDC, HORZRES) - static_cast(mode.size.x)) / 2; - int top = (GetDeviceCaps(screenDC, VERTRES) - static_cast(mode.size.y)) / 2; - int width = static_cast(mode.size.x); - int height = static_cast(mode.size.y); + const HDC screenDC = GetDC(nullptr); + const int left = (GetDeviceCaps(screenDC, HORZRES) - static_cast(mode.size.x)) / 2; + const int top = (GetDeviceCaps(screenDC, VERTRES) - static_cast(mode.size.y)) / 2; + int width = static_cast(mode.size.x); + int height = static_cast(mode.size.y); ReleaseDC(nullptr, screenDC); // Choose the window style according to the Style parameter @@ -337,21 +251,21 @@ m_cursorGrabbed(m_fullscreen) JoystickImpl::setLazyUpdates(true); ++handleCount; + + // By default, the OS limits the size of the window the the desktop size, + // we have to resize it after creation to apply the real size + setSize(mode.size); + + // Switch to fullscreen if requested + if (m_fullscreen) + switchToFullscreen(mode); + + // Try to prepare touch events, if necessary + prepareTouch(); + + // Increment window count + ++windowCount; } - - // By default, the OS limits the size of the window the the desktop size, - // we have to resize it after creation to apply the real size - setSize(mode.size); - - // Switch to fullscreen if requested - if (m_fullscreen) - switchToFullscreen(mode); - - // Try to prepare touch events, if necessary - prepareTouch(); - - // Increment window count - ++windowCount; } @@ -370,17 +284,9 @@ WindowImplWin32::~WindowImplWin32() --handleCount; // This was the last handle + // Reenable automatic joystick polling if (handleCount == 0) - { - // Free User32.dll - if (user32Dll) - { - FreeLibrary(user32Dll); - user32Dll = nullptr; - } - // Reenable automatic joystick polling JoystickImpl::setLazyUpdates(false); - } } if (!m_callback) @@ -895,7 +801,7 @@ void WindowImplWin32::processEvent(UINT message, WPARAM wParam, LPARAM lParam) position.y = static_cast(HIWORD(lParam)); ScreenToClient(m_handle, &position); - auto delta = static_cast(HIWORD(wParam)); + const auto delta = static_cast(HIWORD(wParam)); Event event; @@ -1045,8 +951,8 @@ void WindowImplWin32::processEvent(UINT message, WPARAM wParam, LPARAM lParam) case WM_MOUSEMOVE: { // Extract the mouse local coordinates - int x = static_cast(LOWORD(lParam)); - int y = static_cast(HIWORD(lParam)); + const int x = static_cast(LOWORD(lParam)); + const int y = static_cast(HIWORD(lParam)); // Get the client area of the window RECT area; @@ -1115,7 +1021,7 @@ void WindowImplWin32::processEvent(UINT message, WPARAM wParam, LPARAM lParam) if ((wParam == DBT_DEVICEARRIVAL) || (wParam == DBT_DEVICEREMOVECOMPLETE)) { // Some sort of device change has happened, update joystick connections if it is a device interface - auto* deviceBroadcastHeader = reinterpret_cast(lParam); + const auto* deviceBroadcastHeader = reinterpret_cast(lParam); if (deviceBroadcastHeader && (deviceBroadcastHeader->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)) JoystickImpl::updateConnections(); @@ -1126,67 +1032,64 @@ void WindowImplWin32::processEvent(UINT message, WPARAM wParam, LPARAM lParam) case WM_TOUCH: { // Get the number of events - std::int16_t num = LOWORD(wParam); - - // Reserve memory - PTOUCHINPUT events = new TOUCHINPUT[num]; - - if (events) - { - if (GetTouchInputInfo(reinterpret_cast(lParam), num, events, sizeof(TOUCHINPUT))) - { - POINT point; - for (int i = 0; i < num; ++i) - { - Event event; - int index = getTouchID(events[i].dwID); - - // Out of Ids? Should never happen - if (index == -1) - continue; - - event.touch.finger = static_cast(index); - point.x = TOUCH_COORD_TO_PIXEL(events[i].x); - point.y = TOUCH_COORD_TO_PIXEL(events[i].y); - - POINT cpoint = point; - ScreenToClient(m_handle, &cpoint); - event.touch.x = cpoint.x; - event.touch.y = cpoint.y; - - if (events[i].dwFlags & TOUCHEVENTF_DOWN) - { - event.type = Event::TouchBegan; - pushEvent(event); - - // Prevent initial move event - touches[index] = point; - } - if (events[i].dwFlags & TOUCHEVENTF_UP) - { - event.type = Event::TouchEnded; - pushEvent(event); + const unsigned int count = LOWORD(wParam); - // Remove the stored ID - touchIDs[index] = static_cast(-1); - } - if (events[i].dwFlags & TOUCHEVENTF_MOVE) { - // Only handle real movement - if (touches[index].x != point.x || touches[index].y != point.y) - { - touches[index] = point; - event.type = Event::TouchMoved; - pushEvent(event); - } + // Reserve memory + std::vector events(count); + + if (GetTouchInputInfo(reinterpret_cast(lParam), count, events.data(), sizeof(TOUCHINPUT))) + { + POINT point; + for (unsigned int i = 0; i < count; ++i) + { + Event event; + int index = getTouchID(events[i].dwID); + + // Out of IDs? Should never happen + if (index == -1) + continue; + + event.touch.finger = static_cast(index); + point.x = TOUCH_COORD_TO_PIXEL(events[i].x); + point.y = TOUCH_COORD_TO_PIXEL(events[i].y); + + POINT cpoint = point; + ScreenToClient(m_handle, &cpoint); + event.touch.x = cpoint.x; + event.touch.y = cpoint.y; + + if (events[i].dwFlags & TOUCHEVENTF_DOWN) + { + event.type = Event::TouchBegan; + pushEvent(event); + + // Prevent initial move event + touches[index] = point; + } + if (events[i].dwFlags & TOUCHEVENTF_UP) + { + event.type = Event::TouchEnded; + pushEvent(event); + + // Remove the stored ID + touchIDs[index] = static_cast(-1); + } + if (events[i].dwFlags & TOUCHEVENTF_MOVE) + { + // Only handle real movement + if (touches[index].x != point.x || touches[index].y != point.y) + { + touches[index] = point; + event.type = Event::TouchMoved; + pushEvent(event); } } - CloseTouchInputHandle(reinterpret_cast(lParam)); } - else - { - err() << "Failed to get touch input info: " << getErrorString(GetLastError()) << std::endl; - } - delete[] events; + CloseTouchInputHandle(reinterpret_cast(lParam)); + } + else + { + err() << "Failed to get touch input info: " << getErrorString(GetLastError()) << std::endl; } break; } @@ -1361,26 +1264,20 @@ void WindowImplWin32::prepareTouch() { prepared = true; -#if WINVER < 0x0601 - RegisterTouchWindow = reinterpret_cast(GetProcAddress(user32Dll, "RegisterTouchWindow")); - - touchEnabled = RegisterTouchWindow != nullptr; - - // If we've got touch support, load the other procs - if (touchEnabled) - { - CloseTouchInputHandle = reinterpret_cast(GetProcAddress(user32Dll, "CloseTouchInputHandle")); - GetTouchInputInfo = reinterpret_cast(GetProcAddress(user32Dll, "GetTouchInputInfo")); - - // Reset touch IDs - for (int i = 0; i < 10; ++i) - touchIDs[i] = static_cast(-1); - } -#endif + // Reset touch IDs + std::fill_n(touchIDs, maxTouchPoints, static_cast(-1)); } - if (touchEnabled && m_handle) - RegisterTouchWindow(m_handle, 0); + // Tell Windows this window is touch aware + RegisterTouchWindow(m_handle, 0); + + // Disable right mouse button emulation by long touching + const ATOM atom = GlobalAddAtomA(tabletServiceProperty); + if (atom) + { + SetPropA(m_handle, tabletServiceProperty, reinterpret_cast(tabletDisablePressAndHold)); + GlobalDeleteAtom(atom); + } } //////////////////////////////////////////////////////////// diff --git a/src/SFML/Window/Win32/WindowImplWin32.hpp b/src/SFML/Window/Win32/WindowImplWin32.hpp index 1d5fbd121..40b2669ea 100644 --- a/src/SFML/Window/Win32/WindowImplWin32.hpp +++ b/src/SFML/Window/Win32/WindowImplWin32.hpp @@ -291,7 +291,7 @@ private: /// //////////////////////////////////////////////////////////// void prepareTouch(); - + //////////////////////////////////////////////////////////// // Member data ////////////////////////////////////////////////////////////