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