test.cpp
author sl
Mon, 19 May 2014 21:11:52 +0200
changeset 1 5c2c48109457
child 2 2dbfd75c2bbe
permissions -rw-r--r--
Fixing solution file .
     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 }