diff --git a/gdk/gdkevents.c b/gdk/gdkevents.c
index eeaac2d..e1234db 100644
--- a/gdk/gdkevents.c
+++ b/gdk/gdkevents.c
@@ -464,6 +464,8 @@ gdk_event_new (GdkEventType type)
       new_event->scroll.y = 0.;
       new_event->scroll.x_root = 0.;
       new_event->scroll.y_root = 0.;
+      new_event->scroll.delta_x = 0.;
+      new_event->scroll.delta_y = 0.;
       break;
     case GDK_ENTER_NOTIFY:
     case GDK_LEAVE_NOTIFY:
@@ -1135,6 +1137,36 @@ gdk_event_get_scroll_direction (const GdkEvent *event,
   return fetched;
 }
 
+gboolean
+gdk_event_get_scroll_deltas (const GdkEvent *event,
+                             gdouble        *delta_x,
+                             gdouble        *delta_y)
+{
+  gboolean fetched = TRUE;
+  gdouble dx = 0.0;
+  gdouble dy = 0.0;
+
+  switch (event->type)
+    {
+    case GDK_SCROLL:
+      fetched = event->scroll.has_deltas;
+      dx = event->scroll.delta_x;
+      dy = event->scroll.delta_y;
+      break;
+    default:
+      fetched = FALSE;
+      break;
+    }
+
+  if (delta_x)
+    *delta_x = dx;
+
+  if (delta_y)
+    *delta_y = dy;
+
+  return fetched;
+}
+
 /**
  * gdk_event_get_axis:
  * @event: a #GdkEvent
diff --git a/gdk/gdkevents.h b/gdk/gdkevents.h
index 24a3b8e..8354c79 100644
--- a/gdk/gdkevents.h
+++ b/gdk/gdkevents.h
@@ -629,6 +629,9 @@ struct _GdkEventScroll
   GdkScrollDirection direction;
   GdkDevice *device;
   gdouble x_root, y_root;
+  gboolean has_deltas;
+  gdouble delta_x;
+  gdouble delta_y;
 };
 
 /**
@@ -1067,6 +1070,10 @@ gboolean  gdk_event_get_keycode         (const GdkEvent *event,
                                          guint16        *keycode);
 gboolean gdk_event_get_scroll_direction (const GdkEvent *event,
                                          GdkScrollDirection *direction);
+gboolean  gdk_event_get_scroll_deltas   (const GdkEvent *event,
+                                         gdouble         *delta_x,
+                                         gdouble         *delta_y);
+
 gboolean  gdk_event_get_axis            (const GdkEvent  *event,
                                          GdkAxisUse       axis_use,
                                          gdouble         *value);
diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c
index eb4bcbd..87fe617 100644
--- a/gdk/gdkwindow.c
+++ b/gdk/gdkwindow.c
@@ -9426,6 +9426,9 @@ proxy_button_event (GdkEvent *source_event,
       event->scroll.y_root = source_event->scroll.y_root;
       event->scroll.state = state;
       event->scroll.device = source_event->scroll.device;
+      event->scroll.has_deltas = source_event->scroll.has_deltas;
+      event->scroll.delta_x = source_event->scroll.delta_x;
+      event->scroll.delta_y = source_event->scroll.delta_y;
       gdk_event_set_source_device (event, source_device);
       return TRUE;
 
diff --git a/gdk/quartz/gdkevents-quartz.c b/gdk/quartz/gdkevents-quartz.c
index 546fa23..60db03a 100644
--- a/gdk/quartz/gdkevents-quartz.c
+++ b/gdk/quartz/gdkevents-quartz.c
@@ -850,6 +850,9 @@ fill_scroll_event (GdkWindow          *window,
                    gint                y,
                    gint                x_root,
                    gint                y_root,
+                   gboolean            has_deltas,
+                   gdouble             delta_x,
+                   gdouble             delta_y,
                    GdkScrollDirection  direction)
 {
   NSPoint point;
@@ -866,6 +869,9 @@ fill_scroll_event (GdkWindow          *window,
   event->scroll.state = get_keyboard_modifiers_from_ns_event (nsevent);
   event->scroll.direction = direction;
   event->scroll.device = _gdk_display->core_pointer;
+  event->scroll.has_deltas = has_deltas;
+  event->scroll.delta_x = delta_x;
+  event->scroll.delta_y = delta_y;
 }
 
 static void
@@ -1264,28 +1270,59 @@ gdk_event_translate (GdkEvent *event,
 
     case NSScrollWheel:
       {
-	float dx = [nsevent deltaX];
-	float dy = [nsevent deltaY];
-	GdkScrollDirection direction;
+        GdkScrollDirection direction;
+	float dx;
+	float dy;
 
-        if (dy != 0)
-          {
-            if (dy < 0.0)
-              direction = GDK_SCROLL_DOWN;
+	if (gdk_quartz_osx_version() >= GDK_OSX_LION &&
+	    [nsevent hasPreciseScrollingDeltas])
+	  {
+	    dx = [nsevent scrollingDeltaX];
+	    dy = [nsevent scrollingDeltaY];
+
+            if (fabs (dy) > fabs (dx))
+              {
+                if (dy < 0.0)
+                  direction = GDK_SCROLL_DOWN;
+                else
+                  direction = GDK_SCROLL_UP;
+              }
             else
-              direction = GDK_SCROLL_UP;
+              {
+                if (dx < 0.0)
+                  direction = GDK_SCROLL_RIGHT;
+                else
+                  direction = GDK_SCROLL_LEFT;
+              }
 
-            fill_scroll_event (window, event, nsevent, x, y, x_root, y_root, direction);
-          }
+            fill_scroll_event (window, event, nsevent, x, y, x_root, y_root,
+                               TRUE, -dx, -dy, direction);
+	  }
+	else
+	  {
+	    dx = [nsevent deltaX];
+	    dy = [nsevent deltaY];
 
-        if (dx != 0)
-          {
-            if (dx < 0.0)
-              direction = GDK_SCROLL_RIGHT;
-            else
-              direction = GDK_SCROLL_LEFT;
+            if (dy != 0.0)
+              {
+                if (dy < 0.0)
+                  direction = GDK_SCROLL_DOWN;
+                else
+                  direction = GDK_SCROLL_UP;
+
+                fill_scroll_event (window, event, nsevent, x, y, x_root, y_root,
+                                   FALSE, 0.0, fabs (dy), direction);
+              }
+            else if (dx != 0.0)
+              {
+                if (dx < 0.0)
+                  direction = GDK_SCROLL_RIGHT;
+                else
+                  direction = GDK_SCROLL_LEFT;
 
-            fill_scroll_event (window, event, nsevent, x, y, x_root, y_root, direction);
+                fill_scroll_event (window, event, nsevent, x, y, x_root, y_root,
+                                   FALSE, fabs (dx), 0.0, direction);
+              }
           }
       }
       break;
diff --git a/gdk/x11/gdkdevice-xi2.c b/gdk/x11/gdkdevice-xi2.c
index b86d802..4782acf 100644
--- a/gdk/x11/gdkdevice-xi2.c
+++ b/gdk/x11/gdkdevice-xi2.c
@@ -35,11 +35,21 @@
 
 #endif
 
+typedef struct _ScrollValuator ScrollValuator;
+
+struct _ScrollValuator
+{
+  guint n_valuator;
+  GdkScrollDirection direction;
+  gdouble last_value;
+};
+
 struct _GdkX11DeviceXI2
 {
   GdkDevice parent_instance;
 
   gint device_id;
+  GArray *scroll_valuators;
 };
 
 struct _GdkX11DeviceXI2Class
@@ -51,6 +61,7 @@ G_DEFINE_TYPE (GdkX11DeviceXI2, gdk_x11_device_xi2, GDK_TYPE_DEVICE)
 
 #ifdef XINPUT_2
 
+static void gdk_x11_device_xi2_finalize     (GObject      *object);
 static void gdk_x11_device_xi2_get_property (GObject      *object,
                                              guint         prop_id,
                                              GValue       *value,
@@ -112,6 +123,7 @@ gdk_x11_device_xi2_class_init (GdkX11DeviceXI2Class *klass)
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
   GdkDeviceClass *device_class = GDK_DEVICE_CLASS (klass);
 
+  object_class->finalize = gdk_x11_device_xi2_finalize;
   object_class->get_property = gdk_x11_device_xi2_get_property;
   object_class->set_property = gdk_x11_device_xi2_set_property;
 
@@ -136,6 +148,17 @@ gdk_x11_device_xi2_class_init (GdkX11DeviceXI2Class *klass)
 static void
 gdk_x11_device_xi2_init (GdkX11DeviceXI2 *device)
 {
+  device->scroll_valuators = g_array_new (FALSE, FALSE, sizeof (ScrollValuator));
+}
+
+static void
+gdk_x11_device_xi2_finalize (GObject *object)
+{
+  GdkX11DeviceXI2 *device = GDK_X11_DEVICE_XI2 (object);
+
+  g_array_free (device->scroll_valuators, TRUE);
+
+  G_OBJECT_CLASS (gdk_x11_device_xi2_parent_class)->finalize (object);
 }
 
 static void
@@ -750,6 +773,81 @@ _gdk_x11_device_xi2_translate_state (XIModifierState *mods_state,
   return state;
 }
 
+void
+_gdk_x11_device_xi2_add_scroll_valuator (GdkX11DeviceXI2    *device,
+                                         guint               n_valuator,
+                                         GdkScrollDirection  direction)
+{
+  ScrollValuator scroll;
+
+  g_return_if_fail (GDK_IS_X11_DEVICE_XI2 (device));
+  g_return_if_fail (n_valuator < gdk_device_get_n_axes (GDK_DEVICE (device)));
+
+  scroll.n_valuator = n_valuator;
+  scroll.direction = direction;
+  scroll.last_value = 0;
+
+  g_array_append_val (device->scroll_valuators, scroll);
+}
+
+gboolean
+_gdk_x11_device_xi2_is_scroll_valuator (GdkX11DeviceXI2    *device,
+                                        guint               n_valuator,
+                                        GdkScrollDirection *direction)
+{
+  guint i;
+
+  g_return_val_if_fail (GDK_IS_X11_DEVICE_XI2 (device), FALSE);
+  g_return_val_if_fail (n_valuator < gdk_device_get_n_axes (GDK_DEVICE (device)), FALSE);
+
+  for (i = 0; i < device->scroll_valuators->len; i++)
+    {
+      ScrollValuator *scroll;
+
+      scroll = &g_array_index (device->scroll_valuators, ScrollValuator, i);
+
+      if (scroll->n_valuator == n_valuator)
+        {
+          if (*direction)
+            *direction = scroll->direction;
+
+          return TRUE;
+        }
+    }
+
+  return FALSE;
+}
+
+gdouble
+_gdk_x11_device_xi2_get_scroll_delta (GdkX11DeviceXI2 *device,
+                                      guint            n_valuator,
+                                      gdouble          valuator_value)
+{
+  guint i;
+
+  g_return_val_if_fail (GDK_IS_X11_DEVICE_XI2 (device), 0);
+  g_return_val_if_fail (n_valuator < gdk_device_get_n_axes (GDK_DEVICE (device)), 0);
+
+  for (i = 0; i < device->scroll_valuators->len; i++)
+    {
+      ScrollValuator *scroll;
+
+      scroll = &g_array_index (device->scroll_valuators, ScrollValuator, i);
+
+      if (scroll->n_valuator == n_valuator)
+        {
+          gdouble delta;
+
+          delta = valuator_value - scroll->last_value;
+          scroll->last_value = valuator_value;
+
+          return delta;
+        }
+    }
+
+  return 0;
+}
+
 gint
 _gdk_x11_device_xi2_get_id (GdkX11DeviceXI2 *device)
 {
diff --git a/gdk/x11/gdkdevicemanager-core-x11.c b/gdk/x11/gdkdevicemanager-core-x11.c
index 1fcb67d..17ea50c 100644
--- a/gdk/x11/gdkdevicemanager-core-x11.c
+++ b/gdk/x11/gdkdevicemanager-core-x11.c
@@ -490,6 +490,10 @@ gdk_x11_device_manager_core_translate_event (GdkEventTranslator *translator,
           event->scroll.state = (GdkModifierType) xevent->xbutton.state;
           event->scroll.device = device_manager->core_pointer;
 
+          event->scroll.delta_x = 0;
+          event->scroll.delta_y = 0;
+          event->scroll.has_deltas = FALSE;
+
           if (!set_screen_from_root (display, event, xevent->xbutton.root))
             {
               return_val = FALSE;
diff --git a/gdk/x11/gdkdevicemanager-x11.c b/gdk/x11/gdkdevicemanager-x11.c
index b492304..28e8277 100644
--- a/gdk/x11/gdkdevicemanager-x11.c
+++ b/gdk/x11/gdkdevicemanager-x11.c
@@ -54,7 +54,7 @@ _gdk_x11_device_manager_new (GdkDisplay *display)
           int major, minor;
 
           major = 2;
-          minor = 0;
+          minor = 1;
 
           if (!_gdk_disable_multidevice &&
               XIQueryVersion (xdisplay, &major, &minor) != BadRequest)
diff --git a/gdk/x11/gdkdevicemanager-xi2.c b/gdk/x11/gdkdevicemanager-xi2.c
index c921ce0..c8056e4 100644
--- a/gdk/x11/gdkdevicemanager-xi2.c
+++ b/gdk/x11/gdkdevicemanager-xi2.c
@@ -223,6 +223,20 @@ translate_device_classes (GdkDisplay      *display,
                                     n_valuator);
           n_valuator++;
           break;
+        case XIScrollClass:
+          {
+            XIScrollClassInfo *scroll_info = (XIScrollClassInfo *) class_info;
+            GdkScrollDirection direction;
+
+            if (scroll_info->scroll_type == XIScrollTypeVertical)
+              direction = GDK_SCROLL_DOWN;
+            else
+              direction = GDK_SCROLL_RIGHT;
+
+            _gdk_x11_device_xi2_add_scroll_valuator (GDK_X11_DEVICE_XI2 (device),
+                                                     scroll_info->number,
+                                                     direction);
+          }
         default:
           /* Ignore */
           break;
