version 0.3.1
[expresskeys.git] / src-expresskeys / event_loop.c
1 /*
2  event_loop.c -- Support ExpressKeys & Touch Strips on a Wacom Intuos3 tablet.
3
4  Copyright (C) 2005-2006 - Mats Johannesson, Denis DerSarkisian
5
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  GNU General Public License for more details.
15
16  You should have received a copy of the GNU General Public License
17  along with this program; if not, write to the Free Software
18  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
19 */
20
21 #include "globals.h"
22
23 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
24  Function acts like a traffic cop for keycodes. Sends real keycodes to
25  XTestFakeKeyEvent. Interprets fake keycodes (currently keycodes standing
26  in for mouse button presses) and sends them to the appropriate function.
27  Only existing mouse buttons, defined through the driver of the active core
28  pointer, can be simulated. If you run this function in a graphical debugger,
29  the mouse _will_ be effected.
30  +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
31
32 int fake_event(Display *display, unsigned int keycode, Bool is_press,
33                unsigned long delay)
34 {
35         if ((MOUSE_BUTTON_MIN < keycode) && (keycode < MOUSE_BUTTON_MAX)) {
36                 /* mouse button code */
37                 if (be_verbose) {
38                         fprintf(stderr, "MOUSE: ");
39                 }
40
41                 int i;
42                 unsigned char button_map[MOUSE_BUTTON_MAX - MOUSE_BUTTON_MIN];
43                 for (i = 0; i < (MOUSE_BUTTON_MAX - MOUSE_BUTTON_MIN); i++) {
44                         button_map[i] = '\0';
45                 }
46
47                 XGetPointerMapping(display, button_map, sizeof(button_map));
48                 unsigned int button = keycode - MOUSE_BUTTON_MIN;
49                 unsigned int real_button = button_map[button - 1];
50
51                 if (real_button) {
52                         if (be_verbose) {
53                                 fprintf(stderr, "Real button %d found: ", button);
54                         }
55                         return XTestFakeButtonEvent(display, button, is_press, delay);
56                 } else {
57                         if (be_verbose) {
58                                 fprintf(stderr, "No real button %d found! ", button);
59                         }
60                         return 0;
61                 }
62         } else {
63                 /* keycode */
64                 if (be_verbose) {
65                         fprintf(stderr, "KEYBD: ");
66                 }
67                 return XTestFakeKeyEvent(display, keycode, is_press, delay);
68         }
69 }
70
71 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
72  Function waits perpetually for the X server to deliver information
73  about events from the input devices. Receipt of an event that we've
74  registered for ('pad' button press/release/motion and 'stylus'
75  button press/proximityin) triggers a good deal of activity in a
76  setup phase, after which we send the fake key press. A 'pad' button
77  press will always cause first a motion and then a button event.
78  That's why you'll see the focus window name being printed twice if
79  running with the verbose flag set.
80  +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
81
82 int use_events(Display *display)
83 {
84
85         char curve1buffer [CURVEMAX];
86         char namebuffer [MAXBUFFER];
87         char execbuffer [MAXBUFFER];
88
89         snprintf(curve1buffer, CURVEMAX, "0 0 100 100");
90         snprintf(namebuffer, MAXBUFFER, "dummy");
91
92         XClassHint *class_hint;
93
94         Window focus_window;
95         int focus_state;
96
97         Window root, parent;
98         Window *children;
99         unsigned int num_children;
100
101         struct program *p;
102         int in_list;
103
104         int elder_rotation = 4097; /* Set the Touch Strip state histories to */
105         int old_rotation = 4097;   /* their max values. This is to ensure */
106         int elder_throttle = 4097; /* that no action is taken on the very */
107         int old_throttle = 4097;   /* first touch before a real history is */
108         int rotation;              /* recorded. Bottom is 4097. Top is 2 */
109         int throttle;
110
111         int i = 0;
112         int *button_index = 0;
113
114         XDeviceMotionEvent *motion;
115         XDeviceButtonEvent *button;
116
117         XEvent Event;
118
119         while(1) {
120
121                 XNextEvent(display, &Event);
122
123 /* We've registered for 'stylus' ProximityIn events but will in fact also
124    see the ProximityOut events. Therefore list exactly what should be
125    reacted on, so that we at least can skip one focus window lookup */
126
127                 if ((Event.type == button_press_type)
128                         || (Event.type == button_release_type)
129                         || (Event.type == motion_type)
130                         ||  (Event.type == proximity_in_type)) {
131
132                         class_hint = XAllocClassHint(); /* Memory allocation. Free it later! */
133
134                         focus_window = None;
135                         in_list = 0;
136
137 /* Locate which window that currently has the focus and get its list of
138    related windows. Then pull its ClassHint into our allocated structure */
139
140                         XGetInputFocus(display, &focus_window, &focus_state);
141                         XQueryTree(display, focus_window, &root, &parent, &children, &num_children);
142                         XGetClassHint(display, focus_window, class_hint);
143
144 /* If the class hint (aka WM_CLASS) contains no class string we free the
145    allocated memory for each structure member and get the ClassHint of the
146    window parent, if it has one. Observe that we must skip the root window */
147
148                         if ((!class_hint->res_class) && (parent) && (focus_window != root)) {
149                                 XFree(class_hint->res_class);
150                                 XFree(class_hint->res_name);
151                                 XGetClassHint(display, parent, class_hint);
152                         }
153
154 /* If the root window had the focus, or if the active window or its parent
155    had no class string at all, we use the "default" program definition
156    from the read in configuration file when evaluating the event. Otherwise
157    we start scanning for a match between the class strings in our list and
158    the found window class. Set a flag if a match is encountered */
159
160                         if ((focus_window == root) || (class_hint->res_class == NULL)) {
161                                 p = default_program;
162                         } else {
163                                 for (p = external_list; p < external_list + num_list; p++) {
164                                         if (strcmp (class_hint->res_class, p->class_name) == 0) {
165                                                 in_list = 1;
166                                                 break;
167                                         }
168                                 }
169                         }
170
171 /* Any program not found in our configuration gets the "default" treatment */
172
173                         if (!in_list) {
174                                 p = default_program;
175                         }
176
177 /* The allocated memory for the ClassHint structure, and each of its
178    members, must be freed here. Also, the call to XQueryTree to get a
179    list of related windows might have allocated memory for child entries.
180    It must be released as well */
181
182                         XFree(class_hint->res_class);
183                         XFree(class_hint->res_name);
184                         XFree(class_hint);
185                         if (children) XFree((char *)children);
186
187                         if (be_verbose) {
188                                 fprintf(stderr, "PGR FOCUS = %s\n", p->class_name);
189                         }
190
191 /* Finally start to look at the actual event. Stylus button events come first */
192
193                         if ((Event.type == button_press_type)
194                                 || (Event.type == proximity_in_type)) {
195                                 if (stylus1_info) {
196                                         button = (XDeviceButtonEvent *) &Event;
197                                         if (button->deviceid == stylus1_info->id) {
198                                                 if (in_list || (strcmp ("default", p->class_name) == 0)) {
199                                                         if (strcmp (namebuffer, p->class_name) != 0) {
200                                                                 if (strcmp (curve1buffer, p->stylus1_presscurve) != 0) {
201                                                                         snprintf(curve1buffer, CURVEMAX, "%s", p->stylus1_presscurve);
202                                                                         snprintf(execbuffer, MAXBUFFER, "xsetwacom set %s PressCurve %s", stylus1_name, curve1buffer);
203                                                                         if ((system(execbuffer)) == NON_VALID) {
204                                                                                 fprintf(stderr, "Failed to fork a shell for xsetwacom to set PressCurve!\n");
205                                                                         } else {
206                                                                                 if (be_verbose) {
207                                                                                         fprintf(stderr, "ST1 PRCURVE = \"%s\"\n", p->stylus1_presscurve);
208                                                                                 }
209                                                                         /* Save current program name with focus as a compare history */
210                                                                         snprintf(namebuffer, MAXBUFFER, "%s", p->class_name);
211                                                                         }
212                                                                 }
213                                                         }
214                                                 }
215                                         }
216                                 }
217                         }
218
219 /* Next we look for Touch Strip events */
220
221                         if ((Event.type == motion_type) && (!is_graphire4)) {
222                                 if (pad1_info) {
223
224                                         if (p->handle_touch) {
225                                                 motion = (XDeviceMotionEvent *) &Event;
226                                                 if (motion->deviceid == pad1_info->id) {
227                                                         rotation = motion->axis_data[3];
228                                                         throttle = motion->axis_data[4];
229
230 /* As can be analyzed with Frederic Lepied's excellent xinput-1.2 program
231    the touch strip data comes in on axis 3 and 4 (left and right strips).
232    The pad device never uses x-axis [0], y-axis [1] or wheel [5]. The value
233    is always 0 there. The pressure [2], rotation [3] and throttle [4] all
234    rest with a value of 1. Touch strips send data about the finger position
235    in 13 steps. Furthest down is 4097 while the top is 2. Template:
236    4097, 2049, 1025, 513, 257, 129, 65, 33, 17, 9, 5, 3, 2. We don't care
237    about those numbers per se (for now at least ;-), but just stick them
238    in history buffers. The present value is then compared to an old and
239    an even older one to determine direction of the finger movement. Observe
240    that when we finally send a fake keypress, it has two parts - true and
241    false. It corresponds with a key press and a key release. Order is key ;-) */
242
243 /* Left Touch Strip */
244
245                                                         if (rotation > 1) {
246                                                                 if ((rotation < old_rotation) && (old_rotation <= elder_rotation)) {
247                                                                         if (p->l_touch_up) {
248                                                                                 fake_event(display, p->l_touch_up, True, CurrentTime);
249                                                                                 if (be_verbose) {
250                                                                                         fprintf(stderr, "LTCHUP = %d dn\n", p->l_touch_up);
251                                                                                 }
252                                                                                 if (p->l_touch_up_plus) {
253                                                                                         fake_event(display, p->l_touch_up_plus, True, CurrentTime);
254                                                                                         if (be_verbose) {
255                                                                                                 fprintf(stderr, "LTCHUP+ = %d dn\n", p->l_touch_up_plus);
256                                                                                         }
257                                                                                         fake_event(display, p->l_touch_up_plus, False, CurrentTime);
258                                                                                         if (be_verbose) {
259                                                                                                 fprintf(stderr, "LTCHUP+ = %d up\n", p->l_touch_up_plus);
260                                                                                         }
261                                                                                 }
262                                                                                 fake_event(display, p->l_touch_up, False, CurrentTime);
263                                                                                 if (be_verbose) {
264                                                                                         fprintf(stderr, "LTCHUP = %d up\n", p->l_touch_up);
265                                                                                 }
266                                                                         }
267                                                                 } else if ((rotation > old_rotation) && (old_rotation >= elder_rotation)) {
268                                                                         if (p->l_touch_down) {
269                                                                                 fake_event(display, p->l_touch_down, True, CurrentTime);
270                                                                                 if (be_verbose) {
271                                                                                         fprintf(stderr, "LTCHDN = %d dn\n", p->l_touch_down);
272                                                                                 }
273                                                                                 if (p->l_touch_down_plus) {
274                                                                                         fake_event(display, p->l_touch_down_plus, True, CurrentTime);
275                                                                                         if (be_verbose) {
276                                                                                                 fprintf(stderr, "LTCHDN+ = %d dn\n", p->l_touch_down_plus);
277                                                                                         }
278                                                                                         fake_event(display, p->l_touch_down_plus, False, CurrentTime);
279                                                                                         if (be_verbose) {
280                                                                                                 fprintf(stderr, "LTCHDN+ = %d up\n", p->l_touch_down_plus);
281                                                                                         }
282                                                                                 }
283                                                                                 fake_event(display, p->l_touch_down, False, CurrentTime);
284                                                                                 if (be_verbose) {
285                                                                                         fprintf(stderr, "LTCHDN = %d up\n", p->l_touch_down);
286                                                                                 }
287                                                                         }
288                                                                 }
289                                                                 elder_rotation = old_rotation;
290                                                                 old_rotation = rotation;
291                                                         }
292
293 /* Right Touch Strip */
294
295                                                         if (throttle > 1) {
296                                                                 if ((throttle < old_throttle) && (old_throttle <= elder_throttle)) {
297                                                                         if (p->r_touch_up) {
298                                                                                 fake_event(display, p->r_touch_up, True, CurrentTime);
299                                                                                 if (be_verbose) {
300                                                                                         fprintf(stderr, "RTCHUP = %d dn\n", p->r_touch_up);
301                                                                                 }
302                                                                                 if (p->r_touch_up_plus) {
303                                                                                         fake_event(display, p->r_touch_up_plus, True, CurrentTime);
304                                                                                         if (be_verbose) {
305                                                                                                 fprintf(stderr, "RTCHUP+ = %d dn\n", p->r_touch_up_plus);
306                                                                                         }
307                                                                                         fake_event(display, p->r_touch_up_plus, False, CurrentTime);
308                                                                                         if (be_verbose) {
309                                                                                                 fprintf(stderr, "RTCHUP+ = %d up\n", p->r_touch_up_plus);
310                                                                                         }
311                                                                                 }
312                                                                                 fake_event(display, p->r_touch_up, False, CurrentTime);
313                                                                                 if (be_verbose) {
314                                                                                         fprintf(stderr, "RTCHUP = %d up\n", p->r_touch_up);
315                                                                                 }
316                                                                         }
317                                                                 } else if ((throttle > old_throttle) && (old_throttle >= elder_throttle)) {
318                                                                         if (p->r_touch_down) {
319                                                                                 fake_event(display, p->r_touch_down, True, CurrentTime);
320                                                                                 if (be_verbose) {
321                                                                                         fprintf(stderr, "RTCHDN = %d dn\n", p->r_touch_down);
322                                                                                 }
323                                                                                 if (p->r_touch_down_plus) {
324                                                                                         fake_event(display, p->r_touch_down_plus, True, CurrentTime);
325                                                                                         if (be_verbose) {
326                                                                                                 fprintf(stderr, "RTCHDN+ = %d dn\n", p->r_touch_down_plus);
327                                                                                         }
328                                                                                         fake_event(display, p->r_touch_down_plus, False, CurrentTime);
329                                                                                         if (be_verbose) {
330                                                                                                 fprintf(stderr, "RTCHDN+ = %d up\n", p->r_touch_down_plus);
331                                                                                         }
332                                                                                 }
333                                                                                 fake_event(display, p->r_touch_down, False, CurrentTime);
334                                                                                 if (be_verbose) {
335                                                                                         fprintf(stderr, "RTCHDN = %d up\n", p->r_touch_down);
336                                                                                 }
337                                                                         }
338                                                                 }
339                                                                 elder_throttle = old_throttle;
340                                                                 old_throttle = throttle;
341                                                         }
342                                                 }
343                                         }
344                                 }
345                         }
346
347 /* Now see if the event concerned the pad buttons. Not much to talk about.
348    We follow the configuration definitions, and handle a pen if requested
349    to do so. Wacom have numbered the buttons 9, 10, 11, 12 on the left side
350    and 13, 14, 15, 16 on the right. Template:
351
352 Left ExpressKey Pad
353 ------------
354 |  |   |   |            Wacom Intuos3 defaults are:
355 |  | 9 | T |
356 |11|---| O |            Key 9  = (left) Shift   = keycode 50
357 |  |10 | U |            Key 10 = (left) Alt     = keycode 64
358 |------| C |            Key 11 = (left) Control = keycode 37
359 |  12  | H |            Key 12 = Space          = keycode 65
360 ------------
361
362 Right ExpressKey Pad
363 ------------
364 |   |   |  |            Wacom Intuos3 defaults are:
365 | T |13 |  |
366 | O |---|15|            Key 13 = (left) Shift   = keycode 50
367 | U |14 |  |            Key 14 = (left) Alt     = keycode 64
368 | C |------|            Key 15 = (left) Control = keycode 37
369 | H |  16  |            Key 16 = Space          = keycode 65
370 ------------
371 */
372
373 /* Pad Button Press */
374
375                         if (Event.type == button_press_type) {
376                                 if (pad1_info) {
377
378                                         button = (XDeviceButtonEvent *) &Event;
379                                         if (button->deviceid == pad1_info->id) {
380
381                                                 if (button->button >= 9) {
382                                                         button_index = &p->key_9;
383
384                                                         for (i = 9; i < button->button; i++) {
385                                                                 button_index++;
386                                                                 button_index++;
387                                                         }
388
389                                                         if (*button_index == TOGGLE_STYLUS1) {
390                                                                 if (stylus1_info) {
391                                                                         if (be_verbose) {
392                                                                                 fprintf(stderr, "BTN %d = %d dn\n", button->button, *button_index);
393                                                                         }
394                                                                         toggle_stylus1_mode(display, stylus1_name);
395                                                                 }
396                                                         } else if ((*button_index >= STYLUS1_CURVE_DOWNWARD)
397                                                                                 && (*button_index <= STYLUS1_CURVE_UPWARD)
398                                                                                 && (stylus1_info)) {
399                                                                 if (be_verbose) {
400                                                                         fprintf(stderr, "BTN %d = %d dn\n", button->button, *button_index);
401                                                                 }
402                                                                 call_xsetwacom(*button_index);
403
404                                                         } else {
405                                                                 if (*button_index) {
406                                                                         fake_event(display, *button_index, True, CurrentTime );
407                                                                         if (be_verbose) {
408                                                                                 fprintf(stderr, "BTN %d = %d dn\n", button->button, *button_index);
409                                                                         }
410                                                                 }
411                                                                 button_index++;
412                                                                 if (*button_index) {
413                                                                         fake_event(display, *button_index, True, CurrentTime );
414                                                                         if (be_verbose) {
415                                                                                 fprintf(stderr, "BTN+ %d = %d dn\n", button->button, *button_index);
416                                                                         }
417                                                                 }
418                                                         }
419
420 /*Graphire4 hack*/              } else if (p->handle_touch) {
421                                                         if (button->button == 4) {
422                                                                 if (p->l_touch_up) {
423                                                                         fake_event(display, p->l_touch_up, True, CurrentTime);
424                                                                         if (be_verbose) {
425                                                                                 fprintf(stderr, "SWHLUP = %d dn\n", p->l_touch_up);
426                                                                         }
427                                                                 }
428                                                                 if (p->l_touch_up_plus) {
429                                                                         fake_event(display, p->l_touch_up_plus, True, CurrentTime);
430                                                                         if (be_verbose) {
431                                                                                 fprintf(stderr, "SWHLUP+ = %d dn\n", p->l_touch_up_plus);
432                                                                         }
433                                                                 }
434                                                         }
435                                                         if (button->button == 5) {
436                                                                 if (p->l_touch_down) {
437                                                                         fake_event(display, p->l_touch_down, True, CurrentTime);
438                                                                         if (be_verbose) {
439                                                                                 fprintf(stderr, "SWHLDN = %d dn\n", p->l_touch_down);
440                                                                         }
441                                                                 }
442                                                                 if (p->l_touch_down_plus) {
443                                                                         fake_event(display, p->l_touch_down_plus, True, CurrentTime);
444                                                                         if (be_verbose) {
445                                                                                 fprintf(stderr, "SWHLDN+ = %d dn\n", p->l_touch_down_plus);
446                                                                         }
447                                                                 }
448                                                         }
449                                                 }
450                                         }
451                                 }
452                         }
453
454 /* Pad Button Release */
455
456                         if (Event.type == button_release_type) {
457                                 if (pad1_info) {
458
459                                         button = (XDeviceButtonEvent *) &Event;
460                                         if (button->deviceid == pad1_info->id) {
461
462                                                 if (button->button >= 9) {
463                                                         button_index = &p->key_9;
464
465                                                         for (i = 9; i < button->button; i++) {
466                                                                 button_index++;
467                                                                 button_index++;
468                                                         }
469
470                                                         if (*button_index == TOGGLE_STYLUS1) {
471                                                                 if (stylus1_info) {
472                                                                         if (be_verbose) {
473                                                                                 fprintf(stderr, "BTN %d = %d up\n", button->button, *button_index);
474                                                                         }
475                                                                 }
476                                                         } else if ((*button_index >= STYLUS1_CURVE_DOWNWARD)
477                                                                                 && (*button_index <= STYLUS1_CURVE_UPWARD)
478                                                                                 && (stylus1_info)) {
479                                                                 if (be_verbose) {
480                                                                         fprintf(stderr, "BTN %d = %d up\n", button->button, *button_index);
481                                                                 }
482                                                         } else {
483                                                                 button_index++;
484                                                                 if (*button_index) {
485                                                                         fake_event(display, *button_index, False, CurrentTime );
486                                                                         if (be_verbose) {
487                                                                                 fprintf(stderr, "BTN+ %d = %d up\n", button->button, *button_index);
488                                                                         }
489                                                                 }
490                                                                 button_index--;
491                                                                 if (*button_index) {
492                                                                         fake_event(display, *button_index, False, CurrentTime );
493                                                                         if (be_verbose) {
494                                                                                 fprintf(stderr, "BTN %d = %d up\n", button->button, *button_index);
495                                                                         }
496                                                                 }
497                                                         }
498
499 /*Graphire4 hack*/              } else if (p->handle_touch) {
500                                                         if (button->button == 4) {
501                                                                 if (p->l_touch_up_plus) {
502                                                                         fake_event(display, p->l_touch_up_plus, False, CurrentTime);
503                                                                         if (be_verbose) {
504                                                                                 fprintf(stderr, "SWHLUP+ = %d up\n", p->l_touch_up_plus);
505                                                                         }
506                                                                 }
507                                                                 if (p->l_touch_up) {
508                                                                         fake_event(display, p->l_touch_up, False, CurrentTime);
509                                                                         if (be_verbose) {
510                                                                                 fprintf(stderr, "SWHLUP = %d up\n", p->l_touch_up);
511                                                                         }
512                                                                 }
513                                                         }
514                                                         if (button->button == 5) {
515                                                                 if (p->l_touch_down_plus) {
516                                                                         fake_event(display, p->l_touch_down_plus, False, CurrentTime);
517                                                                         if (be_verbose) {
518                                                                                 fprintf(stderr, "SWHLDN+ = %d up\n", p->l_touch_down_plus);
519                                                                         }
520                                                                 }
521                                                                 if (p->l_touch_down) {
522                                                                         fake_event(display, p->l_touch_down, False, CurrentTime);
523                                                                         if (be_verbose) {
524                                                                                 fprintf(stderr, "SWHLDN = %d up\n", p->l_touch_down);
525                                                                         }
526                                                                 }
527                                                         }
528                                                 }
529                                         }
530                                 }
531                         }
532                 }
533         }
534         exit(EXIT_OK);
535 }
536
537 /* End code */
538