7c500681ccf3b36867ef79220b40885a7f6b6a6f
[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 device. Receipt of an event that we've
74  registered for (button press/release and motion) triggers a good deal
75  of activity in a setup phase, after which we send the fake key press.
76  A button press or release will always cause first a motion and then 
77  a button event. That's why you'll see the focus window name being
78  printed twice if running with the verbose flag set.
79  +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
80
81 int use_events(Display *display)
82 {
83
84         XClassHint *class_hint;
85
86         Window focus_window;
87         int focus_state;
88
89         Window root, parent;
90         Window *children;
91         unsigned int num_children;
92
93         struct program *p;
94         int in_list;
95
96         int elder_rotation = 4097; /* Set the Touch Strip state histories to */
97         int old_rotation = 4097;   /* their max values. This is to ensure */
98         int elder_throttle = 4097; /* that no action is taken on the very */
99         int old_throttle = 4097;   /* first touch before a real history is */
100         int rotation;              /* recorded. Bottom is 4097. Top is 2 */
101         int throttle;
102
103         int i = 0;
104         int *button_index = 0;
105
106         XDeviceMotionEvent *motion;
107         XDeviceButtonEvent *button;
108
109         XEvent Event;
110
111         while(1) {
112
113                 XNextEvent(display, &Event);
114
115                 class_hint = XAllocClassHint(); /* Memory allocation. Free it later! */
116
117                 focus_window = None;
118                 in_list = 0;
119
120 /* Locate which window that currently has the focus and get its list of
121    related windows. Then pull its ClassHint into our allocated structure */
122
123                 XGetInputFocus(display, &focus_window, &focus_state);
124                 XQueryTree(display, focus_window, &root, &parent, &children, &num_children);
125                 XGetClassHint(display, focus_window, class_hint);
126
127 /* If the class hint (aka WM_CLASS) contains no class string we free the
128    allocated memory for each structure member and get the ClassHint of the
129    window parent, if it has one. Observe that we must skip the root window */
130
131                 if ((!class_hint->res_class) && (parent) && (focus_window != root)) {
132                         XFree(class_hint->res_class);
133                         XFree(class_hint->res_name);
134                         XGetClassHint(display, parent, class_hint);
135                 }
136
137 /* If the root window had the focus, or if the active window or its parent
138    had no class string at all, we use the top ("default") program definition
139    from the read in configuration file when evaluating the event. Otherwise
140    we start scanning for a match between the class strings in our list and
141    the found window class. Set a flag if a match is encountered */
142
143                 if ((focus_window == root) || (class_hint->res_class == NULL)) {
144                         p = external_list;
145                 } else {
146                         for (p = external_list; p < external_list + num_list; p++)
147                                 if (strcmp (class_hint->res_class, p->class_name) == 0) {
148                                         in_list = 1;
149                                         break;
150                                 }
151                         }
152
153 /* Any program not found in our configuration gets the "default" treatment */
154
155                 if (!in_list) {
156                         p = external_list;
157                 }
158
159 /* The allocated memory for the ClassHint structure, and each of its
160    members, must be freed here. Also, the call to XQueryTree to get a
161    list of related windows might have allocated memory for child entries.
162    It must be released as well */
163                 
164                 XFree(class_hint->res_class);
165                 XFree(class_hint->res_name);
166                 XFree(class_hint);
167                 if (children) XFree((char *)children);
168
169                 if (be_verbose) {
170                         fprintf(stderr, "PGR FOCUS = %s\n", p->class_name);
171                 }
172
173 /* Finally start to look at the actual event. Touch Strips come first */
174
175                 if (Event.type == motion_type) {
176
177                         if (p->handle_touch) {
178
179                         motion = (XDeviceMotionEvent *) &Event;
180
181                                 rotation = motion->axis_data[3];
182                                 throttle = motion->axis_data[4];
183
184 /* As can be analyzed with Frederic Lepied's excellent xinput-1.2 program
185    the touch strip data comes in on axis 3 and 4 (left and right strips).
186    The pad device never uses x-axis [0], y-axis [1] or wheel [5]. The value
187    is always 0 there. The pressure [2], rotation [3] and throttle [4] all
188    rest with a value of 1. Touch strips send data about the finger position
189    in 13 steps. Furthest down is 4097 while the top is 2. Template:
190    4097, 2049, 1025, 513, 257, 129, 65, 33, 17, 9, 5, 3, 2. We don't care
191    about those numbers per se (for now at least ;-), but just stick them
192    in history buffers. The present value is then compared to an old and
193    an even older one to determine direction of the finger movement. Observe
194    that when we finally send a fake keypress, it has two parts - true and
195    false. It corresponds with a key press and a key release. Order is key ;-) */
196
197 /* Left Touch Strip */
198
199                                 if (rotation > 1) {
200                                         if ((rotation < old_rotation) && (old_rotation <= elder_rotation)) {
201                                                 if (p->l_touch_up) {
202                                                         fake_event(display, p->l_touch_up, True, CurrentTime);
203                                                         if (be_verbose) {
204                                                                 fprintf(stderr, "LTCHUP = %d dn\n", p->l_touch_up);
205                                                         }
206                                                         if (p->l_touch_up_plus) {
207                                                                 fake_event(display, p->l_touch_up_plus, True, CurrentTime);
208                                                                 if (be_verbose) {
209                                                                         fprintf(stderr, "LTCHUP+ = %d dn\n", p->l_touch_up_plus);
210                                                                 }
211                                                                 fake_event(display, p->l_touch_up_plus, False, CurrentTime);
212                                                                 if (be_verbose) {
213                                                                         fprintf(stderr, "LTCHUP+ = %d up\n", p->l_touch_up_plus);
214                                                                 }
215                                                         }
216                                                         fake_event(display, p->l_touch_up, False, CurrentTime);
217                                                         if (be_verbose) {
218                                                                 fprintf(stderr, "LTCHUP = %d up\n", p->l_touch_up);
219                                                         }
220                                                 }
221                                         } else if ((rotation > old_rotation) && (old_rotation >= elder_rotation)) {
222                                                 if (p->l_touch_down) {
223                                                         fake_event(display, p->l_touch_down, True, CurrentTime);
224                                                         if (be_verbose) {
225                                                                 fprintf(stderr, "LTCHDN = %d dn\n", p->l_touch_down);
226                                                         }
227                                                         if (p->l_touch_down_plus) {
228                                                                 fake_event(display, p->l_touch_down_plus, True, CurrentTime);
229                                                                 if (be_verbose) {
230                                                                         fprintf(stderr, "LTCHDN+ = %d dn\n", p->l_touch_down_plus);
231                                                                 }
232                                                                 fake_event(display, p->l_touch_down_plus, False, CurrentTime);
233                                                                 if (be_verbose) {
234                                                                         fprintf(stderr, "LTCHDN+ = %d up\n", p->l_touch_down_plus);
235                                                                 }
236                                                         }
237                                                         fake_event(display, p->l_touch_down, False, CurrentTime);
238                                                         if (be_verbose) {
239                                                                 fprintf(stderr, "LTCHDN = %d up\n", p->l_touch_down);
240                                                         }
241                                                 }
242                                         }
243                                 elder_rotation = old_rotation;
244                                 old_rotation = rotation;
245                                 }
246
247 /* Right Touch Strip */
248
249                                 if (throttle > 1) {
250                                         if ((throttle < old_throttle) && (old_throttle <= elder_throttle)) {
251                                                 if (p->r_touch_up) {
252                                                         fake_event(display, p->r_touch_up, True, CurrentTime);
253                                                         if (be_verbose) {
254                                                                 fprintf(stderr, "RTCHUP = %d dn\n", p->r_touch_up);
255                                                         }
256                                                         if (p->r_touch_up_plus) {
257                                                                 fake_event(display, p->r_touch_up_plus, True, CurrentTime);
258                                                                 if (be_verbose) {
259                                                                         fprintf(stderr, "RTCHUP+ = %d dn\n", p->r_touch_up_plus);
260                                                                 }
261                                                                 fake_event(display, p->r_touch_up_plus, False, CurrentTime);
262                                                                 if (be_verbose) {
263                                                                         fprintf(stderr, "RTCHUP+ = %d up\n", p->r_touch_up_plus);
264                                                                 }
265                                                         }
266                                                         fake_event(display, p->r_touch_up, False, CurrentTime);
267                                                         if (be_verbose) {
268                                                                 fprintf(stderr, "RTCHUP = %d up\n", p->r_touch_up);
269                                                         }
270                                                 }
271                                         } else if ((throttle > old_throttle) && (old_throttle >= elder_throttle)) {
272                                                 if (p->r_touch_down) {
273                                                         fake_event(display, p->r_touch_down, True, CurrentTime);
274                                                         if (be_verbose) {
275                                                                 fprintf(stderr, "RTCHDN = %d dn\n", p->r_touch_down);
276                                                         }
277                                                         if (p->r_touch_down_plus) {
278                                                                 fake_event(display, p->r_touch_down_plus, True, CurrentTime);
279                                                                 if (be_verbose) {
280                                                                         fprintf(stderr, "RTCHDN+ = %d dn\n", p->r_touch_down_plus);
281                                                                 }
282                                                                 fake_event(display, p->r_touch_down_plus, False, CurrentTime);
283                                                                 if (be_verbose) {
284                                                                         fprintf(stderr, "RTCHDN+ = %d up\n", p->r_touch_down_plus);
285                                                                 }
286                                                         }
287                                                         fake_event(display, p->r_touch_down, False, CurrentTime);
288                                                         if (be_verbose) {
289                                                                 fprintf(stderr, "RTCHDN = %d up\n", p->r_touch_down);
290                                                         }
291                                                 }
292                                         }
293                                 elder_throttle = old_throttle;
294                                 old_throttle = throttle;
295                                 }
296                         }
297                 }
298
299 /* Now see if the event concerned the pad buttons. Not much to talk about.
300    We follow the configuration definitions, and handle a pen if requested
301    to do so. Wacom have numbered the buttons 9, 10, 11, 12 on the left side
302    and 13, 14, 15, 16 on the right. Template:
303
304 Left ExpressKey Pad
305 ------------ 
306 |  |   |   |            Wacom Intuos3 defaults are:
307 |  | 9 | T |
308 |11|---| O |            Key 9  = (left) Shift   = keycode 50
309 |  |10 | U |            Key 10 = (left) Alt     = keycode 64
310 |------| C |            Key 11 = (left) Control = keycode 37
311 |  12  | H |            Key 12 = Space          = keycode 65
312 ------------
313
314 Right ExpressKey Pad
315 ------------ 
316 |   |   |  |            Wacom Intuos3 defaults are:
317 | T |13 |  |
318 | O |---|15|            Key 13 = (left) Shift   = keycode 50
319 | U |14 |  |            Key 14 = (left) Alt     = keycode 64
320 | C |------|            Key 15 = (left) Control = keycode 37
321 | H |  16  |            Key 16 = Space          = keycode 65
322 ------------
323 */
324
325 /* Pad Button Press */
326
327                 if (Event.type == button_press_type) {
328
329                         button = (XDeviceButtonEvent *) &Event;
330
331                         button_index = &p->key_9;
332
333                         for (i = 9; i < button->button; i++) {
334                                 button_index++;
335                                 button_index++;
336                         }
337
338                         if (*button_index == TOGGLE_PEN) {
339                                 if (handle_pen) {
340                                         if (be_verbose) {
341                                                 fprintf(stderr, "BTN %d = %d dn\n", button->button, *button_index);
342                                         }
343                                         toggle_pen_mode(display, pen_name);
344                                 }
345                         } else if ((*button_index >= PEN_CURVE_DOWNWARD) &&
346                                                 (*button_index <= PEN_CURVE_UPWARD)) {
347                                 if (be_verbose) {
348                                         fprintf(stderr, "BTN %d = %d dn\n", button->button, *button_index);
349                                 }
350                                 call_xsetwacom(*button_index);
351                         } else {
352                                 if (*button_index) {
353                                         fake_event(display, *button_index, True, CurrentTime );
354                                         if (be_verbose) {
355                                                 fprintf(stderr, "BTN %d = %d dn\n", button->button, *button_index);
356                                         }
357                                 }
358                                 button_index++;
359                                 if (*button_index) {
360                                         fake_event(display, *button_index, True, CurrentTime );
361                                         if (be_verbose) {
362                                                 fprintf(stderr, "BTN+ %d = %d dn\n", button->button, *button_index);
363                                         }
364                                 }
365                         }
366                 }
367
368 /* Pad Button Release */
369
370                 if (Event.type == button_release_type) {
371
372                         button = (XDeviceButtonEvent *) &Event;
373
374                         button_index = &p->key_9;
375
376                         for (i = 9; i < button->button; i++) {
377                                 button_index++;
378                                 button_index++;
379                         }
380
381                         if (*button_index == TOGGLE_PEN) {
382                                 if (be_verbose) {
383                                         fprintf(stderr, "BTN %d = %d up\n", button->button, *button_index);
384                                 }
385                         } else if ((*button_index >= PEN_CURVE_DOWNWARD) &&
386                                                 (*button_index <= PEN_CURVE_UPWARD)) {
387                                 if (be_verbose) {
388                                         fprintf(stderr, "BTN %d = %d up\n", button->button, *button_index);
389                                 }
390                         } else {
391                                 button_index++;
392                                 if (*button_index) {
393                                         fake_event(display, *button_index, False, CurrentTime );
394                                         if (be_verbose) {
395                                                 fprintf(stderr, "BTN+ %d = %d up\n", button->button, *button_index);
396                                         }
397                                 }
398                                 button_index--;
399                                 if (*button_index) {
400                                         fake_event(display, *button_index, False, CurrentTime );
401                                         if (be_verbose) {
402                                                 fprintf(stderr, "BTN %d = %d up\n", button->button, *button_index);
403                                         }
404                                 }
405                         }
406                 }
407         }
408         exit(EXIT_OK);
409 }
410
411 /* End code */
412