Blob Blame History Raw
From c40936e82cf274b1fe828ec5f3ac2cc5acd6a6cf Mon Sep 17 00:00:00 2001
From: Debarshi Ray <debarshir@gnome.org>
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 45d480c83f67..afc394c6f633 100644
--- a/src/vte.cc
+++ b/src/vte.cc
@@ -10427,6 +10427,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(object, 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 cf02367d1a18..4c69e37cf7d5 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 fe5d1c4134f9..12c37e5c56a8 100644
--- a/src/vtegtk.cc
+++ b/src/vtegtk.cc
@@ -729,6 +729,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;
@@ -810,6 +811,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 e2515b3491ae..c175f6d7b06f 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 17d1c4db4fe8..a5df13416d6e 100644
--- a/src/vteinternal.hh
+++ b/src/vteinternal.hh
@@ -631,6 +631,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{};
@@ -1381,6 +1386,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 771a413b07df..932964a1b19e 100644
--- a/src/vteseq.cc
+++ b/src/vteseq.cc
@@ -1397,6 +1397,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,
@@ -6487,6 +6514,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:
@@ -6527,7 +6558,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.24.1


From 7dc608083309754da4f6801657ea42709609dbea Mon Sep 17 00:00:00 2001
From: Debarshi Ray <debarshir@gnome.org>
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 | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/vte.sh b/src/vte.sh
index c200493c34a6..ee010aa36505 100644
--- a/src/vte.sh
+++ b/src/vte.sh
@@ -50,10 +50,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\\%s" "${USER}" "${HOSTNAME%%.*}" "${pwd}" "$(__vte_osc7)"
+  printf '\033]777;notify;Command completed;%s\033\\\033]0;%s@%s:%s\033\\%s' "${command}" "${USER}" "${HOSTNAME%%.*}" "${pwd}" "$(__vte_osc7)"
 }
 
 case "$TERM" in
-- 
2.24.1


From 1bf1f20f1d6fe60f0d1898011fe7c3e21a5c55c1 Mon Sep 17 00:00:00 2001
From: Debarshi Ray <debarshir@gnome.org>
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 5e9f852f19be..cc8aaee14265 100644
--- a/src/app/app.cc
+++ b/src/app/app.cc
@@ -1673,6 +1673,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)
@@ -1901,6 +1909,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.24.1


From bb1b7dba7b0137aa64443332025765a6a02f4d82 Mon Sep 17 00:00:00 2001
From: Debarshi Ray <debarshir@gnome.org>
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 8b67f28359a4..b6a5fcfc2779 100644
--- a/doc/reference/vte-sections.txt
+++ b/doc/reference/vte-sections.txt
@@ -53,6 +53,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 afc394c6f633..454bf3a7da23 100644
--- a/src/vte.cc
+++ b/src/vte.cc
@@ -9632,6 +9632,7 @@ void
 Terminal::widget_scroll(GdkEventScroll *event)
 {
 	gdouble delta_x, delta_y;
+	gdouble scroll_speed;
 	gdouble v;
 	gint cnt, i;
 	int button;
@@ -9688,7 +9689,13 @@ Terminal::widget_scroll(GdkEventScroll *event)
 		return;
 	}
 
-	v = MAX (1., ceil (gtk_adjustment_get_page_increment (m_vadjustment) / 10.));
+	if (m_scroll_speed == 0) {
+		scroll_speed = ceil (gtk_adjustment_get_page_increment (m_vadjustment) / 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);
@@ -9985,6 +9992,16 @@ Terminal::decscusr_cursor_shape()
         }
 }
 
