From b7fd58680337e59c54e8a0eff633542c43ea0de4 Mon Sep 17 00:00:00 2001 From: mike kowalski Date: Sun, 7 Sep 2025 11:37:43 -0700 Subject: [PATCH] Detect and supply missing button release event on canvas When mouse button release event is lost, inject one to keep drawing/selecting working as expected. --- src/ui/widget/canvas.cpp | 28 ++++++++++++++++++++++++++++ src/ui/widget/canvas.h | 3 +++ 2 files changed, 31 insertions(+) diff --git a/src/ui/widget/canvas.cpp b/src/ui/widget/canvas.cpp index 11ef50922d..d8a992bcac 100644 --- a/src/ui/widget/canvas.cpp +++ b/src/ui/widget/canvas.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -945,6 +946,30 @@ Gtk::EventSequenceState Canvas::on_button_pressed(Gtk::GestureClick const &contr d->last_mouse = Geom::Point(x, y); d->unreleased_presses |= 1 << controller.get_current_button(); +#ifdef __APPLE__ + _pointer_position = Geom::Point(x, y); + _button_down = controller.get_current_button(); + _button_release_watchdog = Glib::signal_timeout().connect([this, &controller] { + auto pointer = Gdk::Display::get_default()-> get_default_seat()-> get_pointer(); + double x, y; + Gdk::ModifierType mask; + dynamic_cast(*get_root()).get_surface()->get_device_position(pointer, x, y, mask); + auto buttons = Gdk::ModifierType::BUTTON1_MASK | Gdk::ModifierType::BUTTON2_MASK | Gdk::ModifierType::BUTTON3_MASK; + if ((mask & buttons) == Gdk::ModifierType::NO_MODIFIER_MASK) { + // lost release event + auto event = ButtonReleaseEvent(); + event.modifiers = int(mask); + event.device = controller.get_current_event_device(); + event.pos = _pointer_position; + event.button = _button_down; + event.time = controller.get_current_event_time(); + d->process_event(event); + return false; + } + return true; + }, 100); +#endif + grab_focus(); if (controller.get_current_button() == 3) { @@ -987,6 +1012,8 @@ Gtk::EventSequenceState Canvas::on_button_pressed(Gtk::GestureClick const &contr Gtk::EventSequenceState Canvas::on_button_released(Gtk::GestureClick const &controller, int /*n_press*/, double x, double y) { + _button_release_watchdog.disconnect(); + _state = (int)controller.get_current_event_state(); d->last_mouse = Geom::Point(x, y); d->unreleased_presses &= ~(1 << controller.get_current_button()); @@ -1135,6 +1162,7 @@ void Canvas::on_key_released(Gtk::EventControllerKey const &controller, void Canvas::on_motion(Gtk::EventControllerMotion const &controller, double x, double y) { + _pointer_position = Geom::Point(x, y); auto const mouse = Geom::Point{x, y}; if (mouse == d->last_mouse) { return; // Scrolling produces spurious motion events; discard them. diff --git a/src/ui/widget/canvas.h b/src/ui/widget/canvas.h index 6ae6b317cf..da00c88632 100644 --- a/src/ui/widget/canvas.h +++ b/src/ui/widget/canvas.h @@ -206,6 +206,9 @@ private: bool _all_enter_events; ///< Keep all enter events. Only set true in connector-tool.cpp. bool _is_dragging; ///< Used in selection-chemistry to block undo/redo. int _state; ///< Last known modifier state (SHIFT, CTRL, etc.). + sigc::scoped_connection _button_release_watchdog; + int _button_down = 0; + Geom::Point _pointer_position; Inkscape::CanvasItem *_current_canvas_item; ///< Item containing cursor, nullptr if none. Inkscape::CanvasItem *_current_canvas_item_new; ///< Item to become _current_item, nullptr if none. -- GitLab