Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions src/platform/macos/macos_backend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -484,14 +484,28 @@ namespace lvh::detail {

/**
* @brief Shared macOS backend state.
*
* Local-event suppression on `source` is set to zero so cursor warps do not
* discard the following click or mouse-moved events. CoreGraphics copies
* that configuration into each event at creation time, so `mouse_event` is
* refreshed from `source` after the interval is configured.
*/
class MacosInputState {
public:
MacosInputState():
source {CGEventSourceCreate(kCGEventSourceStateHIDSystemState)},
keyboard_source {CGEventSourceCreate(kCGEventSourceStatePrivate)},
mouse_event {source ? CGEventCreate(source) : nullptr},
scroll_lines_per_detent_value {read_scroll_lines_per_detent(scrollwheel_scaling)} {}
scroll_lines_per_detent_value {read_scroll_lines_per_detent(scrollwheel_scaling)} {
// Each move warps the cursor. The default 250ms suppression then drops the
// following click and mouse-moved events. The cursor image still moves.
if (source) {
CGEventSourceSetLocalEventsSuppressionInterval(source, 0);
Comment thread
ReenigneArcher marked this conversation as resolved.
Resolved
if (mouse_event) {
CGEventSetSource(mouse_event, source);
}
}
}

MacosInputState(const MacosInputState &) = delete;
MacosInputState &operator=(const MacosInputState &) = delete;
Expand All @@ -510,9 +524,9 @@ namespace lvh::detail {
}
}

CGEventSourceRef source {}; ///< CoreGraphics event source for mouse and scroll events.
CGEventSourceRef source {}; ///< Mouse/scroll source with local-event suppression disabled.
CGEventSourceRef keyboard_source {}; ///< CoreGraphics event source for keyboard events.
CGEventRef mouse_event {}; ///< Reusable CoreGraphics mouse event.
CGEventRef mouse_event {}; ///< Reusable mouse event refreshed from `source` after suppression is disabled.
double scrollwheel_scaling = default_scrollwheel_scaling; ///< Raw macOS scroll-wheel scaling preference.
int scroll_lines_per_detent_value = default_scroll_lines_per_detent; ///< Logical lines per wheel detent.
CGEventFlags keyboard_flags {}; ///< Active modifier flags applied to mouse events.
Expand Down
7 changes: 7 additions & 0 deletions tests/fixtures/include/fixtures/macos_backend_test_hooks.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,4 +199,11 @@ namespace lvh::detail::test {
*/
MacosBackendUtilityResult macos_backend_utilities();

/**
* @brief Read the suppression interval stored on the backend's reusable mouse event.
*
* @return Suppression interval in seconds, or no value if event-source creation fails.
*/
std::optional<double> macos_backend_mouse_event_suppression_interval();

} // namespace lvh::detail::test
14 changes: 14 additions & 0 deletions tests/fixtures/macos_backend_test_hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,20 @@ namespace lvh::detail::test {

namespace macos = lvh::detail::macos_backend_test;

std::optional<double> macos_backend_mouse_event_suppression_interval() {
macos::MacosInputState state;
if (!state.mouse_event) {
return std::nullopt;
}
const auto source = CGEventCreateSourceFromEvent(state.mouse_event);
if (!source) {
return std::nullopt;
}
const auto interval = CGEventSourceGetLocalEventsSuppressionInterval(source);
CFRelease(source);
return interval;
}

std::optional<std::uint16_t> macos_backend_key_code(KeyboardKeyCode key_code) {
const auto mapped = macos::macos_key_code(key_code);
if (!mapped) {
Expand Down
6 changes: 6 additions & 0 deletions tests/unit/test_macos_backend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@
*/
class MacosBackendTest: public MacOSTest {};

TEST_F(MacosBackendTest, DisablesSuppressionForReusableMouseEvent) {
const auto interval = lvh::detail::test::macos_backend_mouse_event_suppression_interval();
ASSERT_TRUE(interval.has_value());
EXPECT_DOUBLE_EQ(*interval, 0.0);
}

TEST_F(MacosBackendTest, TranslatesKeyboardKeys) {
EXPECT_EQ(lvh::detail::test::macos_backend_key_code(0x08), kVK_Delete);
EXPECT_EQ(lvh::detail::test::macos_backend_key_code(0x09), kVK_Tab);
Expand Down
Loading