8e81e3f1d2eff658808c6eb91c658eae1c60e0fe
diff --git a/src/marshal.list b/src/marshal.list
index 0276422..59522b7 100644
--- a/src/marshal.list
+++ b/src/marshal.list
@@ -1,3 +1,4 @@
+VOID:ENUM,INT
 VOID:INT,INT
 VOID:OBJECT,OBJECT
 VOID:STRING,UINT
diff --git a/src/vte.c b/src/vte.c
index b7ebb3f..51ef28a 100644
--- a/src/vte.c
+++ b/src/vte.c
@@ -132,6 +132,7 @@ static gboolean update_timeout (gpointer data);
 enum {
     COPY_CLIPBOARD,
     PASTE_CLIPBOARD,
+    MOVE_VIEWPORT,
     LAST_SIGNAL
 };
 static guint signals[LAST_SIGNAL];
@@ -2244,6 +2245,43 @@ vte_terminal_maybe_scroll_to_bottom(VteTerminal *terminal)
 }
 
 static void
+vte_terminal_move_viewport (VteTerminal *terminal,
+                            GtkMovementStep step,
+                            int count)
+{
+        switch (step) {
+                case GTK_MOVEMENT_DISPLAY_LINES:
+                        vte_terminal_scroll_lines(terminal, count);
+                        break;
+                case GTK_MOVEMENT_PAGES:
+                        vte_terminal_scroll_pages(terminal, count);
+                        break;
+                case GTK_MOVEMENT_BUFFER_ENDS:
+                        if (count == 1) {
+                                vte_terminal_maybe_scroll_to_bottom(terminal);
+                        } else if (count == -1) {
+                                vte_terminal_maybe_scroll_to_top(terminal);
+                        }
+                        break;
+                default:
+                        break;
+        }
+}
+
+static void
+vte_terminal_add_move_binding(GtkBindingSet *binding_set,
+                              guint keyval,
+                              guint modmask,
+                              GtkMovementStep step,
+                              int count)
+{
+        gtk_binding_entry_add_signal(binding_set, keyval, modmask,
+                                     "move-viewport", 2,
+                                     G_TYPE_ENUM, step,
+                                     G_TYPE_INT, count);
+}
+
+static void
 _vte_terminal_setup_utf8 (VteTerminal *terminal)
 {
   _vte_pty_set_utf8(terminal->pvt->pty_master,
@@ -4816,7 +4854,7 @@ vte_terminal_key_press(GtkWidget *widget, GdkEventKey *event)
 	int i;
 	const char *special = NULL;
 	struct termios tio;
-	gboolean scrolled = FALSE, steal = FALSE, modifier = FALSE, handled,
+	gboolean steal = FALSE, modifier = FALSE, handled,
 		 suppress_meta_esc = FALSE;
 	guint keyval = 0;
 	gunichar keychar = 0;
@@ -5009,60 +5047,6 @@ vte_terminal_key_press(GtkWidget *widget, GdkEventKey *event)
 			}
 			break;
 		/* Keypad/motion keys. */
-		case GDK_KP_Up:
-		case GDK_Up:
-			if (modifiers & GDK_CONTROL_MASK 
-                            && modifiers & GDK_SHIFT_MASK) {
-				vte_terminal_scroll_lines(terminal, -1);
-				scrolled = TRUE;
-				handled = TRUE;
-				suppress_meta_esc = TRUE;
-			}
-			break;
-		case GDK_KP_Down:
-		case GDK_Down:
-			if (modifiers & GDK_CONTROL_MASK
-                            && modifiers & GDK_SHIFT_MASK) {
-				vte_terminal_scroll_lines(terminal, 1);
-				scrolled = TRUE;
-				handled = TRUE;
-				suppress_meta_esc = TRUE;
-			}
-			break;
-		case GDK_KP_Page_Up:
-		case GDK_Page_Up:
-			if (modifiers & GDK_SHIFT_MASK) {
-				vte_terminal_scroll_pages(terminal, -1);
-				scrolled = TRUE;
-				handled = TRUE;
-				suppress_meta_esc = TRUE;
-			}
-			break;
-		case GDK_KP_Page_Down:
-		case GDK_Page_Down:
-			if (modifiers & GDK_SHIFT_MASK) {
-				vte_terminal_scroll_pages(terminal, 1);
-				scrolled = TRUE;
-				handled = TRUE;
-				suppress_meta_esc = TRUE;
-			}
-			break;
-		case GDK_KP_Home:
-		case GDK_Home:
-			if (modifiers & GDK_SHIFT_MASK) {
-				vte_terminal_maybe_scroll_to_top(terminal);
-				scrolled = TRUE;
-				handled = TRUE;
-			}
-			break;
-		case GDK_KP_End:
-		case GDK_End:
-			if (modifiers & GDK_SHIFT_MASK) {
-				vte_terminal_maybe_scroll_to_bottom(terminal);
-				scrolled = TRUE;
-				handled = TRUE;
-			}
-			break;
 		/* Let Shift +/- tweak the font, like XTerm does. */
 		case GDK_KP_Add:
 		case GDK_KP_Subtract:
@@ -5190,7 +5174,7 @@ vte_terminal_key_press(GtkWidget *widget, GdkEventKey *event)
 			g_free(normal);
 		}
 		/* Keep the cursor on-screen. */
