diff -ur gtk+-3.22.5.orig/gdk/win32/gdkdevicemanager-win32.c gtk+-3.22.5.windows_multitouch/gdk/win32/gdkdevicemanager-win32.c
--- gtk+-3.22.5.orig/gdk/win32/gdkdevicemanager-win32.c	2016-11-23 16:02:35.000000000 -0800
+++ gtk+-3.22.5.windows_multitouch/gdk/win32/gdkdevicemanager-win32.c	2016-12-28 19:44:39.819131975 -0800
@@ -88,6 +88,43 @@
 }
 
 static GdkDevice *
+create_touchscreen(GdkDeviceManagerWin32 *device_manager,
+                   GType g_type,
+                   const char *name,
+                   GdkDeviceType type)
+{
+  GdkDevice *device;
+  device = g_object_new (g_type,
+                         "name", name,
+                         "type", type,
+                         "input-source", GDK_SOURCE_TOUCHSCREEN,
+                         "input-mode", GDK_MODE_DISABLED,
+                         "has-cursor", FALSE,
+                         "display", gdk_device_manager_get_display (device_manager),
+                         "device-manager", device_manager,
+                         "num-touches", 10,
+                          NULL);
+  // todo: find a way to query Windows touchscreen specs - these are captured from the Wacom Linux driver	
+  _gdk_device_add_axis (GDK_DEVICE (device),
+                        GDK_NONE,
+                        GDK_AXIS_X,
+                        0,
+                        12372,
+                        40000);
+  _gdk_device_add_axis (GDK_DEVICE (device),
+                        GDK_NONE,
+                        GDK_AXIS_Y,
+                        0,
+                        6960,
+                        40000);
+
+  _gdk_device_set_associated_device (device, device_manager->core_pointer);
+  _gdk_device_add_slave (device_manager->core_pointer, device);
+
+  return device;
+}
+
+static GdkDevice *
 create_keyboard (GdkDeviceManager *device_manager,
 		 GType g_type,
 		 const char *name,
@@ -725,6 +765,12 @@
 		    GDK_TYPE_DEVICE_WIN32,
 		    "System Aggregated Pointer",
 		    GDK_DEVICE_TYPE_SLAVE);
+
+  device_manager->touchscreen = create_touchscreen(device_manager,
+                                                   GDK_TYPE_DEVICE_WIN32,
+                                                   "touchscreen",
+                                                   GDK_DEVICE_TYPE_SLAVE);
+  
   _gdk_device_virtual_set_active (device_manager->core_pointer,
 				  device_manager->system_pointer);
   _gdk_device_set_associated_device (device_manager->system_pointer, device_manager->core_pointer);
@@ -753,6 +799,8 @@
   gdk_display_add_seat (gdk_device_manager_get_display (GDK_DEVICE_MANAGER (object)), seat);
   gdk_seat_default_add_slave (GDK_SEAT_DEFAULT (seat), device_manager->system_pointer);
   gdk_seat_default_add_slave (GDK_SEAT_DEFAULT (seat), device_manager->system_keyboard);
+  gdk_seat_default_add_slave (GDK_SEAT_DEFAULT (seat), device_manager->touchscreen);
+
   g_object_unref (seat);
 
   /* Only call Wintab init stuff after the default display
diff -ur gtk+-3.22.5.orig/gdk/win32/gdkdevicemanager-win32.h gtk+-3.22.5.windows_multitouch/gdk/win32/gdkdevicemanager-win32.h
--- gtk+-3.22.5.orig/gdk/win32/gdkdevicemanager-win32.h	2016-10-21 21:14:45.000000000 -0700
+++ gtk+-3.22.5.windows_multitouch/gdk/win32/gdkdevicemanager-win32.h	2016-12-28 17:33:36.153202049 -0800
@@ -41,6 +41,7 @@
   /* Fake slave devices */
   GdkDevice *system_pointer;
   GdkDevice *system_keyboard;
+  GdkDevice *touchscreen;
   GList *wintab_devices;
 };
 
diff -ur gtk+-3.22.5.orig/gdk/win32/gdkevents-win32.c gtk+-3.22.5.windows_multitouch/gdk/win32/gdkevents-win32.c
--- gtk+-3.22.5.orig/gdk/win32/gdkevents-win32.c	2016-11-28 11:41:14.000000000 -0800
+++ gtk+-3.22.5.windows_multitouch/gdk/win32/gdkevents-win32.c	2016-12-28 19:21:37.362071638 -0800
@@ -2068,6 +2068,90 @@
   return TRUE;
 }
 
