]> gitweb.factorcode.org Git - factor.git/blob - basis/game-input/iokit/iokit.factor
Delete empty unit tests files, remove 1- and 1+, reorder IN: lines in a lot of places...
[factor.git] / basis / game-input / iokit / iokit.factor
1 USING: cocoa cocoa.plists core-foundation iokit iokit.hid
2 kernel cocoa.enumeration destructors math.parser cocoa.application 
3 sequences locals combinators.short-circuit threads
4 namespaces assocs arrays combinators hints alien
5 core-foundation.run-loop accessors sequences.private
6 alien.c-types math parser game-input vectors bit-arrays ;
7 IN: game-input.iokit
8
9 SINGLETON: iokit-game-input-backend
10
11 SYMBOLS: +hid-manager+ +keyboard-state+ +mouse-state+ +controller-states+ ;
12
13 iokit-game-input-backend game-input-backend set-global
14
15 : make-hid-manager ( -- alien )
16     f 0 IOHIDManagerCreate ;
17
18 : set-hid-manager-matching ( alien matching-seq -- )
19     >plist IOHIDManagerSetDeviceMatchingMultiple ;
20
21 : devices-from-hid-manager ( manager -- vector )
22     [
23         IOHIDManagerCopyDevices
24         [ &CFRelease NSFastEnumeration>vector ] [ f ] if*
25     ] with-destructors ;
26
27 CONSTANT: game-devices-matching-seq
28     {
29         H{ { "DeviceUsage" 2 } { "DeviceUsagePage" 1 } } ! mouses
30         H{ { "DeviceUsage" 4 } { "DeviceUsagePage" 1 } } ! joysticks
31         H{ { "DeviceUsage" 5 } { "DeviceUsagePage" 1 } } ! gamepads
32         H{ { "DeviceUsage" 6 } { "DeviceUsagePage" 1 } } ! keyboards
33         H{ { "DeviceUsage" 7 } { "DeviceUsagePage" 1 } } ! keypads
34         H{ { "DeviceUsage" 8 } { "DeviceUsagePage" 1 } } ! multiaxis controllers
35     }
36
37 CONSTANT: buttons-matching-hash
38     H{ { "UsagePage" 9 } { "Type" 2 } }
39 CONSTANT: keys-matching-hash
40     H{ { "UsagePage" 7 } { "Type" 2 } }
41 CONSTANT: x-axis-matching-hash
42     H{ { "UsagePage" 1 } { "Usage" HEX: 30 } { "Type" 1 } }
43 CONSTANT: y-axis-matching-hash
44     H{ { "UsagePage" 1 } { "Usage" HEX: 31 } { "Type" 1 } }
45 CONSTANT: z-axis-matching-hash
46     H{ { "UsagePage" 1 } { "Usage" HEX: 32 } { "Type" 1 } }
47 CONSTANT: rx-axis-matching-hash
48     H{ { "UsagePage" 1 } { "Usage" HEX: 33 } { "Type" 1 } }
49 CONSTANT: ry-axis-matching-hash
50     H{ { "UsagePage" 1 } { "Usage" HEX: 34 } { "Type" 1 } }
51 CONSTANT: rz-axis-matching-hash
52     H{ { "UsagePage" 1 } { "Usage" HEX: 35 } { "Type" 1 } }
53 CONSTANT: slider-matching-hash
54     H{ { "UsagePage" 1 } { "Usage" HEX: 36 } { "Type" 1 } }
55 CONSTANT: wheel-matching-hash
56     H{ { "UsagePage" 1 } { "Usage" HEX: 38 } { "Type" 1 } }
57 CONSTANT: hat-switch-matching-hash
58     H{ { "UsagePage" 1 } { "Usage" HEX: 39 } { "Type" 1 } }
59
60 : device-elements-matching ( device matching-hash -- vector )
61     [
62         >plist 0 IOHIDDeviceCopyMatchingElements
63         [ &CFRelease NSFastEnumeration>vector ] [ f ] if*
64     ] with-destructors ;
65
66 : button-count ( device -- button-count )
67     buttons-matching-hash device-elements-matching length ;
68
69 : ?axis ( device hash -- axis/f )
70     device-elements-matching [ f ] [ first ] if-empty ;
71
72 : ?x-axis ( device -- ? )
73     x-axis-matching-hash ?axis ;
74 : ?y-axis ( device -- ? )
75     y-axis-matching-hash ?axis ;
76 : ?z-axis ( device -- ? )
77     z-axis-matching-hash ?axis ;
78 : ?rx-axis ( device -- ? )
79     rx-axis-matching-hash ?axis ;
80 : ?ry-axis ( device -- ? )
81     ry-axis-matching-hash ?axis ;
82 : ?rz-axis ( device -- ? )
83     rz-axis-matching-hash ?axis ;
84 : ?slider ( device -- ? )
85     slider-matching-hash ?axis ;
86 : ?hat-switch ( device -- ? )
87     hat-switch-matching-hash ?axis ;
88
89 : device-property ( device key -- value )
90     <NSString> IOHIDDeviceGetProperty [ plist> ] [ f ] if* ;
91 : element-property ( element key -- value )
92     <NSString> IOHIDElementGetProperty [ plist> ] [ f ] if* ;
93 : set-element-property ( element key value -- )
94     [ <NSString> ] [ >plist ] bi* IOHIDElementSetProperty drop ;
95 : transfer-element-property ( element from-key to-key -- )
96     [ dupd element-property ] dip swap
97     [ set-element-property ] [ 2drop ] if* ;
98
99 : mouse-device? ( device -- ? )
100     1 2 IOHIDDeviceConformsTo ;
101
102 : controller-device? ( device -- ? )
103     {
104         [ 1 4 IOHIDDeviceConformsTo ]
105         [ 1 5 IOHIDDeviceConformsTo ]
106         [ 1 8 IOHIDDeviceConformsTo ]
107     } 1|| ;
108
109 : element-usage ( element -- {usage-page,usage} )
110     [ IOHIDElementGetUsagePage ] [ IOHIDElementGetUsage ] bi
111     2array ;
112
113 : button? ( element -- ? )
114     IOHIDElementGetUsagePage 9 = ; inline
115 : keyboard-key? ( element -- ? )
116     IOHIDElementGetUsagePage 7 = ; inline
117 : axis? ( element -- ? )
118     IOHIDElementGetUsagePage 1 = ; inline
119
120 : x-axis? ( {usage-page,usage} -- ? )
121     IOHIDElementGetUsage HEX: 30 = ; inline
122 : y-axis? ( {usage-page,usage} -- ? )
123     IOHIDElementGetUsage HEX: 31 = ; inline
124 : z-axis? ( {usage-page,usage} -- ? )
125     IOHIDElementGetUsage HEX: 32 = ; inline
126 : rx-axis? ( {usage-page,usage} -- ? )
127     IOHIDElementGetUsage HEX: 33 = ; inline
128 : ry-axis? ( {usage-page,usage} -- ? )
129     IOHIDElementGetUsage HEX: 34 = ; inline
130 : rz-axis? ( {usage-page,usage} -- ? )
131     IOHIDElementGetUsage HEX: 35 = ; inline
132 : slider? ( {usage-page,usage} -- ? )
133     IOHIDElementGetUsage HEX: 36 = ; inline
134 : wheel? ( {usage-page,usage} -- ? )
135     IOHIDElementGetUsage HEX: 38 = ; inline
136 : hat-switch? ( {usage-page,usage} -- ? )
137     IOHIDElementGetUsage HEX: 39 = ; inline
138
139 CONSTANT: pov-values
140     {
141         pov-up pov-up-right pov-right pov-down-right
142         pov-down pov-down-left pov-left pov-up-left
143         pov-neutral
144     }
145
146 : button-value ( value -- f/(0,1] )
147     IOHIDValueGetIntegerValue dup zero? [ drop f ] when ;
148 : axis-value ( value -- [-1,1] )
149     kIOHIDValueScaleTypeCalibrated IOHIDValueGetScaledValue ;
150 : mouse-axis-value ( value -- n )
151     IOHIDValueGetIntegerValue ;
152 : pov-value ( value -- pov-direction )
153     IOHIDValueGetIntegerValue pov-values ?nth [ pov-neutral ] unless* ;
154
155 : record-button ( state hid-value element -- )
156     [ buttons>> ] [ button-value ] [ IOHIDElementGetUsage 1 - ] tri* rot set-nth ;
157
158 : record-controller ( controller-state value -- )
159     dup IOHIDValueGetElement {
160         { [ dup button? ] [ record-button ] } 
161         { [ dup axis? ] [ {
162             { [ dup x-axis? ] [ drop axis-value >>x drop ] }
163             { [ dup y-axis? ] [ drop axis-value >>y drop ] }
164             { [ dup z-axis? ] [ drop axis-value >>z drop ] }
165             { [ dup rx-axis? ] [ drop axis-value >>rx drop ] }
166             { [ dup ry-axis? ] [ drop axis-value >>ry drop ] }
167             { [ dup rz-axis? ] [ drop axis-value >>rz drop ] }
168             { [ dup slider? ] [ drop axis-value >>slider drop ] }
169             { [ dup hat-switch? ] [ drop pov-value >>pov drop ] }
170             [ 3drop ]
171         } cond ] }
172         [ 3drop ]
173     } cond ;
174
175 HINTS: record-controller { controller-state alien } ;
176
177 : ?set-nth ( value nth seq -- )
178     2dup bounds-check? [ set-nth-unsafe ] [ 3drop ] if ;
179
180 : record-keyboard ( keyboard-state value -- )
181     dup IOHIDValueGetElement dup keyboard-key? [
182         [ IOHIDValueGetIntegerValue c-bool> ]
183         [ IOHIDElementGetUsage ] bi*
184         rot ?set-nth
185     ] [ 3drop ] if ;
186
187 HINTS: record-keyboard { bit-array alien } ;
188
189 : record-mouse ( mouse-state value -- )
190     dup IOHIDValueGetElement {
191         { [ dup button? ] [ record-button ] }
192         { [ dup axis? ] [ {
193             { [ dup x-axis? ] [ drop mouse-axis-value [ + ] curry change-dx drop ] }
194             { [ dup y-axis? ] [ drop mouse-axis-value [ + ] curry change-dy drop ] }
195             { [ dup wheel?  ] [ drop mouse-axis-value [ + ] curry change-scroll-dx drop ] }
196             { [ dup z-axis? ] [ drop mouse-axis-value [ + ] curry change-scroll-dy drop ] }
197             [ 3drop ]
198         } cond ] }
199         [ 3drop ]
200     } cond ;
201
202 HINTS: record-mouse { mouse-state alien } ;
203
204 M: iokit-game-input-backend read-mouse
205     +mouse-state+ get ;
206
207 M: iokit-game-input-backend reset-mouse
208     +mouse-state+ get
209         0 >>dx
210         0 >>dy
211         0 >>scroll-dx 
212         0 >>scroll-dy
213         drop ;
214
215 : default-calibrate-saturation ( element -- )
216     [ kIOHIDElementMinKey kIOHIDElementCalibrationSaturationMinKey transfer-element-property ]
217     [ kIOHIDElementMaxKey kIOHIDElementCalibrationSaturationMaxKey transfer-element-property ]
218     bi ;
219
220 : default-calibrate-axis ( element -- )
221     [ kIOHIDElementCalibrationMinKey -1.0 set-element-property ]
222     [ kIOHIDElementCalibrationMaxKey 1.0 set-element-property ]
223     [ default-calibrate-saturation ]
224     tri ;
225
226 : default-calibrate-slider ( element -- )
227     [ kIOHIDElementCalibrationMinKey 0.0 set-element-property ]
228     [ kIOHIDElementCalibrationMaxKey 1.0 set-element-property ]
229     [ default-calibrate-saturation ]
230     tri ;
231
232 : (default) ( ? quot -- )
233     [ f ] if* ; inline
234
235 : <device-controller-state> ( device -- controller-state )
236     {
237         [ ?x-axis [ default-calibrate-axis 0.0 ] (default) ]
238         [ ?y-axis [ default-calibrate-axis 0.0 ] (default) ]
239         [ ?z-axis [ default-calibrate-axis 0.0 ] (default) ]
240         [ ?rx-axis [ default-calibrate-axis 0.0 ] (default) ]
241         [ ?ry-axis [ default-calibrate-axis 0.0 ] (default) ]
242         [ ?rz-axis [ default-calibrate-axis 0.0 ] (default) ]
243         [ ?slider [ default-calibrate-slider 0.0 ] (default) ]
244         [ ?hat-switch pov-neutral and ]
245         [ button-count f <array> ]
246     } cleave controller-state boa ;
247
248 : ?add-mouse-buttons ( device -- )
249     button-count +mouse-state+ get buttons>> 
250     2dup length >
251     [ set-length ] [ 2drop ] if ;
252
253 : device-matched-callback ( -- alien )
254     [| context result sender device |
255         {
256             { [ device controller-device? ] [
257                 device <device-controller-state>
258                 device +controller-states+ get set-at
259             ] }
260             { [ device mouse-device? ] [ device ?add-mouse-buttons ] }
261             [ ]
262         } cond
263     ] IOHIDDeviceCallback ;
264
265 : device-removed-callback ( -- alien )
266     [| context result sender device |
267         device +controller-states+ get delete-at
268     ] IOHIDDeviceCallback ;
269
270 : device-input-callback ( -- alien )
271     [| context result sender value |
272         {
273             { [ sender controller-device? ] [
274                 sender +controller-states+ get at value record-controller
275             ] }
276             { [ sender mouse-device? ] [ +mouse-state+ get value record-mouse ] }
277             [ +keyboard-state+ get value record-keyboard ]
278         } cond
279     ] IOHIDValueCallback ;
280
281 : initialize-variables ( manager -- )
282     +hid-manager+ set-global
283     4 <vector> +controller-states+ set-global
284     0 0 0 0 2 <vector> mouse-state boa
285         +mouse-state+ set-global
286     256 <bit-array> +keyboard-state+ set-global ;
287
288 M: iokit-game-input-backend (open-game-input)
289     make-hid-manager {
290         [ initialize-variables ]
291         [ device-matched-callback f IOHIDManagerRegisterDeviceMatchingCallback ]
292         [ device-removed-callback f IOHIDManagerRegisterDeviceRemovalCallback ]
293         [ device-input-callback f IOHIDManagerRegisterInputValueCallback ]
294         [ 0 IOHIDManagerOpen mach-error ]
295         [ game-devices-matching-seq set-hid-manager-matching ]
296         [
297             CFRunLoopGetMain CFRunLoopDefaultMode
298             IOHIDManagerScheduleWithRunLoop
299         ]
300     } cleave ;
301
302 M: iokit-game-input-backend (reset-game-input)
303     { +hid-manager+ +keyboard-state+ +mouse-state+ +controller-states+ }
304     [ f swap set-global ] each ;
305
306 M: iokit-game-input-backend (close-game-input)
307     +hid-manager+ get-global [
308         +hid-manager+ [ 
309             [
310                 CFRunLoopGetMain CFRunLoopDefaultMode
311                 IOHIDManagerUnscheduleFromRunLoop
312             ]
313             [ 0 IOHIDManagerClose drop ]
314             [ CFRelease ] tri
315             f
316         ] change-global
317         f +keyboard-state+ set-global
318         f +mouse-state+ set-global
319         f +controller-states+ set-global
320     ] when ;
321
322 M: iokit-game-input-backend get-controllers ( -- sequence )
323     +controller-states+ get keys [ controller boa ] map ;
324
325 : ?join ( pre post sep -- string )
326     2over start [ swap 2nip ] [ [ 2array ] dip join ] if ;
327
328 M: iokit-game-input-backend product-string ( controller -- string )
329     handle>>
330     [ kIOHIDManufacturerKey device-property ]
331     [ kIOHIDProductKey      device-property ] bi " " ?join ;
332 M: iokit-game-input-backend product-id ( controller -- integer )
333     handle>>
334     [ kIOHIDVendorIDKey  device-property ]
335     [ kIOHIDProductIDKey device-property ] bi 2array ;
336 M: iokit-game-input-backend instance-id ( controller -- integer )
337     handle>> kIOHIDLocationIDKey device-property ;
338
339 M: iokit-game-input-backend read-controller ( controller -- controller-state )
340     handle>> +controller-states+ get at clone ;
341
342 M: iokit-game-input-backend read-keyboard ( -- keyboard-state )
343     +keyboard-state+ get clone keyboard-state boa ;
344
345 M: iokit-game-input-backend calibrate-controller ( controller -- )
346     drop ;