diff --git a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp index 0c5f5ffb01..c0594b6662 100644 --- a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp @@ -49,6 +49,13 @@ // more responsive. This might be useful as a user-customizable option. #define MOUSE_AXIS_SMOOTHING 1.5f +// The scroll axis value should decay a lot faster than the mouse axes since +// it should ideally register each click of the scroll wheel. Decreasing this +// value makes it more likely that a scroll wheel input is registered, but less +// likely to differentiate between different inputs, while increasing it will +// more cleanly separate each scroll wheel click, but risks dropping some inputs +#define SCROLL_AXIS_DECAY 1.1f + namespace ciface::XInput2 { // This function will add zero or more KeyboardMouse objects to devices. @@ -74,6 +81,7 @@ void PopulateDevices(void* const hwnd) XIDeviceInfo* all_masters; XIDeviceInfo* current_master; + double scroll_increment = 1.0f; int num_masters; all_masters = XIQueryDevice(dpy, XIAllMasterDevices, &num_masters); @@ -81,12 +89,26 @@ void PopulateDevices(void* const hwnd) for (int i = 0; i < num_masters; i++) { current_master = &all_masters[i]; + if (current_master->use == XIMasterPointer) { + // We need to query the master for the scroll wheel's increment, since the increment used + // varies depending on what input driver is being used. For example, xf86-libinput uses 120.0. + for (int j = 0; j < current_master->num_classes; j++) + { + if (current_master->classes[j]->type == XIScrollClass) + { + XIScrollClassInfo* scroll_event = + reinterpret_cast(current_master->classes[j]); + scroll_increment = scroll_event->increment; + break; + } + } // Since current_master is a master pointer, its attachment must // be a master keyboard. - g_controller_interface.AddDevice(std::make_shared( - (Window)hwnd, xi_opcode, current_master->deviceid, current_master->attachment)); + g_controller_interface.AddDevice( + std::make_shared((Window)hwnd, xi_opcode, current_master->deviceid, + current_master->attachment, scroll_increment)); } } @@ -128,8 +150,10 @@ void KeyboardMouse::SelectEventsForDevice(XIEventMask* mask, int deviceid) XIFreeDeviceInfo(all_slaves); } -KeyboardMouse::KeyboardMouse(Window window, int opcode, int pointer, int keyboard) - : m_window(window), xi_opcode(opcode), pointer_deviceid(pointer), keyboard_deviceid(keyboard) +KeyboardMouse::KeyboardMouse(Window window, int opcode, int pointer, int keyboard, + double scroll_increment) + : m_window(window), xi_opcode(opcode), pointer_deviceid(pointer), keyboard_deviceid(keyboard), + scroll_increment(scroll_increment) { // The cool thing about each KeyboardMouse object having its own Display // is that each one gets its own separate copy of the X11 event stream, @@ -195,14 +219,21 @@ KeyboardMouse::KeyboardMouse(Window window, int opcode, int pointer, int keyboar for (int i = 0; i != 4; ++i) AddInput(new Cursor(!!(i & 2), !!(i & 1), (i & 2) ? &m_state.cursor.y : &m_state.cursor.x)); - // Mouse Axis, X-/+ and Y-/+ - for (int i = 0; i != 4; ++i) - AddInput(new Axis(!!(i & 2), !!(i & 1), (i & 2) ? &m_state.axis.y : &m_state.axis.x)); + // Mouse Axis, X-/+, Y-/+ and Z-/+ + AddInput(new Axis(0, false, &m_state.axis.x)); + AddInput(new Axis(0, true, &m_state.axis.x)); + AddInput(new Axis(1, false, &m_state.axis.y)); + AddInput(new Axis(1, true, &m_state.axis.y)); + AddInput(new Axis(2, false, &m_state.axis.z)); + AddInput(new Axis(2, true, &m_state.axis.z)); - // Relative Mouse, X-/+ and Y-/+ - for (int i = 0; i != 4; ++i) - AddInput(new RelativeMouse(!!(i & 2), !!(i & 1), - (i & 2) ? &m_state.relative_mouse.y : &m_state.relative_mouse.x)); + // Relative Mouse, X-/+, Y-/+ and Z-/+ + AddInput(new RelativeMouse(0, false, &m_state.relative_mouse.x)); + AddInput(new RelativeMouse(0, true, &m_state.relative_mouse.x)); + AddInput(new RelativeMouse(1, false, &m_state.relative_mouse.y)); + AddInput(new RelativeMouse(1, true, &m_state.relative_mouse.y)); + AddInput(new RelativeMouse(2, false, &m_state.relative_mouse.z)); + AddInput(new RelativeMouse(2, true, &m_state.relative_mouse.z)); } KeyboardMouse::~KeyboardMouse() @@ -256,7 +287,7 @@ void KeyboardMouse::UpdateInput() XFlush(m_display); // for the axis controls - float delta_x = 0.0f, delta_y = 0.0f; + float delta_x = 0.0f, delta_y = 0.0f, delta_z = 0.0f; double delta_delta; bool mouse_moved = false; @@ -293,26 +324,39 @@ void KeyboardMouse::UpdateInput() m_state.keyboard[dev_event->detail / 8] &= ~(1 << (dev_event->detail % 8)); break; case XI_RawMotion: + { mouse_moved = true; - // always safe because there is always at least one byte in - // raw_event->valuators.mask, and if a bit is set in the mask, - // then the value in raw_values is also available. - if (XIMaskIsSet(raw_event->valuators.mask, 0)) + float values[4] = {}; + size_t value_idx = 0; + + // We only care about the first 4 axes, which should always be available at minimum + for (int i = 0; i < 4; ++i) { - delta_delta = raw_event->raw_values[0]; - // test for inf and nan - if (delta_delta == delta_delta && 1 + delta_delta != delta_delta) - delta_x += delta_delta; - } - if (XIMaskIsSet(raw_event->valuators.mask, 1)) - { - delta_delta = raw_event->raw_values[1]; - // test for inf and nan - if (delta_delta == delta_delta && 1 + delta_delta != delta_delta) - delta_y += delta_delta; + if (XIMaskIsSet(raw_event->valuators.mask, i)) + { + values[i] = raw_event->raw_values[value_idx++]; + } } + + delta_delta = values[0]; + // test for inf and nan + if (delta_delta == delta_delta && 1 + delta_delta != delta_delta) + delta_x += delta_delta; + + delta_delta = values[1]; + // test for inf and nan + if (delta_delta == delta_delta && 1 + delta_delta != delta_delta) + delta_y += delta_delta; + + // Scroll wheel input gets scaled to be similar to the mouse axes + delta_delta = values[3] * 8.0 / scroll_increment; + // test for inf and nan + if (delta_delta == delta_delta && 1 + delta_delta != delta_delta) + delta_z += delta_delta; + break; + } case XI_FocusOut: // Clear keyboard state on FocusOut as we will not be receiving KeyRelease events. m_state.keyboard.fill(0); @@ -324,6 +368,7 @@ void KeyboardMouse::UpdateInput() m_state.relative_mouse.x = delta_x; m_state.relative_mouse.y = delta_y; + m_state.relative_mouse.z = delta_z; // apply axis smoothing m_state.axis.x *= MOUSE_AXIS_SMOOTHING; @@ -332,6 +377,8 @@ void KeyboardMouse::UpdateInput() m_state.axis.y *= MOUSE_AXIS_SMOOTHING; m_state.axis.y += delta_y; m_state.axis.y /= MOUSE_AXIS_SMOOTHING + 1.0f; + m_state.axis.z += delta_z; + m_state.axis.z /= SCROLL_AXIS_DECAY; // Get the absolute position of the mouse pointer const bool should_center_mouse = diff --git a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h index b07f6325c7..ff2c787b1d 100644 --- a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h +++ b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h @@ -28,8 +28,8 @@ private: std::array keyboard; unsigned int buttons; Common::Vec2 cursor; - Common::Vec2 axis; - Common::Vec2 relative_mouse; + Common::Vec3 axis; + Common::Vec3 relative_mouse; }; class Key : public Input @@ -113,7 +113,8 @@ private: public: void UpdateInput() override; - KeyboardMouse(Window window, int opcode, int pointer_deviceid, int keyboard_deviceid); + KeyboardMouse(Window window, int opcode, int pointer_deviceid, int keyboard_deviceid, + double scroll_increment); ~KeyboardMouse(); std::string GetName() const override; @@ -126,6 +127,7 @@ private: const int xi_opcode; const int pointer_deviceid; const int keyboard_deviceid; + const double scroll_increment; std::string name; }; } // namespace ciface::XInput2