+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 4c69e37cf7d5..af9818d6447b 100644
--- a/src/vte/vteterminal.h
+++ b/src/vte/vteterminal.h
@@ -291,6 +291,10 @@ void vte_terminal_set_cursor_shape(VteTerminal *terminal,
 _VTE_PUBLIC
 VteCursorShape vte_terminal_get_cursor_shape(VteTerminal *terminal) _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 12c37e5c56a8..570da86dc39d 100644
--- a/src/vtegtk.cc
+++ b/src/vtegtk.cc
@@ -519,6 +519,9 @@ vte_terminal_get_property (GObject *object,
                 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;
@@ -626,6 +629,9 @@ vte_terminal_set_property (GObject *object,
                 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;
@@ -1684,6 +1690,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:
          *
@@ -4326,6 +4347,30 @@ vte_terminal_get_row_count(VteTerminal *terminal)
 	return IMPL(terminal)->m_row_count;
 }
 
+/**
+ * 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 c175f6d7b06f..5541e8a4f875 100644
--- a/src/vtegtk.hh
+++ b/src/vtegtk.hh
@@ -88,6 +88,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 a5df13416d6e..e34a4c57cfaf 100644
--- a/src/vteinternal.hh
+++ b/src/vteinternal.hh
@@ -423,6 +423,7 @@ public:
 	/* Scrolling options. */
         gboolean m_scroll_on_output;
         gboolean m_scroll_on_keystroke;
+        guint m_scroll_speed;
         vte::grid::row_t m_scrollback_lines;
 
         /* Restricted scrolling */
@@ -1259,6 +1260,7 @@ public:
         bool set_pty(VtePty *pty,
                      bool proces_remaining = true);
         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.24.1


From 1d6ddc80ea7b9daa02d45f478ee99e1bc82792bf Mon Sep 17 00:00:00 2001
From: Debarshi Ray <debarshir@gnome.org>
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 cc8aaee14265..218fd7a9e8d8 100644
--- a/src/app/app.cc
+++ b/src/app/app.cc
@@ -95,6 +95,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};
@@ -411,6 +412,8 @@ public:
                           "Save terminal contents to file at exit", nullptr },
                         { "reverse", 0, 0, G_OPTION_ARG_NONE, &reverse,
                           "Reverse foreground/background colors", 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,
@@ -1944,6 +1947,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.24.1


From 88fb27490d87bfb9daf669ea555ed0a679ce98cf Mon Sep 17 00:00:00 2001
From: Debarshi Ray <debarshir@gnome.org>
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

Currently, using C0 controls to emit OSC 777 from PS0 (ie.,
PS0=$(printf "\u009D777;preexec\u009C")) causes spurious characters to
show up. C1 controls don't seem to suffer from this problem.

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            |  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 454bf3a7da23..4d64f69f15dc 100644
--- a/src/vte.cc
+++ b/src/vte.cc
@@ -10453,6 +10453,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(object, 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 b/src/vte.sh
index ee010aa36505..ee424ce70521 100644
--- a/src/vte.sh
+++ b/src/vte.sh
@@ -60,7 +60,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 "\u009D777;preexec\u009C")
     [ -n "$ZSH_VERSION"  ] && precmd_functions+=(__vte_osc7)
     ;;
 esac
diff --git a/src/vte/vteterminal.h b/src/vte/vteterminal.h
index af9818d6447b..57f00989d4f6 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 570da86dc39d..858fea4b2e50 100644
--- a/src/vtegtk.cc
+++ b/src/vtegtk.cc
@@ -736,6 +736,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;
@@ -837,6 +838,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 5541e8a4f875..a4d9ac8ae1c5 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 e34a4c57cfaf..774eff8870a2 100644
--- a/src/vteinternal.hh
+++ b/src/vteinternal.hh
@@ -637,6 +637,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 932964a1b19e..3b82d914c544 100644
--- a/src/vteseq.cc
+++ b/src/vteseq.cc
@@ -1422,6 +1422,10 @@ Terminal::handle_urxvt_extension(vte::parser::Sequence const& seq,
                 m_notification_body = *token;
                 return;
         }
+
+        if (*token == "preexec") {
+                m_shell_preexec = TRUE;
+        }
 }
 
 bool
-- 
2.24.1


From 6d5a0397ed1023deb0c42bcfa74166f42e034a2c Mon Sep 17 00:00:00 2001
From: Debarshi Ray <debarshir@gnome.org>
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 218fd7a9e8d8..267ad650844c 100644
--- a/src/app/app.cc
+++ b/src/app/app.cc
@@ -1684,6 +1684,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)
@@ -1913,6 +1919,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.24.1


From f17c99d8727513c643c8380097bf1690b519b369 Mon Sep 17 00:00:00 2001
From: Debarshi Ray <debarshir@gnome.org>
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            |  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 4d64f69f15dc..0e6dbdf13e68 100644
--- a/src/vte.cc
+++ b/src/vte.cc
@@ -10460,6 +10460,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(object, 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 b/src/vte.sh
index ee424ce70521..331bfec3d3ba 100644
--- a/src/vte.sh
+++ b/src/vte.sh
@@ -55,7 +55,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\\%s' "${command}" "${USER}" "${HOSTNAME%%.*}" "${pwd}" "$(__vte_osc7)"
+  printf '\033]777;notify;Command completed;%s\033\\\033]777;precmd\033\\\033]0;%s@%s:%s\033\\%s' "${command}" "${USER}" "${HOSTNAME%%.*}" "${pwd}" "$(__vte_osc7)"
 }
 
 case "$TERM" in
