Kaynağa Gözat

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

bruvzg 6 yıl önce
ebeveyn
işleme
f675621725

+ 34 - 0
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<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;
 }
 
 ////////////////////////////////////////

+ 9 - 0
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;
 

+ 7 - 1
doc/classes/InputEventMouseMotion.xml

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

+ 5 - 0
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();

+ 121 - 0
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<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;

+ 68 - 0
platform/windows/os_windows.h

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

+ 71 - 4
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<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);
 

+ 3 - 0
platform/x11/os_x11.h

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