-		if (!scrolled && !modifier &&
+		if (!modifier &&
 		    terminal->pvt->scroll_on_keystroke) {
 			vte_terminal_maybe_scroll_to_bottom(terminal);
 		}
@@ -11663,6 +11647,32 @@ vte_terminal_class_init(VteTerminalClass *klass)
                              g_cclosure_marshal_VOID__VOID,
 			     G_TYPE_NONE, 0);
 
+          /**
+          * VteTerminal::move-viewport:
+          * @terminal:
+          * @step: the granularity of the move, as a #GtkMovementStep
+          * @count: the number of @step units to move
+          *
+          * The ::move-viewport signal is a 
+          * <link linkend="keybinding-signals">keybinding signal</link> 
+          * which can be bound to key combinations to allow the user
+          * to move the viewport, i.e. change what part of the terminal view
+          * is visible in a containing scrolled window.
+          *
+          * There are no default bindings for this signal.
+          */
+          signals[MOVE_VIEWPORT] =
+            g_signal_new_class_handler(I_("move-viewport"),
+                                       G_OBJECT_CLASS_TYPE(klass),
+                                       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                                       G_CALLBACK(vte_terminal_move_viewport),
+                                       NULL, NULL,
+                                       _vte_marshal_VOID__ENUM_INT,
+                                       G_TYPE_NONE,
+                                       2,
+                                       GTK_TYPE_SCROLL_STEP,
+                                       G_TYPE_INT);
+
         /**
          * VteTerminal:allow-bold:
          *
@@ -12088,6 +12098,35 @@ vte_terminal_class_init(VteTerminalClass *klass)
 	gtk_binding_entry_add_signal(binding_set, GDK_F18, 0, "paste-clipboard", 0);
 	gtk_binding_entry_add_signal(binding_set, GDK_F20, 0, "copy-clipboard",0);
 
+        /* Scrolling */
+        vte_terminal_add_move_binding(binding_set, GDK_Up, GDK_SHIFT_MASK | GDK_CONTROL_MASK,
+                                      GTK_MOVEMENT_DISPLAY_LINES, -1);
+        vte_terminal_add_move_binding(binding_set, GDK_KP_Up, GDK_SHIFT_MASK | GDK_CONTROL_MASK,
+                                      GTK_MOVEMENT_DISPLAY_LINES, -1);
+        vte_terminal_add_move_binding(binding_set, GDK_Down, GDK_SHIFT_MASK | GDK_CONTROL_MASK,
+                                      GTK_MOVEMENT_DISPLAY_LINES, 1);
+        vte_terminal_add_move_binding(binding_set, GDK_KP_Down, GDK_SHIFT_MASK | GDK_CONTROL_MASK,
+                                      GTK_MOVEMENT_DISPLAY_LINES, 1);
+
+        vte_terminal_add_move_binding(binding_set, GDK_Page_Up, GDK_SHIFT_MASK,
+                                      GTK_MOVEMENT_PAGES, -1);
+        vte_terminal_add_move_binding(binding_set, GDK_KP_Page_Up, GDK_SHIFT_MASK,
+                                      GTK_MOVEMENT_PAGES, -1);
+        vte_terminal_add_move_binding(binding_set, GDK_Page_Down, GDK_SHIFT_MASK,
+                                      GTK_MOVEMENT_PAGES, 1);
+        vte_terminal_add_move_binding(binding_set, GDK_KP_Page_Down, GDK_SHIFT_MASK,
+                                      GTK_MOVEMENT_PAGES, 1);
+        
+        vte_terminal_add_move_binding(binding_set, GDK_Home, GDK_SHIFT_MASK,
+                                      GTK_MOVEMENT_BUFFER_ENDS, -1);
+        vte_terminal_add_move_binding(binding_set, GDK_KP_Home, GDK_SHIFT_MASK,
+                                      GTK_MOVEMENT_BUFFER_ENDS, -1);
+        vte_terminal_add_move_binding(binding_set, GDK_End, GDK_SHIFT_MASK,
+                                      GTK_MOVEMENT_BUFFER_ENDS, 1);
+        vte_terminal_add_move_binding(binding_set, GDK_KP_End, GDK_SHIFT_MASK,
+                                      GTK_MOVEMENT_BUFFER_ENDS, 1);
+
+        /* Timer */
 	process_timer = g_timer_new ();
 }
 
