From 0728f06f528c1ba77a8671c529e44fbabd9c8980 Mon Sep 17 00:00:00 2001 From: Debarshi Ray Date: Wed, 7 Jan 2015 16:01:00 +0100 Subject: [PATCH 01/10] Add sequences and signals for desktop notification Add sequences OSC 777 ; notify ; SUMMARY ; BODY BEL OSC 777 ; notify ; SUMMARY BEL OSC 777 ; notify ; SUMMARY ; BODY ST OSC 777 ; notify ; SUMMARY ST that let terminal applications send a notification to the desktop environment. Based on Enlightenment's Terminology: https://phab.enlightenment.org/T1765 https://bugzilla.gnome.org/show_bug.cgi?id=711059 --- src/marshal.list | 1 + src/vte.cc | 9 +++++++++ src/vte/vteterminal.h | 4 +++- src/vtegtk.cc | 21 +++++++++++++++++++++ src/vtegtk.hh | 1 + src/vteinternal.hh | 8 ++++++++ src/vteseq.cc | 32 +++++++++++++++++++++++++++++++- 7 files changed, 74 insertions(+), 2 deletions(-) diff --git a/src/marshal.list b/src/marshal.list index 241128c3ccfe..4412cf3d5f5c 100644 --- a/src/marshal.list +++ b/src/marshal.list @@ -1,3 +1,4 @@ VOID:STRING,BOXED +VOID:STRING,STRING VOID:STRING,UINT VOID:UINT,UINT diff --git a/src/vte.cc b/src/vte.cc index c33b27c45023..01c7dd7eebc4 100644 --- a/src/vte.cc +++ b/src/vte.cc @@ -10135,6 +10135,15 @@ Terminal::emit_pending_signals() emit_adjustment_changed(); + if (m_notification_received) { + _vte_debug_print (VTE_DEBUG_SIGNALS, + "Emitting `notification-received'.\n"); + g_signal_emit(freezer.get(), signals[SIGNAL_NOTIFICATION_RECEIVED], 0, + m_notification_summary.c_str(), + m_notification_body.c_str()); + m_notification_received = false; + } + if (m_window_title_changed) { if (m_window_title != m_window_title_pending) { m_window_title.swap(m_window_title_pending); diff --git a/src/vte/vteterminal.h b/src/vte/vteterminal.h index d5e96535a867..71c4c66cc4ee 100644 --- a/src/vte/vteterminal.h +++ b/src/vte/vteterminal.h @@ -104,8 +104,10 @@ struct _VteTerminalClass { void (*bell)(VteTerminal* terminal); + void (*notification_received)(VteTerminal* terminal, const gchar *summary, const gchar *body); + /* Padding for future expansion. */ - gpointer padding[16]; + gpointer padding[15]; VteTerminalClassPrivate *priv; }; diff --git a/src/vtegtk.cc b/src/vtegtk.cc index f7a65f72b68e..30e0af0f8015 100644 --- a/src/vtegtk.cc +++ b/src/vtegtk.cc @@ -934,6 +934,7 @@ vte_terminal_class_init(VteTerminalClass *klass) klass->child_exited = NULL; klass->encoding_changed = NULL; klass->char_size_changed = NULL; + klass->notification_received = NULL; klass->window_title_changed = NULL; klass->icon_title_changed = NULL; klass->selection_changed = NULL; @@ -1015,6 +1016,26 @@ vte_terminal_class_init(VteTerminalClass *klass) G_OBJECT_CLASS_TYPE(klass), g_cclosure_marshal_VOID__INTv); + /** + * VteTerminal::notification-received: + * @vteterminal: the object which received the signal + * @summary: The summary + * @body: (allow-none): Extra optional text + * + * Emitted when a process running in the terminal wants to + * send a notification to the desktop environment. + */ + signals[SIGNAL_NOTIFICATION_RECEIVED] = + g_signal_new(I_("notification-received"), + G_OBJECT_CLASS_TYPE(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(VteTerminalClass, notification_received), + NULL, + NULL, + _vte_marshal_VOID__STRING_STRING, + G_TYPE_NONE, + 2, G_TYPE_STRING, G_TYPE_STRING); + /** * VteTerminal::window-title-changed: * @vteterminal: the object which received the signal diff --git a/src/vtegtk.hh b/src/vtegtk.hh index cb207e57f928..b44dfd13a054 100644 --- a/src/vtegtk.hh +++ b/src/vtegtk.hh @@ -56,6 +56,7 @@ enum { SIGNAL_TEXT_INSERTED, SIGNAL_TEXT_MODIFIED, SIGNAL_TEXT_SCROLLED, + SIGNAL_NOTIFICATION_RECEIVED, SIGNAL_WINDOW_TITLE_CHANGED, LAST_SIGNAL }; diff --git a/src/vteinternal.hh b/src/vteinternal.hh index 333f858c164e..7608eecc38d9 100644 --- a/src/vteinternal.hh +++ b/src/vteinternal.hh @@ -917,6 +917,11 @@ public: gboolean m_cursor_moved_pending; gboolean m_contents_changed_pending; + /* desktop notification */ + bool m_notification_received{false}; + std::string m_notification_summary; + std::string m_notification_body; + std::string m_window_title{}; std::string m_current_directory_uri{}; std::string m_current_file_uri{}; @@ -1634,6 +1639,9 @@ public: int osc) noexcept; /* OSC handlers */ + void handle_urxvt_extension(vte::parser::Sequence const& seq, + vte::parser::StringTokeniser::const_iterator& token, + vte::parser::StringTokeniser::const_iterator const& endtoken) noexcept; void set_color(vte::parser::Sequence const& seq, vte::parser::StringTokeniser::const_iterator& token, vte::parser::StringTokeniser::const_iterator const& endtoken, diff --git a/src/vteseq.cc b/src/vteseq.cc index ba9a2df6b9c4..c765b0bfb1f4 100644 --- a/src/vteseq.cc +++ b/src/vteseq.cc @@ -1377,6 +1377,33 @@ Terminal::delete_lines(vte::grid::row_t param) m_text_deleted_flag = TRUE; } +void +Terminal::handle_urxvt_extension(vte::parser::Sequence const& seq, + vte::parser::StringTokeniser::const_iterator& token, + vte::parser::StringTokeniser::const_iterator const& endtoken) noexcept +{ + if (token == endtoken) + return; + + if (*token == "notify") { + ++token; + + if (token == endtoken) + return; + + m_notification_summary = *token; + m_notification_body.clear(); + m_notification_received = true; + ++token; + + if (token == endtoken) + return; + + m_notification_body = *token; + return; + } +} + bool Terminal::get_osc_color_index(int osc, int value, @@ -6475,6 +6502,10 @@ Terminal::OSC(vte::parser::Sequence const& seq) reset_color(VTE_HIGHLIGHT_FG, VTE_COLOR_SOURCE_ESCAPE); break; + case VTE_OSC_URXVT_EXTENSION: + handle_urxvt_extension(seq, it, cend); + break; + case VTE_OSC_XTERM_SET_ICON_TITLE: case VTE_OSC_XTERM_SET_XPROPERTY: case VTE_OSC_XTERM_SET_COLOR_MOUSE_CURSOR_FG: @@ -6515,7 +6546,6 @@ Terminal::OSC(vte::parser::Sequence const& seq) case VTE_OSC_URXVT_SET_FONT_BOLD_ITALIC: case VTE_OSC_URXVT_VIEW_UP: case VTE_OSC_URXVT_VIEW_DOWN: - case VTE_OSC_URXVT_EXTENSION: case VTE_OSC_YF_RQGWR: default: break; -- 2.25.4 From 4c3bce7d70e208bee0f36d64a540ade9975c3ce7 Mon Sep 17 00:00:00 2001 From: Debarshi Ray Date: Thu, 29 Jan 2015 13:09:17 +0100 Subject: [PATCH 02/10] vte.sh: Emit OSC 777 from PROMPT_COMMAND For some reason, the three consecutive backslashes break the parsing. As Christian Persch suggested, replacing the double quotes with singles fixes it. https://bugzilla.gnome.org/show_bug.cgi?id=711059 --- src/vte.sh.in | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vte.sh.in b/src/vte.sh.in index a12e254b6cba..f7c4fdeff30b 100644 --- a/src/vte.sh.in +++ b/src/vte.sh.in @@ -27,10 +27,12 @@ __vte_osc7 () { } __vte_prompt_command() { + local command=$(HISTTIMEFORMAT= history 1 | sed 's/^ *[0-9]\+ *//') + command="${command//;/ }" local pwd='~' [ "$PWD" != "$HOME" ] && pwd=${PWD/#$HOME\//\~\/} pwd="${pwd//[[:cntrl:]]}" - printf "\033]0;%s@%s:%s\033\\" "${USER}" "${HOSTNAME%%.*}" "${pwd}" + printf '\033]777;notify;Command completed;%s\033\\\033]0;%s@%s:%s\033\\' "${command}" "${USER}" "${HOSTNAME%%.*}" "${pwd}" __vte_osc7 } -- 2.25.4 From 5171a84e95ad20048dcb1683c149711430002149 Mon Sep 17 00:00:00 2001 From: Debarshi Ray Date: Thu, 22 Jan 2015 16:37:10 +0100 Subject: [PATCH 03/10] Test the notification-received signal --- bindings/vala/app.vala | 7 +++++++ src/app/app.cc | 10 ++++++++++ 2 files changed, 17 insertions(+) diff --git a/bindings/vala/app.vala b/bindings/vala/app.vala index fc26c2b0dfc3..634b8ddeeb91 100644 --- a/bindings/vala/app.vala +++ b/bindings/vala/app.vala @@ -309,6 +309,8 @@ class Window : Gtk.ApplicationWindow if (Options.object_notifications) terminal.notify.connect(notify_cb); + terminal.notification_received.connect(notification_received_cb); + /* Settings */ if (Options.no_double_buffer) terminal.set_double_buffered(false); @@ -780,6 +782,11 @@ class Window : Gtk.ApplicationWindow set_title(terminal.get_window_title()); } + private void notification_received_cb(Vte.Terminal terminal, string summary, string? body) + { + print ("[%s]: %s\n", summary, body); + } + } /* class Window */ class App : Gtk.Application diff --git a/src/app/app.cc b/src/app/app.cc index d8bfe9e13826..e95f58cd3bac 100644 --- a/src/app/app.cc +++ b/src/app/app.cc @@ -1851,6 +1851,14 @@ window_window_title_changed_cb(VteTerminal* terminal, vte_terminal_get_window_title(window->terminal)); } +static void +notification_received_cb(VteTerminal *terminal, + const gchar *summary, + const gchar *body) +{ + g_print("[%s]: %s\n", summary, body); +} + static void window_lower_window_cb(VteTerminal* terminal, VteappWindow* window) @@ -2086,6 +2094,8 @@ vteapp_window_constructed(GObject *object) if (options.object_notifications) g_signal_connect(window->terminal, "notify", G_CALLBACK(window_notify_cb), window); + g_signal_connect(window->terminal, "notification-received", G_CALLBACK(notification_received_cb), NULL); + /* Settings */ if (options.no_double_buffer) { G_GNUC_BEGIN_IGNORE_DEPRECATIONS; -- 2.25.4 From ed262358646832779ee0eb5afa0a8a7a6c1c2adf Mon Sep 17 00:00:00 2001 From: Debarshi Ray Date: Fri, 13 May 2016 17:53:54 +0200 Subject: [PATCH 04/10] Add a property to configure the scroll speed By default, it is set to zero which gives the current behaviour of moving the buffer by a function of the number of visible rows. https://bugzilla.redhat.com/show_bug.cgi?id=1103380 --- doc/reference/vte-sections.txt | 1 + src/vte.cc | 19 +++++++++++++- src/vte/vteterminal.h | 4 +++ src/vtegtk.cc | 45 ++++++++++++++++++++++++++++++++++ src/vtegtk.hh | 1 + src/vteinternal.hh | 2 ++ 6 files changed, 71 insertions(+), 1 deletion(-) diff --git a/doc/reference/vte-sections.txt b/doc/reference/vte-sections.txt index c222e7fdff0b..6da5eff30715 100644 --- a/doc/reference/vte-sections.txt +++ b/doc/reference/vte-sections.txt @@ -52,6 +52,7 @@ vte_terminal_get_cursor_blink_mode vte_terminal_set_cursor_blink_mode vte_terminal_get_text_blink_mode vte_terminal_set_text_blink_mode +vte_terminal_set_scroll_speed vte_terminal_set_scrollback_lines vte_terminal_get_scrollback_lines vte_terminal_set_font diff --git a/src/vte.cc b/src/vte.cc index 01c7dd7eebc4..af73288b9f20 100644 --- a/src/vte.cc +++ b/src/vte.cc @@ -9334,6 +9334,7 @@ vte_cairo_get_clip_region (cairo_t *cr) bool Terminal::widget_mouse_scroll(MouseEvent const& event) { + gdouble scroll_speed; gdouble v; gint cnt, i; int button; @@ -9390,7 +9391,13 @@ Terminal::widget_mouse_scroll(MouseEvent const& event) return true; } - v = MAX (1., ceil (gtk_adjustment_get_page_increment (m_vadjustment.get()) / 10.)); + if (m_scroll_speed == 0) { + scroll_speed = ceil (gtk_adjustment_get_page_increment (m_vadjustment.get()) / 10.); + } else { + scroll_speed = m_scroll_speed; + } + + v = MAX (1., scroll_speed); _vte_debug_print(VTE_DEBUG_EVENTS, "Scroll speed is %d lines per non-smooth scroll unit\n", (int) v); @@ -9693,6 +9700,16 @@ Terminal::decscusr_cursor_shape() const noexcept } } +bool +Terminal::set_scroll_speed(unsigned int scroll_speed) +{ + if (scroll_speed == m_scroll_speed) + return false; + + m_scroll_speed = scroll_speed; + return true; +} + bool Terminal::set_scrollback_lines(long lines) { diff --git a/src/vte/vteterminal.h b/src/vte/vteterminal.h index 71c4c66cc4ee..203c77e08c57 100644 --- a/src/vte/vteterminal.h +++ b/src/vte/vteterminal.h @@ -306,6 +306,10 @@ void vte_terminal_set_cursor_shape(VteTerminal *terminal, _VTE_PUBLIC VteCursorShape vte_terminal_get_cursor_shape(VteTerminal *terminal) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1); +_VTE_PUBLIC +void vte_terminal_set_scroll_speed(VteTerminal *terminal, + guint scroll_speed) _VTE_GNUC_NONNULL(1); + /* Set the number of scrollback lines, above or at an internal minimum. */ _VTE_PUBLIC void vte_terminal_set_scrollback_lines(VteTerminal *terminal, diff --git a/src/vtegtk.cc b/src/vtegtk.cc index 30e0af0f8015..bf88b9481981 100644 --- a/src/vtegtk.cc +++ b/src/vtegtk.cc @@ -712,6 +712,9 @@ try case PROP_REWRAP_ON_RESIZE: g_value_set_boolean (value, vte_terminal_get_rewrap_on_resize (terminal)); break; + case PROP_SCROLL_SPEED: + g_value_set_uint (value, impl->m_scroll_speed); + break; case PROP_SCROLLBACK_LINES: g_value_set_uint (value, vte_terminal_get_scrollback_lines(terminal)); break; @@ -827,6 +830,9 @@ try case PROP_REWRAP_ON_RESIZE: vte_terminal_set_rewrap_on_resize (terminal, g_value_get_boolean (value)); break; + case PROP_SCROLL_SPEED: + vte_terminal_set_scroll_speed (terminal, g_value_get_uint (value)); + break; case PROP_SCROLLBACK_LINES: vte_terminal_set_scrollback_lines (terminal, g_value_get_uint (value)); break; @@ -1917,6 +1923,21 @@ vte_terminal_class_init(VteTerminalClass *klass) TRUE, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY)); + /** + * VteTerminal:scroll-speed: + * + * The number of lines by which the buffer is moved when + * scrolling with a mouse wheel on top of the terminal + * Setting it to zero will cause the buffer to be moved by an + * amount depending on the number of visible rows the widget + * can display. + */ + pspecs[PROP_SCROLL_SPEED] = + g_param_spec_uint ("scroll-speed", NULL, NULL, + 0, G_MAXUINT, + 0, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY)); + /** * VteTerminal:scrollback-lines: * @@ -5324,6 +5345,30 @@ catch (...) return -1; } +/** + * vte_terminal_set_scroll_speed: + * @terminal: a #VteTerminal + * @scroll_speed: move the buffer by this number of lines while scrolling + * + * Sets the number of lines by which the buffer is moved when + * scrolling with a mouse wheel. Setting it to zero will cause the + * buffer to be moved by an amount depending on the number of visible + * rows the widget can display. + */ +void +vte_terminal_set_scroll_speed(VteTerminal *terminal, guint scroll_speed) +{ + g_return_if_fail(VTE_IS_TERMINAL(terminal)); + + GObject *object = G_OBJECT(terminal); + g_object_freeze_notify(object); + + if (IMPL(terminal)->set_scroll_speed(scroll_speed)) + g_object_notify_by_pspec(object, pspecs[PROP_SCROLL_SPEED]); + + g_object_thaw_notify(object); +} + /** * vte_terminal_set_scrollback_lines: * @terminal: a #VteTerminal diff --git a/src/vtegtk.hh b/src/vtegtk.hh index b44dfd13a054..30b917786b5b 100644 --- a/src/vtegtk.hh +++ b/src/vtegtk.hh @@ -89,6 +89,7 @@ enum { PROP_MOUSE_POINTER_AUTOHIDE, PROP_PTY, PROP_REWRAP_ON_RESIZE, + PROP_SCROLL_SPEED, PROP_SCROLLBACK_LINES, PROP_SCROLL_ON_KEYSTROKE, PROP_SCROLL_ON_OUTPUT, diff --git a/src/vteinternal.hh b/src/vteinternal.hh index 7608eecc38d9..79ea9e7f0da0 100644 --- a/src/vteinternal.hh +++ b/src/vteinternal.hh @@ -696,6 +696,7 @@ public: /* Scrolling options. */ bool m_scroll_on_output{false}; bool m_scroll_on_keystroke{true}; + guint m_scroll_speed; vte::grid::row_t m_scrollback_lines{0}; /* Restricted scrolling */ @@ -1512,6 +1513,7 @@ public: bool set_input_enabled(bool enabled); bool set_mouse_autohide(bool autohide); bool set_rewrap_on_resize(bool rewrap); + bool set_scroll_speed(unsigned int scroll_speed); bool set_scrollback_lines(long lines); bool set_scroll_on_keystroke(bool scroll); bool set_scroll_on_output(bool scroll); -- 2.25.4 From 73e7009c342747fbb3bdbef322811e09467dab9b Mon Sep 17 00:00:00 2001 From: Debarshi Ray Date: Fri, 13 May 2016 17:54:57 +0200 Subject: [PATCH 05/10] Test the scroll-speed property https://bugzilla.redhat.com/show_bug.cgi?id=1103380 --- bindings/vala/app.vala | 4 ++++ src/app/app.cc | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/bindings/vala/app.vala b/bindings/vala/app.vala index 634b8ddeeb91..c984b868246d 100644 --- a/bindings/vala/app.vala +++ b/bindings/vala/app.vala @@ -335,6 +335,7 @@ class Window : Gtk.ApplicationWindow terminal.set_rewrap_on_resize(!Options.no_rewrap); terminal.set_scroll_on_output(false); terminal.set_scroll_on_keystroke(true); + terminal.set_scroll_speed(Options.scroll_speed); terminal.set_scrollback_lines(Options.scrollback_lines); /* Style */ @@ -857,6 +858,7 @@ class App : Gtk.Application public static bool object_notifications = false; public static string? output_filename = null; public static bool reverse = false; + public static uint scroll_speed = 0; public static int scrollback_lines = 512; public static int transparency_percent = 0; public static bool version = false; @@ -1050,6 +1052,8 @@ class App : Gtk.Application "Save terminal contents to file at exit", null }, { "reverse", 0, 0, OptionArg.NONE, ref reverse, "Reverse foreground/background colors", null }, + { "scroll-speed", 0, 0, OptionArg.INT, ref scroll_speed, + "Specify the scroll speed", null }, { "scrollback-lines", 'n', 0, OptionArg.INT, ref scrollback_lines, "Specify the number of scrollback-lines", null }, { "transparent", 'T', 0, OptionArg.INT, ref transparency_percent, diff --git a/src/app/app.cc b/src/app/app.cc index e95f58cd3bac..52893c87414a 100644 --- a/src/app/app.cc +++ b/src/app/app.cc @@ -108,6 +108,7 @@ public: int verbosity{0}; double cell_height_scale{1.0}; double cell_width_scale{1.0}; + unsigned int scroll_speed{0}; VteCursorBlinkMode cursor_blink_mode{VTE_CURSOR_BLINK_SYSTEM}; VteCursorShape cursor_shape{VTE_CURSOR_SHAPE_BLOCK}; VteTextBlinkMode text_blink_mode{VTE_TEXT_BLINK_ALWAYS}; @@ -569,6 +570,8 @@ public: "Reverse foreground/background colors", nullptr }, { "require-systemd-scope", 0, 0, G_OPTION_ARG_NONE, &require_systemd_scope, "Require use of a systemd user scope", nullptr }, + { "scroll-speed", 0, 0, G_OPTION_ARG_INT, &scroll_speed, + "Specify the scroll speed", nullptr }, { "scrollback-lines", 'n', 0, G_OPTION_ARG_INT, &scrollback_lines, "Specify the number of scrollback-lines (-1 for infinite)", nullptr }, { "transparent", 'T', 0, G_OPTION_ARG_INT, &transparency_percent, @@ -2127,6 +2130,7 @@ vteapp_window_constructed(GObject *object) vte_terminal_set_rewrap_on_resize(window->terminal, !options.no_rewrap); vte_terminal_set_scroll_on_output(window->terminal, false); vte_terminal_set_scroll_on_keystroke(window->terminal, true); + vte_terminal_set_scroll_speed(window->terminal, options.scroll_speed); vte_terminal_set_scrollback_lines(window->terminal, options.scrollback_lines); vte_terminal_set_text_blink_mode(window->terminal, options.text_blink_mode); -- 2.25.4 From 2fe27cbe7cbb120e9055284c9e7e34423e5add49 Mon Sep 17 00:00:00 2001 From: Debarshi Ray Date: Wed, 7 Jan 2015 16:01:00 +0100 Subject: [PATCH 06/10] Support preexec notifications from an interactive shell Add sequences OSC 777 ; preexec BEL OSC 777 ; preexec ST that can be used from an interactive shell's preexec hook to notify the terminal emulator that a new command is about to be executed. Examples of such hooks are Bash's PS0 and Zsh's preexec. The OSC 777 escape sequence is taken from Enlightenment's Terminology: https://phab.enlightenment.org/T1765 https://bugzilla.gnome.org/show_bug.cgi?id=711059 https://bugzilla.gnome.org/show_bug.cgi?id=711060 --- src/vte.cc | 7 +++++++ src/vte.sh.in | 2 +- src/vte/vteterminal.h | 3 ++- src/vtegtk.cc | 18 ++++++++++++++++++ src/vtegtk.hh | 1 + src/vteinternal.hh | 2 ++ src/vteseq.cc | 4 ++++ 7 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/vte.cc b/src/vte.cc index af73288b9f20..d77d27967b50 100644 --- a/src/vte.cc +++ b/src/vte.cc @@ -10161,6 +10161,13 @@ Terminal::emit_pending_signals() m_notification_received = false; } + if (m_shell_preexec) { + _vte_debug_print (VTE_DEBUG_SIGNALS, + "Emitting `shell-preexec'.\n"); + g_signal_emit(freezer.get(), signals[SIGNAL_SHELL_PREEXEC], 0); + m_shell_preexec = FALSE; + } + if (m_window_title_changed) { if (m_window_title != m_window_title_pending) { m_window_title.swap(m_window_title_pending); diff --git a/src/vte.sh.in b/src/vte.sh.in index f7c4fdeff30b..ec094db032c7 100644 --- a/src/vte.sh.in +++ b/src/vte.sh.in @@ -38,7 +38,7 @@ __vte_prompt_command() { case "$TERM" in xterm*|vte*) - [ -n "${BASH_VERSION:-}" ] && PROMPT_COMMAND="__vte_prompt_command" + [ -n "${BASH_VERSION:-}" ] && PROMPT_COMMAND="__vte_prompt_command" && PS0=$(printf "\033]777;preexec\033\\") [ -n "${ZSH_VERSION:-}" ] && precmd_functions+=(__vte_osc7) ;; esac diff --git a/src/vte/vteterminal.h b/src/vte/vteterminal.h index 203c77e08c57..5668ac7d71df 100644 --- a/src/vte/vteterminal.h +++ b/src/vte/vteterminal.h @@ -105,9 +105,10 @@ struct _VteTerminalClass { void (*bell)(VteTerminal* terminal); void (*notification_received)(VteTerminal* terminal, const gchar *summary, const gchar *body); + void (*shell_preexec)(VteTerminal* terminal); /* Padding for future expansion. */ - gpointer padding[15]; + gpointer padding[14]; VteTerminalClassPrivate *priv; }; diff --git a/src/vtegtk.cc b/src/vtegtk.cc index bf88b9481981..df247f27c038 100644 --- a/src/vtegtk.cc +++ b/src/vtegtk.cc @@ -941,6 +941,7 @@ vte_terminal_class_init(VteTerminalClass *klass) klass->encoding_changed = NULL; klass->char_size_changed = NULL; klass->notification_received = NULL; + klass->shell_preexec = NULL; klass->window_title_changed = NULL; klass->icon_title_changed = NULL; klass->selection_changed = NULL; @@ -1042,6 +1043,23 @@ vte_terminal_class_init(VteTerminalClass *klass) G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING); + /** + * VteTerminal::shell-preexec: + * @vteterminal: the object which received the signal + * + * Emitted when the interactive shell has read in a complete + * command and is about to execute it. + */ + signals[SIGNAL_SHELL_PREEXEC] = + g_signal_new(I_("shell-preexec"), + G_OBJECT_CLASS_TYPE(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(VteTerminalClass, shell_preexec), + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + /** * VteTerminal::window-title-changed: * @vteterminal: the object which received the signal diff --git a/src/vtegtk.hh b/src/vtegtk.hh index 30b917786b5b..d3ad1435ae53 100644 --- a/src/vtegtk.hh +++ b/src/vtegtk.hh @@ -52,6 +52,7 @@ enum { SIGNAL_RESIZE_WINDOW, SIGNAL_RESTORE_WINDOW, SIGNAL_SELECTION_CHANGED, + SIGNAL_SHELL_PREEXEC, SIGNAL_TEXT_DELETED, SIGNAL_TEXT_INSERTED, SIGNAL_TEXT_MODIFIED, diff --git a/src/vteinternal.hh b/src/vteinternal.hh index 79ea9e7f0da0..009500a3ffc6 100644 --- a/src/vteinternal.hh +++ b/src/vteinternal.hh @@ -923,6 +923,8 @@ public: std::string m_notification_summary; std::string m_notification_body; + gboolean m_shell_preexec; + std::string m_window_title{}; std::string m_current_directory_uri{}; std::string m_current_file_uri{}; diff --git a/src/vteseq.cc b/src/vteseq.cc index c765b0bfb1f4..ad5935063bd9 100644 --- a/src/vteseq.cc +++ b/src/vteseq.cc @@ -1402,6 +1402,10 @@ Terminal::handle_urxvt_extension(vte::parser::Sequence const& seq, m_notification_body = *token; return; } + + if (*token == "preexec") { + m_shell_preexec = TRUE; + } } bool -- 2.25.4 From 62b4dec7a7493f531de60ee814596176f3b9c082 Mon Sep 17 00:00:00 2001 From: Debarshi Ray Date: Fri, 20 Apr 2018 18:21:53 +0200 Subject: [PATCH 07/10] Test the shell-preexec signal https://bugzilla.gnome.org/show_bug.cgi?id=711059 https://bugzilla.gnome.org/show_bug.cgi?id=711060 --- bindings/vala/app.vala | 6 ++++++ src/app/app.cc | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/bindings/vala/app.vala b/bindings/vala/app.vala index c984b868246d..83af686be106 100644 --- a/bindings/vala/app.vala +++ b/bindings/vala/app.vala @@ -310,6 +310,7 @@ class Window : Gtk.ApplicationWindow terminal.notify.connect(notify_cb); terminal.notification_received.connect(notification_received_cb); + terminal.shell_preexec.connect(shell_preexec_cb); /* Settings */ if (Options.no_double_buffer) @@ -788,6 +789,11 @@ class Window : Gtk.ApplicationWindow print ("[%s]: %s\n", summary, body); } + private void shell_preexec_cb(Vte.Terminal terminal) + { + print("[shell] executing command\n"); + } + } /* class Window */ class App : Gtk.Application diff --git a/src/app/app.cc b/src/app/app.cc index 52893c87414a..3ef597e97365 100644 --- a/src/app/app.cc +++ b/src/app/app.cc @@ -1862,6 +1862,12 @@ notification_received_cb(VteTerminal *terminal, g_print("[%s]: %s\n", summary, body); } +static void +shell_preexec_cb(VteTerminal *terminal) +{ + g_print("[shell] executing command\n"); +} + static void window_lower_window_cb(VteTerminal* terminal, VteappWindow* window) @@ -2098,6 +2104,7 @@ vteapp_window_constructed(GObject *object) g_signal_connect(window->terminal, "notify", G_CALLBACK(window_notify_cb), window); g_signal_connect(window->terminal, "notification-received", G_CALLBACK(notification_received_cb), NULL); + g_signal_connect(window->terminal, "shell-preexec", G_CALLBACK(shell_preexec_cb), NULL); /* Settings */ if (options.no_double_buffer) { -- 2.25.4 From ce908501de9d32ab9d3e0cc600058e104bec2130 Mon Sep 17 00:00:00 2001 From: Debarshi Ray Date: Wed, 2 May 2018 17:20:30 +0200 Subject: [PATCH 08/10] Support precmd notifications from an interactive shell Add sequences OSC 777 ; precmd BEL OSC 777 ; precmd ST that can be used from an interactive shell's precmd hook to notify the terminal emulator that a first level prompt is about to be shown. Examples of such hooks are Bash's PROMPT_COMMAND and Zsh's precmd. The OSC 777 escape sequence is taken from Enlightenment's Terminology: https://phab.enlightenment.org/T1765 https://bugzilla.gnome.org/show_bug.cgi?id=711059 https://bugzilla.gnome.org/show_bug.cgi?id=711060 --- src/vte.cc | 7 +++++++ src/vte.sh.in | 2 +- src/vte/vteterminal.h | 3 ++- src/vtegtk.cc | 18 ++++++++++++++++++ src/vtegtk.hh | 1 + src/vteinternal.hh | 1 + src/vteseq.cc | 4 +++- 7 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/vte.cc b/src/vte.cc index d77d27967b50..54654c8589ab 100644 --- a/src/vte.cc +++ b/src/vte.cc @@ -10168,6 +10168,13 @@ Terminal::emit_pending_signals() m_shell_preexec = FALSE; } + if (m_shell_precmd) { + _vte_debug_print (VTE_DEBUG_SIGNALS, + "Emitting `shell-precmd'.\n"); + g_signal_emit(freezer.get(), signals[SIGNAL_SHELL_PRECMD], 0); + m_shell_precmd = FALSE; + } + if (m_window_title_changed) { if (m_window_title != m_window_title_pending) { m_window_title.swap(m_window_title_pending); diff --git a/src/vte.sh.in b/src/vte.sh.in index ec094db032c7..18bb266e8d87 100644 --- a/src/vte.sh.in +++ b/src/vte.sh.in @@ -32,7 +32,7 @@ __vte_prompt_command() { local pwd='~' [ "$PWD" != "$HOME" ] && pwd=${PWD/#$HOME\//\~\/} pwd="${pwd//[[:cntrl:]]}" - printf '\033]777;notify;Command completed;%s\033\\\033]0;%s@%s:%s\033\\' "${command}" "${USER}" "${HOSTNAME%%.*}" "${pwd}" + printf '\033]777;notify;Command completed;%s\033\\\033]777;precmd\033\\\033]0;%s@%s:%s\033\\' "${command}" "${USER}" "${HOSTNAME%%.*}" "${pwd}" __vte_osc7 } diff --git a/src/vte/vteterminal.h b/src/vte/vteterminal.h index 5668ac7d71df..cbd2a77f9f29 100644 --- a/src/vte/vteterminal.h +++ b/src/vte/vteterminal.h @@ -105,10 +105,11 @@ struct _VteTerminalClass { void (*bell)(VteTerminal* terminal); void (*notification_received)(VteTerminal* terminal, const gchar *summary, const gchar *body); + void (*shell_precmd)(VteTerminal* terminal); void (*shell_preexec)(VteTerminal* terminal); /* Padding for future expansion. */ - gpointer padding[14]; + gpointer padding[13]; VteTerminalClassPrivate *priv; }; diff --git a/src/vtegtk.cc b/src/vtegtk.cc index df247f27c038..afccd6d85c65 100644 --- a/src/vtegtk.cc +++ b/src/vtegtk.cc @@ -941,6 +941,7 @@ vte_terminal_class_init(VteTerminalClass *klass) klass->encoding_changed = NULL; klass->char_size_changed = NULL; klass->notification_received = NULL; + klass->shell_precmd = NULL; klass->shell_preexec = NULL; klass->window_title_changed = NULL; klass->icon_title_changed = NULL; @@ -1043,6 +1044,23 @@ vte_terminal_class_init(VteTerminalClass *klass) G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING); + /** + * VteTerminal::shell-precmd: + * @vteterminal: the object which received the signal + * + * Emitted right before an interactive shell shows a + * first-level prompt. + */ + signals[SIGNAL_SHELL_PRECMD] = + g_signal_new(I_("shell-precmd"), + G_OBJECT_CLASS_TYPE(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(VteTerminalClass, shell_precmd), + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + /** * VteTerminal::shell-preexec: * @vteterminal: the object which received the signal diff --git a/src/vtegtk.hh b/src/vtegtk.hh index d3ad1435ae53..85c2a38d61ec 100644 --- a/src/vtegtk.hh +++ b/src/vtegtk.hh @@ -52,6 +52,7 @@ enum { SIGNAL_RESIZE_WINDOW, SIGNAL_RESTORE_WINDOW, SIGNAL_SELECTION_CHANGED, + SIGNAL_SHELL_PRECMD, SIGNAL_SHELL_PREEXEC, SIGNAL_TEXT_DELETED, SIGNAL_TEXT_INSERTED, diff --git a/src/vteinternal.hh b/src/vteinternal.hh index 009500a3ffc6..18ffeaf3a270 100644 --- a/src/vteinternal.hh +++ b/src/vteinternal.hh @@ -923,6 +923,7 @@ public: std::string m_notification_summary; std::string m_notification_body; + gboolean m_shell_precmd; gboolean m_shell_preexec; std::string m_window_title{}; diff --git a/src/vteseq.cc b/src/vteseq.cc index ad5935063bd9..4b66681d713c 100644 --- a/src/vteseq.cc +++ b/src/vteseq.cc @@ -1403,7 +1403,9 @@ Terminal::handle_urxvt_extension(vte::parser::Sequence const& seq, return; } - if (*token == "preexec") { + if (*token == "precmd") { + m_shell_precmd = TRUE; + } else if (*token == "preexec") { m_shell_preexec = TRUE; } } -- 2.25.4 From c57e30715838cb9d6eb0d4390867746bea0c42d5 Mon Sep 17 00:00:00 2001 From: Debarshi Ray Date: Wed, 2 May 2018 17:30:48 +0200 Subject: [PATCH 09/10] Test the shell-precmd signal https://bugzilla.gnome.org/show_bug.cgi?id=711059 https://bugzilla.gnome.org/show_bug.cgi?id=711060 --- bindings/vala/app.vala | 6 ++++++ src/app/app.cc | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/bindings/vala/app.vala b/bindings/vala/app.vala index 83af686be106..300384f5c74b 100644 --- a/bindings/vala/app.vala +++ b/bindings/vala/app.vala @@ -310,6 +310,7 @@ class Window : Gtk.ApplicationWindow terminal.notify.connect(notify_cb); terminal.notification_received.connect(notification_received_cb); + terminal.shell_precmd.connect(shell_precmd_cb); terminal.shell_preexec.connect(shell_preexec_cb); /* Settings */ @@ -789,6 +790,11 @@ class Window : Gtk.ApplicationWindow print ("[%s]: %s\n", summary, body); } + private void shell_precmd_cb(Vte.Terminal terminal) + { + print("[shell] showing command prompt\n"); + } + private void shell_preexec_cb(Vte.Terminal terminal) { print("[shell] executing command\n"); diff --git a/src/app/app.cc b/src/app/app.cc index 3ef597e97365..e1b10ca43b2c 100644 --- a/src/app/app.cc +++ b/src/app/app.cc @@ -1862,6 +1862,12 @@ notification_received_cb(VteTerminal *terminal, g_print("[%s]: %s\n", summary, body); } +static void +shell_precmd_cb(VteTerminal *terminal) +{ + g_print("[shell] showing command prompt\n"); +} + static void shell_preexec_cb(VteTerminal *terminal) { @@ -2104,6 +2110,7 @@ vteapp_window_constructed(GObject *object) g_signal_connect(window->terminal, "notify", G_CALLBACK(window_notify_cb), window); g_signal_connect(window->terminal, "notification-received", G_CALLBACK(notification_received_cb), NULL); + g_signal_connect(window->terminal, "shell-precmd", G_CALLBACK(shell_precmd_cb), NULL); g_signal_connect(window->terminal, "shell-preexec", G_CALLBACK(shell_preexec_cb), NULL); /* Settings */ -- 2.25.4 From 3e049907ff0981ba235df9b97b56828cb814fb76 Mon Sep 17 00:00:00 2001 From: Debarshi Ray Date: Mon, 10 Jun 2019 20:30:18 +0200 Subject: [PATCH 10/10] Support tracking the active container inside the terminal Add sequences OSC 777 ; container ; push ; NAME ; RUNTIME BEL OSC 777 ; container ; push ; NAME ; RUNTIME ST OSC 777 ; container ; pop ; NAME ; RUNTIME BEL OSC 777 ; container ; pop ; NAME ; RUNTIME ST that let container tools notify the terminal emulator when entering and leaving a container environment. The RUNTIME argument namespaces the NAME and identifies the container tooling being used. eg., docker, flatpak, podman, toolbox, etc.. The OSC 777 escape sequence is taken from Enlightenment's Terminology: https://phab.enlightenment.org/T1765 It's a VTE-specific extension until a standard escape sequence is agreed upon across multiple different terminal emulators [1]. [1] https://gitlab.freedesktop.org/terminal-wg/specifications/issues/17 --- src/vte.cc | 9 +++++ src/vte/vteterminal.h | 4 +++ src/vtegtk.cc | 77 +++++++++++++++++++++++++++++++++++++++++++ src/vtegtk.hh | 2 ++ src/vteinternal.hh | 16 +++++++++ src/vteseq.cc | 31 +++++++++++++++++ 6 files changed, 139 insertions(+) diff --git a/src/vte.cc b/src/vte.cc index 54654c8589ab..b5f1258bbd99 100644 --- a/src/vte.cc +++ b/src/vte.cc @@ -10189,6 +10189,15 @@ Terminal::emit_pending_signals() m_window_title_changed = false; } + if (m_containers_changed) { + _vte_debug_print(VTE_DEBUG_SIGNALS, + "Notifying `current-container-name' and `current-container-runtime'.\n"); + + g_object_notify_by_pspec(freezer.get(), pspecs[PROP_CURRENT_CONTAINER_NAME]); + g_object_notify_by_pspec(freezer.get(), pspecs[PROP_CURRENT_CONTAINER_RUNTIME]); + m_containers_changed = false; + } + if (m_current_directory_uri_changed) { if (m_current_directory_uri != m_current_directory_uri_pending) { m_current_directory_uri.swap(m_current_directory_uri_pending); diff --git a/src/vte/vteterminal.h b/src/vte/vteterminal.h index cbd2a77f9f29..15aa421a0c68 100644 --- a/src/vte/vteterminal.h +++ b/src/vte/vteterminal.h @@ -484,6 +484,10 @@ glong vte_terminal_get_column_count(VteTerminal *terminal) _VTE_CXX_NOEXCEPT _VT _VTE_PUBLIC const char *vte_terminal_get_window_title(VteTerminal *terminal) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1); _VTE_PUBLIC +const char *vte_terminal_get_current_container_name(VteTerminal *terminal) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1); +_VTE_PUBLIC +const char *vte_terminal_get_current_container_runtime(VteTerminal *terminal) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1); +_VTE_PUBLIC const char *vte_terminal_get_current_directory_uri(VteTerminal *terminal) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1); _VTE_PUBLIC const char *vte_terminal_get_current_file_uri(VteTerminal *terminal) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1); diff --git a/src/vtegtk.cc b/src/vtegtk.cc index afccd6d85c65..242d6d979ddc 100644 --- a/src/vtegtk.cc +++ b/src/vtegtk.cc @@ -664,6 +664,12 @@ try case PROP_CURSOR_BLINK_MODE: g_value_set_enum (value, vte_terminal_get_cursor_blink_mode (terminal)); break; + case PROP_CURRENT_CONTAINER_NAME: + g_value_set_string (value, vte_terminal_get_current_container_name (terminal)); + break; + case PROP_CURRENT_CONTAINER_RUNTIME: + g_value_set_string (value, vte_terminal_get_current_container_runtime (terminal)); + break; case PROP_CURRENT_DIRECTORY_URI: g_value_set_string (value, vte_terminal_get_current_directory_uri (terminal)); break; @@ -2036,6 +2042,27 @@ vte_terminal_class_init(VteTerminalClass *klass) NULL, (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY)); + /** + * VteTerminal:current-container-name: + * + * The name of the current container, or %NULL if unset. + */ + pspecs[PROP_CURRENT_CONTAINER_NAME] = + g_param_spec_string ("current-container-name", NULL, NULL, + NULL, + (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY)); + + /** + * VteTerminal:current-container-runtime: + * + * The name of the runtime toolset used to set up the current + * container, or %NULL if unset. + */ + pspecs[PROP_CURRENT_CONTAINER_RUNTIME] = + g_param_spec_string ("current-container-runtime", NULL, NULL, + NULL, + (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY)); + /** * VteTerminal:current-directory-uri: * @@ -4531,6 +4558,56 @@ catch (...) return -1; } +/** + * vte_terminal_get_current_container_name: + * @terminal: a #VteTerminal + * + * Returns: (nullable) (transfer none): the name of the current + * container, or %NULL + */ +const char * +vte_terminal_get_current_container_name(VteTerminal *terminal) noexcept +try +{ + g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL); + auto impl = IMPL(terminal); + if (impl->m_containers.empty()) + return NULL; + + const VteContainer &container = impl->m_containers.top(); + return container.m_name.c_str(); +} +catch (...) +{ + vte::log_exception(); + return NULL; +} + +/** + * vte_terminal_get_current_container_runtime: + * @terminal: a #VteTerminal + * + * Returns: (nullable) (transfer none): the name of the runtime + * toolset used to set up the current container, or %NULL + */ +const char * +vte_terminal_get_current_container_runtime(VteTerminal *terminal) noexcept +try +{ + g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL); + auto impl = IMPL(terminal); + if (impl->m_containers.empty()) + return NULL; + + const VteContainer &container = impl->m_containers.top(); + return container.m_runtime.c_str(); +} +catch (...) +{ + vte::log_exception(); + return NULL; +} + /** * vte_terminal_get_current_directory_uri: * @terminal: a #VteTerminal diff --git a/src/vtegtk.hh b/src/vtegtk.hh index 85c2a38d61ec..a258902a3092 100644 --- a/src/vtegtk.hh +++ b/src/vtegtk.hh @@ -76,6 +76,8 @@ enum { PROP_CJK_AMBIGUOUS_WIDTH, PROP_CURSOR_BLINK_MODE, PROP_CURSOR_SHAPE, + PROP_CURRENT_CONTAINER_NAME, + PROP_CURRENT_CONTAINER_RUNTIME, PROP_CURRENT_DIRECTORY_URI, PROP_CURRENT_FILE_URI, PROP_DELETE_BINDING, diff --git a/src/vteinternal.hh b/src/vteinternal.hh index 18ffeaf3a270..f6b98f670ad5 100644 --- a/src/vteinternal.hh +++ b/src/vteinternal.hh @@ -56,6 +56,7 @@ #include #include #include +#include #include #include #include @@ -94,6 +95,18 @@ typedef enum _VteCharacterReplacement { VTE_CHARACTER_REPLACEMENT_LINE_DRAWING } VteCharacterReplacement; +struct VteContainer { +public: + VteContainer(const std::string &name, const std::string &runtime) : + m_name{name}, + m_runtime{runtime} + { + } + + std::string m_name; + std::string m_runtime; +}; + typedef struct _VtePaletteColor { struct { vte::color::rgb color; @@ -918,6 +931,9 @@ public: gboolean m_cursor_moved_pending; gboolean m_contents_changed_pending; + bool m_containers_changed{false}; + std::stack m_containers; + /* desktop notification */ bool m_notification_received{false}; std::string m_notification_summary; diff --git a/src/vteseq.cc b/src/vteseq.cc index 4b66681d713c..6e7cb8741696 100644 --- a/src/vteseq.cc +++ b/src/vteseq.cc @@ -1385,6 +1385,37 @@ Terminal::handle_urxvt_extension(vte::parser::Sequence const& seq, if (token == endtoken) return; + if (*token == "container") { + ++token; + + if (token == endtoken) + return; + + const std::string sub_command = *token; + ++token; + + if (sub_command == "pop") { + if (!m_containers.empty()) { + m_containers.pop(); + m_containers_changed = true; + } + } else if (sub_command == "push") { + if (token == endtoken) + return; + + const std::string name = *token; + ++token; + + if (token == endtoken) + return; + + const std::string runtime = *token; + + m_containers.emplace(name, runtime); + m_containers_changed = true; + } + } + if (*token == "notify") { ++token; -- 2.25.4