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	20 Aug 2004 21:56:18 -0000
@@ -358,6 +358,8 @@ struct _VteTerminalPrivate {
 
 	/* Adjustment updates pending. */
 	gboolean adjustment_changed_tag;
+	glong adjustment_freeze_value;
+	glong adjustment_freeze_scroll_delta;
 
 	/* Background images/"transparency". */
 	gboolean bg_update_pending;
@@ -489,6 +491,7 @@ 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);
 
 /* Free a no-longer-used row data array. */
 static void
@@ -652,9 +655,7 @@ vte_invalidate_cells(VteTerminal *termin
 static void
 vte_invalidate_all(VteTerminal *terminal)
 {
-	GdkRectangle rect;
 	GtkWidget *widget;
-	int width, height;
 
 	g_return_if_fail(VTE_IS_TERMINAL(terminal));
 	if (!GTK_IS_WIDGET(terminal)) {
@@ -669,13 +670,7 @@ vte_invalidate_all(VteTerminal *terminal
 	}
 
 	/* Expose the entire widget area. */
-	width = height = 0;
-	gdk_drawable_get_size(widget->window, &width, &height);
-	rect.x = 0;
-	rect.y = 0;
-	rect.width = width;
-	rect.height = height;
-	gdk_window_invalidate_rect(widget->window, &rect, FALSE);
+	gdk_window_invalidate_rect(widget->window, NULL, FALSE);
 }
 
 /* Scroll a rectangular region up or down by a fixed number of lines. */
@@ -1828,6 +1823,54 @@ vte_terminal_queue_adjustment_changed(Vt
 	}
 }
 
+/* simple freeze/thaw implementation to stop the scrollbar from scrolling immediately
+ * note that this functions do not work cumulative */
+#define VTE_TERMINAL_ADJUSTMENT_IS_FROZEN(terminal) ((terminal)->pvt->adjustment_freeze_value >= 0)
+static void
+vte_terminal_freeze_adjustment (VteTerminal *terminal)
+{
+	g_return_if_fail (!VTE_TERMINAL_ADJUSTMENT_IS_FROZEN (terminal));
+	terminal->pvt->adjustment_freeze_value = terminal->pvt->screen->scroll_delta;
+	terminal->pvt->adjustment_freeze_scroll_delta = terminal->pvt->screen->scroll_delta;
+}
+
+static void
+vte_terminal_thaw_adjustment (VteTerminal *terminal)
+{
+	g_return_if_fail (VTE_TERMINAL_ADJUSTMENT_IS_FROZEN (terminal));
+	if (floor (terminal->adjustment->value) != terminal->pvt->adjustment_freeze_value) {
+		terminal->pvt->screen->scroll_delta = terminal->pvt->adjustment_freeze_scroll_delta;
+		terminal->pvt->adjustment_freeze_value = -1;
+		gtk_adjustment_value_changed (terminal->adjustment);
+	} else {
+		terminal->pvt->adjustment_freeze_value = -1;
+		if (terminal->pvt->screen->scroll_delta != terminal->pvt->adjustment_freeze_scroll_delta) {
+			terminal->pvt->screen->scroll_delta = terminal->pvt->adjustment_freeze_scroll_delta;
+			vte_terminal_handle_scroll (terminal);
+		}
+	}
+}
+
+static void
+vte_terminal_set_adjustment_value (VteTerminal *terminal, glong value)
+{
+	GtkAdjustment *adjustment = terminal->adjustment;
+	value = CLAMP (value, adjustment->lower, adjustment->upper);
+
+	if (value != floor (adjustment->value)) {
+		adjustment->value = value;
+		if (!VTE_TERMINAL_ADJUSTMENT_IS_FROZEN (terminal)) {
+			gtk_adjustment_value_changed (adjustment);
+		} else {
+			terminal->pvt->screen->scroll_delta = value + 
+				_vte_ring_delta (terminal->pvt->screen->row_data);
+		}
+	} else {
+		terminal->pvt->screen->scroll_delta = value + 
+				_vte_ring_delta (terminal->pvt->screen->row_data);
+	}
+}
+
 /* 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 +1897,19 @@ 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);
 	if (terminal->adjustment->upper != rows) {
 		terminal->adjustment->upper = rows;
 		changed = TRUE;
@@ -1896,21 +1936,22 @@ 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)
+		vte_terminal_set_adjustment_value(terminal, 0);
+	if (terminal->pvt->screen->scroll_delta - delta > MAX (0, terminal->adjustment->upper - terminal->row_count))
+		vte_terminal_set_adjustment_value(terminal, terminal->adjustment->upper - terminal->row_count);
+	if (floor(gtk_adjustment_get_value(terminal->adjustment)) + delta != terminal->pvt->screen->scroll_delta)
+		vte_terminal_set_adjustment_value(terminal, terminal->pvt->screen->scroll_delta - delta);
 
 	/* If anything changed, signal that there was a change. */
 	if (changed == TRUE) {
 #ifdef VTE_DEBUG
 		if (_vte_debug_on(VTE_DEBUG_IO)) {
-			fprintf(stderr, "Changed adjustment values "
-				"(delta = %ld, scroll = %ld).\n",
-				delta, terminal->pvt->screen->scroll_delta);
+			fprintf (stderr, "adjustment: [%ld, %ld, %ld] scroll_delta: %ld ring: [%ld, %ld]\n", 
+				(long) terminal->adjustment->lower, (long) terminal->adjustment->value, 
+				(long) terminal->adjustment->upper, terminal->pvt->screen->scroll_delta, 
+				_vte_ring_delta(terminal->pvt->screen->row_data),
+				_vte_ring_next(terminal->pvt->screen->row_data));
 		}
 #endif
 		if (immediate) {
@@ -1940,7 +1981,7 @@ vte_terminal_scroll_pages(VteTerminal *t
 			    terminal->adjustment->lower,
 			    terminal->adjustment->upper - terminal->row_count);
 	/* Tell the scrollbar to adjust itself. */
-	gtk_adjustment_set_value(terminal->adjustment, destination);
+	vte_terminal_set_adjustment_value(terminal, destination);
 	/* Clear dingus match set. */
 	vte_terminal_match_contents_clear(terminal);
 	/* Notify viewers that the contents have changed. */
@@ -1951,12 +1992,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) {
+		vte_terminal_set_adjustment_value(terminal, 0);
 	}
 }
 
@@ -1966,10 +2004,10 @@ vte_terminal_maybe_scroll_to_bottom(VteT
 {
 	glong delta;
 	g_return_if_fail(VTE_IS_TERMINAL(terminal));
-	if (floor(gtk_adjustment_get_value(terminal->adjustment)) !=
-	    terminal->pvt->screen->insert_delta) {
-		delta = terminal->pvt->screen->insert_delta;
-		gtk_adjustment_set_value(terminal->adjustment, delta);
+
+	delta = terminal->pvt->screen->insert_delta - _vte_ring_delta (terminal->pvt->screen->row_data);
+	if (floor(gtk_adjustment_get_value(terminal->adjustment)) != delta) {
+		vte_terminal_set_adjustment_value(terminal, delta);
 	}
 }
 
@@ -4661,6 +4699,7 @@ vte_sequence_handler_decset_internal(Vte
 		 NULL, NULL},
 	};
 
+	vte_terminal_thaw_adjustment (terminal);
 	/* Handle the setting. */
 	for (i = 0; i < G_N_ELEMENTS(settings); i++)
 	if (settings[i].setting == setting) {
@@ -4906,6 +4945,7 @@ vte_sequence_handler_decset_internal(Vte
 			  setting);
 	}
 #endif
+	vte_terminal_freeze_adjustment (terminal);
 	return again;
 }
 
@@ -7400,6 +7440,7 @@ vte_terminal_process_incoming(gpointer d
 	g_return_val_if_fail(VTE_IS_TERMINAL(data), FALSE);
 	widget = GTK_WIDGET(data);
 	terminal = VTE_TERMINAL(data);
+	vte_terminal_freeze_adjustment (terminal);
 
 	bottom = (terminal->pvt->screen->insert_delta ==
 		  terminal->pvt->screen->scroll_delta);
@@ -7713,6 +7754,7 @@ vte_terminal_process_incoming(gpointer d
 		}
 		terminal->pvt->processing_tag = VTE_INVALID_SOURCE;
 	}
+	vte_terminal_thaw_adjustment (terminal);
 
 #ifdef VTE_DEBUG
 	if (_vte_debug_on(VTE_DEBUG_IO)) {
@@ -10031,7 +10073,7 @@ vte_terminal_autoscroll(gpointer data)
 				    terminal->adjustment->lower,
 				    terminal->adjustment->upper -
 				    terminal->row_count);
-			gtk_adjustment_set_value(terminal->adjustment, adj);
+			vte_terminal_set_adjustment_value(terminal, adj);
 			extend = TRUE;
 		}
 #ifdef VTE_DEBUG
@@ -10048,7 +10090,7 @@ vte_terminal_autoscroll(gpointer data)
 				    terminal->adjustment->lower,
 				    terminal->adjustment->upper -
 				    terminal->row_count);
-			gtk_adjustment_set_value(terminal->adjustment, adj);
+			vte_terminal_set_adjustment_value(terminal, adj);
 			extend = TRUE;
 		}
 #ifdef VTE_DEBUG
@@ -10804,7 +10846,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 +10858,24 @@ 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);
 	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 +11308,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->adjustment_freeze_value = -1;
 
 	/* Setting the terminal type and size requires the PTY master to
 	 * be set up properly first. */
@@ -14135,7 +14179,7 @@ vte_terminal_scroll(GtkWidget *widget, G
 	}
 
 	new_value = CLAMP(new_value, adj->lower, adj->upper - adj->page_size);
-	gtk_adjustment_set_value(adj, new_value);
+	vte_terminal_set_adjustment_value(terminal, new_value);
 
 	return TRUE;
 }
