Quick search

Table Of Contents

Source code for kivy.input.providers.wm_touch

'''
Support for WM_TOUCH messages (Windows platform)
================================================
'''

__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.motionevent import MotionEvent
from kivy.input.shape import ShapeRect


[docs]class WM_MotionEvent(MotionEvent): '''MotionEvent representing the WM_MotionEvent event. Supports pos, shape and size profiles. ''' __attrs__ = ('size', ) def depack(self, args): self.is_touch = True self.shape = ShapeRect() self.sx, self.sy = args[0], args[1] self.shape.width = args[2][0] self.shape.height = args[2][1] self.size = self.shape.width * self.shape.height self.profile = ('pos', 'shape', 'size') super(WM_MotionEvent, self).depack(args) def __str__(self): args = (self.id, self.uid, str(self.spos), self.device) return '<WMMotionEvent id:%d uid:%d pos:%s device:%s>' % args
if 'KIVY_DOC' in os.environ: # documentation hack 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 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): self.touch_events = deque() self.touches = {} self.uid = 0 # get window handle, and register to recive WM_TOUCH messages self.hwnd = windll.user32.GetActiveWindow() windll.user32.RegisterTouchWindow(self.hwnd, 1) # inject our own wndProc to handle messages # before window manager does self.new_windProc = WNDPROC(self._touch_wndProc) self.old_windProc = SetWindowLong_wrapper( self.hwnd, GWL_WNDPROC, self.new_windProc) 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 while True: try: t = self.touch_events.pop() except: 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) # actually dispatch input if t.event_type == 'begin': self.uid += 1 self.touches[t.id] = WM_MotionEvent( self.device, self.uid, [x, y, t.size()]) dispatch_fn('begin', self.touches[t.id]) if t.event_type == 'update' and t.id in self.touches: self.touches[t.id].move([x, y, t.size()]) dispatch_fn('update', self.touches[t.id]) if t.event_type == 'end' and t.id in self.touches: touch = self.touches[t.id] touch.move([x, y, t.size()]) touch.update_time_end() dispatch_fn('end', touch) del self.touches[t.id] def stop(self): windll.user32.UnregisterTouchWindow(self.hwnd) self.new_windProc = SetWindowLong_wrapper( self.hwnd, GWL_WNDPROC, self.old_windProc) # we inject this wndProc into our main window, to process # WM_TOUCH and mouse messages before the window manager does def _touch_wndProc(self, hwnd, msg, wParam, lParam): done = False if msg == WM_TABLET_QUERYSYSTEMGESTURE: return QUERYSYSTEMGESTURE_WNDPROC if msg == WM_TOUCH: done = self._touch_handler(msg, wParam, lParam) if msg >= WM_MOUSEMOVE and msg <= WM_MOUSELAST: done = self._mouse_handler(msg, wParam, lParam) if not done: return windll.user32.CallWindowProcW(self.old_windProc, hwnd, msg, wParam, lParam) return 1 # this on pushes WM_TOUCH messages onto our event stack def _touch_handler(self, msg, wParam, lParam): touches = (TOUCHINPUT * wParam)() windll.user32.GetTouchInputInfo(HANDLE(lParam), wParam, touches, sizeof(TOUCHINPUT)) for i in range(wParam): self.touch_events.appendleft(touches[i]) return True # filter fake mouse events, because touch and stylus # also make mouse events def _mouse_handler(self, msg, wparam, lParam): info = windll.user32.GetMessageExtraInfo() # its a touch or a pen if (info & PEN_OR_TOUCH_MASK) == PEN_OR_TOUCH_SIGNATURE: if info & PEN_EVENT_TOUCH_MASK: return True MotionEventFactory.register('wm_touch', WM_MotionEventProvider)