1 /* xstroke.c - full-screen gesture recognition for X
2
3 Copyright 2001 Carl Worth
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
8 any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14 */
15
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <sys/types.h>
19 #include <sys/wait.h>
20 #include <sys/stat.h>
21 #include <unistd.h>
22 #include <errno.h>
23 #include <string.h>
24 #include <signal.h>
25 #include <X11/X.h>
26 /* XXX: doesn't seem like this define is the right thing to do... */
27 #define XK_MISCELLANY
28 #include <X11/keysymdef.h>
29 #include <X11/extensions/XTest.h>
30
31 #include <math.h>
32
33 #include "xstroke.h"
34 #include "rec.h"
35
36 #ifdef DMALLOC
37 #include "dmalloc.h"
38 #endif
39
40 static int xstroke_init(xstroke_t * xstroke, args_t *args);
41 static void xstroke_deinit(xstroke_t *xstroke);
42 static void root_configure_notify_cb(XEvent *xev, void *data);
43 static void key_action_cb(void *action_data, void *data);
44 static int set_keycode_and_flags(Display *dpy, action_key_data_t *key_data);
45 static void press_modifiers(xstroke_t *xstroke, int must_press_shift);
46 static void release_modifiers(xstroke_t *xstroke, int must_release_shift);
47 static void button_action_cb(void *action_data, void *data);
48 static void mode_action_cb(void *action_data, void *data);
49 static void exec_action_cb(void *action_data, void *data);
50
51 static void initialize_modifiers(xstroke_t *xstroke);
52 static void register_xlp_callbacks(xstroke_t *xstroke);
53 static void register_rec_callbacks(xstroke_t *xstroke);
54 static void register_signal_handlers(xstroke_t *xstroke);
55 static void reap_children(int signum);
56 static int keysym_to_keycode_and_column(Display *dpy, KeySym keysym,
57 KeyCode *code_ret, int *col_ret);
58 static int find_modifier_index(xstroke_t *xstroke, KeyCode keycode);
59
60 static void draw_stroke_box(xstroke_t *xstroke);
61 #ifdef XSTROKE_DEBUG_ROTATION
62 static void draw_stroke(xstroke_t *xstroke, stroke_t *stroke);
63 #endif
64
65 static args_t args;
66
main(int argc,char * argv[])67 int main(int argc, char *argv[])
68 {
69 int optind;
70 int err;
71 xstroke_t xstroke;
72
73 args_init(&args);
74 optind = args_parse(&args, argc, argv);
75 if (optind != argc) {
76 if (optind > 0) {
77 fprintf(stderr, "%s: Extraneous options:", argv[0]);
78 while (optind < argc) {
79 fprintf(stderr, " %s", argv[optind++]);
80 }
81 fprintf(stderr, "\n");
82 }
83 args_usage(argv);
84 exit(1);
85 }
86
87 err = xstroke_init(&xstroke, &args);
88 if (err) {
89 fprintf(stderr, "%s: An error occurred during xstroke_init\n", __FUNCTION__);
90 return err;
91 }
92
93 if (args.verify) {
94 rec_verify(&xstroke.rec, args.verify);
95 }
96
97 err = xlp_main_loop_start(&xstroke.xlp);
98
99 xstroke_deinit(&xstroke);
100 args_deinit(&args);
101
102 return err;
103 }
104
xstroke_init(xstroke_t * xstroke,args_t * args)105 static int xstroke_init(xstroke_t * xstroke, args_t *args)
106 {
107 int err;
108 Display *dpy;
109 struct stat stat_buf;
110 int scr;
111
112 err = xlp_init(&xstroke->xlp, args->display);
113 if (err) {
114 fprintf(stderr, "%s: An error occurred during xlp_init\n", __FUNCTION__);
115 return err;
116 }
117 dpy = xstroke->xlp.dpy;
118
119 #ifdef XSTROKE_SYNCHRONOUS
120 XSynchronize(dpy, True);
121 #endif
122 #ifdef XSTROKE_DEBUG
123 fprintf(stderr, "WARNING: xstroke compiled with XSTROKE_DEBUG defined.\n\tThis may have a negative impact on performance.\n\tOh no!! (Emphasis by Stacy)\n");
124 #endif
125
126 scr = DefaultScreen(dpy);
127 xstroke->modifier_map = XGetModifierMapping(dpy);
128
129 err = backing_init(&xstroke->backing, dpy, DefaultRootWindow(dpy),
130 DisplayWidth(dpy, scr), DisplayHeight(dpy, scr),
131 DefaultDepth(dpy, scr));
132 if (err) {
133 fprintf(stderr, "%s: An error occurred during backing_init\n", __FUNCTION__);
134 return err;
135 }
136
137 err = control_win_init(&xstroke->control_win, xstroke, args);
138 if (err) {
139 fprintf(stderr, "%s: An error occurred during control_win_init\n", __FUNCTION__);
140 return err;
141 }
142
143 err = brush_init(&xstroke->brush, &xstroke->backing);
144 if (err) {
145 fprintf(stderr, "%s: An error occurred during brush_init\n", __FUNCTION__);
146 return err;
147 }
148
149 err = stat(args->alphabet, &stat_buf);
150 if (err) {
151 if (errno != ENOENT) {
152 fprintf(stderr, "%s: Warning: failed to stat %s: %s. Falling back to %s\n",
153 __FUNCTION__, args->alphabet, strerror(errno),
154 ARGS_FALLBACK_ALPHABET);
155 }
156 free(args->alphabet);
157 args->alphabet = strdup(ARGS_FALLBACK_ALPHABET);
158 }
159 err = rec_init(&xstroke->rec, args,
160 DisplayWidth(dpy, scr), DisplayHeight(dpy, scr));
161 if (err) {
162 fprintf(stderr, "%s: An error occurred during rec_init\n", __FUNCTION__);
163 return err;
164 }
165
166 initialize_modifiers(xstroke);
167 register_xlp_callbacks(xstroke);
168 register_rec_callbacks(xstroke);
169 register_signal_handlers(xstroke);
170
171 control_win_set_mode(&xstroke->control_win, xstroke->rec.current_mode);
172
173 return 0;
174 }
175
xstroke_deinit(xstroke_t * xstroke)176 static void xstroke_deinit(xstroke_t *xstroke)
177 {
178 rec_deinit(&xstroke->rec);
179 brush_deinit(&xstroke->brush);
180 control_win_deinit(&xstroke->control_win);
181 backing_deinit(&xstroke->backing);
182 XFreeModifiermap(xstroke->modifier_map);
183 xlp_deinit(&xstroke->xlp);
184 }
185
xstroke_reload(xstroke_t * xstroke)186 void xstroke_reload(xstroke_t *xstroke)
187 {
188 int err;
189 Display *dpy = xstroke->xlp.dpy;
190 int scr = DefaultScreen(dpy);
191
192 rec_deinit(&xstroke->rec);
193 err = rec_init(&xstroke->rec, &args,
194 DisplayWidth(dpy, scr), DisplayHeight(dpy, scr));
195 if (err) {
196 fprintf(stderr, "%s: An error occurred during rec_init\n",
197 __FUNCTION__);
198 xstroke_exit(xstroke, 1);
199 }
200 }
201
xstroke_exit(xstroke_t * xstroke,int exit_code)202 void xstroke_exit(xstroke_t *xstroke, int exit_code)
203 {
204 xlp_main_loop_stop(&xstroke->xlp, exit_code);
205 }
206
xstroke_button_press(xstroke_t * xstroke,XButtonEvent * bev)207 void xstroke_button_press(xstroke_t *xstroke, XButtonEvent *bev)
208 {
209 if (args.nograb == 0) {
210 XGrabServer(xstroke->xlp.dpy);
211 }
212 backing_save(&xstroke->backing,
213 bev->x_root - xstroke->brush.image_width,
214 bev->y_root - xstroke->brush.image_height);
215 backing_save(&xstroke->backing,
216 bev->x_root + xstroke->brush.image_width,
217 bev->y_root + xstroke->brush.image_height);
218 if (args.rotation
219 && fabs(xstroke->rec.history.orientation) > ARGS_DEFAULT_ROTATION_LIMIT) {
220 draw_stroke_box(xstroke);
221 }
222 brush_draw(&xstroke->brush, bev->x_root, bev->y_root);
223
224 rec_begin_stroke(&xstroke->rec, bev->x_root, bev->y_root, bev->time);
225 }
226
xstroke_motion_notify(xstroke_t * xstroke,XMotionEvent * mev)227 void xstroke_motion_notify(xstroke_t *xstroke, XMotionEvent *mev)
228 {
229 backing_save(&xstroke->backing,
230 mev->x_root - xstroke->brush.image_width,
231 mev->y_root - xstroke->brush.image_height);
232 backing_save(&xstroke->backing,
233 mev->x_root + xstroke->brush.image_width,
234 mev->y_root + xstroke->brush.image_height);
235 brush_line_to(&xstroke->brush, mev->x_root, mev->y_root);
236
237 rec_extend_stroke(&xstroke->rec, mev->x_root, mev->y_root, mev->time);
238 }
239
xstroke_button_release(xstroke_t * xstroke,XButtonEvent * bev)240 void xstroke_button_release(xstroke_t *xstroke, XButtonEvent *bev)
241 {
242 backing_restore(&xstroke->backing);
243 rec_end_stroke(&xstroke->rec, bev->x_root, bev->y_root, bev->time);
244 if (args.nograb == 0) {
245 /* Force the restoration of root to complete before ungrabbing */
246 XSync(xstroke->xlp.dpy, False);
247 XUngrabServer(xstroke->xlp.dpy);
248 }
249
250 #ifdef XSTROKE_DEBUG_ROTATION
251 if (args.rotation) {
252 draw_stroke(xstroke, &xstroke->rec.stroke);
253 }
254 #endif
255 }
256
root_configure_notify_cb(XEvent * xev,void * data)257 static void root_configure_notify_cb(XEvent *xev, void *data)
258 {
259 XConfigureEvent *cev = &xev->xconfigure;
260 xstroke_t *xstroke = (xstroke_t *) data;
261 Display *dpy = xstroke->xlp.dpy;
262 int scr = DefaultScreen(dpy);
263
264 backing_reconfigure(&xstroke->backing, cev->width, cev->height,
265 DefaultDepth(dpy, scr));
266 rec_set_size(&xstroke->rec, cev->width, cev->height);
267 }
268
key_action_cb(void * action_data,void * data)269 static void key_action_cb(void *action_data, void *data)
270 {
271 int err;
272
273 action_key_data_t *key_data = (action_key_data_t *) action_data;
274 xstroke_t *xstroke = (xstroke_t *) data;
275 Display *dpy = xstroke->xlp.dpy;
276
277 if (key_data->keycode == 0) {
278 err = set_keycode_and_flags(dpy, key_data);
279 if (err) {
280 fprintf(stderr, "%s: ERROR: not sending keystroke\n", __FUNCTION__);
281 return;
282 }
283 }
284
285 if (key_data->flags & ACTION_KEY_IS_MODIFIER
286 && key_data->flags & ACTION_KEY_IS_PRESS) {
287
288 int modifier_index = find_modifier_index(xstroke, key_data->keycode);
289 action_key_data_t *mod = &xstroke->modifiers[modifier_index];
290
291 mod->flags ^= ACTION_KEY_IS_PRESS;
292
293 control_win_update_modifiers(&xstroke->control_win);
294 } else {
295 int must_have_shift = key_data->flags & ACTION_KEY_REQUIRES_SHIFT;
296
297 press_modifiers(xstroke, must_have_shift);
298 XTestFakeKeyEvent(dpy, key_data->keycode, True, CurrentTime);
299 XTestFakeKeyEvent(dpy, key_data->keycode, False, CurrentTime);
300 release_modifiers(xstroke, must_have_shift);
301
302 control_win_update_modifiers(&xstroke->control_win);
303 }
304 }
305
set_keycode_and_flags(Display * dpy,action_key_data_t * key_data)306 static int set_keycode_and_flags(Display *dpy, action_key_data_t *key_data)
307 {
308 int col;
309 int err;
310
311 err = (keysym_to_keycode_and_column(dpy, key_data->keysym,
312 &key_data->keycode, &col));
313 if (err) {
314 fprintf(stderr, "%s: No keycode found for keysym %ld\n",
315 __FUNCTION__, key_data->keysym);
316 return EINVAL;
317 } else {
318 if (col & 1) {
319 key_data->flags |= ACTION_KEY_REQUIRES_SHIFT;
320 } else {
321 key_data->flags &= ~ACTION_KEY_REQUIRES_SHIFT;
322 }
323 }
324
325 return 0;
326 }
327
press_modifiers(xstroke_t * xstroke,int must_press_shift)328 static void press_modifiers(xstroke_t *xstroke, int must_press_shift)
329 {
330 int i;
331 Display *dpy = xstroke->xlp.dpy;
332
333 for (i=0; i < 8; i++) {
334 if (xstroke->modifiers[i].flags & ACTION_KEY_IS_PRESS
335 || (i == ShiftMapIndex && must_press_shift)) {
336 XTestFakeKeyEvent(dpy, xstroke->modifiers[i].keycode,
337 True, CurrentTime);
338 /* XXX: The server doesn't really expect a release of
339 Caps_Lock here, (but with xkb it wants one). So,
340 with an xkb server we actually need the following
341 release of Caps_Lock
342 if (i == LockMapIndex) {
343 XTestFakeKeyEvent(dpy, xstroke->modifiers[i].keycode,
344 False, CurrentTime);
345 }
346 */
347 }
348 }
349 }
350
release_modifiers(xstroke_t * xstroke,int must_release_shift)351 static void release_modifiers(xstroke_t *xstroke, int must_release_shift)
352 {
353 int i;
354 Display *dpy = xstroke->xlp.dpy;
355
356 for (i=0; i < 8; i++) {
357 if (xstroke->modifiers[i].flags & ACTION_KEY_IS_PRESS
358 || (i == ShiftMapIndex && must_release_shift)) {
359 /* XXX: The server doesn't really expect a press of
360 Caps_Lock here, (but with xkb it wants one). So,
361 with an xkb server we actually need the following
362 release of Caps_Lock
363 if (i == LockMapIndex) {
364 XTestFakeKeyEvent(dpy, xstroke->modifiers[i].keycode,
365 True, CurrentTime);
366 }
367 */
368 XTestFakeKeyEvent(dpy, xstroke->modifiers[i].keycode,
369 False, CurrentTime);
370 }
371 if (i != LockMapIndex)
372 xstroke->modifiers[i].flags &= ~ACTION_KEY_IS_PRESS;
373 }
374 }
375
button_action_cb(void * action_data,void * data)376 static void button_action_cb(void *action_data, void *data)
377 {
378 action_button_data_t *button_data = (action_button_data_t *) action_data;
379 xstroke_t *xstroke = (xstroke_t *) data;
380 Display *dpy = xstroke->xlp.dpy;
381 XTestFakeButtonEvent(dpy, button_data->button, True, CurrentTime);
382 XTestFakeButtonEvent(dpy, button_data->button, False, CurrentTime);
383 }
384
mode_action_cb(void * action_data,void * data)385 static void mode_action_cb(void *action_data, void *data)
386 {
387 action_mode_data_t *mode_data = (action_mode_data_t *) action_data;
388 xstroke_t *xstroke = (xstroke_t *) data;
389 int i;
390
391 /* Reset all modifiers on any mode change */
392 for (i=0; i < 8; i++) {
393 xstroke->modifiers[i].flags &= ~ACTION_KEY_IS_PRESS;
394 }
395 control_win_set_mode(&xstroke->control_win, mode_data->mode);
396 }
397
exec_action_cb(void * action_data,void * data)398 static void exec_action_cb(void *action_data, void *data)
399 {
400 pid_t pid;
401 action_exec_data_t *exec_data = (action_exec_data_t *) action_data;
402
403 pid = fork();
404 if (pid == -1) {
405 fprintf(stderr, "%s: error forking before executing `%s': %s\n",
406 __FUNCTION__, exec_data->exec, strerror(errno));
407 return;
408 }
409
410 if (pid == 0) {
411 /* XXX: Rewrite this to not call /bin/sh? (to avoid extra
412 shell invocations) */
413 execlp("/bin/sh", "sh", "-c", exec_data->exec, (char *) NULL);
414 fprintf(stderr, "%s: ERROR: failed to exec `%s': %s\n",
415 __FUNCTION__, exec_data->exec, strerror(errno));
416 exit(1);
417 }
418 }
419
keysym_to_keycode_and_column(Display * dpy,KeySym keysym,KeyCode * code_ret,int * col_ret)420 static int keysym_to_keycode_and_column(Display *dpy, KeySym keysym,
421 KeyCode *code_ret, int *col_ret)
422 {
423 int col;
424 int keycode;
425 KeySym k;
426 int min_kc, max_kc;
427
428 XDisplayKeycodes(dpy, &min_kc, &max_kc);
429
430 for (keycode = min_kc; keycode <= max_kc; keycode++) {
431 col = 0;
432 while (1) {
433 k = XKeycodeToKeysym (dpy, keycode, col);
434 if (k == NoSymbol)
435 break;
436 if (k == keysym) {
437 *code_ret = keycode;
438 if (col_ret)
439 *col_ret = col;
440 return 0;
441 }
442 col++;
443 }
444 }
445 return 1;
446 }
447
find_modifier_index(xstroke_t * xstroke,KeyCode keycode)448 static int find_modifier_index(xstroke_t *xstroke, KeyCode keycode)
449 {
450 XModifierKeymap *map = xstroke->modifier_map;
451 int key;
452 int index;
453 KeyCode kc;
454
455 for (index=0; index<8; index++) {
456 for (key=0; key < map->max_keypermod; key++) {
457 kc = map->modifiermap[index * map->max_keypermod + key];
458 if (kc == keycode) {
459 return index;
460 }
461 }
462 }
463 return -1;
464 }
465
initialize_modifiers(xstroke_t * xstroke)466 static void initialize_modifiers(xstroke_t *xstroke)
467 {
468 XModifierKeymap *map;
469 int index;
470 KeyCode keycode;
471 KeySym keysym;
472 Display *dpy = xstroke->xlp.dpy;
473
474 map = XGetModifierMapping(dpy);
475 for (index=0; index<8; index++) {
476 keycode = map->modifiermap[index * map->max_keypermod];
477 keysym = XKeycodeToKeysym(dpy, keycode, 0);
478 action_keysym_data_init(&xstroke->modifiers[index], keysym, 0);
479 xstroke->modifiers[index].keycode = keycode;
480 }
481 XFreeModifiermap(map);
482 }
483
register_xlp_callbacks(xstroke_t * xstroke)484 static void register_xlp_callbacks(xstroke_t *xstroke)
485 {
486 xlp_win_register_callback(&xstroke->control_win.root_win, ConfigureNotify,
487 root_configure_notify_cb, xstroke);
488 }
489
register_rec_callbacks(xstroke_t * xstroke)490 static void register_rec_callbacks(xstroke_t *xstroke)
491 {
492 rec_register_callback(&xstroke->rec, ACTION_KEY,
493 key_action_cb, xstroke);
494 rec_register_callback(&xstroke->rec, ACTION_BUTTON,
495 button_action_cb, xstroke);
496 rec_register_callback(&xstroke->rec, ACTION_MODE,
497 mode_action_cb, xstroke);
498 rec_register_callback(&xstroke->rec, ACTION_EXEC,
499 exec_action_cb, xstroke);
500 }
501
register_signal_handlers(xstroke_t * xstroke)502 static void register_signal_handlers(xstroke_t *xstroke)
503 {
504 struct sigaction action;
505
506 sigemptyset(&action.sa_mask);
507 action.sa_handler = reap_children;
508 action.sa_flags = SA_NOCLDSTOP|SA_RESTART;
509 sigaction(SIGCHLD, &action, NULL);
510 }
511
reap_children(int signum)512 static void reap_children(int signum)
513 {
514 pid_t pid;
515
516 do {
517 pid = waitpid(-1, NULL, WNOHANG);
518 } while (pid > 0);
519 }
520
draw_stroke_box(xstroke_t * xstroke)521 static void draw_stroke_box(xstroke_t *xstroke)
522 {
523 int x = xstroke->rec.history.x;
524 int y = xstroke->rec.history.y;
525 int width = xstroke->rec.history.width * 1.2;
526 int height = xstroke->rec.history.height * 1.2;
527 double theta = xstroke->rec.history.orientation;
528 double phi = theta + M_PI_2;
529 double xm, ym;
530 double rad_cos_theta = (width / 2.0) * cos(theta);
531 double rad_sin_theta = (width / 2.0) * sin(theta);
532 int x1, y1, x2, y2;
533
534 xm = x + (height / 2.0) * cos(phi);
535 ym = y + (height / 2.0) * sin(phi);
536
537 x1 = xm - rad_cos_theta;
538 y1 = ym - rad_sin_theta;
539 x2 = xm + rad_cos_theta;
540 y2 = ym + rad_sin_theta;
541
542 backing_save(&xstroke->backing,
543 x1 - xstroke->brush.image_width,
544 y1 - xstroke->brush.image_height);
545 backing_save(&xstroke->backing,
546 x1 + xstroke->brush.image_width,
547 y1 + xstroke->brush.image_height);
548 backing_save(&xstroke->backing,
549 x2 - xstroke->brush.image_width,
550 y2 - xstroke->brush.image_height);
551 backing_save(&xstroke->backing,
552 x2 + xstroke->brush.image_width,
553 y2 + xstroke->brush.image_height);
554 brush_draw(&xstroke->brush, x1, y1);
555 brush_line_to(&xstroke->brush, x2, y2);
556 }
557
558 #ifdef XSTROKE_DEBUG_ROTATION
draw_stroke(xstroke_t * xstroke,stroke_t * stroke)559 static void draw_stroke(xstroke_t *xstroke, stroke_t *stroke)
560 {
561 int i;
562 int x, y;
563
564 fprintf(stderr, "%s: Drawing rotated stroke\n", __FUNCTION__);
565
566 x = stroke->pts[0].x;
567 y = stroke->pts[0].y;
568 backing_save(&xstroke->backing,
569 x - xstroke->brush.image_width,
570 y - xstroke->brush.image_height);
571 backing_save(&xstroke->backing,
572 x + xstroke->brush.image_width,
573 y + xstroke->brush.image_height);
574 brush_draw(&xstroke->brush, x, y);
575
576 for (i=1; i < stroke->num_pts; i++) {
577 x = stroke->pts[i].x;
578 y = stroke->pts[i].y;
579 backing_save(&xstroke->backing,
580 x - xstroke->brush.image_width,
581 y - xstroke->brush.image_height);
582 backing_save(&xstroke->backing,
583 x + xstroke->brush.image_width,
584 y + xstroke->brush.image_height);
585 brush_line_to(&xstroke->brush, x, y);
586 }
587 }
588 #endif
589