Blob Blame History Raw
From 8d4a539a20142a3944e12f029ded76a27749296c 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 2614e5b4d20f..ac54d8791ba2 100644
--- a/src/vte.cc
+++ b/src/vte.cc
@@ -10143,6 +10143,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 41de14ba1019..09a18439277f 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.29.2


From e75a6e2b9b4a1789ab7c2c1572e13c38dc73daf5 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.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.29.2


From d726c0779babba159dc01a1dd20c0a6678dbd4dc 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 6453ef8516f9..65521e1f4177 100644
--- a/src/app/app.cc
+++ b/src/app/app.cc
@@ -1862,6 +1862,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)
@@ -2097,6 +2105,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.29.2


From f3ec33fc7a644aa9a8d640af16e8ef87a7db5db7 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 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 ac54d8791ba2..68fc4bf8f448 100644
--- a/src/vte.cc
+++ b/src/vte.cc
@@ -9342,6 +9342,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;
@@ -9398,7 +9399,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);
@@ -9701,6 +9708,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 09a18439277f..934e0ab2bd37 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:
          *
@@ -5323,6 +5344,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.29.2


From 9538e23477d7e99909961f7ea5868f9c19c043e1 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 65521e1f4177..28e934d9d9c0 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};
@@ -580,6 +581,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,
@@ -2138,6 +2141,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.29.2


From 8e616fea1e377e03d5f3cbac0786df5e76da896d 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

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 68fc4bf8f448..09068deebdf2 100644
--- a/src/vte.cc
+++ b/src/vte.cc
@@ -10169,6 +10169,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 934e0ab2bd37..0bba24ee534f 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.29.2


From c7c410786da41bf9bbcdf8ca1be21dcf0d89637d 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 28e934d9d9c0..c9f7b5637d16 100644
--- a/src/app/app.cc
+++ b/src/app/app.cc
@@ -1873,6 +1873,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)
@@ -2109,6 +2115,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.29.2


From 034f0a8ae047af6d6a2ee9c443b6686a4eff3018 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.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 09068deebdf2..6d3d3c5a2854 100644
--- a/src/vte.cc
+++ b/src/vte.cc
@@ -10176,6 +10176,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 0bba24ee534f..4e0bc3a68464 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.29.2


From 6083ed32c023a7a24f4b4856147a0970d59f9a35 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 c9f7b5637d16..b679bc0bff50 100644
--- a/src/app/app.cc
+++ b/src/app/app.cc
@@ -1873,6 +1873,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)
 {
@@ -2115,6 +2121,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.29.2


From b0a81273d4ff929afa4e37750273980e70017c77 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 ; UID BEL
  OSC 777 ; container ; push ; NAME ; RUNTIME ; UID ST
  OSC 777 ; container ; pop ; NAME ; RUNTIME ; UID BEL
  OSC 777 ; container ; pop ; NAME ; RUNTIME ; UID ST

  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 UID argument is the real UID of the
container tools. Only those containers that are used with the same real
UID as the VTE process are tracked.

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         | 86 +++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 194 insertions(+)

diff --git a/src/vte.cc b/src/vte.cc
index 6d3d3c5a2854..74db14f593dd 100644
--- a/src/vte.cc
+++ b/src/vte.cc
@@ -10197,6 +10197,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 4e0bc3a68464..2e6859798c42 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:
          *
@@ -4530,6 +4557,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 <list>
 #include <queue>
 #include <optional>
+#include <stack>
 #include <string>
 #include <variant>
 #include <vector>
@@ -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<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 4b66681d713c..4240896b95e8 100644
--- a/src/vteseq.cc
+++ b/src/vteseq.cc
@@ -20,10 +20,14 @@
 
 #include "config.h"
 
+#include <string>
+
 #include <search.h>
 #include <stdlib.h>
 #include <string.h>
 #include <limits.h>
+#include <unistd.h>
+#include <sys/types.h>
 #ifdef HAVE_SYS_SYSLIMITS_H
 #include <sys/syslimits.h>
 #endif
@@ -1385,6 +1389,88 @@ 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 (token == endtoken)
+                                return;
+
+                        ++token;
+
+                        if (token == endtoken)
+                                return;
+
+                        ++token;
+
+                        if (token == endtoken) {
+                                if (!m_containers.empty()) {
+                                        m_containers.pop();
+                                        m_containers_changed = true;
+                                }
+
+                                return;
+                        }
+
+                        const std::string uid_token = *token;
+                        ++token;
+
+                        const uid_t uid = getuid();
+                        const std::string uid_str = std::to_string(uid);
+
+                        if (uid_token == uid_str) {
+                                if (!m_containers.empty()) {
+                                        m_containers.pop();
+                                        m_containers_changed = true;
+                                }
+
+                                return;
+                        }
+
+                        return;
+                } else if (sub_command == "push") {
+                        if (token == endtoken)
+                                return;
+
+                        const std::string name = *token;
+                        ++token;
+
+                        if (token == endtoken)
+                                return;
+
+                        const std::string runtime = *token;
+                        ++token;
+
+                        if (token == endtoken) {
+                                m_containers.emplace(name, runtime);
+                                m_containers_changed = true;
+                                return;
+                        }
+
+                        const std::string uid_token = *token;
+                        ++token;
+
+                        const uid_t uid = getuid();
+                        const std::string uid_str = std::to_string(uid);
+
+                        if (uid_token == uid_str) {
+                                m_containers.emplace(name, runtime);
+                                m_containers_changed = true;
+                                return;
+                        }
+
+                        return;
+                }
+
+                return;
+        }
+
         if (*token == "notify") {
                 ++token;
 
-- 
2.29.2