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: |
|
run: |
|
||||||
CLANG_VERSION=$(clang++ --version | sed -n 's/.*version \([0-9]\+\)\..*/\1/p')
|
CLANG_VERSION=$(clang++ --version | sed -n 's/.*version \([0-9]\+\)\..*/\1/p')
|
||||||
echo "CLANG_VERSION=$CLANG_VERSION" >> $GITHUB_ENV
|
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
|
- name: Remove ALSA Library
|
||||||
if: runner.os == 'Linux' && matrix.platform.name != 'Android'
|
if: runner.os == 'Linux' && matrix.platform.name != 'Android'
|
||||||
@ -367,7 +367,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Install Linux Dependencies
|
- name: Install Linux Dependencies
|
||||||
if: runner.os == 'Linux'
|
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
|
- name: Install macOS Dependencies
|
||||||
if: runner.os == 'macOS'
|
if: runner.os == 'macOS'
|
||||||
@ -406,7 +406,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Install Linux Dependencies
|
- name: Install Linux Dependencies
|
||||||
if: runner.os == 'Linux'
|
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
|
- 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}}
|
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
|
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
|
/// \brief Mouse entered event
|
||||||
///
|
///
|
||||||
@ -303,6 +336,7 @@ private:
|
|||||||
MouseButtonPressed,
|
MouseButtonPressed,
|
||||||
MouseButtonReleased,
|
MouseButtonReleased,
|
||||||
MouseMoved,
|
MouseMoved,
|
||||||
|
MouseMovedRaw,
|
||||||
MouseEntered,
|
MouseEntered,
|
||||||
MouseLeft,
|
MouseLeft,
|
||||||
JoystickButtonPressed,
|
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)
|
find_package(GBM REQUIRED)
|
||||||
target_link_libraries(sfml-window PRIVATE DRM::DRM GBM::GBM)
|
target_link_libraries(sfml-window PRIVATE DRM::DRM GBM::GBM)
|
||||||
else()
|
else()
|
||||||
find_package(X11 REQUIRED COMPONENTS Xrandr Xcursor)
|
find_package(X11 REQUIRED COMPONENTS Xrandr Xcursor Xi)
|
||||||
target_link_libraries(sfml-window PRIVATE X11::X11 X11::Xrandr X11::Xcursor)
|
target_link_libraries(sfml-window PRIVATE X11::X11 X11::Xrandr X11::Xcursor X11::Xi)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
target_link_libraries(sfml-window PUBLIC SFML::System)
|
target_link_libraries(sfml-window PUBLIC SFML::System)
|
||||||
|
@ -45,10 +45,12 @@
|
|||||||
|
|
||||||
#include <X11/Xatom.h>
|
#include <X11/Xatom.h>
|
||||||
#include <X11/Xutil.h>
|
#include <X11/Xutil.h>
|
||||||
|
#include <X11/extensions/XInput2.h>
|
||||||
#include <X11/extensions/Xrandr.h>
|
#include <X11/extensions/Xrandr.h>
|
||||||
#include <X11/keysym.h>
|
#include <X11/keysym.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
#include <bitset>
|
#include <bitset>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
@ -98,7 +100,7 @@ constexpr unsigned int maxTrialsCount = 5;
|
|||||||
// NOLINTNEXTLINE(readability-non-const-parameter)
|
// NOLINTNEXTLINE(readability-non-const-parameter)
|
||||||
Bool checkEvent(::Display*, XEvent* event, XPointer userData)
|
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
|
// The event matches the current window so pick it up
|
||||||
return true;
|
return true;
|
||||||
@ -367,6 +369,36 @@ bool isWMAbsolutePositionGood()
|
|||||||
std::end(wmAbsPosGood),
|
std::end(wmAbsPosGood),
|
||||||
[&](const sf::String& name) { return name == windowManagerName; });
|
[&](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 WindowImplX11Impl
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@ -1561,6 +1593,13 @@ void WindowImplX11::initialize()
|
|||||||
1);
|
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
|
// Show the window
|
||||||
setVisible(true);
|
setVisible(true);
|
||||||
|
|
||||||
@ -2011,6 +2050,33 @@ bool WindowImplX11::processEvent(XEvent& windowEvent)
|
|||||||
|
|
||||||
break;
|
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;
|
return true;
|
||||||
|
@ -126,6 +126,16 @@ void setProcessDpiAware()
|
|||||||
FreeLibrary(user32Dll);
|
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
|
||||||
|
|
||||||
namespace sf::priv
|
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 we're the first window handle, we only need to poll for joysticks when WM_DEVICECHANGE message is received
|
||||||
if (handleCount == 0)
|
if (handleCount == 0)
|
||||||
|
{
|
||||||
JoystickImpl::setLazyUpdates(true);
|
JoystickImpl::setLazyUpdates(true);
|
||||||
|
|
||||||
|
initRawMouse();
|
||||||
|
}
|
||||||
|
|
||||||
++handleCount;
|
++handleCount;
|
||||||
|
|
||||||
// We change the event procedure of the control (it is important to save the old one)
|
// 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 (m_handle)
|
||||||
{
|
{
|
||||||
if (handleCount == 0)
|
if (handleCount == 0)
|
||||||
|
{
|
||||||
JoystickImpl::setLazyUpdates(true);
|
JoystickImpl::setLazyUpdates(true);
|
||||||
|
|
||||||
|
initRawMouse();
|
||||||
|
}
|
||||||
|
|
||||||
++handleCount;
|
++handleCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1085,6 +1103,23 @@ void WindowImplWin32::processEvent(UINT message, WPARAM wParam, LPARAM lParam)
|
|||||||
break;
|
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
|
// Hardware configuration change event
|
||||||
case WM_DEVICECHANGE:
|
case WM_DEVICECHANGE:
|
||||||
{
|
{
|
||||||
|
@ -123,6 +123,13 @@ TEST_CASE("[Window] sf::Event")
|
|||||||
const auto& mouseMoved = *event.getIf<sf::Event::MouseMoved>();
|
const auto& mouseMoved = *event.getIf<sf::Event::MouseMoved>();
|
||||||
CHECK(mouseMoved.position == sf::Vector2i(4, 2));
|
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{};
|
event = sf::Event::MouseEntered{};
|
||||||
CHECK(event);
|
CHECK(event);
|
||||||
CHECK(event.is<sf::Event::MouseEntered>());
|
CHECK(event.is<sf::Event::MouseEntered>());
|
||||||
@ -254,6 +261,9 @@ TEST_CASE("[Window] sf::Event")
|
|||||||
const sf::Event::MouseMoved mouseMoved;
|
const sf::Event::MouseMoved mouseMoved;
|
||||||
CHECK(mouseMoved.position == sf::Vector2i());
|
CHECK(mouseMoved.position == sf::Vector2i());
|
||||||
|
|
||||||
|
const sf::Event::MouseMovedRaw mouseMovedRaw;
|
||||||
|
CHECK(mouseMovedRaw.delta == sf::Vector2i());
|
||||||
|
|
||||||
const sf::Event::JoystickButtonPressed joystickButtonPressed;
|
const sf::Event::JoystickButtonPressed joystickButtonPressed;
|
||||||
CHECK(joystickButtonPressed.joystickId == 0);
|
CHECK(joystickButtonPressed.joystickId == 0);
|
||||||
CHECK(joystickButtonPressed.button == 0);
|
CHECK(joystickButtonPressed.button == 0);
|
||||||
|
Loading…
Reference in New Issue
Block a user