sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

test.cpp (15229B)


      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 
     31 class MainWindow : public FXMainWindow {
     32 	FXDECLARE(MainWindow)
     33 	
     34 public:
     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 	
     49 private:
     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 
     72 protected:
     73 	MainWindow() {};
     74 public:
     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 
     98 FXMainWindow *g_main_window;
     99 
    100 
    101 FXDEFMAP(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 
    113 FXIMPLEMENT(MainWindow, FXMainWindow, MainWindowMap, ARRAYNUMBER(MainWindowMap));
    114 
    115 MainWindow::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 
    196 MainWindow::~MainWindow()
    197 {
    198 	if (connected_device)
    199 		hid_close(connected_device);
    200 	hid_exit();
    201 	delete title_font;
    202 }
    203 
    204 void
    205 MainWindow::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 
    221 long
    222 MainWindow::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 
    265 long
    266 MainWindow::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 
    282 long
    283 MainWindow::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 
    317 size_t
    318 MainWindow::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 */
    348 int
    349 MainWindow::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 
    372 long
    373 MainWindow::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 
    403 long
    404 MainWindow::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 
    434 long
    435 MainWindow::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 
    472 long
    473 MainWindow::onClear(FXObject *sender, FXSelector sel, void *ptr)
    474 {
    475 	input_text->setText("");
    476 	return 1;
    477 }
    478 
    479 long
    480 MainWindow::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 
    511 long
    512 MainWindow::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 
    524 int 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 }