62107854c26ad07b61ce27027a4a1586a09d7020
[expresskeys.git] / src-expresskeys / get_device.c
1 /*
2  expresskeys - Support ExpressKeys, Touch Strips, Scroll Wheel on Wacom tablets.
3
4  Copyright (C) 2005-2007 - Mats Johannesson
5
6  register_events() based on test.c 1996 by Frederic Lepied (xinput-1.2)
7
8  This program is free software; you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation; either version 2 of the License, or
11  (at your option) any later version.
12
13  This program is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  GNU General Public License for more details.
17
18  You should have received a copy of the GNU General Public License along
19  with this program; if not, write to the Free Software Foundation, Inc.,
20  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22
23 #include <stdio.h> /* NULL, FILE, fgets, popen, pclose, snprintf */
24 #include <string.h> /* strlen, strstr, strcspn, strncmp, strcmp */
25 #include <ctype.h> /* isdigit, tolower */
26 #include <stdlib.h> /* atoi */
27 #include <X11/extensions/XInput.h>
28
29 #include "defines.h"
30
31 /* Globals: */
32
33 /* State flags: */
34 int have_pad;
35 int ok_xsetwacom;
36
37 /* Numbers we get from registering our interest in certain events: */
38 int button_press_type;
39 int button_release_type;
40 int motion_type;
41 int proximity_in_type;
42 int proximity_out_type;
43
44 /* Used in many situations as identification, for math and for flow control: */
45 const int bee = 5;
46 const int i3 = 4;
47 const int i3s = 3;
48 const int g4 = 2;
49 const int g4b = 1;
50 const int nop = 0;
51
52 /* The large structure emanating from XListInputDevices: */
53 XDeviceInfo* xdevice_list;
54
55 /* Arrays used as storage before transfer to structures, and for quick loops: */
56 XDevice* pad_xdevice[MAXMODEL][MAXPAD];
57 XDevice* stylus_xdevice[MAXSTYLUS][MAXMODEL * MAXPAD];
58
59 unsigned long int* pad_id[MAXMODEL][MAXPAD];
60 unsigned long int* stylus_id[MAXSTYLUS][MAXMODEL * MAXPAD];
61
62 const char* pad_name[MAXMODEL][MAXPAD];
63 const char* stylus_name[MAXSTYLUS][MAXMODEL * MAXPAD];
64
65 unsigned char* stylus_mode[MAXSTYLUS][MAXMODEL * MAXPAD];
66
67 /* 'Internal' Globals: */
68
69 /* Counting to keep track, and store in the right array element: */
70 static int pad_num;
71 static int stylus_num;
72
73 /* Recycled placeholder: */
74 static XDevice* tmp_device;
75
76 /* Externals: */
77
78 extern int screen;
79 extern Display* display;
80
81 extern const char* our_prog_name;
82
83 extern const char* pad_string;
84 extern const char* stylus_string;
85
86 extern const char* user_pad;
87 extern const char* user_stylus1;
88 extern const char* user_stylus2;
89
90 static int get_xsetwacom_version(void)
91 {
92         FILE* execfp = NULL;
93         int major, minor, release, ret;
94         char read_buffer[MAXBUFFER];
95
96         major = minor = release = 0;
97
98         if ((execfp = popen("xsetwacom -V", "r")) != NULL) {
99                 fgets(read_buffer, MAXBUFFER, execfp);
100                 if (pclose(execfp) != NON_VALID) {
101                         ret = sscanf(read_buffer, "%i.%i.%i", &major, &minor,
102                                      &release);
103                         if (ret == 3)
104                                 return major * 100 + minor * 10 + release;
105                 }
106         }
107
108         return 0;
109 }
110
111 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
112  This function uses the "popen" command which creates a pipe, forks and
113  invokes a shell where xsetwacom can be run. First action is to ensure that
114  version 0.0.7 or greater of xsetwacom is present (ie linuxwacom-0.7.5-2
115  where the option GetTabletID was introduced). Thereafter we match the tablet
116  decimal number string against known tablet numbers. A full table can be
117  found in src/xdrv/wcmUSB.c of the linuxwacom sources (Hex numbers).
118  +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
119
120 static int identify_device(char* device_name)
121 {
122         const char* cintiq_20wsx = "197"; /* 0xC5 */
123         const char* cintiq_21UX = "63"; /* 0x3F */
124         const char* i3_6x8 = "177"; /* 0xB1 */
125         const char* i3_9x12 = "178"; /* 0xB2 */
126         const char* i3_12x12 = "179"; /* 0xB3 */
127         const char* i3_12x19 = "180"; /* 0xB4 */
128         const char* i3_6x11 = "181"; /* 0xB5 */
129         const char* i3s_4x5 = "176"; /* 0xB0 */
130         const char* i3s_4x6 = "183"; /* 0xB7 */
131         const char* g4_4x5 = "21"; /* 0x15 */
132         const char* g4_6x8 = "22"; /* 0x16 */
133         const char* g4b_6x8 = "129"; /* 0x81 */
134
135 /* Minimum xsetwacom version we can use is 0.0.7 */
136         const int min_xsetwacom = 7;
137
138         char read_buffer[MAXBUFFER];
139         char write_buffer[MAXBUFFER];
140
141         int len = 0;
142         int ok_value = 0;
143
144         FILE* execfp = NULL;
145         read_buffer[0] = '\0';
146
147         ok_xsetwacom = 0;
148         if (get_xsetwacom_version() > min_xsetwacom)
149                 ok_xsetwacom = 1;
150
151 /* linuxwacom-0.7.7-3 changed GetTabletID to plain TabletID. Later, support
152  for both strings were introduced. We follow the same pattern here, defaulting
153  to the new way, should the old format disappear in a linuxwacom future: */
154         if (ok_xsetwacom) {
155                 read_buffer[0] = '\0';
156                 snprintf(write_buffer,MAXBUFFER, "xsetwacom get %s TabletID",
157                                                                 device_name);
158                 if ((execfp = popen(write_buffer, "r")) != NULL) {
159                         fgets(read_buffer, MAXBUFFER, execfp);
160                         if (((pclose(execfp)) != NON_VALID)
161                         && (isdigit(read_buffer[0]))) {
162                                 ok_value = 1;
163                         } else {
164                                 read_buffer[0] = '\0';
165                                 snprintf(write_buffer,MAXBUFFER,
166                                 "xsetwacom get %s GetTabletID", device_name);
167                                 if ((execfp = popen(write_buffer, "r"))
168                                                                 != NULL) {
169                                         fgets(read_buffer, MAXBUFFER, execfp);
170                                         if (((pclose(execfp)) != NON_VALID)
171                                         && (isdigit(read_buffer[0]))) {
172                                                 ok_value = 1;
173                                         }
174                                 }
175                         }
176                 }
177
178                 if (ok_value) {
179                         len = strcspn(read_buffer, " \t\n");
180                         if ((strncmp(read_buffer, cintiq_20wsx, len)) == 0) {
181                                 return bee;
182                         }
183                         if ((strncmp(read_buffer, cintiq_21UX, len)) == 0) {
184                                 return i3;
185                         }
186                         if (((strncmp(read_buffer, i3_6x8, len)) == 0)
187                                 || ((strncmp(read_buffer, i3_9x12, len)) == 0)
188                                 || ((strncmp(read_buffer, i3_12x12, len)) == 0)
189                                 || ((strncmp(read_buffer, i3_12x19, len)) == 0)
190                                 || ((strncmp(read_buffer, i3_6x11, len)) == 0)){
191                                 return i3;
192                         }
193                         if (((strncmp(read_buffer, i3s_4x5, len)) == 0)
194                                 || ((strncmp(read_buffer, i3s_4x6, len)) == 0)){
195                                 return i3s;
196                         }
197                         if (((strncmp(read_buffer, g4_4x5, len)) == 0)
198                                 || ((strncmp(read_buffer, g4_6x8, len)) == 0)) {
199                                 return g4;
200                         }
201                         if ((strncmp(read_buffer, g4b_6x8, len)) == 0) {
202                                 return g4b;
203                         }
204                 }
205         }
206
207 /* We must treat all tablets as 'padless' if xsetwacom is missing or too old: */
208         if (!ok_xsetwacom) {
209                 have_pad = 0;
210         }
211         return nop;
212
213 }
214
215 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
216  Start looking for supported event types. The scope should be the root window
217  (ie everywhere). We're interested in motion events and proximity in/out for
218  the touch strips, and button press/release for the pad buttons. For the
219  styli we ask about button press and proximity in. Having found the info
220  we ask the X server to keep us continuously notified about these events:
221  +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
222
223 static int register_events(const char* name)
224 {
225         int i;
226         int count = 0;
227
228         XInputClassInfo* ip;
229         Window root_win;
230
231         root_win = RootWindow(display, screen);
232
233         if (name == pad_string) {
234                 XEventClass event_list[5];
235                 if (tmp_device->num_classes > 0) {
236                         for (ip = tmp_device->classes,
237                                 i = 0; i < tmp_device->num_classes; ip++, i++) {
238                                 switch (ip->input_class) {
239
240                                 case ButtonClass:
241                                 DeviceButtonPress(tmp_device,
242                                         button_press_type, event_list[count]);
243                                 count++;
244                                 DeviceButtonRelease(tmp_device,
245                                         button_release_type, event_list[count]);
246                                 count++;
247                                 break;
248
249                                 case ValuatorClass:
250                                 DeviceMotionNotify(tmp_device,
251                                         motion_type, event_list[count]);
252                                 count++;
253                                 ProximityIn(tmp_device,
254                                         proximity_in_type, event_list[count]);
255                                 count++;
256                                 ProximityOut(tmp_device,
257                                         proximity_out_type, event_list[count]);
258                                 count++;
259                                 break;
260
261                                 default:
262                                 break;
263                                 }
264                         }
265                 }
266                 if (XSelectExtensionEvent(display, root_win,
267                                         event_list, count)) {
268                         return 0;
269                 }
270                 return count;
271         }
272
273         if (name == stylus_string) {
274                 XEventClass event_list[2];
275                 if (tmp_device->num_classes > 0) {
276                         for (ip = tmp_device->classes,
277                                 i = 0; i < tmp_device->num_classes; ip++, i++) {
278                                 switch (ip->input_class) {
279
280                                 case ButtonClass:
281                                 DeviceButtonPress(tmp_device,
282                                         button_press_type, event_list[count]);
283                                 count++;
284                                 break;
285
286                                 case ValuatorClass:
287                                 ProximityIn(tmp_device,
288                                         proximity_in_type, event_list[count]);
289                                 count++;
290                                 break;
291
292                                 default:
293                                 break;
294                                 }
295                         }
296                 }
297                 if (XSelectExtensionEvent(display, root_win,
298                                         event_list, count)) {
299                         return 0;
300                 }
301                 return count;
302         }
303         return 0;
304 }
305
306 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
307  Find next empty element in the pad-array:
308  +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
309
310 static int count_pad(int model)
311 {
312         int i;
313         for (i = 0; i < MAXPAD; i++) {
314                 if (!pad_xdevice[model][i]) {
315                         break;
316                 }
317         }
318         return i;
319 }
320
321 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
322  Find next empty element in the stylus-array:
323  +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
324
325 static int count_stylus(int model)
326 {
327         int i, j;
328         pad_num = 0;
329
330         for (j = 0; j < MAXPAD && pad_num == 0; j++) {
331                 for (i = 0; i < MAXSTYLUS; i++) {
332                         if (!stylus_xdevice[i][model * MAXPAD + j]) {
333                                 pad_num = 1;
334                                 break;
335                         }
336                 }
337         }
338         pad_num = model * MAXPAD + --j;
339         return i;
340 }
341
342 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
343  Open a pad-device and start collecting data:
344  +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
345
346 static void follow_pad(XDeviceInfo* xdevice_list, int number)
347 {
348         int model;
349
350         if ((tmp_device = XOpenDevice(display, xdevice_list[number].id))) {
351                 if (register_events(pad_string)) {
352                         model = identify_device(xdevice_list[number].name);
353                         pad_num = count_pad(model);
354                         if (pad_num < MAXPAD) {
355                                 pad_xdevice[model][pad_num] = tmp_device;
356                                 pad_id[model][pad_num] = &tmp_device->device_id;
357                                 pad_name[model][pad_num] = xdevice_list[number]
358                                                                         .name;
359                                 have_pad = 1;
360                         } else {
361                                 XFree(tmp_device);
362                         }
363                 }
364         }
365
366 }
367
368 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
369  Open a stylus-device and start collecting data:
370  +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
371
372 static void follow_stylus(XDeviceInfo* xdevice_list, int number)
373 {
374         int i;
375         int model;
376
377         XValuatorInfoPtr valuator;
378         XAnyClassPtr anyclass;
379
380         if ((tmp_device = XOpenDevice(display, xdevice_list[number].id))) {
381                 if (register_events(stylus_string)) {
382                         model = identify_device(xdevice_list[number].name);
383                         stylus_num = count_stylus(model);
384                         if ((pad_num < model * MAXPAD + MAXPAD)
385                         && (stylus_num < MAXSTYLUS)) {
386                                 stylus_xdevice[stylus_num][pad_num]= tmp_device;
387                                 stylus_id[stylus_num][pad_num] =
388                                                         &tmp_device->device_id;
389                                 stylus_name[stylus_num][pad_num] =
390                                                 xdevice_list[number].name;
391                                 anyclass = (XAnyClassPtr)
392                                         (xdevice_list[number].inputclassinfo);
393                                 for (i = 0; i < xdevice_list[number]
394                                                         .num_classes; i++) {
395                                         if (anyclass->class == ValuatorClass) {
396                                                 valuator =
397                                                 (XValuatorInfoPtr)anyclass;
398                                                 stylus_mode[stylus_num][pad_num]
399                                                         = &valuator->mode;
400                                         }
401                                         anyclass = (XAnyClassPtr)
402                                         ((char*)anyclass + anyclass->length);
403                                 }
404                         } else {
405                                 XFree(tmp_device);
406                         }
407                 }
408         }
409
410 }
411
412 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
413  First processing of an extension-device's name-string:
414  +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
415
416 static int check_name(char* read_buffer, char* write_buffer, int len)
417 {
418         int i;
419
420         if (len >= MAXBUFFER) {
421                 len = MAXBUFFER - 1;
422         }
423
424         for (i = 0; i < len; i++) {
425                 write_buffer[i] = tolower(read_buffer[i]);
426         }
427         write_buffer[i] = '\0';
428
429         if ((strstr(write_buffer, pad_string) !=NULL)
430                 || (strstr(write_buffer, stylus_string) !=NULL)) {
431                 return 1;
432         }
433         return 0;
434
435 }
436
437 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
438  Find all extension-devices containing the strings 'pad'/'stylus'. If the
439  user has specified which pad/stylus(es) to use on the command line, we
440  ignore all others:
441  +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
442
443 void get_device_info()
444 {
445         int i;
446         int len;
447         int found;
448         int nr_devices;
449
450         char read_buffer[MAXBUFFER];
451         char write_buffer[MAXBUFFER];
452
453         const char* user_dummy = "not_specified";
454         if ((user_pad) || (user_stylus1)) {
455                 if (user_pad) {
456                         pad_string = user_pad;
457                 } else {
458                         pad_string = user_dummy;
459                 }
460                 if (user_stylus1) {
461                         stylus_string = user_stylus1;
462                 } else {
463                         stylus_string = user_dummy;
464                 }
465         }
466
467         xdevice_list = XListInputDevices(display, &nr_devices);
468
469         for(i = 0; i < nr_devices; i++) {
470                 if (xdevice_list[i].use == IsXExtensionDevice) {
471                         len = strlen(xdevice_list[i].name);
472                         snprintf(read_buffer, MAXBUFFER, "%s", xdevice_list[i]
473                                                                         .name);
474                         if (check_name(read_buffer, write_buffer, len)) {
475                                 found = 0;
476                                 if (user_pad) {
477                                         if (strcmp(read_buffer, pad_string)
478                                                                         == 0) {
479                                                 found = 1;
480                                         }
481                                 } else if (strstr(write_buffer, pad_string)) {
482                                         found = 1;
483                                 }
484                                 if ((strstr(write_buffer, pad_string))
485                                                                 && (found)) {
486                                         follow_pad(xdevice_list, i);
487                                 }
488                                 if (user_stylus1) {
489                                         if (strcmp(read_buffer, stylus_string)
490                                                                         == 0) {
491                                                 found = 1;
492                                         }
493                                 } else if (strstr(write_buffer,
494                                                         stylus_string)) {
495                                         found = 1;
496                                 }
497                                 if ((strstr(write_buffer, stylus_string))
498                                                                 && (found)) {
499                                         follow_stylus(xdevice_list, i);
500                                         if (user_stylus2) {
501                                                 stylus_string = user_stylus2;
502                                         }
503                                 }
504                         }
505                 }
506         }
507
508 }
509
510 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
511  Compare device names specified on the command line with found devices:
512  +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
513
514 void check_user(FILE* errorfp)
515 {
516         int userpad = 0;
517         int userstylus1 = 0;
518         int userstylus2 = 0;
519
520         int i, j;
521
522         if (user_pad) {
523                 for (i = 0; i < MAXMODEL && userpad == 0; i++) {
524                         for (j = 0; j < MAXPAD; j++) {
525                                 if (pad_name[i][j]) {
526                                         if (strcmp(user_pad,
527                                                         pad_name[i][j]) == 0) {
528                                                 userpad = 1;
529                                                 break;
530                                         }
531                                 }
532                         }
533                 }
534                 if (!userpad) {
535                         exit_on_error(errorfp,
536                         "\n%s ERROR: User named device \"%s\" not found!\n",
537                                                 our_prog_name, user_pad);
538                 }
539         }
540
541         if (user_stylus1) {
542                 for (i = 0; i < MAXSTYLUS && userstylus1 == 0; i++) {
543                         for (j = 0; j < MAXMODEL * MAXPAD; j++){
544                                 if (stylus_name[i][j]) {
545                                         if (strcmp(user_stylus1,
546                                                 stylus_name[i][j]) == 0) {
547                                                 userstylus1 = 1;
548                                                 break;
549                                         }
550                                 }
551                         }
552                 }
553                 if (!userstylus1) {
554                         exit_on_error(errorfp,
555                         "\n%s ERROR: User named device \"%s\" not found!\n",
556                                                 our_prog_name, user_stylus1);
557                 }
558         }
559
560         if (user_stylus2) {
561                 for (i = 0; i < MAXSTYLUS && userstylus2 == 0; i++) {
562                         for (j = 0; j < MAXMODEL * MAXPAD; j++){
563                                 if (stylus_name[i][j]) {
564                                         if (strcmp(user_stylus2,
565                                                 stylus_name[i][j]) == 0) {
566                                                 userstylus2 = 1;
567                                                 break;
568                                         }
569                                 }
570                         }
571                 }
572                 if (!userstylus2) {
573                         exit_on_error(errorfp,
574                         "\n%s ERROR: User named device \"%s\" not found!\n",
575                                                 our_prog_name, user_stylus2);
576                 }
577         }
578
579 }
580
581 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
582  XListInputDevices doesn't provide the devices in a predictable order, so the
583  stylus array could be out of 'sync' with reality. For example, one tablet
584  might get two styli, while another tablet of the same model gets none.
585  The crude heuristics used below is: "If the next tablet of this model has
586  an empty 'slot' zero, and the previous tablet more than 'slot' zero occupied,
587  transfer the last stylus from the previous tablet to the next tablet". The
588  user can also list the styli differently in xorg.conf to correct errors:
589  +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
590
591 void shuffle_styli()
592 {
593         int i, j;
594
595         for (i = MAXSTYLUS - 1; i < MAXSTYLUS; i++) {
596                 for (j = 0; j < MAXMODEL * MAXPAD; j++){
597                         if (stylus_xdevice[i][j]) {
598                                 if (((j+1)-((j/MAXPAD)*MAXPAD)) < MAXPAD) {
599                                         if ((pad_xdevice[j/MAXPAD][(j+1)-
600                                                         ((j/MAXPAD)*MAXPAD)])
601                                                 && (!stylus_xdevice[0][j+1])) {
602                                                 stylus_xdevice[0][j+1]
603                                                         = stylus_xdevice[i][j];
604                                                 stylus_xdevice[i][j] = NULL;
605                                                 stylus_id[0][j+1]
606                                                         = stylus_id[i][j];
607                                                 stylus_id[i][j] = NULL;
608                                                 stylus_name[0][j+1]
609                                                         = stylus_name[i][j];
610                                                 stylus_name[i][j] = NULL;
611                                                 stylus_mode[0][j+1]
612                                                         = stylus_mode[i][j];
613                                                 stylus_mode[i][j] = NULL;
614                                         }
615                                 }
616                         }
617                 }
618         }
619
620 }
621
622 /* End Code */
623