[vte] widget: Add wrapper class for mouse events



commit 477c45bcbec34841dda0ee63b58aebe37c356b93
Author: Christian Persch <chpe src gnome org>
Date:   Fri May 1 23:08:42 2020 +0200

    widget: Add wrapper class for mouse events
    
    Extract all information from the GdkEvent{Button,Crossing,Scroll},
    put them into a wrapper class, and pass that down to Terminal,
    instead of the native event.

 src/vte.cc         | 269 ++++++++++++++++++++++-------------------------------
 src/vtegtk.cc      |  16 ++--
 src/vteinternal.hh | 232 ++++++++++++++++++++++++++++++++++++---------
 src/widget.cc      |  56 +++++++++--
 src/widget.hh      |  49 ++++++++--
 5 files changed, 396 insertions(+), 226 deletions(-)
---
diff --git a/src/vte.cc b/src/vte.cc
index 740ad3ab..84b5044e 100644
--- a/src/vte.cc
+++ b/src/vte.cc
@@ -1437,7 +1437,7 @@ Terminal::regex_match_check(vte::grid::column_t column,
 
 /*
  * Terminal::view_coords_from_event:
- * @event: a #GdkEvent
+ * @event: a mouse event
  *
  * Translates the event coordinates to view coordinates, by
  * subtracting the padding and window offset.
@@ -1446,15 +1446,9 @@ Terminal::regex_match_check(vte::grid::column_t column,
  * at that side; use view_coords_visible() to check for that.
  */
 vte::view::coords
-Terminal::view_coords_from_event(GdkEvent const* event) const
+Terminal::view_coords_from_event(MouseEvent const& event) const
 {
-        double x, y;
-        if (event == nullptr ||
-            ((reinterpret_cast<GdkEventAny const*>(event))->window != m_real_widget->event_window()) ||
-            !gdk_event_get_coords(event, &x, &y))
-                return vte::view::coords(-1, -1);
-
-        return vte::view::coords(x - m_padding.left, y - m_padding.top);
+        return vte::view::coords(event.x() - m_padding.left, event.y() - m_padding.top);
 }
 
 bool
@@ -1465,7 +1459,7 @@ Terminal::widget_realized() const noexcept
 
 /*
  * Terminal::grid_coords_from_event:
- * @event: a #GdkEvent
+ * @event: a mouse event
  *
  * Translates the event coordinates to view coordinates, by
  * subtracting the padding and window offset.
@@ -1474,20 +1468,20 @@ Terminal::widget_realized() const noexcept
  * at that side; use grid_coords_visible() to check for that.
  */
 vte::grid::coords
-Terminal::grid_coords_from_event(GdkEvent const* event) const
+Terminal::grid_coords_from_event(MouseEvent const& event) const
 {
         return grid_coords_from_view_coords(view_coords_from_event(event));
 }
 
 /*
  * Terminal::confined_grid_coords_from_event:
- * @event: a #GdkEvent
+ * @event: a mouse event
  *
  * Like grid_coords_from_event(), but also confines the coordinates
  * to an actual cell in the visible area.
  */
 vte::grid::coords
-Terminal::confined_grid_coords_from_event(GdkEvent const* event) const
+Terminal::confined_grid_coords_from_event(MouseEvent const& event) const
 {
         auto pos = view_coords_from_event(event);
         return confined_grid_coords_from_view_coords(pos);
@@ -1688,9 +1682,9 @@ Terminal::selection_maybe_swap_endpoints(vte::view::coords const& pos)
 }
 
 bool
-Terminal::rowcol_from_event(GdkEvent *event,
-                                      long *column,
-                                      long *row)
+Terminal::rowcol_from_event(MouseEvent const& event,
+                            long *column,
+                            long *row)
 {
         auto rowcol = grid_coords_from_event(event);
         if (!grid_coords_visible(rowcol))
@@ -1702,7 +1696,7 @@ Terminal::rowcol_from_event(GdkEvent *event,
 }
 
 char *
-Terminal::hyperlink_check(GdkEvent *event)
+Terminal::hyperlink_check(MouseEvent const& event)
 {
         long col, row;
         const char *hyperlink;
@@ -1734,7 +1728,7 @@ Terminal::hyperlink_check(GdkEvent *event)
 }
 
 char *
-Terminal::regex_match_check(GdkEvent* event,
+Terminal::regex_match_check(MouseEvent const& event,
                             int *tag)
 {
         long col, row;
@@ -1751,7 +1745,7 @@ Terminal::regex_match_check(GdkEvent* event,
 }
 
 bool
-Terminal::regex_match_check_extra(GdkEvent *event,
+Terminal::regex_match_check_extra(MouseEvent const& event,
                                   vte::base::Regex const** regexes,
                                   size_t n_regexes,
                                   uint32_t match_flags,
@@ -1764,7 +1758,6 @@ Terminal::regex_match_check_extra(GdkEvent *event,
         long col, row;
         guint i;
 
-        assert(event);
         assert(regexes != nullptr || n_regexes == 0);
         assert(matches != nullptr);
 
@@ -4495,12 +4488,12 @@ Terminal::beep()
 }
 
 unsigned
-Terminal::translate_ctrlkey(vte::terminal::KeyEvent const& event) const noexcept
+Terminal::translate_ctrlkey(KeyEvent const& event) const noexcept
 {
        if (event.keyval() < 128)
                return event.keyval();
 
-        auto display = gdk_window_get_display(gdk_event_get_window((GdkEvent*)event.platform_event()));
+        auto display = gdk_window_get_display(gdk_event_get_window(event.platform_event()));
         auto keymap = gdk_keymap_get_for_display(display);
         auto keyval = unsigned{event.keyval()};
 
@@ -4523,33 +4516,8 @@ Terminal::translate_ctrlkey(vte::terminal::KeyEvent const& event) const noexcept
         return keyval;
 }
 
-void
-Terminal::read_modifiers(GdkEvent *event)
-{
-        GdkKeymap *keymap;
-       GdkModifierType mods;
-        guint mask;
-
-       /* Read the modifiers. */
-       if (!gdk_event_get_state((GdkEvent*)event, &mods))
-                return;
-
-        keymap = gdk_keymap_get_for_display(gdk_window_get_display(((GdkEventAny*)event)->window));
-
-        gdk_keymap_add_virtual_modifiers (keymap, &mods);
-
-        mask = (guint)mods;
-#if 1
-        /* HACK! Treat ALT as ALT; see bug #663779. */
-        if (mask & GDK_MOD1_MASK)
-                mask |= VTE_ALT_MASK;
-#endif
-
-        m_modifiers = mask;
-}
-
 bool
-Terminal::widget_key_press(vte::terminal::KeyEvent const& event)
+Terminal::widget_key_press(KeyEvent const& event)
 {
        char *normal = NULL;
        gsize normal_length = 0;
@@ -5012,7 +4980,7 @@ Terminal::widget_key_press(vte::terminal::KeyEvent const& event)
 }
 
 bool
-Terminal::widget_key_release(vte::terminal::KeyEvent const& event)
+Terminal::widget_key_release(KeyEvent const& event)
 {
         m_modifiers = event.modifiers();
 
@@ -5673,31 +5641,30 @@ Terminal::maybe_feed_focus_event(bool in)
  */
 bool
 Terminal::maybe_send_mouse_button(vte::grid::coords const& unconfined_rowcol,
-                                            GdkEventType event_type,
-                                            int event_button)
+                                  MouseEvent const& event)
 {
-       switch (event_type) {
-       case GDK_BUTTON_PRESS:
+       switch (event.type()) {
+        case EventBase::Type::eMOUSE_PRESS:
                if (m_mouse_tracking_mode < MouseTrackingMode::eSEND_XY_ON_CLICK) {
                        return false;
                }
                break;
-       case GDK_BUTTON_RELEASE: {
+        case EventBase::Type::eMOUSE_RELEASE:
                if (m_mouse_tracking_mode < MouseTrackingMode::eSEND_XY_ON_BUTTON) {
                        return false;
                }
                break;
-       }
+        case EventBase::Type::eMOUSE_DOUBLE_PRESS:
+        case EventBase::Type::eMOUSE_TRIPLE_PRESS:
        default:
                return false;
-               break;
        }
 
         auto rowcol = confine_grid_coords(unconfined_rowcol);
         return feed_mouse_event(rowcol,
-                                event_button,
+                                event.button_value(),
                                 false /* not drag */,
-                                event_type == GDK_BUTTON_RELEASE);
+                                event.is_mouse_release());
 }
 
 /*
@@ -5712,7 +5679,7 @@ Terminal::maybe_send_mouse_button(vte::grid::coords const& unconfined_rowcol,
  */
 bool
 Terminal::maybe_send_mouse_drag(vte::grid::coords const& unconfined_rowcol,
-                                          GdkEventType event_type)
+                                MouseEvent const& event)
 {
         /* Need to ensure the ringview is updated. */
         ringview_update();
@@ -5720,8 +5687,8 @@ Terminal::maybe_send_mouse_drag(vte::grid::coords const& unconfined_rowcol,
         auto rowcol = confine_grid_coords(unconfined_rowcol);
 
        /* First determine if we even want to send notification. */
-       switch (event_type) {
-       case GDK_MOTION_NOTIFY:
+        switch (event.type()) {
+        case EventBase::Type::eMOUSE_MOTION:
                if (m_mouse_tracking_mode < MouseTrackingMode::eCELL_MOTION_TRACKING)
                        return false;
 
@@ -5740,7 +5707,6 @@ Terminal::maybe_send_mouse_drag(vte::grid::coords const& unconfined_rowcol,
                break;
        default:
                return false;
-               break;
        }
 
         /* As per xterm, report the leftmost pressed button - if any. */
@@ -6856,58 +6822,50 @@ Terminal::start_autoscroll()
 }
 
 bool
-Terminal::widget_motion_notify(GdkEventMotion *event)
+Terminal::widget_mouse_motion(MouseEvent const& event)
 {
-       bool handled = false;
-
         /* Need to ensure the ringview is updated. */
         ringview_update();
 
-        GdkEvent* base_event = reinterpret_cast<GdkEvent*>(event);
-        auto pos = view_coords_from_event(base_event);
+        auto pos = view_coords_from_event(event);
         auto rowcol = grid_coords_from_view_coords(pos);
 
        _vte_debug_print(VTE_DEBUG_EVENTS,
                          "Motion notify %s %s\n",
                          pos.to_string(), rowcol.to_string());
 
-       read_modifiers(base_event);
+        m_modifiers = event.modifiers();
 
-       switch (event->type) {
-       case GDK_MOTION_NOTIFY:
-                if (m_will_select_after_threshold) {
-                       if (!gtk_drag_check_threshold (m_widget,
-                                                      m_mouse_last_position.x,
-                                                      m_mouse_last_position.y,
-                                                      pos.x, pos.y))
-                               return true;
+        if (m_will_select_after_threshold) {
+                if (!gtk_drag_check_threshold(m_widget,
+                                              m_mouse_last_position.x,
+                                              m_mouse_last_position.y,
+                                              pos.x, pos.y))
+                        return true;
 
-                        start_selection(vte::view::coords(m_mouse_last_position.x, m_mouse_last_position.y),
-                                        SelectionType::eCHAR);
-               }
+                start_selection(vte::view::coords(m_mouse_last_position.x, m_mouse_last_position.y),
+                                SelectionType::eCHAR);
+        }
 
-               if (m_selecting &&
-                    (m_mouse_handled_buttons & 1) != 0) {
-                       _vte_debug_print(VTE_DEBUG_EVENTS, "Mousing drag 1.\n");
-                        modify_selection(pos);
-
-                       /* Start scrolling if we need to. */
-                       if (pos.y < 0 || pos.y >= m_view_usable_extents.height()) {
-                               /* Give mouse wigglers something. */
-                                stop_autoscroll();
-                               mouse_autoscroll_timer_callback();
-                               start_autoscroll();
-                       }
+        auto handled = bool{false};
+        if (m_selecting &&
+            (m_mouse_handled_buttons & 1) != 0) {
+                _vte_debug_print(VTE_DEBUG_EVENTS, "Mousing drag 1.\n");
+                modify_selection(pos);
+
+                /* Start scrolling if we need to. */
+                if (pos.y < 0 || pos.y >= m_view_usable_extents.height()) {
+                        /* Give mouse wigglers something. */
+                        stop_autoscroll();
+                        mouse_autoscroll_timer_callback();
+                        start_autoscroll();
+                }
 
-                       handled = true;
-               }
+                handled = true;
+        }
 
-               if (!handled && m_input_enabled)
-                       maybe_send_mouse_drag(rowcol, event->type);
-               break;
-       default:
-               break;
-       }
+        if (!handled && m_input_enabled)
+                maybe_send_mouse_drag(rowcol, event);
 
         if (pos != m_mouse_last_position) {
                 m_mouse_last_position = pos;
@@ -6921,7 +6879,7 @@ Terminal::widget_motion_notify(GdkEventMotion *event)
 }
 
 bool
-Terminal::widget_button_press(GdkEventButton *event)
+Terminal::widget_mouse_press(MouseEvent const& event)
 {
        bool handled = false;
        gboolean start_selecting = FALSE, extend_selecting = FALSE;
@@ -6929,21 +6887,20 @@ Terminal::widget_button_press(GdkEventButton *event)
         /* Need to ensure the ringview is updated. */
         ringview_update();
 
-        GdkEvent* base_event = reinterpret_cast<GdkEvent*>(event);
-        auto pos = view_coords_from_event(base_event);
+        auto pos = view_coords_from_event(event);
         auto rowcol = grid_coords_from_view_coords(pos);
 
-       read_modifiers(base_event);
+        m_modifiers = event.modifiers();
 
-       switch (event->type) {
-       case GDK_BUTTON_PRESS:
+        switch (event.type()) {
+        case EventBase::Type::eMOUSE_PRESS:
                _vte_debug_print(VTE_DEBUG_EVENTS,
                                  "Button %d single-click at %s\n",
-                                 event->button,
+                                 event.button_value(),
                                  rowcol.to_string());
                /* Handle this event ourselves. */
-               switch (event->button) {
-               case 1:
+                switch (event.button()) {
+                case MouseEvent::Button::eLEFT:
                        _vte_debug_print(VTE_DEBUG_EVENTS,
                                        "Handling click ourselves.\n");
                        /* Grab focus. */
@@ -6985,7 +6942,7 @@ Terminal::widget_button_press(GdkEventButton *event)
                        break;
                /* Paste if the user pressed shift or we're not sending events
                 * to the app. */
-               case 2:
+                case MouseEvent::Button::eMIDDLE:
                        if ((m_modifiers & GDK_SHIFT_MASK) ||
                            m_mouse_tracking_mode == MouseTrackingMode::eNONE) {
                                 gboolean do_paste;
@@ -6998,29 +6955,29 @@ Terminal::widget_button_press(GdkEventButton *event)
                                handled = do_paste;
                        }
                        break;
-               case 3:
+                case MouseEvent::Button::eRIGHT:
                default:
                        break;
                }
-                if (event->button >= 1 && event->button <= 3) {
+                if (event.button_value() >= 1 && event.button_value() <= 3) {
                         if (handled)
-                                m_mouse_handled_buttons |= (1 << (event->button - 1));
+                                m_mouse_handled_buttons |= (1 << (event.button_value() - 1));
                         else
-                                m_mouse_handled_buttons &= ~(1 << (event->button - 1));
+                                m_mouse_handled_buttons &= ~(1 << (event.button_value() - 1));
                 }
                /* If we haven't done anything yet, try sending the mouse
                 * event to the app. */
                if (handled == FALSE) {
-                       handled = maybe_send_mouse_button(rowcol, event->type, event->button);
+                        handled = maybe_send_mouse_button(rowcol, event);
                }
                break;
-       case GDK_2BUTTON_PRESS:
+        case EventBase::Type::eMOUSE_DOUBLE_PRESS:
                _vte_debug_print(VTE_DEBUG_EVENTS,
                                  "Button %d double-click at %s\n",
-                                 event->button,
+                                 event.button_value(),
                                  rowcol.to_string());
-               switch (event->button) {
-               case 1:
+                switch (event.button()) {
+                case MouseEvent::Button::eLEFT:
                         if (m_will_select_after_threshold) {
                                 start_selection(pos,
                                                 SelectionType::eCHAR);
@@ -7032,27 +6989,27 @@ Terminal::widget_button_press(GdkEventButton *event)
                                handled = true;
                        }
                        break;
-               case 2:
-               case 3:
+                case MouseEvent::Button::eMIDDLE:
+                case MouseEvent::Button::eRIGHT:
                default:
                        break;
                }
                break;
-       case GDK_3BUTTON_PRESS:
+        case EventBase::Type::eMOUSE_TRIPLE_PRESS:
                _vte_debug_print(VTE_DEBUG_EVENTS,
                                  "Button %d triple-click at %s\n",
-                                 event->button,
+                                 event.button_value(),
                                  rowcol.to_string());
-               switch (event->button) {
-               case 1:
+                switch (event.button()) {
+                case MouseEvent::Button::eLEFT:
                         if ((m_mouse_handled_buttons & 1) != 0) {
                                 start_selection(pos,
                                                 SelectionType::eLINE);
                                handled = true;
                        }
                        break;
-               case 2:
-               case 3:
+                case MouseEvent::Button::eMIDDLE:
+                case MouseEvent::Button::eRIGHT:
                default:
                        break;
                }
@@ -7061,8 +7018,8 @@ Terminal::widget_button_press(GdkEventButton *event)
        }
 
        /* Save the pointer state for later use. */
-        if (event->button >= 1 && event->button <= 3)
-                m_mouse_pressed_buttons |= (1 << (event->button - 1));
+        if (event.button_value() >= 1 && event.button_value() <= 3)
+                m_mouse_pressed_buttons |= (1 << (event.button_value() - 1));
 
        m_mouse_last_position = pos;
 
@@ -7074,41 +7031,40 @@ Terminal::widget_button_press(GdkEventButton *event)
 }
 
 bool
-Terminal::widget_button_release(GdkEventButton *event)
+Terminal::widget_mouse_release(MouseEvent const& event)
 {
        bool handled = false;
 
         /* Need to ensure the ringview is updated. */
         ringview_update();
 
-        GdkEvent* base_event = reinterpret_cast<GdkEvent*>(event);
-        auto pos = view_coords_from_event(base_event);
+        auto pos = view_coords_from_event(event);
         auto rowcol = grid_coords_from_view_coords(pos);
 
        stop_autoscroll();
 
-       read_modifiers(base_event);
+        m_modifiers = event.modifiers();
 
-       switch (event->type) {
-       case GDK_BUTTON_RELEASE:
+        switch (event.type()) {
+        case EventBase::Type::eMOUSE_RELEASE:
                _vte_debug_print(VTE_DEBUG_EVENTS,
                                  "Button %d released at %s\n",
-                                 event->button, rowcol.to_string());
-               switch (event->button) {
-               case 1:
+                                 event.button_value(), rowcol.to_string());
+                switch (event.button()) {
+                case MouseEvent::Button::eLEFT:
                         if ((m_mouse_handled_buttons & 1) != 0)
                                 handled = maybe_end_selection();
                        break;
-               case 2:
+                case MouseEvent::Button::eMIDDLE:
                         handled = (m_mouse_handled_buttons & 2) != 0;
                         m_mouse_handled_buttons &= ~2;
                        break;
-               case 3:
+                case MouseEvent::Button::eRIGHT:
                default:
                        break;
                }
                if (!handled && m_input_enabled) {
-                       handled = maybe_send_mouse_button(rowcol, event->type, event->button);
+                        handled = maybe_send_mouse_button(rowcol, event);
                }
                break;
        default:
@@ -7116,8 +7072,8 @@ Terminal::widget_button_release(GdkEventButton *event)
        }
 
        /* Save the pointer state for later use. */
-        if (event->button >= 1 && event->button <= 3)
-                m_mouse_pressed_buttons &= ~(1 << (event->button - 1));
+        if (event.button_value() >= 1 && event.button_value() <= 3)
+                m_mouse_pressed_buttons &= ~(1 << (event.button_value() - 1));
 
        m_mouse_last_position = pos;
         m_will_select_after_threshold = false;
@@ -7190,10 +7146,9 @@ Terminal::widget_focus_out(GdkEventFocus *event)
 }
 
 void
-Terminal::widget_enter(GdkEventCrossing *event)
+Terminal::widget_mouse_enter(MouseEvent const& event)
 {
-        GdkEvent* base_event = reinterpret_cast<GdkEvent*>(event);
-        auto pos = view_coords_from_event(base_event);
+        auto pos = view_coords_from_event(event);
 
         // FIXMEchpe read event modifiers here
 
@@ -7209,10 +7164,9 @@ Terminal::widget_enter(GdkEventCrossing *event)
 }
 
 void
-Terminal::widget_leave(GdkEventCrossing *event)
+Terminal::widget_mouse_leave(MouseEvent const& event)
 {
-        GdkEvent* base_event = reinterpret_cast<GdkEvent*>(event);
-        auto pos = view_coords_from_event(base_event);
+        auto pos = view_coords_from_event(event);
 
         // FIXMEchpe read event modifiers here
 
@@ -9432,9 +9386,8 @@ vte_cairo_get_clip_region (cairo_t *cr)
 }
 
 void
-Terminal::widget_scroll(GdkEventScroll *event)
+Terminal::widget_mouse_scroll(MouseEvent const& event)
 {
-       gdouble delta_x, delta_y;
        gdouble v;
        gint cnt, i;
        int button;
@@ -9442,27 +9395,27 @@ Terminal::widget_scroll(GdkEventScroll *event)
         /* Need to ensure the ringview is updated. */
         ringview_update();
 
-        GdkEvent *base_event = reinterpret_cast<GdkEvent*>(event);
-        auto rowcol = confined_grid_coords_from_event(base_event);
+        auto rowcol = confined_grid_coords_from_event(event);
 
-       read_modifiers(base_event);
+        m_modifiers = event.modifiers();
 
-       switch (event->direction) {
-       case GDK_SCROLL_UP:
+        switch (event.scroll_direction()) {
+        case MouseEvent::ScrollDirection::eUP:
                m_mouse_smooth_scroll_delta -= 1.;
                _vte_debug_print(VTE_DEBUG_EVENTS, "Scroll up\n");
                break;
-       case GDK_SCROLL_DOWN:
+        case MouseEvent::ScrollDirection::eDOWN:
                m_mouse_smooth_scroll_delta += 1.;
                _vte_debug_print(VTE_DEBUG_EVENTS, "Scroll down\n");
                break;
-       case GDK_SCROLL_SMOOTH:
-               gdk_event_get_scroll_deltas ((GdkEvent*) event, &delta_x, &delta_y);
+        case MouseEvent::ScrollDirection::eSMOOTH: {
+                auto const delta_y = event.scroll_delta_y();
                m_mouse_smooth_scroll_delta += delta_y;
                _vte_debug_print(VTE_DEBUG_EVENTS,
                                "Smooth scroll by %f, delta now at %f\n",
                                delta_y, m_mouse_smooth_scroll_delta);
                break;
+        }
        default:
                break;
        }
diff --git a/src/vtegtk.cc b/src/vtegtk.cc
index e9b568d6..a7b0390a 100644
--- a/src/vtegtk.cc
+++ b/src/vtegtk.cc
@@ -2207,7 +2207,7 @@ vte_terminal_match_check(VteTerminal *terminal,
                         int *tag)
 {
         g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL);
-        return IMPL(terminal)->regex_match_check(column, row, tag);
+        return WIDGET(terminal)->regex_match_check(column, row, tag);
 }
 
 
@@ -2235,7 +2235,7 @@ vte_terminal_match_check_event(VteTerminal *terminal,
                                int *tag)
 {
         g_return_val_if_fail(VTE_IS_TERMINAL(terminal), FALSE);
-        return IMPL(terminal)->regex_match_check(event, tag);
+        return WIDGET(terminal)->regex_match_check(event, tag);
 }
 
 /**
@@ -2260,7 +2260,7 @@ vte_terminal_hyperlink_check_event(VteTerminal *terminal,
                                    GdkEvent *event)
 {
         g_return_val_if_fail(VTE_IS_TERMINAL(terminal), FALSE);
-        return IMPL(terminal)->hyperlink_check(event);
+        return WIDGET(terminal)->hyperlink_check(event);
 }
 
 /**
@@ -2348,11 +2348,11 @@ vte_terminal_event_check_regex_simple(VteTerminal *terminal,
         }
         g_return_val_if_fail(matches != NULL, FALSE);
 
-        return IMPL(terminal)->regex_match_check_extra(event,
-                                                       regex_array_from_wrappers(regexes),
-                                                       n_regexes,
-                                                       match_flags,
-                                                       matches);
+        return WIDGET(terminal)->regex_match_check_extra(event,
+                                                         regex_array_from_wrappers(regexes),
+                                                         n_regexes,
+                                                         match_flags,
+                                                         matches);
 }
 
 /**
diff --git a/src/vteinternal.hh b/src/vteinternal.hh
index e238fb32..91a2b7c5 100644
--- a/src/vteinternal.hh
+++ b/src/vteinternal.hh
@@ -267,40 +267,83 @@ class Widget;
 
 namespace terminal {
 
-class KeyEvent {
+class EventBase {
         friend class vte::platform::Widget;
         friend class Terminal;
 
-protected:
-
+public:
         enum class Type {
-                ePRESS,
-                eRELEASE,
+                eKEY_PRESS,
+                eKEY_RELEASE,
+                eMOUSE_DOUBLE_PRESS,
+                eMOUSE_ENTER,
+                eMOUSE_LEAVE,
+                eMOUSE_MOTION,
+                eMOUSE_PRESS,
+                eMOUSE_RELEASE,
+                eMOUSE_SCROLL,
+                eMOUSE_TRIPLE_PRESS,
         };
 
+protected:
+
+        EventBase() noexcept = default;
+
+        constexpr EventBase(GdkEvent* gdk_event,
+                            Type type,
+                            unsigned timestamp) noexcept
+                : m_platform_event{gdk_event},
+                  m_type{type},
+                  m_timestamp{timestamp}
+        {
+        }
+
+        constexpr auto platform_event() const noexcept { return m_platform_event; }
+
+public:
+        ~EventBase() noexcept = default;
+
+        EventBase(EventBase const&) = default;
+        EventBase(EventBase&&) = default;
+        EventBase& operator=(EventBase const&) = delete;
+        EventBase& operator=(EventBase&&) = delete;
+
+        constexpr auto const timestamp()   const noexcept { return m_timestamp;   }
+        constexpr auto const type()        const noexcept { return m_type;        }
+
+private:
+        GdkEvent* m_platform_event;
+        Type m_type;
+        unsigned m_timestamp;
+}; // class EventBase
+
+class KeyEvent : public EventBase {
+        friend class vte::platform::Widget;
+        friend class Terminal;
+
+protected:
+
         KeyEvent() noexcept = default;
 
-        constexpr KeyEvent(Type type,
-                           unsigned int modifiers,
-                           unsigned int keyval,
-                           unsigned int keycode,
+        constexpr KeyEvent(GdkEvent* gdk_event,
+                           Type type,
+                           unsigned timestamp,
+                           unsigned modifiers,
+                           unsigned keyval,
+                           unsigned keycode,
                            uint8_t group,
-                           unsigned int timestamp,
-                           bool is_modifier,
-                           GdkEventKey* gdk_event) noexcept
-                : m_type{type},
+                           bool is_modifier) noexcept
+                : EventBase{gdk_event,
+                            type,
+                            timestamp},
                   m_modifiers{modifiers},
                   m_keyval{keyval},
                   m_keycode{keycode},
                   m_group{group},
-                  m_timestamp{timestamp},
-                  m_is_modifier{is_modifier},
-                  m_platform_event{gdk_event}
+                  m_is_modifier{is_modifier}
         {
         }
 
-        constexpr auto platform_event() const noexcept { return m_platform_event; }
-
 public:
         ~KeyEvent() noexcept = default;
 
@@ -314,23 +357,124 @@ public:
         constexpr auto const keycode()     const noexcept { return m_keycode;     }
         constexpr auto const keyval()      const noexcept { return m_keyval;      }
         constexpr auto const modifiers()   const noexcept { return m_modifiers;   }
-        constexpr auto const timestamp()   const noexcept { return m_timestamp;   }
-        constexpr auto const type()        const noexcept { return m_type;        }
 
-        constexpr auto const is_key_press()   const noexcept { return type() == Type::ePRESS;   }
-        constexpr auto const is_key_release() const noexcept { return type() == Type::eRELEASE; }
+        constexpr auto const is_key_press()   const noexcept { return type() == Type::eKEY_PRESS;   }
+        constexpr auto const is_key_release() const noexcept { return type() == Type::eKEY_RELEASE; }
 
 private:
-        Type m_type;
         unsigned m_modifiers;
         unsigned m_keyval;
         unsigned m_keycode;
         uint8_t m_group;
-        unsigned m_timestamp;
         bool m_is_modifier;
-        GdkEventKey* m_platform_event;
 }; // class KeyEvent
 
+class MouseEvent : public EventBase {
+        friend class vte::platform::Widget;
+        friend class Terminal;
+
+public:
+        enum class Button {
+                eNONE   = 0,
+                eLEFT   = 1,
+                eMIDDLE = 2,
+                eRIGHT  = 3,
+                eFOURTH = 4,
+                eFIFTH  = 5,
+        };
+
+        enum class ScrollDirection {
+                eUP,
+                eDOWN,
+                eLEFT,
+                eRIGHT,
+                eSMOOTH,
+                eNONE,
+        };
+
+protected:
+
+        MouseEvent() noexcept = default;
+
+        constexpr MouseEvent(GdkEvent* gdk_event,
+                             Type type,
+                             unsigned timestamp,
+                             unsigned modifiers,
+                             Button button,
+                             double x,
+                             double y) noexcept
+                : EventBase{gdk_event,
+                            type,
+                            timestamp},
+                  m_modifiers{modifiers},
+                  m_button{button},
+                  m_x{x},
+                  m_y{y}
+        {
+        }
+
+public:
+        ~MouseEvent() noexcept = default;
+
+        MouseEvent(MouseEvent const&) = default;
+        MouseEvent(MouseEvent&&) = default;
+        MouseEvent& operator=(MouseEvent const&) = delete;
+        MouseEvent& operator=(MouseEvent&&) = delete;
+
+        constexpr auto const button()       const noexcept { return m_button;           }
+        constexpr auto const button_value() const noexcept { return unsigned(m_button); }
+        constexpr auto const modifiers()    const noexcept { return m_modifiers;        }
+        constexpr auto const x()            const noexcept { return m_x;                }
+        constexpr auto const y()            const noexcept { return m_y;                }
+
+        constexpr auto const is_mouse_double_press() const noexcept { return type() == 
Type::eMOUSE_DOUBLE_PRESS; }
+        constexpr auto const is_mouse_enter()        const noexcept { return type() == Type::eMOUSE_ENTER;   
     }
+        constexpr auto const is_mouse_leave()        const noexcept { return type() == Type::eMOUSE_LEAVE;   
     }
+        constexpr auto const is_mouse_motion()       const noexcept { return type() == Type::eMOUSE_MOTION;  
     }
+        constexpr auto const is_mouse_press()        const noexcept { return type() == Type::eMOUSE_PRESS;   
   }
+        constexpr auto const is_mouse_release()      const noexcept { return type() == Type::eMOUSE_RELEASE; 
     }
+        constexpr auto const is_mouse_scroll()       const noexcept { return type() == Type::eMOUSE_SCROLL;  
     }
+        constexpr auto const is_mouse_single_press() const noexcept { return type() == Type::eMOUSE_PRESS;   
     }
+        constexpr auto const is_mouse_triple_press() const noexcept { return type() == 
Type::eMOUSE_TRIPLE_PRESS; }
+
+        ScrollDirection scroll_direction() const noexcept
+        {
+                /* Note that we cannot use gdk_event_get_scroll_direction() here since it
+                 * returns false for smooth scroll events.
+                 */
+                if (!is_mouse_scroll())
+                        return ScrollDirection::eNONE;
+                switch (reinterpret_cast<GdkEventScroll*>(platform_event())->direction) {
+                case GDK_SCROLL_UP:     return ScrollDirection::eUP;
+                case GDK_SCROLL_DOWN:   return ScrollDirection::eDOWN;
+                case GDK_SCROLL_LEFT:   return ScrollDirection::eLEFT;
+                case GDK_SCROLL_RIGHT:  return ScrollDirection::eRIGHT;
+                case GDK_SCROLL_SMOOTH: return ScrollDirection::eSMOOTH;
+                default: return ScrollDirection::eNONE;
+                }
+        }
+
+        auto scroll_delta_x() const noexcept
+        {
+                auto delta = double{0.};
+                gdk_event_get_scroll_deltas(platform_event(), &delta, nullptr);
+                return delta;
+        }
+
+        auto scroll_delta_y() const noexcept
+        {
+                auto delta = double{0.};
+                gdk_event_get_scroll_deltas(platform_event(), nullptr, &delta);
+                return delta;
+        }
+
+private:
+        unsigned m_modifiers;
+        Button m_button;
+        double m_x;
+        double m_y;
+}; // class MouseEvent
+
 class Terminal {
         friend class vte::platform::Widget;
 
@@ -946,8 +1090,8 @@ public:
         inline vte::view::coord_t get_allocated_width() const { return m_allocated_rect.width; }
         inline vte::view::coord_t get_allocated_height() const { return m_allocated_rect.height; }
 
-        vte::view::coords view_coords_from_event(GdkEvent const* event) const;
-        vte::grid::coords grid_coords_from_event(GdkEvent const* event) const;
+        vte::view::coords view_coords_from_event(MouseEvent const& event) const;
+        vte::grid::coords grid_coords_from_event(MouseEvent const& event) const;
 
         vte::view::coords view_coords_from_grid_coords(vte::grid::coords const& rowcol) const;
         vte::grid::coords grid_coords_from_view_coords(vte::view::coords const& pos) const;
@@ -960,7 +1104,7 @@ public:
 
         vte::grid::row_t confine_grid_row(vte::grid::row_t const& row) const;
         vte::grid::coords confine_grid_coords(vte::grid::coords const& rowcol) const;
-        vte::grid::coords confined_grid_coords_from_event(GdkEvent const* event) const;
+        vte::grid::coords confined_grid_coords_from_event(MouseEvent const&) const;
         vte::grid::coords confined_grid_coords_from_view_coords(vte::view::coords const& pos) const;
 
         void confine_coordinates(long *xp,
@@ -984,14 +1128,14 @@ public:
         void widget_style_updated();
         void widget_focus_in(GdkEventFocus *event);
         void widget_focus_out(GdkEventFocus *event);
-        bool widget_key_press(vte::terminal::KeyEvent const& event);
-        bool widget_key_release(vte::terminal::KeyEvent const& event);
-        bool widget_button_press(GdkEventButton *event);
-        bool widget_button_release(GdkEventButton *event);
-        void widget_enter(GdkEventCrossing *event);
-        void widget_leave(GdkEventCrossing *event);
-        void widget_scroll(GdkEventScroll *event);
-        bool widget_motion_notify(GdkEventMotion *event);
+        bool widget_key_press(KeyEvent const& event);
+        bool widget_key_release(KeyEvent const& event);
+        bool widget_mouse_motion(MouseEvent const& event);
+        bool widget_mouse_press(MouseEvent const& event);
+        bool widget_mouse_release(MouseEvent const& event);
+        void widget_mouse_enter(MouseEvent const& event);
+        void widget_mouse_leave(MouseEvent const& event);
+        void widget_mouse_scroll(MouseEvent const& event);
         void widget_draw(cairo_t *cr);
         void widget_get_preferred_width(int *minimum_width,
                                         int *natural_width);
@@ -1173,8 +1317,7 @@ public:
 
         void vadjustment_value_changed();
 
-        void read_modifiers(GdkEvent *event);
-        unsigned translate_ctrlkey(vte::terminal::KeyEvent const& event) const noexcept;
+        unsigned translate_ctrlkey(KeyEvent const& event) const noexcept;
 
         void apply_mouse_cursor();
         void set_pointer_autohidden(bool autohidden);
@@ -1228,13 +1371,13 @@ public:
         void match_hilite_clear();
         void match_hilite_update();
 
-        bool rowcol_from_event(GdkEvent *event,
+        bool rowcol_from_event(MouseEvent const& event,
                                long *column,
                                long *row);
 
-        char *hyperlink_check(GdkEvent *event);
+        char *hyperlink_check(MouseEvent const& event);
 
-        bool regex_match_check_extra(GdkEvent* event,
+        bool regex_match_check_extra(MouseEvent const& event,
                                      vte::base::Regex const** regexes,
                                      size_t n_regexes,
                                      uint32_t match_flags,
@@ -1243,7 +1386,7 @@ public:
         char *regex_match_check(vte::grid::column_t column,
                                 vte::grid::row_t row,
                                 int *tag);
-        char *regex_match_check(GdkEvent *event,
+        char *regex_match_check(MouseEvent const& event,
                                 int *tag);
         void regex_match_remove(int tag) noexcept;
         void regex_match_remove_all() noexcept;
@@ -1289,10 +1432,9 @@ public:
                               bool is_drag,
                               bool is_release);
         bool maybe_send_mouse_button(vte::grid::coords const& rowcol,
-                                     GdkEventType event_type,
-                                     int event_button);
+                                     MouseEvent const& event);
         bool maybe_send_mouse_drag(vte::grid::coords const& rowcol,
-                                   GdkEventType event_type);
+                                   MouseEvent const& event);
 
         void feed_focus_event(bool in);
         void feed_focus_event_initial();
diff --git a/src/widget.cc b/src/widget.cc
index 0a67a7bd..a23fe255 100644
--- a/src/widget.cc
+++ b/src/widget.cc
@@ -205,7 +205,8 @@ Widget::im_filter_keypress(vte::terminal::KeyEvent const& event) noexcept
 {
         // FIXMEchpe this can only be called when realized, so the m_im_context check is redundant
         return m_im_context &&
-                gtk_im_context_filter_keypress(m_im_context.get(), event.platform_event());
+                gtk_im_context_filter_keypress(m_im_context.get(),
+                                               reinterpret_cast<GdkEventKey*>(event.platform_event()));
 }
 
 void
@@ -264,21 +265,58 @@ Widget::read_modifiers_from_gdk(GdkEvent* event) const noexcept
 vte::terminal::KeyEvent
 Widget::key_event_from_gdk(GdkEventKey* event) const
 {
-        auto type = vte::terminal::KeyEvent::Type{};
+        auto type = vte::terminal::EventBase::Type{};
         switch (gdk_event_get_event_type(reinterpret_cast<GdkEvent*>(event))) {
-        case GDK_KEY_PRESS: type = vte::terminal::KeyEvent::Type::ePRESS; break;
-        case GDK_KEY_RELEASE: type = vte::terminal::KeyEvent::Type::eRELEASE; break;
+        case GDK_KEY_PRESS: type = vte::terminal::KeyEvent::Type::eKEY_PRESS;     break;
+        case GDK_KEY_RELEASE: type = vte::terminal::KeyEvent::Type::eKEY_RELEASE; break;
         default: g_assert_not_reached(); return {};
         }
 
-        return {type,
-                read_modifiers_from_gdk(reinterpret_cast<GdkEvent*>(event)),
+        auto base_event = reinterpret_cast<GdkEvent*>(event);
+        return {base_event,
+                type,
+                event->time,
+                read_modifiers_from_gdk(base_event),
                 event->keyval,
                 event->hardware_keycode, // gdk_event_get_scancode(event),
                 event->group,
-                event->time,
-                event->is_modifier != 0,
-                event};
+                event->is_modifier != 0};
+}
+
+std::optional<vte::terminal::MouseEvent>
+Widget::mouse_event_from_gdk(GdkEvent* event) const
+{
+        auto type = vte::terminal::EventBase::Type{};
+        switch (gdk_event_get_event_type(event)) {
+        case GDK_2BUTTON_PRESS:  type = vte::terminal::MouseEvent::Type::eMOUSE_DOUBLE_PRESS; break;
+        case GDK_3BUTTON_PRESS:  type = vte::terminal::MouseEvent::Type::eMOUSE_TRIPLE_PRESS; break;
+        case GDK_BUTTON_PRESS:   type = vte::terminal::MouseEvent::Type::eMOUSE_PRESS;        break;
+        case GDK_BUTTON_RELEASE: type = vte::terminal::MouseEvent::Type::eMOUSE_RELEASE;      break;
+        case GDK_ENTER_NOTIFY:   type = vte::terminal::MouseEvent::Type::eMOUSE_ENTER;        break;
+        case GDK_LEAVE_NOTIFY:   type = vte::terminal::MouseEvent::Type::eMOUSE_LEAVE;        break;
+        case GDK_MOTION_NOTIFY:  type = vte::terminal::MouseEvent::Type::eMOUSE_MOTION;       break;
+        case GDK_SCROLL:         type = vte::terminal::MouseEvent::Type::eMOUSE_SCROLL;       break;
+        default:
+                return std::nullopt;
+        }
+
+        auto x = double{};
+        auto y = double{};
+        if (gdk_event_get_window(event) != m_event_window ||
+            !gdk_event_get_coords(event, &x, &y))
+                x = y = -1.; // FIXMEchpe or return std::nullopt?
+
+        auto button = unsigned{0};
+        (void)gdk_event_get_button(event, &button);
+
+        auto mouse_event = vte::terminal::MouseEvent{event,
+                                                     type,
+                                                     gdk_event_get_time(event),
+                                                     read_modifiers_from_gdk(event),
+                                                     vte::terminal::MouseEvent::Button(button),
+                                                     x,
+                                                     y};
+        return mouse_event;
 }
 
 void
diff --git a/src/widget.hh b/src/widget.hh
index 721b13cc..eed4da85 100644
--- a/src/widget.hh
+++ b/src/widget.hh
@@ -76,12 +76,12 @@ public:
         void focus_out(GdkEventFocus *event) noexcept { m_terminal->widget_focus_out(event); }
         bool key_press(GdkEventKey *event) noexcept { return 
m_terminal->widget_key_press(key_event_from_gdk(event)); }
         bool key_release(GdkEventKey *event) noexcept { return 
m_terminal->widget_key_release(key_event_from_gdk(event)); }
-        bool button_press(GdkEventButton *event) noexcept { return m_terminal->widget_button_press(event); }
-        bool button_release(GdkEventButton *event) noexcept { return 
m_terminal->widget_button_release(event); }
-        void enter(GdkEventCrossing *event) noexcept { m_terminal->widget_enter(event); }
-        void leave(GdkEventCrossing *event) noexcept { m_terminal->widget_leave(event); }
-        void scroll(GdkEventScroll *event) noexcept { m_terminal->widget_scroll(event); }
-        bool motion_notify(GdkEventMotion *event) noexcept { return m_terminal->widget_motion_notify(event); 
}
+        bool button_press(GdkEventButton *event) noexcept { return 
m_terminal->widget_mouse_press(*mouse_event_from_gdk(reinterpret_cast<GdkEvent*>(event))); }
+        bool button_release(GdkEventButton *event) noexcept { return 
m_terminal->widget_mouse_release(*mouse_event_from_gdk(reinterpret_cast<GdkEvent*>(event))); }
+        void enter(GdkEventCrossing *event) noexcept { 
m_terminal->widget_mouse_enter(*mouse_event_from_gdk(reinterpret_cast<GdkEvent*>(event))); }
+        void leave(GdkEventCrossing *event) noexcept { 
m_terminal->widget_mouse_leave(*mouse_event_from_gdk(reinterpret_cast<GdkEvent*>(event))); }
+        void scroll(GdkEventScroll *event) noexcept { 
m_terminal->widget_mouse_scroll(*mouse_event_from_gdk(reinterpret_cast<GdkEvent*>(event))); }
+        bool motion_notify(GdkEventMotion *event) noexcept { return 
m_terminal->widget_mouse_motion(*mouse_event_from_gdk(reinterpret_cast<GdkEvent*>(event))); }
 
         void paste(GdkAtom board) noexcept { m_terminal->widget_paste(board); }
         void copy(VteSelection sel,
@@ -136,6 +136,42 @@ public:
         void feed_child(std::string_view const& str) { terminal()->feed_child(str); }
         void feed_child_binary(std::string_view const& str) { terminal()->feed_child_binary(str); }
 
+        char *regex_match_check(vte::grid::column_t column,
+                                vte::grid::row_t row,
+                                int* tag)
+        {
+                return terminal()->regex_match_check(column, row, tag);
+        }
+
+        char* regex_match_check(GdkEvent* event,
+                                int* tag)
+        {
+                if (auto mouse_event = mouse_event_from_gdk(event))
+                        return terminal()->regex_match_check(*mouse_event, tag);
+                else
+                        return nullptr;
+        }
+
+        bool regex_match_check_extra(GdkEvent* event,
+                                     vte::base::Regex const** regexes,
+                                     size_t n_regexes,
+                                     uint32_t match_flags,
+                                     char** matches)
+        {
+                if (auto mouse_event = mouse_event_from_gdk(event))
+                        return terminal()->regex_match_check_extra(*mouse_event, regexes, n_regexes, 
match_flags, matches);
+                else
+                        return false;
+        }
+
+        char* hyperlink_check(GdkEvent* event)
+        {
+                if (auto mouse_event = mouse_event_from_gdk(event))
+                        return terminal()->hyperlink_check(*mouse_event);
+                else
+                        return nullptr;
+        }
+
         bool should_emit_signal(int id) noexcept;
 
 protected:
@@ -181,6 +217,7 @@ public: // FIXMEchpe
 private:
         unsigned read_modifiers_from_gdk(GdkEvent* event) const noexcept;
         vte::terminal::KeyEvent key_event_from_gdk(GdkEventKey* event) const;
+        std::optional<vte::terminal::MouseEvent> mouse_event_from_gdk(GdkEvent* event) const;
 
         GtkWidget* m_widget;
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]