Merge pull request #506 from andrewcsmith/patch-2
[supercollider.git] / lang / LangPrimSource / SC_LID.cpp
blobd41e280121b733fe5057f3d2eb8b14c5fb183a64
1 /*
2 Linux Input Device support.
3 Copyright (c) 2004 stefan kersten.
4 modifications by Marije Baalman 2006-9
6 ====================================================================
8 SuperCollider real time audio synthesis system
9 Copyright (c) 2002 James McCartney. All rights reserved.
10 http://www.audiosynth.com
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 2 of the License, or
15 (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program; if not, write to the Free Software
24 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
26 Linux Input Device interface, 2004 <sk>
29 #include "SCBase.h"
30 #include "VMGlobals.h"
31 #include "PyrSymbolTable.h"
32 #include "PyrInterpreter.h"
33 #include "PyrKernel.h"
35 #include "PyrPrimitive.h"
36 #include "PyrObjectProto.h"
37 #include "PyrPrimitiveProto.h"
38 #include "PyrKernelProto.h"
39 #include "SC_InlineUnaryOp.h"
40 #include "SC_InlineBinaryOp.h"
41 #include "PyrSched.h"
42 #include "GC.h"
43 #include <boost/atomic.hpp>
44 #include "SC_LanguageClient.h"
46 #if HAVE_LID
47 #include <errno.h>
48 #include <fcntl.h>
49 #include <linux/input.h>
50 #include <stdio.h>
51 #include <string.h>
52 #include <sys/select.h>
53 #include <sys/types.h>
54 #include <unistd.h>
56 #define BITS_PER_LONG (sizeof(long) * 8)
57 #define NBITS(x) ((((x) - 1) / BITS_PER_LONG) + 1)
58 #define OFF(x) ((x) % BITS_PER_LONG)
59 #define BIT(x) (1UL << OFF(x))
60 #define LONG(x) ((x) / BITS_PER_LONG)
61 #define TEST_BIT(array, bit) (((array)[LONG(bit)] >> OFF(bit)) & 1)
63 extern bool compiledOK;
65 static PyrSymbol* s_inputDeviceClass = 0;
66 static PyrSymbol* s_inputDeviceInfoClass = 0;
67 static PyrSymbol* s_absInfoClass = 0;
68 static PyrSymbol* s_handleEvent = 0;
69 static PyrSymbol* s_readError = 0;
71 // =====================================================================
72 // SC_LID
74 struct SC_LID
76 SC_LID(PyrObject *obj);
77 ~SC_LID();
79 int open(const char *path);
80 int close();
82 bool isEventTypeSupported(int evtType);
83 bool isEventCodeSupported(int evtType, int evtCode);
85 int getName(char* buf, size_t size);
86 int getInfo(struct input_id *info, char *bufPhys, size_t sizePhys, char *bufUniq, size_t sizeUniq);
87 int getKeyState(int evtCode);
88 int getAbsInfo(int evtCode, struct input_absinfo *info);
90 int setLedState( int evtCode, int evtValue, int evtType );
92 int grab(int flag);
93 void handleEvent(struct input_event& evt, boost::atomic<bool> const & shouldBeRunning);
94 void readError(boost::atomic<bool> const & shouldBeRunning);
96 static PyrObject* getObject(PyrSlot* slot)
98 return isKindOfSlot(slot, s_inputDeviceClass->u.classobj) ? slotRawObject(slot) : 0;
101 static SC_LID* getDevice(PyrObject* obj)
103 return (SC_LID*)slotRawPtr(&obj->slots[0]);
106 SC_LID* m_next;
107 PyrObject* m_obj;
108 int m_fd;
109 int m_lastEventType;
110 unsigned long m_eventTypeCaps[NBITS(EV_MAX)];
111 unsigned long m_eventCodeCaps[NBITS(KEY_MAX)];
112 unsigned long m_keyState[NBITS(KEY_MAX)];
115 // =====================================================================
116 // SC_LIDManager
118 struct SC_LIDManager
120 public:
121 static SC_LIDManager& instance();
123 int start();
124 int stop();
126 int add(SC_LID *dev);
127 int remove(SC_LID *dev);
129 private:
130 SC_LIDManager();
131 ~SC_LIDManager();
133 enum
135 kStop,
136 kAdd,
137 kRemove
140 struct Command
142 int id;
143 union
145 SC_LID* dev;
146 } arg;
149 int sendCommand(const Command& cmd);
150 void devicesChanged();
151 bool asyncAddDevice(SC_LID *dev);
152 bool asyncRemoveDevice(SC_LID *dev);
153 void loop();
155 static void* threadFunc(void*);
157 pthread_t m_thread;
158 pthread_mutex_t m_mutex;
159 boost::atomic<bool> m_running;
160 boost::atomic<bool> mShouldBeRunning;
161 int m_cmdFifo[2];
162 int m_nfds;
163 fd_set m_fds;
164 SC_LID* m_devices;
167 // =====================================================================
168 // SC_LID
170 SC_LID::SC_LID(PyrObject* obj)
171 : m_next(0),
172 m_obj(obj),
173 m_fd(-1),
174 m_lastEventType(-1)
176 SetPtr(obj->slots+0, this);
179 SC_LID::~SC_LID()
181 if (m_fd != -1) ::close(m_fd);
184 int SC_LID::open(const char* path)
186 m_fd = ::open(path, O_RDWR);
188 if (m_fd == -1) {
189 error("LID (1): %s\n", strerror(errno));
190 return errFailed;
193 memset(m_eventTypeCaps, 0, sizeof(m_eventTypeCaps));
194 if (ioctl(m_fd, EVIOCGBIT(0, EV_MAX), m_eventTypeCaps) == -1) {
195 error("LID (2): %s\n", strerror(errno));
196 return errFailed;
199 memset(m_keyState, 0, sizeof(m_keyState));
200 if (ioctl(m_fd, EVIOCGKEY(sizeof(m_keyState)), m_keyState) == -1) {
201 error("LID (3): %s\n", strerror(errno));
202 return errFailed;
205 return SC_LIDManager::instance().add(this);
208 int SC_LID::close()
210 SetNil(m_obj->slots+0);
211 return SC_LIDManager::instance().remove(this);
214 bool SC_LID::isEventTypeSupported(int evtType)
216 return TEST_BIT(m_eventTypeCaps, evtType);
219 bool SC_LID::isEventCodeSupported(int evtType, int evtCode)
221 if (evtType != m_lastEventType) {
222 m_lastEventType = evtType;
223 memset(m_eventCodeCaps, 0, sizeof(m_eventCodeCaps));
224 if (ioctl(m_fd, EVIOCGBIT(evtType, KEY_MAX), m_eventCodeCaps) == -1) {
225 post("LID failed to check event code (error %s)\n", strerror(errno));
226 return false;
229 return TEST_BIT(m_eventCodeCaps, evtCode);
232 int SC_LID::getName(char* buf, size_t size)
234 if (ioctl(m_fd, EVIOCGNAME(size), buf) == -1) {
235 error("LID (5): %s\n", strerror(errno));
236 return errFailed;
238 return errNone;
241 int SC_LID::getInfo(struct input_id *info, char *bufPhys, size_t sizePhys, char *bufUniq, size_t sizeUniq)
243 if (ioctl(m_fd, EVIOCGID, info) == -1) {
244 error("LID (6): %s\n", strerror(errno));
245 return errFailed;
247 if (ioctl(m_fd, EVIOCGPHYS(sizePhys), bufPhys) == -1) {
248 // strcpy( sizePhys, strerror(errno));
249 post("LID could not retrieve physical location (error: %s)\n", strerror(errno));
250 // return errFailed;
252 if (ioctl(m_fd, EVIOCGUNIQ(sizeUniq), bufUniq) == -1) {
253 // strcpy( strerror(errno), sizeof( strerror(errno)), sizeUniq );
254 post("LID could not get unique identifier (error: %s)\n", strerror(errno));
255 // return errFailed;
258 return errNone;
261 int SC_LID::getKeyState(int evtCode)
263 return TEST_BIT(m_keyState, evtCode);
266 int SC_LID::getAbsInfo(int evtCode, struct input_absinfo* info)
268 if (ioctl(m_fd, EVIOCGABS(evtCode), info) == -1) {
269 error("LID (9): %s\n", strerror(errno));
270 return errFailed;
272 return errNone;
275 int SC_LID::setLedState( int evtCode, int evtValue, int evtType )
276 { // added by marije baalman
277 struct input_event ev;
278 // post( "set led state called, putting event" );
279 ev.code = evtCode;
280 ev.value = evtValue;
281 ev.type = evtType;
282 // post( "m_fd %i", m_fd );
283 // post( "code %i, value %i ", evtCode, evtValue );
284 if ( write(m_fd, &ev, sizeof(struct input_event)) == -1 )
286 // post( "error writing event" );
287 return errFailed;
289 return errNone;
292 int SC_LID::grab(int flag)
294 if (ioctl(m_fd, EVIOCGRAB, flag) == -1) {
295 error("LID (10): %s\n", strerror(errno));
296 return errFailed;
298 return errNone;
301 void SC_LID::handleEvent(struct input_event& evt, boost::atomic<bool> const & shouldBeRunning)
303 if (evt.type != EV_SYN) {
304 int status = lockLanguageOrQuit(shouldBeRunning);
305 if (status == EINTR)
306 return;
307 if (status) {
308 postfl("error when locking language (%d)\n", status);
309 return;
312 if (compiledOK) {
313 VMGlobals* g = gMainVMGlobals;
314 g->canCallOS = false;
315 ++g->sp; SetObject(g->sp, m_obj);
316 ++g->sp; SetInt(g->sp, evt.type);
317 ++g->sp; SetInt(g->sp, evt.code);
318 ++g->sp; SetInt(g->sp, evt.value);
319 runInterpreter(g, s_handleEvent, 4);
320 g->canCallOS = false;
322 pthread_mutex_unlock(&gLangMutex);
326 void SC_LID::readError(boost::atomic<bool> const & shouldBeRunning)
328 int status = lockLanguageOrQuit(shouldBeRunning);
329 if (status == EINTR)
330 return;
331 if (status) {
332 postfl("error when locking language (%d)\n", status);
333 return;
336 if (compiledOK) {
337 VMGlobals* g = gMainVMGlobals;
338 g->canCallOS = false;
339 ++g->sp; SetObject(g->sp, m_obj);
340 runInterpreter(g, s_readError, 1);
341 g->canCallOS = false;
343 pthread_mutex_unlock(&gLangMutex);
346 // =====================================================================
347 // SC_LIDManager
349 SC_LIDManager& SC_LIDManager::instance()
351 static SC_LIDManager instance;
352 return instance;
355 SC_LIDManager::SC_LIDManager()
356 : m_running(false),
357 m_devices(0)
359 if (pipe(m_cmdFifo) == -1) {
360 m_cmdFifo[0] = m_cmdFifo[1] = -1;
362 devicesChanged();
365 SC_LIDManager::~SC_LIDManager()
367 close(m_cmdFifo[0]);
368 close(m_cmdFifo[1]);
371 int SC_LIDManager::start()
373 mShouldBeRunning = true;
374 int err = pthread_create(&m_thread, 0, &threadFunc, this);
375 if (err != 0) return errFailed;
376 return errNone;
379 int SC_LIDManager::stop()
381 if (m_running == false)
382 return errNone;
384 Command cmd;
386 cmd.id = kStop;
387 int err = sendCommand(cmd);
388 if (err) return err;
390 mShouldBeRunning = false;
391 err = pthread_join(m_thread, 0);
392 if (err != 0) return errFailed;
394 return errNone;
397 int SC_LIDManager::add(SC_LID* dev)
399 Command cmd;
400 cmd.id = kAdd;
401 cmd.arg.dev = dev;
402 return sendCommand(cmd);
405 int SC_LIDManager::remove(SC_LID* dev)
407 Command cmd;
408 cmd.id = kRemove;
409 cmd.arg.dev = dev;
410 return sendCommand(cmd);
413 int SC_LIDManager::sendCommand(const Command& cmd)
415 return write(m_cmdFifo[1], &cmd, sizeof(cmd)) == sizeof(cmd) ? errNone : errFailed;
418 void SC_LIDManager::devicesChanged()
420 int fdMax = m_cmdFifo[0];
422 FD_ZERO(&m_fds);
423 FD_SET(fdMax, &m_fds);
425 SC_LID *dev = m_devices;
426 while (dev) {
427 int fd = dev->m_fd;
428 if (fd != -1) {
429 FD_SET(fd, &m_fds);
430 if (fd > fdMax) fdMax = fd;
432 dev = dev->m_next;
435 m_nfds = fdMax + 1;
438 bool SC_LIDManager::asyncAddDevice(SC_LID* dev)
440 if (dev->m_next) return false;
441 dev->m_next = m_devices;
442 m_devices = dev;
443 devicesChanged();
444 return true;
447 bool SC_LIDManager::asyncRemoveDevice(SC_LID* dev)
449 SC_LID *prev = 0, *cur = m_devices;
451 while (cur) {
452 if (cur == dev) {
453 if (prev) prev->m_next = dev->m_next;
454 else m_devices = dev->m_next;
455 dev->m_next = 0;
456 delete dev;
457 devicesChanged();
458 return true;
460 prev = cur;
461 cur = cur->m_next;
464 return false;
467 void* SC_LIDManager::threadFunc(void* arg)
469 ((SC_LIDManager*)arg)->loop();
470 return 0;
473 void SC_LIDManager::loop()
475 m_running = true;
476 post("LID: event loop started\n");
478 while (true) {
479 fd_set fds;
480 memcpy(&fds, &m_fds, sizeof(fd_set));
481 int n = select(m_nfds, &fds, 0, 0, 0);
482 if (n == -1) {
483 if( errno == EINTR ) continue;
484 post("LID: error in input handler: %s\n", strerror(errno));
485 goto quit;
486 } else if (n > 0) {
487 if (FD_ISSET(m_cmdFifo[0], &fds)) {
488 Command cmd;
489 --n;
490 int err = read(m_cmdFifo[0], &cmd, sizeof(cmd));
491 if (err == -1) {
492 if( errno != EINTR ) {
493 post("LID: error in input handler: %s\n", strerror(errno));
494 goto quit;
497 else {
498 switch (cmd.id) {
499 case kStop:
500 goto quit;
501 case kAdd:
502 if (asyncAddDevice(cmd.arg.dev)) {
503 post("LID: added device %p\n", cmd.arg);
504 } else {
505 post("LID: cannot add device\n");
507 break;
508 case kRemove:
509 if (asyncRemoveDevice(cmd.arg.dev)) {
510 post("LID: removed device %p\n", cmd.arg);
511 } else {
512 post("LID: couldn't remove device\n");
514 break;
515 default:
516 post("LID: unknown command in input handler\n");
520 if (n > 0) {
521 SC_LID *dev = m_devices;
522 while (dev) {
523 int fd = dev->m_fd;
524 if (FD_ISSET(fd, &fds)) {
525 struct input_event evt;
526 if (read(fd, &evt, sizeof(evt)) == sizeof(evt)) {
527 dev->handleEvent(evt, mShouldBeRunning);
529 else {
530 dev->readError(mShouldBeRunning);
533 if (!mShouldBeRunning)
534 goto quit;
535 dev = dev->m_next;
541 quit:
542 m_running = false;
543 post("LID: event loop stopped\n");
546 // =====================================================================
547 // Primitive Interface
549 int prLID_Open(VMGlobals *g, int numArgsPushed)
551 PyrSlot* args = g->sp - 1;
552 int err;
554 PyrObject* obj = SC_LID::getObject(args+0);
555 if (!obj) return errWrongType;
557 char path[PATH_MAX];
558 err = slotStrVal(args+1, path, sizeof(path));
559 if (err) return err;
561 SC_LID* dev = new SC_LID(obj);
562 err = dev->open(path);
563 if (err) {
564 delete dev;
565 return err;
568 return errNone;
571 int prLID_Close(VMGlobals *g, int numArgsPushed)
573 PyrSlot* args = g->sp;
575 PyrObject* obj = SC_LID::getObject(args+0);
576 if (!obj) return errWrongType;
578 SC_LID* dev = SC_LID::getDevice(obj);
579 if (!dev) return errFailed;
581 return dev->close();
584 int prLID_EventTypeSupported(VMGlobals *g, int numArgsPushed)
586 PyrSlot* args = g->sp - 1;
587 int evtType;
588 int err;
590 if (!g->canCallOS) return errCantCallOS;
592 PyrObject* obj = SC_LID::getObject(args+0);
593 if (!obj) return errWrongType;
595 err = slotIntVal(args+1, &evtType);
596 if (err) return err;
598 SC_LID* dev = SC_LID::getDevice(obj);
599 if (!dev) return errFailed;
601 SetBool(args, dev->isEventTypeSupported(evtType));
603 return errNone;
606 int prLID_EventCodeSupported(VMGlobals *g, int numArgsPushed)
608 PyrSlot* args = g->sp - 2;
609 int evtType, evtCode;
610 int err;
612 if (!g->canCallOS) return errCantCallOS;
614 PyrObject* obj = SC_LID::getObject(args+0);
615 if (!obj) return errWrongType;
617 err = slotIntVal(args+1, &evtType);
618 if (err) return err;
620 err = slotIntVal(args+2, &evtCode);
621 if (err) return err;
623 SC_LID* dev = SC_LID::getDevice(obj);
624 if (!dev) return errFailed;
626 SetBool(args, dev->isEventCodeSupported(evtType, evtCode));
628 return errNone;
631 int prLID_GetInfo(VMGlobals* g, int numArgsPushed)
633 PyrSlot* args = g->sp - 1;
634 int err;
636 PyrObject* obj = SC_LID::getObject(args+0);
637 if (!obj) return errWrongType;
639 if (!isKindOfSlot(args+1, s_inputDeviceInfoClass->u.classobj))
640 return errWrongType;
641 PyrObject* infoObj = slotRawObject(&args[1]);
643 SC_LID* dev = SC_LID::getDevice(obj);
644 if (!dev) return errFailed;
646 char name[128];
647 err = dev->getName(name, sizeof(name));
648 if (err) return err;
650 struct input_id info;
651 char namePhys[128];
652 char nameUniq[128];
653 err = dev->getInfo(&info, namePhys, sizeof( namePhys ), nameUniq, sizeof( nameUniq ) );
654 if (err) return err;
656 SetSymbol(infoObj->slots+0, getsym(name));
657 SetInt(infoObj->slots+1, info.bustype);
658 SetInt(infoObj->slots+2, info.vendor);
659 SetInt(infoObj->slots+3, info.product);
660 SetInt(infoObj->slots+4, info.version);
661 SetSymbol(infoObj->slots+5, getsym(namePhys));
662 SetSymbol(infoObj->slots+6, getsym(nameUniq));
664 slotCopy(&args[0], &args[1]);
666 return errNone;
669 int prLID_GetKeyState(VMGlobals *g, int numArgsPushed)
671 PyrSlot* args = g->sp - 1;
672 int evtCode;
673 int err;
675 PyrObject* obj = SC_LID::getObject(args+0);
676 if (!obj) return errWrongType;
678 err = slotIntVal(args+1, &evtCode);
679 if (err) return err;
681 SC_LID* dev = SC_LID::getDevice(obj);
682 if (!dev) return errFailed;
684 SetInt(args, dev->getKeyState(evtCode));
686 return errNone;
689 int prLID_GetAbsInfo(VMGlobals *g, int numArgsPushed)
691 PyrSlot* args = g->sp - 2;
692 int evtCode;
693 int err;
695 PyrObject* obj = SC_LID::getObject(args+0);
696 if (!obj) return errWrongType;
698 err = slotIntVal(args+1, &evtCode);
699 if (err) return err;
701 if (!isKindOfSlot(args+2, s_absInfoClass->u.classobj))
702 return errWrongType;
703 PyrObject* infoObj = slotRawObject(&args[2]);
705 SC_LID* dev = SC_LID::getDevice(obj);
706 if (!dev) return errFailed;
708 struct input_absinfo info;
709 err = dev->getAbsInfo(evtCode, &info);
710 if (err) return err;
712 SetInt(infoObj->slots+0, info.value);
713 SetInt(infoObj->slots+1, info.minimum);
714 SetInt(infoObj->slots+2, info.maximum);
715 SetInt(infoObj->slots+3, info.fuzz);
716 SetInt(infoObj->slots+4, info.flat);
718 slotCopy(&args[0], &args[2]);
720 return errNone;
723 int prLID_Grab(VMGlobals *g, int numArgsPushed)
725 PyrSlot* args = g->sp - 1;
727 PyrObject* obj = SC_LID::getObject(args+0);
728 if (!obj) return errWrongType;
730 SC_LID* dev = SC_LID::getDevice(obj);
731 if (!dev) return errFailed;
733 return dev->grab(IsTrue(args+1));
736 int prLID_Start(VMGlobals* g, int numArgsPushed)
738 // if (!g->canCallOS) return errCantCallOS;
739 return SC_LIDManager::instance().start();
742 int prLID_Stop(VMGlobals* g, int numArgsPushed)
744 // if (!g->canCallOS) return errCantCallOS;
745 return SC_LIDManager::instance().stop();
748 int prLID_SetLedState(VMGlobals *g, int numArgsPushed)
750 // post( "set led state primitive called" );
751 PyrSlot* args = g->sp - 2;
752 int evtCode, evtValue;
753 int err;
755 PyrObject* obj = SC_LID::getObject(args+0);
756 if (!obj) return errWrongType;
758 err = slotIntVal(args+1, &evtCode);
759 if (err) return err;
761 err = slotIntVal(args+2, &evtValue);
762 if (err) return err;
764 SC_LID* dev = SC_LID::getDevice(obj);
765 if (!dev) return errFailed;
767 SetInt(args, dev->setLedState(evtCode,evtValue, EV_LED));
768 return errNone;
771 int prLID_SetMscState(VMGlobals *g, int numArgsPushed)
773 // post( "set msc state primitive called\n" );
774 PyrSlot* args = g->sp - 2;
775 int evtCode, evtValue;
776 int err;
778 PyrObject* obj = SC_LID::getObject(args+0);
779 if (!obj) return errWrongType;
781 err = slotIntVal(args+1, &evtCode);
782 if (err) return err;
784 err = slotIntVal(args+2, &evtValue);
785 if (err) return err;
787 SC_LID* dev = SC_LID::getDevice(obj);
788 if (!dev) return errFailed;
790 SetInt(args, dev->setLedState(evtCode,evtValue, EV_MSC));
791 return errNone;
794 void SC_LIDInit()
796 int base, index;
798 s_inputDeviceClass = getsym("LID");
799 s_inputDeviceInfoClass = getsym("LIDInfo");
800 s_absInfoClass = getsym("LIDAbsInfo");
801 s_handleEvent = getsym("prHandleEvent");
802 s_readError = getsym("prReadError");
804 base = nextPrimitiveIndex();
805 index = 0;
807 definePrimitive(base, index++, "_LID_Open", prLID_Open, 2, 0);
808 definePrimitive(base, index++, "_LID_Close", prLID_Close, 1, 0);
809 definePrimitive(base, index++, "_LID_EventTypeSupported", prLID_EventTypeSupported, 2, 0);
810 definePrimitive(base, index++, "_LID_EventCodeSupported", prLID_EventCodeSupported, 3, 0);
811 definePrimitive(base, index++, "_LID_GetInfo", prLID_GetInfo, 2, 0);
812 definePrimitive(base, index++, "_LID_GetKeyState", prLID_GetKeyState, 2, 0);
813 definePrimitive(base, index++, "_LID_GetAbsInfo", prLID_GetAbsInfo, 3, 0);
814 definePrimitive(base, index++, "_LID_Grab", prLID_Grab, 2, 0);
815 definePrimitive(base, index++, "_LID_Start", prLID_Start, 1, 0);
816 definePrimitive(base, index++, "_LID_Stop", prLID_Stop, 1, 0);
817 definePrimitive(base, index++, "_LID_SetLedState", prLID_SetLedState, 3, 0); // added by Marije Baalman
818 definePrimitive(base, index++, "_LID_SetMscState", prLID_SetMscState, 3, 0);
820 #else // !HAVE_LID
821 int prLID_Start(VMGlobals* g, int numArgsPushed)
823 return errNone;
826 int prLID_Stop(VMGlobals* g, int numArgsPushed)
828 return errNone;
831 void SC_LIDInit()
833 int base, index;
835 base = nextPrimitiveIndex();
836 index = 0;
838 definePrimitive(base, index++, "_LID_Start", prLID_Start, 1, 0);
839 definePrimitive(base, index++, "_LID_Stop", prLID_Stop, 1, 0);
841 #endif // HAVE_LID
843 void initHIDPrimitives()
845 SC_LIDInit();
848 // EOF