Skip to content

Commit

Permalink
Added multi-touch support for Windows
Browse files Browse the repository at this point in the history
  • Loading branch information
MarioLiebisch committed Mar 2, 2017
1 parent f053871 commit 02b332b
Show file tree
Hide file tree
Showing 3 changed files with 271 additions and 19 deletions.
27 changes: 18 additions & 9 deletions src/SFML/Window/Win32/InputImpl.cpp
Expand Up @@ -35,6 +35,7 @@
#define _WIN32_WINNT 0x0501
#include <SFML/Window/Window.hpp>
#include <SFML/Window/Win32/InputImpl.hpp>
#include <SFML/Window/Win32/WindowImplWin32.hpp>
#include <windows.h>


Expand Down Expand Up @@ -229,26 +230,34 @@ void InputImpl::setMousePosition(const Vector2i& position, const Window& relativ


////////////////////////////////////////////////////////////
bool InputImpl::isTouchDown(unsigned int /*finger*/)
bool InputImpl::isTouchDown(unsigned int finger)
{
// Not applicable
return false;
return WindowImplWin32::isTouchDown(finger);
}


////////////////////////////////////////////////////////////
Vector2i InputImpl::getTouchPosition(unsigned int /*finger*/)
Vector2i InputImpl::getTouchPosition(unsigned int finger)
{
// Not applicable
return Vector2i();
return WindowImplWin32::getTouchPosition(finger);
}


////////////////////////////////////////////////////////////
Vector2i InputImpl::getTouchPosition(unsigned int /*finger*/, const Window& /*relativeTo*/)
Vector2i InputImpl::getTouchPosition(unsigned int finger, const Window& relativeTo)
{
// Not applicable
return Vector2i();
WindowHandle handle = relativeTo.getSystemHandle();
Vector2i pos;

if (handle && WindowImplWin32::isTouchDown(finger))
{
pos = WindowImplWin32::getTouchPosition(finger);
POINT point = { pos.x, pos.y };
ScreenToClient(handle, &point);
pos.x = point.x;
pos.y = point.y;
}
return pos;
}

} // namespace priv
Expand Down
237 changes: 227 additions & 10 deletions src/SFML/Window/Win32/WindowImplWin32.cpp
Expand Up @@ -66,11 +66,91 @@ namespace
unsigned int handleCount = 0; // All window handles
const wchar_t* className = L"SFML_Window";
sf::priv::WindowImplWin32* fullscreenWindow = NULL;
HINSTANCE user32Dll = NULL;
DWORD touchIDs[10];
POINT touches[10];

#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 = NULL;
CloseTouchInputHandleFuncType CloseTouchInputHandle = NULL;
GetTouchInputInfoFuncType GetTouchInputInfo = NULL;
bool touchEnabled = false;
#else
static const bool touchEnabled = true;
#endif

// Convert a hardware dependent ID to a 0 based index we can use
sf::Int8 getTouchID(DWORD id)
{
for (int i = 0; i < 10; ++i)
{
if (touchIDs[i] == id)
return i;
}
for (int i = 0; i < 10; ++i)
{
if (touchIDs[i] == -1)
{
touchIDs[i] = id;
return i;
}
}
return -1;
}

// Get a system error string from an error code
std::string getErrorString(DWORD error)
{
PTCHAR buffer;

if (FormatMessage(FORMAT_MESSAGE_MAX_WIDTH_MASK | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, error, 0, reinterpret_cast<PTCHAR>(&buffer), 0, NULL) == 0)
return "Unknown error.";

sf::String message = buffer;
LocalFree(buffer);
return message.toAnsiString();
}

