version 0.3.0
[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) {
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                                                 button_index = &p->key_9;
381
382                                                 for (i = 9; i < button->button; i++) {
383                                                         button_index++;
384                                                         button_index++;
385                                                 }
386
387                                                 if (*button_index == TOGGLE_STYLUS1) {
388                                                         if (stylus1_info) {
389                                                                 if (be_verbose) {
390                                                                         fprintf(stderr, "BTN %d = %d dn\n", button->button, *button_index);
391                                                                 }
392                                                                 toggle_stylus1_mode(display, stylus1_name);
393                                                         }
394                                                 } else if ((*button_index >= STYLUS1_CURVE_DOWNWARD)
395                                                                         && (*button_index <= STYLUS1_CURVE_UPWARD)
396                                                                         && (stylus1_info)) {
397                                                         if (be_verbose) {
398                                                                 fprintf(stderr, "BTN %d = %d dn\n", button->button, *button_index);
399                                                         }
400                                                         call_xsetwacom(*button_index);
401
402                                                 } else {
403                                                         if (*button_index) {
404                                                                 fake_event(display, *button_index, True, CurrentTime );
405                                                                 if (be_verbose) {
406                                                                         fprintf(stderr, "BTN %d = %d dn\n", button->button, *button_index);
407                                                                 }
408                                                         }
409                                                         button_index++;
410                                                         if (*button_index) {
411                                                                 fake_event(display, *button_index, True, CurrentTime );
412                                                                 if (be_verbose) {
413                                                                         fprintf(stderr, "BTN+ %d = %d dn\n", button->button, *button_index);
414                                                                 }
415                                                         }
416                                                 }
417                                         }
418                                 }
419                         }
420
421 /* Pad Button Release */
422
423                         if (Event.type == button_release_type) {
424                                 if (pad1_info) {
425
426                                         button = (XDeviceButtonEvent *) &Event;
427                                         if (button->deviceid == pad1_info->id) {
428                                                 button_index = &p->key_9;
429
430                                                 for (i = 9; i < button->button; i++) {
431                                                         button_index++;
432                                                         button_index++;
433                                                 }
434
435                                                 if (*button_index == TOGGLE_STYLUS1) {
436                                                         if (stylus1_info) {
437                                                                 if (be_verbose) {
438                                                                         fprintf(stderr, "BTN %d = %d up\n", button->button, *button_index);
439                                                                 }
440                                                         }
441                                                 } else if ((*button_index >= STYLUS1_CURVE_DOWNWARD)
442                                                                         && (*button_index <= STYLUS1_CURVE_UPWARD)
443                                                                         && (stylus1_info)) {
444                                                         if (be_verbose) {
445                                                                 fprintf(stderr, "BTN %d = %d up\n", button->button, *button_index);
446                                                         }
447                                                 } else {
448                                                         button_index++;
449                                                         if (*button_index) {
450                                                                 fake_event(display, *button_index, False, CurrentTime );
451                                                                 if (be_verbose) {
452                                                                         fprintf(stderr, "BTN+ %d = %d up\n", button->button, *button_index);
453                                                                 }
454                                                         }
455                                                         button_index--;
456                                                         if (*button_index) {
457                                                                 fake_event(display, *button_index, False, CurrentTime );
458                                                                 if (be_verbose) {
459                                                                         fprintf(stderr, "BTN %d = %d up\n", button->button, *button_index);
460                                                                 }
461                                                         }
462                                                 }
463                                         }
464                                 }
465                         }
466                 }
467         }
468         exit(EXIT_OK);
469 }
470
471 /* End code */
472