Index: vte.c
===================================================================
RCS file: /cvs/gnome/vte/src/vte.c,v
retrieving revision 1.404
diff -u -3 -p -r1.404 vte.c
--- vte.c	2 May 2004 06:43:01 -0000	1.404
+++ vte.c	18 Aug 2004 21:33:00 -0000
@@ -368,6 +368,9 @@ struct _VteTerminalPrivate {
 	GdkColor bg_tint_color;
 	long bg_saturation;	/* out of VTE_SATURATION_MAX */
 
+	/* pending scroll updates */
+	guint scroll_update;
+
 	/* Key modifiers. */
 	GdkModifierType modifiers;
 
@@ -489,6 +492,8 @@ static char *vte_terminal_get_text_maybe
 						 gboolean include_trailing_spaces);
 static void _vte_terminal_disconnect_pty_read(VteTerminal *terminal);
 static void _vte_terminal_disconnect_pty_write(VteTerminal *terminal);
+static void vte_terminal_handle_scroll(VteTerminal *terminal);
+static void vte_terminal_stop_pending_scroll_update (VteTerminal *terminal);
 
 /* Free a no-longer-used row data array. */
 static void
@@ -657,6 +662,7 @@ vte_invalidate_all(VteTerminal *terminal
 	int width, height;
 
 	g_return_if_fail(VTE_IS_TERMINAL(terminal));
+	vte_terminal_stop_pending_scroll_update (terminal);
 	if (!GTK_IS_WIDGET(terminal)) {
 	       return;
 	}
@@ -1828,6 +1834,36 @@ vte_terminal_queue_adjustment_changed(Vt
 	}
 }
 
+static gboolean
+vte_terminal_update_scroll (gpointer data)
+{
+  VteTerminal *terminal = VTE_TERMINAL (data);
+
+  terminal->pvt->scroll_update = VTE_INVALID_SOURCE;
+  vte_terminal_handle_scroll (terminal);
+  return FALSE;
+}
+
+static void
+vte_terminal_require_scroll_update (VteTerminal *terminal)
+{
+	if (terminal->pvt->scroll_update == VTE_INVALID_SOURCE) {
+		terminal->pvt->scroll_update = g_idle_add_full (
+				G_PRIORITY_DEFAULT,
+				vte_terminal_update_scroll, 
+				terminal, NULL);
+	}
+}
+
+static void
+vte_terminal_stop_pending_scroll_update (VteTerminal *terminal)
+{
+	if (terminal->pvt->scroll_update != VTE_INVALID_SOURCE) {
+		g_source_remove(terminal->pvt->scroll_update);
+		terminal->pvt->scroll_update = VTE_INVALID_SOURCE;
+	}
+}
+
 /* Update the adjustment field of the widget.  This function should be called
  * whenever we add rows to or remove rows from the history or switch screens. */
 static void
@@ -1854,22 +1890,20 @@ vte_terminal_adjust_adjustments(VteTermi
 			delta, screen->scroll_delta);
 	}
 #endif
-	if (terminal->adjustment->lower != delta) {
-		terminal->adjustment->lower = delta;
-		changed = TRUE;
-	}
 
 	/* Snap the insert delta and the cursor position to be in the visible
-	 * area.  Leave the scrolling delta alone because it will be updated
-	 * when the adjustment changes. */
+	 * area.  */
 	screen->insert_delta = MAX(screen->insert_delta, delta);
 	screen->cursor_current.row = MAX(screen->cursor_current.row,
 					 screen->insert_delta);
 
 	/* The upper value is the number of rows which might be visible.  (Add
 	 * one to the cursor offset because it's zero-based.) */
-	rows = MAX(_vte_ring_next(terminal->pvt->screen->row_data),
-		   terminal->pvt->screen->cursor_current.row + 1);
+	rows = MAX (0,
+		    MAX(_vte_ring_next(terminal->pvt->screen->row_data),
+			terminal->pvt->screen->cursor_current.row + 1) 
+			- delta 
+			- terminal->row_count);
 	if (terminal->adjustment->upper != rows) {
 		terminal->adjustment->upper = rows;
 		changed = TRUE;
@@ -1896,13 +1930,10 @@ vte_terminal_adjust_adjustments(VteTermi
 	}
 
 	/* Set the scrollbar adjustment to where the screen wants it to be. */
-	if (floor(terminal->adjustment->value) !=
-	    terminal->pvt->screen->scroll_delta) {
-		/* This emits a "value-changed" signal, so no need to screw
-		 * with anything else for just this. */
-		gtk_adjustment_set_value(terminal->adjustment,
-					 terminal->pvt->screen->scroll_delta);
-	}
+	if (terminal->pvt->screen->scroll_delta < delta)
+		gtk_adjustment_set_value(terminal->adjustment, 0);
+	if (terminal->pvt->screen->scroll_delta - delta > terminal->adjustment->upper)
+		gtk_adjustment_set_value(terminal->adjustment, terminal->adjustment->upper);
 
 	/* If anything changed, signal that there was a change. */
 	if (changed == TRUE) {
@@ -1919,6 +1950,9 @@ vte_terminal_adjust_adjustments(VteTermi
 			vte_terminal_queue_adjustment_changed(terminal);
 		}
 	}
+	/* we can't scroll here, because scrolling might call gdk_window_move 
+	 * and that looks weird on screen */
+	vte_terminal_require_scroll_update (terminal);
 }
 
 /* Scroll up or down in the current screen. */