@@ -1008,6 +1022,52 @@ gdk_x11_device_manager_xi2_translate_core_event (GdkEventTranslator *translator,
 }
 
 static gboolean
+scroll_valuators_changed (GdkX11DeviceXI2 *device,
+                          XIValuatorState *valuators,
+                          gdouble         *dx,
+                          gdouble         *dy)
+{
+  gdouble has_scroll_valuators = FALSE;
+  GdkScrollDirection direction;
+  guint n_axes, i, n_val;
+  gdouble *vals;
+
+  n_axes = gdk_device_get_n_axes (GDK_DEVICE (device));
+  vals = valuators->values;
+  n_val = 0;
+
+  for (i = 0; i < MIN (valuators->mask_len * 8, n_axes); i++)
+    {
+      if (!XIMaskIsSet (valuators->mask, i))
+        continue;
+
+      if (_gdk_x11_device_xi2_is_scroll_valuator (device, i, &direction))
+        {
+          gdouble delta;
+
+          has_scroll_valuators = TRUE;
+          delta = _gdk_x11_device_xi2_get_scroll_delta (device, i, vals[n_val]);
+
+          if (direction == GDK_SCROLL_UP ||
+              direction == GDK_SCROLL_DOWN)
+            {
+              if (dy)
+                *dy = delta;
+            }
+          else
+            {
+              if (dx)
+                *dx = delta;
+            }
+        }
+
+      n_val++;
+    }
+
+  return has_scroll_valuators;
+}
+
+static gboolean
 gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator,
                                             GdkDisplay         *display,
                                             GdkEvent           *event,