+#define MOUSEEVENTF_FROMTOUCH 0xff515700
+#define MAX_TOUCH_INPUTS 16
+
+gboolean
+gdk_input_touch_event (GdkDisplay *display,
+                       MSG        *msg,
+                       GdkWindow  *window)
+{
+  GdkDeviceManagerWin32 *device_manager;
+  device_manager = GDK_DEVICE_MANAGER_WIN32 (gdk_display_get_device_manager (display));
+
+  TOUCHINPUT points[MAX_TOUCH_INPUTS];
+  int pointCount = MIN(msg->wParam, MAX_TOUCH_INPUTS);
+  if (GetTouchInputInfo(msg->lParam, pointCount, points, sizeof(TOUCHINPUT)))
+  {
+      int i;
+      for (i = 0; i < pointCount; ++i)
+      {
+        TOUCHINPUT *p = &points[i];
+        POINT temp;
+        GdkEventType type;
+        GdkEventTouch *event;
+        static int activeTouches;
+        static unsigned emulationSequence;  // per XI2proto.txt, if there are multiple sequences, only 1 should be used to generate emulated mouse events
+        if (p->dwFlags & (TOUCHEVENTF_PEN | TOUCHEVENTF_PALM))
+          continue;
+
+        // can there ever be a combined UP & DOWN event?
+        if (p->dwFlags & TOUCHEVENTF_DOWN)
+          type = GDK_TOUCH_BEGIN;
+        else if (p->dwFlags & TOUCHEVENTF_UP)
+          type = GDK_TOUCH_END;
+        else
+          type = GDK_TOUCH_UPDATE;
+
+        event = (GdkEventTouch *)gdk_event_new(type);
+        
+        if (event->type == GDK_TOUCH_BEGIN)
+        {
+          if (activeTouches == 0)
+            emulationSequence = p->dwID;
+          ++activeTouches;
+        }
+        else if (event->type == GDK_TOUCH_END)
+        {
+          --activeTouches;
+        }
+        event->time = p->dwTime;
+        event->window = window;
+        event->state = event->type == GDK_TOUCH_END ? 0 : GDK_BUTTON1_MASK;
+       
+        event->send_event = FALSE;
+        GdkDevice *device = device_manager->core_pointer;
+        gdk_event_set_source_device(event, device_manager->touchscreen);
+        gdk_event_set_device(event, device);
+
+        gdk_event_set_seat(event, gdk_device_get_seat (device));
+
+        event->x_root = p->x * 0.01;
+        event->y_root = p->y * 0.01;
+
+        temp.x = lrint(event->x_root);
+        temp.y = lrint(event->y_root);
+        ScreenToClient(msg->hwnd, &temp);
+        
+        event->x = temp.x;
+        event->y = temp.y;
+
+        event->axes = g_new0(gdouble, 2);
+        event->axes[0] = temp.x;    // screen relative
+        event->axes[1] = temp.y;
+
+        event->sequence = GUINT_TO_POINTER(p->dwID);
+
+        event->emulating_pointer = emulationSequence == p->dwID;
+        if (event->emulating_pointer)
+          gdk_event_set_pointer_emulated((GdkEvent *)event, TRUE);
+        _gdk_win32_append_event((GdkEvent *)event);
+      }
+  }
+  CloseTouchInputHandle(msg->lParam);
+  return TRUE;
+}
+
 #define GDK_ANY_BUTTON_MASK (GDK_BUTTON1_MASK | \
 			     GDK_BUTTON2_MASK | \
 			     GDK_BUTTON3_MASK | \
@@ -2638,6 +2719,9 @@
 	button = 5;
 
     buttondown0:
+      // ignore emulated touch events - GTK handles this in gtk_widget_real_touch_event()
+      if ((GetMessageExtraInfo() & 0xffffff00) == MOUSEEVENTF_FROMTOUCH)
+        break;
       GDK_NOTE (EVENTS,
 		g_print (" (%d,%d)",
 			 GET_X_LPARAM (msg->lParam), GET_Y_LPARAM (msg->lParam)));
@@ -2677,6 +2761,9 @@
 	button = 5;
 
     buttonup0:
+      // ignore emulated touch events - GTK handles this in gtk_widget_real_touch_event()
+      if ((GetMessageExtraInfo() & 0xffffff00) == MOUSEEVENTF_FROMTOUCH)
+        break;
       GDK_NOTE (EVENTS,
 		g_print (" (%d,%d)",
 			 GET_X_LPARAM (msg->lParam), GET_Y_LPARAM (msg->lParam)));