void setProcessDpiAware()
{
// Try SetProcessDpiAwareness first
HINSTANCE shCoreDll = LoadLibrary(L"Shcore.dll");
HINSTANCE shCoreDll = LoadLibraryA("Shcore.dll");

if (shCoreDll)
{
Expand Down Expand Up @@ -105,8 +185,6 @@ namespace

// Fall back to SetProcessDPIAware if SetProcessDpiAwareness
// is not available on this system
HINSTANCE user32Dll = LoadLibrary(L"user32.dll");

if (user32Dll)
{
typedef BOOL (WINAPI* SetProcessDPIAwareFuncType)(void);
Expand All @@ -117,8 +195,6 @@ namespace
if (!SetProcessDPIAwareFunc())
sf::err() << "Failed to set process DPI awareness" << std::endl;
}

FreeLibrary(user32Dll);
}
}
}
Expand All @@ -141,12 +217,20 @@ m_mouseInside (false),
m_fullscreen (false),
m_cursorGrabbed (false)
{
// Set that this process is DPI aware and can handle DPI scaling
setProcessDpiAware();
// 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
// We only need to poll for joysticks when WM_DEVICECHANGE message is received
if (handleCount == 0)
JoystickImpl::setLazyUpdates(true);

Expand All @@ -155,6 +239,9 @@ m_cursorGrabbed (false)
// We change the event procedure of the control (it is important to save the old one)
SetWindowLongPtrW(m_handle, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
m_callback = SetWindowLongPtrW(m_handle, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(&WindowImplWin32::globalOnEvent));

// Try to prepare touch events, if necessary
prepareTouch();
}
}

Expand All @@ -173,8 +260,16 @@ m_mouseInside (false),
m_fullscreen (style & Style::Fullscreen),
m_cursorGrabbed (m_fullscreen)
{
// Set that this process is DPI aware and can handle DPI scaling
setProcessDpiAware();
// 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();
}

// Register the window class at first call
if (windowCount == 0)
Expand Down Expand Up @@ -230,6 +325,9 @@ m_cursorGrabbed (m_fullscreen)
if (m_fullscreen)
switchToFullscreen(mode);

// Try to prepare touch events, if necessary
prepareTouch();

// Increment window count
windowCount++;
}
Expand All @@ -247,8 +345,18 @@ WindowImplWin32::~WindowImplWin32()
{
--handleCount;

// This was the last handle
if (handleCount == 0)
{
// Free User32.dll
if (user32Dll)
{
FreeLibrary(user32Dll);
user32Dll = NULL;
}
// Reenable automatic joystick polling
JoystickImpl::setLazyUpdates(false);
}
}

if (!m_callback)
Expand Down Expand Up @@ -972,6 +1080,73 @@ void WindowImplWin32::processEvent(UINT message, WPARAM wParam, LPARAM lParam)
JoystickImpl::updateConnections();
break;
}
case WM_TOUCH:
{
// Get the number of events
Uint16 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;
Int8 index = getTouchID(events[i].dwID);

// Out of Ids? Should never happen
if (index == -1)
continue;

event.touch.finger = 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] = -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;
}
break;
}
}
}

Expand Down Expand Up @@ -1133,6 +1308,48 @@ LRESULT CALLBACK WindowImplWin32::globalOnEvent(HWND handle, UINT message, WPARA
return DefWindowProcW(handle, message, wParam, lParam);
}

////////////////////////////////////////////////////////////
void WindowImplWin32::prepareTouch()
{
static bool prepared = false;
if (!prepared)
{
prepared = true;

#if WINVER < 0x0601
RegisterTouchWindow = reinterpret_cast<RegisterTouchWindowFuncType>(GetProcAddress(user32Dll, "RegisterTouchWindow"));

touchEnabled = RegisterTouchWindow != NULL;

// 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] = -1;
}
#endif
}

if (touchEnabled && m_handle)
RegisterTouchWindow(m_handle, 0);
}

////////////////////////////////////////////////////////////
bool WindowImplWin32::isTouchDown(unsigned int finger)
{
return touchIDs[finger] != -1;
}

////////////////////////////////////////////////////////////
Vector2i WindowImplWin32::getTouchPosition(unsigned int finger)
{
return Vector2i(touches[finger].x, touches[finger].y);
}

} // namespace priv

} // namespace sf
Expand Down

0 comments on commit 02b332b

Please sign in to comment.