diff --git a/src/vte.cc b/src/vte.cc
index e8f0fcf..349af16 100644
--- a/src/vte.cc
+++ b/src/vte.cc
@@ -3497,6 +3497,36 @@ void
 VteTerminalPrivate::pty_termios_changed()
 {
         _vte_debug_print(VTE_DEBUG_IO, "Termios changed\n");
+
+        pty_termios_update();
+}
+
+void
+VteTerminalPrivate::pty_termios_update()
+{
+        if (!m_pty)
+                return;
+        int fd = vte_pty_get_fd(m_pty);
+        if (fd == -1)
+                return;
+
+        struct termios t;
+        if (tcgetattr(fd, &t) != 0) {
+                _vte_debug_print(VTE_DEBUG_IO, "Failed to get termios: %m");
+                return;
+        }
+
+        bool new_noecho = ((t.c_lflag & ICANON) && !(t.c_lflag & ECHO));
+        if (new_noecho == m_noecho)
+                return;
+
+        _vte_debug_print(VTE_DEBUG_IO, "Password mode (icanon, no echo) %s\n",
+                         new_noecho ? "entered" : "left");
+
+        m_noecho = new_noecho;
+
+        /* We show a special cursor for noecho mode, so need to invalidate */
+        invalidate_cursor_once();
 }
 
 void
@@ -3505,6 +3535,8 @@ VteTerminalPrivate::pty_scroll_lock_changed(bool locked)
         _vte_debug_print(VTE_DEBUG_IO, "Output %s (^%c)\n",
                          locked ? "stopped" : "started",
                          locked ? 'Q' : 'S');
+
+        m_scroll_locked = locked;
 }
 
 /*
@@ -4082,6 +4114,11 @@ next_match:
 						   &rect);
 	}
 
+        /* We can't get notified about termios changes in general,
+         * so we need to poll for it here.
+         */
+        pty_termios_update();
+
 	_vte_debug_print (VTE_DEBUG_WORK, ")");
 	_vte_debug_print (VTE_DEBUG_IO,
 			"%ld chars and %ld bytes in %" G_GSIZE_FORMAT " chunks left to process.\n",
@@ -4473,6 +4510,11 @@ vte_terminal_send(VteTerminal *terminal, const char *encoding,
 		if (crcount > 0) {
 			g_free(cooked);
 		}
+
+                /* We can't get notified about termios changes in general,
+                 * so we need to poll for it here.
+                 */
+                terminal->pvt->pty_termios_update();
 	}
 	return;
 }
@@ -9645,6 +9687,24 @@ vte_terminal_paint_cursor(VteTerminal *terminal)
 	x = item.x;
 	y = item.y;
 
+        if (terminal->pvt->m_scroll_locked) {
+                // TODO: better cursor shape
+                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;
+        }
+
+        if (terminal->pvt->m_noecho) {
+                // TODO: better cursor shape
+                vte_terminal_fill_rectangle(terminal, &bg,
+                                            x, y, width, height / 3);
+                vte_terminal_fill_rectangle(terminal, &bg,
+                                            x, y + height - height / 3, width, height / 3);
+                return;
+        }
+
         switch (terminal->pvt->decscusr_cursor_shape()) {
 
 		case VTE_CURSOR_SHAPE_IBEAM: {
diff --git a/src/vteinternal.hh b/src/vteinternal.hh
index 59e8b65..8363876 100644
--- a/src/vteinternal.hh
+++ b/src/vteinternal.hh
@@ -217,6 +217,8 @@ public:
 	gboolean pty_input_active;
 	GPid pty_pid;			/* pid of child using pty slave */
 	guint child_watch_source;
+        bool m_scroll_locked;
+        bool m_noecho;
 
 	/* Input data queues. */
 	const char *encoding;		/* the pty's encoding */
@@ -510,6 +512,7 @@ public:
         void connect_pty_write();
         void disconnect_pty_write();
 
+        void pty_termios_update();
         void pty_termios_changed();
         void pty_scroll_lock_changed(bool locked);
 
