diff --git a/src/vte.cc b/src/vte.cc
index 8df3902..96b3d63 100644
--- a/src/vte.cc
+++ b/src/vte.cc
@@ -4502,8 +4502,35 @@ vte_terminal_send(VteTerminal *terminal, const char *encoding,
 		/* If there's a place for it to go, add the data to the
 		 * outgoing buffer. */
 		if ((cooked_length > 0) && (terminal->pvt->pty != NULL)) {
+                        struct termios t;
+                        tcgetattr(vte_pty_get_fd(terminal->pvt->pty), &t);
+
 			_vte_byte_array_append(terminal->pvt->outgoing,
 					   cooked, cooked_length);
+
+                        // TODO: handle VLNEXT; requires remembering the state.
+                        for (i = 0; i < cooked_length; i++) {
+                                cc_t c = cooked[i];
+                                if (terminal->pvt->scroll_locked) {
+                                        if (!(t.c_iflag & IXON) ||
+                                            ((t.c_iflag & IXANY) && !(c != 0 && c == t.c_cc[VSTOP])) ||
+                                            (c != 0 && c == t.c_cc[VSTART]) ||
+                                            ((t.c_lflag & ISIG) && c != 0 && c == t.c_cc[VINTR]) ||
+                                            ((t.c_lflag & ISIG) && c != 0 && c == t.c_cc[VQUIT]) ||
+                                            ((t.c_lflag & ISIG) && c != 0 && c == t.c_cc[VSUSP])) {
+                                                _vte_debug_print(VTE_DEBUG_KEYBOARD, "Scroll lock released\n");
+                                                terminal->pvt->scroll_locked = FALSE;
+                                                _vte_invalidate_cursor_once(terminal, FALSE);
+                                        }
+                                } else {
+                                        if ((t.c_iflag & IXON) && c != 0 && c == t.c_cc[VSTOP]) {
+                                                _vte_debug_print(VTE_DEBUG_KEYBOARD, "Scroll locked\n");
+                                                terminal->pvt->scroll_locked = TRUE;
+                                                _vte_invalidate_cursor_once(terminal, FALSE);
+                                        }
+                                }
+                        }
+
 			_VTE_DEBUG_IF(VTE_DEBUG_KEYBOARD) {
 				for (i = 0; i < cooked_length; i++) {
 					if ((((guint8) cooked[i]) < 32) ||
@@ -9997,6 +10024,14 @@ vte_terminal_paint_cursor(VteTerminal *terminal)
 	x = item.x;
 	y = item.y;
 
+        if (terminal->pvt->scroll_locked) {
+                vte_terminal_fill_rectangle(terminal, &bg,
+                                            x, y, width / 3, height);
+                vte_terminal_fill_rectangle(terminal, &bg,
+                                            x + width - width / 3, y, width / 3, height);
+                return;
+        }
+
         switch (_vte_terminal_decscusr_cursor_shape(terminal)) {
 
 		case VTE_CURSOR_SHAPE_IBEAM: {
diff --git a/src/vteinternal.hh b/src/vteinternal.hh
index d585365..41eb1a0 100644
--- a/src/vteinternal.hh
+++ b/src/vteinternal.hh
@@ -403,4 +403,6 @@ public:
         guint hscroll_policy : 1; /* unused */
 
         guint vscroll_policy : 1;
+
+        gboolean scroll_locked;
 };
