-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmultitouch.py
236 lines (192 loc) · 8.34 KB
/
multitouch.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
"""
multitouch.py
~~~~~~~~~~~~~~~
Access raw touchpad data using Apple's MultitouchSupport private framework.
To start data collection, create a Trackpad() instance and call its start()
method. To stop, call stop(). Data for a single start-stop cycle is stored in
Trackpad's touchData list. See touch_callback() for the types of data stored.
Also includes a VisualTrackpad class that draws data input in real time.
Source code for lines 23-117 from
http://blog.sendapatch.se/2009/november/macbook-multitouch-in-python.html
The source was modified to be importable.
"""
# Standard Libraries
import ctypes
import time
class MTPoint(ctypes.Structure):
_fields_ = [("x", ctypes.c_float),
("y", ctypes.c_float)]
class MTVector(ctypes.Structure):
_fields_ = [("position", MTPoint),
("velocity", MTPoint)]
class MTData(ctypes.Structure):
_fields_ = [
("frame", ctypes.c_int),
("timestamp", ctypes.c_double),
("identifier", ctypes.c_int),
("state", ctypes.c_int), # Current state (of unknown meaning).
("unknown1", ctypes.c_int),
("unknown2", ctypes.c_int),
("normalized", MTVector), # Normalized position and vector of
# the touch (0 to 1).
("size", ctypes.c_float), # The area of the touch.
("unknown3", ctypes.c_int),
# The following three define the ellipsoid of a finger.
("angle", ctypes.c_float),
("major_axis", ctypes.c_float),
("minor_axis", ctypes.c_float),
("unknown4", MTVector),
("unknown5_1", ctypes.c_int),
("unknown5_2", ctypes.c_int),
("unknown6", ctypes.c_float),
]
class Trackpad(object):
# Initializes contents of private framework MultitouchSupport
def __init__(self):
self.touchData = [] # holds data from start() method call
self.lastTouch = None
self.CFArrayRef = ctypes.c_void_p
self.CFMutableArrayRef = ctypes.c_void_p
self.CFIndex = ctypes.c_long
self.MultitouchSupport = ctypes.CDLL("/System/Library/PrivateFrameworks/" +
"MultitouchSupport.framework/MultitouchSupport")
self.CFArrayGetCount = self.MultitouchSupport.CFArrayGetCount
self.CFArrayGetCount.argtypes = [self.CFArrayRef]
self.CFArrayGetCount.restype = self.CFIndex
self.CFArrayGetValueAtIndex = self.MultitouchSupport.CFArrayGetValueAtIndex
self.CFArrayGetValueAtIndex.argtypes = [self.CFArrayRef, self.CFIndex]
self.CFArrayGetValueAtIndex.restype = ctypes.c_void_p
self.MTDeviceCreateList = self.MultitouchSupport.MTDeviceCreateList
self.MTDeviceCreateList.argtypes = []
self.MTDeviceCreateList.restype = self.CFMutableArrayRef
self.MTDataRef = ctypes.POINTER(MTData)
self.MTContactCallbackFunction = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int,
self.MTDataRef, ctypes.c_int, ctypes.c_double, ctypes.c_int)
self.MTDeviceRef = ctypes.c_void_p
self.MTRegisterContactFrameCallback = self.MultitouchSupport.MTRegisterContactFrameCallback
self.MTRegisterContactFrameCallback.argtypes = [self.MTDeviceRef, self.MTContactCallbackFunction]
self.MTRegisterContactFrameCallback.restype = None
self.MTDeviceStart = self.MultitouchSupport.MTDeviceStart
self.MTDeviceStart.argtypes = [self.MTDeviceRef, ctypes.c_int]
self.MTDeviceStart.restype = None
self.MTDeviceStop = self.MultitouchSupport.MTDeviceStop
self.MTDeviceStop.argtypes = [self.MTDeviceRef]
#MTDeviceStop.restype = None
def _cfarray_to_list(self, arr):
self.rv = []
self.n = self.CFArrayGetCount(arr)
for i in xrange(self.n):
self.rv.append(self.CFArrayGetValueAtIndex(arr, i))
return self.rv
def init_multitouch(self, cb):
self.strokeStart = False
self.devices = self._cfarray_to_list(self.MultitouchSupport.MTDeviceCreateList())
for device in self.devices:
self.MTRegisterContactFrameCallback(device, cb)
self.MTDeviceStart(device, 0)
return self.devices
def stop_multitouch(self, devices):
self.strokeStart = True
for device in devices:
self.MTDeviceStop(device)
def touch_callback(self, device, data_ptr, n_fingers, timestamp, frame):
data = data_ptr[0] # only use the first finger
pos = data.normalized.position
self.touchData.append((pos.x, pos.y, timestamp))
self.lastTouch = (pos.x, pos.y, time.time())
return 0
def start(self):
del self.touchData[:] # reset data
self.lastTouch = None
self.touch_callback = self.MTContactCallbackFunction(self.touch_callback)
self.devs = self.init_multitouch(self.touch_callback)
def stop(self):
self.stop_multitouch(self.devs)
class VisualTrackpad(Trackpad):
"""A graphical representation of the Trackpad class.
This version draws a toggle when receiving input.
Args:
x (int): Left canvas coordinate (in pixels).
y (int): Top canvas coordinate.
width (int): Width of the visual trackpad.
height (int): Height of the visual trackpad.
Attributes:
isDrawing (bool): True if currently receiving input, False otherwise.
fg (str): foreground color
bg (str): background color
active (str): active trackpad color
highlight (str): highlight color
lineWidth (int): Drawing width of line.
"""
def __init__(self, x, y, width, height):
super(VisualTrackpad, self).__init__() # call parent __init__
self.x = x
self.y = y
self.width = width
self.height = height
self.isDrawing = False
self.fg = "#666666"
self.bg = "#e5e6e6"
# self.active = "#cae2ed"
self.active = self.bg
self.highlight = "#72bdf6"
self.lineWidth = self.height / 60
def draw(self, canvas):
x0 = self.x
y0 = self.y
x1 = self.x + self.width
y1 = self.y + self.height
color = self.bg if self.isDrawing == False else self.active
canvas.create_rectangle(x0, y0, x1, y1, fill=color, width=1,
outline="lightgrey")
self.drawLastTouch(canvas)
self.drawDataLine(canvas)
# self.drawData(canvas)
def drawData(self, canvas):
r = 5
"""
# draw the most recent point larger and highlighted
if (self.touchData != []):
(normx0, normy0, time0) = self.touchData[-1] # most recent point
x0 = self.x + normx0 * self.width
y0 = self.y + self.height - normy0 * self.height
self.drawDot(canvas, x0, y0, r * 2, self.highlight)
"""
for (normx, normy, timestamp) in self.touchData:
x = self.x + normx * self.width
y = self.y + self.height - normy * self.height
self.drawDot(canvas, x, y, r, self.fg)
def drawDataLine(self, canvas):
for i in xrange(len(self.touchData) - 1):
(normx0, normy0, time0) = self.touchData[i]
(normx1, normy1, time1) = self.touchData[i + 1]
if (abs(time1 - time0) > 0.08):
continue
x0 = self.x + normx0 * self.width
y0 = self.y + self.height - normy0 * self.height
x1 = self.x + normx1 * self.width
y1 = self.y + self.height - normy1 * self.height
canvas.create_line(x0, y0, x1, y1, fill=self.fg,
width=self.lineWidth, capstyle="round")
def drawLastTouch(self, canvas):
"""Draws a blue highlight where finger is."""
if (self.lastTouch == None):
return
(normx, normy, t0) = self.lastTouch
if (abs(time.time() - t0) < 0.05):
x = self.x + normx * self.width
y = self.y + self.height - normy * self.height
r = self.lineWidth * 3 / 2
self.drawDot(canvas, x, y, r, self.highlight)
def drawDot(self, canvas, cx, cy, r, color):
x0 = cx - r
x1 = cx + r
y0 = cy - r
y1 = cy + r
canvas.create_oval(x0, y0, x1, y1, fill=color, width=0)
def start(self):
super(VisualTrackpad, self).start()
self.isDrawing = True
def stop(self):
super(VisualTrackpad, self).stop()
self.isDrawing = False