Repository: kivy/kivy

Issue:
touch.y coordinate is wrong on windows  [$100] 
There is an issue with touch.y coordinate on windows which makes software appear unreliable when attempting to touch small widgets.

In fullscreen the touch.y is about 30 pixels higher  than the actual touch point over the whole screen.

Fullscreen - Touch at top of screen:
![fullscreen top of screen](https://cloud.githubusercontent.com/assets/329604/15535823/e712b3aa-2266-11e6-9788-c53876c181f1.jpg)

Fullscreen - Touch at middle of screen:
![fullscreen middle of screen](https://cloud.githubusercontent.com/assets/329604/15535822/e71158f2-2266-11e6-84d1-0acb973d8aa9.jpg)

Fullscreen - Touch at bottom of screen
![fullscreen bottom of screen](https://cloud.githubusercontent.com/assets/329604/15535824/e712b9a4-2266-11e6-9db9-d7dc1abe53d6.jpg)

In windowed mode the touch.y is slightly below the actual touch point at the top of the screen.
touch.y is correct around 1/4 of the way down the screen.
touch.y is around 35 pixels above the actual touch position at the bottom of the screen.

Windowed - Touch at top of screen
![windowed top of screen](https://cloud.githubusercontent.com/assets/329604/15535825/e714d7e8-2266-11e6-8610-365c70b071ea.jpg)

Windowed - Touch at middle of screen
![windowed middle of screen](https://cloud.githubusercontent.com/assets/329604/15535827/e7157a90-2266-11e6-9892-ec42e554ccc2.jpg)

Windowed - Touch at bottom of screen
![windowed bottom of screen](https://cloud.githubusercontent.com/assets/329604/15535826/e7151fb4-2266-11e6-82be-077e8960661c.jpg)

This issue can make software appear unreliable as users think they are touching a widget, but the widget does not respond as the y.coordinate is offset.
This is very noticeable on large screens.



Discussion:
This issue has been observed on Windows 8.1 and Windows 10 using an  Acer T272HL and an Asus PT201Q.

$100 bounty on bountysource
https://www.bountysource.com/issues/34557886-touch-y-coordinate-is-wrong-on-windows

Is the bug also present in Kivy master?

I have just installed the nightly wheel.
The issue in fullscreen mode has been resolved.
However the problem still remains in windowed mode.

I have absolutely the same issue on my Asus Transformer Book T100TAM (windows 8.1) on 1.9.0, 1.9.1 and last dev version of kivy.


Resolving patch:
diff --git a/kivy/input/providers/wm_common.py b/kivy/input/providers/wm_common.py
--- a/kivy/input/providers/wm_common.py
+++ b/kivy/input/providers/wm_common.py
@@ -4,6 +4,7 @@
 
 This file provides common definitions for constants used by WM_Touch / WM_Pen.
 '''
+import os
 
 WM_MOUSEFIRST = 512
 WM_MOUSEMOVE = 512
@@ -53,3 +54,98 @@
     TABLET_DISABLE_FLICKFALLBACKKEYS |
     TABLET_DISABLE_TOUCHSWITCH |
     TABLET_DISABLE_FLICKS)
+
+if 'KIVY_DOC' not in os.environ:
+    from ctypes.wintypes import (ULONG, HANDLE, DWORD, LONG, UINT,
+                                 WPARAM, LPARAM, BOOL)
+    from ctypes import (windll, WINFUNCTYPE, POINTER,
+                        c_int, Structure, sizeof, byref)
+
+    class RECT(Structure):
+        _fields_ = [
+            ('left', LONG),
+            ('top', LONG),
+            ('right', LONG),
+            ('bottom', LONG)]
+
+        x = property(lambda self: self.left)
+        y = property(lambda self: self.top)
+        w = property(lambda self: self.right - self.left)
+        h = property(lambda self: self.bottom - self.top)
+
+    class POINT(Structure):
+        _fields_ = [
+            ('x', LONG),
+            ('y', LONG)]
+
+    # check availability of RegisterTouchWindow
+    if not hasattr(windll.user32, 'RegisterTouchWindow'):
+        raise Exception('Unsupported Window version')
+
+    LRESULT = LPARAM
+    WNDPROC = WINFUNCTYPE(LRESULT, HANDLE, UINT, WPARAM, LPARAM)
+
+    class TOUCHINPUT(Structure):
+        _fields_ = [
+            ('x', LONG),
+            ('y', LONG),
+            ('pSource', HANDLE),
+            ('id', DWORD),
+            ('flags', DWORD),
+            ('mask', DWORD),
+            ('time', DWORD),
+            ('extraInfo', POINTER(ULONG)),
+            ('size_x', DWORD),
+            ('size_y', DWORD)]
+
+        def size(self):
+            return (self.size_x, self.size_y)
+
+        def screen_x(self):
+            return self.x / 100.0
+
+        def screen_y(self):
+            return self.y / 100.0
+
+        def _event_type(self):
+            if self.flags & TOUCHEVENTF_MOVE:
+                return 'update'
+            if self.flags & TOUCHEVENTF_DOWN:
+                return 'begin'
+            if self.flags & TOUCHEVENTF_UP:
+                return 'end'
+        event_type = property(_event_type)
+
+    try:
+        windll.user32.SetWindowLongPtrW.restype = WNDPROC
+        windll.user32.SetWindowLongPtrW.argtypes = [HANDLE, c_int, WNDPROC]
+        SetWindowLong_wrapper = windll.user32.SetWindowLongPtrW
+    except AttributeError:
+        windll.user32.SetWindowLongW.restype = WNDPROC
+        windll.user32.SetWindowLongW.argtypes = [HANDLE, c_int, WNDPROC]
+        SetWindowLong_wrapper = windll.user32.SetWindowLongW
+
+    windll.user32.GetMessageExtraInfo.restype = LPARAM
+    windll.user32.GetMessageExtraInfo.argtypes = []
+    windll.user32.GetClientRect.restype = BOOL
+    windll.user32.GetClientRect.argtypes = [HANDLE, POINTER(RECT)]
+    windll.user32.GetWindowRect.restype = BOOL
+    windll.user32.GetWindowRect.argtypes = [HANDLE, POINTER(RECT)]
+    windll.user32.CallWindowProcW.restype = LRESULT
+    windll.user32.CallWindowProcW.argtypes = [WNDPROC, HANDLE, UINT, WPARAM,
+                                              LPARAM]
+    windll.user32.GetActiveWindow.restype = HANDLE
+    windll.user32.GetActiveWindow.argtypes = []
+    windll.user32.RegisterTouchWindow.restype = BOOL
+    windll.user32.RegisterTouchWindow.argtypes = [HANDLE, ULONG]
+    windll.user32.UnregisterTouchWindow.restype = BOOL
+    windll.user32.UnregisterTouchWindow.argtypes = [HANDLE]
+    windll.user32.GetTouchInputInfo.restype = BOOL
+    windll.user32.GetTouchInputInfo.argtypes = [HANDLE, UINT,
+                                                POINTER(TOUCHINPUT), c_int]
+    windll.user32.GetSystemMetrics.restype = c_int
+    windll.user32.GetSystemMetrics.argtypes = [c_int]
+
+    windll.user32.ClientToScreen.restype = BOOL
+    windll.user32.ClientToScreen.argtypes = [HANDLE, POINTER(POINT)]
+
diff --git a/kivy/input/providers/wm_pen.py b/kivy/input/providers/wm_pen.py
--- a/kivy/input/providers/wm_pen.py
+++ b/kivy/input/providers/wm_pen.py
@@ -6,11 +6,7 @@
 __all__ = ('WM_PenProvider', 'WM_Pen')
 
 import os
-from kivy.input.providers.wm_common import (
-    PEN_OR_TOUCH_SIGNATURE, PEN_OR_TOUCH_MASK, GWL_WNDPROC,
-    WM_MOUSEMOVE, WM_LBUTTONUP, WM_LBUTTONDOWN,
-    WM_TABLET_QUERYSYSTEMGESTURE, QUERYSYSTEMGESTURE_WNDPROC,
-    PEN_EVENT_TOUCH_MASK)
+from kivy.input.providers.wm_common import *
 from kivy.input.motionevent import MotionEvent
 
 
@@ -31,48 +27,12 @@ def __str__(self):
 
 else:
     from collections import deque
-    from ctypes.wintypes import (ULONG, UINT, WPARAM, LPARAM,
-                                 HANDLE, BOOL)
-    from ctypes import (Structure, windll, byref, c_int16,
-                        c_int, WINFUNCTYPE, POINTER)
+    from ctypes import windll, byref, c_int16, c_int
     from kivy.input.provider import MotionEventProvider
     from kivy.input.factory import MotionEventFactory
 
-    LRESULT = LPARAM
-    WNDPROC = WINFUNCTYPE(LRESULT, HANDLE, UINT, WPARAM, LPARAM)
-
-    class RECT(Structure):
-        _fields_ = [
-            ('left', ULONG),
-            ('top', ULONG),
-            ('right', ULONG),
-            ('bottom', ULONG)]
-
-        x = property(lambda self: self.left)
-        y = property(lambda self: self.top)
-        w = property(lambda self: self.right - self.left)
-        h = property(lambda self: self.bottom - self.top)
     win_rect = RECT()
 
-    try:
-        windll.user32.SetWindowLongPtrW.restype = WNDPROC
-        windll.user32.SetWindowLongPtrW.argtypes = [HANDLE, c_int, WNDPROC]
-        SetWindowLong_wrapper = windll.user32.SetWindowLongPtrW
-    except AttributeError:
-        windll.user32.SetWindowLongW.restype = WNDPROC
-        windll.user32.SetWindowLongW.argtypes = [HANDLE, c_int, WNDPROC]
-        SetWindowLong_wrapper = windll.user32.SetWindowLongW
-
-    windll.user32.GetMessageExtraInfo.restype = LPARAM
-    windll.user32.GetMessageExtraInfo.argtypes = []
-    windll.user32.GetClientRect.restype = BOOL
-    windll.user32.GetClientRect.argtypes = [HANDLE, POINTER(RECT)]
-    windll.user32.CallWindowProcW.restype = LRESULT
-    windll.user32.CallWindowProcW.argtypes = [WNDPROC, HANDLE, UINT, WPARAM,
-                                              LPARAM]
-    windll.user32.GetActiveWindow.restype = HANDLE
-    windll.user32.GetActiveWindow.argtypes = []
-
     class WM_PenProvider(MotionEventProvider):
 
         def _is_pen_message(self, msg):
diff --git a/kivy/input/providers/wm_touch.py b/kivy/input/providers/wm_touch.py
--- a/kivy/input/providers/wm_touch.py
+++ b/kivy/input/providers/wm_touch.py
@@ -6,12 +6,7 @@
 __all__ = ('WM_MotionEventProvider', 'WM_MotionEvent')
 
 import os
-from kivy.input.providers.wm_common import (
-    WM_TABLET_QUERYSYSTEMGESTURE,
-    GWL_WNDPROC, QUERYSYSTEMGESTURE_WNDPROC, WM_TOUCH, WM_MOUSEMOVE,
-    WM_MOUSELAST, PEN_OR_TOUCH_MASK, PEN_OR_TOUCH_SIGNATURE,
-    PEN_EVENT_TOUCH_MASK, TOUCHEVENTF_UP, TOUCHEVENTF_DOWN,
-    TOUCHEVENTF_MOVE, SM_CYCAPTION)
+from kivy.input.providers.wm_common import *
 from kivy.input.motionevent import MotionEvent
 from kivy.input.shape import ShapeRect
 
@@ -44,94 +39,12 @@ def __str__(self):
     WM_MotionEventProvider = None
 
 else:
-    from ctypes.wintypes import (ULONG, HANDLE, DWORD, LONG, UINT,
-                                 WPARAM, LPARAM, BOOL)
-    from ctypes import (windll, WINFUNCTYPE, POINTER,
-                        c_int, Structure, sizeof, byref)
+    from ctypes.wintypes import HANDLE
+    from ctypes import (windll, sizeof, byref)
     from collections import deque
     from kivy.input.provider import MotionEventProvider
     from kivy.input.factory import MotionEventFactory
 
-    # check availability of RegisterTouchWindow
-    if not hasattr(windll.user32, 'RegisterTouchWindow'):
-        raise Exception('Unsupported Window version')
-
-    LRESULT = LPARAM
-    WNDPROC = WINFUNCTYPE(LRESULT, HANDLE, UINT, WPARAM, LPARAM)
-
-    class TOUCHINPUT(Structure):
-        _fields_ = [
-            ('x', LONG),
-            ('y', LONG),
-            ('pSource', HANDLE),
-            ('id', DWORD),
-            ('flags', DWORD),
-            ('mask', DWORD),
-            ('time', DWORD),
-            ('extraInfo', POINTER(ULONG)),
-            ('size_x', DWORD),
-            ('size_y', DWORD)]
-
-        def size(self):
-            return (self.size_x, self.size_y)
-
-        def screen_x(self):
-            return self.x / 100.0
-
-        def screen_y(self):
-            return self.y / 100.0
-
-        def _event_type(self):
-            if self.flags & TOUCHEVENTF_MOVE:
-                return 'update'
-            if self.flags & TOUCHEVENTF_DOWN:
-                return 'begin'
-            if self.flags & TOUCHEVENTF_UP:
-                return 'end'
-        event_type = property(_event_type)
-
-    class RECT(Structure):
-        _fields_ = [
-            ('left', LONG),
-            ('top', LONG),
-            ('right', LONG),
-            ('bottom', LONG)]
-
-        x = property(lambda self: self.left)
-        y = property(lambda self: self.top)
-        w = property(lambda self: self.right - self.left)
-        h = property(lambda self: self.bottom - self.top)
-
-    try:
-        windll.user32.SetWindowLongPtrW.restype = WNDPROC
-        windll.user32.SetWindowLongPtrW.argtypes = [HANDLE, c_int, WNDPROC]
-        SetWindowLong_wrapper = windll.user32.SetWindowLongPtrW
-    except AttributeError:
-        windll.user32.SetWindowLongW.restype = WNDPROC
-        windll.user32.SetWindowLongW.argtypes = [HANDLE, c_int, WNDPROC]
-        SetWindowLong_wrapper = windll.user32.SetWindowLongW
-
-    windll.user32.GetMessageExtraInfo.restype = LPARAM
-    windll.user32.GetMessageExtraInfo.argtypes = []
-    windll.user32.GetClientRect.restype = BOOL
-    windll.user32.GetClientRect.argtypes = [HANDLE, POINTER(RECT)]
-    windll.user32.GetWindowRect.restype = BOOL
-    windll.user32.GetWindowRect.argtypes = [HANDLE, POINTER(RECT)]
-    windll.user32.CallWindowProcW.restype = LRESULT
-    windll.user32.CallWindowProcW.argtypes = [WNDPROC, HANDLE, UINT, WPARAM,
-                                              LPARAM]
-    windll.user32.GetActiveWindow.restype = HANDLE
-    windll.user32.GetActiveWindow.argtypes = []
-    windll.user32.RegisterTouchWindow.restype = BOOL
-    windll.user32.RegisterTouchWindow.argtypes = [HANDLE, ULONG]
-    windll.user32.UnregisterTouchWindow.restype = BOOL
-    windll.user32.UnregisterTouchWindow.argtypes = [HANDLE]
-    windll.user32.GetTouchInputInfo.restype = BOOL
-    windll.user32.GetTouchInputInfo.argtypes = [HANDLE, UINT,
-                                                POINTER(TOUCHINPUT), c_int]
-    windll.user32.GetSystemMetrics.restype = c_int
-    windll.user32.GetSystemMetrics.argtypes = [c_int]
-
     class WM_MotionEventProvider(MotionEventProvider):
 
         def start(self):
@@ -153,15 +66,13 @@ def start(self):
             self.old_windProc = SetWindowLong_wrapper(
                 self.hwnd, GWL_WNDPROC, self.new_windProc)
 
-            if Window.borderless or Window.fullscreen:
-                self.caption_size = 0
-            else:
-                self.caption_size = windll.user32.GetSystemMetrics(SM_CYCAPTION)
-
         def update(self, dispatch_fn):
-            win_rect = RECT()
-            windll.user32.GetWindowRect(self.hwnd, byref(win_rect))
-            caption = self.caption_size
+            c_rect = RECT()
+            windll.user32.GetClientRect(self.hwnd, byref(c_rect))
+            pt = POINT(x=0, y=0)
+            windll.user32.ClientToScreen(self.hwnd, byref(pt))
+            x_offset, y_offset = pt.x, pt.y
+            usable_w, usable_h = float(c_rect.w), float(c_rect.h)
 
             while True:
                 try:
@@ -170,9 +81,8 @@ def update(self, dispatch_fn):
                     break
 
                 # adjust x,y to window coordinates (0.0 to 1.0)
-                x = (t.screen_x() - win_rect.x) / float(win_rect.w)
-                y = 1.0 - (t.screen_y() - win_rect.y - caption
-                           ) / float(win_rect.h)
+                x = (t.screen_x() - x_offset) / usable_w
+                y = 1.0 - (t.screen_y() - y_offset) / usable_h
 
                 # actually dispatch input
                 if t.event_type == 'begin':


Test patch: