2 var <name, <bustype, <vendor, <product, <version, <physical, <unique;
6 stream << $( << name << ", ";
13 ].collect({ | x | "0x" ++ x.asHexString(4) }).printItemsOn(stream);
14 stream << ", " << physical << ", " << unique;
20 var <value = 0, <min = 0, <max = 0, <fuzz = 0, <flat = 0;
24 << this.class.name << $(
25 << "value: " << value << ", "
26 << "min: " << min << ", "
27 << "max: " << max << ", "
28 << "fuzz: " << fuzz << ", "
29 << "flat: " << flat << $)
34 var dataPtr, <path, <info, <caps, <spec, <slots, <isGrabbed=false, <>action;
36 classvar all, eventTypes, <>specs, <>deviceRoot = "/dev/input", deviceList;
37 classvar < eventLoopIsRunning = true;
41 specs = IdentityDictionary.new;
43 // maps event type (index) to max code value
49 0x000f, // EV_SW (switch) added by nescivi
63 0x0001, // EV_FF_STATUS
72 this.prStartEventLoop;
75 *buildDeviceTable{ |name|
76 "WARNING: buildDeviceTable is obsolete, please use buildDeviceList".postln;
77 ^LID.buildDeviceList( name );
81 "WARNING: deviceTable is obsolete, please use deviceList".postln;
89 *buildDeviceList{ |name|
90 var table, devices, d, open;
91 name = name ? "event";
92 devices = (deviceRoot++"/"++name++"*").pathMatch;
93 deviceList = Array.fill( devices.size, 0 );
96 if ( all.detect({ | dev | dev.path == it }).notNil,
98 d = try { LID( it ) };
101 deviceList[i] = [ it, d.info, d.slots ];
106 deviceList[i] = [ it, "could not open device" ];
115 b1: #[0x0001, 0x0110], // left button
116 b2: #[0x0001, 0x0111], // middle button
117 b3: #[0x0001, 0x0112], // right button
119 x: #[0x0002, 0x0000], // x axis
120 y: #[0x0002, 0x0001], // y axis
121 s: #[0x0002, 0x0008] // scroll wheel
124 *keyboardDeviceSpec {
127 one: [1, 2], two: [1, 3], three: [1, 4], four: [1, 5],
128 five: [1, 6], six: [1, 7], seven: [1, 8], eight: [1, 9],
129 nine: [1, 10], zero: [1, 11], minus: [1, 12], equal: [1, 13],
131 tab: [1, 15], q: [1, 16], w: [1, 17], e: [1, 18],
132 r: [1, 19], t: [1, 20], y: [1, 21], u: [1, 22], i: [1, 23],
133 o: [1, 24], p: [1, 25], leftbrace: [1, 26], rightbrace: [1, 27],
136 a: [1, 30], s: [1, 31], d: [1, 32], f: [1, 33], g: [1, 34],
137 h: [1, 35], j: [1, 36], k: [1, 37], l: [1, 38], semicolon: [1, 39],
142 z: [1, 44], x: [1, 45], c: [1, 46], v: [1, 47], b: [1, 48],
143 n: [1, 49], m: [1, 50], comma: [1, 51], dot: [1, 52],
144 slash: [1, 53], rightshift: [1, 54],
146 leftalt: [1, 56], space: [1, 57], capslock: [1, 58],
147 f1: [1, 59], f2: [1, 60], f3: [1, 61], f4: [1, 62],
148 f5: [1, 63], f6: [1, 64], f7: [1, 65], f8: [1, 66],
149 f9: [1, 67], f10: [1, 68], numlock: [1, 69], scrolllock: [1, 70],
150 kp7: [1, 71], kp8: [1, 72], kp9: [1, 73], kpminus: [1, 74],
151 kp4: [1, 75], kp5: [1, 76], kp6: [1, 77], kpplus: [1, 78],
152 kp1: [1, 79], kp2: [1, 80], kp3: [1, 81],
153 kp0: [1, 82], kpdot: [1, 83],
154 zenkakuhankaku: [1, 85],
162 katakanahiragana: [1, 93],
183 volumedown: [1, 114],
187 kpplusminus: [1, 118],
214 deletefile: [1, 146],
222 cyclewindows: [1, 154],
230 ejectclosecd: [1, 162],
233 previoussong: [1, 165],
246 scrolldown: [1, 178],
247 kpleftparen: [1, 179],
248 kprightparen: [1, 180],
270 fastforward: [1, 208],
286 brightnessdown: [1, 224],
287 brightnessup: [1, 225],
289 switchvideomode: [1, 227],
290 kbdillumtoggle: [1, 228],
291 kbdillumdown: [1, 229],
292 kbdillumup: [1, 230],
295 forwardmail: [1, 233],
304 all.copy.do({ | dev | dev.close });
306 *register { | name, spec |
310 path = PathName(path);
311 if (path.isRelativePath) {
312 path = (deviceRoot ++ "/" ++ path.fullPath).standardizePath
314 path = path.fullPath;
316 ^all.detect({ | dev | dev.path == path }) ?? { super.new.prInit(path) }
328 caps.keys.do { | evtType |
329 Post << "0x" << evtType.asHexString << ":\n";
330 caps[evtType].do { | evtCode |
331 Post << $\t << "0x" << evtCode.asHexString << "\n";
336 action = { | evtType, evtCode, value |
337 [evtType.asHexString, evtCode.asHexString, value].postln;
340 slot { | evtType, evtCode |
341 ^slots.atFail(evtType, {
342 Error("event type not supported").throw
344 Error("event code not supported").throw
348 ^this.slot(*spec.atFail(controlName, {
349 Error("invalid control name").throw
352 getAbsInfo { | evtCode |
353 ^this.prGetAbsInfo(evtCode, LIDAbsInfo.new)
355 getKeyState { | evtCode |
356 ^this.prGetKeyState(evtCode)
358 getLEDState { | evtCode |
361 setLEDState { |evtCode, evtValue |
362 ^this.prSetLedState( evtCode, evtValue )
364 setMSCState { |evtCode, evtValue |
365 ^this.prSetMscState( evtCode, evtValue )
367 grab { | flag = true |
368 // useful when using mouse or keyboard. be sure to have an
369 // 'exit point', or your desktop will be rendered useless ...
370 if (isGrabbed != flag) {
382 ^this.primitiveFailed
386 ^this.primitiveFailed
389 this.prOpen(argPath);
393 info = this.prGetInfo(LIDInfo.new);
394 spec = specs.atFail(info.name, { IdentityDictionary.new });
395 caps = IdentityDictionary.new;
396 slots = IdentityDictionary.new;
397 eventTypes.do { | evtTypeMax, evtType |
398 // nescivi: below was evtType.notNil, but since that is the index, that makes no sense... however evtTypeMax can be nil, and should be skipped if it is... so I'm changing it.
399 if (evtTypeMax.notNil and: { this.prEventTypeSupported(evtType) }) {
400 caps[evtType] = List.new;
401 slots[evtType] = IdentityDictionary.new;
402 for (0, evtTypeMax, { | evtCode |
403 if (this.prEventCodeSupported(evtType, evtCode)) {
404 caps[evtType].add(evtCode);
405 slots[evtType][evtCode] = LIDSlot.new(
406 this, evtType, evtCode
416 ^this.primitiveFailed
420 ^this.primitiveFailed
422 prEventTypeSupported { | evtType |
423 _LID_EventTypeSupported
424 ^this.primitiveFailed
426 prEventCodeSupported { | evtType, evtCode |
427 _LID_EventCodeSupported
428 ^this.primitiveFailed
432 ^this.primitiveFailed
434 prGetKeyState { | evtCode |
436 ^this.primitiveFailed
438 prGetAbsInfo { | evtCode, absInfo |
440 ^this.primitiveFailed
444 ^this.primitiveFailed
446 prHandleEvent { | evtType, evtCode, evtValue |
447 // not either or for the device action. Do slot actions in any case:
448 slots[evtType][evtCode].value_(evtValue);
451 action.value(evtType, evtCode, evtValue, slots[evtType][evtCode].value);
455 // this prevents a high cpu cycle when device was detached; added by marije
458 ("WARNING: Device was removed: " + this.path + this.info).postln;
462 prSetLedState { |evtCode, evtValue| // added by Marije Baalman
465 ^this.primitiveFailed
467 prSetMscState { |evtCode, evtValue|
470 ^this.primitiveFailed
475 var <device, <type, <code, value=0, <spec, <>action;
476 classvar slotTypeMap, <slotTypeStrings;
479 slotTypeMap = IdentityDictionary.new.addAll([
480 0x0001 -> LIDKeySlot,
481 0x0002 -> LIDRelSlot,
482 0x0003 -> LIDAbsSlot,
485 slotTypeStrings = IdentityDictionary.new.addAll([
488 0x0002 -> "Relative",
489 0x0003 -> "Absolute",
494 0x0015 -> "Force Feedback",
496 0x0017 -> "Force Feedback Status"
499 *new { | device, evtType, evtCode |
500 ^(slotTypeMap[evtType] ? this).newCopyArgs(device, evtType, evtCode).initSpec
503 spec = ControlSpec(0, 1, \lin, 1, 0);
511 value_ { | rawValue |
520 LIDKeySlot : LIDSlot {
523 value = device.getKeyState(code);
527 LIDRelSlot : LIDSlot {
528 var delta, <>deltaAction;
534 value = value + delta;
536 deltaAction.value(this);
542 LIDLedSlot : LIDSlot {
548 device.setLEDState( code, value );
553 LIDAbsSlot : LIDSlot {
557 info = device.getAbsInfo(code);
558 spec = ControlSpec(info.min, info.max, \lin, 1);
559 spec.default = spec.map(0.5).asInteger;