Initial re-upload of spice2x-24-08-24
This commit is contained in:
60
touch/handler.h
Normal file
60
touch/handler.h
Normal file
@@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <windows.h>
|
||||
|
||||
#include "touch.h"
|
||||
|
||||
// touch states
|
||||
extern std::mutex TOUCH_POINTS_M;
|
||||
extern std::mutex TOUCH_EVENTS_M;
|
||||
extern std::vector<TouchPoint> TOUCH_POINTS;
|
||||
|
||||
enum msg_handler_action {
|
||||
/*
|
||||
* The message was unhandled by the touch handler. This is the default value set when
|
||||
* the touch handler is called.
|
||||
*/
|
||||
ACTION_PASS = 0,
|
||||
|
||||
/*
|
||||
* The message was handled by the touch handler, but `DefWindowProc` should be called
|
||||
* and its return value used instead of the stored one.
|
||||
*/
|
||||
ACTION_RETURN_DEFAULT = 1,
|
||||
|
||||
/*
|
||||
* The message was handled by the touch handler and the stored `return_value` should be
|
||||
* used as the return value.
|
||||
*/
|
||||
ACTION_RETURN_STORED = 2,
|
||||
};
|
||||
|
||||
struct msg_handler_result {
|
||||
msg_handler_action action;
|
||||
LRESULT return_value;
|
||||
};
|
||||
|
||||
class TouchHandler {
|
||||
public:
|
||||
|
||||
TouchHandler(std::string name);
|
||||
virtual ~TouchHandler() = default;
|
||||
|
||||
static bool is_available() {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool window_register(HWND hWnd) {
|
||||
return true;
|
||||
}
|
||||
virtual bool window_unregister(HWND hWnd) {
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void handle_message(msg_handler_result &result, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) = 0;
|
||||
};
|
||||
|
||||
void add_touch_event(TouchEvent *te);
|
||||
|
||||
void update_card_button();
|
||||
930
touch/touch.cpp
Normal file
930
touch/touch.cpp
Normal file
@@ -0,0 +1,930 @@
|
||||
|
||||
// enable touch functions - set version to windows 7
|
||||
// mingw otherwise doesn't load touch stuff
|
||||
#define _WIN32_WINNT 0x0601
|
||||
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <windowsx.h>
|
||||
|
||||
#include "touch.h"
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "external/imgui/imgui.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "overlay/overlay.h"
|
||||
#include "rawinput/touch.h"
|
||||
#include "util/circular_buffer.h"
|
||||
#include "util/detour.h"
|
||||
#include "util/libutils.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
#include "handler.h"
|
||||
#include "win7.h"
|
||||
#include "win8.h"
|
||||
|
||||
// constants
|
||||
const char *SPICETOUCH_CLASS_NAME = "spiceTouchClass";
|
||||
const char *SPICETOUCH_FONT_NAME = "Courier New";
|
||||
const char *INSERT_CARD_TEXT = "Insert Card";
|
||||
|
||||
// settings
|
||||
static const int TOUCH_EVENT_BUFFER_SIZE = 1024 * 4;
|
||||
static const int TOUCH_EVENT_BUFFER_THRESHOLD1 = 1024 * 2;
|
||||
static const int TOUCH_EVENT_BUFFER_THRESHOLD2 = 1024 * 3;
|
||||
|
||||
// in mainline spicetools, this was false (show by default)
|
||||
// in spice2x, this is true (hide by default)
|
||||
bool SPICETOUCH_CARD_DISABLE = true;
|
||||
HWND SPICETOUCH_TOUCH_HWND = nullptr;
|
||||
int SPICETOUCH_TOUCH_X = 0;
|
||||
int SPICETOUCH_TOUCH_Y = 0;
|
||||
int SPICETOUCH_TOUCH_WIDTH = 0;
|
||||
int SPICETOUCH_TOUCH_HEIGHT = 0;
|
||||
|
||||
// touch states
|
||||
std::vector<TouchPoint> TOUCH_POINTS;
|
||||
std::mutex TOUCH_POINTS_M;
|
||||
static circular_buffer<TouchEvent> TOUCH_EVENTS(TOUCH_EVENT_BUFFER_SIZE);
|
||||
std::mutex TOUCH_EVENTS_M;
|
||||
|
||||
// general states
|
||||
static bool SPICETOUCH_INITIALIZED = false;
|
||||
static bool SPICETOUCH_ATTACHED = false;
|
||||
static bool SPICETOUCH_ATTACHED_DXHOOK = false;
|
||||
static HWND SPICETOUCH_HWND = nullptr;
|
||||
static WNDPROC SPICETOUCH_OLD_PROC = nullptr;
|
||||
static bool SPICETOUCH_ENABLE_MOUSE = true;
|
||||
static bool SPICETOUCH_REGISTERED_TOUCH = false;
|
||||
static bool SPICETOUCH_CALL_OLD_PROC = false;
|
||||
static std::thread *SPICETOUCH_TOUCH_THREAD = nullptr;
|
||||
static HFONT SPICETOUCH_FONT;
|
||||
static RECT SPICETOUCH_CARD_RECT;
|
||||
static bool SPICETOUCH_CARD_ENABLED = false;
|
||||
static const char *LOG_MODULE_NAME = "touch";
|
||||
|
||||
static TouchHandler *TOUCH_HANDLER = nullptr;
|
||||
|
||||
TouchHandler::TouchHandler(std::string name) {
|
||||
log_info("touch", "Using touch handler: {}", name);
|
||||
}
|
||||
|
||||
class RawInputTouchHandler : public TouchHandler {
|
||||
public:
|
||||
RawInputTouchHandler() : TouchHandler("rawinput") {
|
||||
}
|
||||
virtual bool window_register(HWND hWnd) override {
|
||||
return true;
|
||||
}
|
||||
virtual bool window_unregister(HWND hWnd) override {
|
||||
return true;
|
||||
}
|
||||
virtual void handle_message(msg_handler_result&, HWND, UINT, WPARAM, LPARAM) override {
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Add touch event but take care of buffer size
|
||||
* Be careful, this doesn't lock the mutex on it's own
|
||||
*/
|
||||
void add_touch_event(TouchEvent *te) {
|
||||
|
||||
// check if first threshold is passed
|
||||
if (TOUCH_EVENTS.size() > TOUCH_EVENT_BUFFER_THRESHOLD1) {
|
||||
switch (te->type) {
|
||||
case TOUCH_DOWN:
|
||||
|
||||
// ignore touch down events after first threshold
|
||||
return;
|
||||
|
||||
case TOUCH_MOVE:
|
||||
|
||||
// add move event if we're not over the second threshold
|
||||
if (TOUCH_EVENTS.size() <= TOUCH_EVENT_BUFFER_THRESHOLD2) {
|
||||
TOUCH_EVENTS.put(*te);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
case TOUCH_UP:
|
||||
|
||||
// check if buffer is full
|
||||
if (TOUCH_EVENTS.full()) {
|
||||
|
||||
// ignore event
|
||||
return;
|
||||
}
|
||||
|
||||
// when the buffer isn't full yet, add the touch up event
|
||||
TOUCH_EVENTS.put(*te);
|
||||
return;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// add the touch up event
|
||||
TOUCH_EVENTS.put(*te);
|
||||
}
|
||||
|
||||
static void touch_initialize() {
|
||||
|
||||
// check if already initialized
|
||||
if (SPICETOUCH_INITIALIZED) {
|
||||
return;
|
||||
}
|
||||
SPICETOUCH_INITIALIZED = true;
|
||||
|
||||
// initialize handler
|
||||
if (RI_MGR && rawinput::touch::is_enabled(RI_MGR.get())) {
|
||||
TOUCH_HANDLER = new RawInputTouchHandler();
|
||||
} else if (Win8Handler::is_available()) {
|
||||
TOUCH_HANDLER = new Win8Handler();
|
||||
} else if (Win7Handler::is_available()) {
|
||||
TOUCH_HANDLER = new Win7Handler();
|
||||
} else {
|
||||
log_warning(LOG_MODULE_NAME, "no touch handler available");
|
||||
}
|
||||
}
|
||||
|
||||
static inline void touch_register_window(HWND hWnd) {
|
||||
|
||||
// register touch handling for window
|
||||
if (TOUCH_HANDLER != nullptr) {
|
||||
TOUCH_HANDLER->window_register(hWnd);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void touch_unregister_window(HWND hWnd) {
|
||||
|
||||
// unregister touch handling for window
|
||||
if (TOUCH_HANDLER != nullptr) {
|
||||
TOUCH_HANDLER->window_unregister(hWnd);
|
||||
}
|
||||
}
|
||||
|
||||
bool is_touch_available() {
|
||||
|
||||
// initialize touch handler
|
||||
touch_initialize();
|
||||
|
||||
// Check if a touch handler has been set. No need to call `is_available` here
|
||||
// as `touch_initialize` does that.
|
||||
return TOUCH_HANDLER != nullptr;
|
||||
}
|
||||
|
||||
void update_card_button() {
|
||||
|
||||
// check if enabled
|
||||
if (!SPICETOUCH_CARD_ENABLED) {
|
||||
return;
|
||||
}
|
||||
|
||||
// check touch points
|
||||
for (TouchPoint touchPoint : TOUCH_POINTS) {
|
||||
POINT pt {};
|
||||
pt.x = touchPoint.x;
|
||||
pt.y = touchPoint.y;
|
||||
|
||||
if (PtInRect(&SPICETOUCH_CARD_RECT, pt) != 0) {
|
||||
eamuse_card_insert(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static LRESULT CALLBACK SpiceTouchWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
||||
|
||||
// check if touch was registered
|
||||
if (!SPICETOUCH_REGISTERED_TOUCH) {
|
||||
SPICETOUCH_REGISTERED_TOUCH = true;
|
||||
|
||||
// check if touch is available
|
||||
if (is_touch_available()) {
|
||||
|
||||
// notify the handler of our window
|
||||
TOUCH_HANDLER->window_register(hWnd);
|
||||
|
||||
// enable card unless the feature is disabled
|
||||
if (!SPICETOUCH_CARD_DISABLE) {
|
||||
SPICETOUCH_CARD_ENABLED = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update rawinput touch display resolution
|
||||
if (msg == WM_DISPLAYCHANGE) {
|
||||
log_info("touch", "detected display mode change");
|
||||
rawinput::touch::display_update();
|
||||
}
|
||||
|
||||
if (msg == WM_CLOSE) {
|
||||
if ((GRAPHICS_IIDX_WSUB && hWnd == TDJ_SUBSCREEN_WINDOW) || (hWnd == SDVX_SUBSCREEN_WINDOW)) {
|
||||
log_misc("touch", "ignore WM_CLOSE for subscreen window");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// check messages for dedicated window
|
||||
if (SPICETOUCH_TOUCH_THREAD != nullptr) {
|
||||
|
||||
// check if overlay is enabled
|
||||
auto overlay_enabled = overlay::OVERLAY
|
||||
&& overlay::OVERLAY->renderer == overlay::OverlayRenderer::SOFTWARE;
|
||||
|
||||
// we don't want mouse events to interfere
|
||||
SPICETOUCH_ENABLE_MOUSE = !overlay_enabled || !overlay::OVERLAY->get_active();
|
||||
|
||||
switch (msg) {
|
||||
case WM_MOUSEACTIVATE: {
|
||||
// Set the main window as the active window to reactivate the imgui cursor
|
||||
HWND parent = GetParent(hWnd);
|
||||
if (GetActiveWindow() != parent) {
|
||||
SetActiveWindow(parent);
|
||||
}
|
||||
return MA_NOACTIVATE;
|
||||
}
|
||||
case WM_SETCURSOR: {
|
||||
|
||||
// set cursor back to the overlay one
|
||||
if (overlay_enabled && LOWORD(lParam) == HTCLIENT && overlay::OVERLAY->update_cursor()) {
|
||||
//return true; TODO: can make cursor invisible?
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_TIMER: {
|
||||
InvalidateRect(hWnd, NULL, TRUE);
|
||||
break;
|
||||
}
|
||||
case WM_PAINT: {
|
||||
|
||||
// get window rect
|
||||
HWND parent = GetParent(hWnd);
|
||||
RECT windowRect {}, clientRect {};
|
||||
GetWindowRect(parent, &windowRect);
|
||||
GetClientRect(parent, &clientRect);
|
||||
|
||||
// adjust to client area
|
||||
POINT p1 {.x = clientRect.left, .y = clientRect.top};
|
||||
POINT p2 {.x = clientRect.right, .y = clientRect.bottom};
|
||||
ClientToScreen(parent, &p1);
|
||||
ClientToScreen(parent, &p2);
|
||||
windowRect.left = p1.x;
|
||||
windowRect.top = p1.y;
|
||||
windowRect.right = p2.x;
|
||||
windowRect.bottom = p2.y;
|
||||
|
||||
// check if rect needs to update
|
||||
RECT windowRectOld {};
|
||||
GetWindowRect(hWnd, &windowRectOld);
|
||||
if (memcmp(&windowRectOld, &windowRect, sizeof(RECT)) != 0) {
|
||||
SetWindowPos(hWnd, HWND_TOP,
|
||||
windowRect.left, windowRect.top,
|
||||
windowRect.right - windowRect.left,
|
||||
windowRect.bottom - windowRect.top,
|
||||
SWP_NOZORDER | SWP_NOREDRAW | SWP_NOREPOSITION | SWP_NOACTIVATE);
|
||||
}
|
||||
|
||||
// draw overlay
|
||||
if (overlay_enabled) {
|
||||
|
||||
// update and render
|
||||
overlay::OVERLAY->update();
|
||||
overlay::OVERLAY->new_frame();
|
||||
overlay::OVERLAY->render();
|
||||
|
||||
// get pixel data
|
||||
int width, height;
|
||||
uint32_t *pixel_data = overlay::OVERLAY.get()->sw_get_pixel_data(&width, &height);
|
||||
if (width > 0 && height > 0) {
|
||||
|
||||
// create bitmap
|
||||
HBITMAP bitmap = CreateBitmap(width, height, 1, 8 * sizeof(uint32_t), pixel_data);
|
||||
|
||||
// prepare paint
|
||||
PAINTSTRUCT paint {};
|
||||
HDC hdc = BeginPaint(hWnd, &paint);
|
||||
HDC hdcMem = CreateCompatibleDC(hdc);
|
||||
SetBkMode(hdc, TRANSPARENT);
|
||||
|
||||
/*
|
||||
* draw bitmap
|
||||
* - this currently sets the background to black because of SRCCOPY
|
||||
* - SRCPAINT will blend but colors are wrong
|
||||
* - once this is figured out we could also try hooking WM_PAINT and
|
||||
* draw directly to the game window
|
||||
*/
|
||||
SelectObject(hdcMem, bitmap);
|
||||
BitBlt(hdc, 0, 0, width, height, hdcMem, 0, 0, SRCCOPY);
|
||||
|
||||
// clean up
|
||||
DeleteObject(bitmap);
|
||||
DeleteDC(hdcMem);
|
||||
EndPaint(hWnd, &paint);
|
||||
}
|
||||
}
|
||||
|
||||
// draw card input
|
||||
if (SPICETOUCH_CARD_ENABLED && (SPICETOUCH_FONT != nullptr)) {
|
||||
|
||||
// prepare paint
|
||||
PAINTSTRUCT paint {};
|
||||
HDC hdc = BeginPaint(hWnd, &paint);
|
||||
SetBkMode(hdc, TRANSPARENT);
|
||||
|
||||
// create brushes
|
||||
HBRUSH brushBorder = CreateSolidBrush(RGB(0, 196, 0));
|
||||
HBRUSH brushFill = CreateSolidBrush(RGB(255, 192, 203));
|
||||
|
||||
// get window rect
|
||||
RECT windowRect {};
|
||||
GetWindowRect(hWnd, &windowRect);
|
||||
|
||||
bool should_rotate = avs::game::is_model({ "J44", "K44" });
|
||||
|
||||
// create box rect
|
||||
RECT boxRect {};
|
||||
if (should_rotate) {
|
||||
boxRect.left = windowRect.right - 75;
|
||||
boxRect.top = 20;
|
||||
boxRect.right = windowRect.right - 44;
|
||||
boxRect.bottom = 151;
|
||||
} else {
|
||||
boxRect.left = 20;
|
||||
boxRect.top = 44;
|
||||
boxRect.right = 141;
|
||||
boxRect.bottom = 75;
|
||||
}
|
||||
|
||||
// save box rect for touch input
|
||||
SPICETOUCH_CARD_RECT = boxRect;
|
||||
|
||||
// draw borders
|
||||
FillRect(hdc, &boxRect, brushBorder);
|
||||
|
||||
// modify box rect
|
||||
boxRect.left += 1;
|
||||
boxRect.top += 1;
|
||||
boxRect.right -= 1;
|
||||
boxRect.bottom -= 1;
|
||||
|
||||
// fill box
|
||||
FillRect(hdc, &boxRect, brushFill);
|
||||
|
||||
// modify box rect
|
||||
if (should_rotate) {
|
||||
boxRect.left += 5 + 20;
|
||||
boxRect.top += 5;
|
||||
} else {
|
||||
boxRect.left += 5;
|
||||
boxRect.top += 5;
|
||||
}
|
||||
|
||||
// draw text
|
||||
SelectObject(hdc, SPICETOUCH_FONT);
|
||||
SetTextColor(hdc, RGB(0, 196, 0));
|
||||
DrawText(hdc, INSERT_CARD_TEXT, -1, &boxRect, DT_LEFT | DT_BOTTOM | DT_NOCLIP);
|
||||
|
||||
// delete objects
|
||||
DeleteObject(brushFill);
|
||||
DeleteObject(brushBorder);
|
||||
|
||||
// end paint
|
||||
EndPaint(hWnd, &paint);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// call default window procedure
|
||||
return DefWindowProc(hWnd, msg, wParam, lParam);
|
||||
}
|
||||
case WM_CREATE: {
|
||||
|
||||
// set to layered window
|
||||
SetWindowLong(hWnd, GWL_EXSTYLE, GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
|
||||
|
||||
// set alpha value
|
||||
SetLayeredWindowAttributes(hWnd, 0, 0xFFu, LWA_ALPHA);
|
||||
SetLayeredWindowAttributes(hWnd, RGB(255, 192, 203), 0, LWA_COLORKEY);
|
||||
|
||||
// get window rect
|
||||
RECT windowRect {};
|
||||
GetWindowRect(hWnd, &windowRect);
|
||||
|
||||
bool should_rotate = avs::game::is_model({ "J44", "K44" });
|
||||
auto rotation = should_rotate ? 2700 : 0;
|
||||
|
||||
// load font
|
||||
SPICETOUCH_FONT = CreateFont(20, 0, rotation, rotation, FW_NORMAL, FALSE, FALSE, FALSE,
|
||||
ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
|
||||
NONANTIALIASED_QUALITY, DEFAULT_PITCH,
|
||||
SPICETOUCH_FONT_NAME);
|
||||
if (SPICETOUCH_FONT == nullptr) {
|
||||
log_warning(LOG_MODULE_NAME, "font '{}' could not be loaded", SPICETOUCH_FONT_NAME);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
case WM_DESTROY: {
|
||||
PostQuitMessage(0);
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
msg_handler_result result {
|
||||
.action = ACTION_PASS,
|
||||
.return_value = 0,
|
||||
};
|
||||
|
||||
// check if imgui is handling this mouse event
|
||||
if (overlay::OVERLAY != nullptr && overlay::OVERLAY->get_active() && ImGui::GetIO().WantCaptureMouse) {
|
||||
result.action = ACTION_RETURN_DEFAULT;
|
||||
|
||||
} else if (TOUCH_HANDLER != nullptr) {
|
||||
|
||||
// call touch handler
|
||||
TOUCH_HANDLER->handle_message(result, hWnd, msg, wParam, lParam);
|
||||
}
|
||||
|
||||
if (result.action == ACTION_RETURN_STORED) {
|
||||
|
||||
// return the value from the touch handler
|
||||
return result.return_value;
|
||||
}
|
||||
|
||||
if (result.action == ACTION_PASS) {
|
||||
|
||||
// parse mouse messages
|
||||
switch (msg) {
|
||||
case WM_LBUTTONDOWN: {
|
||||
|
||||
// check if mouse is enabled
|
||||
if (SPICETOUCH_ENABLE_MOUSE) {
|
||||
|
||||
// lock touch points
|
||||
std::lock_guard<std::mutex> lock_points(TOUCH_POINTS_M);
|
||||
std::lock_guard<std::mutex> lock_events(TOUCH_EVENTS_M);
|
||||
|
||||
// remove all points with ID 0
|
||||
for (size_t x = 0; x < TOUCH_POINTS.size(); x++) {
|
||||
TouchPoint *tp = &TOUCH_POINTS[x];
|
||||
|
||||
if (tp->id == 0u) {
|
||||
|
||||
// generate touch up event
|
||||
TouchEvent te {
|
||||
.id = tp->id,
|
||||
.x = tp->x,
|
||||
.y = tp->y,
|
||||
.type = TOUCH_UP,
|
||||
.mouse = tp->mouse,
|
||||
};
|
||||
add_touch_event(&te);
|
||||
|
||||
// erase touch point
|
||||
TOUCH_POINTS.erase(TOUCH_POINTS.begin() + x);
|
||||
}
|
||||
}
|
||||
|
||||
// create touch point
|
||||
TouchPoint tp {
|
||||
.id = 0,
|
||||
.x = GET_X_LPARAM(lParam),
|
||||
.y = GET_Y_LPARAM(lParam),
|
||||
.mouse = true,
|
||||
};
|
||||
TOUCH_POINTS.push_back(tp);
|
||||
|
||||
// add touch down event
|
||||
TouchEvent te {
|
||||
.id = tp.id,
|
||||
.x = tp.x,
|
||||
.y = tp.y,
|
||||
.type = TOUCH_DOWN,
|
||||
.mouse = tp.mouse,
|
||||
};
|
||||
add_touch_event(&te);
|
||||
|
||||
// card button
|
||||
update_card_button();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case WM_MOUSEMOVE: {
|
||||
|
||||
// check if mouse is enabled
|
||||
if (SPICETOUCH_ENABLE_MOUSE) {
|
||||
|
||||
// lock touch points
|
||||
std::lock_guard<std::mutex> lock_points(TOUCH_POINTS_M);
|
||||
std::lock_guard<std::mutex> lock_events(TOUCH_EVENTS_M);
|
||||
|
||||
// update point
|
||||
for (auto &x : TOUCH_POINTS) {
|
||||
TouchPoint *tp = &x;
|
||||
|
||||
// find ID 0
|
||||
if (tp->id == 0u) {
|
||||
|
||||
// update touch point position
|
||||
tp->x = GET_X_LPARAM(lParam);
|
||||
tp->y = GET_Y_LPARAM(lParam);
|
||||
|
||||
// add touch move event
|
||||
TouchEvent te {
|
||||
.id = tp->id,
|
||||
.x = tp->x,
|
||||
.y = tp->y,
|
||||
.type = TOUCH_MOVE,
|
||||
.mouse = tp->mouse,
|
||||
};
|
||||
add_touch_event(&te);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case WM_LBUTTONUP: {
|
||||
|
||||
// check if mouse is enabled
|
||||
if (SPICETOUCH_ENABLE_MOUSE) {
|
||||
|
||||
// lock touch points
|
||||
std::lock_guard<std::mutex> lock_points(TOUCH_POINTS_M);
|
||||
std::lock_guard<std::mutex> lock_events(TOUCH_EVENTS_M);
|
||||
|
||||
// remove all points with ID 0
|
||||
for (size_t x = 0; x < TOUCH_POINTS.size(); x++) {
|
||||
TouchPoint *tp = &TOUCH_POINTS[x];
|
||||
|
||||
if (tp->id == 0u) {
|
||||
|
||||
// generate touch up event
|
||||
TouchEvent te {
|
||||
.id = tp->id,
|
||||
.x = tp->x,
|
||||
.y = tp->y,
|
||||
.type = TOUCH_UP,
|
||||
.mouse = tp->mouse,
|
||||
};
|
||||
add_touch_event(&te);
|
||||
|
||||
// remove touch point
|
||||
TOUCH_POINTS.erase(TOUCH_POINTS.begin() + x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
// call original function
|
||||
if (SPICETOUCH_CALL_OLD_PROC && SPICETOUCH_OLD_PROC != nullptr) {
|
||||
return SPICETOUCH_OLD_PROC(hWnd, msg, wParam, lParam);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// clean up
|
||||
if (SPICETOUCH_ATTACHED_DXHOOK) {
|
||||
return 0;
|
||||
} else {
|
||||
return DefWindowProc(hWnd, msg, wParam, lParam);
|
||||
}
|
||||
}
|
||||
|
||||
void touch_attach_wnd(HWND hWnd) {
|
||||
|
||||
// check if already attached
|
||||
if (SPICETOUCH_ATTACHED) {
|
||||
return;
|
||||
}
|
||||
SPICETOUCH_ATTACHED = true;
|
||||
|
||||
// initialize touch handler
|
||||
touch_initialize();
|
||||
touch_register_window(hWnd);
|
||||
|
||||
// hook window process
|
||||
SPICETOUCH_HWND = hWnd;
|
||||
SPICETOUCH_OLD_PROC = (WNDPROC) SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR) SpiceTouchWndProc);
|
||||
|
||||
// update rawinput
|
||||
rawinput::touch::display_update();
|
||||
}
|
||||
|
||||
void touch_attach_dx_hook() {
|
||||
|
||||
// check if already attached
|
||||
if (SPICETOUCH_ATTACHED) {
|
||||
return;
|
||||
}
|
||||
SPICETOUCH_ATTACHED = true;
|
||||
SPICETOUCH_ATTACHED_DXHOOK = true;
|
||||
|
||||
// initialize touch handler
|
||||
touch_initialize();
|
||||
|
||||
// add dx hook
|
||||
graphics_add_wnd_proc(SpiceTouchWndProc);
|
||||
|
||||
// update rawinput
|
||||
rawinput::touch::display_update();
|
||||
}
|
||||
|
||||
void touch_create_wnd(HWND hWnd, bool overlay) {
|
||||
|
||||
// check if already attached
|
||||
if (SPICETOUCH_ATTACHED) {
|
||||
return;
|
||||
}
|
||||
SPICETOUCH_ATTACHED = true;
|
||||
|
||||
// initialize touch handler
|
||||
touch_initialize();
|
||||
|
||||
// create thread
|
||||
SPICETOUCH_TOUCH_THREAD = new std::thread([hWnd, overlay]() {
|
||||
|
||||
// create class
|
||||
WNDCLASSEX wndClass {};
|
||||
wndClass.cbSize = sizeof(WNDCLASSEX);
|
||||
wndClass.style = 3;
|
||||
wndClass.lpfnWndProc = SpiceTouchWndProc;
|
||||
wndClass.cbClsExtra = 0;
|
||||
wndClass.cbWndExtra = 0;
|
||||
wndClass.hInstance = GetModuleHandle(nullptr);
|
||||
wndClass.hIcon = nullptr;
|
||||
wndClass.hCursor = LoadCursor(nullptr, IDC_ARROW);
|
||||
wndClass.hbrBackground = CreateSolidBrush(RGB(255, 192, 203));
|
||||
wndClass.lpszMenuName = nullptr;
|
||||
wndClass.lpszClassName = SPICETOUCH_CLASS_NAME;
|
||||
wndClass.hIconSm = nullptr;
|
||||
|
||||
// register class
|
||||
if (!RegisterClassExA(&wndClass)) {
|
||||
log_warning(LOG_MODULE_NAME, "failed to register SpiceTouch class: {}", GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
// calculate touch window dimensions
|
||||
update_spicetouch_window_dimensions(hWnd);
|
||||
|
||||
// create window
|
||||
HWND touch_window = CreateWindowExA(
|
||||
0,
|
||||
SPICETOUCH_CLASS_NAME,
|
||||
"SpiceTools Touch",
|
||||
(DWORD) CW_USEDEFAULT,
|
||||
SPICETOUCH_TOUCH_X,
|
||||
SPICETOUCH_TOUCH_Y,
|
||||
SPICETOUCH_TOUCH_WIDTH,
|
||||
SPICETOUCH_TOUCH_HEIGHT,
|
||||
hWnd,
|
||||
nullptr,
|
||||
GetModuleHandle(nullptr),
|
||||
nullptr
|
||||
);
|
||||
|
||||
log_misc(
|
||||
LOG_MODULE_NAME, "create SpiceTouch window ({}x{} @ {}, {})",
|
||||
SPICETOUCH_TOUCH_WIDTH, SPICETOUCH_TOUCH_HEIGHT, SPICETOUCH_TOUCH_X, SPICETOUCH_TOUCH_Y);
|
||||
|
||||
// check window
|
||||
if (touch_window == nullptr) {
|
||||
log_warning(LOG_MODULE_NAME, "failed to create SpiceTouch window: {}", GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
// save reference
|
||||
SPICETOUCH_TOUCH_HWND = touch_window;
|
||||
|
||||
// window settings
|
||||
ShowWindow(touch_window, SW_SHOWNOACTIVATE);
|
||||
UpdateWindow(touch_window);
|
||||
|
||||
// register
|
||||
touch_register_window(touch_window);
|
||||
|
||||
// overlay
|
||||
if (overlay && overlay::ENABLED) {
|
||||
if (overlay::OVERLAY) {
|
||||
log_warning(LOG_MODULE_NAME, "requested overlay, but already existing");
|
||||
} else {
|
||||
|
||||
// create instance
|
||||
overlay::OVERLAY.reset(new overlay::SpiceOverlay(touch_window));
|
||||
|
||||
// draw overlay in 30 FPS
|
||||
SetTimer(touch_window, 1, 1000 / 30, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
// window loop
|
||||
MSG msg {};
|
||||
while (GetMessage(&msg, nullptr, 0, 0) && SPICETOUCH_TOUCH_THREAD != nullptr) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
|
||||
// kill overlay
|
||||
if (overlay) {
|
||||
overlay::OVERLAY.reset();
|
||||
}
|
||||
|
||||
// unregister
|
||||
touch_unregister_window(touch_window);
|
||||
log_misc(LOG_MODULE_NAME, "window closed");
|
||||
});
|
||||
|
||||
// update rawinput
|
||||
rawinput::touch::display_update();
|
||||
}
|
||||
|
||||
void touch_detach() {
|
||||
|
||||
// remove window process hook
|
||||
if (SPICETOUCH_HWND != nullptr) {
|
||||
touch_unregister_window(SPICETOUCH_HWND);
|
||||
|
||||
SetWindowLongPtr(SPICETOUCH_HWND, GWLP_WNDPROC, (LONG_PTR) SPICETOUCH_OLD_PROC);
|
||||
SPICETOUCH_HWND = nullptr;
|
||||
SPICETOUCH_OLD_PROC = nullptr;
|
||||
}
|
||||
|
||||
// remove dx hook
|
||||
graphics_remove_wnd_proc(SpiceTouchWndProc);
|
||||
SPICETOUCH_TOUCH_THREAD = nullptr;
|
||||
SPICETOUCH_ATTACHED_DXHOOK = false;
|
||||
SPICETOUCH_ATTACHED = false;
|
||||
}
|
||||
|
||||
void touch_write_points(std::vector<TouchPoint> *touch_points) {
|
||||
|
||||
// check size first
|
||||
if (touch_points->empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// lock
|
||||
std::lock_guard<std::mutex> points(TOUCH_POINTS_M);
|
||||
std::lock_guard<std::mutex> events(TOUCH_EVENTS_M);
|
||||
|
||||
// iterate through all the provided touch points
|
||||
for (auto &tp : *touch_points) {
|
||||
|
||||
// find touch point to update
|
||||
bool found = false;
|
||||
for (auto &arr_tp : TOUCH_POINTS) {
|
||||
if (arr_tp.id == tp.id) {
|
||||
|
||||
// mark as found
|
||||
found = true;
|
||||
|
||||
// update position
|
||||
arr_tp.x = tp.x;
|
||||
arr_tp.y = tp.y;
|
||||
|
||||
// add touch move event
|
||||
TouchEvent te {
|
||||
.id = tp.id,
|
||||
.x = tp.x,
|
||||
.y = tp.y,
|
||||
.type = TOUCH_MOVE,
|
||||
.mouse = tp.mouse,
|
||||
};
|
||||
add_touch_event(&te);
|
||||
}
|
||||
}
|
||||
|
||||
// create new touch point when not found
|
||||
if (!found) {
|
||||
|
||||
// add touch point
|
||||
TOUCH_POINTS.push_back(tp);
|
||||
|
||||
// add touch down event
|
||||
TouchEvent te {
|
||||
.id = tp.id,
|
||||
.x = tp.x,
|
||||
.y = tp.y,
|
||||
.type = TOUCH_DOWN,
|
||||
.mouse = tp.mouse,
|
||||
};
|
||||
add_touch_event(&te);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void touch_remove_points(std::vector<DWORD> *touch_point_ids) {
|
||||
|
||||
// check size first
|
||||
if (touch_point_ids->empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// lock
|
||||
std::lock_guard<std::mutex> points(TOUCH_POINTS_M);
|
||||
std::lock_guard<std::mutex> events(TOUCH_EVENTS_M);
|
||||
|
||||
// find the touch points to remove
|
||||
for (auto id : *touch_point_ids) {
|
||||
for (size_t x = 0; x < TOUCH_POINTS.size(); x++) {
|
||||
TouchPoint *tp = &TOUCH_POINTS[x];
|
||||
|
||||
// check if the IDs match
|
||||
if (tp->id == id) {
|
||||
|
||||
// add touch up event
|
||||
TouchEvent te {
|
||||
.id = id,
|
||||
.x = tp->x,
|
||||
.y = tp->y,
|
||||
.type = TOUCH_UP,
|
||||
.mouse = tp->mouse,
|
||||
};
|
||||
add_touch_event(&te);
|
||||
|
||||
// delete touch point
|
||||
TOUCH_POINTS.erase(TOUCH_POINTS.begin() + x);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void touch_get_points(std::vector<TouchPoint> &touch_points, bool overlay) {
|
||||
|
||||
// update timeouts
|
||||
if (RI_MGR) {
|
||||
rawinput::touch::update_timeouts(RI_MGR.get());
|
||||
}
|
||||
|
||||
// overlay override
|
||||
if (!overlay &&
|
||||
overlay::OVERLAY &&
|
||||
overlay::OVERLAY->get_active() &&
|
||||
!overlay::OVERLAY->can_transform_touch_input() &&
|
||||
ImGui::GetIO().WantCaptureMouse) {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// lock
|
||||
std::lock_guard<std::mutex> lock(TOUCH_POINTS_M);
|
||||
|
||||
// append touch points
|
||||
touch_points.insert(touch_points.end(), TOUCH_POINTS.begin(), TOUCH_POINTS.end());
|
||||
}
|
||||
|
||||
void touch_get_events(std::vector<TouchEvent> &touch_events, bool overlay) {
|
||||
|
||||
// update timeouts
|
||||
if (RI_MGR) {
|
||||
rawinput::touch::update_timeouts(RI_MGR.get());
|
||||
}
|
||||
|
||||
// lock
|
||||
std::lock_guard<std::mutex> lock(TOUCH_EVENTS_M);
|
||||
|
||||
// overlay override
|
||||
if (!overlay &&
|
||||
overlay::OVERLAY &&
|
||||
overlay::OVERLAY->get_active() &&
|
||||
!overlay::OVERLAY->can_transform_touch_input() &&
|
||||
ImGui::GetIO().WantCaptureMouse) {
|
||||
|
||||
TOUCH_EVENTS.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
// append touch points
|
||||
while (!TOUCH_EVENTS.empty()) {
|
||||
touch_events.push_back(TOUCH_EVENTS.get());
|
||||
}
|
||||
}
|
||||
|
||||
void update_spicetouch_window_dimensions(HWND hWnd) {
|
||||
RECT clientRect {};
|
||||
GetClientRect(hWnd, &clientRect);
|
||||
|
||||
// adjust to client area
|
||||
POINT topleft {.x = 0, .y = 0};
|
||||
ClientToScreen(hWnd, &topleft);
|
||||
POINT bottomright {.x = clientRect.right, .y = clientRect.bottom};
|
||||
ClientToScreen(hWnd, &bottomright);
|
||||
|
||||
SPICETOUCH_TOUCH_X = topleft.x;
|
||||
SPICETOUCH_TOUCH_Y = topleft.y;
|
||||
SPICETOUCH_TOUCH_WIDTH = bottomright.x - topleft.x;
|
||||
SPICETOUCH_TOUCH_HEIGHT = bottomright.y - topleft.y;
|
||||
}
|
||||
43
touch/touch.h
Normal file
43
touch/touch.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <windows.h>
|
||||
|
||||
struct TouchPoint {
|
||||
DWORD id;
|
||||
LONG x, y;
|
||||
bool mouse;
|
||||
};
|
||||
enum TouchEventType {
|
||||
TOUCH_DOWN,
|
||||
TOUCH_MOVE,
|
||||
TOUCH_UP
|
||||
};
|
||||
struct TouchEvent {
|
||||
DWORD id;
|
||||
LONG x, y;
|
||||
TouchEventType type;
|
||||
bool mouse;
|
||||
};
|
||||
|
||||
extern bool SPICETOUCH_CARD_DISABLE;
|
||||
extern HWND SPICETOUCH_TOUCH_HWND;
|
||||
extern int SPICETOUCH_TOUCH_X;
|
||||
extern int SPICETOUCH_TOUCH_Y;
|
||||
extern int SPICETOUCH_TOUCH_WIDTH;
|
||||
extern int SPICETOUCH_TOUCH_HEIGHT;
|
||||
|
||||
bool is_touch_available();
|
||||
|
||||
void touch_attach_wnd(HWND hWnd);
|
||||
void touch_attach_dx_hook();
|
||||
void touch_create_wnd(HWND hWnd, bool overlay = false);
|
||||
void touch_detach();
|
||||
|
||||
void touch_write_points(std::vector<TouchPoint> *touch_points);
|
||||
void touch_remove_points(std::vector<DWORD> *touch_point_ids);
|
||||
|
||||
void touch_get_points(std::vector<TouchPoint> &touch_points, bool overlay = false);
|
||||
void touch_get_events(std::vector<TouchEvent> &touch_events, bool overlay = false);
|
||||
|
||||
void update_spicetouch_window_dimensions(HWND hWnd);
|
||||
57
touch/touch_indicators.cpp
Normal file
57
touch/touch_indicators.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
|
||||
// set version to Windows 8 to enable Windows 8 touch functions
|
||||
#define _WIN32_WINNT 0x0602
|
||||
|
||||
#include "touch_indicators.h"
|
||||
#include "util/libutils.h"
|
||||
#include "util/logging.h"
|
||||
|
||||
static HINSTANCE USER32_INSTANCE = nullptr;
|
||||
typedef BOOL (WINAPI *SetWindowFeedbackSetting_t)(HWND, FEEDBACK_TYPE, DWORD, UINT32, const VOID *);
|
||||
static SetWindowFeedbackSetting_t pSetWindowFeedbackSetting = nullptr;
|
||||
|
||||
void disable_touch_indicators(HWND hwnd) {
|
||||
|
||||
if (USER32_INSTANCE == nullptr) {
|
||||
USER32_INSTANCE = libutils::load_library("user32.dll");
|
||||
}
|
||||
if (USER32_INSTANCE == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pSetWindowFeedbackSetting == nullptr) {
|
||||
pSetWindowFeedbackSetting = libutils::try_proc<SetWindowFeedbackSetting_t>(
|
||||
USER32_INSTANCE, "SetWindowFeedbackSetting");
|
||||
}
|
||||
if (pSetWindowFeedbackSetting == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
log_info("touch_indicators", "disable visual feedback for touch events for HWND={}", fmt::ptr(hwnd));
|
||||
|
||||
BOOL enabled = FALSE;
|
||||
pSetWindowFeedbackSetting(
|
||||
hwnd,
|
||||
FEEDBACK_TOUCH_CONTACTVISUALIZATION,
|
||||
0, sizeof(enabled), &enabled);
|
||||
pSetWindowFeedbackSetting(
|
||||
hwnd,
|
||||
FEEDBACK_TOUCH_TAP,
|
||||
0, sizeof(enabled), &enabled);
|
||||
pSetWindowFeedbackSetting(
|
||||
hwnd,
|
||||
FEEDBACK_TOUCH_DOUBLETAP,
|
||||
0, sizeof(enabled), &enabled);
|
||||
pSetWindowFeedbackSetting(
|
||||
hwnd,
|
||||
FEEDBACK_TOUCH_PRESSANDHOLD,
|
||||
0, sizeof(enabled), &enabled);
|
||||
pSetWindowFeedbackSetting(
|
||||
hwnd,
|
||||
FEEDBACK_TOUCH_RIGHTTAP,
|
||||
0, sizeof(enabled), &enabled);
|
||||
pSetWindowFeedbackSetting(
|
||||
hwnd,
|
||||
FEEDBACK_GESTURE_PRESSANDTAP,
|
||||
0, sizeof(enabled), &enabled);
|
||||
}
|
||||
5
touch/touch_indicators.h
Normal file
5
touch/touch_indicators.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
void disable_touch_indicators(HWND hwnd);
|
||||
297
touch/win7.cpp
Normal file
297
touch/win7.cpp
Normal file
@@ -0,0 +1,297 @@
|
||||
|
||||
// enable touch functions - set version to windows 7
|
||||
// mingw otherwise doesn't load touch stuff
|
||||
#define _WIN32_WINNT 0x0601
|
||||
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <windowsx.h>
|
||||
|
||||
#include "win7.h"
|
||||
|
||||
#include "util/libutils.h"
|
||||
#include "util/logging.h"
|
||||
#include "rawinput/touch.h"
|
||||
|
||||
// mingw issue #2205 workaround
|
||||
// https://sourceforge.net/p/mingw/bugs/2205/
|
||||
#undef TOUCHEVENTF_MOVE
|
||||
#define TOUCHEVENTF_MOVE 0x0001
|
||||
#undef TOUCHEVENTF_DOWN
|
||||
#define TOUCHEVENTF_DOWN 0x0002
|
||||
|
||||
// mingw doesn't seem to have these
|
||||
#ifndef WM_TABLET_DEFBASE
|
||||
#define WM_TABLET_DEFBASE 0x02C0
|
||||
#define WM_TABLET_QUERYSYSTEMGESTURESTATUS (WM_TABLET_DEFBASE + 12)
|
||||
#define TABLET_DISABLE_PRESSANDHOLD 0x00000001
|
||||
#define TABLET_DISABLE_PENTAPFEEDBACK 0x00000008
|
||||
#define TABLET_DISABLE_PENBARRELFEEDBACK 0x00000010
|
||||
#define TABLET_DISABLE_TOUCHUIFORCEON 0x00000100
|
||||
#define TABLET_DISABLE_TOUCHUIFORCEOFF 0x00000200
|
||||
#define TABLET_DISABLE_TOUCHSWITCH 0x00008000
|
||||
#define TABLET_DISABLE_FLICKS 0x00010000
|
||||
#define TABLET_DISABLE_SMOOTHSCROLLING 0x00080000
|
||||
#define TABLET_DISABLE_FLICKFALLBACKKEYS 0x00100000
|
||||
#define TABLET_ENABLE_MULTITOUCHDATA 0x01000000
|
||||
#endif
|
||||
|
||||
// general states
|
||||
static HMODULE USER32_INSTANCE = nullptr;
|
||||
static const char *LOG_MODULE_NAME = "touch::win7";
|
||||
|
||||
/* dynamic touch functions
|
||||
* to maintain compatibility with windows XP
|
||||
*/
|
||||
static BOOL (WINAPI *pSetGestureConfig)(HWND, DWORD, UINT, PGESTURECONFIG, UINT);
|
||||
static BOOL (WINAPI *pRegisterTouchWindow)(HWND, ULONG);
|
||||
static BOOL (WINAPI *pGetTouchInputInfo)(HANDLE, UINT, PTOUCHINPUT, int);
|
||||
static BOOL (WINAPI *pCloseTouchInputHandle)(HANDLE);
|
||||
static BOOL (WINAPI *pUnregisterTouchWindow)(HWND hWnd);
|
||||
|
||||
// other
|
||||
static const char ATOM_NAME[] = "MicrosoftTabletPenServiceProperty";
|
||||
|
||||
Win7Handler::Win7Handler() : TouchHandler("win7") {
|
||||
|
||||
// check if already loaded
|
||||
static bool functions_loaded = false;
|
||||
if (functions_loaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
// load user32
|
||||
if (USER32_INSTANCE == nullptr) {
|
||||
USER32_INSTANCE = libutils::load_library("user32.dll");
|
||||
}
|
||||
|
||||
// load touch functions
|
||||
pSetGestureConfig = libutils::try_proc<decltype(pSetGestureConfig)>(
|
||||
USER32_INSTANCE, "SetGestureConfig");
|
||||
pRegisterTouchWindow = libutils::try_proc<decltype(pRegisterTouchWindow)>(
|
||||
USER32_INSTANCE, "RegisterTouchWindow");
|
||||
pGetTouchInputInfo = libutils::try_proc<decltype(pGetTouchInputInfo)>(
|
||||
USER32_INSTANCE, "GetTouchInputInfo");
|
||||
pCloseTouchInputHandle = libutils::try_proc<decltype(pCloseTouchInputHandle)>(
|
||||
USER32_INSTANCE, "CloseTouchInputHandle");
|
||||
pUnregisterTouchWindow = libutils::try_proc<decltype(pUnregisterTouchWindow)>(
|
||||
USER32_INSTANCE, "UnregisterTouchWindow");
|
||||
|
||||
functions_loaded = true;
|
||||
}
|
||||
|
||||
bool Win7Handler::is_available() {
|
||||
bool win_touch_available = (GetSystemMetrics(94) & 0x80) == 0x80;
|
||||
|
||||
if (win_touch_available) {
|
||||
log_info(LOG_MODULE_NAME, "WinTouch available");
|
||||
} else {
|
||||
log_info(LOG_MODULE_NAME, "WinTouch unavailable");
|
||||
}
|
||||
|
||||
return win_touch_available;
|
||||
}
|
||||
|
||||
bool Win7Handler::window_register(HWND hWnd) {
|
||||
|
||||
bool result = true;
|
||||
|
||||
// atom settings
|
||||
DWORD dwHwndTabletProperty = TABLET_DISABLE_PRESSANDHOLD |
|
||||
TABLET_DISABLE_PENTAPFEEDBACK |
|
||||
TABLET_DISABLE_PENBARRELFEEDBACK |
|
||||
TABLET_DISABLE_FLICKS;
|
||||
|
||||
// get atom ID
|
||||
ATOM atom_id = GlobalAddAtom(ATOM_NAME);
|
||||
|
||||
// disable gestures
|
||||
if (atom_id > 0) {
|
||||
SetProp(hWnd, ATOM_NAME, (HANDLE) ((unsigned long long) dwHwndTabletProperty));
|
||||
}
|
||||
|
||||
// register touch window
|
||||
if ((pRegisterTouchWindow == nullptr) || (pRegisterTouchWindow(hWnd, TWF_WANTPALM) == 0)) {
|
||||
log_warning(LOG_MODULE_NAME, "could not register touch window");
|
||||
|
||||
result = false;
|
||||
}
|
||||
|
||||
// set gesture config
|
||||
if (pSetGestureConfig != nullptr) {
|
||||
GESTURECONFIG gc {};
|
||||
gc.dwID = 0;
|
||||
gc.dwWant = 0;
|
||||
gc.dwBlock = 1;
|
||||
pSetGestureConfig(hWnd, 0, 1, &gc, sizeof(GESTURECONFIG));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Win7Handler::window_unregister(HWND hWnd) {
|
||||
|
||||
// dispose window
|
||||
if (pUnregisterTouchWindow != nullptr) {
|
||||
pUnregisterTouchWindow(hWnd);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Win7Handler::handle_message(msg_handler_result &result, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
||||
|
||||
// parse touch messages
|
||||
switch (msg) {
|
||||
case WM_TOUCH: {
|
||||
|
||||
// touch input fields
|
||||
UINT cInputs = LOWORD(wParam);
|
||||
auto pInputs = std::make_unique<TOUCHINPUT[]>(cInputs);
|
||||
|
||||
// get touch input
|
||||
if ((pGetTouchInputInfo != nullptr) &&
|
||||
(pGetTouchInputInfo((HANDLE) lParam, cInputs, pInputs.get(), sizeof(TOUCHINPUT)) != 0)) {
|
||||
|
||||
// lock touch points
|
||||
std::lock_guard<std::mutex> lock_points(TOUCH_POINTS_M);
|
||||
std::lock_guard<std::mutex> lock_events(TOUCH_EVENTS_M);
|
||||
|
||||
// iterate all inputs
|
||||
static long prev_x, prev_y;
|
||||
for (UINT i = 0; i < cInputs; i++) {
|
||||
auto &ti = pInputs[i];
|
||||
|
||||
// touch down
|
||||
if (ti.dwFlags & TOUCHEVENTF_DOWN) {
|
||||
|
||||
// convert to window position
|
||||
POINT point {};
|
||||
point.x = ti.x / 100;
|
||||
point.y = ti.y / 100;
|
||||
ScreenToClient(hWnd, &point);
|
||||
|
||||
// create new touch point
|
||||
TouchPoint tp {
|
||||
.id = ti.dwID,
|
||||
.x = point.x,
|
||||
.y = point.y,
|
||||
.mouse = false,
|
||||
};
|
||||
TOUCH_POINTS.push_back(tp);
|
||||
|
||||
// add touch down event
|
||||
TouchEvent te {
|
||||
.id = tp.id,
|
||||
.x = tp.x,
|
||||
.y = tp.y,
|
||||
.type = TOUCH_DOWN,
|
||||
.mouse = tp.mouse,
|
||||
};
|
||||
add_touch_event(&te);
|
||||
|
||||
// set prev coordinates
|
||||
prev_x = point.x;
|
||||
prev_y = point.y;
|
||||
|
||||
// card button
|
||||
update_card_button();
|
||||
}
|
||||
|
||||
// touch move
|
||||
if ((ti.dwFlags & TOUCHEVENTF_MOVE) != 0u) {
|
||||
|
||||
// convert to window position
|
||||
POINT point {};
|
||||
point.x = ti.x / 100;
|
||||
point.y = ti.y / 100;
|
||||
ScreenToClient(hWnd, &point);
|
||||
|
||||
// check prev coordinates
|
||||
if (point.x != prev_x || point.y != prev_y) {
|
||||
|
||||
// update point
|
||||
for (auto &tp : TOUCH_POINTS) {
|
||||
if (tp.id == ti.dwID) {
|
||||
|
||||
// update values
|
||||
tp.x = point.x;
|
||||
tp.y = point.y;
|
||||
|
||||
// add touch move event
|
||||
TouchEvent te {
|
||||
.id = tp.id,
|
||||
.x = tp.x,
|
||||
.y = tp.y,
|
||||
.type = TOUCH_MOVE,
|
||||
.mouse = tp.mouse,
|
||||
};
|
||||
add_touch_event(&te);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set prev coordinates
|
||||
prev_x = point.x;
|
||||
prev_y = point.y;
|
||||
}
|
||||
|
||||
// touch up
|
||||
if ((ti.dwFlags & TOUCHEVENTF_UP) != 0u) {
|
||||
|
||||
// remove point
|
||||
for (size_t x = 0; x < TOUCH_POINTS.size(); x++) {
|
||||
auto &tp = TOUCH_POINTS[x];
|
||||
|
||||
if (tp.id == ti.dwID) {
|
||||
|
||||
// add touch up event
|
||||
TouchEvent te {
|
||||
.id = tp.id,
|
||||
.x = tp.x,
|
||||
.y = tp.y,
|
||||
.type = TOUCH_UP,
|
||||
.mouse = tp.mouse,
|
||||
};
|
||||
add_touch_event(&te);
|
||||
|
||||
// remove from active touch points
|
||||
TOUCH_POINTS.erase(TOUCH_POINTS.begin() + x);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// clean up
|
||||
if (pCloseTouchInputHandle != nullptr) {
|
||||
pCloseTouchInputHandle((HANDLE) lParam);
|
||||
}
|
||||
|
||||
result.action = ACTION_RETURN_DEFAULT;
|
||||
|
||||
break;
|
||||
}
|
||||
case WM_TABLET_QUERYSYSTEMGESTURESTATUS: {
|
||||
result.action = ACTION_RETURN_STORED;
|
||||
result.return_value = TABLET_DISABLE_PRESSANDHOLD
|
||||
| TABLET_DISABLE_PENTAPFEEDBACK
|
||||
| TABLET_DISABLE_PENBARRELFEEDBACK
|
||||
| TABLET_DISABLE_TOUCHUIFORCEON
|
||||
| TABLET_DISABLE_TOUCHUIFORCEOFF
|
||||
| TABLET_DISABLE_TOUCHSWITCH
|
||||
| TABLET_DISABLE_FLICKS
|
||||
| TABLET_DISABLE_SMOOTHSCROLLING
|
||||
| TABLET_DISABLE_FLICKFALLBACKKEYS
|
||||
| TABLET_ENABLE_MULTITOUCHDATA;
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
18
touch/win7.h
Normal file
18
touch/win7.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <windows.h>
|
||||
|
||||
#include "handler.h"
|
||||
|
||||
class Win7Handler : public TouchHandler {
|
||||
public:
|
||||
|
||||
Win7Handler();
|
||||
|
||||
static bool is_available();
|
||||
|
||||
virtual bool window_register(HWND hWnd) override;
|
||||
virtual bool window_unregister(HWND hWnd) override;
|
||||
virtual void handle_message(msg_handler_result &result, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) override;
|
||||
};
|
||||
316
touch/win8.cpp
Normal file
316
touch/win8.cpp
Normal file
@@ -0,0 +1,316 @@
|
||||
|
||||
// set version to Windows 8 to enable Windows 8 touch functions
|
||||
#define _WIN32_WINNT 0x0602
|
||||
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <initguid.h>
|
||||
#include <windowsx.h>
|
||||
#include <propsys.h>
|
||||
|
||||
#include "win8.h"
|
||||
|
||||
#include "util/libutils.h"
|
||||
#include "util/logging.h"
|
||||
#include "rawinput/touch.h"
|
||||
|
||||
// mingw does not seem to have this either
|
||||
DEFINE_GUID(PKEY_EdgeGesture_DisableTouchWhenFullscreen_Format, 0x32CE38B2, 0x2C9A, 0x41B1, 0x9B, 0xC5, 0xB3, 0x78, 0x43, 0x94, 0xAA, 0x44);
|
||||
|
||||
// mingw issue #2205 workaround
|
||||
// https://sourceforge.net/p/mingw/bugs/2205/
|
||||
#undef TOUCHEVENTF_MOVE
|
||||
#define TOUCHEVENTF_MOVE 0x0001
|
||||
#undef TOUCHEVENTF_DOWN
|
||||
#define TOUCHEVENTF_DOWN 0x0002
|
||||
|
||||
// mingw doesn't seem to have these
|
||||
#ifndef WM_TABLET_DEFBASE
|
||||
#define WM_TABLET_DEFBASE 0x02C0
|
||||
#define WM_TABLET_QUERYSYSTEMGESTURESTATUS (WM_TABLET_DEFBASE + 12)
|
||||
#define TABLET_DISABLE_PRESSANDHOLD 0x00000001
|
||||
#define TABLET_DISABLE_PENTAPFEEDBACK 0x00000008
|
||||
#define TABLET_DISABLE_PENBARRELFEEDBACK 0x00000010
|
||||
#define TABLET_DISABLE_TOUCHUIFORCEON 0x00000100
|
||||
#define TABLET_DISABLE_TOUCHUIFORCEOFF 0x00000200
|
||||
#define TABLET_DISABLE_TOUCHSWITCH 0x00008000
|
||||
#define TABLET_DISABLE_FLICKS 0x00010000
|
||||
#define TABLET_DISABLE_SMOOTHSCROLLING 0x00080000
|
||||
#define TABLET_DISABLE_FLICKFALLBACKKEYS 0x00100000
|
||||
#define TABLET_ENABLE_MULTITOUCHDATA 0x01000000
|
||||
#endif
|
||||
|
||||
// general states
|
||||
static HINSTANCE USER32_INSTANCE = nullptr;
|
||||
static HINSTANCE SHELL32_INSTANCE = nullptr;
|
||||
const char *LOG_MODULE_NAME = "touch::win8";
|
||||
|
||||
/*
|
||||
* dynamic touch functions
|
||||
* to maintain compatibility with windows XP
|
||||
*/
|
||||
typedef BOOL (WINAPI *GetPointerFrameInfoHistory_t)(UINT32, UINT32 *, UINT32 *, POINTER_INFO *);
|
||||
typedef BOOL (WINAPI *SkipPointerFrameMessages_t)(UINT32);
|
||||
typedef HRESULT (WINAPI *SHGetPropertyStoreForWindow_t)(HWND, REFIID, void **);
|
||||
static GetPointerFrameInfoHistory_t pGetPointerFrameInfoHistory = nullptr;
|
||||
static SkipPointerFrameMessages_t pSkipPointerFrameMessages = nullptr;
|
||||
static SHGetPropertyStoreForWindow_t pSHGetPropertyStoreForWindow = nullptr;
|
||||
|
||||
// other
|
||||
static const char ATOM_NAME[] = "MicrosoftTabletPenServiceProperty";
|
||||
|
||||
static void load_functions() {
|
||||
|
||||
// check if already loaded
|
||||
static bool functions_loaded = false;
|
||||
if (functions_loaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
// load libraries
|
||||
if (USER32_INSTANCE == nullptr) {
|
||||
USER32_INSTANCE = libutils::load_library("user32.dll");
|
||||
}
|
||||
if (SHELL32_INSTANCE == nullptr) {
|
||||
SHELL32_INSTANCE = libutils::try_library("shell32.dll");
|
||||
}
|
||||
|
||||
// load touch functions
|
||||
pGetPointerFrameInfoHistory = libutils::try_proc<GetPointerFrameInfoHistory_t>(
|
||||
USER32_INSTANCE, "GetPointerFrameInfoHistory");
|
||||
pSkipPointerFrameMessages = libutils::try_proc<SkipPointerFrameMessages_t>(
|
||||
USER32_INSTANCE, "SkipPointerFrameMessages");
|
||||
|
||||
if (SHELL32_INSTANCE != nullptr) {
|
||||
pSHGetPropertyStoreForWindow = libutils::try_proc<SHGetPropertyStoreForWindow_t>(
|
||||
SHELL32_INSTANCE, "SHGetPropertyStoreForWindow");
|
||||
}
|
||||
|
||||
functions_loaded = true;
|
||||
}
|
||||
|
||||
Win8Handler::Win8Handler() : TouchHandler("win8") {
|
||||
load_functions();
|
||||
}
|
||||
|
||||
bool Win8Handler::is_available() {
|
||||
bool have_digitizers = (GetSystemMetrics(94) & 0x80) == 0x80;
|
||||
|
||||
load_functions();
|
||||
|
||||
if (pGetPointerFrameInfoHistory != nullptr) {
|
||||
if (have_digitizers) {
|
||||
log_info(LOG_MODULE_NAME, "Pointer API available");
|
||||
} else {
|
||||
log_info(LOG_MODULE_NAME, "Pointer API available, but no touch screens active");
|
||||
}
|
||||
} else {
|
||||
log_info(LOG_MODULE_NAME, "Pointer API unavailable");
|
||||
}
|
||||
|
||||
return have_digitizers && pGetPointerFrameInfoHistory != nullptr;
|
||||
}
|
||||
|
||||
bool Win8Handler::window_register(HWND hWnd) {
|
||||
|
||||
// atom settings
|
||||
DWORD dwHwndTabletProperty = TABLET_DISABLE_PRESSANDHOLD |
|
||||
TABLET_DISABLE_PENTAPFEEDBACK |
|
||||
TABLET_DISABLE_PENBARRELFEEDBACK |
|
||||
TABLET_DISABLE_FLICKS;
|
||||
|
||||
// get atom ID
|
||||
ATOM atomID = GlobalAddAtom(ATOM_NAME);
|
||||
|
||||
// disable gestures
|
||||
if (atomID > 0) {
|
||||
SetProp(hWnd, ATOM_NAME, (HANDLE) ((unsigned long long) dwHwndTabletProperty));
|
||||
}
|
||||
|
||||
IPropertyStore *ps = nullptr;
|
||||
HRESULT hr = pSHGetPropertyStoreForWindow(hWnd, IID_IPropertyStore, (void **) &ps);
|
||||
|
||||
if (SUCCEEDED(hr) && ps != nullptr) {
|
||||
PROPERTYKEY PKEY_EdgeGesture_DisableTouchWhenFullscreen = {
|
||||
PKEY_EdgeGesture_DisableTouchWhenFullscreen_Format,
|
||||
2,
|
||||
};
|
||||
|
||||
PROPVARIANT var {};
|
||||
var.vt = VT_BOOL;
|
||||
var.boolVal = VARIANT_TRUE;
|
||||
|
||||
hr = ps->SetValue(PKEY_EdgeGesture_DisableTouchWhenFullscreen, var);
|
||||
|
||||
ps->Release();
|
||||
|
||||
if (FAILED(hr)) {
|
||||
log_warning(LOG_MODULE_NAME, "Failed to disable edge gestures on window");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Win8Handler::window_unregister(HWND hWnd) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void Win8Handler::handle_message(msg_handler_result &result, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
||||
|
||||
// parse touch messages
|
||||
switch (msg) {
|
||||
case WM_POINTERACTIVATE: {
|
||||
result.action = ACTION_RETURN_STORED;
|
||||
result.return_value = PA_ACTIVATE;
|
||||
|
||||
break;
|
||||
}
|
||||
case WM_NCPOINTERUPDATE:
|
||||
case WM_NCPOINTERDOWN:
|
||||
case WM_NCPOINTERUP:
|
||||
case WM_POINTERUPDATE:
|
||||
case WM_POINTERDOWN:
|
||||
case WM_POINTERUP: {
|
||||
|
||||
UINT entries_count = 0, pointer_count = 0;
|
||||
|
||||
if (pGetPointerFrameInfoHistory == nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!pGetPointerFrameInfoHistory(GET_POINTERID_WPARAM(wParam), &entries_count, &pointer_count, NULL)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (entries_count * pointer_count == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
auto pointer_info = std::make_unique<POINTER_INFO[]>(entries_count * pointer_count);
|
||||
|
||||
if (!pGetPointerFrameInfoHistory(GET_POINTERID_WPARAM(wParam), &entries_count, &pointer_count, pointer_info.get())) {
|
||||
break;
|
||||
}
|
||||
|
||||
// lock touch points
|
||||
std::lock_guard<std::mutex> lock_points(TOUCH_POINTS_M);
|
||||
std::lock_guard<std::mutex> lock_events(TOUCH_EVENTS_M);
|
||||
|
||||
// iterate all inputs
|
||||
static long prev_x, prev_y;
|
||||
for (size_t i = 0; i < entries_count * pointer_count; i++) {
|
||||
auto &pi = pointer_info[i];
|
||||
|
||||
if (pi.pointerFlags & POINTER_FLAG_DOWN) {
|
||||
|
||||
// convert to window position
|
||||
POINT point = pi.ptPixelLocation;
|
||||
ScreenToClient(hWnd, &point);
|
||||
|
||||
// create new touch point
|
||||
TouchPoint tp {
|
||||
.id = pi.pointerId,
|
||||
.x = point.x,
|
||||
.y = point.y,
|
||||
.mouse = false,
|
||||
};
|
||||
TOUCH_POINTS.push_back(tp);
|
||||
|
||||
// add touch down event
|
||||
TouchEvent te {
|
||||
.id = tp.id,
|
||||
.x = tp.x,
|
||||
.y = tp.y,
|
||||
.type = TOUCH_DOWN,
|
||||
.mouse = tp.mouse,
|
||||
};
|
||||
add_touch_event(&te);
|
||||
|
||||
// set prev coordinates
|
||||
prev_x = point.x;
|
||||
prev_y = point.y;
|
||||
|
||||
}
|
||||
|
||||
if ((pi.pointerFlags & POINTER_FLAG_UPDATE) != 0) {
|
||||
|
||||
// convert to window position
|
||||
POINT point = pi.ptPixelLocation;
|
||||
ScreenToClient(hWnd, &point);
|
||||
|
||||
// check prev coordinates
|
||||
if (point.x != prev_x || point.y != prev_y) {
|
||||
|
||||
// update point
|
||||
for (auto &tp : TOUCH_POINTS) {
|
||||
if (tp.id == pi.pointerId) {
|
||||
|
||||
// update values
|
||||
tp.x = point.x;
|
||||
tp.y = point.y;
|
||||
|
||||
// add touch move event
|
||||
TouchEvent te {
|
||||
.id = tp.id,
|
||||
.x = tp.x,
|
||||
.y = tp.y,
|
||||
.type = TOUCH_MOVE,
|
||||
.mouse = tp.mouse,
|
||||
};
|
||||
add_touch_event(&te);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set prev coordinates
|
||||
prev_x = point.x;
|
||||
prev_y = point.y;
|
||||
|
||||
}
|
||||
|
||||
if ((pi.pointerFlags & POINTER_FLAG_UP) != 0) {
|
||||
|
||||
// remove point
|
||||
for (size_t x = 0; x < TOUCH_POINTS.size(); x++) {
|
||||
auto &tp = TOUCH_POINTS[x];
|
||||
|
||||
if (tp.id == pi.pointerId) {
|
||||
|
||||
// add touch up event
|
||||
TouchEvent te {
|
||||
.id = pi.pointerId,
|
||||
.x = tp.x,
|
||||
.y = tp.y,
|
||||
.type = TOUCH_UP,
|
||||
.mouse = tp.mouse,
|
||||
};
|
||||
add_touch_event(&te);
|
||||
|
||||
// remove from active touch points
|
||||
TOUCH_POINTS.erase(TOUCH_POINTS.begin() + x);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pSkipPointerFrameMessages != nullptr) {
|
||||
pSkipPointerFrameMessages(GET_POINTERID_WPARAM(wParam));
|
||||
}
|
||||
|
||||
// card button
|
||||
update_card_button();
|
||||
|
||||
result.action = ACTION_RETURN_STORED;
|
||||
result.return_value = 0;
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
18
touch/win8.h
Normal file
18
touch/win8.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <windows.h>
|
||||
|
||||
#include "handler.h"
|
||||
|
||||
class Win8Handler : public TouchHandler {
|
||||
public:
|
||||
|
||||
Win8Handler();
|
||||
|
||||
static bool is_available();
|
||||
|
||||
virtual bool window_register(HWND hWnd) override;
|
||||
virtual bool window_unregister(HWND hWnd) override;
|
||||
virtual void handle_message(msg_handler_result &result, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) override;
|
||||
};
|
||||
Reference in New Issue
Block a user