/*
* 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