@@ -1951,12 +1985,9 @@ vte_terminal_scroll_pages(VteTerminal *t
 static void
 vte_terminal_maybe_scroll_to_top(VteTerminal *terminal)
 {
-	long delta;
 	g_return_if_fail(VTE_IS_TERMINAL(terminal));
-	if (floor(gtk_adjustment_get_value(terminal->adjustment)) !=
-	    _vte_ring_delta(terminal->pvt->screen->row_data)) {
-		delta = _vte_ring_delta(terminal->pvt->screen->row_data);
-		gtk_adjustment_set_value(terminal->adjustment, delta);
+	if (floor(gtk_adjustment_get_value(terminal->adjustment)) != 0) {
+		gtk_adjustment_set_value(terminal->adjustment, 0);
 	}
 }
 
@@ -1966,9 +1997,10 @@ vte_terminal_maybe_scroll_to_bottom(VteT
 {
 	glong delta;
 	g_return_if_fail(VTE_IS_TERMINAL(terminal));
+
+	delta = terminal->pvt->screen->insert_delta - _vte_ring_delta (terminal->pvt->screen->row_data);
 	if (floor(gtk_adjustment_get_value(terminal->adjustment)) !=
-	    terminal->pvt->screen->insert_delta) {
-		delta = terminal->pvt->screen->insert_delta;
+	    terminal->pvt->screen->insert_delta - _vte_ring_delta (terminal->pvt->screen->row_data)) {
 		gtk_adjustment_set_value(terminal->adjustment, delta);
 	}
 }
@@ -10804,7 +10836,7 @@ vte_terminal_set_size(VteTerminal *termi
 static void
 vte_terminal_handle_scroll(VteTerminal *terminal)
 {
-	long dy, adj;
+	long dy, adj, delta;
 	GtkWidget *widget;
 	VteScreen *screen;
 
@@ -10816,23 +10848,25 @@ vte_terminal_handle_scroll(VteTerminal *
 		return;
 	}
 
-	/* This may generate multiple redraws, so freeze it while we do them. */
-	gdk_window_freeze_updates(widget->window);
-
 	/* Read the new adjustment value and save the difference. */
-	adj = floor(gtk_adjustment_get_value(terminal->adjustment));
+	delta = _vte_ring_delta (terminal->pvt->screen->row_data);
+	adj = floor(gtk_adjustment_get_value(terminal->adjustment)) + delta;
 	dy = adj - screen->scroll_delta;
-	screen->scroll_delta = adj;
+	screen->scroll_delta = CLAMP (adj, delta, delta + terminal->adjustment->upper - 1);
+	vte_terminal_stop_pending_scroll_update (terminal);
 	if (dy != 0) {
+		/* This may generate multiple redraws, so freeze it while we do them. */
+		gdk_window_freeze_updates(widget->window);
+
 		vte_terminal_match_contents_clear(terminal);
 		vte_terminal_scroll_region(terminal, screen->scroll_delta,
 					   terminal->row_count, -dy);
 		vte_terminal_emit_text_scrolled(terminal, dy);
 		vte_terminal_emit_contents_changed(terminal);
-	}
 
-	/* Let the refreshing begin. */
-	gdk_window_thaw_updates(widget->window);
+		/* Let the refreshing begin. */
+		gdk_window_thaw_updates(widget->window);
+	}
 }
 
 /* Set the adjustment objects used by the terminal widget. */
@@ -11265,6 +11299,7 @@ vte_terminal_init(VteTerminal *terminal,
 	pvt->dec_saved = g_hash_table_new(g_direct_hash, g_direct_equal);
 	pvt->default_column_count = 80;
 	pvt->default_row_count = 24;
+	pvt->scroll_update = VTE_INVALID_SOURCE;
 
 	/* Setting the terminal type and size requires the PTY master to
 	 * be set up properly first. */
@@ -11896,6 +11931,7 @@ vte_terminal_finalize(GObject *object)
 		g_source_remove(terminal->pvt->processing_tag);
 		terminal->pvt->processing_tag = VTE_INVALID_SOURCE;
 	}
+	vte_terminal_stop_pending_scroll_update (terminal);
 
 	/* Discard any pending data. */
 	if (terminal->pvt->incoming != NULL) {
