diff --git a/hooklib/config.c b/hooklib/config.c index 5fc9383..9473717 100644 --- a/hooklib/config.c +++ b/hooklib/config.c @@ -14,3 +14,14 @@ void dvd_config_load(struct dvd_config *cfg, const wchar_t *filename) cfg->enable = GetPrivateProfileIntW(L"dvd", L"enable", 1, filename); } + +void touch_screen_config_load(struct touch_screen_config *cfg, const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + cfg->enable = GetPrivateProfileIntW(L"touch", L"enable", 1, filename); + cfg->remap = GetPrivateProfileIntW(L"touch", L"remap", 1, filename); + cfg->cursor = GetPrivateProfileIntW(L"touch", L"cursor", 1, filename); + cfg->setwndptr = GetPrivateProfileIntW(L"touch", L"setwndptr", 0, filename); +} diff --git a/hooklib/config.h b/hooklib/config.h index 5da045d..9763274 100644 --- a/hooklib/config.h +++ b/hooklib/config.h @@ -3,5 +3,7 @@ #include #include "hooklib/dvd.h" +#include "hooklib/touch.h" void dvd_config_load(struct dvd_config *cfg, const wchar_t *filename); +void touch_screen_config_load(struct touch_screen_config *cfg, const wchar_t *filename); diff --git a/hooklib/meson.build b/hooklib/meson.build index b4f9f2c..d932c81 100644 --- a/hooklib/meson.build +++ b/hooklib/meson.build @@ -26,5 +26,7 @@ hooklib_lib = static_library( 'setupapi.h', 'spike.c', 'spike.h', + 'touch.c', + 'touch.h', ], ) diff --git a/hooklib/touch.c b/hooklib/touch.c new file mode 100644 index 0000000..13684cf --- /dev/null +++ b/hooklib/touch.c @@ -0,0 +1,298 @@ +/* +This part (touch screen hook) is mostly taken from spicetools, which is GPL. +This means there can be some license issues if you do use this code in some other places without including source code. +*/ +#include + +#include +#include +#include + +#include "hook/com-proxy.h" +#include "hook/table.h" + +#include "hooklib/config.h" +#include "hooklib/dll.h" +#include "hooklib/touch.h" + +#include "util/dprintf.h" + +/* API hooks */ + +static LRESULT hook_wndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); + +static ATOM WINAPI hook_RegisterClassExA( + WNDCLASSEXA* wndClass +); + +static int WINAPI hook_GetSystemMetrics( + int nIndex +); + +static BOOL WINAPI hook_RegisterTouchWindow( + HWND hwnd, + ULONG ulFlags +); + +static BOOL WINAPI hook_GetTouchInputInfo( + HANDLE hTouchInput, + UINT cInputs, + PTOUCHINPUT pInputs, + int cbSize +); + +static HCURSOR WINAPI hook_SetCursor(HCURSOR cursor); + +static int WINAPI hook_ShowCursor(BOOL bShow); + +static LONG_PTR WINAPI hook_SetWindowLongPtrA(HWND hWnd, int nIndex, LONG_PTR dwNewLong); + +/* Link pointers */ + +static ATOM (WINAPI *next_RegisterClassExA)( + const WNDCLASSEXA* wndClass +); + +static int (WINAPI *next_GetSystemMetrics)( + int nIndex +); + +static BOOL (WINAPI *next_RegisterTouchWindow)( + HWND hwnd, + ULONG ulFlags +); + +static BOOL (WINAPI *next_GetTouchInputInfo)( + HANDLE hTouchInput, + UINT cInputs, + PTOUCHINPUT pInputs, + int cbSize +); + +static HCURSOR(WINAPI *next_SetCursor)(HCURSOR cursor); + +static int (WINAPI *next_ShowCursor)(BOOL bShow); + +static LONG_PTR (WINAPI *next_SetWindowLongPtrA)(HWND hWnd, int nIndex, LONG_PTR dwNewLong); + +static bool touch_hook_initted; +static bool touch_held; +static HWND registered_hWnd; +static struct touch_screen_config touch_config; +static WNDPROC orig_wndProc; +static HCURSOR defaultCursor; + +static const struct hook_symbol touch_hooks[] = { + { + .name = "RegisterClassExA", + .patch = hook_RegisterClassExA, + .link = (void**)&next_RegisterClassExA + }, + { + .name = "GetSystemMetrics", + .patch = hook_GetSystemMetrics, + .link = (void **) &next_GetSystemMetrics + }, + { + .name = "RegisterTouchWindow", + .patch = hook_RegisterTouchWindow, + .link = (void **) &next_RegisterTouchWindow + }, + { + .name = "GetTouchInputInfo", + .patch = hook_GetTouchInputInfo, + .link = (void **) &next_GetTouchInputInfo + }, + { + .name = "ShowCursor", + .patch = hook_ShowCursor, + .link = (void **) &next_ShowCursor + }, + { + .name = "SetCursor", + .patch = hook_SetCursor, + .link = (void **) &next_SetCursor + }, + { + .name = "SetWindowLongPtrA", + .patch = hook_SetWindowLongPtrA, + .link = (void**) &next_SetWindowLongPtrA + }, +}; + +void touch_screen_hook_init(const struct touch_screen_config *cfg, HINSTANCE self) +{ + assert(cfg != NULL); + + if (!cfg->enable) { + return; + } + + if (touch_hook_initted) { + return; + } + + touch_hook_initted = true; + + defaultCursor = LoadCursorA(NULL, IDC_CROSS); + + memcpy(&touch_config, cfg, sizeof(*cfg)); + touch_hook_insert_hooks(NULL); + dprintf("TOUCH: hook enabled.\n"); +} + +void touch_hook_insert_hooks(HMODULE target) +{ + hook_table_apply( + target, + "user32.dll", + touch_hooks, + _countof(touch_hooks)); +} + +static int WINAPI hook_ShowCursor(BOOL bShow) +{ + if (touch_config.cursor) { + return 1; + } + + return next_ShowCursor(bShow); +} + +static HCURSOR WINAPI hook_SetCursor(HCURSOR cursor) { + if (cursor == 0 && touch_config.cursor) + return next_SetCursor(defaultCursor); + + return next_SetCursor(cursor); +} + +// remap mouse events to touch events + +static LRESULT hook_wndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { + if (hWnd == registered_hWnd && (Msg == WM_LBUTTONDOWN || Msg == WM_LBUTTONUP || (touch_held && Msg == WM_MOUSEMOVE))) { + orig_wndProc(hWnd, WM_TOUCH, 1, 1); + } + + if (Msg == WM_SETCURSOR && touch_config.cursor) { + SetCursor(defaultCursor); + return TRUE; + } + + return orig_wndProc(hWnd, Msg, wParam, lParam); +} + +static ATOM WINAPI hook_RegisterClassExA( + WNDCLASSEXA* wndClass +) { + if (!touch_config.setwndptr && wndClass->lpfnWndProc) { + orig_wndProc = wndClass->lpfnWndProc; + wndClass->lpfnWndProc = (WNDPROC) hook_wndProc; + } + + return next_RegisterClassExA(wndClass); +} + +// Spicetools misc/wintouchemu.cpp + +static int WINAPI hook_GetSystemMetrics( + int nIndex +) +{ + if (nIndex == 94) return 0x01 | 0x02 | 0x40 | 0x80; + + int orig = next_GetSystemMetrics(nIndex); + + return orig; +} + +static BOOL WINAPI hook_RegisterTouchWindow( + HWND hwnd, + ULONG ulFlags +) +{ + registered_hWnd = hwnd; + return true; +} + +// Soul Reverse uses this to initialize wndprocs handling input + +static LONG_PTR WINAPI hook_SetWindowLongPtrA(HWND hWnd, int nIndex, LONG_PTR dwNewLong) { + if (touch_config.setwndptr && nIndex == -4) { + orig_wndProc = (WNDPROC) dwNewLong; + dwNewLong = (LONG_PTR) hook_wndProc; + } + + return next_SetWindowLongPtrA(hWnd, nIndex, dwNewLong); +} + +// Converting mouse event to touch event +static BOOL WINAPI hook_GetTouchInputInfo( + HANDLE hTouchInput, + UINT cInputs, + PTOUCHINPUT pInputs, + int cbSize +) +{ + bool result = false; + int sw, sh, cw, ch; + RECT cRect; + sw = GetSystemMetrics(SM_CXSCREEN); + sh = GetSystemMetrics(SM_CYSCREEN); + GetClientRect(registered_hWnd, &cRect); + cw = cRect.right - cRect.left; + ch = cRect.bottom - cRect.top; + static bool mouse_state_old = false; + + for (UINT input = 0; input < cInputs; input++) { + TOUCHINPUT *touch_input = &pInputs[input]; + + touch_input->x = 0; + touch_input->y = 0; + touch_input->hSource = NULL; + touch_input->dwID = 0; + touch_input->dwFlags = 0; + touch_input->dwMask = 0; + touch_input->dwTime = 0; + touch_input->dwExtraInfo = 0; + touch_input->cxContact = 0; + touch_input->cyContact = 0; + + bool mouse_state = (GetKeyState(VK_LBUTTON) & 0x100) != 0; + + if (mouse_state || mouse_state_old) { + POINT cursorPos; + + GetCursorPos(&cursorPos); + + if (touch_config.remap) { + ScreenToClient(registered_hWnd, &cursorPos); + cursorPos.x = (long)(cursorPos.x * ((double)sw / cw)); + cursorPos.y = (long)(cursorPos.y * ((double)sh / ch)); + } + + result = true; + touch_input->x = cursorPos.x * 100; + touch_input->y = cursorPos.y * 100; + touch_input->hSource = hTouchInput; + touch_input->dwID = 1; + touch_input->dwFlags = 0; + if (mouse_state && !mouse_state_old) { + touch_input->dwFlags |= TOUCHEVENTF_DOWN; + touch_held = true; + } else if (mouse_state && mouse_state_old) { + touch_input->dwFlags |= TOUCHEVENTF_MOVE; + } else if (!mouse_state && mouse_state_old) { + touch_input->dwFlags |= TOUCHEVENTF_UP; + touch_held = false; + } + touch_input->dwMask = 0; + touch_input->dwTime = 0; + touch_input->dwExtraInfo = 0; + touch_input->cxContact = 0; + touch_input->cyContact = 0; + } + mouse_state_old = mouse_state; + } + + return result; +} diff --git a/hooklib/touch.h b/hooklib/touch.h new file mode 100644 index 0000000..04b1031 --- /dev/null +++ b/hooklib/touch.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +#include + +struct touch_screen_config { + bool enable; + bool remap; + bool cursor; + bool setwndptr; +}; + +/* Init is not thread safe because API hook init is not thread safe blah + blah blah you know the drill by now. */ + +void touch_screen_hook_init(const struct touch_screen_config *cfg, HINSTANCE self); +void touch_hook_insert_hooks(HMODULE target); diff --git a/saohook/config.c b/saohook/config.c index 4652cfc..2b32fee 100644 --- a/saohook/config.c +++ b/saohook/config.c @@ -4,6 +4,8 @@ #include "saohook/config.h" +#include "hooklib/config.h" + #include "platform/config.h" void sao_dll_config_load( @@ -41,18 +43,6 @@ void systype_config_load( ); } -void sao_touch_config_load( - struct sao_touch_config *cfg, - const wchar_t *filename) -{ - cfg->enable = GetPrivateProfileIntW( - L"touch", - L"enable", - 1, - filename - ); -} - void sao_hook_config_load( struct sao_hook_config *cfg, const wchar_t *filename) @@ -67,6 +57,6 @@ void sao_hook_config_load( qr_config_load(&cfg->qr, filename); usio_config_load(&cfg->usio, filename); systype_config_load(&cfg->systype, filename); - sao_touch_config_load(&cfg->touch, filename); + touch_screen_config_load(&cfg->touch, filename); vfd_config_load(&cfg->vfd, filename); } diff --git a/saohook/config.h b/saohook/config.h index 4c3ba8b..4c5f074 100644 --- a/saohook/config.h +++ b/saohook/config.h @@ -5,11 +5,11 @@ #include "saohook/sao-dll.h" #include "platform/config.h" +#include "hooklib/touch.h" #include "gfxhook/config.h" #include "amcus/config.h" #include "board/config.h" #include "saohook/systype.h" -#include "saohook/touch.h" struct sao_hook_config { struct platform_config platform; @@ -20,7 +20,7 @@ struct sao_hook_config { struct qr_config qr; struct usio_config usio; struct systype_config systype; - struct sao_touch_config touch; + struct touch_screen_config touch; struct vfd_config vfd; }; @@ -39,8 +39,3 @@ void sao_hook_config_load( void systype_config_load( struct systype_config *cfg, const wchar_t *filename); - -void sao_touch_config_load( - struct sao_touch_config *cfg, - const wchar_t *filename); - diff --git a/saohook/dllmain.c b/saohook/dllmain.c index 904b69e..22970db 100644 --- a/saohook/dllmain.c +++ b/saohook/dllmain.c @@ -6,13 +6,13 @@ #include "saohook/usio.h" #include "saohook/unity.h" #include "saohook/systype.h" -#include "saohook/touch.h" #include "amcus/amcus.h" #include "hook/process.h" #include "hooklib/serial.h" +#include "hooklib/touch.h" #include "board/sg-reader.h" #include "board/vfd.h" @@ -35,6 +35,7 @@ static DWORD CALLBACK sao_pre_startup(void) sao_hook_config_load(&sao_hook_cfg, L".\\bananatools.ini"); + touch_screen_hook_init(&sao_hook_cfg.touch, sao_hook_mod); serial_hook_init(); struct dongle_info dinfo; @@ -79,12 +80,6 @@ static DWORD CALLBACK sao_pre_startup(void) hr = systype_hook_init(&sao_hook_cfg.systype, &sao_hook_cfg.platform.dongle); - if (FAILED(hr)) { - ExitProcess(EXIT_FAILURE); - } - - hr = sao_touch_hook_init(&sao_hook_cfg.touch); - if (FAILED(hr)) { ExitProcess(EXIT_FAILURE); } diff --git a/saohook/meson.build b/saohook/meson.build index 9e215fc..83e81b6 100644 --- a/saohook/meson.build +++ b/saohook/meson.build @@ -29,8 +29,6 @@ shared_library( 'sao-dll.h', 'systype.c', 'systype.h', - 'touch.c', - 'touch.h', 'usio.c', 'usio.h', 'unity.c', diff --git a/saohook/touch.c b/saohook/touch.c deleted file mode 100644 index d1e7716..0000000 --- a/saohook/touch.c +++ /dev/null @@ -1,42 +0,0 @@ -#include -#include -#include - -#include "hook/table.h" -#include "hooklib/reg.h" -#include "util/dprintf.h" -#include "saohook/touch.h" - -static int WINAPI my_GetSystemMetrics(int nIndex); -static int (WINAPI *next_GetSystemMetrics)(int nIndex); - -static const struct hook_symbol touch_user32_syms[] = { - { - .name = "GetSystemMetrics", - .patch = my_GetSystemMetrics, - .link = (void **) &next_GetSystemMetrics, - }, -}; - -HRESULT sao_touch_hook_init(const struct sao_touch_config *cfg) -{ - if (!cfg->enable) { - return S_OK; - } - - hook_table_apply( - NULL, - "User32.dll", - touch_user32_syms, - _countof(touch_user32_syms)); - - return S_OK; -} - -static int WINAPI my_GetSystemMetrics(int nIndex) -{ - if (nIndex == SM_DIGITIZER) { - return NID_MULTI_INPUT; - } - return next_GetSystemMetrics(nIndex); -} diff --git a/saohook/touch.h b/saohook/touch.h deleted file mode 100644 index 8975d73..0000000 --- a/saohook/touch.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include -#include - -struct sao_touch_config { - bool enable; -}; - -HRESULT sao_touch_hook_init(const struct sao_touch_config *cfg); \ No newline at end of file