@@ -2732,6 +2819,9 @@
       break;
 
     case WM_MOUSEMOVE:
+      // ignore emulated touch events - GTK handles this in gtk_widget_real_touch_event()
+      if ((GetMessageExtraInfo() & 0xffffff00) == MOUSEEVENTF_FROMTOUCH)
+        break;
       GDK_NOTE (EVENTS,
 		g_print (" %p (%d,%d)",
 			 (gpointer) msg->wParam,
@@ -3725,6 +3815,9 @@
 	gdk_event_free (event);
 
       break;
+    case WM_TOUCH:
+      gdk_input_touch_event(display, msg, window);
+      break;
     }
 
 done:
diff -ur gtk+-3.22.5.orig/gdk/win32/gdkprivate-win32.h gtk+-3.22.5.windows_multitouch/gdk/win32/gdkprivate-win32.h
--- gtk+-3.22.5.orig/gdk/win32/gdkprivate-win32.h	2016-11-23 16:02:35.000000000 -0800
+++ gtk+-3.22.5.windows_multitouch/gdk/win32/gdkprivate-win32.h	2016-12-28 17:33:36.185201573 -0800
@@ -27,7 +27,7 @@
 
 #ifndef WINVER
 /* Vista or newer */
-#define WINVER 0x0600
+#define WINVER 0x0601
 #endif
 
 #ifndef _WIN32_WINNT
diff -ur gtk+-3.22.5.orig/gdk/win32/gdkwindow-win32.c gtk+-3.22.5.windows_multitouch/gdk/win32/gdkwindow-win32.c
--- gtk+-3.22.5.orig/gdk/win32/gdkwindow-win32.c	2016-11-23 16:02:35.000000000 -0800
+++ gtk+-3.22.5.windows_multitouch/gdk/win32/gdkwindow-win32.c	2016-12-28 19:23:44.339867825 -0800
@@ -960,6 +960,11 @@
   if (attributes_mask & GDK_WA_CURSOR)
     gdk_window_set_cursor (window, attributes->cursor);
 
+  if (event_mask & GDK_TOUCH_MASK)
+  {
+    gboolean pass = RegisterTouchWindow(hwndNew, 0);
+    g_return_if_fail(pass);
+  }
   _gdk_win32_window_enable_transparency (window);
 }
 
diff -ur gtk+-3.22.5.orig/tests/testinput.c gtk+-3.22.5.windows_multitouch/tests/testinput.c
--- gtk+-3.22.5.orig/tests/testinput.c	2016-11-23 16:02:36.000000000 -0800
+++ gtk+-3.22.5.windows_multitouch/tests/testinput.c	2016-12-28 18:10:27.367194333 -0800
@@ -23,6 +23,7 @@
  */
 
 #include "config.h"
+#include <stdint.h>
 #include <stdio.h>
 #include "gtk/gtk.h"
 #include <math.h>
@@ -265,6 +266,14 @@
   return TRUE;
 }
 
+static gint
+touch_event (GtkWidget *widget, GdkEventTouch *event)
+{
+  // Microsoft printf doesn't recognize %z for sequence, so cast to ll
+  g_printf("touch type=%d seq=%llu ev=(%f %f) axes=(%f %f) state=%x emu=%d\n", event->type, (uint64_t)event->sequence, event->x, event->y, event->axes[0], event->axes[1], event->state, event->emulating_pointer);
+  return FALSE;    // return false so that default handler, gtk_widget_real_touch_event() is called. If not called, then touch events won't get translated to emulated mouse events
+}
+
 /* We track the next two events to know when we need to draw a
    cursor */
 
@@ -345,13 +354,16 @@
 		    G_CALLBACK (leave_notify_event), NULL);
   g_signal_connect (drawing_area, "proximity_out_event",
 		    G_CALLBACK (proximity_out_event), NULL);
+  g_signal_connect (drawing_area, "touch_event",
+                    G_CALLBACK(touch_event), NULL);
 
   event_mask = GDK_EXPOSURE_MASK |
     GDK_LEAVE_NOTIFY_MASK |
     GDK_BUTTON_PRESS_MASK |
     GDK_KEY_PRESS_MASK |
     GDK_POINTER_MOTION_MASK |
-    GDK_PROXIMITY_OUT_MASK;
+    GDK_PROXIMITY_OUT_MASK |
+    GDK_TOUCH_MASK;
 
   gtk_widget_set_events (drawing_area, event_mask);
