Atlas - test.cpp

Home / ext / SDL / src / hidapi / testgui Lines: 14 | Size: 15229 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/******************************************************* 2 Demo Program for HIDAPI 3 4 Alan Ott 5 Signal 11 Software 6 7 2010-07-20 8 9 Copyright 2010, All Rights Reserved 10 11 This contents of this file may be used by anyone 12 for any reason without any conditions and may be 13 used as a starting point for your own applications 14 which use HIDAPI. 15********************************************************/ 16 17 18#include <fx.h> 19 20#include "hidapi.h" 21#include "mac_support.h" 22#include <string.h> 23#include <stdlib.h> 24#include <limits.h> 25 26#ifdef _WIN32 27 // Thanks Microsoft, but I know how to use strncpy(). 28 #pragma warning(disable:4996) 29#endif 30 31class MainWindow : public FXMainWindow { 32 FXDECLARE(MainWindow) 33 34public: 35 enum { 36 ID_FIRST = FXMainWindow::ID_LAST, 37 ID_CONNECT, 38 ID_DISCONNECT, 39 ID_RESCAN, 40 ID_SEND_OUTPUT_REPORT, 41 ID_SEND_FEATURE_REPORT, 42 ID_GET_FEATURE_REPORT, 43 ID_CLEAR, 44 ID_TIMER, 45 ID_MAC_TIMER, 46 ID_LAST, 47 }; 48 49private: 50 FXList *device_list; 51 FXButton *connect_button; 52 FXButton *disconnect_button; 53 FXButton *rescan_button; 54 FXButton *output_button; 55 FXLabel *connected_label; 56 FXTextField *output_text; 57 FXTextField *output_len; 58 FXButton *feature_button; 59 FXButton *get_feature_button; 60 FXTextField *feature_text; 61 FXTextField *feature_len; 62 FXTextField *get_feature_text; 63 FXText *input_text; 64 FXFont *title_font; 65 66 struct hid_device_info *devices; 67 hid_device *connected_device; 68 size_t getDataFromTextField(FXTextField *tf, char *buf, size_t len); 69 int getLengthFromTextField(FXTextField *tf); 70 71 72protected: 73 MainWindow() {}; 74public: 75 MainWindow(FXApp *a); 76 ~MainWindow(); 77 virtual void create(); 78 79 long onConnect(FXObject *sender, FXSelector sel, void *ptr); 80 long onDisconnect(FXObject *sender, FXSelector sel, void *ptr); 81 long onRescan(FXObject *sender, FXSelector sel, void *ptr); 82 long onSendOutputReport(FXObject *sender, FXSelector sel, void *ptr); 83 long onSendFeatureReport(FXObject *sender, FXSelector sel, void *ptr); 84 long onGetFeatureReport(FXObject *sender, FXSelector sel, void *ptr); 85 long onClear(FXObject *sender, FXSelector sel, void *ptr); 86 long onTimeout(FXObject *sender, FXSelector sel, void *ptr); 87 long onMacTimeout(FXObject *sender, FXSelector sel, void *ptr); 88}; 89 90// FOX 1.7 changes the timeouts to all be nanoseconds. 91// Fox 1.6 had all timeouts as milliseconds. 92#if (FOX_MINOR >= 7) 93 const int timeout_scalar = 1000*1000; 94#else 95 const int timeout_scalar = 1; 96#endif 97 98FXMainWindow *g_main_window; 99 100 101FXDEFMAP(MainWindow) MainWindowMap [] = { 102 FXMAPFUNC(SEL_COMMAND, MainWindow::ID_CONNECT, MainWindow::onConnect ), 103 FXMAPFUNC(SEL_COMMAND, MainWindow::ID_DISCONNECT, MainWindow::onDisconnect ), 104 FXMAPFUNC(SEL_COMMAND, MainWindow::ID_RESCAN, MainWindow::onRescan ), 105 FXMAPFUNC(SEL_COMMAND, MainWindow::ID_SEND_OUTPUT_REPORT, MainWindow::onSendOutputReport ), 106 FXMAPFUNC(SEL_COMMAND, MainWindow::ID_SEND_FEATURE_REPORT, MainWindow::onSendFeatureReport ), 107 FXMAPFUNC(SEL_COMMAND, MainWindow::ID_GET_FEATURE_REPORT, MainWindow::onGetFeatureReport ), 108 FXMAPFUNC(SEL_COMMAND, MainWindow::ID_CLEAR, MainWindow::onClear ), 109 FXMAPFUNC(SEL_TIMEOUT, MainWindow::ID_TIMER, MainWindow::onTimeout ), 110 FXMAPFUNC(SEL_TIMEOUT, MainWindow::ID_MAC_TIMER, MainWindow::onMacTimeout ), 111}; 112 113FXIMPLEMENT(MainWindow, FXMainWindow, MainWindowMap, ARRAYNUMBER(MainWindowMap)); 114 115MainWindow::MainWindow(FXApp *app) 116 : FXMainWindow(app, "HIDAPI Test Application", NULL, NULL, DECOR_ALL, 200,100, 425,700) 117{ 118 devices = NULL; 119 connected_device = NULL; 120 121 FXVerticalFrame *vf = new FXVerticalFrame(this, LAYOUT_FILL_Y|LAYOUT_FILL_X); 122 123 FXLabel *label = new FXLabel(vf, "HIDAPI Test Tool"); 124 title_font = new FXFont(getApp(), "Arial", 14, FXFont::Bold); 125 label->setFont(title_font); 126 127 new FXLabel(vf, 128 "Select a device and press Connect.", NULL, JUSTIFY_LEFT); 129 new FXLabel(vf, 130 "Output data bytes can be entered in the Output section, \n" 131 "separated by space, comma or brackets. Data starting with 0x\n" 132 "is treated as hex. Data beginning with a 0 is treated as \n" 133 "octal. All other data is treated as decimal.", NULL, JUSTIFY_LEFT); 134 new FXLabel(vf, 135 "Data received from the device appears in the Input section.", 136 NULL, JUSTIFY_LEFT); 137 new FXLabel(vf, 138 "Optionally, a report length may be specified. Extra bytes are\n" 139 "padded with zeros. If no length is specified, the length is \n" 140 "inferred from the data.", 141 NULL, JUSTIFY_LEFT); 142 new FXLabel(vf, ""); 143 144 // Device List and Connect/Disconnect buttons 145 FXHorizontalFrame *hf = new FXHorizontalFrame(vf, LAYOUT_FILL_X); 146 //device_list = new FXList(new FXHorizontalFrame(hf,FRAME_SUNKEN|FRAME_THICK, 0,0,0,0, 0,0,0,0), NULL, 0, LISTBOX_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT, 0,0,300,200); 147 device_list = new FXList(new FXHorizontalFrame(hf,FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_X|LAYOUT_FILL_Y, 0,0,0,0, 0,0,0,0), NULL, 0, LISTBOX_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_Y, 0,0,300,200); 148 FXVerticalFrame *buttonVF = new FXVerticalFrame(hf); 149 connect_button = new FXButton(buttonVF, "Connect", NULL, this, ID_CONNECT, BUTTON_NORMAL|LAYOUT_FILL_X); 150 disconnect_button = new FXButton(buttonVF, "Disconnect", NULL, this, ID_DISCONNECT, BUTTON_NORMAL|LAYOUT_FILL_X); 151 disconnect_button->disable(); 152 rescan_button = new FXButton(buttonVF, "Re-Scan devices", NULL, this, ID_RESCAN, BUTTON_NORMAL|LAYOUT_FILL_X); 153 new FXHorizontalFrame(buttonVF, 0, 0,0,0,0, 0,0,50,0); 154 155 connected_label = new FXLabel(vf, "Disconnected"); 156 157 new FXHorizontalFrame(vf); 158 159 // Output Group Box 160 FXGroupBox *gb = new FXGroupBox(vf, "Output", FRAME_GROOVE|LAYOUT_FILL_X); 161 FXMatrix *matrix = new FXMatrix(gb, 3, MATRIX_BY_COLUMNS|LAYOUT_FILL_X); 162 new FXLabel(matrix, "Data"); 163 new FXLabel(matrix, "Length"); 164 new FXLabel(matrix, ""); 165 166 //hf = new FXHorizontalFrame(gb, LAYOUT_FILL_X); 167 output_text = new FXTextField(matrix, 30, NULL, 0, TEXTFIELD_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN); 168 output_text->setText("1 0x81 0"); 169 output_len = new FXTextField(matrix, 5, NULL, 0, TEXTFIELD_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN); 170 output_button = new FXButton(matrix, "Send Output Report", NULL, this, ID_SEND_OUTPUT_REPORT, BUTTON_NORMAL|LAYOUT_FILL_X); 171 output_button->disable(); 172 //new FXHorizontalFrame(matrix, LAYOUT_FILL_X); 173 174 //hf = new FXHorizontalFrame(gb, LAYOUT_FILL_X); 175 feature_text = new FXTextField(matrix, 30, NULL, 0, TEXTFIELD_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN); 176 feature_len = new FXTextField(matrix, 5, NULL, 0, TEXTFIELD_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN); 177 feature_button = new FXButton(matrix, "Send Feature Report", NULL, this, ID_SEND_FEATURE_REPORT, BUTTON_NORMAL|LAYOUT_FILL_X); 178 feature_button->disable(); 179 180 get_feature_text = new FXTextField(matrix, 30, NULL, 0, TEXTFIELD_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN); 181 new FXWindow(matrix); 182 get_feature_button = new FXButton(matrix, "Get Feature Report", NULL, this, ID_GET_FEATURE_REPORT, BUTTON_NORMAL|LAYOUT_FILL_X); 183 get_feature_button->disable(); 184 185 186 // Input Group Box 187 gb = new FXGroupBox(vf, "Input", FRAME_GROOVE|LAYOUT_FILL_X|LAYOUT_FILL_Y); 188 FXVerticalFrame *innerVF = new FXVerticalFrame(gb, LAYOUT_FILL_X|LAYOUT_FILL_Y); 189 input_text = new FXText(new FXHorizontalFrame(innerVF,LAYOUT_FILL_X|LAYOUT_FILL_Y|FRAME_SUNKEN|FRAME_THICK, 0,0,0,0, 0,0,0,0), NULL, 0, LAYOUT_FILL_X|LAYOUT_FILL_Y); 190 input_text->setEditable(false); 191 new FXButton(innerVF, "Clear", NULL, this, ID_CLEAR, BUTTON_NORMAL|LAYOUT_RIGHT); 192 193 194} 195 196MainWindow::~MainWindow() 197{ 198 if (connected_device) 199 hid_close(connected_device); 200 hid_exit(); 201 delete title_font; 202} 203 204void 205MainWindow::create() 206{ 207 FXMainWindow::create(); 208 show(); 209 210 onRescan(NULL, 0, NULL); 211 212 213#ifdef __APPLE__ 214 init_apple_message_system(); 215#endif 216 217 getApp()->addTimeout(this, ID_MAC_TIMER, 218 50 * timeout_scalar /*50ms*/); 219} 220 221long 222MainWindow::onConnect(FXObject *sender, FXSelector sel, void *ptr) 223{ 224 if (connected_device != NULL) 225 return 1; 226 227 FXint cur_item = device_list->getCurrentItem(); 228 if (cur_item < 0) 229 return -1; 230 FXListItem *item = device_list->getItem(cur_item); 231 if (!item) 232 return -1; 233 struct hid_device_info *device_info = (struct hid_device_info*) item->getData(); 234 if (!device_info) 235 return -1; 236 237 connected_device = hid_open_path(device_info->path); 238 239 if (!connected_device) { 240 FXMessageBox::error(this, MBOX_OK, "Device Error", "Unable To Connect to Device"); 241 return -1; 242 } 243 244 hid_set_nonblocking(connected_device, 1); 245 246 getApp()->addTimeout(this, ID_TIMER, 247 5 * timeout_scalar /*5ms*/); 248 249 FXString s; 250 s.format("Connected to: %04hx:%04hx -", device_info->vendor_id, device_info->product_id); 251 s += FXString(" ") + device_info->manufacturer_string; 252 s += FXString(" ") + device_info->product_string; 253 connected_label->setText(s); 254 output_button->enable(); 255 feature_button->enable(); 256 get_feature_button->enable(); 257 connect_button->disable(); 258 disconnect_button->enable(); 259 input_text->setText(""); 260 261 262 return 1; 263} 264 265long 266MainWindow::onDisconnect(FXObject *sender, FXSelector sel, void *ptr) 267{ 268 hid_close(connected_device); 269 connected_device = NULL; 270 connected_label->setText("Disconnected"); 271 output_button->disable(); 272 feature_button->disable(); 273 get_feature_button->disable(); 274 connect_button->enable(); 275 disconnect_button->disable(); 276 277 getApp()->removeTimeout(this, ID_TIMER); 278 279 return 1; 280} 281 282long 283MainWindow::onRescan(FXObject *sender, FXSelector sel, void *ptr) 284{ 285 struct hid_device_info *cur_dev; 286 287 device_list->clearItems(); 288 289 // List the Devices 290 hid_free_enumeration(devices); 291 devices = hid_enumerate(0x0, 0x0); 292 cur_dev = devices; 293 while (cur_dev) { 294 // Add it to the List Box. 295 FXString s; 296 FXString usage_str; 297 s.format("%04hx:%04hx -", cur_dev->vendor_id, cur_dev->product_id); 298 s += FXString(" ") + cur_dev->manufacturer_string; 299 s += FXString(" ") + cur_dev->product_string; 300 usage_str.format(" (usage: %04hx:%04hx) ", cur_dev->usage_page, cur_dev->usage); 301 s += usage_str; 302 FXListItem *li = new FXListItem(s, NULL, cur_dev); 303 device_list->appendItem(li); 304 305 cur_dev = cur_dev->next; 306 } 307 308 if (device_list->getNumItems() == 0) 309 device_list->appendItem("*** No Devices Connected ***"); 310 else { 311 device_list->selectItem(0); 312 } 313 314 return 1; 315} 316 317size_t 318MainWindow::getDataFromTextField(FXTextField *tf, char *buf, size_t len) 319{ 320 const char *delim = " ,{}\t\r\n"; 321 FXString data = tf->getText(); 322 const FXchar *d = data.text(); 323 size_t i = 0; 324 325 // Copy the string from the GUI. 326 size_t sz = strlen(d); 327 char *str = (char*) malloc(sz+1); 328 strcpy(str, d); 329 330 // For each token in the string, parse and store in buf[]. 331 char *token = strtok(str, delim); 332 while (token) { 333 char *endptr; 334 long int val = strtol(token, &endptr, 0); 335 buf[i++] = val; 336 token = strtok(NULL, delim); 337 } 338 339 free(str); 340 return i; 341} 342 343/* getLengthFromTextField() 344 Returns length: 345 0: empty text field 346 >0: valid length 347 -1: invalid length */ 348int 349MainWindow::getLengthFromTextField(FXTextField *tf) 350{ 351 long int len; 352 FXString str = tf->getText(); 353 size_t sz = str.length(); 354 355 if (sz > 0) { 356 char *endptr; 357 len = strtol(str.text(), &endptr, 0); 358 if (endptr != str.text() && *endptr == '\0') { 359 if (len <= 0) { 360 FXMessageBox::error(this, MBOX_OK, "Invalid length", "Enter a length greater than zero."); 361 return -1; 362 } 363 return len; 364 } 365 else 366 return -1; 367 } 368 369 return 0; 370} 371 372long 373MainWindow::onSendOutputReport(FXObject *sender, FXSelector sel, void *ptr) 374{ 375 char buf[256]; 376 size_t data_len, len; 377 int textfield_len; 378 379 memset(buf, 0x0, sizeof(buf)); 380 textfield_len = getLengthFromTextField(output_len); 381 data_len = getDataFromTextField(output_text, buf, sizeof(buf)); 382 383 if (textfield_len < 0) { 384 FXMessageBox::error(this, MBOX_OK, "Invalid length", "Length field is invalid. Please enter a number in hex, octal, or decimal."); 385 return 1; 386 } 387 388 if (textfield_len > sizeof(buf)) { 389 FXMessageBox::error(this, MBOX_OK, "Invalid length", "Length field is too long."); 390 return 1; 391 } 392 393 len = (textfield_len)? textfield_len: data_len; 394 395 int res = hid_write(connected_device, (const unsigned char*)buf, len); 396 if (res < 0) { 397 FXMessageBox::error(this, MBOX_OK, "Error Writing", "Could not write to device. Error reported was: %ls", hid_error(connected_device)); 398 } 399 400 return 1; 401} 402 403long 404MainWindow::onSendFeatureReport(FXObject *sender, FXSelector sel, void *ptr) 405{ 406 char buf[256]; 407 size_t data_len, len; 408 int textfield_len; 409 410 memset(buf, 0x0, sizeof(buf)); 411 textfield_len = getLengthFromTextField(feature_len); 412 data_len = getDataFromTextField(feature_text, buf, sizeof(buf)); 413 414 if (textfield_len < 0) { 415 FXMessageBox::error(this, MBOX_OK, "Invalid length", "Length field is invalid. Please enter a number in hex, octal, or decimal."); 416 return 1; 417 } 418 419 if (textfield_len > sizeof(buf)) { 420 FXMessageBox::error(this, MBOX_OK, "Invalid length", "Length field is too long."); 421 return 1; 422 } 423 424 len = (textfield_len)? textfield_len: data_len; 425 426 int res = hid_send_feature_report(connected_device, (const unsigned char*)buf, len); 427 if (res < 0) { 428 FXMessageBox::error(this, MBOX_OK, "Error Writing", "Could not send feature report to device. Error reported was: %ls", hid_error(connected_device)); 429 } 430 431 return 1; 432} 433 434long 435MainWindow::onGetFeatureReport(FXObject *sender, FXSelector sel, void *ptr) 436{ 437 char buf[256]; 438 size_t len; 439 440 memset(buf, 0x0, sizeof(buf)); 441 len = getDataFromTextField(get_feature_text, buf, sizeof(buf)); 442 443 if (len != 1) { 444 FXMessageBox::error(this, MBOX_OK, "Too many numbers", "Enter only a single report number in the text field"); 445 } 446 447 int res = hid_get_feature_report(connected_device, (unsigned char*)buf, sizeof(buf)); 448 if (res < 0) { 449 FXMessageBox::error(this, MBOX_OK, "Error Getting Report", "Could not get feature report from device. Error reported was: %ls", hid_error(connected_device)); 450 } 451 452 if (res > 0) { 453 FXString s; 454 s.format("Returned Feature Report. %d bytes:\n", res); 455 for (int i = 0; i < res; i++) { 456 FXString t; 457 t.format("%02hhx ", buf[i]); 458 s += t; 459 if ((i+1) % 4 == 0) 460 s += " "; 461 if ((i+1) % 16 == 0) 462 s += "\n"; 463 } 464 s += "\n"; 465 input_text->appendText(s); 466 input_text->setBottomLine(INT_MAX); 467 } 468 469 return 1; 470} 471 472long 473MainWindow::onClear(FXObject *sender, FXSelector sel, void *ptr) 474{ 475 input_text->setText(""); 476 return 1; 477} 478 479long 480MainWindow::onTimeout(FXObject *sender, FXSelector sel, void *ptr) 481{ 482 unsigned char buf[256]; 483 int res = hid_read(connected_device, buf, sizeof(buf)); 484 485 if (res > 0) { 486 FXString s; 487 s.format("Received %d bytes:\n", res); 488 for (int i = 0; i < res; i++) { 489 FXString t; 490 t.format("%02hhx ", buf[i]); 491 s += t; 492 if ((i+1) % 4 == 0) 493 s += " "; 494 if ((i+1) % 16 == 0) 495 s += "\n"; 496 } 497 s += "\n"; 498 input_text->appendText(s); 499 input_text->setBottomLine(INT_MAX); 500 } 501 if (res < 0) { 502 input_text->appendText("hid_read() returned error\n"); 503 input_text->setBottomLine(INT_MAX); 504 } 505 506 getApp()->addTimeout(this, ID_TIMER, 507 5 * timeout_scalar /*5ms*/); 508 return 1; 509} 510 511long 512MainWindow::onMacTimeout(FXObject *sender, FXSelector sel, void *ptr) 513{ 514#ifdef __APPLE__ 515 check_apple_events(); 516 517 getApp()->addTimeout(this, ID_MAC_TIMER, 518 50 * timeout_scalar /*50ms*/); 519#endif 520 521 return 1; 522} 523 524int main(int argc, char **argv) 525{ 526 FXApp app("HIDAPI Test Application", "Signal 11 Software"); 527 app.init(argc, argv); 528 g_main_window = new MainWindow(&app); 529 app.create(); 530 app.run(); 531 return 0; 532} 533
[FILE END]
(C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.