From f67562172598efda6944950fb53c098b30937e9e Mon Sep 17 00:00:00 2001 From: bruvzg <7645683+bruvzg@users.noreply.github.com> Date: Thu, 13 Dec 2018 22:32:11 +0200 Subject: [PATCH] [macOS, Windows, X11] Add graphic tablet pen pressure and tilt support to InputEventMouseMotion event. --- core/os/input_event.cpp | 34 ++++++++ core/os/input_event.h | 9 ++ doc/classes/InputEventMouseMotion.xml | 8 +- platform/osx/os_osx.mm | 5 ++ platform/windows/os_windows.cpp | 121 ++++++++++++++++++++++++++ platform/windows/os_windows.h | 68 +++++++++++++++ platform/x11/os_x11.cpp | 75 +++++++++++++++- platform/x11/os_x11.h | 3 + 8 files changed, 318 insertions(+), 5 deletions(-) diff --git a/core/os/input_event.cpp b/core/os/input_event.cpp index 30fca0c155aa..381ba9d0109b 100644 --- a/core/os/input_event.cpp +++ b/core/os/input_event.cpp @@ -557,10 +557,31 @@ InputEventMouseButton::InputEventMouseButton() { //////////////////////////////////////////// +void InputEventMouseMotion::set_tilt(const Vector2 &p_tilt) { + + tilt = p_tilt; +} + +Vector2 InputEventMouseMotion::get_tilt() const { + + return tilt; +} + +void InputEventMouseMotion::set_pressure(float p_pressure) { + + pressure = p_pressure; +} + +float InputEventMouseMotion::get_pressure() const { + + return pressure; +} + void InputEventMouseMotion::set_relative(const Vector2 &p_relative) { relative = p_relative; } + Vector2 InputEventMouseMotion::get_relative() const { return relative; @@ -570,6 +591,7 @@ void InputEventMouseMotion::set_speed(const Vector2 &p_speed) { speed = p_speed; } + Vector2 InputEventMouseMotion::get_speed() const { return speed; @@ -590,6 +612,8 @@ Ref InputEventMouseMotion::xformed_by(const Transform2D &p_xform, co mm->set_modifiers_from_event(this); mm->set_position(l); + mm->set_pressure(get_pressure()); + mm->set_tilt(get_tilt()); mm->set_global_position(g); mm->set_button_mask(get_button_mask()); @@ -665,17 +689,27 @@ bool InputEventMouseMotion::accumulate(const Ref &p_event) { void InputEventMouseMotion::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_tilt", "tilt"), &InputEventMouseMotion::set_tilt); + ClassDB::bind_method(D_METHOD("get_tilt"), &InputEventMouseMotion::get_tilt); + + ClassDB::bind_method(D_METHOD("set_pressure", "pressure"), &InputEventMouseMotion::set_pressure); + ClassDB::bind_method(D_METHOD("get_pressure"), &InputEventMouseMotion::get_pressure); + ClassDB::bind_method(D_METHOD("set_relative", "relative"), &InputEventMouseMotion::set_relative); ClassDB::bind_method(D_METHOD("get_relative"), &InputEventMouseMotion::get_relative); ClassDB::bind_method(D_METHOD("set_speed", "speed"), &InputEventMouseMotion::set_speed); ClassDB::bind_method(D_METHOD("get_speed"), &InputEventMouseMotion::get_speed); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "tilt"), "set_tilt", "get_tilt"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "pressure"), "set_pressure", "get_pressure"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "relative"), "set_relative", "get_relative"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "speed"), "set_speed", "get_speed"); } InputEventMouseMotion::InputEventMouseMotion() { + + pressure = 0; } //////////////////////////////////////// diff --git a/core/os/input_event.h b/core/os/input_event.h index 28658e38651f..14649502eeb8 100644 --- a/core/os/input_event.h +++ b/core/os/input_event.h @@ -351,6 +351,9 @@ public: class InputEventMouseMotion : public InputEventMouse { GDCLASS(InputEventMouseMotion, InputEventMouse); + + Vector2 tilt; + float pressure; Vector2 relative; Vector2 speed; @@ -358,6 +361,12 @@ protected: static void _bind_methods(); public: + void set_tilt(const Vector2 &p_tilt); + Vector2 get_tilt() const; + + void set_pressure(float p_pressure); + float get_pressure() const; + void set_relative(const Vector2 &p_relative); Vector2 get_relative() const; diff --git a/doc/classes/InputEventMouseMotion.xml b/doc/classes/InputEventMouseMotion.xml index fab4cc0fb966..cb89a746bfc9 100644 --- a/doc/classes/InputEventMouseMotion.xml +++ b/doc/classes/InputEventMouseMotion.xml @@ -4,7 +4,7 @@ Input event type for mouse motion events. - Contains mouse motion information. Supports relative, absolute positions and speed. See [method Node._input]. + Contains mouse and pen motion information. Supports relative, absolute positions and speed. See [method Node._input]. https://docs.godotengine.org/en/latest/tutorials/inputs/mouse_and_input_coordinates.html @@ -18,6 +18,12 @@ The mouse speed in pixels per second. + + Represents the pressure the user puts on the pen. Ranges from [code]0.0[/code] to [code]1.0[/code]. + + + Represents the angles of tilt of the pen. Positive X-coordinate value indicates a tilt to the right. Positive Y-coordinate value indicates a tilt toward the user. Ranges from [code]-1.0[/code] to [code]1.0[/code] for both axes. + diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm index 15885e1266c9..ba3ac9862e4f 100644 --- a/platform/osx/os_osx.mm +++ b/platform/osx/os_osx.mm @@ -709,6 +709,11 @@ static void _mouseDownEvent(NSEvent *event, int index, int mask, bool pressed) { const CGFloat backingScaleFactor = [[event window] backingScaleFactor]; const Vector2 pos = get_mouse_pos([event locationInWindow], backingScaleFactor); mm->set_position(pos); + mm->set_pressure([event pressure]); + if ([event subtype] == NSTabletPointEventSubtype) { + const NSPoint p = [event tilt]; + mm->set_tilt(Vector2(p.x, p.y)); + } mm->set_global_position(pos); mm->set_speed(OS_OSX::singleton->input->get_last_mouse_speed()); Vector2 relativeMotion = Vector2(); diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 55d667c10102..c4cd8e068c02 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -70,6 +70,10 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; #define WM_TOUCH 576 #endif +#ifndef WM_POINTERUPDATE +#define WM_POINTERUPDATE 0x0245 +#endif + typedef struct { int count; int screen; @@ -192,6 +196,9 @@ BOOL WINAPI HandlerRoutine(_In_ DWORD dwCtrlType) { } } +GetPointerTypePtr OS_Windows::win8p_GetPointerType = NULL; +GetPointerPenInfoPtr OS_Windows::win8p_GetPointerPenInfo = NULL; + void OS_Windows::initialize_debugging() { SetConsoleCtrlHandler(HandlerRoutine, TRUE); @@ -481,6 +488,113 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) } delete[] lpb; } break; + case WM_POINTERUPDATE: { + if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) { + break; + } + + if (!win8p_GetPointerType || !win8p_GetPointerPenInfo) { + break; + } + + uint32_t pointer_id = LOWORD(wParam); + POINTER_INPUT_TYPE pointer_type = PT_POINTER; + if (!win8p_GetPointerType(pointer_id, &pointer_type)) { + break; + } + + if (pointer_type != PT_PEN) { + break; + } + + POINTER_PEN_INFO pen_info; + if (!win8p_GetPointerPenInfo(pointer_id, &pen_info)) { + break; + } + + if (input->is_emulating_mouse_from_touch()) { + // Universal translation enabled; ignore OS translation + LPARAM extra = GetMessageExtraInfo(); + if (IsTouchEvent(extra)) { + break; + } + } + + if (outside) { + //mouse enter + + if (main_loop && mouse_mode != MOUSE_MODE_CAPTURED) + main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_ENTER); + + CursorShape c = cursor_shape; + cursor_shape = CURSOR_MAX; + set_cursor_shape(c); + outside = false; + + //Once-Off notification, must call again.... + TRACKMOUSEEVENT tme; + tme.cbSize = sizeof(TRACKMOUSEEVENT); + tme.dwFlags = TME_LEAVE; + tme.hwndTrack = hWnd; + tme.dwHoverTime = HOVER_DEFAULT; + TrackMouseEvent(&tme); + } + + // Don't calculate relative mouse movement if we don't have focus in CAPTURED mode. + if (!window_has_focus && mouse_mode == MOUSE_MODE_CAPTURED) + break; + + Ref mm; + mm.instance(); + + mm->set_pressure(pen_info.pressure ? (float)pen_info.pressure / 1024 : 0); + mm->set_tilt(Vector2(pen_info.tiltX ? (float)pen_info.tiltX / 90 : 0, pen_info.tiltY ? (float)pen_info.tiltY / 90 : 0)); + + mm->set_control((wParam & MK_CONTROL) != 0); + mm->set_shift((wParam & MK_SHIFT) != 0); + mm->set_alt(alt_mem); + + mm->set_button_mask(last_button_state); + + mm->set_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); + mm->set_global_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); + + if (mouse_mode == MOUSE_MODE_CAPTURED) { + + Point2i c(video_mode.width / 2, video_mode.height / 2); + old_x = c.x; + old_y = c.y; + + if (mm->get_position() == c) { + center = c; + return 0; + } + + Point2i ncenter = mm->get_position(); + center = ncenter; + POINT pos = { (int)c.x, (int)c.y }; + ClientToScreen(hWnd, &pos); + SetCursorPos(pos.x, pos.y); + } + + input->set_mouse_position(mm->get_position()); + mm->set_speed(input->get_last_mouse_speed()); + + if (old_invalid) { + + old_x = mm->get_position().x; + old_y = mm->get_position().y; + old_invalid = false; + } + + mm->set_relative(Vector2(mm->get_position() - Vector2(old_x, old_y))); + old_x = mm->get_position().x; + old_y = mm->get_position().y; + if (window_has_focus && main_loop) + input->parse_input_event(mm); + + return 0; //Pointer event handled return 0 to avoid duplicate WM_MOUSEMOVE event + } break; case WM_MOUSEMOVE: { if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) { break; @@ -3256,6 +3370,13 @@ OS_Windows::OS_Windows(HINSTANCE _hInstance) { was_maximized = false; console_visible = IsWindowVisible(GetConsoleWindow()); + //Note: Functions for pen input, available on Windows 8+ + HMODULE user32_lib = LoadLibraryW(L"user32.dll"); + if (user32_lib) { + win8p_GetPointerType = (GetPointerTypePtr)GetProcAddress(user32_lib, "GetPointerType"); + win8p_GetPointerPenInfo = (GetPointerPenInfoPtr)GetProcAddress(user32_lib, "GetPointerPenInfo"); + } + hInstance = _hInstance; pressrc = 0; old_invalid = true; diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index b0e665e57459..ce279fb03321 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -56,6 +56,71 @@ #include #include +#ifndef POINTER_STRUCTURES + +#define POINTER_STRUCTURES + +typedef DWORD POINTER_INPUT_TYPE; +typedef UINT32 POINTER_FLAGS; +typedef UINT32 PEN_FLAGS; +typedef UINT32 PEN_MASK; + +enum tagPOINTER_INPUT_TYPE { + PT_POINTER = 0x00000001, + PT_TOUCH = 0x00000002, + PT_PEN = 0x00000003, + PT_MOUSE = 0x00000004, + PT_TOUCHPAD = 0x00000005 +}; + +typedef enum tagPOINTER_BUTTON_CHANGE_TYPE { + POINTER_CHANGE_NONE, + POINTER_CHANGE_FIRSTBUTTON_DOWN, + POINTER_CHANGE_FIRSTBUTTON_UP, + POINTER_CHANGE_SECONDBUTTON_DOWN, + POINTER_CHANGE_SECONDBUTTON_UP, + POINTER_CHANGE_THIRDBUTTON_DOWN, + POINTER_CHANGE_THIRDBUTTON_UP, + POINTER_CHANGE_FOURTHBUTTON_DOWN, + POINTER_CHANGE_FOURTHBUTTON_UP, + POINTER_CHANGE_FIFTHBUTTON_DOWN, + POINTER_CHANGE_FIFTHBUTTON_UP, +} POINTER_BUTTON_CHANGE_TYPE; + +typedef struct tagPOINTER_INFO { + POINTER_INPUT_TYPE pointerType; + UINT32 pointerId; + UINT32 frameId; + POINTER_FLAGS pointerFlags; + HANDLE sourceDevice; + HWND hwndTarget; + POINT ptPixelLocation; + POINT ptHimetricLocation; + POINT ptPixelLocationRaw; + POINT ptHimetricLocationRaw; + DWORD dwTime; + UINT32 historyCount; + INT32 InputData; + DWORD dwKeyStates; + UINT64 PerformanceCount; + POINTER_BUTTON_CHANGE_TYPE ButtonChangeType; +} POINTER_INFO; + +typedef struct tagPOINTER_PEN_INFO { + POINTER_INFO pointerInfo; + PEN_FLAGS penFlags; + PEN_MASK penMask; + UINT32 pressure; + UINT32 rotation; + INT32 tiltX; + INT32 tiltY; +} POINTER_PEN_INFO; + +#endif + +typedef BOOL(WINAPI *GetPointerTypePtr)(uint32_t p_id, POINTER_INPUT_TYPE *p_type); +typedef BOOL(WINAPI *GetPointerPenInfoPtr)(uint32_t p_id, POINTER_PEN_INFO *p_pen_info); + typedef struct { BYTE bWidth; // Width, in pixels, of the image BYTE bHeight; // Height, in pixels, of the image @@ -77,6 +142,9 @@ typedef struct { class JoypadWindows; class OS_Windows : public OS { + static GetPointerTypePtr win8p_GetPointerType; + static GetPointerPenInfoPtr win8p_GetPointerPenInfo; + enum { KEY_EVENT_BUFFER_SIZE = 512 }; diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index 694aea74624d..c695d657e732 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -83,6 +83,12 @@ #define XINPUT_CLIENT_VERSION_MAJOR 2 #define XINPUT_CLIENT_VERSION_MINOR 2 +#define VALUATOR_ABSX 0 +#define VALUATOR_ABSY 1 +#define VALUATOR_PRESSURE 2 +#define VALUATOR_TILTX 3 +#define VALUATOR_TILTY 4 + static const double abs_resolution_mult = 10000.0; static const double abs_resolution_range_mult = 10.0; @@ -665,6 +671,15 @@ bool OS_X11::refresh_device_info() { int range_min_y = 0; int range_max_x = 0; int range_max_y = 0; + int pressure_resolution = 0; + int pressure_min = 0; + int pressure_max = 0; + int tilt_resolution_x = 0; + int tilt_resolution_y = 0; + int tilt_range_min_x = 0; + int tilt_range_min_y = 0; + int tilt_range_max_x = 0; + int tilt_range_max_y = 0; for (int j = 0; j < dev->num_classes; j++) { #ifdef TOUCH_ENABLED if (dev->classes[j]->type == XITouchClass && ((XITouchClassInfo *)dev->classes[j])->mode == XIDirectTouch) { @@ -674,16 +689,28 @@ bool OS_X11::refresh_device_info() { if (dev->classes[j]->type == XIValuatorClass) { XIValuatorClassInfo *class_info = (XIValuatorClassInfo *)dev->classes[j]; - if (class_info->number == 0 && class_info->mode == XIModeAbsolute) { + if (class_info->number == VALUATOR_ABSX && class_info->mode == XIModeAbsolute) { resolution_x = class_info->resolution; range_min_x = class_info->min; range_max_x = class_info->max; absolute_mode = true; - } else if (class_info->number == 1 && class_info->mode == XIModeAbsolute) { + } else if (class_info->number == VALUATOR_ABSY && class_info->mode == XIModeAbsolute) { resolution_y = class_info->resolution; range_min_y = class_info->min; range_max_y = class_info->max; absolute_mode = true; + } else if (class_info->number == VALUATOR_PRESSURE && class_info->mode == XIModeAbsolute) { + pressure_resolution = class_info->resolution; + pressure_min = class_info->min; + pressure_max = class_info->max; + } else if (class_info->number == VALUATOR_TILTX && class_info->mode == XIModeAbsolute) { + tilt_resolution_x = class_info->resolution; + tilt_range_min_x = class_info->min; + tilt_range_max_x = class_info->max; + } else if (class_info->number == VALUATOR_TILTY && class_info->mode == XIModeAbsolute) { + tilt_resolution_y = class_info->resolution; + tilt_range_min_y = class_info->min; + tilt_range_max_y = class_info->max; } } } @@ -703,6 +730,18 @@ bool OS_X11::refresh_device_info() { xi.absolute_devices[dev->deviceid] = Vector2(abs_resolution_mult / resolution_x, abs_resolution_mult / resolution_y); print_verbose("XInput: Absolute pointing device: " + String(dev->name)); } + + if (pressure_resolution <= 0) { + pressure_resolution = (pressure_max - pressure_min); + } + if (tilt_resolution_x <= 0) { + tilt_resolution_x = (tilt_range_max_x - tilt_range_min_x); + } + if (tilt_resolution_y <= 0) { + tilt_resolution_y = (tilt_range_max_y - tilt_range_min_y); + } + xi.pressure = 0; + xi.pen_devices[dev->deviceid] = Vector3(pressure_resolution, tilt_resolution_x, tilt_resolution_y); } XIFreeDeviceInfo(info); @@ -2095,14 +2134,39 @@ void OS_X11::process_xevents() { double rel_x = 0.0; double rel_y = 0.0; + double pressure = 0.0; + double tilt_x = 0.0; + double tilt_y = 0.0; - if (XIMaskIsSet(raw_event->valuators.mask, 0)) { + if (XIMaskIsSet(raw_event->valuators.mask, VALUATOR_ABSX)) { rel_x = *values; values++; } - if (XIMaskIsSet(raw_event->valuators.mask, 1)) { + if (XIMaskIsSet(raw_event->valuators.mask, VALUATOR_ABSY)) { rel_y = *values; + values++; + } + + if (XIMaskIsSet(raw_event->valuators.mask, VALUATOR_PRESSURE)) { + pressure = *values; + values++; + } + + if (XIMaskIsSet(raw_event->valuators.mask, VALUATOR_TILTX)) { + tilt_x = *values; + values++; + } + + if (XIMaskIsSet(raw_event->valuators.mask, VALUATOR_TILTY)) { + tilt_y = *values; + } + + Map::Element *pen_info = xi.pen_devices.find(device_id); + if (pen_info) { + Vector3 mult = pen_info->value(); + if (mult.x != 0.0) xi.pressure = pressure / mult.x; + if ((mult.y != 0.0) && (mult.z != 0.0)) xi.tilt = Vector2(tilt_x / mult.y, tilt_y / mult.z); } // https://bugs.freedesktop.org/show_bug.cgi?id=71609 @@ -2417,6 +2481,9 @@ void OS_X11::process_xevents() { Ref mm; mm.instance(); + mm->set_pressure(xi.pressure); + mm->set_tilt(xi.tilt); + // Make the absolute position integral so it doesn't look _too_ weird :) Point2i posi(pos); diff --git a/platform/x11/os_x11.h b/platform/x11/os_x11.h index e6c2effacf41..a5576f44022e 100644 --- a/platform/x11/os_x11.h +++ b/platform/x11/os_x11.h @@ -131,9 +131,12 @@ class OS_X11 : public OS_Unix { int opcode; Vector touch_devices; Map absolute_devices; + Map pen_devices; XIEventMask all_event_mask; XIEventMask all_master_event_mask; Map state; + double pressure; + Vector2 tilt; Vector2 mouse_pos_to_filter; Vector2 relative_motion; Vector2 raw_pos;