1LIDInfo { 2 var <name, <bustype, <vendorID, <productID, <version, <physical, <unique; 3 var <>path; 4 5 printOn { | stream | 6 super.printOn(stream); 7 stream << $( << name << ", " << path << ", "; 8 [ 9 vendorID, 10 productID, 11 version, 12 bustype 13 ].collect({ | x | "0x" ++ x.asHexString(4) }).printItemsOn(stream); 14 stream << ", " << physical << ", " << unique; 15 stream.put($)); 16 } 17 18 postInfo { 19 "\tName: \t%\n".postf( name ); 20 "\tVendor and product ID: \t%, %\n".postf( vendorID, productID ); 21 "\tPath: \t%\n".postf( path ); 22 "\tPhysical: \t%\n".postf( physical ); 23 "\tVersion and bustype: \t%, %\n".postf( version, bustype ); 24 "\tUnique: \t%\n".postf( unique ); 25 // "\tUsage name and page: \t%, \t%\n".postf( this.usageName, this.pageName ); 26 // "\tVendor name: \t%\n".postf( vendor ); 27 // "\tProduct name: \t%\n".postf( product ); 28 } 29 30 open{ 31 ^LID.new( path ); 32 } 33 34 findArgs { 35 ^[vendorID, productID, path, version, physical, unique] 36 } 37} 38 39LIDAbsInfo { 40 var <value = 0, <min = 0, <max = 0, <fuzz = 0, <flat = 0; 41 42 printOn { | stream | 43 stream 44 << this.class.name << $( 45 << "value: " << value << ", " 46 << "min: " << min << ", " 47 << "max: " << max << ", " 48 << "fuzz: " << fuzz << ", " 49 << "flat: " << flat << $) 50 } 51} 52 53LID { 54 var dataPtr, <path, <info, <caps, spec, <slots, <isGrabbed=false, <>action; 55 var <>closeAction; 56 var debugAction; 57 classvar openDevices, eventTypes, <specs, <>deviceRoot = "/dev/input", <available; 58 classvar eventLoopIsRunning = false; 59 classvar globalDebugAction; 60 classvar <action, prAction; 61 62 *running{ 63 ^eventLoopIsRunning; 64 } 65 66 *initClass { 67 // all = []; // becomes openDevices 68 specs = IdentityDictionary.new; 69 70 available = IdentityDictionary.new; 71 openDevices = []; 72 // availableUsages = IdentityDictionary.new; 73 74 eventTypes = [ 75 // maps event type (index) to max code value 76 0x0001, // EV_SYN 77 0x02ff, // EV_KEY 78 0x000f, // EV_REL 79 0x003f, // EV_ABS 80 0x0007, // EV_MSC 81 0x000f, // EV_SW (switch) added by nescivi 82 83 nil, nil, nil, 84 nil, nil, nil, nil, 85 nil, nil, nil, nil, 86 87 0x000f, // EV_LED 88 0x0007, // EV_SND 89 90 nil, 91 92 0x0001, // EV_REP 93 0x007f, // EV_FF 94 0x0000, // EV_PWR 95 0x0001, // EV_FF_STATUS 96 97 nil, nil, nil, nil, 98 nil, nil, nil 99 ]; 100 } 101 102 *initializeLID{ 103 "Starting LID eventloop".postln; 104 this.prStartEventLoop; 105 eventLoopIsRunning = true; 106 ShutDown.add { 107 this.closeAll; 108 this.prStopEventLoop; 109 }; 110 } 111 112 *findAvailable{ |name| 113 var devicePaths, d, open; 114 if ( eventLoopIsRunning.not ){ this.initializeLID; }; 115 name = name ? "event"; 116 devicePaths = (deviceRoot++"/"++name++"*").pathMatch; 117 // deviceList = Array.fill( devices.size, 0 ); 118 119 available = IdentityDictionary.new; 120 121 devicePaths.do{ |it,i| 122 open = false; 123 if ( openDevices.detect({ | dev | dev.path == it }).notNil, {open = true}); 124 d = try { LID( it ) }; 125 if ( d != nil, 126 { 127 d.info.path_( it ); 128 available.put( i, d.info ); // why did I need the slots already here? 129 if ( open.not, {d.close} ); 130 },{ 131 // just print that device is not openable, and don't add it to the available list 132 ("LID: could not open device with path"+ it + "\n" ).warn; 133 } 134 ); 135 }; 136 "LID: found % devices\n".postf( available.size ); 137 ^available 138 } 139 140 *postAvailable { 141 this.available.sortedKeysValuesDo { |k, v| "%: ".postf( k ); v.postInfo; }; 142 } 143 144 *register { | name, spec | 145 specs[name] = spec; 146 } 147 148 *mouseDeviceSpec { 149 ^( 150 // key 151 b1: #[0x0001, 0x0110], // left button 152 b2: #[0x0001, 0x0111], // middle button 153 b3: #[0x0001, 0x0112], // right button 154 // rel 155 x: #[0x0002, 0x0000], // x axis 156 y: #[0x0002, 0x0001], // y axis 157 s: #[0x0002, 0x0008] // scroll wheel 158 ) 159 } 160 *keyboardDeviceSpec { 161 ^( 162 esc: [1, 1], 163 one: [1, 2], two: [1, 3], three: [1, 4], four: [1, 5], 164 five: [1, 6], six: [1, 7], seven: [1, 8], eight: [1, 9], 165 nine: [1, 10], zero: [1, 11], minus: [1, 12], equal: [1, 13], 166 backspace: [1, 14], 167 tab: [1, 15], q: [1, 16], w: [1, 17], e: [1, 18], 168 r: [1, 19], t: [1, 20], y: [1, 21], u: [1, 22], i: [1, 23], 169 o: [1, 24], p: [1, 25], leftbrace: [1, 26], rightbrace: [1, 27], 170 enter: [1, 28], 171 leftctrl: [1, 29], 172 a: [1, 30], s: [1, 31], d: [1, 32], f: [1, 33], g: [1, 34], 173 h: [1, 35], j: [1, 36], k: [1, 37], l: [1, 38], semicolon: [1, 39], 174 apostrophe: [1, 40], 175 grave: [1, 41], 176 leftshift: [1, 42], 177 backslash: [1, 43], 178 z: [1, 44], x: [1, 45], c: [1, 46], v: [1, 47], b: [1, 48], 179 n: [1, 49], m: [1, 50], comma: [1, 51], dot: [1, 52], 180 slash: [1, 53], rightshift: [1, 54], 181 kpasterisk: [1, 55], 182 leftalt: [1, 56], space: [1, 57], capslock: [1, 58], 183 f1: [1, 59], f2: [1, 60], f3: [1, 61], f4: [1, 62], 184 f5: [1, 63], f6: [1, 64], f7: [1, 65], f8: [1, 66], 185 f9: [1, 67], f10: [1, 68], numlock: [1, 69], scrolllock: [1, 70], 186 kp7: [1, 71], kp8: [1, 72], kp9: [1, 73], kpminus: [1, 74], 187 kp4: [1, 75], kp5: [1, 76], kp6: [1, 77], kpplus: [1, 78], 188 kp1: [1, 79], kp2: [1, 80], kp3: [1, 81], 189 kp0: [1, 82], kpdot: [1, 83], 190 zenkakuhankaku: [1, 85], 191 the102nd: [1, 86], 192 f11: [1, 87], 193 f12: [1, 88], 194 ro: [1, 89], 195 katakana: [1, 90], 196 hiragana: [1, 91], 197 henkan: [1, 92], 198 katakanahiragana: [1, 93], 199 muhenkan: [1, 94], 200 kpjpcomma: [1, 95], 201 kpenter: [1, 96], 202 rightctrl: [1, 97], 203 kpslash: [1, 98], 204 sysrq: [1, 99], 205 rightalt: [1, 100], 206 linefeed: [1, 101], 207 home: [1, 102], 208 up: [1, 103], 209 pageup: [1, 104], 210 left: [1, 105], 211 right: [1, 106], 212 end: [1, 107], 213 down: [1, 108], 214 pagedown: [1, 109], 215 insert: [1, 110], 216 delete: [1, 111], 217 macro: [1, 112], 218 mute: [1, 113], 219 volumedown: [1, 114], 220 volumeup: [1, 115], 221 power: [1, 116], 222 kpequal: [1, 117], 223 kpplusminus: [1, 118], 224 pause: [1, 119], 225 kpcomma: [1, 121], 226 hanguel: [1, 122], 227 hanja: [1, 123], 228 yen: [1, 124], 229 leftmeta: [1, 125], 230 rightmeta: [1, 126], 231 compose: [1, 127], 232 stop: [1, 128], 233 again: [1, 129], 234 props: [1, 130], 235 undo: [1, 131], 236 front: [1, 132], 237 copy: [1, 133], 238 open: [1, 134], 239 paste: [1, 135], 240 find: [1, 136], 241 cut: [1, 137], 242 help: [1, 138], 243 menu: [1, 139], 244 calc: [1, 140], 245 setup: [1, 141], 246 sleep: [1, 142], 247 wakeup: [1, 143], 248 file: [1, 144], 249 sendfile: [1, 145], 250 deletefile: [1, 146], 251 xfer: [1, 147], 252 prog1: [1, 148], 253 prog2: [1, 149], 254 www: [1, 150], 255 msdos: [1, 151], 256 coffee: [1, 152], 257 direction: [1, 153], 258 cyclewindows: [1, 154], 259 mail: [1, 155], 260 bookmarks: [1, 156], 261 computer: [1, 157], 262 back: [1, 158], 263 forward: [1, 159], 264 closecd: [1, 160], 265 ejectcd: [1, 161], 266 ejectclosecd: [1, 162], 267 nextsong: [1, 163], 268 playpause: [1, 164], 269 previoussong: [1, 165], 270 stopcd: [1, 166], 271 record: [1, 167], 272 rewind: [1, 168], 273 phone: [1, 169], 274 iso: [1, 170], 275 config: [1, 171], 276 homepage: [1, 172], 277 refresh: [1, 173], 278 exit: [1, 174], 279 move: [1, 175], 280 edit: [1, 176], 281 scrollup: [1, 177], 282 scrolldown: [1, 178], 283 kpleftparen: [1, 179], 284 kprightparen: [1, 180], 285 new: [1, 181], 286 redo: [1, 182], 287 f13: [1, 183], 288 f14: [1, 184], 289 f15: [1, 185], 290 f16: [1, 186], 291 f17: [1, 187], 292 f18: [1, 188], 293 f19: [1, 189], 294 f20: [1, 190], 295 f21: [1, 191], 296 f22: [1, 192], 297 f23: [1, 193], 298 f24: [1, 194], 299 playcd: [1, 200], 300 pausecd: [1, 201], 301 prog3: [1, 202], 302 prog4: [1, 203], 303 suspend: [1, 205], 304 close: [1, 206], 305 play: [1, 207], 306 fastforward: [1, 208], 307 bassboost: [1, 209], 308 print: [1, 210], 309 hp: [1, 211], 310 camera: [1, 212], 311 sound: [1, 213], 312 question: [1, 214], 313 email: [1, 215], 314 chat: [1, 216], 315 search: [1, 217], 316 connect: [1, 218], 317 finance: [1, 219], 318 sport: [1, 220], 319 shop: [1, 221], 320 alterase: [1, 222], 321 cancel: [1, 223], 322 brightnessdown: [1, 224], 323 brightnessup: [1, 225], 324 media: [1, 226], 325 switchvideomode: [1, 227], 326 kbdillumtoggle: [1, 228], 327 kbdillumdown: [1, 229], 328 kbdillumup: [1, 230], 329 send: [1, 231], 330 reply: [1, 232], 331 forwardmail: [1, 233], 332 save: [1, 234], 333 documents: [1, 235] 334 ) 335 } 336 337 *openDevices{ 338 ^openDevices.copy; 339 } 340 341 *closeAll { 342 openDevices.copy.do{ |dev| dev.close }; 343 this.prStopEventLoop; 344 eventLoopIsRunning = false; 345 } 346 347 *openAt{ |index| 348 ^available.at( index ).open; 349 } 350 351 *findBy{ |vendorID, productID, path, version, physical, unique| 352 if ( [vendorID, productID, path, version, physical, unique].every( _.isNil ) ) { 353 ^nil; 354 }; 355 ^LID.available.select{ |info| 356 vendorID.isNil or: { info.vendorID == vendorID } and: 357 { productID.isNil or: { info.productID == productID } } and: 358 { path.isNil or: { info.path == path } } and: 359 { version.isNil or: { info.version == version } } and: 360 { physical.isNil or: { info.physical == physical.asSymbol } } and: 361 { unique.isNil or: { info.unique == unique.asSymbol } } 362 }; 363 } 364 365 *open{ |vendorID, productID, path, version, physical, unique| 366 var devInfo, device; 367 devInfo = this.findBy( vendorID, productID, path, version, physical, unique ); 368 if ( devInfo.isNil ){ 369 ("LID: could not find device" + vendorID + "," + productID + "," + path + "\n").error; 370 ^nil; 371 }; 372 devInfo = devInfo.asArray.first; 373 device = LID.new( devInfo.path ); 374 // merge usageDict? 375 ^device; 376 } 377 378 *openPath { |path| 379 // "LID: Opening device %\n".postf( path ); 380 ^LID.new( path ); 381 } 382 383 /* 384 *mergeUsageDict { |dev| 385 dev.usages.keysValuesDo { |key, val| 386 if ( availableUsages.at( key ).isNil ) { 387 availableUsages.put( key, IdentityDictionary.new ); 388 }; 389 availableUsages.at( key ).put( dev.id, val ); 390 }; 391 } 392 393 *removeUsageDict { |dev| // when device is closed 394 availableUsages.do { |val| 395 val.removeAt( dev.id ); 396 }; 397 } 398 */ 399 400 *new { | path | 401 path = PathName(path); 402 if (path.isRelativePath) { 403 path = (deviceRoot ++ "/" ++ path.fullPath).standardizePath 404 }{ 405 path = path.fullPath; 406 }; 407 ^openDevices.detect({ | dev | dev.path == path }) ?? { super.new.prInit(path) } 408 } 409 410 postInfo{ 411 this.info.postInfo; 412 } 413 414 vendor{ 415 ^this.info.vendorID; 416 } 417 418 product{ 419 ^this.info.productID; 420 } 421 422 postSlots{ 423 slots.sortedKeysValuesDo{ |k,v| 424 v.sortedKeysValuesDo{ |ks,vs| 425 "%,%: %\n".format( k, ks, vs.key ).post; 426 vs.postInfo; 427 }; 428 }; 429 } 430 431 isOpen { 432 ^dataPtr.notNil 433 } 434 435 close { 436 if (this.isOpen) { 437 closeAction.value; 438 this.prClose; 439 openDevices.remove(this); 440 }; 441 } 442 443 dumpCaps { 444 caps.keys.do { | evtType | 445 Post << "0x" << evtType.asHexString << ":\n"; 446 caps[evtType].do { | evtCode | 447 Post << $\t << "0x" << evtCode.asHexString << "\n"; 448 } 449 } 450 } 451 452 debug_{ |onoff| 453 if ( onoff ){ 454 debugAction = { | evtType, evtCode, value | 455 [this.info.name, evtType, evtCode, value].postln; 456 }; 457 }{ 458 debugAction = nil; 459 } 460 } 461 462 debug{ 463 ^debugAction.notNil; 464 } 465 466 slot { | evtType, evtCode | 467 ^slots.atFail(evtType, { 468 Error("event type not supported").throw 469 }).atFail(evtCode, { 470 Error("event code not supported").throw 471 }) 472 } 473 at { | controlName | 474 ^this.slot(*this.spec.atFail(controlName, { 475 Error("invalid control name").throw 476 })) 477 } 478 479 getAbsInfo { | evtCode | 480 ^this.prGetAbsInfo(evtCode, LIDAbsInfo.new) 481 } 482 getKeyState { | evtCode | 483 ^this.prGetKeyState(evtCode) 484 } 485 getLEDState { | evtCode | 486 ^0 487 } 488 setLEDState { |evtCode, evtValue | 489 ^this.prSetLedState( evtCode, evtValue ) 490 } 491 setMSCState { |evtCode, evtValue | 492 ^this.prSetMscState( evtCode, evtValue ) 493 } 494 495 grab { | flag = true | 496 // useful when using mouse or keyboard. be sure to have an 497 // 'exit point', or your desktop will be rendered useless ... 498 if (isGrabbed != flag) { 499 this.prGrab(flag); 500 isGrabbed = flag; 501 }; 502 } 503 ungrab { 504 this.grab(false) 505 } 506 507 *debug_{ |onoff = true| 508 if ( onoff ){ 509 globalDebugAction = { | device, evtType, evtCode, value | 510 [device.info.name, evtType, evtCode, value].postln; 511 }; 512 }{ 513 globalDebugAction = nil; 514 } 515 } 516 517 *debug{ 518 ^globalDebugAction.notNil; 519 } 520 521 // action interface: 522 *addRecvFunc { |function| 523 if ( prAction.isNil ) { 524 prAction = FunctionList.new; 525 }; 526 prAction = prAction.addFunc( function ); 527 } 528 529 *removeRecvFunc { |function| 530 prAction.removeFunc( function ); 531 } 532 533 534 *action_ { |function| 535 if ( action.notNil ) { 536 this.removeRecvFunc( action ); 537 }; 538 action = function; 539 this.addRecvFunc( function ); 540 } 541 542 spec{ |forceLookup = false| 543 if ( spec.notNil and: forceLookup.not ){ ^spec }; 544 spec = specs.atFail(info.name, { IdentityDictionary.new }); 545 spec.keysValuesDo{ |k,v| 546 var slot = slots[ v[0] ][ v[1] ]; 547 if ( slot.notNil ){ slot.key = k }; 548 }; 549 ^spec; 550 } 551 552 // PRIVATE 553 *prStartEventLoop { 554 _LID_Start 555 ^this.primitiveFailed 556 } 557 *prStopEventLoop { 558 _LID_Stop 559 ^this.primitiveFailed 560 } 561 prInit { | argPath | 562 this.prOpen(argPath); 563 openDevices = openDevices.add(this); 564 closeAction = {}; 565 path = argPath; 566 info = this.prGetInfo(LIDInfo.new); 567 info.path_( path ); 568 ("LID: Opened device: %\n".postf( this.info ) ); 569 caps = IdentityDictionary.new; 570 slots = IdentityDictionary.new; 571 eventTypes.do { | evtTypeMax, evtType | 572 // 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. 573 if (evtTypeMax.notNil and: { this.prEventTypeSupported(evtType) }) { 574 caps[evtType] = List.new; 575 slots[evtType] = IdentityDictionary.new; 576 for (0, evtTypeMax, { | evtCode | 577 if (this.prEventCodeSupported(evtType, evtCode)) { 578 caps[evtType].add(evtCode); 579 slots[evtType][evtCode] = LIDSlot.new( 580 this, evtType, evtCode 581 ); 582 }; 583 }); 584 caps[evtType].sort; 585 } 586 }; 587 } 588 prOpen { | path | 589 _LID_Open 590 ^this.primitiveFailed 591 } 592 prClose { 593 _LID_Close 594 ^this.primitiveFailed 595 } 596 prEventTypeSupported { | evtType | 597 _LID_EventTypeSupported 598 ^this.primitiveFailed 599 } 600 prEventCodeSupported { | evtType, evtCode | 601 _LID_EventCodeSupported 602 ^this.primitiveFailed 603 } 604 prGetInfo { | info | 605 _LID_GetInfo 606 ^this.primitiveFailed 607 } 608 prGetKeyState { | evtCode | 609 _LID_GetKeyState 610 ^this.primitiveFailed 611 } 612 prGetAbsInfo { | evtCode, absInfo | 613 _LID_GetAbsInfo 614 ^this.primitiveFailed 615 } 616 prGrab { | flag | 617 _LID_Grab 618 ^this.primitiveFailed 619 } 620 prHandleEvent { | evtType, evtCode, evtValue | 621 if ( debugAction.notNil ){ 622 debugAction.value( evtType, evtCode, evtValue ); 623 }; 624 if ( globalDebugAction.notNil ){ 625 globalDebugAction.value( this, evtType, evtCode, evtValue ); 626 }; 627 if ( slots.notNil ){ 628 // not either or for the device action. Do slot actions in any case: 629 slots[evtType][evtCode].rawValue_(evtValue); 630 // event callback 631 if (action.notNil) { 632 action.value(evtType, evtCode, evtValue, slots[evtType][evtCode].value); 633 }; 634 }; 635 if ( prAction.notNil ){ 636 prAction.value( this, evtType, evtCode, evtValue ); 637 } 638 } 639 640 // this prevents a high cpu cycle when device was detached; added by marije 641 prReadError{ 642 this.close; 643 ("WARNING: Device was removed: " + this.path + this.info).postln; 644 } 645 646 prSetLedState { |evtCode, evtValue| // added by Marije Baalman 647 // set LED value 648 _LID_SetLedState 649 ^this.primitiveFailed 650 } 651 prSetMscState { |evtCode, evtValue| 652 // set MSC value 653 _LID_SetMscState 654 ^this.primitiveFailed 655 } 656} 657 658LIDSlot { 659 var <device, <type, <code, <spec, <>action; 660 var rawValue = 0; 661 classvar slotTypeMap, <slotTypeStrings; 662 var <bus, busAction; 663 var debugAction; 664 var <>key; 665 666 667 *initClass { 668 slotTypeMap = IdentityDictionary.new.addAll([ 669 0x0001 -> LIDKeySlot, 670 0x0002 -> LIDRelSlot, 671 0x0003 -> LIDAbsSlot, 672 0x0004 -> LIDMscSlot, 673 0x0011 -> LIDLedSlot, 674 ]); 675 slotTypeStrings = IdentityDictionary.new.addAll([ 676 0x0000 -> "Syn", 677 0x0001 -> "Button", 678 0x0002 -> "Relative", 679 0x0003 -> "Absolute", 680 0x0004 -> "Miscellaneous", 681 0x0011 -> "LED", 682 0x0012 -> "Sound", 683 0x0014 -> "Rep", 684 0x0015 -> "Force Feedback", 685 0x0016 -> "Power", 686 0x0017 -> "Force Feedback Status", 687 0x0FFF -> "Linear" 688 ]); 689 690 } 691 692 postInfo { 693 "\tType: \t%, %\n".postf( type, slotTypeStrings.at( type ) ); 694 "\tCode: \t%\n".postf( code ); 695 "\tKey: \t%\n".postf( key ); 696 "\tSpec: \t%\n".postf( spec ); 697 "\tValue: \t%\n".postf( this.value ); 698 } 699 700 *new { | device, evtType, evtCode | 701 ^(slotTypeMap[evtType] ? this).newCopyArgs(device, evtType, evtCode).init.initSpec 702 } 703 704 init{ 705 busAction = {}; 706 debugAction = {}; 707 action = {}; 708 } 709 710 initSpec { 711 spec = ControlSpec(0, 1, \lin, 1, 0); 712 } 713 rawValue { 714 ^rawValue 715 } 716 value { 717 ^spec.unmap(rawValue) 718 } 719 rawValue_ { | inValue | 720 rawValue = inValue; 721 action.value(this); 722 busAction.value( this ); 723 debugAction.value( this ); 724 } 725 next { 726 ^this.value 727 } 728 729 debug_{ |onoff| 730 if ( onoff, { 731 debugAction = { |slot| [ slot.type, slot.code, slot.value, slot.key ].postln; }; 732 }, { 733 debugAction = {}; 734 }); 735 } 736 737 debug{ 738 ^debugAction.notNil; 739 } 740 741 createBus { |server| 742 server = server ? Server.default; 743 if ( bus.isNil, { 744 bus = Bus.control( server, 1 ); 745 }, { 746 if ( bus.index.isNil, { 747 bus = Bus.control( server, 1 ); 748 }); 749 }); 750 busAction = { |v| bus.set( v.value ); }; 751 } 752 753 freeBus { 754 busAction = {}; 755 bus.free; 756 bus = nil; 757 } 758 759 // JITLib support 760 kr { 761 this.createBus; 762 ^In.kr( bus ); 763 } 764} 765 766LIDKeySlot : LIDSlot { 767 initSpec { 768 super.initSpec; 769 rawValue = device.getKeyState(code); 770 } 771} 772 773LIDRelSlot : LIDSlot { 774 var delta = 0, <>deltaAction; 775 776 initSpec { } 777 value { ^rawValue } 778 rawValue_ { | inDelta | 779 delta = inDelta; 780 rawValue = rawValue + delta; 781 action.value(this); 782 busAction.value( this ); 783 debugAction.value( this ); 784 deltaAction.value(this); 785 } 786 787 delta { ^delta } 788 789 debug_{ |onoff| 790 if ( onoff, { 791 debugAction = { |slot| [ slot.type, slot.code, slot.value, slot.delta, slot.key ].postln; }; 792 }, { 793 debugAction = {}; 794 }); 795 } 796 797 798} 799 800LIDLedSlot : LIDSlot { 801 802 initSpec { } 803 value { ^rawValue } 804 value_{ |inValue| this.rawValue_( spec.map( inValue ) ); } 805 rawValue_ { | inValue | 806 rawValue = inValue; 807 device.setLEDState( code, inValue ); 808 action.value(this); 809 busAction.value( this ); 810 debugAction.value( this ); 811 } 812} 813 814LIDMscSlot : LIDSlot { 815 816 initSpec { } 817 value { ^rawValue } 818 value_{ |inValue| this.rawValue_( spec.map( inValue ) ); } 819 rawValue_ { | inValue | 820 rawValue = inValue; 821 device.setMSCState( code, rawValue ); 822 action.value(this); 823 busAction.value( this ); 824 debugAction.value( this ); 825 } 826} 827 828LIDAbsSlot : LIDSlot { 829 var <info; 830 831 initSpec { 832 info = device.getAbsInfo(code); 833 spec = ControlSpec(info.min, info.max, \lin, 1); 834 spec.default = spec.map(0.5).asInteger; 835 rawValue = info.value; 836 } 837} 838 839// EOF 840