diff --git a/src/vte-private.h b/src/vte-private.h
index b6867f1..9b389fc 100644
--- a/src/vte-private.h
+++ b/src/vte-private.h
@@ -112,11 +112,6 @@ void _vte_terminal_handle_sequence(VteTerminal *terminal,
 				   const char *match,
 				   GValueArray *params);
 
-gboolean _vte_terminal_xy_to_grid(VteTerminal *terminal,
-                                  long x,
-                                  long y,
-                                  long *col,
-                                  long *row);
 gboolean _vte_terminal_size_to_grid_size(VteTerminal *terminal,
                                          long w,
                                          long h,
diff --git a/src/vte.cc b/src/vte.cc
index 716350a..2c21e1d 100644
--- a/src/vte.cc
+++ b/src/vte.cc
@@ -383,6 +383,63 @@ _vte_terminal_set_default_attributes(VteTerminal *terminal)
         terminal->pvt->fill_defaults = terminal->pvt->defaults;
 }
 
+static glong
+_vte_terminal_scroll_delta_pixel (VteTerminal *terminal)
+{
+        return round(terminal->pvt->screen->scroll_delta * terminal->pvt->char_height);
+}
+
+/* Pixel is relative to viewport, top padding excluded.
+ * Row is relative to the beginning of the terminal history, like {insert,scroll}_delta. */
+static glong
+_vte_terminal_pixel_to_row (VteTerminal *terminal,
+                            glong y)
+{
+        return (_vte_terminal_scroll_delta_pixel(terminal) + y) / terminal->pvt->char_height;
+}
+
+/* Row is relative to the beginning of the terminal history, like {insert,scroll}_delta.
+ * Pixel is relative to viewport, top padding excluded. */
+static glong
+_vte_terminal_row_to_pixel (VteTerminal *terminal,
+                            glong row)
+{
+        return row * terminal->pvt->char_height - (glong) round(terminal->pvt->screen->scroll_delta * terminal->pvt->char_height);
+}
+
+/* x, y are coordinates excluding the padding.
+ * col, row are in 0..width-1, 0..height-1.
+ * Returns FALSE if clicked over scrollback content; output values are unchanged then.
+ */
+static gboolean
+_vte_terminal_mouse_pixels_to_grid (VteTerminal *terminal,
+                                    long x, long y,
+                                    long *col, long *row)
+{
+        VteTerminalPrivate *pvt = terminal->pvt;
+        long c, r;
+
+        /* Vertical is tricky: combine smooth scrolling, extra area near the bottom,
+         * and scrollbar being dragged back a bit. */
+        /* Confine top padding clicks while still in pixels. */
+        y = MAX(y, 0);
+        r = _vte_terminal_pixel_to_row(terminal, y);
+        /* Clicking on scrollback area. */
+        if (r < terminal->pvt->screen->insert_delta)
+                return FALSE;
+        r -= terminal->pvt->screen->insert_delta;
+        /* Handle bottom padding now. */
+        r = MIN(r, terminal->pvt->row_count - 1);
+
+        /* After these, horizontal is piece of cake. */
+        c = x / pvt->char_width;
+        c = CLAMP (c, 0, pvt->column_count - 1);
+
+        *col = c;
+        *row = r;
+        return TRUE;
+}
+
 /* Cause certain cells to be repainted. */
 void
 _vte_invalidate_cells(VteTerminal *terminal,
@@ -390,7 +447,7 @@ _vte_invalidate_cells(VteTerminal *terminal,
 		      glong row_start, gint row_count)
 {
 	cairo_rectangle_int_t rect;
-	glong i;
+	GtkAllocation allocation;
 
 	if (G_UNLIKELY (!gtk_widget_get_realized(&terminal->widget)))
                 return;
@@ -404,32 +461,12 @@ _vte_invalidate_cells(VteTerminal *terminal,
 	}
 
 	_vte_debug_print (VTE_DEBUG_UPDATES,
-			"Invalidating cells at (%ld,%ld+%ld)x(%d,%d).\n",
+			"Invalidating cells at (%ld,%ld+%f)x(%d,%d).\n",
 			column_start, row_start,
-			(long)terminal->pvt->screen->scroll_delta,
+			terminal->pvt->screen->scroll_delta,
 			column_count, row_count);
 	_vte_debug_print (VTE_DEBUG_WORK, "?");
 
-	/* Subtract the scrolling offset from the row start so that the
-	 * resulting rectangle is relative to the visible portion of the
-	 * buffer. */
-	row_start -= terminal->pvt->screen->scroll_delta;
-
-	/* Ensure the start of region is on screen */
-	if (column_start > terminal->pvt->column_count ||
-			row_start > terminal->pvt->row_count) {
-		return;
-	}
-
-	/* Clamp the start values to reasonable numbers. */
-	i = row_start + row_count;
-	row_start = MAX (0, row_start);
-	row_count = CLAMP (i - row_start, 0, terminal->pvt->row_count);
-
-	i = column_start + column_count;
-	column_start = MAX (0, column_start);
-	column_count = CLAMP (i - column_start, 0 , terminal->pvt->column_count);
-
 	if (!column_count || !row_count) {
 		return;
 	}
@@ -439,30 +476,37 @@ _vte_invalidate_cells(VteTerminal *terminal,
 		return;
 	}
 
+	gtk_widget_get_allocation (&terminal->widget, &allocation);
+
 	/* Convert the column and row start and end to pixel values
 	 * by multiplying by the size of a character cell.
 	 * Always include the extra pixel border and overlap pixel.
 	 */
-	rect.x = column_start * terminal->pvt->char_width - 1;
-	if (column_start != 0) {
-		rect.x += terminal->pvt->padding.left;
-	}
-	rect.width = (column_start + column_count) * terminal->pvt->char_width + 3 + terminal->pvt->padding.left;
-	if (column_start + column_count == terminal->pvt->column_count) {
-		rect.width += terminal->pvt->padding.right;
-	}
+        rect.x = terminal->pvt->padding.left + column_start * terminal->pvt->char_width - 1;
+        if (rect.x <= terminal->pvt->padding.left)
+                rect.x = 0;
+        /* Temporarily misuse rect.width for the end x coordinate... */
+        rect.width = terminal->pvt->padding.left + (column_start + column_count) * terminal->pvt->char_width + 2; // why 2 and not 1?
+        if (rect.width >= terminal->pvt->padding.left + terminal->pvt->column_count * terminal->pvt->char_width)
+                rect.width = allocation.width;
+        /* ... fix that here */
 	rect.width -= rect.x;
 
-	rect.y = row_start * terminal->pvt->char_height - 1;
-	if (row_start != 0) {
-		rect.y += terminal->pvt->padding.top;
-	}
-	rect.height = (row_start + row_count) * terminal->pvt->char_height + 2 + terminal->pvt->padding.top;
-	if (row_start + row_count == terminal->pvt->row_count) {
-		rect.height += terminal->pvt->padding.bottom;
-	}
+        rect.y = terminal->pvt->padding.top + _vte_terminal_row_to_pixel(terminal, row_start) - 1;
+        if (rect.y <= terminal->pvt->padding.top)
+	        rect.y = 0;
+
+        /* Temporarily misuse rect.height for the end y coordinate... */
+        rect.height = terminal->pvt->padding.top + _vte_terminal_row_to_pixel(terminal, row_start + row_count) + 1;
+        if (rect.height >= terminal->pvt->padding.top + terminal->pvt->row_count * terminal->pvt->char_height)
+                rect.height = allocation.height;
+        /* ... fix that here */
 	rect.height -= rect.y;
 
+        /* Ensure the values make sense */
+        if (rect.width <= 0 || rect.height <= 0)
+                return;
+
 	_vte_debug_print (VTE_DEBUG_UPDATES,
 			"Invalidating pixels at (%d,%d)x(%d,%d).\n",
 			rect.x, rect.y, rect.width, rect.height);
@@ -1134,12 +1178,21 @@ static void
 vte_terminal_match_contents_refresh(VteTerminal *terminal)
 {
 	GArray *array;
+        long start_row, start_col, end_row, end_col;
+
+        start_row = floor(terminal->pvt->screen->scroll_delta);
+        start_col = 0;
+        end_row = ceil(terminal->pvt->screen->scroll_delta) + terminal->pvt->row_count - 1;
+        end_col = terminal->pvt->column_count - 1;
+
 	vte_terminal_match_contents_clear(terminal);
 	array = g_array_new(FALSE, TRUE, sizeof(struct _VteCharAttributes));
-	terminal->pvt->match_contents = vte_terminal_get_text(terminal,
-							      always_selected,
-							      NULL,
-							      array);
+        terminal->pvt->match_contents = vte_terminal_get_text_range(terminal,
+                                                                    start_row, start_col,
+                                                                    end_row, end_col,
+                                                                    always_selected,
+                                                                    NULL,
+                                                                    array);
 	terminal->pvt->match_attributes = array;
 }
 
@@ -2153,10 +2206,18 @@ vte_terminal_match_check_event(VteTerminal *terminal,
                 return FALSE;
         if (!gdk_event_get_coords(event, &x, &y))
                 return FALSE;
-        if (!_vte_terminal_xy_to_grid(terminal, x, y, &col, &row))
+
+        x -= terminal->pvt->padding.left;
+        y -= terminal->pvt->padding.top;
+        if (x < 0 || x >= terminal->pvt->column_count * terminal->pvt->char_width ||
+            y < 0 || y >= terminal->pvt->row_count * terminal->pvt->char_height)
                 return FALSE;
+        col = x / terminal->pvt->char_width;
+	row = _vte_terminal_pixel_to_row(terminal, y);
 
-        return vte_terminal_match_check(terminal, col, row, tag);
+        // FIXME subtracting floor(scroll_delta) which will be added back in
+        // vte_terminal_match_check is ugly
+        return vte_terminal_match_check(terminal, col, row - floor(terminal->pvt->screen->scroll_delta), tag);
 }
 
 /* Emit an adjustment changed signal on our adjustment object. */
@@ -2202,11 +2263,11 @@ vte_terminal_emit_adjustment_changed(VteTerminal *terminal)
 		terminal->pvt->adjustment_changed_pending = FALSE;
 	}
 	if (terminal->pvt->adjustment_value_changed_pending) {
-		glong v, delta;
+		double v, delta;
 		_vte_debug_print(VTE_DEBUG_SIGNALS,
 				"Emitting adjustment_value_changed.\n");
 		terminal->pvt->adjustment_value_changed_pending = FALSE;
-		v = round (gtk_adjustment_get_value(terminal->pvt->vadjustment));
+		v = gtk_adjustment_get_value(terminal->pvt->vadjustment);
 		if (v != terminal->pvt->screen->scroll_delta) {
 			/* this little dance is so that the scroll_delta is
 			 * updated immediately, but we still handled scrolling
@@ -2229,9 +2290,12 @@ vte_terminal_queue_adjustment_changed(VteTerminal *terminal)
 }
 
 static void
-vte_terminal_queue_adjustment_value_changed(VteTerminal *terminal, glong v)
+vte_terminal_queue_adjustment_value_changed(VteTerminal *terminal, double v)
 {
 	if (v != terminal->pvt->screen->scroll_delta) {
+                _vte_debug_print(VTE_DEBUG_ADJ,
+                                 "Adjustment value changed to %f\n",
+                                 v);
 		terminal->pvt->screen->scroll_delta = v;
 		terminal->pvt->adjustment_value_changed_pending = TRUE;
 		add_update_timeout (terminal);
@@ -2239,7 +2303,7 @@ vte_terminal_queue_adjustment_value_changed(VteTerminal *terminal, glong v)
 }
 
 static void
-vte_terminal_queue_adjustment_value_changed_clamped(VteTerminal *terminal, glong v)
+vte_terminal_queue_adjustment_value_changed_clamped(VteTerminal *terminal, double v)
 {
 	gdouble lower, upper;
 
@@ -2340,10 +2404,15 @@ _vte_terminal_adjust_adjustments_full (VteTerminal *terminal)
 static void
 vte_terminal_scroll_lines(VteTerminal *terminal, gint lines)
 {
-	glong destination;
+	double destination;
 	_vte_debug_print(VTE_DEBUG_ADJ, "Scrolling %d lines.\n", lines);
 	/* Calculate the ideal position where we want to be before clamping. */
 	destination = terminal->pvt->screen->scroll_delta;
+        /* Snap to whole cell offset. */
+        if (lines > 0)
+                destination = floor(destination);
+        else if (lines < 0)
+                destination = ceil(destination);
 	destination += lines;
 	/* Tell the scrollbar to adjust itself. */
 	vte_terminal_queue_adjustment_value_changed_clamped (terminal, destination);
@@ -5995,40 +6064,6 @@ vte_terminal_paste_cb(GtkClipboard *clipboard, const gchar *text, gpointer data)
 	}
 }
 
-/**
- * _vte_terminal_xy_to_grid:
- * @x: the X coordinate
- * @y: the Y coordinate
- * @col: return location to store the column
- * @row: return location to store the row
- *
- * Translates from widget coordinates to grid coordinates.
- *
- * If the coordinates are outside the grid, returns %FALSE.
- */
-gboolean
-_vte_terminal_xy_to_grid(VteTerminal *terminal,
-                         long x,
-                         long y,
-                         long *col,
-                         long *row)
-{
-        VteTerminalPrivate *pvt = terminal->pvt;
-        long c, r;
-
-        /* FIXMEchpe: is this correct for RTL? */
-        c = (x - pvt->padding.left) / pvt->char_width;
-        r = (y - pvt->padding.top) / pvt->char_height;
-
-        if ((c < 0 || c >= pvt->column_count) ||
-            (r < 0 || r >= pvt->row_count))
-          return FALSE;
-
-        *col = c;
-        *row = r;
-        return TRUE;
-}
-
 /*
  * _vte_terminal_size_to_grid_size:
  * @w: the width in px
@@ -6119,11 +6154,9 @@ vte_terminal_feed_mouse_event(VteTerminal *terminal,
 		cb |= 32;
 	}
 
-	/* Clamp the cursor coordinates. Make them 1-based. */
-	cx = CLAMP(1 + col,
-		   1, terminal->pvt->column_count);
-	cy = CLAMP(1 + row,
-		   1, terminal->pvt->row_count);
+	/* Make coordinates 1-based. */
+	cx = col + 1;
+	cy = row + 1;
 
 	/* Check the extensions in decreasing order of preference. Encoding the release event above assumes that 1006 comes first. */
 	if (terminal->pvt->mouse_xterm_extension) {
@@ -6148,13 +6181,12 @@ vte_terminal_send_mouse_button_internal(VteTerminal *terminal,
 					long         x,
 					long         y)
 {
-	int width = terminal->pvt->char_width;
-	int height = terminal->pvt->char_height;
-	long col = (x - terminal->pvt->padding.left) / width;
-        long row = (y - terminal->pvt->padding.top) / height -
-                   (terminal->pvt->screen->insert_delta - terminal->pvt->screen->scroll_delta);
+        long col, row;
 
-        if (row < 0)
+        if (!_vte_terminal_mouse_pixels_to_grid (terminal,
+                                                 x - terminal->pvt->padding.left,
+                                                 y - terminal->pvt->padding.top,
+                                                 &col, &row))
                 return;
 
 	vte_terminal_feed_mouse_event(terminal, button, FALSE /* not drag */, is_release, col, row);
@@ -6232,14 +6264,13 @@ vte_terminal_maybe_send_mouse_button(VteTerminal *terminal,
 static gboolean
 vte_terminal_maybe_send_mouse_drag(VteTerminal *terminal, GdkEventMotion *event)
 {
-	int width = terminal->pvt->char_width;
-	int height = terminal->pvt->char_height;
-	long col = ((long) event->x - terminal->pvt->padding.left) / width;
-        long row = ((long) event->y - terminal->pvt->padding.top) / height -
-                   (terminal->pvt->screen->insert_delta - terminal->pvt->screen->scroll_delta);
+        long col, row;
         int button;
 
-        if (row < 0)
+        if (!_vte_terminal_mouse_pixels_to_grid (terminal,
+                                                 (long) event->x - terminal->pvt->padding.left,
+                                                 (long) event->y - terminal->pvt->padding.top,
+                                                 &col, &row))
                 return FALSE;
 
 	/* First determine if we even want to send notification. */
@@ -6255,8 +6286,8 @@ vte_terminal_maybe_send_mouse_drag(VteTerminal *terminal, GdkEventMotion *event)
 			}
 			/* the xterm doc is not clear as to whether
 			 * all-tracking also sends degenerate same-cell events */
-			if (col == terminal->pvt->mouse_last_x / width &&
-			    row == terminal->pvt->mouse_last_y / height)
+                        if (col == terminal->pvt->mouse_last_col &&
+                            row == terminal->pvt->mouse_last_row)
 				return FALSE;
 		}
 		break;
@@ -6311,10 +6342,8 @@ vte_terminal_match_hilite_clear(VteTerminal *terminal)
 static gboolean
 cursor_inside_match (VteTerminal *terminal, long x, long y)
 {
-	gint width = terminal->pvt->char_width;
-	gint height = terminal->pvt->char_height;
-	glong col = x / width;
-	glong row = y / height + terminal->pvt->screen->scroll_delta;
+	glong col = x / terminal->pvt->char_width;
+	glong row = _vte_terminal_pixel_to_row(terminal, y);
 	if (terminal->pvt->match_start.row == terminal->pvt->match_end.row) {
 		return row == terminal->pvt->match_start.row &&
 			col >= terminal->pvt->match_start.col &&
@@ -6367,28 +6396,20 @@ vte_terminal_match_hilite_hide(VteTerminal *terminal)
 static void
 vte_terminal_match_hilite_update(VteTerminal *terminal, long x, long y)
 {
-	int start, end, width, height;
+	int start, end;
 	char *match;
 	struct _VteCharAttributes *attr;
-	VteScreen *screen;
-	long delta;
-
-	width = terminal->pvt->char_width;
-	height = terminal->pvt->char_height;
 
 	/* Check for matches. */
-	screen = terminal->pvt->screen;
-	delta = screen->scroll_delta;
-
 	_vte_debug_print(VTE_DEBUG_EVENTS,
 			"Match hilite update (%ld, %ld) -> %ld, %ld\n",
 			x, y,
-			x / width,
-			y / height + delta);
+			x / terminal->pvt->char_width,
+			_vte_terminal_pixel_to_row(terminal, y));
 
 	match = vte_terminal_match_check_internal(terminal,
-						  x / width,
-						  y / height + delta,
+						  x / terminal->pvt->char_width,
+						  _vte_terminal_pixel_to_row(terminal, y),
 						  &terminal->pvt->match_tag,
 						  &start,
 						  &end);
@@ -6461,12 +6482,8 @@ vte_terminal_match_hilite_update(VteTerminal *terminal, long x, long y)
 static void
 vte_terminal_match_hilite(VteTerminal *terminal, long x, long y)
 {
-	int width, height;
 	GtkAllocation allocation;
 
-	width = terminal->pvt->char_width;
-	height = terminal->pvt->char_height;
-
 	gtk_widget_get_allocation (&terminal->widget, &allocation);
 
 	/* if the cursor is not above a cell, skip */
@@ -6477,8 +6494,8 @@ vte_terminal_match_hilite(VteTerminal *terminal, long x, long y)
 
 	/* If the pointer hasn't moved to another character cell, then we
 	 * need do nothing. */
-	if (x / width  == terminal->pvt->mouse_last_x / width &&
-	    y / height == terminal->pvt->mouse_last_y / height) {
+	if (x / terminal->pvt->char_width  == terminal->pvt->mouse_last_x / terminal->pvt->char_width &&
+	    _vte_terminal_pixel_to_row(terminal, y) == _vte_terminal_pixel_to_row(terminal, terminal->pvt->mouse_last_y)) {
 		terminal->pvt->show_match = terminal->pvt->match != NULL;
 		return;
 	}
@@ -7204,8 +7221,6 @@ static void
 vte_terminal_start_selection(VteTerminal *terminal, long x, long y,
 			     enum vte_selection_type selection_type)
 {
-	long delta;
-
 	if (terminal->pvt->selection_block_mode)
 		selection_type = selection_type_char;
 
@@ -7213,10 +7228,9 @@ vte_terminal_start_selection(VteTerminal *terminal, long x, long y,
 	vte_terminal_confine_coordinates(terminal, &x, &y);
 
 	/* Record that we have the selection, and where it started. */
-	delta = terminal->pvt->screen->scroll_delta;
 	terminal->pvt->has_selection = TRUE;
 	terminal->pvt->selection_last.x = x;
-	terminal->pvt->selection_last.y = y + (terminal->pvt->char_height * delta);
+	terminal->pvt->selection_last.y = _vte_terminal_scroll_delta_pixel(terminal) + y;
 
 	/* Decide whether or not to restart on the next drag. */
 	switch (selection_type) {
@@ -7496,9 +7510,8 @@ static void
 vte_terminal_extend_selection(VteTerminal *terminal, long x, long y,
 			      gboolean always_grow, gboolean force)
 {
-	VteScreen *screen;
 	int width, height;
-	long delta, residual;
+	long residual;
 	struct selection_event_coords *origin, *last, *start, *end;
 	VteVisualPosition old_start, old_end, *sc, *ec, *so, *eo;
 	gboolean invalidate_selected = FALSE;
@@ -7510,15 +7523,11 @@ vte_terminal_extend_selection(VteTerminal *terminal, long x, long y,
 	/* Confine coordinates into the visible area. (#563024, #722635c7) */
 	vte_terminal_confine_coordinates(terminal, &x, &y);
 
-	screen = terminal->pvt->screen;
 	old_start = terminal->pvt->selection_start;
 	old_end = terminal->pvt->selection_end;
 	so = &old_start;
 	eo = &old_end;
 
-	/* Convert the event coordinates to cell coordinates. */
-	delta = screen->scroll_delta;
-
 	/* If we're restarting on a drag, then mark this as the start of
 	 * the selected block. */
 	if (terminal->pvt->selecting_restart) {
@@ -7544,7 +7553,7 @@ vte_terminal_extend_selection(VteTerminal *terminal, long x, long y,
 	origin = &terminal->pvt->selection_origin;
 	if (terminal->pvt->selection_block_mode) {
 		last->x = x;
-		last->y = y + height * delta;
+		last->y = _vte_terminal_scroll_delta_pixel(terminal) + y;
 
 		/* We don't support always_grow in block mode */
 		if (always_grow)
@@ -7562,7 +7571,7 @@ vte_terminal_extend_selection(VteTerminal *terminal, long x, long y,
 	} else {
 		if (!always_grow) {
 			last->x = x;
-			last->y = y + height * delta;
+			last->y = _vte_terminal_scroll_delta_pixel(terminal) + y;
 		}
 
 		if ((origin->y / height < last->y / height) ||
@@ -7581,15 +7590,15 @@ vte_terminal_extend_selection(VteTerminal *terminal, long x, long y,
 		 * closer to the new point. */
 		if (always_grow) {
 			/* New endpoint is before existing selection. */
-			if ((y / height < ((start->y / height) - delta)) ||
-			    ((y / height == ((start->y / height) - delta)) &&
+			if ((y / height < ((start->y / height) /* - delta wtf ??? fixme */)) ||
+			    ((y / height == ((start->y / height) /* - delta wtf ??? fixme */)) &&
 			     (x / width < start->x / width))) {
 				start->x = x;
-				start->y = y + height * delta;
+				start->y = _vte_terminal_scroll_delta_pixel(terminal) + y;
 			} else {
 				/* New endpoint is after existing selection. */
 				end->x = x;
-				end->y = y + height * delta;
+				end->y = _vte_terminal_scroll_delta_pixel(terminal) + y;
 			}
 		}
 	}
@@ -7865,7 +7874,8 @@ vte_terminal_motion_notify(GtkWidget *widget, GdkEventMotion *event)
 	_vte_debug_print(VTE_DEBUG_EVENTS,
 			"Motion notify (%ld,%ld) [%ld, %ld].\n",
 			x, y,
-			x / width, y / height + terminal->pvt->screen->scroll_delta);
+			x / width,
+                        _vte_terminal_pixel_to_row(terminal, y));
 
 	vte_terminal_read_modifiers (terminal, (GdkEvent*) event);
 
@@ -7924,6 +7934,10 @@ vte_terminal_motion_notify(GtkWidget *widget, GdkEventMotion *event)
 	/* Save the pointer coordinates for later use. */
 	terminal->pvt->mouse_last_x = x;
 	terminal->pvt->mouse_last_y = y;
+        _vte_terminal_mouse_pixels_to_grid (terminal,
+                                            x, y,
+                                            &terminal->pvt->mouse_last_col,
+                                            &terminal->pvt->mouse_last_row);
 
 	return handled;
 }
@@ -7933,7 +7947,6 @@ static gint
 vte_terminal_button_press(GtkWidget *widget, GdkEventButton *event)
 {
 	VteTerminal *terminal;
-	long height, width, delta;
 	gboolean handled = FALSE;
 	gboolean start_selecting = FALSE, extend_selecting = FALSE;
 	long cellx, celly;
@@ -7944,10 +7957,6 @@ vte_terminal_button_press(GtkWidget *widget, GdkEventButton *event)
 	x = event->x - terminal->pvt->padding.left;
 	y = event->y - terminal->pvt->padding.top;
 
-	height = terminal->pvt->char_height;
-	width = terminal->pvt->char_width;
-	delta = terminal->pvt->screen->scroll_delta;
-
 	vte_terminal_match_hilite(terminal, x, y);
 
 	_vte_terminal_set_pointer_visible(terminal, TRUE);
@@ -7955,15 +7964,15 @@ vte_terminal_button_press(GtkWidget *widget, GdkEventButton *event)
 	vte_terminal_read_modifiers (terminal, (GdkEvent*) event);
 
 	/* Convert the event coordinates to cell coordinates. */
-	cellx = x / width;
-	celly = y / height + delta;
+	cellx = x / terminal->pvt->char_width;
+	celly = _vte_terminal_pixel_to_row(terminal, y);
 
 	switch (event->type) {
 	case GDK_BUTTON_PRESS:
 		_vte_debug_print(VTE_DEBUG_EVENTS,
 				"Button %d single-click at (%ld,%ld)\n",
 				event->button,
-				x, y + terminal->pvt->char_height * delta);
+				x, _vte_terminal_scroll_delta_pixel(terminal) + y);
 		/* Handle this event ourselves. */
 		switch (event->button) {
 		case 1:
@@ -8047,7 +8056,7 @@ vte_terminal_button_press(GtkWidget *widget, GdkEventButton *event)
 		_vte_debug_print(VTE_DEBUG_EVENTS,
 				"Button %d double-click at (%ld,%ld)\n",
 				event->button,
-				x, y + (terminal->pvt->char_height * delta));
+				x, _vte_terminal_scroll_delta_pixel(terminal) + y);
 		switch (event->button) {
 		case 1:
 			if (terminal->pvt->selecting_after_threshold) {
@@ -8073,7 +8082,7 @@ vte_terminal_button_press(GtkWidget *widget, GdkEventButton *event)
 		_vte_debug_print(VTE_DEBUG_EVENTS,
 				"Button %d triple-click at (%ld,%ld).\n",
 				event->button,
-				x, y + (terminal->pvt->char_height * delta));
+				x, _vte_terminal_scroll_delta_pixel(terminal) + y);
 		switch (event->button) {
 		case 1:
                         if ((terminal->pvt->mouse_handled_buttons & 1) != 0) {
@@ -8097,6 +8106,10 @@ vte_terminal_button_press(GtkWidget *widget, GdkEventButton *event)
                 terminal->pvt->mouse_pressed_buttons |= (1 << (event->button - 1));
 	terminal->pvt->mouse_last_x = x;
 	terminal->pvt->mouse_last_y = y;
+        _vte_terminal_mouse_pixels_to_grid (terminal,
+                                            x, y,
+                                            &terminal->pvt->mouse_last_col,
+                                            &terminal->pvt->mouse_last_row);
 
 	return handled;
 }
@@ -8153,6 +8166,10 @@ vte_terminal_button_release(GtkWidget *widget, GdkEventButton *event)
                 terminal->pvt->mouse_pressed_buttons &= ~(1 << (event->button - 1));
 	terminal->pvt->mouse_last_x = x;
 	terminal->pvt->mouse_last_y = y;
+        _vte_terminal_mouse_pixels_to_grid (terminal,
+                                            x, y,
+                                            &terminal->pvt->mouse_last_col,
+                                            &terminal->pvt->mouse_last_row);
 	terminal->pvt->selecting_after_threshold = FALSE;
 
 	return handled;
@@ -8583,19 +8600,19 @@ vte_terminal_screen_set_size(VteTerminal *terminal, VteScreen *screen, glong old
 	gboolean was_scrolled_to_top = (screen->scroll_delta == _vte_ring_delta(ring));
 	gboolean was_scrolled_to_bottom = (screen->scroll_delta == screen->insert_delta);
 	glong old_top_lines;
-	glong new_scroll_delta;
+	double new_scroll_delta;
 
         if (terminal->pvt->selection_block_mode && do_rewrap && old_columns != terminal->pvt->column_count)
                 vte_terminal_deselect_all(terminal);
 
 	_vte_debug_print(VTE_DEBUG_RESIZE,
 			"Resizing %s screen\n"
-			"Old  insert_delta=%ld  scroll_delta=%ld\n"
-                        "     cursor (absolute)  row=%ld  (visual line %ld)  col=%ld\n"
+			"Old  insert_delta=%ld  scroll_delta=%f\n"
+                        "     cursor (absolute)  row=%ld  col=%ld\n"
 			"     cursor_saved (relative to insert_delta)  row=%ld  col=%ld\n",
 			screen == &terminal->pvt->normal_screen ? "normal" : "alternate",
 			screen->insert_delta, screen->scroll_delta,
-                        terminal->pvt->cursor.row, terminal->pvt->cursor.row - screen->scroll_delta + 1, terminal->pvt->cursor.col,
+                        terminal->pvt->cursor.row, terminal->pvt->cursor.col,
                         screen->saved.cursor.row, screen->saved.cursor.col);
 
         cursor_saved_absolute.row = screen->saved.cursor.row + screen->insert_delta;
@@ -8681,6 +8698,8 @@ vte_terminal_screen_set_size(VteTerminal *terminal, VteScreen *screen, glong old
 			   TODO: What would be the best behavior if the bottom of the screen is a
 			   soft line break, i.e. only a partial line is visible at the bottom? */
 			new_scroll_delta = below_viewport.row - terminal->pvt->row_count;
+			/* Keep the old fractional part. */
+			new_scroll_delta += screen->scroll_delta - floor(screen->scroll_delta);
 			_vte_debug_print(VTE_DEBUG_RESIZE,
 					"Scroll so bottom row stays\n");
 		}
@@ -8692,11 +8711,11 @@ vte_terminal_screen_set_size(VteTerminal *terminal, VteScreen *screen, glong old
         screen->saved.cursor.col = cursor_saved_absolute.col;
 
 	_vte_debug_print(VTE_DEBUG_RESIZE,
-			"New  insert_delta=%ld  scroll_delta=%ld\n"
-                        "     cursor (absolute)  row=%ld  (visual line %ld)  col=%ld\n"
+			"New  insert_delta=%ld  scroll_delta=%f\n"
+                        "     cursor (absolute)  row=%ld  col=%ld\n"
 			"     cursor_saved (relative to insert_delta)  row=%ld  col=%ld\n\n",
 			screen->insert_delta, new_scroll_delta,
-                        terminal->pvt->cursor.row, terminal->pvt->cursor.row - new_scroll_delta + 1, terminal->pvt->cursor.col,
+                        terminal->pvt->cursor.row, terminal->pvt->cursor.col,
                         screen->saved.cursor.row, screen->saved.cursor.col);
 
 	if (screen == terminal->pvt->screen)
@@ -8777,13 +8796,13 @@ vte_terminal_set_size(VteTerminal *terminal, glong columns, glong rows)
 static void
 vte_terminal_handle_scroll(VteTerminal *terminal)
 {
-	long dy, adj;
+	double dy, adj;
 	VteScreen *screen;
 
 	screen = terminal->pvt->screen;
 
 	/* Read the new adjustment value and save the difference. */
-	adj = round (gtk_adjustment_get_value(terminal->pvt->vadjustment));
+	adj = gtk_adjustment_get_value(terminal->pvt->vadjustment);
 	dy = adj - screen->scroll_delta;
 	screen->scroll_delta = adj;
 
@@ -8796,9 +8815,9 @@ vte_terminal_handle_scroll(VteTerminal *terminal)
 
 	if (dy != 0) {
 		_vte_debug_print(VTE_DEBUG_ADJ,
-			    "Scrolling by %ld\n", dy);
+			    "Scrolling by %f\n", dy);
 		_vte_terminal_scroll_region(terminal, screen->scroll_delta,
-					   terminal->pvt->row_count, -dy);
+					   terminal->pvt->row_count, dy < 0 ? -dy + 1 : -dy - 1); // FIXME
 		vte_terminal_emit_text_scrolled(terminal, dy);
 		_vte_terminal_queue_contents_changed(terminal);
 	} else {
@@ -10397,15 +10416,21 @@ vte_terminal_expand_region (VteTerminal *terminal, cairo_region_t *region, const
 	int width, height;
 	int row, col, row_stop, col_stop;
 	cairo_rectangle_int_t rect;
+	GtkAllocation allocation;
 
 	width = terminal->pvt->char_width;
 	height = terminal->pvt->char_height;
 
+	gtk_widget_get_allocation (&terminal->widget, &allocation);
+
 	/* increase the paint by one pixel on all sides to force the
 	 * inclusion of neighbouring cells */
-	row = MAX(0, (area->y - terminal->pvt->padding.top - 1) / height);
-	row_stop = MIN(howmany(area->height + area->y - terminal->pvt->padding.top + 1, height),
-		       terminal->pvt->row_count);
+        row = _vte_terminal_pixel_to_row(terminal, MAX(0, area->y - terminal->pvt->padding.top - 1));
+        /* Both the value given by MIN() and row_stop are exclusive.
+         * _vte_terminal_pixel_to_row expects an actual value corresponding
+         * to the bottom visible pixel, hence the - 1 + 1 magic. */
+        row_stop = _vte_terminal_pixel_to_row(terminal, MIN(area->height + area->y - terminal->pvt->padding.top + 1,
+                                                            allocation.height - terminal->pvt->padding.bottom) - 1) + 1;
 	if (row_stop <= row) {
 		return;
 	}
@@ -10419,7 +10444,7 @@ vte_terminal_expand_region (VteTerminal *terminal, cairo_region_t *region, const
 	rect.x = col*width + terminal->pvt->padding.left;
 	rect.width = (col_stop - col) * width;
 
-	rect.y = row*height + terminal->pvt->padding.top;
+	rect.y = _vte_terminal_row_to_pixel(terminal, row) + terminal->pvt->padding.top;
 	rect.height = (row_stop - row)*height;
 
 	/* the rect must be cell aligned to avoid overlapping XY bands */
@@ -10439,17 +10464,23 @@ static void
 vte_terminal_paint_area (VteTerminal *terminal, const GdkRectangle *area)
 {
 	VteScreen *screen;
-	int width, height, delta;
+	int width, height;
 	int row, col, row_stop, col_stop;
+	GtkAllocation allocation;
 
 	screen = terminal->pvt->screen;
 
 	width = terminal->pvt->char_width;
 	height = terminal->pvt->char_height;
 
-	row = MAX(0, (area->y - terminal->pvt->padding.top) / height);
-	row_stop = MIN((area->height + area->y - terminal->pvt->padding.top) / height,
-		       terminal->pvt->row_count);
+	gtk_widget_get_allocation (&terminal->widget, &allocation);
+
+        row = _vte_terminal_pixel_to_row(terminal, MAX(0, area->y - terminal->pvt->padding.top));
+        /* Both the value given by MIN() and row_stop are exclusive.
+         * _vte_terminal_pixel_to_row expects an actual value corresponding
+         * to the bottom visible pixel, hence the - 1 + 1 magic. */
+        row_stop = _vte_terminal_pixel_to_row(terminal, MIN(area->height + area->y - terminal->pvt->padding.top,
+                                                            allocation.height - terminal->pvt->padding.bottom) - 1) + 1;
 	if (row_stop <= row) {
 		return;
 	}
@@ -10473,13 +10504,12 @@ vte_terminal_paint_area (VteTerminal *terminal, const GdkRectangle *area)
 
 	/* Now we're ready to draw the text.  Iterate over the rows we
 	 * need to draw. */
-	delta = screen->scroll_delta;
 	vte_terminal_draw_rows(terminal,
 			      screen,
-			      row + delta, row_stop - row,
+			      row, row_stop - row,
 			      col, col_stop - col,
 			      col * width,
-			      row * height,
+			      _vte_terminal_row_to_pixel(terminal, row),
 			      width,
 			      height);
 }
@@ -10487,11 +10517,10 @@ vte_terminal_paint_area (VteTerminal *terminal, const GdkRectangle *area)
 static void
 vte_terminal_paint_cursor(VteTerminal *terminal)
 {
-	VteScreen *screen;
 	const VteCell *cell;
 	struct _vte_draw_text_request item;
-	int row, drow, col;
-	long width, height, delta, cursor_width;
+	int drow, col;
+	long width, height, cursor_width;
 	guint fore, back;
 	PangoColor bg;
 	int x, y;
@@ -10500,16 +10529,13 @@ vte_terminal_paint_cursor(VteTerminal *terminal)
 	if (!terminal->pvt->cursor_visible)
 		return;
 
-	screen = terminal->pvt->screen;
-	delta = screen->scroll_delta;
         col = terminal->pvt->cursor.col;
         drow = terminal->pvt->cursor.row;
-	row = drow - delta;
 	width = terminal->pvt->char_width;
 	height = terminal->pvt->char_height;
 
-	if ((CLAMP(col, 0, terminal->pvt->column_count - 1) != col) ||
-	    (CLAMP(row, 0, terminal->pvt->row_count    - 1) != row))
+// FIXME: clamp on rows? tricky...
+	if (CLAMP(col, 0, terminal->pvt->column_count - 1) != col)
 		return;
 
 	focus = terminal->pvt->has_focus;
@@ -10530,7 +10556,7 @@ vte_terminal_paint_cursor(VteTerminal *terminal)
 	item.c = (cell && cell->c) ? cell->c : ' ';
 	item.columns = item.c == '\t' ? 1 : cell ? cell->attr.columns : 1;
 	item.x = col * width;
-	item.y = row * height;
+	item.y = _vte_terminal_row_to_pixel(terminal, drow);
 	cursor_width = item.columns * width;
 	if (cell && cell->c != 0) {
 		guint style;
@@ -10605,6 +10631,7 @@ vte_terminal_paint_cursor(VteTerminal *terminal)
 							     x, y,
 							     cursor_width, height);
 
+#if 0 // FIXME
                                 if (cell && terminal->pvt->show_match && terminal->pvt->match_underline) {
                                         hilite = vte_cell_is_between(col, row,
                                                         terminal->pvt->match_start.col,
@@ -10613,6 +10640,7 @@ vte_terminal_paint_cursor(VteTerminal *terminal)
                                                         terminal->pvt->match_end.row,
                                                         TRUE);
                                 }
+#endif
                                 if (cell && cell->c != 0 && cell->c != ' ') {
                                         vte_terminal_draw_cells(terminal,
                                                         &item, 1,
@@ -10645,8 +10673,8 @@ static void
 vte_terminal_paint_im_preedit_string(VteTerminal *terminal)
 {
 	VteScreen *screen;
-	int row, col, columns;
-	long width, height, delta;
+	int col, columns;
+	long width, height;
 	int i, len;
 	guint fore, back;
 
@@ -10659,9 +10687,6 @@ vte_terminal_paint_im_preedit_string(VteTerminal *terminal)
 	/* Keep local copies of rendering information. */
 	width = terminal->pvt->char_width;
 	height = terminal->pvt->char_height;
-	delta = screen->scroll_delta;
-
-        row = terminal->pvt->cursor.row - delta;
 
 	/* Find out how many columns the pre-edit string takes up. */
 	columns = vte_terminal_preedit_width(terminal, FALSE);
@@ -10686,13 +10711,13 @@ vte_terminal_paint_im_preedit_string(VteTerminal *terminal)
                         items[i].columns = _vte_unichar_width(items[i].c,
                                                               terminal->pvt->utf8_ambiguous_width);
 			items[i].x = (col + columns) * width;
-			items[i].y = row * height;
+			items[i].y = _vte_terminal_row_to_pixel(terminal, terminal->pvt->cursor.row);
 			columns += items[i].columns;
 			preedit = g_utf8_next_char(preedit);
 		}
 		_vte_draw_clear(terminal->pvt->draw,
 				col * width + terminal->pvt->padding.left,
-				row * height + terminal->pvt->padding.top,
+				_vte_terminal_row_to_pixel(terminal, terminal->pvt->cursor.row) + terminal->pvt->padding.top,
 				width * columns,
 				height);
                 fore = terminal->pvt->color_defaults.attr.fore;
@@ -10751,6 +10776,12 @@ vte_terminal_draw(GtkWidget *widget,
 	_vte_draw_clear (terminal->pvt->draw, 0, 0,
 			 allocated_width, allocated_height);
 
+        /* Clip vertically, for the sake of smooth scrolling. We want the top and bottom paddings to be unused.
+         * Don't clip horizontally so that antialiasing can legally overflow to the right padding. */
+        cairo_save(cr);
+        cairo_rectangle(cr, 0, terminal->pvt->padding.top, allocated_width, allocated_height - terminal->pvt->padding.top - terminal->pvt->padding.bottom);
+        cairo_clip(cr);
+
 	/* Calculate the bounding rectangle. */
 	{
 		cairo_rectangle_int_t *rectangles;
@@ -10787,9 +10818,18 @@ vte_terminal_draw(GtkWidget *widget,
 		g_free (rectangles);
 	}
 
+	vte_terminal_paint_im_preedit_string(terminal);
+
+        /* Re-clip, allowing 1 more pixel row for the cursor. */
+        /* FIXME: only do so for outlined cursor!!! */
+        cairo_restore(cr);
+        cairo_save(cr);
+        cairo_rectangle(cr, 0, terminal->pvt->padding.top - 1, allocated_width, allocated_height - terminal->pvt->padding.top - terminal->pvt->padding.bottom + 2);
+        cairo_clip(cr);
+
 	vte_terminal_paint_cursor(terminal);
 
-	vte_terminal_paint_im_preedit_string(terminal);
+	cairo_restore(cr);
 
 	/* Done with various structures. */
 	_vte_draw_set_cairo(terminal->pvt->draw, NULL);
@@ -10907,19 +10947,19 @@ vte_terminal_scroll(GtkWidget *widget, GdkEventScroll *event)
 	_vte_debug_print(VTE_DEBUG_EVENTS,
 			"Scroll speed is %d lines per non-smooth scroll unit\n",
 			(int) v);
-	cnt = v * terminal->pvt->mouse_smooth_scroll_delta;
-	if (cnt == 0)
-		return TRUE;
-	terminal->pvt->mouse_smooth_scroll_delta -= cnt / v;
-	_vte_debug_print(VTE_DEBUG_EVENTS,
-			"Scroll by %d lines, smooth scroll delta set back to %f\n",
-			cnt, terminal->pvt->mouse_smooth_scroll_delta);
-
 	if (terminal->pvt->screen == &terminal->pvt->alternate_screen &&
             terminal->pvt->alternate_screen_scroll) {
 		char *normal;
 		gssize normal_length;
 
+		cnt = v * terminal->pvt->mouse_smooth_scroll_delta;
+		if (cnt == 0)
+			return TRUE;
+		terminal->pvt->mouse_smooth_scroll_delta -= cnt / v;
+		_vte_debug_print(VTE_DEBUG_EVENTS,
+				"Scroll by %d lines, smooth scroll delta set back to %f\n",
+				cnt, terminal->pvt->mouse_smooth_scroll_delta);
+
 		/* In the alternate screen there is no scrolling,
 		 * so fake a few cursor keystrokes. */
 
@@ -10939,8 +10979,11 @@ vte_terminal_scroll(GtkWidget *widget, GdkEventScroll *event)
 		g_free (normal);
 	} else {
 		/* Perform a history scroll. */
-		cnt += terminal->pvt->screen->scroll_delta;
-		vte_terminal_queue_adjustment_value_changed_clamped (terminal, cnt);
+		// Fixme: ugly
+		double dcnt = v * terminal->pvt->mouse_smooth_scroll_delta;
+		dcnt += terminal->pvt->screen->scroll_delta;
+		terminal->pvt->mouse_smooth_scroll_delta = 0;
+		vte_terminal_queue_adjustment_value_changed_clamped (terminal, dcnt);
 	}
 
 	return TRUE;
@@ -12565,7 +12608,8 @@ vte_terminal_set_scrollback_lines(VteTerminal *terminal, glong lines)
 {
         VteTerminalPrivate *pvt;
         GObject *object;
-        glong scroll_delta, low, high, next;
+        glong low, high, next;
+        double scroll_delta;
 	VteScreen *screen;
 
 	g_return_if_fail(VTE_IS_TERMINAL(terminal));
@@ -12853,6 +12897,8 @@ vte_terminal_reset(VteTerminal *terminal,
         pvt->mouse_handled_buttons = 0;
 	pvt->mouse_last_x = 0;
 	pvt->mouse_last_y = 0;
+        pvt->mouse_last_col = 0;
+        pvt->mouse_last_row = 0;
 	pvt->mouse_xterm_extension = FALSE;
 	pvt->mouse_urxvt_extension = FALSE;
 	pvt->mouse_smooth_scroll_delta = 0.;
@@ -14028,10 +14074,10 @@ vte_terminal_search_rows(VteTerminal *terminal,
 	value = gtk_adjustment_get_value(terminal->pvt->vadjustment);
 	page_size = gtk_adjustment_get_page_size(terminal->pvt->vadjustment);
 	if (backward) {
-		if (end_row < value || end_row >= value + page_size)
+		if (end_row < value || end_row > value + page_size - 1)
 			vte_terminal_queue_adjustment_value_changed_clamped (terminal, end_row - page_size + 1);
 	} else {
-		if (start_row < value || start_row >= value + page_size)
+		if (start_row < value || start_row > value + page_size - 1)
 			vte_terminal_queue_adjustment_value_changed_clamped (terminal, start_row);
 	}
 
diff --git a/src/vteaccess.cc b/src/vteaccess.cc
index e33b3fd..8b8dbd7 100644
--- a/src/vteaccess.cc
+++ b/src/vteaccess.cc
@@ -509,7 +509,9 @@ vte_terminal_accessible_text_scrolled(VteTerminal *terminal,
 	long delta, row_count;
 	guint i, len;
 
-	g_assert(howmuch != 0);
+// FIXME!
+//	g_assert(howmuch != 0);
+	if (howmuch == 0) return;
 
         row_count = vte_terminal_get_row_count(terminal);
 	if (((howmuch < 0) && (howmuch <= -row_count)) ||
@@ -1379,7 +1381,6 @@ vte_terminal_accessible_get_offset_at_point(AtkText *text,
 	terminal = VTE_TERMINAL (gtk_accessible_get_widget (GTK_ACCESSIBLE (text)));
 
 	atk_component_get_extents (ATK_COMPONENT (text), &base_x, &base_y, &w, &h, coords);
-        /* FIXME: use _vte_terminal_xy_to_grid */
 	char_width = vte_terminal_get_char_width (terminal);
 	char_height = vte_terminal_get_char_height (terminal);
 	x -= base_x;
diff --git a/src/vteinternal.hh b/src/vteinternal.hh
index 43226f6..68a1c27 100644
--- a/src/vteinternal.hh
+++ b/src/vteinternal.hh
@@ -123,7 +123,7 @@ struct _vte_incoming_chunk{
 typedef struct _VteScreen VteScreen;
 struct _VteScreen {
         VteRing row_data[1];	/* buffer contents */
-        long scroll_delta;	/* scroll offset */
+        double scroll_delta;	/* scroll offset */
         long insert_delta;	/* insertion offset */
 
         /* Stuff saved along with the cursor */
@@ -326,6 +326,7 @@ public:
         guint mouse_pressed_buttons;      /* bits 0, 1, 2 resp. for buttons 1, 2, 3 */
         guint mouse_handled_buttons;      /* similar bitmap for buttons we handled ourselves */
 	long mouse_last_x, mouse_last_y;
+        long mouse_last_col, mouse_last_row;
 	gboolean mouse_autohide;
 	guint mouse_autoscroll_tag;
 	gboolean mouse_xterm_extension;