@@ -1119,6 +1179,7 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator,
           case 5:
           case 6:
           case 7:
+#if 0
              /* Button presses of button 4-7 are scroll events */
             if (ev->evtype == XI_ButtonPress)
               {
@@ -1139,6 +1200,9 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator,
                 event->scroll.y = (gdouble) xev->event_y;
                 event->scroll.x_root = (gdouble) xev->root_x;
                 event->scroll.y_root = (gdouble) xev->root_y;
+		event->scroll.delta_x = 0;
+		event->scroll.delta_y = 0;
+		event->scroll.has_deltas = FALSE;
 
                 event->scroll.device = g_hash_table_lookup (device_manager->id_table,
                                                             GUINT_TO_POINTER (xev->deviceid));
@@ -1152,6 +1216,7 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator,
               }
             /* Button presses of button 4-7 are scroll events, so ignore the release */
             else if (ev->evtype == XI_ButtonRelease)
+#endif
               {
                 return_val = FALSE;
                 break;
@@ -1211,6 +1276,49 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator,
       {
         XIDeviceEvent *xev = (XIDeviceEvent *) ev;
         GdkDevice *source_device;
+        gdouble delta_x, delta_y;
+
+        source_device = g_hash_table_lookup (device_manager->id_table,
+                                             GUINT_TO_POINTER (xev->sourceid));
+
+        if (scroll_valuators_changed (GDK_X11_DEVICE_XI2 (source_device),
+                                      &xev->valuators, &delta_x, &delta_y))
+          {
+            event->scroll.type = GDK_SCROLL;
+
+            /* both x/y deltas are provided, but
+             * only one scroll direction is reported here
+             */
+            if (delta_y < 0)
+              event->scroll.direction = GDK_SCROLL_UP;
+            else if (delta_y > 0)
+              event->scroll.direction = GDK_SCROLL_DOWN;
+            else if (delta_x < 0)
+              event->scroll.direction = GDK_SCROLL_LEFT;
+            else
+              event->scroll.direction = GDK_SCROLL_RIGHT;
+
+            delta_x *= 10;
+            delta_y *= 10;
+
+            event->scroll.window = window;
+            event->scroll.time = xev->time;
+            event->scroll.x = (gdouble) xev->event_x;
+            event->scroll.y = (gdouble) xev->event_y;
+            event->scroll.x_root = (gdouble) xev->root_x;
+            event->scroll.y_root = (gdouble) xev->root_y;
+            event->scroll.delta_x = delta_x;
+            event->scroll.delta_y = delta_y;
+            event->scroll.has_deltas = TRUE;
+
+            event->scroll.device = g_hash_table_lookup (device_manager->id_table,
+                                                        GUINT_TO_POINTER (xev->deviceid));
+
+            gdk_event_set_source_device (event, source_device);
+
+            event->scroll.state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group);
+            break;
+          }
 
         event->motion.type = GDK_MOTION_NOTIFY;
 
@@ -1225,8 +1333,6 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator,
         event->motion.device = g_hash_table_lookup (device_manager->id_table,
                                                     GINT_TO_POINTER (xev->deviceid));
 
-        source_device = g_hash_table_lookup (device_manager->id_table,
-                                             GUINT_TO_POINTER (xev->sourceid));
         gdk_event_set_source_device (event, source_device);
 
         event->motion.state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group);
