1 /*
2     HIDAPI based HID support.
3     Copyright (c) 2013 Marije Baalman, Tim Blechmann
4 
5     ====================================================================
6 
7     SuperCollider real time audio synthesis system
8     Copyright (c) 2002 James McCartney. All rights reserved.
9     http://www.audiosynth.com
10 
11     This program is free software; you can redistribute it and/or modify
12     it under the terms of the GNU General Public License as published by
13     the Free Software Foundation; either version 2 of the License, or
14     (at your option) any later version.
15 
16     This program is distributed in the hope that it will be useful,
17     but WITHOUT ANY WARRANTY; without even the implied warranty of
18     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19     GNU General Public License for more details.
20 
21     You should have received a copy of the GNU General Public License
22     along with this program; if not, write to the Free Software
23     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
24 
25 */
26 
27 #include "SCBase.h"
28 #include "VMGlobals.h"
29 #include "PyrSymbolTable.h"
30 #include "PyrInterpreter.h"
31 #include "PyrKernel.h"
32 
33 #include "PyrPrimitive.h"
34 #include "PyrObjectProto.h"
35 #include "PyrPrimitiveProto.h"
36 #include "PyrKernelProto.h"
37 #include "SC_InlineUnaryOp.h"
38 #include "SC_InlineBinaryOp.h"
39 #include "PyrSched.h"
40 #include "GC.h"
41 
42 #include <atomic>
43 #include <cstring>
44 
45 #include "SC_LanguageClient.h"
46 
47 extern bool compiledOK;
48 
49 #ifdef HAVE_HIDAPI
50 
51 #    if 1
trace(const char * fmt,...)52 static inline void trace(const char* fmt, ...) {
53     va_list ap;
54     va_start(ap, fmt);
55     vpost(fmt, ap);
56     va_end(ap);
57 }
58 #    else
trace(...)59 static inline void trace(...) {}
60 
61 #    endif
62 
63 
64 // needed for string conversions
65 #    include <wchar.h>
66 #    include <cstdlib>
67 
68 #    include <hidapi.h>
69 #    include <hidapi_parser.h>
70 
71 #    include <map>
72 
73 typedef std::map<int, hid_dev_desc*> hid_map_t;
74 
75 // helper function to convert from unicode to ansi
wchar_to_char(wchar_t * wchs)76 char* wchar_to_char(wchar_t* wchs) {
77     if (wchs == nullptr)
78         return nullptr;
79 
80     int len = wcslen(wchs) + 1;
81     char* chs = (char*)malloc(sizeof(char) * len);
82     std::wcstombs(chs, wchs, len);
83     return chs;
84 }
85 
char_to_wchar(char * chs)86 wchar_t* char_to_wchar(char* chs) {
87     if (chs == nullptr)
88         return nullptr;
89 
90     int len = std::strlen(chs) + 1;
91     wchar_t* wchs = (wchar_t*)malloc(sizeof(wchar_t) * len);
92     std::mbstowcs(wchs, chs, len);
93     return wchs;
94 }
95 
96 static PyrSymbol* s_hidapi = nullptr;
97 static PyrSymbol* s_hidElementData = nullptr;
98 static PyrSymbol* s_hidDeviceData = nullptr;
99 static PyrSymbol* s_hidClosed = nullptr;
100 
101 
102 class SC_HID_API_Threadpool {
103     typedef std::map<hid_dev_desc*, std::thread> ThreadMap;
104 
105 public:
openDevice(hid_dev_desc * desc,std::atomic<bool> & shouldBeRunning)106     void openDevice(hid_dev_desc* desc, std::atomic<bool>& shouldBeRunning) {
107         trace("open device %p\n");
108         std::lock_guard<std::mutex> lock(guard);
109         if (map.find(desc) != map.end())
110             // thread already polling device
111             return;
112 
113         std::thread deviceThread([=, &shouldBeRunning] {
114             trace("start polling thread for %d\n", desc);
115 
116             while (true) {
117                 unsigned char buf[256];
118 
119                 int res = hid_read_timeout(desc->device, buf, sizeof(buf), 250);
120                 if (res > 0) {
121                     hid_parse_input_report(buf, res, desc);
122                 } else if (res == -1) {
123                     trace("device thread interrupted \n");
124                     hid_throw_readerror(desc);
125                     trace("device thread closed device \n");
126                     return;
127                 }
128             }
129             std::lock_guard<std::mutex> lock_(guard);
130             auto it = map.find(desc);
131             std::thread thisThread = std::move(it->second);
132             map.erase(it);
133             thisThread.detach();
134         });
135 
136         map.emplace(desc, std::move(deviceThread));
137     }
138 
closeDevice(hid_dev_desc * desc)139     void closeDevice(hid_dev_desc* desc) {
140         std::thread thread;
141         {
142             std::lock_guard<std::mutex> lock(guard);
143             auto it = map.find(desc);
144             if (it == map.end()) {
145                 std::printf("device already closed %p\n", desc->device);
146                 return;
147             }
148             thread = std::move(it->second);
149         }
150 
151         thread.detach();
152         hid_close_device(desc);
153         trace("close device: interrupted \n");
154     }
155 
156 private:
157     ThreadMap map;
158     std::mutex guard;
159 };
160 
161 struct SC_HID_APIManager {
162 public:
163     static SC_HID_APIManager& instance();
164 
165     int init();
166     int closeAll();
167 
168     int build_devicelist();
169     int free_devicelist();
170 
171     int open_device_path(const char* path, int vendor, int product);
172     int open_device(int vendor, int product, char* serial_number = nullptr);
173     int close_device(int joy_idx);
174     void close_all_devices();
175 
176     struct hid_dev_desc* get_device(int joy_idx);
177 
178     void setPyrObject(PyrObject* obj);
179 
180     SC_HID_APIManager();
181     ~SC_HID_APIManager();
182 
183     int initialize_hidapi();
184 
185     void elementData(int, struct hid_device_element*);
186     void deviceData(int, struct hid_dev_desc*);
187     void deviceRepetitiveReadError(int, struct hid_dev_desc*);
188 
189     struct hid_device_info* devinfos;
190 
191 protected:
192     void handleDevice(int, struct hid_dev_desc*, std::atomic<bool> const& shouldBeRunning);
193     void handleElement(int, struct hid_device_element*, std::atomic<bool> const& shouldBeRunning);
194     void deviceClosed(int, struct hid_dev_desc*, std::atomic<bool> const& shouldBeRunning);
195 
196 private:
197     hid_map_t hiddevices; // declares a vector of hiddevices
198     int number_of_hids;
199 
200     // language interface
201     PyrObject* m_obj;
202 
203     std::atomic<bool> m_running;
204     std::atomic<bool> mShouldBeRunning;
205     SC_HID_API_Threadpool mThreads;
206 };
207 
208 
hid_element_cb(struct hid_device_element * el,void * data)209 static void hid_element_cb(struct hid_device_element* el, void* data) {
210     SC_HID_APIManager::instance().elementData(*((int*)data), el);
211 }
212 
hid_descriptor_cb(struct hid_dev_desc * dd,void * data)213 static void hid_descriptor_cb(struct hid_dev_desc* dd, void* data) {
214     SC_HID_APIManager::instance().deviceData(*((int*)data), dd);
215 }
216 
hid_readerror_cb(hid_dev_desc * dd,void * data)217 void hid_readerror_cb(hid_dev_desc* dd, void* data) {
218     SC_HID_APIManager::instance().deviceRepetitiveReadError(*((int*)data), dd);
219 }
220 
221 
deviceData(int id,struct hid_dev_desc * dd)222 void SC_HID_APIManager::deviceData(int id, struct hid_dev_desc* dd) { handleDevice(id, dd, mShouldBeRunning); }
223 
elementData(int id,struct hid_device_element * ele)224 void SC_HID_APIManager::elementData(int id, struct hid_device_element* ele) {
225     handleElement(id, ele, mShouldBeRunning);
226 }
227 
deviceRepetitiveReadError(int id,struct hid_dev_desc * dd)228 void SC_HID_APIManager::deviceRepetitiveReadError(int id, struct hid_dev_desc* dd) {
229     deviceClosed(id, dd, mShouldBeRunning);
230     // 	hiddevices.erase( id );
231 }
232 
233 
setPyrObject(PyrObject * obj)234 void SC_HID_APIManager::setPyrObject(PyrObject* obj) { m_obj = obj; }
235 
close_all_devices()236 void SC_HID_APIManager::close_all_devices() {
237     for (auto elem : hiddevices)
238         mThreads.closeDevice(elem.second);
239 
240     hiddevices.clear();
241 }
242 
open_device_path(const char * path,int vendor,int product)243 int SC_HID_APIManager::open_device_path(const char* path, int vendor, int product) { //
244     struct hid_dev_desc* newdevdesc;
245     newdevdesc = hid_open_device_path(path, vendor, product);
246     if (!newdevdesc) {
247         post("HIDAPI : Unable to open device %s: %d, %d\n", path, vendor, product);
248         return -1;
249     } else {
250         hiddevices.insert(std::pair<int, hid_dev_desc*>(number_of_hids, newdevdesc));
251         newdevdesc->index = number_of_hids;
252 
253         hid_set_descriptor_callback(newdevdesc, (hid_descriptor_callback)hid_descriptor_cb, &newdevdesc->index);
254         hid_set_readerror_callback(newdevdesc, (hid_device_readerror_callback)hid_readerror_cb, &newdevdesc->index);
255         hid_set_element_callback(newdevdesc, (hid_element_callback)hid_element_cb, &newdevdesc->index);
256 
257         number_of_hids++;
258 
259         mThreads.openDevice(newdevdesc, mShouldBeRunning);
260 
261         return newdevdesc->index;
262     }
263 }
264 
open_device(int vendor,int product,char * serial_number)265 int SC_HID_APIManager::open_device(int vendor, int product, char* serial_number) { //
266     struct hid_dev_desc* newdevdesc;
267     if (serial_number != nullptr) {
268         wchar_t* serialW = char_to_wchar(serial_number);
269         newdevdesc = hid_open_device(vendor, product, serialW);
270         free(serialW);
271     } else {
272         newdevdesc = hid_open_device(vendor, product, nullptr);
273     }
274     if (!newdevdesc) {
275         post("HIDAPI: Unable to open device %d, %d %s\n", vendor, product, serial_number);
276         return -1;
277     } else {
278         hiddevices.insert(std::pair<int, hid_dev_desc*>(number_of_hids, newdevdesc));
279         newdevdesc->index = number_of_hids;
280 
281         hid_set_descriptor_callback(newdevdesc, (hid_descriptor_callback)hid_descriptor_cb, &newdevdesc->index);
282         hid_set_readerror_callback(newdevdesc, (hid_device_readerror_callback)hid_readerror_cb, &newdevdesc->index);
283         hid_set_element_callback(newdevdesc, (hid_element_callback)hid_element_cb, &newdevdesc->index);
284 
285         number_of_hids++;
286 
287         mThreads.openDevice(newdevdesc, mShouldBeRunning);
288 
289         return newdevdesc->index;
290     }
291 }
292 
close_device(int joy_idx)293 int SC_HID_APIManager::close_device(int joy_idx) {
294     struct hid_dev_desc* hidtoclose = get_device(joy_idx);
295     if (hidtoclose == nullptr) {
296         post("HIDAPI: could not find device to close %d\n", joy_idx);
297         return 1; // not a fatal error
298     } else {
299         mThreads.closeDevice(hidtoclose);
300         hiddevices.erase(joy_idx);
301     }
302 
303     return 1;
304 }
305 
get_device(int joy_idx)306 struct hid_dev_desc* SC_HID_APIManager::get_device(int joy_idx) {
307     auto iterator = hiddevices.find(joy_idx);
308     if (iterator == hiddevices.end()) {
309         post("HIDAPI : device was not open %d\n", joy_idx);
310         return nullptr;
311     }
312 
313     return hiddevices.find(joy_idx)->second;
314 }
315 
instance()316 SC_HID_APIManager& SC_HID_APIManager::instance() {
317     static SC_HID_APIManager instance;
318     return instance;
319 }
320 
SC_HID_APIManager()321 SC_HID_APIManager::SC_HID_APIManager(): m_running(false) { number_of_hids = 0; }
322 
~SC_HID_APIManager()323 SC_HID_APIManager::~SC_HID_APIManager() { close_all_devices(); }
324 
init()325 int SC_HID_APIManager::init() {
326     number_of_hids = 0;
327     mShouldBeRunning = true;
328     if (!m_running)
329         initialize_hidapi();
330 
331     return m_running ? errNone : errFailed;
332 }
333 
closeAll()334 int SC_HID_APIManager::closeAll() {
335     m_running = false;
336     mShouldBeRunning = false;
337     close_all_devices();
338     return errNone;
339 }
340 
initialize_hidapi()341 int SC_HID_APIManager::initialize_hidapi() {
342     m_running = false;
343 
344     if (hid_init()) {
345         post("Unable to initialize hidapi\n");
346         return errFailed;
347     }
348 
349     m_running = true;
350     return errNone;
351 }
352 
build_devicelist()353 int SC_HID_APIManager::build_devicelist() {
354     struct hid_device_info* cur_dev;
355     devinfos = hid_enumerate(0x0, 0x0);
356 
357     cur_dev = devinfos;
358     int count = 0;
359     while (cur_dev) {
360         count++;
361         cur_dev = cur_dev->next;
362     }
363     return count;
364 }
365 
free_devicelist()366 int SC_HID_APIManager::free_devicelist() {
367     hid_free_enumeration(devinfos);
368     devinfos = nullptr;
369     return errNone;
370 }
371 
deviceClosed(int joy_idx,struct hid_dev_desc * dd,std::atomic<bool> const & shouldBeRunning)372 void SC_HID_APIManager::deviceClosed(int joy_idx, struct hid_dev_desc* dd, std::atomic<bool> const& shouldBeRunning) {
373     int status = lockLanguageOrQuit(shouldBeRunning);
374     if (status == EINTR)
375         return;
376     if (status) {
377         trace("error when locking language (%d)\n", status);
378         return;
379     }
380     if (compiledOK) {
381         VMGlobals* g = gMainVMGlobals;
382         g->canCallOS = false;
383         ++g->sp;
384         SetObject(g->sp, s_hidapi->u.classobj); // set the class HID_API
385         ++g->sp;
386         SetInt(g->sp, joy_idx);
387         runInterpreter(g, s_hidClosed, 2);
388         g->canCallOS = false;
389     }
390     gLangMutex.unlock();
391 }
392 
handleElement(int joy_idx,struct hid_device_element * ele,std::atomic<bool> const & shouldBeRunning)393 void SC_HID_APIManager::handleElement(int joy_idx, struct hid_device_element* ele,
394                                       std::atomic<bool> const& shouldBeRunning) {
395     int status = lockLanguageOrQuit(shouldBeRunning);
396     if (status == EINTR)
397         return;
398     if (status) {
399         postfl("error when locking language (%d)\n", status);
400         return;
401     }
402 
403     if (compiledOK) {
404         VMGlobals* g = gMainVMGlobals;
405         g->canCallOS = false;
406         ++g->sp;
407         SetObject(g->sp, s_hidapi->u.classobj); // set the class HID_API
408         ++g->sp;
409         SetInt(g->sp, joy_idx);
410         ++g->sp;
411         SetInt(g->sp, ele->index);
412         ++g->sp;
413         SetInt(g->sp, ele->usage_page);
414         ++g->sp;
415         SetInt(g->sp, ele->usage);
416         ++g->sp;
417         SetInt(g->sp, ele->value);
418         ++g->sp;
419         SetFloat(g->sp, hid_element_map_logical(ele));
420         ++g->sp;
421         SetFloat(g->sp, hid_element_map_physical(ele));
422         ++g->sp;
423         SetInt(g->sp, ele->array_value);
424         runInterpreter(g, s_hidElementData, 9);
425         g->canCallOS = false;
426     }
427     gLangMutex.unlock();
428 }
429 
handleDevice(int joy_idx,struct hid_dev_desc * devd,std::atomic<bool> const & shouldBeRunning)430 void SC_HID_APIManager::handleDevice(int joy_idx, struct hid_dev_desc* devd, std::atomic<bool> const& shouldBeRunning) {
431     int status = lockLanguageOrQuit(shouldBeRunning);
432     if (status == EINTR)
433         return;
434     if (status) {
435         postfl("error when locking language (%d)\n", status);
436         return;
437     }
438 
439     if (compiledOK) {
440         VMGlobals* g = gMainVMGlobals;
441         g->canCallOS = false;
442         ++g->sp;
443         SetObject(g->sp, s_hidapi->u.classobj); // set the class HID_API
444         ++g->sp;
445         SetInt(g->sp, joy_idx);
446         ++g->sp;
447         SetInt(g->sp, devd->device_collection->num_elements);
448         runInterpreter(g, s_hidDeviceData, 3);
449         g->canCallOS = false;
450     }
451     gLangMutex.unlock();
452 }
453 
454 // ----------  primitive calls: ---------------
455 
prHID_API_Initialize(VMGlobals * g,int numArgsPushed)456 int prHID_API_Initialize(VMGlobals* g, int numArgsPushed) {
457     PyrSlot* args = g->sp - numArgsPushed + 1;
458     PyrSlot* self = args + 0;
459 
460     SC_HID_APIManager::instance().setPyrObject(slotRawObject(self));
461     // initialize HID_APIManager
462     return SC_HID_APIManager::instance().init();
463 }
464 
prHID_API_CloseAll(VMGlobals * g,int numArgsPushed)465 int prHID_API_CloseAll(VMGlobals* g, int numArgsPushed) {
466     // close all devices, and cleanup manager
467     return SC_HID_APIManager::instance().closeAll();
468 }
469 
prHID_API_BuildDeviceList(VMGlobals * g,int numArgsPushed)470 int prHID_API_BuildDeviceList(VMGlobals* g, int numArgsPushed) {
471     PyrSlot* args = g->sp - numArgsPushed + 1;
472     PyrSlot* self = args + 0;
473     // no arguments
474 
475     const char emptyString[] = "";
476 
477     // iterate over available devices and return info to language to populate the list there
478     int result = SC_HID_APIManager::instance().build_devicelist();
479     if (result > 0) {
480         PyrObject* allDevsArray = newPyrArray(g->gc, result * sizeof(PyrObject), 0, true);
481         SetObject(self, allDevsArray); // this is okay here as we don't use the receiver
482 
483         struct hid_device_info* cur_dev = SC_HID_APIManager::instance().devinfos;
484         while (cur_dev) {
485             PyrObject* devInfo = newPyrArray(g->gc, 11 * sizeof(PyrObject), 0, true);
486             SetObject(allDevsArray->slots + allDevsArray->size++, devInfo);
487             g->gc->GCWriteNew(allDevsArray, devInfo); // we know devInfo is white so we can use GCWriteNew
488 
489             SetInt(devInfo->slots + devInfo->size++, cur_dev->vendor_id);
490             SetInt(devInfo->slots + devInfo->size++, cur_dev->product_id);
491 
492             PyrString* dev_path_name = newPyrString(g->gc, cur_dev->path, 0, true);
493             SetObject(devInfo->slots + devInfo->size++, dev_path_name);
494             g->gc->GCWriteNew(devInfo, dev_path_name); // we know dev_path_name is white so we can use GCWriteNew
495 
496             const char* mystring;
497             if (cur_dev->serial_number != nullptr)
498                 mystring = wchar_to_char(cur_dev->serial_number);
499             else
500                 mystring = emptyString;
501 
502             PyrString* dev_serial = newPyrString(g->gc, mystring, 0, true);
503             SetObject(devInfo->slots + devInfo->size++, dev_serial);
504             g->gc->GCWriteNew(devInfo, dev_serial); // we know dev_serial is white so we can use GCWriteNew
505 
506             if (mystring != emptyString)
507                 free((void*)mystring);
508             if (cur_dev->manufacturer_string != nullptr)
509                 mystring = wchar_to_char(cur_dev->manufacturer_string);
510             else
511                 mystring = emptyString;
512 
513             PyrString* dev_man_name = newPyrString(g->gc, mystring, 0, true);
514             SetObject(devInfo->slots + devInfo->size++, dev_man_name);
515             g->gc->GCWriteNew(devInfo, dev_man_name); // we know dev_man_name is white so we can use GCWriteNew
516 
517             if (mystring != emptyString)
518                 free((void*)mystring);
519 
520             if (cur_dev->product_string != nullptr)
521                 mystring = wchar_to_char(cur_dev->product_string);
522             else
523                 mystring = emptyString;
524 
525             PyrString* dev_prod_name = newPyrString(g->gc, mystring, 0, true);
526             SetObject(devInfo->slots + devInfo->size++, dev_prod_name);
527             g->gc->GCWriteNew(devInfo, dev_prod_name); // we know dev_prod_name is white so we can use GCWriteNew
528 
529             if (mystring != emptyString)
530                 free((void*)mystring);
531 
532             SetInt(devInfo->slots + devInfo->size++, cur_dev->release_number);
533             SetInt(devInfo->slots + devInfo->size++, cur_dev->interface_number);
534 
535             SetInt(devInfo->slots + devInfo->size++, cur_dev->usage_page);
536             SetInt(devInfo->slots + devInfo->size++, cur_dev->usage);
537 
538             cur_dev = cur_dev->next;
539         }
540 
541         SC_HID_APIManager::instance().free_devicelist();
542     } else {
543         // send back info that no devices were found, or empty array
544         SetInt(self, 0);
545     }
546     return errNone;
547 }
548 
prHID_API_Open(VMGlobals * g,int numArgsPushed)549 int prHID_API_Open(VMGlobals* g, int numArgsPushed) {
550     PyrSlot* args = g->sp - numArgsPushed + 1;
551     PyrSlot* self = args + 0;
552     PyrSlot* arg1 = args + 1;
553     PyrSlot* arg2 = args + 2;
554     PyrSlot* arg3 = args + 3;
555 
556     int err;
557     char path[256];
558     int vendorid;
559     int productid;
560     // 	char serial_number[256]; // could also use serial_number as specification to open device, but this is not
561     // working yet
562 
563     err = slotIntVal(arg1, &vendorid);
564     if (err != errNone)
565         return err;
566 
567     err = slotIntVal(arg2, &productid);
568     if (err != errNone)
569         return err;
570 
571     int result;
572 
573     err = slotStrVal(arg3, path, sizeof(path));
574     if (err)
575         return err;
576     result = SC_HID_APIManager::instance().open_device_path(path, vendorid, productid);
577 
578     /*
579     // could also use serial_number as specification to open device, but this is not working yet
580   if ( NotNil( arg3 ) ){
581     err = slotStrVal(arg3, serial_number, sizeof(serial_number));
582     if (err) return err;
583     // open device
584     result = SC_HID_APIManager::instance().open_device( vendorid, productid, serial_number );
585   } else {
586       // open device
587     result = SC_HID_APIManager::instance().open_device( vendorid, productid, NULL );
588   }
589 */
590     SetInt(self, result);
591 
592     return errNone;
593 }
594 
prHID_API_Close(VMGlobals * g,int numArgsPushed)595 int prHID_API_Close(VMGlobals* g, int numArgsPushed) {
596     PyrSlot* args = g->sp - numArgsPushed + 1;
597     PyrSlot* self = args + 0;
598     PyrSlot* arg = args + 1;
599 
600     int err;
601     int joyid;
602 
603     err = slotIntVal(arg, &joyid);
604     if (err != errNone)
605         return err;
606 
607     int result = SC_HID_APIManager::instance().close_device(joyid);
608 
609     SetInt(self, result);
610 
611     return errNone;
612 }
613 
prHID_API_GetInfo(VMGlobals * g,int numArgsPushed)614 int prHID_API_GetInfo(VMGlobals* g, int numArgsPushed) {
615     PyrSlot* args = g->sp - numArgsPushed + 1;
616     PyrSlot* self = args + 0;
617     PyrSlot* arg = args + 1;
618 
619     int err;
620     int joyid;
621 
622     err = slotIntVal(arg, &joyid);
623     if (err != errNone)
624         return err;
625     const char emptyString[] = "";
626 
627     struct hid_dev_desc* devdesc = SC_HID_APIManager::instance().get_device(joyid);
628     struct hid_device_info* cur_dev = devdesc->info;
629 
630     if (cur_dev != nullptr) {
631         PyrObject* devInfo = newPyrArray(g->gc, 9 * sizeof(PyrObject), 0, true);
632         SetObject(self, devInfo);
633 
634         SetInt(devInfo->slots + devInfo->size++, cur_dev->vendor_id);
635         SetInt(devInfo->slots + devInfo->size++, cur_dev->product_id);
636 
637         PyrString* dev_path_name = newPyrString(g->gc, cur_dev->path, 0, true);
638         SetObject(devInfo->slots + devInfo->size++, dev_path_name);
639         g->gc->GCWriteNew(devInfo, dev_path_name); // we know dev_path_name is white so we can use GCWriteNew
640 
641         const char* mystring;
642         if (cur_dev->serial_number != nullptr) {
643             mystring = wchar_to_char(cur_dev->serial_number);
644         } else {
645             mystring = emptyString;
646         }
647 
648         PyrString* dev_serial = newPyrString(g->gc, mystring, 0, true);
649         SetObject(devInfo->slots + devInfo->size++, dev_serial);
650         g->gc->GCWriteNew(devInfo, dev_serial); // we know dev_serial is white so we can use GCWriteNew
651 
652         if (mystring != emptyString)
653             free((void*)mystring);
654 
655         if (cur_dev->manufacturer_string != nullptr) {
656             mystring = wchar_to_char(cur_dev->manufacturer_string);
657         } else {
658             mystring = emptyString;
659         }
660         PyrString* dev_man_name = newPyrString(g->gc, mystring, 0, true);
661         SetObject(devInfo->slots + devInfo->size++, dev_man_name);
662         g->gc->GCWriteNew(devInfo, dev_man_name); // we know dev_man_name is white so we can use GCWriteNew
663         if (mystring != emptyString)
664             free((void*)mystring);
665 
666 
667         if (cur_dev->product_string != nullptr) {
668             mystring = wchar_to_char(cur_dev->product_string);
669         } else {
670             mystring = emptyString;
671         }
672         PyrString* dev_prod_name = newPyrString(g->gc, mystring, 0, true);
673         SetObject(devInfo->slots + devInfo->size++, dev_prod_name);
674         g->gc->GCWriteNew(devInfo, dev_prod_name); // we know dev_prod_name is white so we can use GCWriteNew
675         if (mystring != emptyString)
676             free((void*)mystring);
677 
678         SetInt(devInfo->slots + devInfo->size++, cur_dev->release_number);
679         SetInt(devInfo->slots + devInfo->size++, cur_dev->interface_number);
680     } else {
681         SetInt(self, 0);
682     }
683     return errNone;
684 }
685 
prHID_API_GetNumberOfCollections(VMGlobals * g,int numArgsPushed)686 int prHID_API_GetNumberOfCollections(VMGlobals* g, int numArgsPushed) {
687     PyrSlot* args = g->sp - numArgsPushed + 1;
688     PyrSlot* self = args + 0;
689     PyrSlot* arg = args + 1;
690 
691     int err;
692     int joyid;
693 
694     err = slotIntVal(arg, &joyid);
695     if (err != errNone)
696         return err;
697 
698     struct hid_dev_desc* devdesc = SC_HID_APIManager::instance().get_device(joyid);
699     struct hid_device_collection* cur_dev = devdesc->device_collection;
700 
701     if (cur_dev != nullptr) {
702         SetInt(self, cur_dev->num_collections);
703     } else {
704         SetInt(self, 0);
705     }
706     return errNone;
707 }
708 
prHID_API_GetCollectionInfo(VMGlobals * g,int numArgsPushed)709 int prHID_API_GetCollectionInfo(VMGlobals* g, int numArgsPushed) {
710     PyrSlot* args = g->sp - numArgsPushed + 1;
711     PyrSlot* self = args + 0;
712     PyrSlot* arg1 = args + 1;
713     PyrSlot* arg2 = args + 2;
714 
715     int err;
716     int joyid;
717     int collectionid;
718 
719     err = slotIntVal(arg1, &joyid);
720     if (err != errNone)
721         return err;
722 
723     err = slotIntVal(arg2, &collectionid);
724     if (err != errNone)
725         return err;
726 
727     struct hid_dev_desc* devdesc = SC_HID_APIManager::instance().get_device(joyid);
728     struct hid_device_collection* curdev = devdesc->device_collection;
729     struct hid_device_collection* curcollection = curdev->first_collection;
730     struct hid_device_collection* thiscollection = nullptr;
731 
732     bool found = curcollection->index == collectionid;
733     if (found) {
734         thiscollection = curcollection;
735     }
736     while (curcollection != nullptr && !found) {
737         found = curcollection->index == collectionid;
738         if (found) {
739             thiscollection = curcollection;
740         }
741         curcollection = curcollection->next_collection;
742     }
743 
744     if (thiscollection != nullptr) {
745         PyrObject* elInfo = newPyrArray(g->gc, 9 * sizeof(PyrObject), 0, true);
746         SetObject(self, elInfo);
747 
748         SetInt(elInfo->slots + elInfo->size++, thiscollection->index);
749         SetInt(elInfo->slots + elInfo->size++, thiscollection->type);
750         SetInt(elInfo->slots + elInfo->size++, thiscollection->usage_page);
751         SetInt(elInfo->slots + elInfo->size++, thiscollection->usage_index);
752 
753         if (thiscollection->parent_collection != nullptr) {
754             SetInt(elInfo->slots + elInfo->size++, thiscollection->parent_collection->index);
755         } else {
756             SetInt(elInfo->slots + elInfo->size++, -2);
757         }
758 
759         SetInt(elInfo->slots + elInfo->size++, thiscollection->num_collections);
760 
761         if (thiscollection->first_collection != nullptr) {
762             SetInt(elInfo->slots + elInfo->size++, thiscollection->first_collection->index);
763         } else {
764             SetInt(elInfo->slots + elInfo->size++, -1);
765         }
766 
767         SetInt(elInfo->slots + elInfo->size++, thiscollection->num_elements);
768 
769         if (thiscollection->first_element != nullptr) {
770             SetInt(elInfo->slots + elInfo->size++, thiscollection->first_element->index);
771         } else {
772             SetInt(elInfo->slots + elInfo->size++, -1);
773         }
774 
775     } else {
776         SetInt(self, 0);
777     }
778     return errNone;
779 }
780 
prHID_API_GetNumberOfElements(VMGlobals * g,int numArgsPushed)781 int prHID_API_GetNumberOfElements(VMGlobals* g, int numArgsPushed) {
782     PyrSlot* args = g->sp - numArgsPushed + 1;
783     PyrSlot* self = args + 0;
784     PyrSlot* arg = args + 1;
785 
786     int err;
787     int joyid;
788 
789     err = slotIntVal(arg, &joyid);
790     if (err != errNone)
791         return err;
792 
793     struct hid_dev_desc* devdesc = SC_HID_APIManager::instance().get_device(joyid);
794     struct hid_device_collection* cur_dev = devdesc->device_collection;
795 
796     if (cur_dev != nullptr) {
797         SetInt(self, cur_dev->num_elements);
798     } else {
799         SetInt(self, 0);
800     }
801     return errNone;
802 }
803 
prHID_API_GetElementInfo(VMGlobals * g,int numArgsPushed)804 int prHID_API_GetElementInfo(VMGlobals* g, int numArgsPushed) {
805     PyrSlot* args = g->sp - numArgsPushed + 1;
806     PyrSlot* self = args + 0;
807     PyrSlot* arg1 = args + 1;
808     PyrSlot* arg2 = args + 2;
809 
810     int err;
811     int joyid;
812     int elementid;
813 
814     err = slotIntVal(arg1, &joyid);
815     if (err != errNone)
816         return err;
817 
818     err = slotIntVal(arg2, &elementid);
819     if (err != errNone)
820         return err;
821 
822     struct hid_dev_desc* devdesc = SC_HID_APIManager::instance().get_device(joyid);
823     struct hid_device_collection* curdev = devdesc->device_collection;
824     struct hid_device_element* curelement = curdev->first_element;
825     struct hid_device_element* thiselement = nullptr;
826 
827     bool found = curelement->index == elementid;
828     if (found) {
829         thiselement = curelement;
830     }
831     while (curelement != nullptr && !found) {
832         found = curelement->index == elementid;
833         if (found) {
834             thiselement = curelement;
835         }
836         curelement = curelement->next;
837     }
838 
839     if (thiselement != nullptr) {
840         PyrObject* elInfo = newPyrArray(g->gc, 18 * sizeof(PyrObject), 0, true);
841         SetObject(self, elInfo);
842 
843         SetInt(elInfo->slots + elInfo->size++, thiselement->index);
844         SetInt(elInfo->slots + elInfo->size++, thiselement->io_type);
845         SetInt(elInfo->slots + elInfo->size++, thiselement->type);
846         SetInt(elInfo->slots + elInfo->size++, thiselement->usage_page);
847         SetInt(elInfo->slots + elInfo->size++, thiselement->usage);
848 
849         SetInt(elInfo->slots + elInfo->size++, thiselement->usage_min);
850         SetInt(elInfo->slots + elInfo->size++, thiselement->usage_max);
851 
852         SetInt(elInfo->slots + elInfo->size++, thiselement->logical_min);
853         SetInt(elInfo->slots + elInfo->size++, thiselement->logical_max);
854         SetInt(elInfo->slots + elInfo->size++, thiselement->phys_min);
855         SetInt(elInfo->slots + elInfo->size++, thiselement->phys_max);
856         SetInt(elInfo->slots + elInfo->size++, thiselement->unit_exponent);
857 
858         SetInt(elInfo->slots + elInfo->size++, thiselement->unit);
859         SetInt(elInfo->slots + elInfo->size++, thiselement->report_size);
860         SetInt(elInfo->slots + elInfo->size++, thiselement->report_id);
861         SetInt(elInfo->slots + elInfo->size++, thiselement->report_index);
862         SetInt(elInfo->slots + elInfo->size++, thiselement->value);
863 
864         SetInt(elInfo->slots + elInfo->size++, thiselement->parent_collection->index);
865 
866     } else {
867         SetInt(self, 0);
868     }
869     return errNone;
870 }
871 
prHID_API_SetElementOutput(VMGlobals * g,int numArgsPushed)872 int prHID_API_SetElementOutput(VMGlobals* g, int numArgsPushed) {
873     PyrSlot* args = g->sp - numArgsPushed + 1;
874     PyrSlot* joyIdSlot = args + 1;
875     PyrSlot* elementIdSlot = args + 2;
876     PyrSlot* valueSlot = args + 3;
877 
878     int err;
879     int joyid;
880     int elementid;
881     int value;
882 
883     err = slotIntVal(joyIdSlot, &joyid);
884     if (err != errNone)
885         return err;
886 
887     err = slotIntVal(elementIdSlot, &elementid);
888     if (err != errNone)
889         return err;
890 
891     err = slotIntVal(valueSlot, &value);
892     if (err != errNone)
893         return err;
894 
895     struct hid_dev_desc* devdesc = SC_HID_APIManager::instance().get_device(joyid);
896     struct hid_device_collection* curdev = devdesc->device_collection;
897     struct hid_device_element* curelement = curdev->first_element;
898     struct hid_device_element* thiselement = nullptr;
899 
900     if (devdesc != nullptr) {
901         bool found = false;
902         while (curelement != nullptr && !found) {
903             found = (curelement->index == elementid) && (curelement->io_type == 2);
904             if (found) {
905                 thiselement = curelement;
906             }
907             curelement = hid_get_next_output_element(curelement);
908         }
909         if (thiselement != nullptr) {
910             thiselement->value = value;
911             hid_send_output_report(devdesc, thiselement->report_id);
912         }
913     }
914     return errNone;
915 }
916 
prHID_API_SetElementRepeat(VMGlobals * g,int numArgsPushed)917 int prHID_API_SetElementRepeat(VMGlobals* g, int numArgsPushed) {
918     PyrSlot* args = g->sp - numArgsPushed + 1;
919     PyrSlot* joyIdSlot = args + 1;
920     PyrSlot* elementIdSlot = args + 2;
921     PyrSlot* valueSlot = args + 3;
922 
923     int err;
924     int joyid;
925     int elementid;
926     int value;
927 
928     err = slotIntVal(joyIdSlot, &joyid);
929     if (err != errNone)
930         return err;
931 
932     err = slotIntVal(elementIdSlot, &elementid);
933     if (err != errNone)
934         return err;
935 
936     err = slotIntVal(valueSlot, &value);
937     if (err != errNone)
938         return err;
939 
940     struct hid_dev_desc* devdesc = SC_HID_APIManager::instance().get_device(joyid);
941     struct hid_device_collection* curdev = devdesc->device_collection;
942     struct hid_device_element* curelement = curdev->first_element;
943     struct hid_device_element* thiselement = nullptr;
944 
945     if (devdesc != nullptr) {
946         bool found = false;
947         while (curelement != nullptr && !found) {
948             found = (curelement->index == elementid) && (curelement->io_type == 1);
949             if (found) {
950                 thiselement = curelement;
951             }
952             curelement = hid_get_next_input_element(curelement);
953         }
954         if (thiselement != nullptr) {
955             thiselement->repeat = value;
956         }
957     }
958     return errNone;
959 }
960 
close_HID_API_Devices()961 void close_HID_API_Devices() { SC_HID_APIManager::instance().closeAll(); }
962 
initHIDAPIPrimitives()963 void initHIDAPIPrimitives() {
964     int base, index;
965 
966     close_HID_API_Devices();
967 
968     s_hidapi = getsym("HID");
969 
970     base = nextPrimitiveIndex();
971     index = 0;
972 
973     definePrimitive(base, index++, "_HID_API_Initialize", prHID_API_Initialize, 1,
974                     0); // this initializes the hid subsystem
975     definePrimitive(base, index++, "_HID_API_CloseAll", prHID_API_CloseAll, 1,
976                     0); // this also cleans up and closes devices
977 
978     definePrimitive(base, index++, "_HID_API_BuildDeviceList", prHID_API_BuildDeviceList, 1,
979                     0); // this gets device info about the various devices that are attached
980 
981     definePrimitive(base, index++, "_HID_API_OpenDevice", prHID_API_Open, 3, 0); // opens a specific device
982     definePrimitive(base, index++, "_HID_API_CloseDevice", prHID_API_Close, 2, 0); // closes a specific device
983 
984     definePrimitive(base, index++, "_HID_API_GetInfo", prHID_API_GetInfo, 2, 0); // gets info about a specific device
985     definePrimitive(base, index++, "_HID_API_GetNumberOfCollections", prHID_API_GetNumberOfCollections, 2,
986                     0); // gets number of elements of a device
987     definePrimitive(base, index++, "_HID_API_GetCollectionInfo", prHID_API_GetCollectionInfo, 3,
988                     0); // gets info about a specific device element
989     definePrimitive(base, index++, "_HID_API_GetNumberOfElements", prHID_API_GetNumberOfElements, 2,
990                     0); // gets number of elements of a device
991     definePrimitive(base, index++, "_HID_API_GetElementInfo", prHID_API_GetElementInfo, 4,
992                     0); // gets info about a specific device element
993 
994     definePrimitive(base, index++, "_HID_API_SetElementOutput", prHID_API_SetElementOutput, 3,
995                     0); // sets the output value of a specific device element, and sends the report
996     definePrimitive(base, index++, "_HID_API_SetElementRepeat", prHID_API_SetElementRepeat, 3,
997                     0); // sets the repeat property of a specific device element
998 
999     s_hidElementData = getsym("prHIDElementData"); // send back element data
1000     s_hidDeviceData = getsym("prHIDDeviceData"); // send back device data
1001     s_hidClosed = getsym("prHIDDeviceClosed"); // send back that device was closed
1002 }
1003 
deinitHIDAPIPrimitives()1004 void deinitHIDAPIPrimitives() { SC_HID_APIManager::instance().closeAll(); }
1005 
1006 #else // no HID API
1007 
initHIDAPIPrimitives()1008 void initHIDAPIPrimitives() {
1009     // other platforms?
1010 }
1011 
deinitHIDAPIPrimitives()1012 void deinitHIDAPIPrimitives() {}
1013 
1014 #endif /// HAVE_API_HID
1015 
1016 
1017 // EOF
1018