Simplified the Windows touch implementation

This commit is contained in:
Mario Liebisch 2022-10-25 10:19:15 +02:00
parent 3e1031e3d4
commit d8dd4b9bf9
No known key found for this signature in database
GPG Key ID: 32B56AC1F87EFBF9
3 changed files with 123 additions and 226 deletions

View File

@ -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;

View File

@ -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<SetProcessDpiAwarenessFuncType>(
reinterpret_cast<void*>(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<SetProcessDPIAwareFuncType>(
reinterpret_cast<void*>(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<int>(mode.size.x)) / 2;
int top = (GetDeviceCaps(screenDC, VERTRES) - static_cast<int>(mode.size.y)) / 2;
int width = static_cast<int>(mode.size.x);
int height = static_cast<int>(mode.size.y);
const HDC screenDC = GetDC(nullptr);
const int left = (GetDeviceCaps(screenDC, HORZRES) - static_cast<int>(mode.size.x)) / 2;
const int top = (GetDeviceCaps(screenDC, VERTRES) - static_cast<int>(mode.size.y)) / 2;
int width = static_cast<int>(mode.size.x);
int height = static_cast<int>(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<std::int16_t>(HIWORD(lParam));
ScreenToClient(m_handle, &position);
auto delta = static_cast<std::int16_t>(HIWORD(wParam));
const auto delta = static_cast<std::int16_t>(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<std::int16_t>(LOWORD(lParam));
int y = static_cast<std::int16_t>(HIWORD(lParam));
const int x = static_cast<std::int16_t>(LOWORD(lParam));
const int y = static_cast<std::int16_t>(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<DEV_BROADCAST_HDR*>(lParam);
const auto* deviceBroadcastHeader = reinterpret_cast<DEV_BROADCAST_HDR*>(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<HTOUCHINPUT>(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<unsigned int>(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<DWORD>(-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<TOUCHINPUT> events(count);
if (GetTouchInputInfo(reinterpret_cast<HTOUCHINPUT>(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<unsigned int>(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<DWORD>(-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<HTOUCHINPUT>(lParam));
}
else
{
err() << "Failed to get touch input info: " << getErrorString(GetLastError()) << std::endl;
}
delete[] events;
CloseTouchInputHandle(reinterpret_cast<HTOUCHINPUT>(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<RegisterTouchWindowFuncType>(GetProcAddress(user32Dll, "RegisterTouchWindow"));
touchEnabled = RegisterTouchWindow != nullptr;
// If we've got touch support, load the other procs
if (touchEnabled)
{
CloseTouchInputHandle = reinterpret_cast<CloseTouchInputHandleFuncType>(GetProcAddress(user32Dll, "CloseTouchInputHandle"));
GetTouchInputInfo = reinterpret_cast<GetTouchInputInfoFuncType>(GetProcAddress(user32Dll, "GetTouchInputInfo"));
// Reset touch IDs
for (int i = 0; i < 10; ++i)
touchIDs[i] = static_cast<DWORD>(-1);
}
#endif
// Reset touch IDs
std::fill_n(touchIDs, maxTouchPoints, static_cast<DWORD>(-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<HANDLE>(tabletDisablePressAndHold));
GlobalDeleteAtom(atom);
}
}
////////////////////////////////////////////////////////////

View File

@ -291,7 +291,7 @@ private:
///
////////////////////////////////////////////////////////////
void prepareTouch();
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////