/* * Copyright © 2018 Christian Persch * * This library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library. If not, see . */ #pragma once #include #include #include #include #include #include #include "vteterminal.h" #include "vtepty.h" #include "vteinternal.hh" #include "fwd.hh" #include "clipboard-gtk.hh" #include "regex.hh" #include "refptr.hh" namespace vte { namespace terminal { class Terminal; } // namespace terminal namespace platform { class EventBase { friend class vte::platform::Widget; friend class Terminal; public: enum class Type { eKEY_PRESS, eKEY_RELEASE, eMOUSE_ENTER, eMOUSE_LEAVE, eMOUSE_MOTION, eMOUSE_PRESS, eMOUSE_RELEASE, eMOUSE_SCROLL, }; protected: EventBase() noexcept = default; constexpr EventBase(Type type) noexcept : m_type{type} { } public: ~EventBase() noexcept = default; EventBase(EventBase const&) = default; EventBase(EventBase&&) = default; EventBase& operator=(EventBase const&) = delete; EventBase& operator=(EventBase&&) = delete; constexpr auto type() const noexcept { return m_type; } private: Type m_type; }; // class EventBase class KeyEvent : public EventBase { friend class vte::platform::Widget; friend class Terminal; protected: KeyEvent() noexcept = default; constexpr KeyEvent(GdkEvent* gdk_event, Type type, unsigned modifiers, unsigned keyval, unsigned keycode, unsigned group, bool is_modifier) noexcept : EventBase{type}, m_platform_event{gdk_event}, m_modifiers{modifiers}, m_keyval{keyval}, m_keycode{keycode}, m_group{group}, m_is_modifier{is_modifier} { } constexpr auto platform_event() const noexcept { return m_platform_event; } public: ~KeyEvent() noexcept = default; KeyEvent(KeyEvent const&) = delete; KeyEvent(KeyEvent&&) = delete; KeyEvent& operator=(KeyEvent const&) = delete; KeyEvent& operator=(KeyEvent&&) = delete; constexpr auto group() const noexcept { return m_group; } constexpr auto is_modifier() const noexcept { return m_is_modifier; } constexpr auto keycode() const noexcept { return m_keycode; } constexpr auto keyval() const noexcept { return m_keyval; } constexpr auto modifiers() const noexcept { return m_modifiers; } constexpr auto is_key_press() const noexcept { return type() == Type::eKEY_PRESS; } constexpr auto is_key_release() const noexcept { return type() == Type::eKEY_RELEASE; } bool matches(unsigned keyval, unsigned modifiers) const noexcept { #if VTE_GTK == 3 return false; // FIXMEgtk3 #elif VTE_GTK == 4 return gdk_key_event_matches(platform_event(), keyval, GdkModifierType(modifiers)) == GDK_KEY_MATCH_EXACT; #endif } private: GdkEvent* m_platform_event; unsigned m_modifiers; unsigned m_keyval; unsigned m_keycode; unsigned m_group; bool m_is_modifier; }; // 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, }; protected: MouseEvent() noexcept = default; constexpr MouseEvent(Type type, int press_count, unsigned modifiers, Button button, double x, double y) noexcept : EventBase{type}, m_press_count{press_count}, 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 button() const noexcept { return m_button; } constexpr auto button_value() const noexcept { return unsigned(m_button); } constexpr auto press_count() const noexcept { return m_press_count; } constexpr auto modifiers() const noexcept { return m_modifiers; } constexpr auto x() const noexcept { return m_x; } constexpr auto y() const noexcept { return m_y; } constexpr auto is_mouse_enter() const noexcept { return type() == Type::eMOUSE_ENTER; } constexpr auto is_mouse_leave() const noexcept { return type() == Type::eMOUSE_LEAVE; } constexpr auto is_mouse_motion() const noexcept { return type() == Type::eMOUSE_MOTION; } constexpr auto is_mouse_press() const noexcept { return type() == Type::eMOUSE_PRESS; } constexpr auto is_mouse_release() const noexcept { return type() == Type::eMOUSE_RELEASE; } private: int m_press_count; unsigned m_modifiers; Button m_button; double m_x; double m_y; }; // class MouseEvent class ScrollEvent : public EventBase { friend class vte::platform::Widget; friend class Terminal; protected: ScrollEvent() noexcept = default; constexpr ScrollEvent(unsigned modifiers, double dx, double dy) noexcept : EventBase{EventBase::Type::eMOUSE_SCROLL}, m_modifiers{modifiers}, m_dx{dx}, m_dy{dy} { } public: ~ScrollEvent() noexcept = default; ScrollEvent(ScrollEvent const&) = default; ScrollEvent(ScrollEvent&&) = default; ScrollEvent& operator=(ScrollEvent const&) = delete; ScrollEvent& operator=(ScrollEvent&&) = delete; constexpr auto modifiers() const noexcept { return m_modifiers; } constexpr auto dx() const noexcept { return m_dx; } constexpr auto dy() const noexcept { return m_dy; } private: unsigned m_modifiers; double m_dx; double m_dy; }; // class ScrollEvent class Widget : public std::enable_shared_from_this { public: friend class vte::terminal::Terminal; Widget(VteTerminal* t); ~Widget() noexcept; Widget(Widget const&) = delete; Widget(Widget&&) = delete; Widget& operator= (Widget const&) = delete; Widget& operator= (Widget&&) = delete; GObject* object() const noexcept { return reinterpret_cast(m_widget); } GtkWidget* gtk() const noexcept { return m_widget; } VteTerminal* vte() const noexcept { return reinterpret_cast(m_widget); } inline constexpr vte::terminal::Terminal* terminal() const noexcept { return m_terminal; } void constructed() noexcept; void dispose() noexcept; void realize() noexcept; void unrealize() noexcept; void map() noexcept; void unmap() noexcept; void state_flags_changed(GtkStateFlags old_flags); void direction_changed(GtkTextDirection old_direction) noexcept; bool query_tooltip(int x, int y, bool keyboard, GtkTooltip* tooltip) noexcept; void connect_settings(); bool padding_changed() noexcept; void settings_changed(); #if VTE_GTK == 3 void style_updated() noexcept; void draw(cairo_t *cr) noexcept { m_terminal->widget_draw(cr); } void get_preferred_width(int *minimum_width, int *natural_width) const noexcept { m_terminal->widget_measure_width(minimum_width, natural_width); } void get_preferred_height(int *minimum_height, int *natural_height) const noexcept { m_terminal->widget_measure_height(minimum_height, natural_height); } void size_allocate(GtkAllocation *allocation); void event_focus_in(GdkEventFocus *event); void event_focus_out(GdkEventFocus *event); bool event_key_press(GdkEventKey *event); bool event_key_release(GdkEventKey *event); bool event_button_press(GdkEventButton *event); bool event_button_release(GdkEventButton *event); void event_enter(GdkEventCrossing *event); void event_leave(GdkEventCrossing *event); bool event_scroll(GdkEventScroll *event); bool event_motion_notify(GdkEventMotion *event); void screen_changed (GdkScreen *previous_screen) noexcept; #endif /* VTE_GTK == 3 */ #if VTE_GTK == 4 void size_allocate(int width, int height, int baseline); void root(); void unroot(); void measure(GtkOrientation orientation, int for_size, int* minimum, int* natural, int* minimum_baseline, int* natural_baseline) noexcept; std::pair compute_expand(); void css_changed(GtkCssStyleChange* change); void root_realize(); void root_unrealize(); void root_surface_state_notify(); void root_surface_focused_changed(); auto root_focused() const noexcept { return (m_root_surface_state & GDK_TOPLEVEL_STATE_FOCUSED) != 0; } void system_setting_changed(GtkSystemSetting setting); void snapshot(GtkSnapshot* snapshot) noexcept { terminal()->widget_snapshot(snapshot); } bool contains(double x, double y); void display_changed() noexcept; bool event_key_pressed(GtkEventControllerKey* controller, unsigned key, unsigned keycode, unsigned modifiers); void event_key_released(GtkEventControllerKey* controller, unsigned key, unsigned keycode, unsigned modifiers); bool event_key_modifiers(GtkEventControllerKey* controller, unsigned modifiers); void event_focus_enter(GtkEventControllerFocus* controller); void event_focus_leave(GtkEventControllerFocus* controller); void event_motion_enter(GtkEventControllerMotion* controller, double x, double y); void event_motion_leave(GtkEventControllerMotion* controller); void event_motion(GtkEventControllerMotion* controller, double x, double y); void event_motion_notify_is_pointer(GtkEventControllerMotion* controller); void event_motion_notify_contains_pointer(GtkEventControllerMotion* controller); void event_scroll_begin(GtkEventControllerScroll* controller); bool event_scroll(GtkEventControllerScroll* controller, double dx, double dy); void event_scroll_end(GtkEventControllerScroll* controller); void event_scroll_decelerate(GtkEventControllerScroll* controller, double vx, double vy); void gesture_click_pressed(GtkGestureClick* gesture, int press_count, double x, double y); void gesture_click_released(GtkGestureClick* gesture, int press_count, double x, double y); void gesture_click_stopped(GtkGestureClick* gesture); void gesture_click_unpaired_release(GtkGestureClick* gesture, double x, double y, unsigned button, GdkEventSequence* sequence); #endif /* VTE_GTK == 4 */ void grab_focus() noexcept { gtk_widget_grab_focus(gtk()); } bool primary_paste_enabled() const noexcept; Clipboard& clipboard_get(ClipboardType type) const; void clipboard_offer_data(ClipboardType type, ClipboardFormat format) noexcept; void clipboard_request_text(ClipboardType type) noexcept; void clipboard_set_text(ClipboardType type, std::string_view const& str) noexcept; void paste_text(std::string_view const& text) { m_terminal->widget_paste(text); } void paste(vte::platform::ClipboardType type) { clipboard_request_text(type); } void copy(vte::platform::ClipboardType type, vte::platform::ClipboardFormat format) noexcept { m_terminal->widget_copy(type, format); } void beep() noexcept; void set_hadjustment(vte::glib::RefPtr adjustment) noexcept { m_hadjustment = std::move(adjustment); } void set_vadjustment(vte::glib::RefPtr adjustment); auto hadjustment() noexcept { return m_hadjustment.get(); } auto vadjustment() noexcept { return m_vadjustment.get(); } void set_hscroll_policy(GtkScrollablePolicy policy); void set_vscroll_policy(GtkScrollablePolicy policy); auto hscroll_policy() const noexcept { return m_hscroll_policy; } auto vscroll_policy() const noexcept { return m_vscroll_policy; } constexpr bool set_scroll_unit_is_pixels(bool enable) noexcept { auto const rv = m_scroll_unit_is_pixels != enable; m_scroll_unit_is_pixels = enable; return rv; } constexpr auto scroll_unit_is_pixels() const noexcept { return m_scroll_unit_is_pixels; } bool set_cursor_blink_mode(VteCursorBlinkMode mode) { return terminal()->set_cursor_blink_mode(vte::terminal::Terminal::CursorBlinkMode(mode)); } auto cursor_blink_mode() const noexcept { return VteCursorBlinkMode(terminal()->cursor_blink_mode()); } bool set_cursor_shape(VteCursorShape shape) { return terminal()->set_cursor_shape(vte::terminal::Terminal::CursorShape(shape)); } auto cursor_shape() const noexcept { return VteCursorShape(terminal()->cursor_shape()); } bool set_backspace_binding(VteEraseBinding mode) { return terminal()->set_backspace_binding(vte::terminal::Terminal::EraseMode(mode)); } auto backspace_binding() const noexcept { return VteEraseBinding(terminal()->backspace_binding()); } bool set_delete_binding(VteEraseBinding mode) { return terminal()->set_delete_binding(vte::terminal::Terminal::EraseMode(mode)); } auto delete_binding() const noexcept { return VteEraseBinding(terminal()->delete_binding()); } bool set_text_blink_mode(VteTextBlinkMode mode) { return terminal()->set_text_blink_mode(vte::terminal::Terminal::TextBlinkMode(mode)); } auto text_blink_mode() const noexcept { return VteTextBlinkMode(terminal()->text_blink_mode()); } bool set_word_char_exceptions(std::optional stropt); auto word_char_exceptions() const noexcept { return m_word_char_exceptions ? m_word_char_exceptions.value().c_str() : nullptr; } bool set_fallback_scrolling(bool set) { return terminal()->set_fallback_scrolling(set); } bool fallback_scrolling() const noexcept { return terminal()->fallback_scrolling(); } char const* encoding() const noexcept { return m_terminal->encoding(); } void emit_child_exited(int status) noexcept; void emit_eof() noexcept; bool set_pty(VtePty* pty) noexcept; inline auto pty() const noexcept { return m_pty.get(); } void feed(std::string_view const& str) { terminal()->feed(str); } 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); } #if VTE_GTK == 3 char* regex_match_check(GdkEvent* event, int* tag) { return terminal()->regex_match_check(mouse_event_from_gdk(event), tag); } bool regex_match_check_extra(GdkEvent* event, vte::base::Regex const** regexes, size_t n_regexes, uint32_t match_flags, char** matches) { return terminal()->regex_match_check_extra(mouse_event_from_gdk(event), regexes, n_regexes, match_flags, matches); } char* hyperlink_check(GdkEvent* event) { return terminal()->hyperlink_check(mouse_event_from_gdk(event)); } #elif VTE_GTK == 4 char* regex_match_check_at(double x, double y, int* tag) { return terminal()->regex_match_check_at(x, y, tag); } bool regex_match_check_extra_at(double x, double y, vte::base::Regex const** regexes, size_t n_regexes, uint32_t match_flags, char** matches) { return terminal()->regex_match_check_extra_at(x, y, regexes, n_regexes, match_flags, matches); } char* hyperlink_check_at(double x, double y) { return terminal()->hyperlink_check_at(x, y); } #endif /* VTE_GTK */ bool should_emit_signal(int id) noexcept; bool set_sixel_enabled(bool enabled) noexcept { return m_terminal->set_sixel_enabled(enabled); } bool sixel_enabled() const noexcept { return m_terminal->sixel_enabled(); } constexpr auto xalign() const noexcept { return m_xalign; } constexpr auto yalign() const noexcept { return m_yalign; } constexpr auto xfill() const noexcept { return m_xfill; } constexpr auto yfill() const noexcept { return m_yfill; } bool set_xalign(VteAlign align) noexcept { if (align == m_xalign) return false; m_xalign = VteAlign(align); gtk_widget_queue_allocate(gtk()); return true; } bool set_yalign(VteAlign align) noexcept { if (align == m_yalign) return false; m_yalign = align; gtk_widget_queue_allocate(gtk()); return true; } bool set_xfill(bool fill) noexcept { if (fill == m_xfill) return false; m_xfill = fill; gtk_widget_queue_allocate(gtk()); return true; } bool set_yfill(bool fill) noexcept { if (fill == m_yfill) return false; m_yfill = fill; gtk_widget_queue_allocate(gtk()); return true; } protected: enum class CursorType { eDefault, eInvisible, eMousing, eHyperlink }; #if VTE_GTK == 3 GdkWindow* event_window() const noexcept { return m_event_window; } #endif bool realized() const noexcept { return gtk_widget_get_realized(m_widget); } vte::glib::RefPtr create_cursor(std::string const& name) const noexcept; void set_cursor(CursorType type) noexcept; void set_cursor(GdkCursor* cursor) noexcept; void set_cursor(Cursor const& cursor) noexcept; bool im_filter_keypress(KeyEvent const& event) noexcept; void im_focus_in() noexcept; void im_focus_out() noexcept; void im_reset() noexcept { if (m_im_context) gtk_im_context_reset(m_im_context.get()); } void im_set_cursor_location(cairo_rectangle_int_t const* rect) noexcept; void unset_pty() noexcept; unsigned key_event_translate_ctrlkey(KeyEvent const& event) const noexcept; void notify_scroll_bounds_changed(bool value_changed = false); void notify_scroll_value_changed(); void notify_char_size_changed(int width, int height); public: // FIXMEchpe void im_preedit_changed() noexcept; void vadjustment_value_changed(); private: KeyEvent key_event_from_gdk(GdkEvent* event) const; #if VTE_GTK == 3 unsigned read_modifiers_from_gdk(GdkEvent* event) const noexcept; MouseEvent mouse_event_from_gdk(GdkEvent* event) const /* throws */; std::optional scroll_event_from_gdk(GdkEvent* event) const; #elif VTE_GTK == 4 MouseEvent mouse_event_from_gesture_click(EventBase::Type type, GtkGestureClick* gesture, int press_count, double x, double y) const /* throws */; #endif void clipboard_request_received_cb(Clipboard const& clipboard, std::string_view const& text); void clipboard_request_failed_cb(Clipboard const& clipboard); std::optional clipboard_data_get_cb(Clipboard const& clipboard, ClipboardFormat format); void clipboard_data_clear_cb(Clipboard const& clipboard); GtkWidget* m_widget; vte::terminal::Terminal* m_terminal; #if VTE_GTK == 3 /* Event window */ GdkWindow *m_event_window; #endif vte::glib::RefPtr m_settings{nullptr}; /* Cursors */ vte::glib::RefPtr m_default_cursor; vte::glib::RefPtr m_invisible_cursor; vte::glib::RefPtr m_mousing_cursor; vte::glib::RefPtr m_hyperlink_cursor; /* Input method */ vte::glib::RefPtr m_im_context; /* PTY */ vte::glib::RefPtr m_pty; /* Clipboard */ std::shared_ptr m_clipboard; std::shared_ptr m_primary_clipboard; /* Misc */ std::optional m_word_char_exceptions{}; vte::glib::RefPtr m_vadjustment{}; vte::glib::RefPtr m_hadjustment{}; unsigned m_hscroll_policy:1{GTK_SCROLL_NATURAL}; unsigned m_vscroll_policy:1{GTK_SCROLL_NATURAL}; unsigned m_scroll_unit_is_pixels:1{false}; unsigned m_changing_scroll_position:1{false}; VteAlign m_xalign{VTE_ALIGN_START}; VteAlign m_yalign{VTE_ALIGN_START}; bool m_xfill{true}; bool m_yfill{true}; #if VTE_GTK == 4 GdkToplevelState m_root_surface_state{GdkToplevelState(0)}; long m_root_realize_id{0}; long m_root_unrealize_id{0}; long m_root_surface_state_notify_id{0}; #endif /* VTE_GTK == 4 */ }; } // namespace platform } // namespace vte