From ac6c00da26fcd1e699488f4446ffd1b28f416f4a Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Mon, 2 Dec 2024 21:10:37 +0100 Subject: [PATCH] Add support for Touch events to Ardour Canvas --- libs/canvas/canvas.cc | 154 ++++++++++++++++++++++++++++-------- libs/canvas/canvas/canvas.h | 8 ++ 2 files changed, 127 insertions(+), 35 deletions(-) diff --git a/libs/canvas/canvas.cc b/libs/canvas/canvas.cc index 632dd8b70e..16d027ef62 100644 --- a/libs/canvas/canvas.cc +++ b/libs/canvas/canvas.cc @@ -557,8 +557,9 @@ GtkCanvas::GtkCanvas () /* these are the events we want to know about */ add_events (Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::POINTER_MOTION_MASK | - Gdk::SCROLL_MASK | Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK | - Gdk::KEY_PRESS_MASK | Gdk::KEY_RELEASE_MASK); + Gdk::SCROLL_MASK | Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK | + Gdk::TOUCH_BEGIN_MASK | Gdk::TOUCH_UPDATE_MASK | Gdk::TOUCH_END_MASK | + Gdk::KEY_PRESS_MASK | Gdk::KEY_RELEASE_MASK); } void @@ -616,39 +617,8 @@ GtkCanvas::pick_current_item (Duple const & point, int state) /* find the items at the given window position */ - vector items; - _root.add_items_at_point (point, items); - - DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("%1 covers %2 items\n", point, items.size())); - -#ifndef NDEBUG - if (DEBUG_ENABLED(PBD::DEBUG::CanvasEnterLeave)) { - for (auto const & item : items) { - std::cerr << "\tItem " << item->whoami() << " ignore events ? " << item->ignore_events() << " vis ? " << item->visible() << std::endl; - } - } -#endif - - /* put all items at point that are event-sensitive and visible and NOT - groups into within_items. Note that items is sorted from bottom to - top, but we're going to reverse that for within_items so that its - first item is the upper-most item that can be chosen as _current_item. - */ - - vector::const_iterator i; list within_items; - - for (i = items.begin(); i != items.end(); ++i) { - - Item const * possible_item = *i; - - /* We ignore invisible items, containers and items that ignore events */ - - if (!possible_item->visible() || possible_item->ignore_events() || dynamic_cast(possible_item) != 0) { - continue; - } - within_items.push_front (possible_item); - } + get_items_enclosing(point, within_items); DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("after filtering insensitive + containers, we have %1 items\n", within_items.size())); @@ -680,6 +650,42 @@ GtkCanvas::pick_current_item (Duple const & point, int state) } + +/** Given @p point (a position in window coordinates) + * return a list of items in order top to bottom. + */ +void +GtkCanvas::get_items_enclosing (Duple const & point, list &enclosing_items) +{ + /* find the items at the given window position */ + vector items; + _root.add_items_at_point (point, items); + + DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("%1 covers %2 items\n", point, items.size())); + +#ifndef NDEBUG + if (DEBUG_ENABLED(PBD::DEBUG::CanvasEnterLeave)) { + for (auto const& item : items) { + std::cerr << "\tItem " << item->whoami() << " ignore events ? " << item->ignore_events() << " vis ? " << item->visible() << std::endl; + } + } +#endif + + /* put all items at point that are event-sensitive and visible and NOT + * groups into enclosing_items. Note that items is sorted from bottom to + * top, but we're going to reverse that for enclosing_items so that its + * first item is the upper-most item that can be chosen as _current_item. + */ + + for (auto const& possible_item : items) { + /* We ignore invisible items, containers and items that ignore events */ + if (!possible_item->visible() || possible_item->ignore_events() || dynamic_cast(possible_item) != 0) { + continue; + } + enclosing_items.push_front (possible_item); + } +} + /** Deliver a series of enter & leave events based on the pointer position being at window * coordinate @p point, and pointer @p state (modifier keys, etc) */ @@ -851,9 +857,27 @@ bool GtkCanvas::deliver_event (GdkEvent* event) { /* Point in in canvas coordinate space */ - const Item* event_item; + /* touch events require a separate path with no pre-light, and a 'grab' for each finger (i.e. sequence) */ + if (event->type == GDK_TOUCH_BEGIN || event->type == GDK_TOUCH_UPDATE || event->type == GDK_TOUCH_END ) { + int touchp = event->touch.sequence; + std::map::iterator ei = _touched_item.find (touchp); + if (ei == _touched_item.end ()) { + return false; + } + event_item = ei->second; + if (event_item && !event_item->ignore_events () && event_item->Event (event)) { + /* this item has just handled the event */ + DEBUG_TRACE ( + PBD::DEBUG::CanvasEvents, + string_compose ("canvas touch event handled by %1 %2\n", event_item->whatami(), event_item->name.empty() ? "[unknown]" : event_item->name) + ); + return true; + } + return false; + } + if (_grabbed_item) { /* we have a grabbed item, so everything gets sent there */ DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("%1 %2 (%3) was grabbed, send event there\n", @@ -1279,6 +1303,66 @@ GtkCanvas::on_motion_notify_event (GdkEventMotion* ev) return deliver_event (reinterpret_cast (©)); } +bool +GtkCanvas::on_touch_begin_event (GdkEventTouch *ev) +{ +std::cout << "GtkCanvas::on_touch_begin\n" << std::endl; + + /* translate event coordinates from window to canvas */ + + GdkEvent copy = *((GdkEvent*)ev); + Duple winpos = Duple (ev->x, ev->y); + Duple where = window_to_canvas (winpos); + + copy.touch.x = where.x; + copy.touch.y = where.y; + + /* Coordinates in the event will be canvas coordinates, correctly adjusted + for scroll if this GtkCanvas is in a GtkCanvasViewport. + */ + + std::list within_items; + get_items_enclosing (winpos, within_items); + + if (!within_items.empty()) { + _touched_item[ev->sequence] = const_cast (within_items.front()); + } + + DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("canvas scroll @ %1, %2 => %3\n", ev->x, ev->y, where)); + return deliver_event (reinterpret_cast(©)); +} + +bool +GtkCanvas::on_touch_update_event (GdkEventTouch* ev) +{ + GdkEvent copy = *((GdkEvent*)ev); + Duple winpos = Duple (ev->x, ev->y); + Duple where = window_to_canvas (winpos); + + copy.touch.x = where.x; + copy.touch.y = where.y; + + return deliver_event (reinterpret_cast(©)); +} + +bool +GtkCanvas::on_touch_end_event (GdkEventTouch *ev) +{ + GdkEvent copy = *((GdkEvent*)ev); + Duple winpos = Duple (ev->x, ev->y); + Duple where = window_to_canvas (winpos); + + copy.touch.x = where.x; + copy.touch.y = where.y; + + bool ret = deliver_event (reinterpret_cast(©)); + + /* remove "GRAB" */ + _touched_item.erase (ev->sequence); + + return ret; +} + bool GtkCanvas::on_enter_notify_event (GdkEventCrossing* ev) { diff --git a/libs/canvas/canvas/canvas.h b/libs/canvas/canvas/canvas.h index 2b05c92cf0..3eec1e5ddb 100644 --- a/libs/canvas/canvas/canvas.h +++ b/libs/canvas/canvas/canvas.h @@ -269,6 +269,9 @@ protected: bool on_button_press_event (GdkEventButton *); bool on_button_release_event (GdkEventButton* event); bool on_motion_notify_event (GdkEventMotion *); + bool on_touch_begin_event (GdkEventTouch *); + bool on_touch_update_event (GdkEventTouch* event); + bool on_touch_end_event (GdkEventTouch *); bool on_enter_notify_event (GdkEventCrossing*); bool on_leave_notify_event (GdkEventCrossing*); void on_style_changed (const Glib::RefPtr&); @@ -283,6 +286,8 @@ protected: bool deliver_event (GdkEvent *); void deliver_enter_leave (Duple const & point, int state); + void get_items_enclosing (Duple const& point, std::list& enclosing_items); + void pick_current_item (int state); void pick_current_item (Duple const &, int state); @@ -300,6 +305,9 @@ private: /** the item that currently has key focus or 0 */ Item * _focused_item; + /** for multitouch: the item(s) that have been touched (effectively a GRAB) */ + std::map _touched_item; + bool _single_exposure; bool _use_image_surface;