mirror of
https://github.com/SFML/SFML.git
synced 2024-11-24 20:31:05 +08:00
Add support for raw mouse input
This commit is contained in:
parent
bfd65989e9
commit
92bba1ed6f
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
@ -133,7 +133,7 @@ jobs:
|
||||
run: |
|
||||
CLANG_VERSION=$(clang++ --version | sed -n 's/.*version \([0-9]\+\)\..*/\1/p')
|
||||
echo "CLANG_VERSION=$CLANG_VERSION" >> $GITHUB_ENV
|
||||
sudo apt-get update && sudo apt-get install xorg-dev libxrandr-dev libxcursor-dev libudev-dev libflac-dev libvorbis-dev libgl1-mesa-dev libegl1-mesa-dev libdrm-dev libgbm-dev xvfb fluxbox ccache gcovr ${{ matrix.platform.name == 'Linux Clang' && 'llvm-$CLANG_VERSION' || '' }}
|
||||
sudo apt-get update && sudo apt-get install xorg-dev libxrandr-dev libxcursor-dev libxi-dev libudev-dev libflac-dev libvorbis-dev libgl1-mesa-dev libegl1-mesa-dev libdrm-dev libgbm-dev xvfb fluxbox ccache gcovr ${{ matrix.platform.name == 'Linux Clang' && 'llvm-$CLANG_VERSION' || '' }}
|
||||
|
||||
- name: Remove ALSA Library
|
||||
if: runner.os == 'Linux' && matrix.platform.name != 'Android'
|
||||
@ -367,7 +367,7 @@ jobs:
|
||||
|
||||
- name: Install Linux Dependencies
|
||||
if: runner.os == 'Linux'
|
||||
run: sudo apt-get update && sudo apt-get install libfreetype-dev libxrandr-dev libxcursor-dev libudev-dev libflac-dev libvorbis-dev libgl1-mesa-dev libegl1-mesa-dev libdrm-dev libgbm-dev
|
||||
run: sudo apt-get update && sudo apt-get install libfreetype-dev libxrandr-dev libxcursor-dev libxi-dev libudev-dev libflac-dev libvorbis-dev libgl1-mesa-dev libegl1-mesa-dev libdrm-dev libgbm-dev
|
||||
|
||||
- name: Install macOS Dependencies
|
||||
if: runner.os == 'macOS'
|
||||
@ -406,7 +406,7 @@ jobs:
|
||||
|
||||
- name: Install Linux Dependencies
|
||||
if: runner.os == 'Linux'
|
||||
run: sudo apt-get update && sudo apt-get install xorg-dev libxrandr-dev libxcursor-dev libudev-dev libflac-dev libvorbis-dev libgl1-mesa-dev libegl1-mesa-dev libdrm-dev libgbm-dev xvfb fluxbox && sudo apt-get remove -y libasound2
|
||||
run: sudo apt-get update && sudo apt-get install xorg-dev libxrandr-dev libxcursor-dev libxi-dev libudev-dev libflac-dev libvorbis-dev libgl1-mesa-dev libegl1-mesa-dev libdrm-dev libgbm-dev xvfb fluxbox && sudo apt-get remove -y libasound2
|
||||
|
||||
- name: Configure
|
||||
run: cmake --preset dev -GNinja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER=clang++ -DSFML_BUILD_EXAMPLES=OFF -DSFML_ENABLE_SANITIZERS=ON ${{matrix.platform.flags}}
|
||||
|
@ -154,6 +154,39 @@ public:
|
||||
Vector2i position; //!< Position of the mouse pointer, relative to the top left of the owner window
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Mouse move raw event
|
||||
///
|
||||
/// Raw mouse input data comes unprocessed from the
|
||||
/// operating system hence "raw". While the MouseMoved
|
||||
/// position value is dependent on the screen resolution,
|
||||
/// raw data is not. If the physical mouse is moved too
|
||||
/// little to cause the screen cursor to move at least a
|
||||
/// single pixel, no MouseMoved event will be generated. In
|
||||
/// contrast, any movement information generated by the
|
||||
/// mouse independent of its sensor resolution will always
|
||||
/// generate a MouseMovedRaw event.
|
||||
///
|
||||
/// In addition to screen resolution independence, raw
|
||||
/// mouse data also does not have mouse acceleration or
|
||||
/// smoothing applied to it as MouseMoved does.
|
||||
///
|
||||
/// Raw mouse movement data is intended for controlling
|
||||
/// non-cursor movement, e.g. controlling the camera
|
||||
/// orientation in a first person view, whereas MouseMoved
|
||||
/// is intended primarily for controlling things related to
|
||||
/// the screen cursor hence the additional processing
|
||||
/// applied to it.
|
||||
///
|
||||
/// Currently, raw mouse input events will only be generated
|
||||
/// on Windows and Linux.
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
struct MouseMovedRaw
|
||||
{
|
||||
Vector2i delta; ///< Delta movement of the mouse since the last event
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Mouse entered event
|
||||
///
|
||||
@ -303,6 +336,7 @@ private:
|
||||
MouseButtonPressed,
|
||||
MouseButtonReleased,
|
||||
MouseMoved,
|
||||
MouseMovedRaw,
|
||||
MouseEntered,
|
||||
MouseLeft,
|
||||
JoystickButtonPressed,
|
||||
|
@ -273,8 +273,8 @@ if(SFML_OS_LINUX OR SFML_OS_FREEBSD OR SFML_OS_OPENBSD OR SFML_OS_NETBSD)
|
||||
find_package(GBM REQUIRED)
|
||||
target_link_libraries(sfml-window PRIVATE DRM::DRM GBM::GBM)
|
||||
else()
|
||||
find_package(X11 REQUIRED COMPONENTS Xrandr Xcursor)
|
||||
target_link_libraries(sfml-window PRIVATE X11::X11 X11::Xrandr X11::Xcursor)
|
||||
find_package(X11 REQUIRED COMPONENTS Xrandr Xcursor Xi)
|
||||
target_link_libraries(sfml-window PRIVATE X11::X11 X11::Xrandr X11::Xcursor X11::Xi)
|
||||
endif()
|
||||
endif()
|
||||
target_link_libraries(sfml-window PUBLIC SFML::System)
|
||||
|
@ -45,10 +45,12 @@
|
||||
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/extensions/XInput2.h>
|
||||
#include <X11/extensions/Xrandr.h>
|
||||
#include <X11/keysym.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
#include <fcntl.h>
|
||||
#include <filesystem>
|
||||
@ -98,7 +100,7 @@ constexpr unsigned int maxTrialsCount = 5;
|
||||
// NOLINTNEXTLINE(readability-non-const-parameter)
|
||||
Bool checkEvent(::Display*, XEvent* event, XPointer userData)
|
||||
{
|
||||
if (event->xany.window == reinterpret_cast<::Window>(userData))
|
||||
if (event->xany.window == reinterpret_cast<::Window>(userData) || event->type == GenericEvent)
|
||||
{
|
||||
// The event matches the current window so pick it up
|
||||
return true;
|
||||
@ -367,6 +369,36 @@ bool isWMAbsolutePositionGood()
|
||||
std::end(wmAbsPosGood),
|
||||
[&](const sf::String& name) { return name == windowManagerName; });
|
||||
}
|
||||
|
||||
// Initialize raw mouse input
|
||||
bool initRawMouse(::Display* disp)
|
||||
{
|
||||
int opcode = 0;
|
||||
int event = 0;
|
||||
int error = 0;
|
||||
|
||||
if (XQueryExtension(disp, "XInputExtension", &opcode, &event, &error))
|
||||
{
|
||||
int major = 2;
|
||||
int minor = 0;
|
||||
|
||||
if (XIQueryVersion(disp, &major, &minor) != BadRequest)
|
||||
{
|
||||
std::array<unsigned char, XIMaskLen(XI_LASTEVENT)> mask{};
|
||||
XISetMask(mask.data(), XI_RawMotion);
|
||||
|
||||
XIEventMask xiEventMask;
|
||||
xiEventMask.deviceid = XIAllDevices;
|
||||
xiEventMask.mask_len = mask.size();
|
||||
xiEventMask.mask = mask.data();
|
||||
|
||||
if (XISelectEvents(disp, DefaultRootWindow(disp), &xiEventMask, 1) == Success)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
} // namespace WindowImplX11Impl
|
||||
} // namespace
|
||||
|
||||
@ -1561,6 +1593,13 @@ void WindowImplX11::initialize()
|
||||
1);
|
||||
}
|
||||
|
||||
// Enable raw input in first window
|
||||
if (allWindows.empty())
|
||||
{
|
||||
if (!initRawMouse(m_display.get()))
|
||||
sf::err() << "Failed to initialize raw mouse input" << std::endl;
|
||||
}
|
||||
|
||||
// Show the window
|
||||
setVisible(true);
|
||||
|
||||
@ -2011,6 +2050,33 @@ bool WindowImplX11::processEvent(XEvent& windowEvent)
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Raw input
|
||||
case GenericEvent:
|
||||
{
|
||||
if (XGetEventData(m_display.get(), &windowEvent.xcookie))
|
||||
{
|
||||
if (windowEvent.xcookie.evtype == XI_RawMotion)
|
||||
{
|
||||
const auto* rawEvent = static_cast<const XIRawEvent*>(windowEvent.xcookie.data);
|
||||
int relativeValueX = 0;
|
||||
int relativeValueY = 0;
|
||||
|
||||
// Get relative input values
|
||||
if ((rawEvent->valuators.mask_len > 0) && XIMaskIsSet(rawEvent->valuators.mask, 0))
|
||||
relativeValueX = static_cast<int>(rawEvent->raw_values[0]);
|
||||
|
||||
if ((rawEvent->valuators.mask_len > 1) && XIMaskIsSet(rawEvent->valuators.mask, 1))
|
||||
relativeValueY = static_cast<int>(rawEvent->raw_values[1]);
|
||||
|
||||
pushEvent(Event::MouseMovedRaw{{relativeValueX, relativeValueY}});
|
||||
}
|
||||
|
||||
XFreeEventData(m_display.get(), &windowEvent.xcookie);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -126,6 +126,16 @@ void setProcessDpiAware()
|
||||
FreeLibrary(user32Dll);
|
||||
}
|
||||
}
|
||||
|
||||
// Register a RAWINPUTDEVICE representing the mouse to receive raw
|
||||
// mouse deltas using WM_INPUT
|
||||
void initRawMouse()
|
||||
{
|
||||
const RAWINPUTDEVICE rawMouse{0x01, 0x02, 0, nullptr}; // HID usage: mouse device class, no flags, follow keyboard focus
|
||||
|
||||
if (RegisterRawInputDevices(&rawMouse, 1, sizeof(rawMouse)) != TRUE)
|
||||
sf::err() << "Failed to initialize raw mouse input" << std::endl;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace sf::priv
|
||||
@ -140,8 +150,12 @@ WindowImplWin32::WindowImplWin32(WindowHandle handle) : m_handle(handle)
|
||||
{
|
||||
// If we're the first window handle, we only need to poll for joysticks when WM_DEVICECHANGE message is received
|
||||
if (handleCount == 0)
|
||||
{
|
||||
JoystickImpl::setLazyUpdates(true);
|
||||
|
||||
initRawMouse();
|
||||
}
|
||||
|
||||
++handleCount;
|
||||
|
||||
// We change the event procedure of the control (it is important to save the old one)
|
||||
@ -222,8 +236,12 @@ m_cursorGrabbed(m_fullscreen)
|
||||
if (m_handle)
|
||||
{
|
||||
if (handleCount == 0)
|
||||
{
|
||||
JoystickImpl::setLazyUpdates(true);
|
||||
|
||||
initRawMouse();
|
||||
}
|
||||
|
||||
++handleCount;
|
||||
}
|
||||
|
||||
@ -1085,6 +1103,23 @@ void WindowImplWin32::processEvent(UINT message, WPARAM wParam, LPARAM lParam)
|
||||
break;
|
||||
}
|
||||
|
||||
// Raw input event
|
||||
case WM_INPUT:
|
||||
{
|
||||
RAWINPUT input;
|
||||
UINT size = sizeof(input);
|
||||
|
||||
GetRawInputData(reinterpret_cast<HRAWINPUT>(lParam), RID_INPUT, &input, &size, sizeof(RAWINPUTHEADER));
|
||||
|
||||
if (input.header.dwType == RIM_TYPEMOUSE)
|
||||
{
|
||||
if (const RAWMOUSE* rawMouse = &input.data.mouse; (rawMouse->usFlags & 0x01) == MOUSE_MOVE_RELATIVE)
|
||||
pushEvent(Event::MouseMovedRaw{{rawMouse->lLastX, rawMouse->lLastY}});
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Hardware configuration change event
|
||||
case WM_DEVICECHANGE:
|
||||
{
|
||||
|
@ -123,6 +123,13 @@ TEST_CASE("[Window] sf::Event")
|
||||
const auto& mouseMoved = *event.getIf<sf::Event::MouseMoved>();
|
||||
CHECK(mouseMoved.position == sf::Vector2i(4, 2));
|
||||
|
||||
event = sf::Event::MouseMovedRaw{{3, 7}};
|
||||
CHECK(event);
|
||||
CHECK(event.is<sf::Event::MouseMovedRaw>());
|
||||
CHECK(event.getIf<sf::Event::MouseMovedRaw>());
|
||||
const auto& mouseMovedRaw = *event.getIf<sf::Event::MouseMovedRaw>();
|
||||
CHECK(mouseMovedRaw.delta == sf::Vector2i(3, 7));
|
||||
|
||||
event = sf::Event::MouseEntered{};
|
||||
CHECK(event);
|
||||
CHECK(event.is<sf::Event::MouseEntered>());
|
||||
@ -254,6 +261,9 @@ TEST_CASE("[Window] sf::Event")
|
||||
const sf::Event::MouseMoved mouseMoved;
|
||||
CHECK(mouseMoved.position == sf::Vector2i());
|
||||
|
||||
const sf::Event::MouseMovedRaw mouseMovedRaw;
|
||||
CHECK(mouseMovedRaw.delta == sf::Vector2i());
|
||||
|
||||
const sf::Event::JoystickButtonPressed joystickButtonPressed;
|
||||
CHECK(joystickButtonPressed.joystickId == 0);
|
||||
CHECK(joystickButtonPressed.button == 0);
|
||||
|
Loading…
Reference in New Issue
Block a user