1 /* Wrapper for HIDDeviceService for General HID support */
7 classvar <>debug = false;
14 Class.initClassTree( Event );
15 extraClasses = Event.new;
16 Class.initClassTree( GeneralHID );
17 GeneralHID.add( this );
19 all = IdentityDictionary.new;
28 *put { arg key, object;
29 extraClasses.put( key, object );
32 *doesNotUnderstand { arg selector ... args;
33 ^extraClasses.perform( selector, *args );
36 /// ----------------- functions ---------------
39 HIDDeviceService.buildDeviceList(nil,nil);
40 //deviceList = HIDDeviceServer.deviceList;
41 deviceList = HIDDeviceService.devices.collect{ |dev,i|
42 [ dev, this.getInfo( dev ) ]
48 "HID devices at your disposal:".postln;
49 deviceList.do{ |dev,i|
50 "\t%:\t[\"%\"], vendor: %, product: %, locID: [%]\n".postf( i, dev[1].name, dev[1].vendor, dev[1].product, dev[1].physical );
54 *postDevicesAndProperties {
55 deviceList.do{ |dev,i|
56 [ i, dev[0], dev[1].asString ].postcs;
57 dev[0].elements.do({arg ele;
59 [ele.type, ele.usage, ele.cookie, ele.min, ele.max, ele.ioType, ele.usagePage, ele.usageType].postln;
66 info = GeneralHIDInfo.new(
77 *startEventLoop{ |rate|
78 if ( rate.isNil or: (rate.size == 0),
80 HIDDeviceService.runEventLoop;
82 HIDDeviceService.runEventLoop(rate[0]);
88 HIDDeviceService.stopEventLoop;
92 ^HIDDeviceService.eventLoopIsRunning;
96 ^super.new.init( dev );
100 HIDDeviceService.action_({arg productID, vendorID, locID, cookie, val;
101 // [productID, vendorID, locID, cookie, val].postln;
102 // if (debug) {("debug"+[productID, vendorID, locID, cookie, val]).postln;};
103 all.at( locID ).hidDeviceAction.value( cookie, val );
105 all.at( locID ).slots.at( cookie ).value_(val);
107 // fall thru to next action here...
108 if (debug) {("fall thru"+[productID, vendorID, locID, cookie, val]).postln;}
114 hidDeviceAction = {};
118 if ( dev.isKindOf( HIDDevice ),
122 slots = IdentityDictionary.new;
123 all = all.put( device.locID, this );
124 ^GeneralHIDDevice.new( this );
126 "not a valid device".warn;
132 info = GeneralHIDInfo.new(
144 var mySlots = IdentityDictionary.new;
145 var devElements = device.elements;
146 "From version 3.5, SuperCollider GeneralHID is using a different slot numbering scheme on OSX. Unfortunately, this breaks backwards compatibility. The advantage is that all elements will always be available.".inform;
147 // new version, using usage page and usage type
148 devElements.do{ |ele,i|
150 if ( mySlots[ ele.usagePage ].isNil) {
151 mySlots[ ele.usagePage ] = IdentityDictionary.new;
153 newSlot = MXHIDSlot.new( this, ele );
154 slots.put( ele.cookie, newSlot);
156 mySlots[ele.usagePage][ele.usageType] = GeneralHIDSlot.new( ele.usagePage, ele.usageType, this, newSlot );
163 // using the typemap:
164 GeneralHIDSlot.typeMap.keysValuesDo{ |key|
165 mySlots.put( key, IdentityDictionary.new );
167 devElements.do{ |ele,i|
169 if ( this.class.usageMap[ele.usagePage].notNil,{
170 key = this.class.usageMap[ele.usagePage].matchAt(ele.usageType);
172 //("key"+key+ele.usagePage+ele.usageType).postln;
174 newSlot = MXHIDSlot.new( device, key, ele.usageType, ele.cookie );
175 slots.put( ele.cookie, newSlot);
177 mySlots[key][ele.usageType] = GeneralHIDSlot.new( key, ele.usageType, device, newSlot );
180 mySlots = mySlots.select(_.notEmpty); // remove empty dicts
188 device.dequeueDevice;
211 grab{ // not implemented on osx
214 ungrab{ // not implemented on osx
220 // the usageMap maps the usagePage and usageType to the different slot types we are using
221 // this map is not yet complete, due to time limitations. Please write to the sc-user list
222 // if you use a device not on one of the defined pages, or when stuff is missing
223 usageMap = IdentityDictionary.new;
224 usageMap.put( 0x01, Dictionary.new ); // Generic Desktop
225 usageMap.at( 0x01 ).put( { |v| ( v >= 0x30 and: ( v <= 0x39 ) ); }, 0x0003 );
226 usageMap.at( 0x01 ).put( 0x3B, 0x0002 );
227 usageMap.at( 0x01 ).put( { |v| ( v == 0x3D or: ( v == 0x3E ) ); }, 0x0001 );
228 usageMap.at( 0x01 ).put( { |v| ( v >= 0x40 and: ( v <= 0x46 ) ); }, 0x0003 ); // maybe 0x0002 ??
229 usageMap.at( 0x01 ).put( 0x48, 0x0002 );
230 usageMap.at( 0x01 ).put( { |v| ( v >= 0x81 and: ( v <= 0x93 ) ); }, 0x0001 );
231 usageMap.at( 0x01 ).put( { |v| ( v >= 0xA0 and: ( v <= 0xA8 ) ); }, 0x0001 );
232 usageMap.at( 0x01 ).put( { |v| ( v >= 0xB0 and: ( v <= 0xB7 ) ); }, 0x0001 );
234 usageMap.put( 0x02, Dictionary.new ); // Simulations Controls
235 // feel free to fill in
236 usageMap.put( 0x03, Dictionary.new ); // VR Controls
237 // feel free to fill in
238 usageMap.put( 0x04, Dictionary.new ); // Sport Controls
239 // feel free to fill in
241 usageMap.put( 0x05, Dictionary.new ); // Game Controls
242 usageMap.at( 0x05 ).put( { |v| ( v >= 0x20 and: ( v <= 0x29 ) ); }, 0x0003 );
243 usageMap.at( 0x05 ).put( { |v| ( v >= 0x2A and: ( v <= 0x31 ) ); }, 0x0001 );
244 usageMap.at( 0x05 ).put( { |v| ( v >= 0x33 and: ( v <= 0x35 ) ); }, 0x0001 ); // ?? could be wrong, as they are select type
246 usageMap.put( 0x06, Dictionary.new ); // Generic Device Controls
247 // feel free to fill in
249 usageMap.put( 0x07, Dictionary.new ); // Keyboard / Keypad
250 usageMap.at( 0x07 ).put( { |v| ( v >= 0x01 and: ( v <= 0xA4 ) ); }, 0x0001 );
251 usageMap.at( 0x07 ).put( { |v| ( v >= 0xB0 and: ( v <= 0xDD ) ); }, 0x0001 );
252 usageMap.at( 0x07 ).put( { |v| ( v >= 0xE0 and: ( v <= 0xE7 ) ); }, 0x0001 );
254 usageMap.put( 0x08, Dictionary.new ); // LED
255 usageMap.at( 0x08 ).put( { |v| ( v >= 0x01 and: ( v <= 0x39 ) ); }, 0x0011 );
256 usageMap.at( 0x08 ).put( { |v| ( v >= 0x4B and: ( v <= 0x4D ) ); }, 0x0011 );
258 usageMap.put( 0x09, Dictionary.new ); // Button
259 usageMap.at( 0x09 ).put( { |v| ( v >= 0x01 and: ( v <= 0xFFFF ) ); }, 0x0001 );
261 usageMap.put( 0x0A, Dictionary.new ); // Ordinal Page
262 // feel free to fill in
264 usageMap.put( 0x0B, Dictionary.new ); // Telephony Page
265 // feel free to fill in
267 usageMap.put( 0x0C, Dictionary.new ); // Consumer
268 // feel free to fill in
270 usageMap.put( 0x0D, Dictionary.new ); // Digitizer controls
271 usageMap.at( 0x0D ).put( { |v| ( v >= 0x30 and: ( v <= 0x31 ) ); }, 0x0003 );
272 usageMap.at( 0x0D ).put( { |v| ( v >= 0x32 and: ( v <= 0x36 ) ); }, 0x0001 );
273 usageMap.at( 0x0D ).put( 0x38, 0x0003 );
274 usageMap.at( 0x0D ).put( 0x3B, 0x0003 );
275 usageMap.at( 0x0D ).put( 0x3C, 0x0001 );
276 usageMap.at( 0x0D ).put( { |v| ( v >= 0x3D and: ( v <= 0x41 ) ); }, 0x0003 );
277 usageMap.at( 0x0D ).put( { |v| ( v >= 0x42 and: ( v <= 0x46 ) ); }, 0x0001 );
279 usageMap.put( 0x0F, Dictionary.new ); // Physical Interface Device (Force Feedback)
280 // feel free to fill in
282 usageMap.put( 0x10, Dictionary.new ); // Unicode
283 // feel free to fill in
285 usageMap.put( 0x14, Dictionary.new ); // Alphanumeric
286 // feel free to fill in
288 usageMap.put( 0x40, Dictionary.new ); // Medical instrument
289 // feel free to fill in
297 var <device, <cookie, <value=0, <rawValue=0, <>action;
303 classvar slotTypeMap;
306 slotTypeMap = IdentityDictionary.new.addAll([
307 0x0001 -> MXHIDKeySlot,
308 0x0002 -> MXHIDRelSlot,
309 0x0003 -> MXHIDAbsSlot,
310 0x0011 -> MXHIDLedSlot
313 *new { | device, evtType, evtCode, evtCookie |
314 ^(slotTypeMap[evtType] ? this).newCopyArgs(device, evtType, evtCode, evtCookie).initSpec
318 *new{ |device, element|
319 ^super.new.init( device, element )
326 cookie = element.cookie;
327 type = element.usagePage;
328 code = element.usageType;
332 spec = ControlSpec(element.min, element.max, \lin, 1, 0);
337 value = spec.unmap( value );
339 device.action.value( type, code, rawValue, value );
347 MXHIDKeySlot : MXHIDSlot {
351 value = device.valueByCookie(cookie);
355 MXHIDRelSlot : MXHIDSlot {
356 var delta, <>deltaAction;
362 value = value + delta;
364 deltaAction.value(this);
365 device.action.value( type, code, dta, value );
371 MXHIDLedSlot : MXHIDSlot {
377 // FIXME: device.setLEDState( code, value );
378 device.setValueByCookie(cookie);
380 device.action.value( type, code, value, value );
384 MXHIDAbsSlot : MXHIDSlot {
388 info = device.elements.detect( { |ele| ele.cookie == cookie } );
389 spec = ControlSpec(info.min, info.max, \lin, 1);
390 spec.default = spec.map(0.5).asInteger;
391 //value = info.value;
396 var <value = 0, <min = 0, <max = 0, <fuzz = 0, <flat = 0;
400 << this.class.name << $(
401 << "value: " << value << ", "
402 << "min: " << min << ", "
403 << "max: " << max << ", "
404 << "fuzz: " << fuzz << ", "
405 << "flat: " << flat << $)