155 lines
5.7 KiB
Python
155 lines
5.7 KiB
Python
|
'''
|
||
|
Support for WM_TOUCH messages (Windows platform)
|
||
|
================================================
|
||
|
'''
|
||
|
|
||
|
__all__ = ('WM_MotionEventProvider', 'WM_MotionEvent')
|
||
|
|
||
|
import os
|
||
|
from kivy.input.providers.wm_common import WNDPROC, \
|
||
|
SetWindowLong_WndProc_wrapper, RECT, POINT, WM_TABLET_QUERYSYSTEMGESTURE, \
|
||
|
QUERYSYSTEMGESTURE_WNDPROC, WM_TOUCH, WM_MOUSEMOVE, WM_MOUSELAST, \
|
||
|
TOUCHINPUT, PEN_OR_TOUCH_MASK, PEN_OR_TOUCH_SIGNATURE, PEN_EVENT_TOUCH_MASK
|
||
|
from kivy.input.motionevent import MotionEvent
|
||
|
from kivy.input.shape import ShapeRect
|
||
|
|
||
|
Window = None
|
||
|
|
||
|
|
||
|
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 HANDLE
|
||
|
from ctypes import (windll, sizeof, byref)
|
||
|
from collections import deque
|
||
|
from kivy.input.provider import MotionEventProvider
|
||
|
from kivy.input.factory import MotionEventFactory
|
||
|
|
||
|
class WM_MotionEventProvider(MotionEventProvider):
|
||
|
|
||
|
def start(self):
|
||
|
global Window
|
||
|
if not Window:
|
||
|
from kivy.core.window import Window
|
||
|
|
||
|
self.touch_events = deque()
|
||
|
self.touches = {}
|
||
|
self.uid = 0
|
||
|
|
||
|
# get window handle, and register to receive 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_WndProc_wrapper(
|
||
|
self.hwnd, self.new_windProc)
|
||
|
|
||
|
def update(self, dispatch_fn):
|
||
|
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:
|
||
|
t = self.touch_events.pop()
|
||
|
except:
|
||
|
break
|
||
|
|
||
|
# adjust x,y to window coordinates (0.0 to 1.0)
|
||
|
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':
|
||
|
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_WndProc_wrapper(
|
||
|
self.hwnd, 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])
|
||
|
windll.user32.CloseTouchInputHandle(HANDLE(lParam))
|
||
|
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)
|