diff --git a/gdk/x11/gdkprivate-x11.h b/gdk/x11/gdkprivate-x11.h
index 7a65581..18a779f 100644
--- a/gdk/x11/gdkprivate-x11.h
+++ b/gdk/x11/gdkprivate-x11.h
@@ -256,6 +256,15 @@ gint     _gdk_x11_device_xi2_get_id               (GdkX11DeviceXI2 *device);
 
 GdkDevice * _gdk_x11_device_manager_xi2_lookup    (GdkX11DeviceManagerXI2 *device_manager_xi2,
                                                    gint                    device_id);
+void     _gdk_x11_device_xi2_add_scroll_valuator  (GdkX11DeviceXI2    *device,
+                                                   guint               n_valuator,
+                                                   GdkScrollDirection  direction);
+gboolean _gdk_x11_device_xi2_is_scroll_valuator   (GdkX11DeviceXI2    *device,
+                                                   guint               n_valuator,
+                                                   GdkScrollDirection *direction);
+gdouble  _gdk_x11_device_xi2_get_scroll_delta     (GdkX11DeviceXI2    *device,
+                                                   guint               n_valuator,
+                                                   gdouble             valuator_value);
 
 #endif
 
diff --git a/gtk/gtkrange.c b/gtk/gtkrange.c
index 69b959b..37e3d6a 100644
--- a/gtk/gtkrange.c
+++ b/gtk/gtkrange.c
@@ -2709,7 +2709,7 @@ gtk_range_button_release (GtkWidget      *widget,
 /**
  * _gtk_range_get_wheel_delta:
  * @range: a #GtkRange
- * @direction: A #GdkScrollDirection
+ * @event: A #GdkEventScroll
  * 
  * Returns a good step value for the mouse wheel.
  * 
@@ -2718,28 +2718,58 @@ gtk_range_button_release (GtkWidget      *widget,
  * Since: 2.4
  **/
 gdouble
-_gtk_range_get_wheel_delta (GtkRange           *range,
-			    GdkScrollDirection  direction)
+_gtk_range_get_wheel_delta (GtkRange       *range,
+                            GdkEventScroll *event)
 {
   GtkRangePrivate *priv = range->priv;
   GtkAdjustment *adjustment = priv->adjustment;
+  gdouble dx, dy;
   gdouble delta;
+  gdouble page_size;
 
-  if (GTK_IS_SCROLLBAR (range))
-    delta = pow (gtk_adjustment_get_page_size (adjustment), 2.0 / 3.0);
+  page_size = gtk_adjustment_get_page_size (adjustment);
+
+  if (gdk_event_get_scroll_deltas ((GdkEvent *) event, &dx, &dy))
+    {
+      GtkAllocation allocation;
+
+      gtk_widget_get_allocation (GTK_WIDGET (range), &allocation);
+
+      if (gtk_orientable_get_orientation (GTK_ORIENTABLE (range)) == GTK_ORIENTATION_HORIZONTAL)
+        {
+          if (GTK_IS_SCROLLBAR (range) && page_size > 0)
+            delta = dx * page_size / allocation.width;
+          else
+            delta = dx * (gtk_adjustment_get_upper (adjustment) -
+                          gtk_adjustment_get_lower (adjustment)) / allocation.width;
+        }
+      else
+        {
+          if (GTK_IS_SCROLLBAR (range) && page_size > 0)
+            delta = dy * page_size / allocation.height;
+          else
+            delta = dy * (gtk_adjustment_get_upper (adjustment) -
+                          gtk_adjustment_get_lower (adjustment)) / allocation.height;
+        }
+    }
   else
-    delta = gtk_adjustment_get_step_increment (adjustment) * 2;
-  
-  if (direction == GDK_SCROLL_UP ||
-      direction == GDK_SCROLL_LEFT)
-    delta = - delta;
-  
+    {
+      if (GTK_IS_SCROLLBAR (range))
+        delta = pow (page_size, 2.0 / 3.0);
+      else
+        delta = gtk_adjustment_get_page_increment (adjustment) * 2;
+
+      if (event->direction == GDK_SCROLL_UP ||
+          event->direction == GDK_SCROLL_LEFT)
+        delta = - delta;
+    }
+
   if (priv->inverted)
     delta = - delta;
 
   return delta;
 }
-      
+
 static gboolean
 gtk_range_scroll_event (GtkWidget      *widget,
 			GdkEventScroll *event)
@@ -2752,7 +2782,7 @@ gtk_range_scroll_event (GtkWidget      *widget,
       gdouble delta;
       gboolean handled;
 
-      delta = _gtk_range_get_wheel_delta (range, event->direction);
+      delta = _gtk_range_get_wheel_delta (range, event);
 
       g_signal_emit (range, signals[CHANGE_VALUE], 0,
                      GTK_SCROLL_JUMP, gtk_adjustment_get_value (priv->adjustment) + delta,
diff --git a/gtk/gtkrange.h b/gtk/gtkrange.h
index 7c01ab2..c77bf45 100644
--- a/gtk/gtkrange.h
+++ b/gtk/gtkrange.h
@@ -148,8 +148,8 @@ void               gtk_range_set_round_digits              (GtkRange      *range
 gint                gtk_range_get_round_digits              (GtkRange      *range);
 
 /* internal API */
-gdouble            _gtk_range_get_wheel_delta              (GtkRange      *range,
-                                                            GdkScrollDirection direction);
+gdouble            _gtk_range_get_wheel_delta              (GtkRange       *range,
+                                                            GdkEventScroll *event);
 
 void               _gtk_range_set_stop_values              (GtkRange      *range,
                                                             gdouble       *values,
diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c
index 3bd5f3b..fef98b1 100644
--- a/gtk/gtkscrolledwindow.c
+++ b/gtk/gtkscrolledwindow.c
@@ -1844,7 +1844,9 @@ gtk_scrolled_window_scroll_event (GtkWidget      *widget,
 {
   GtkScrolledWindowPrivate *priv;
   GtkScrolledWindow *scrolled_window;
-  GtkWidget *range;
+  gboolean handled = FALSE;
+  gdouble delta_x;
+  gdouble delta_y;
 
   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (widget), FALSE);
   g_return_val_if_fail (event != NULL, FALSE);  
@@ -1852,24 +1854,72 @@ gtk_scrolled_window_scroll_event (GtkWidget      *widget,
   scrolled_window = GTK_SCROLLED_WINDOW (widget);
   priv = scrolled_window->priv;
 
-  if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
-    range = priv->vscrollbar;
-  else
-    range = priv->hscrollbar;
+  if (gdk_event_get_scroll_deltas ((GdkEvent *) event, &delta_x, &delta_y))
+    {
+      if (delta_x != 0.0 && priv->hscrollbar &&
+          gtk_widget_get_visible (priv->hscrollbar))
+        {
+          GtkAdjustment *adj;
+          gdouble new_value;
+
+          adj = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
+
+          new_value = CLAMP (gtk_adjustment_get_value (adj) + delta_x,
+                             gtk_adjustment_get_lower (adj),
+                             gtk_adjustment_get_upper (adj) -
+                             gtk_adjustment_get_page_size (adj));
+
+          gtk_adjustment_set_value (adj, new_value);
+
+          handled = TRUE;
+        }
+
+      if (delta_y != 0.0 && priv->vscrollbar &&
+          gtk_widget_get_visible (priv->vscrollbar))
+        {
+          GtkAdjustment *adj;
+          gdouble new_value;
+
+          adj = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
+
+          new_value = CLAMP (gtk_adjustment_get_value (adj) + delta_y,
+                             gtk_adjustment_get_lower (adj),
+                             gtk_adjustment_get_upper (adj) -
+                             gtk_adjustment_get_page_size (adj));
 
-  if (range && gtk_widget_get_visible (range))
+          gtk_adjustment_set_value (adj, new_value);
+
+          handled = TRUE;
+        }
+    }
+  else
     {
-      GtkAdjustment *adjustment = gtk_range_get_adjustment (GTK_RANGE (range));
-      gdouble delta;
+      GtkWidget *range;
+
+      if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
+        range = priv->vscrollbar;
+      else
+        range = priv->hscrollbar;
 
-      delta = _gtk_range_get_wheel_delta (GTK_RANGE (range), event->direction);
+      if (range && gtk_widget_get_visible (range))
+        {
+          GtkAdjustment *adj = gtk_range_get_adjustment (GTK_RANGE (range));
+          gdouble delta, new_value;
 
-      gtk_adjustment_set_value (adjustment, gtk_adjustment_get_value (adjustment) + delta);
+          delta = _gtk_range_get_wheel_delta (GTK_RANGE (range), event);
 
-      return TRUE;
+          new_value = CLAMP (gtk_adjustment_get_value (adj) + delta,
+                             gtk_adjustment_get_lower (adj),
+                             gtk_adjustment_get_upper (adj) -
+                             gtk_adjustment_get_page_size (adj));
+
+          gtk_adjustment_set_value (adj, new_value);
+
+          handled = TRUE;
+        }
     }
 
-  return FALSE;
+  return handled;
 }
 
 static gboolean
