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