rev |
line source |
nuclear@0
|
1 #include <stdio.h>
|
nuclear@0
|
2 #include <stdlib.h>
|
nuclear@0
|
3 #include <signal.h>
|
nuclear@0
|
4 #include <assert.h>
|
nuclear@0
|
5 #include <windows.h>
|
nuclear@0
|
6 extern "C" {
|
nuclear@0
|
7 #include <hidsdi.h>
|
nuclear@0
|
8 #include <setupapi.h>
|
nuclear@0
|
9 }
|
nuclear@0
|
10
|
nuclear@0
|
11 typedef __int16 int16_t;
|
nuclear@0
|
12 typedef unsigned __int32 uint32_t;
|
nuclear@0
|
13
|
nuclear@0
|
14 /* TODO: notification
|
nuclear@0
|
15 Obtain the Windows GUID for HID devices by way of a call to HidD_GetHidGuid()
|
nuclear@0
|
16 Clear the contents of a DEV_BROADCAST_DEVICEINTERFACE structure 0.
|
nuclear@0
|
17 Assign the members of the structure such that you specify the HID GUID.
|
nuclear@0
|
18 Register the application for device notifications by calling the function RegisterDeviceNotification().
|
nuclear@0
|
19 If the previous step returns an invalid handle, then an error has occurred, and the function should return an error. Otherwise, the return is successful.
|
nuclear@0
|
20 */
|
nuclear@0
|
21
|
nuclear@0
|
22 #pragma pack(push, 1)
|
nuclear@0
|
23 union Report {
|
nuclear@0
|
24 unsigned char type;
|
nuclear@0
|
25
|
nuclear@0
|
26 struct { int16_t x, y, z; } motion;
|
nuclear@0
|
27 struct { uint32_t state; } button;
|
nuclear@0
|
28 };
|
nuclear@0
|
29 #pragma pack(pop)
|
nuclear@0
|
30
|
nuclear@0
|
31 static int WINAPI console_handler(DWORD s);
|
nuclear@0
|
32
|
nuclear@0
|
33 bool done;
|
nuclear@0
|
34
|
nuclear@0
|
35 int main()
|
nuclear@0
|
36 {
|
nuclear@0
|
37 HANDLE dev_handle = 0;
|
nuclear@0
|
38 Report *report = 0;
|
nuclear@0
|
39
|
nuclear@0
|
40 GUID hid_guid;
|
nuclear@0
|
41 HidD_GetHidGuid(&hid_guid);
|
nuclear@0
|
42
|
nuclear@0
|
43 HDEVINFO devs = SetupDiGetClassDevs(&hid_guid, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
|
nuclear@0
|
44 if(!devs) {
|
nuclear@0
|
45 fprintf(stderr, "failed to retrieve all USB HID devices\n");
|
nuclear@0
|
46 return 1;
|
nuclear@0
|
47 }
|
nuclear@0
|
48
|
nuclear@0
|
49 for(int i=0; ; i++) {
|
nuclear@0
|
50 SP_DEVICE_INTERFACE_DATA dev_data;
|
nuclear@0
|
51 dev_data.cbSize = sizeof dev_data;
|
nuclear@0
|
52
|
nuclear@0
|
53 if(!SetupDiEnumDeviceInterfaces(devs, 0, &hid_guid, i, &dev_data)) {
|
nuclear@0
|
54 if(GetLastError() != ERROR_NO_MORE_ITEMS) {
|
nuclear@0
|
55 fprintf(stderr, "failed to enumarate device %d\n", i);
|
nuclear@0
|
56 }
|
nuclear@0
|
57 break;
|
nuclear@0
|
58 }
|
nuclear@0
|
59
|
nuclear@0
|
60 DWORD bufsz;
|
nuclear@0
|
61 SetupDiGetDeviceInterfaceDetail(devs, &dev_data, 0, 0, &bufsz, 0);
|
nuclear@0
|
62
|
nuclear@0
|
63 SP_DEVICE_INTERFACE_DETAIL_DATA *dev_details = (SP_DEVICE_INTERFACE_DETAIL_DATA*)malloc(bufsz);
|
nuclear@0
|
64 assert(dev_details);
|
nuclear@0
|
65 dev_details->cbSize = sizeof *dev_details;
|
nuclear@0
|
66
|
nuclear@0
|
67 if(!SetupDiGetDeviceInterfaceDetail(devs, &dev_data, dev_details, bufsz, &bufsz, 0)) {
|
nuclear@0
|
68 int err = GetLastError();
|
nuclear@0
|
69 fprintf(stderr, "failed to get interface %d details\n", i);
|
nuclear@0
|
70 continue;
|
nuclear@0
|
71 }
|
nuclear@0
|
72 dev_handle = CreateFile(dev_details->DevicePath, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
|
nuclear@0
|
73 if(!dev_handle) {
|
nuclear@0
|
74 fprintf(stderr, "failed to open device file: %s\n", dev_details->DevicePath);
|
nuclear@0
|
75 free(dev_details);
|
nuclear@0
|
76 continue;
|
nuclear@0
|
77 }
|
nuclear@0
|
78 free(dev_details);
|
nuclear@0
|
79
|
nuclear@0
|
80 HIDD_ATTRIBUTES attr;
|
nuclear@0
|
81 attr.Size = sizeof attr;
|
nuclear@0
|
82 if(!HidD_GetAttributes(dev_handle, &attr)) {
|
nuclear@0
|
83 CloseHandle(dev_handle);
|
nuclear@0
|
84 dev_handle = 0;
|
nuclear@0
|
85 continue;
|
nuclear@0
|
86 }
|
nuclear@0
|
87
|
nuclear@0
|
88 char name[256];
|
nuclear@0
|
89 if(!HidD_GetProductString(dev_handle, name, sizeof name)) {
|
nuclear@0
|
90 strcpy(name, "<unknown>");
|
nuclear@0
|
91 }
|
nuclear@0
|
92
|
nuclear@0
|
93 printf("Device: %x:%x: %s\n", (unsigned int)attr.VendorID, (unsigned int)attr.ProductID, name);
|
nuclear@0
|
94
|
nuclear@0
|
95 if(attr.VendorID == 0x46d && attr.ProductID == 0xc626) {
|
nuclear@0
|
96 break; // found it
|
nuclear@0
|
97 }
|
nuclear@0
|
98
|
nuclear@0
|
99 CloseHandle(dev_handle);
|
nuclear@0
|
100 dev_handle = 0;
|
nuclear@0
|
101 }
|
nuclear@0
|
102 SetupDiDestroyDeviceInfoList(devs);
|
nuclear@0
|
103
|
nuclear@0
|
104 if(!dev_handle) {
|
nuclear@0
|
105 fprintf(stderr, "failed to find matching USB HID device\n");
|
nuclear@0
|
106 return 1;
|
nuclear@0
|
107 }
|
nuclear@0
|
108
|
nuclear@0
|
109 printf("device found\n");
|
nuclear@0
|
110 _HIDP_PREPARSED_DATA *predata;
|
nuclear@0
|
111
|
nuclear@0
|
112 if(!HidD_GetPreparsedData(dev_handle, &predata)) {
|
nuclear@0
|
113 fprintf(stderr, "failed to get preparsed data(?)\n");
|
nuclear@0
|
114 goto cleanup;
|
nuclear@0
|
115 }
|
nuclear@0
|
116
|
nuclear@0
|
117 HIDP_CAPS caps;
|
nuclear@0
|
118 if(!HidP_GetCaps(predata, &caps)) {
|
nuclear@0
|
119 fprintf(stderr, "failed to retrieve device capabilities\n");
|
nuclear@0
|
120 HidD_FreePreparsedData(predata);
|
nuclear@0
|
121 goto cleanup;
|
nuclear@0
|
122 }
|
nuclear@0
|
123
|
nuclear@0
|
124 unsigned short num_value_caps = caps.NumberInputValueCaps;
|
nuclear@0
|
125 HIDP_VALUE_CAPS *value_caps = (HIDP_VALUE_CAPS*)malloc(num_value_caps * sizeof *value_caps);
|
nuclear@0
|
126 if(!value_caps) {
|
nuclear@0
|
127 perror("failed to allocate value caps array");
|
nuclear@0
|
128 HidD_FreePreparsedData(predata);
|
nuclear@0
|
129 goto cleanup;
|
nuclear@0
|
130 }
|
nuclear@0
|
131
|
nuclear@0
|
132 if(HidP_GetValueCaps(HidP_Input, value_caps, &num_value_caps, predata) != HIDP_STATUS_SUCCESS) {
|
nuclear@0
|
133 fprintf(stderr, "failed to retrieve device value capabilities\n");
|
nuclear@0
|
134 HidD_FreePreparsedData(predata);
|
nuclear@0
|
135 goto cleanup;
|
nuclear@0
|
136 }
|
nuclear@0
|
137 HidD_FreePreparsedData(predata);
|
nuclear@0
|
138
|
nuclear@0
|
139 int min_val = 0;
|
nuclear@0
|
140 int max_val = 0;
|
nuclear@0
|
141 for(unsigned int i=0; i<num_value_caps; i++) {
|
nuclear@0
|
142 printf("Usage page: %d\n", (int)value_caps[i].UsagePage);
|
nuclear@0
|
143 printf("Report ID: %d\n", (int)value_caps[i].ReportID);
|
nuclear@0
|
144 printf("Logical range: %d - %d\n", (int)value_caps[i].LogicalMin, (int)value_caps[i].LogicalMax);
|
nuclear@0
|
145 if(i == 0) {
|
nuclear@0
|
146 min_val = value_caps[i].LogicalMin;
|
nuclear@0
|
147 max_val = value_caps[i].LogicalMax;
|
nuclear@0
|
148 }
|
nuclear@0
|
149 }
|
nuclear@0
|
150 int val_range = max_val - min_val;
|
nuclear@0
|
151 printf("value range: %d\n", val_range);
|
nuclear@0
|
152
|
nuclear@0
|
153 int report_size = caps.InputReportByteLength;
|
nuclear@0
|
154 if(!(report = (Report*)malloc(report_size))) {
|
nuclear@0
|
155 perror("failed to allocate input report buffer");
|
nuclear@0
|
156 goto cleanup;
|
nuclear@0
|
157 }
|
nuclear@0
|
158
|
nuclear@0
|
159 //signal(SIGINT, sig_handler);
|
nuclear@0
|
160 if(!SetConsoleCtrlHandler(console_handler, 1)) {
|
nuclear@0
|
161 fprintf(stderr, "failed to install console control handler\n");
|
nuclear@0
|
162 }
|
nuclear@0
|
163
|
nuclear@0
|
164 static uint32_t bnstate = 0;
|
nuclear@0
|
165
|
nuclear@0
|
166 while(!done) {
|
nuclear@0
|
167 if(WaitForSingleObject(dev_handle, INFINITE) != 0) {
|
nuclear@0
|
168 fprintf(stderr, "error while waiting for device events\n");
|
nuclear@0
|
169 break;
|
nuclear@0
|
170 }
|
nuclear@0
|
171
|
nuclear@0
|
172 DWORD num_read;
|
nuclear@0
|
173 if(!ReadFile(dev_handle, report, report_size, &num_read, 0)) {
|
nuclear@0
|
174 fprintf(stderr, "failed to read input report\n");
|
nuclear@0
|
175 continue;
|
nuclear@0
|
176 }
|
nuclear@0
|
177
|
nuclear@0
|
178 switch(report->type) {
|
nuclear@0
|
179 case 1: /* translation */
|
nuclear@0
|
180 {
|
nuclear@0
|
181 int x = report->motion.x;
|
nuclear@0
|
182 int y = report->motion.y;
|
nuclear@0
|
183 int z = report->motion.z;
|
nuclear@0
|
184 if(val_range != 0) {
|
nuclear@0
|
185 x = 100 * (x - min_val) / val_range - 50;
|
nuclear@0
|
186 y = 100 * (y - min_val) / val_range - 50;
|
nuclear@0
|
187 z = 100 * (z - min_val) / val_range - 50;
|
nuclear@0
|
188 }
|
nuclear@0
|
189 printf("MOTION %4d %4d %4d\n", x, y, z);
|
nuclear@0
|
190 }
|
nuclear@0
|
191 break;
|
nuclear@0
|
192
|
nuclear@0
|
193 case 2: /* rotation */
|
nuclear@0
|
194 {
|
nuclear@0
|
195 int x = report->motion.x;
|
nuclear@0
|
196 int y = report->motion.y;
|
nuclear@0
|
197 int z = report->motion.z;
|
nuclear@0
|
198 if(val_range != 0) {
|
nuclear@0
|
199 x = 100 * (x - min_val) / val_range - 50;
|
nuclear@0
|
200 y = 100 * (y - min_val) / val_range - 50;
|
nuclear@0
|
201 z = 100 * (z - min_val) / val_range - 50;
|
nuclear@0
|
202 }
|
nuclear@0
|
203 printf("ROTATE %4d %4d %4d\n", x, y, z);
|
nuclear@0
|
204 }
|
nuclear@0
|
205 break;
|
nuclear@0
|
206
|
nuclear@0
|
207 case 3:
|
nuclear@0
|
208 bnstate = report->button.state;
|
nuclear@0
|
209 fputs("B: ", stdout);
|
nuclear@0
|
210 for(int i=0; i<24; i++) {
|
nuclear@0
|
211 int bn = 23 - i;
|
nuclear@0
|
212 if(bnstate & (1 << bn)) {
|
nuclear@0
|
213 fputc('1', stdout);
|
nuclear@0
|
214 } else {
|
nuclear@0
|
215 fputc('0', stdout);
|
nuclear@0
|
216 }
|
nuclear@0
|
217 }
|
nuclear@0
|
218 fputc('\n', stdout);
|
nuclear@0
|
219 break;
|
nuclear@0
|
220
|
nuclear@0
|
221 default:
|
nuclear@0
|
222 printf("unknown event type: %d\n", (int)report->type);
|
nuclear@0
|
223 }
|
nuclear@0
|
224 }
|
nuclear@0
|
225
|
nuclear@0
|
226 cleanup:
|
nuclear@0
|
227 CloseHandle(dev_handle);
|
nuclear@0
|
228 return 0;
|
nuclear@0
|
229 }
|
nuclear@0
|
230
|
nuclear@0
|
231 static int WINAPI console_handler(DWORD s)
|
nuclear@0
|
232 {
|
nuclear@0
|
233 if(s == CTRL_C_EVENT) {
|
nuclear@0
|
234 printf("Caught interrupt signal, quitting...\n");
|
nuclear@0
|
235 done = true;
|
nuclear@0
|
236 }
|
nuclear@0
|
237 return 0;
|
nuclear@0
|
238 } |