diff --git a/src/vte.cc b/src/vte.cc
index 353d7703..d1f656d3 100644
--- a/src/vte.cc
+++ b/src/vte.cc
@@ -2983,6 +2983,8 @@ VteTerminalPrivate::insert_char(gunichar c,
                 0x00b7,  /* ~ => bullet */
         };
 
+        m_last_printable_char = c;
+
         insert |= m_insert_mode;
 	invalidate_now |= insert;
 
@@ -8047,6 +8049,7 @@ VteTerminalPrivate::VteTerminalPrivate(VteTerminal *t) :
 	m_conv_buffer = _vte_byte_array_new();
 	set_encoding(nullptr /* UTF-8 */);
 	g_assert_cmpstr(m_encoding, ==, "UTF-8");
+        m_last_printable_char = 0;
 
         /* Set up the emulation. */
 	m_keypad_mode = VTE_KEYMODE_NORMAL;
@@ -10287,6 +10290,7 @@ VteTerminalPrivate::reset(bool clear_tabstops,
         m_iso2022 = _vte_iso2022_state_new(nullptr);
 	_vte_iso2022_state_set_codeset(m_iso2022,
 				       m_encoding);
+        m_last_printable_char = 0;
 	/* Reset keypad/cursor key modes. */
 	m_keypad_mode = VTE_KEYMODE_NORMAL;
 	m_cursor_mode = VTE_KEYMODE_NORMAL;
diff --git a/src/vteinternal.hh b/src/vteinternal.hh
index 037b5911..aabfb554 100644
--- a/src/vteinternal.hh
+++ b/src/vteinternal.hh
@@ -315,6 +315,8 @@ public:
 	/* IConv buffer. */
         VteByteArray *m_conv_buffer;
 
+        gunichar m_last_printable_char;  /* XXX FIXME: vteunistr? */
+
 	/* Screen data.  We support the normal screen, and an alternate
 	 * screen, which seems to be a DEC-specific feature. */
         struct _VteScreen m_normal_screen, m_alternate_screen, *m_screen;
diff --git a/src/vteseq-n.gperf b/src/vteseq-n.gperf
index eec89aee..7cd1de50 100644
--- a/src/vteseq-n.gperf
+++ b/src/vteseq-n.gperf
@@ -28,7 +28,7 @@ struct vteseq_n_struct {
 "tab", VTE_SEQUENCE_HANDLER(vte_sequence_handler_tab)
 "index", VTE_SEQUENCE_HANDLER(vte_sequence_handler_index)
 "decset", VTE_SEQUENCE_HANDLER(vte_sequence_handler_decset)
-#"repeat", VTE_SEQUENCE_HANDLER_NULL
+"repeat", VTE_SEQUENCE_HANDLER(vte_sequence_handler_repeat)
 "tab-set", VTE_SEQUENCE_HANDLER(vte_sequence_handler_tab_set)
 "decreset", VTE_SEQUENCE_HANDLER(vte_sequence_handler_decreset)
 "set-mode", VTE_SEQUENCE_HANDLER(vte_sequence_handler_set_mode)
diff --git a/src/vteseq.cc b/src/vteseq.cc
index 1391e153..304c8247 100644
--- a/src/vteseq.cc
+++ b/src/vteseq.cc
@@ -598,6 +598,14 @@ vte_set_focus_tracking_mode(VteTerminalPrivate *that,
         that->feed_focus_event_initial();
 }
 
+/* Repeat the last character once. */
+static void
+vte_sequence_handler_repeat_internal (VteTerminalPrivate *that, GValueArray *params)
+{
+        if (that->m_last_printable_char != 0)
+                that->insert_char (that->m_last_printable_char, false, true);
+}
+
 struct decset_t {
         gint16 setting;
         /* offset in VteTerminalPrivate (> 0) or VteScreen (< 0) */
@@ -2661,6 +2669,16 @@ vte_sequence_handler_reset_mode (VteTerminalPrivate *that, GValueArray *params)
 	}
 }
 
+/* Repeat the last character n times. */
+static void
+vte_sequence_handler_repeat (VteTerminalPrivate *that, GValueArray *params)
+{
+        vte_sequence_handler_multiple_limited (that,
+                                               params,
+                                               vte_sequence_handler_repeat_internal,
+                                               65535);
+}
+
 /* Set certain terminal attributes. */
 static void
 vte_sequence_handler_decset (VteTerminalPrivate *that, GValueArray *params)