diff --git a/src/vte/vteterminal.h b/src/vte/vteterminal.h
index 57f00989d4f6..21d99e6abd93 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 858fea4b2e50..6c53aec2eab1 100644
--- a/src/vtegtk.cc
+++ b/src/vtegtk.cc
@@ -736,6 +736,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;
@@ -838,6 +839,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 a4d9ac8ae1c5..3d46483e3a56 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 774eff8870a2..842755f92915 100644
--- a/src/vteinternal.hh
+++ b/src/vteinternal.hh
@@ -637,6 +637,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 3b82d914c544..d21181684a75 100644
--- a/src/vteseq.cc
+++ b/src/vteseq.cc
@@ -1423,7 +1423,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.24.1


From 8e38fc8dc123a796ec5fc4202115ea9a09f5d2d8 Mon Sep 17 00:00:00 2001
From: Debarshi Ray <debarshir@gnome.org>
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 267ad650844c..eab844eabc4a 100644
--- a/src/app/app.cc
+++ b/src/app/app.cc
@@ -1684,6 +1684,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)
 {
@@ -1919,6 +1925,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.24.1


From 0ec82af9c13db70a3275467daff7aa268fea6348 Mon Sep 17 00:00:00 2001
From: Debarshi Ray <debarshir@gnome.org>
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         | 65 +++++++++++++++++++++++++++++++++++++++++++
 src/vtegtk.hh         |  2 ++
 src/vteinternal.hh    | 16 +++++++++++
 src/vteseq.cc         | 31 +++++++++++++++++++++
 6 files changed, 127 insertions(+)

diff --git a/src/vte.cc b/src/vte.cc
index 0e6dbdf13e68..c99d19702b10 100644
--- a/src/vte.cc
+++ b/src/vte.cc
@@ -10481,6 +10481,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(object, pspecs[PROP_CURRENT_CONTAINER_NAME]);
+                g_object_notify_by_pspec(object, 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 21d99e6abd93..b55df3f76e50 100644
--- a/src/vte/vteterminal.h
+++ b/src/vte/vteterminal.h
@@ -462,6 +462,10 @@ glong vte_terminal_get_column_count(VteTerminal *terminal) _VTE_GNUC_NONNULL(1);
 _VTE_PUBLIC
 const char *vte_terminal_get_window_title(VteTerminal *terminal) _VTE_GNUC_NONNULL(1);
 _VTE_PUBLIC
+const char *vte_terminal_get_current_container_name(VteTerminal *terminal) _VTE_GNUC_NONNULL(1);
+_VTE_PUBLIC
+const char *vte_terminal_get_current_container_runtime(VteTerminal *terminal) _VTE_GNUC_NONNULL(1);
+_VTE_PUBLIC
 const char *vte_terminal_get_current_directory_uri(VteTerminal *terminal) _VTE_GNUC_NONNULL(1);
 _VTE_PUBLIC
 const char *vte_terminal_get_current_file_uri(VteTerminal *terminal) _VTE_GNUC_NONNULL(1);
diff --git a/src/vtegtk.cc b/src/vtegtk.cc
index 6c53aec2eab1..2427e6bfd2a1 100644
--- a/src/vtegtk.cc
+++ b/src/vtegtk.cc
@@ -474,6 +474,12 @@ vte_terminal_get_property (GObject *object,
                 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;
@@ -1803,6 +1809,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:
          *
@@ -3699,6 +3726,44 @@ vte_terminal_get_column_count(VteTerminal *terminal)
 	return IMPL(terminal)->m_column_count;
 }
 
+/**
+ * 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)
+{
+        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();
+}
+
+/**
+ * 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)
+{
+        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();
+}
+
 /**
  * vte_terminal_get_current_directory_uri:
  * @terminal: a #VteTerminal
diff --git a/src/vtegtk.hh b/src/vtegtk.hh
index 3d46483e3a56..24052b15f75c 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 842755f92915..20c4bcc5d900 100644
--- a/src/vteinternal.hh
+++ b/src/vteinternal.hh
@@ -41,6 +41,7 @@
 
 #include <list>
 #include <queue>
+#include <stack>
 #include <string>
 #include <variant>
 #include <vector>
@@ -118,6 +119,18 @@ typedef enum _VteCharacterReplacement {
         VTE_CHARACTER_REPLACEMENT_BRITISH
 } 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;
@@ -632,6 +645,9 @@ public:
         gboolean m_cursor_moved_pending;
         gboolean m_contents_changed_pending;
 
+        bool m_containers_changed{false};
+        std::stack<VteContainer> 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 d21181684a75..a521241df03f 100644
--- a/src/vteseq.cc
+++ b/src/vteseq.cc
@@ -1405,6 +1405,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.24.1