* newer Xorg servers changed 'use' field on the input devices struct
[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 /* newer Xorg servers changed the "use" field on the list of input devices */
438 #ifdef IsXExtensionKeyboard
439 #define EXTENSION_DEVICE IsXExtensionKeyboard
440 #else
441 #define EXTENSION_DEVICE IsXExtensionDevice
442 #endif
443
444 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
445  Find all extension-devices containing the strings 'pad'/'stylus'. If the
446  user has specified which pad/stylus(es) to use on the command line, we
447  ignore all others:
448  +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
449
450 void get_device_info()
451 {
452         int i;
453         int len;
454         int found;
455         int nr_devices;
456
457         char read_buffer[MAXBUFFER];
458         char write_buffer[MAXBUFFER];
459
460         const char* user_dummy = "not_specified";
461         if ((user_pad) || (user_stylus1)) {
462                 if (user_pad) {
463                         pad_string = user_pad;
464                 } else {
465                         pad_string = user_dummy;
466                 }
467                 if (user_stylus1) {
468                         stylus_string = user_stylus1;
469                 } else {
470                         stylus_string = user_dummy;
471                 }
472         }
473
474         xdevice_list = XListInputDevices(display, &nr_devices);
475
476         for(i = 0; i < nr_devices; i++) {
477                 if (xdevice_list[i].use == EXTENSION_DEVICE) {
478                         len = strlen(xdevice_list[i].name);
479                         snprintf(read_buffer, MAXBUFFER, "%s", xdevice_list[i]
480                                                                         .name);
481                         if (check_name(read_buffer, write_buffer, len)) {
482                                 found = 0;
483                                 if (user_pad) {
484                                         if (strcmp(read_buffer, pad_string)
485                                                                         == 0) {
486                                                 found = 1;
487                                         }
488                                 } else if (strstr(write_buffer, pad_string)) {
489                                         found = 1;
490                                 }
491                                 if ((strstr(write_buffer, pad_string))
492                                                                 && (found)) {
493                                         follow_pad(xdevice_list, i);
494                                 }
495                                 if (user_stylus1) {
496                                         if (strcmp(read_buffer, stylus_string)
497                                                                         == 0) {
498                                                 found = 1;
499                                         }
500                                 } else if (strstr(write_buffer,
501                                                         stylus_string)) {
502                                         found = 1;
503                                 }
504                                 if ((strstr(write_buffer, stylus_string))
505                                                                 && (found)) {
506                                         follow_stylus(xdevice_list, i);
507                                         if (user_stylus2) {
508                                                 stylus_string = user_stylus2;
509                                         }
510                                 }
511                         }
512                 }
513         }
514
515 }
516
517 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
518  Compare device names specified on the command line with found devices:
519  +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
520
521 void check_user(FILE* errorfp)
522 {
523         int userpad = 0;
524         int userstylus1 = 0;
525         int userstylus2 = 0;
526
527         int i, j;
528
529         if (user_pad) {
530                 for (i = 0; i < MAXMODEL && userpad == 0; i++) {
531                         for (j = 0; j < MAXPAD; j++) {
532                                 if (pad_name[i][j]) {
533                                         if (strcmp(user_pad,
534                                                         pad_name[i][j]) == 0) {
535                                                 userpad = 1;
536                                                 break;
537                                         }
538                                 }
539                         }
540                 }
541                 if (!userpad) {
542                         exit_on_error(errorfp,
543                         "\n%s ERROR: User named device \"%s\" not found!\n",
544                                                 our_prog_name, user_pad);
545                 }
546         }
547
548         if (user_stylus1) {
549                 for (i = 0; i < MAXSTYLUS && userstylus1 == 0; i++) {
550                         for (j = 0; j < MAXMODEL * MAXPAD; j++){
551                                 if (stylus_name[i][j]) {
552                                         if (strcmp(user_stylus1,
553                                                 stylus_name[i][j]) == 0) {
554                                                 userstylus1 = 1;
555                                                 break;
556                                         }
557                                 }
558                         }
559                 }
560                 if (!userstylus1) {
561                         exit_on_error(errorfp,
562                         "\n%s ERROR: User named device \"%s\" not found!\n",
563                                                 our_prog_name, user_stylus1);
564                 }
565         }
566
567         if (user_stylus2) {
568                 for (i = 0; i < MAXSTYLUS && userstylus2 == 0; i++) {
569                         for (j = 0; j < MAXMODEL * MAXPAD; j++){
570                                 if (stylus_name[i][j]) {
571                                         if (strcmp(user_stylus2,
572                                                 stylus_name[i][j]) == 0) {
573                                                 userstylus2 = 1;
574                                                 break;
575                                         }
576                                 }
577                         }
578                 }
579                 if (!userstylus2) {
580                         exit_on_error(errorfp,
581                         "\n%s ERROR: User named device \"%s\" not found!\n",
582                                                 our_prog_name, user_stylus2);
583                 }
584         }
585
586 }
587
588 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
589  XListInputDevices doesn't provide the devices in a predictable order, so the
590  stylus array could be out of 'sync' with reality. For example, one tablet
591  might get two styli, while another tablet of the same model gets none.
592  The crude heuristics used below is: "If the next tablet of this model has
593  an empty 'slot' zero, and the previous tablet more than 'slot' zero occupied,
594  transfer the last stylus from the previous tablet to the next tablet". The
595  user can also list the styli differently in xorg.conf to correct errors:
596  +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
597
598 void shuffle_styli()
599 {
600         int i, j;
601
602         for (i = MAXSTYLUS - 1; i < MAXSTYLUS; i++) {
603                 for (j = 0; j < MAXMODEL * MAXPAD; j++){
604                         if (stylus_xdevice[i][j]) {
605                                 if (((j+1)-((j/MAXPAD)*MAXPAD)) < MAXPAD) {
606                                         if ((pad_xdevice[j/MAXPAD][(j+1)-
607                                                         ((j/MAXPAD)*MAXPAD)])
608                                                 && (!stylus_xdevice[0][j+1])) {
609                                                 stylus_xdevice[0][j+1]
610                                                         = stylus_xdevice[i][j];
611                                                 stylus_xdevice[i][j] = NULL;
612                                                 stylus_id[0][j+1]
613                                                         = stylus_id[i][j];
614                                                 stylus_id[i][j] = NULL;
615                                                 stylus_name[0][j+1]
616                                                         = stylus_name[i][j];
617                                                 stylus_name[i][j] = NULL;
618                                                 stylus_mode[0][j+1]
619                                                         = stylus_mode[i][j];
620                                                 stylus_mode[i][j] = NULL;
621                                         }
622                                 }
623                         }
624                 }
625         }
626
627 }
628
629 /* End Code */
630