2
Native support of MultitouchSupport framework for MacBook (MaxOSX platform)
3
===========================================================================
6
__all__ = ('MacMotionEventProvider', )
12
from kivy.input.provider import MotionEventProvider
13
from kivy.input.factory import MotionEventFactory
14
from kivy.input.motionevent import MotionEvent
15
from kivy.input.shape import ShapeRect
17
if 'KIVY_DOC' not in os.environ:
18
CFArrayRef = ctypes.c_void_p
19
CFMutableArrayRef = ctypes.c_void_p
20
CFIndex = ctypes.c_long
22
dll = '/System/Library/PrivateFrameworks/' + \
23
'MultitouchSupport.framework/MultitouchSupport'
24
MultitouchSupport = ctypes.CDLL(dll)
26
CFArrayGetCount = MultitouchSupport.CFArrayGetCount
27
CFArrayGetCount.argtypes = [CFArrayRef]
28
CFArrayGetCount.restype = CFIndex
30
CFArrayGetValueAtIndex = MultitouchSupport.CFArrayGetValueAtIndex
31
CFArrayGetValueAtIndex.argtypes = [CFArrayRef, CFIndex]
32
CFArrayGetValueAtIndex.restype = ctypes.c_void_p
34
MTDeviceCreateList = MultitouchSupport.MTDeviceCreateList
35
MTDeviceCreateList.argtypes = []
36
MTDeviceCreateList.restype = CFMutableArrayRef
38
class MTPoint(ctypes.Structure):
39
_fields_ = [('x', ctypes.c_float),
40
('y', ctypes.c_float)]
42
class MTVector(ctypes.Structure):
43
_fields_ = [('position', MTPoint),
44
('velocity', MTPoint)]
46
class MTData(ctypes.Structure):
48
('frame', ctypes.c_int),
49
('timestamp', ctypes.c_double),
50
('identifier', ctypes.c_int),
51
# Current state (of unknown meaning).
52
('state', ctypes.c_int),
53
('unknown1', ctypes.c_int),
54
('unknown2', ctypes.c_int),
55
# Normalized position and vector of the touch (0 to 1)
56
('normalized', MTVector),
57
# The area of the touch.
58
('size', ctypes.c_float),
59
('unknown3', ctypes.c_int),
60
# The following three define the ellipsoid of a finger.
61
('angle', ctypes.c_float),
62
('major_axis', ctypes.c_float),
63
('minor_axis', ctypes.c_float),
64
('unknown4', MTVector),
65
('unknown5_1', ctypes.c_int),
66
('unknown5_2', ctypes.c_int),
67
('unknown6', ctypes.c_float), ]
69
MTDataRef = ctypes.POINTER(MTData)
71
MTContactCallbackFunction = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int,
72
MTDataRef, ctypes.c_int,
73
ctypes.c_double, ctypes.c_int)
75
MTDeviceRef = ctypes.c_void_p
77
MTRegisterContactFrameCallback = \
78
MultitouchSupport.MTRegisterContactFrameCallback
79
MTRegisterContactFrameCallback.argtypes = \
80
[MTDeviceRef, MTContactCallbackFunction]
81
MTRegisterContactFrameCallback.restype = None
83
MTDeviceStart = MultitouchSupport.MTDeviceStart
84
MTDeviceStart.argtypes = [MTDeviceRef, ctypes.c_int]
85
MTDeviceStart.restype = None
88
MTContactCallbackFunction = lambda x: None
91
class MacMotionEvent(MotionEvent):
92
'''MotionEvent representing a contact point on the touchpad. Supports pos
96
def depack(self, args):
98
self.shape = ShapeRect()
99
self.sx, self.sy = args[0], args[1]
100
self.shape.width = args[2]
101
self.shape.height = args[2]
102
self.profile = ('pos', 'shape')
103
super(MacMotionEvent, self).depack(args)
106
return '<MacMotionEvent id=%d pos=(%f, %f) device=%s>' \
107
% (self.id, self.sx, self.sy, self.device)
112
class MacMotionEventProvider(MotionEventProvider):
114
def __init__(self, *largs, **kwargs):
116
if _instance is not None:
117
raise Exception('Only one MacMotionEvent provider is allowed.')
119
super(MacMotionEventProvider, self).__init__(*largs, **kwargs)
124
# touches will be per devices
126
# lock needed to access on uid
127
self.lock = threading.Lock()
128
# event queue to dispatch in main thread
129
self.queue = collections.deque()
131
# ok, listing devices, and attach !
132
devices = MultitouchSupport.MTDeviceCreateList()
133
num_devices = CFArrayGetCount(devices)
134
for i in range(num_devices):
135
device = CFArrayGetValueAtIndex(devices, i)
136
# create touch dict for this device
137
data_id = str(device)
138
self.touches[data_id] = {}
140
MTRegisterContactFrameCallback(device, self._mts_callback)
141
MTDeviceStart(device, 0)
143
def update(self, dispatch_fn):
144
# dispatch all event from threads
147
event_type, touch = self.queue.popleft()
148
dispatch_fn(event_type, touch)
153
# i don't known how to stop it...
156
@MTContactCallbackFunction
157
def _mts_callback(device, data_ptr, n_fingers, timestamp, frame):
161
# XXX create live touch, we get one case that
162
# the device announced by macosx don't match the device
163
# in _mts_callback....
164
if not devid in _instance.touches:
165
_instance.touches[devid] = {}
167
touches = _instance.touches[devid]
170
for i in range(n_fingers):
171
# get pointer on data
174
# add this touch as an active touch
175
actives.append(data.identifier)
178
data_id = data.identifier
180
# prepare argument position
181
norm_pos = data.normalized.position
182
args = (norm_pos.x, norm_pos.y, data.size)
184
if not data_id in touches:
186
_instance.lock.acquire()
189
touch = MacMotionEvent(_instance.device, _instance.uid, args)
190
_instance.lock.release()
192
_instance.queue.append(('begin', touch))
194
touches[data_id] = touch
196
touch = touches[data_id]
197
# check if he really moved
198
if data.normalized.position.x == touch.sx and \
199
data.normalized.position.y == touch.sy:
202
_instance.queue.append(('update', touch))
205
for tid in list(touches.keys())[:]:
206
if tid not in actives:
208
touch.update_time_end()
209
_instance.queue.append(('end', touch))
214
MotionEventFactory.register('mactouch', MacMotionEventProvider)