[macOS, Windows, X11] Add graphic tablet pen pressure and tilt support to InputEventMouseMotion event.

This commit is contained in:
bruvzg 2018-12-13 22:32:11 +02:00
parent 924db5fa58
commit f675621725
No known key found for this signature in database
GPG key ID: 89DD917D9CE4218D
8 changed files with 318 additions and 5 deletions

View file

@ -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<InputEvent> 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<InputEvent> &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;
}
////////////////////////////////////////

View file

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

View file

@ -4,7 +4,7 @@
Input event type for mouse motion events.
</brief_description>
<description>
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].
</description>
<tutorials>
<link>https://docs.godotengine.org/en/latest/tutorials/inputs/mouse_and_input_coordinates.html</link>
@ -18,6 +18,12 @@
<member name="speed" type="Vector2" setter="set_speed" getter="get_speed" default="Vector2( 0, 0 )">
The mouse speed in pixels per second.
</member>
<member name="pressure" type="float" setter="set_pressure" getter="get_pressure">
Represents the pressure the user puts on the pen. Ranges from [code]0.0[/code] to [code]1.0[/code].
</member>
<member name="tilt" type="Vector2" setter="set_tilt" getter="get_tilt">
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.
</member>
</members>
<constants>
</constants>

View file

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

View file

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

View file

@ -56,6 +56,71 @@
#include <windows.h>
#include <windowsx.h>
#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
};

View file

@ -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<int, Vector3>::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<InputEventMouseMotion> 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);

View file

@ -131,9 +131,12 @@ class OS_X11 : public OS_Unix {
int opcode;
Vector<int> touch_devices;
Map<int, Vector2> absolute_devices;
Map<int, Vector3> pen_devices;
XIEventMask all_event_mask;
XIEventMask all_master_event_mask;
Map<int, Vector2> state;
double pressure;
Vector2 tilt;
Vector2 mouse_pos_to_filter;
Vector2 relative_motion;
Vector2 raw_pos;