1 /* xlp_event.c - event handling functions
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 <unistd.h>
19 #include <pthread.h>
20 #include <errno.h>
21 #include <signal.h>
22 #include <limits.h>
23 #include <sys/time.h>
24 #include <X11/Xlib.h>
25 
26 #include "xlp.h"
27 
28 #ifdef DMALLOC
29 #include "dmalloc.h"
30 #endif
31 
32 static long event_mask_for_type(int type);
33 static int add_callback(xlp_t *xlp, xlp_callback_t *cb);
34 static void remove_timeout_and_adjust_others(xlp_t *xlp, xlp_timeout_t *timeout);
35 
36 static void *xlp_x_event_thread_start(void *arg);
37 static void *xlp_timeout_thread_start(void *arg);
38 
xlp_init(xlp_t * xlp,char * display)39 int xlp_init(xlp_t *xlp, char *display)
40 {
41     int i;
42 
43     pthread_mutex_init(&xlp->dpy_mutex, NULL);
44 
45     for (i=0; i < LASTEvent; i++) {
46 	xlp->callbacks[i] = NULL;
47     }
48 
49     pthread_mutex_init(&xlp->timeouts_mutex, NULL);
50     pthread_cond_init(&xlp->timeouts_cond, NULL);
51 
52     pthread_mutex_lock(&xlp->timeouts_mutex);
53     xlp->timeouts = NULL;
54     pthread_mutex_unlock(&xlp->timeouts_mutex);
55 
56     xlp->dpy = XOpenDisplay(display);
57     if (xlp->dpy == NULL) {
58 	fprintf(stderr, "%s: An error occurred trying to open display %s\n",
59 		__FUNCTION__, display);
60 	return EIO;
61     }
62 
63     return 0;
64 }
65 
xlp_deinit(xlp_t * xlp)66 void xlp_deinit(xlp_t *xlp)
67 {
68     int i;
69     xlp_callback_t *cb, *prev_cb;
70     xlp_timeout_t *to, *prev_to;
71 
72     pthread_mutex_destroy(&xlp->dpy_mutex);
73 
74     for (i=0; i < LASTEvent; i++) {
75 	cb = xlp->callbacks[i];
76 	while (cb) {
77 	    prev_cb = cb;
78 	    cb = cb->next;
79 	    xlp_callback_deinit(prev_cb);
80 	    /* alloc'ed in xlp_register_window_callback */
81 	    free(prev_cb);
82 	}
83 	xlp->callbacks[i] = NULL;
84     }
85 
86     pthread_mutex_lock(&xlp->timeouts_mutex);
87     to = xlp->timeouts;
88     while (to) {
89 	prev_to = to;
90 	to = to->next;
91 	xlp_timeout_deinit(prev_to);
92 	/* alloc'ed in xlp_register_timeout */
93 	free(prev_to);
94     }
95     xlp->timeouts = NULL;
96     pthread_mutex_unlock(&xlp->timeouts_mutex);
97 
98     pthread_cond_destroy(&xlp->timeouts_cond);
99     pthread_mutex_destroy(&xlp->timeouts_mutex);
100 
101     XCloseDisplay(xlp->dpy);
102     xlp->dpy = NULL;
103 }
104 
xlp_register_window_callback(xlp_t * xlp,xlp_win_t * xlp_win,Window window,int type,xlp_cb_fun_t cb_fun,void * data)105 int xlp_register_window_callback(xlp_t *xlp, xlp_win_t *xlp_win,
106 				 Window window, int type,
107 				 xlp_cb_fun_t cb_fun, void *data)
108 {
109     xlp_callback_t *cb;
110 
111     /* free'ed in xlp_deinit */
112     cb = malloc(sizeof(xlp_callback_t));
113     if (cb == NULL) {
114 	fprintf(stderr, "%s: Out of memory\n", __FUNCTION__);
115 	return ENOMEM;
116     }
117 
118     xlp_callback_init(cb, xlp_win, window, type,
119 		      &xlp_win->xlp->dpy_mutex,
120 		      cb_fun, data);
121     add_callback(xlp, cb);
122 
123     xlp_win->event_mask |= event_mask_for_type(type);
124     XSelectInput(xlp_win->dpy, xlp_win->window, xlp_win->event_mask);
125 
126     return 0;
127 }
128 
xlp_register_callback(xlp_t * xlp,xlp_win_t * xlp_win,int type,xlp_cb_fun_t cb_fun,void * data)129 int xlp_register_callback(xlp_t *xlp, xlp_win_t *xlp_win, int type,
130 			  xlp_cb_fun_t cb_fun, void *data)
131 {
132     return xlp_register_window_callback(xlp, xlp_win, xlp_win->window, type,
133 					cb_fun, data);
134 }
135 
136 
xlp_register_timeout(xlp_t * xlp,long delay_ms,xlp_to_fun_t to_fun,void * data,struct timeval * start_tv_ret)137 int xlp_register_timeout(xlp_t *xlp, long delay_ms,
138 			 xlp_to_fun_t to_fun, void *data,
139 			 struct timeval *start_tv_ret)
140 {
141     xlp_timeout_t *to;
142 
143 
144     /* free'ed in xlp_deinit or remove_timeout_and_adjust_others */
145     to = malloc(sizeof(xlp_timeout_t));
146     if (to == NULL) {
147 	fprintf(stderr, "%s: Out of memory\n", __FUNCTION__);
148 	return ENOMEM;
149     }
150     xlp_timeout_init(to, delay_ms, &xlp->dpy_mutex, to_fun, data);
151 
152     if (start_tv_ret) {
153 	*start_tv_ret = to->start_tv;
154     }
155 
156     pthread_mutex_lock(&xlp->timeouts_mutex);
157 
158     /* Don't care about the order of timeouts, place them at the head
159        of the list where it is easy. */
160     to->next = xlp->timeouts;
161     xlp->timeouts = to;
162 
163     pthread_cond_signal(&xlp->timeouts_cond);
164 
165     pthread_mutex_unlock(&xlp->timeouts_mutex);
166 
167     return 0;
168 }
169 
add_callback(xlp_t * xlp,xlp_callback_t * cb)170 static int add_callback(xlp_t *xlp, xlp_callback_t *cb)
171 {
172     xlp_callback_t *last;
173 
174     cb->next = NULL;
175 
176     /* New callbacks go to the end of the list */
177     last = xlp->callbacks[cb->type];
178     if (last == NULL) {
179 	xlp->callbacks[cb->type] = cb;
180     } else {
181 	while (last->next)
182 	    last = last->next;
183 	last->next = cb;
184     }
185 
186     return 0;
187 }
188 
xlp_main_loop_start(xlp_t * xlp)189 int xlp_main_loop_start(xlp_t *xlp)
190 {
191     int err;
192     sigset_t set;
193 
194     xlp->exit_requested = 0;
195     xlp->exit_code = 0;
196 
197     sigemptyset(&set);
198     sigaddset(&set, SIGALRM);
199     sigaddset(&set, SIGUSR1);
200     err = pthread_sigmask(SIG_BLOCK, &set, NULL);
201     if (err) {
202 	fprintf(stderr, "%s: Error calling pthread_sigmask.\n", __FUNCTION__);
203 	return err;
204     }
205 
206     err = pthread_create(&xlp->timeout_thread, NULL,
207 			 xlp_timeout_thread_start, xlp);
208     if (err) {
209 	fprintf(stderr, "%s: Error starting timeout_thread.\n",	__FUNCTION__);
210 	return err;
211     }
212     pthread_detach(xlp->timeout_thread);
213 
214     return (int) xlp_x_event_thread_start(xlp);
215 }
216 
xlp_main_loop_stop(xlp_t * xlp,int exit_code)217 void xlp_main_loop_stop(xlp_t *xlp, int exit_code)
218 {
219     xlp->exit_requested = 1;
220     xlp->exit_code = exit_code;
221 }
222 
xlp_x_event_thread_start(void * arg)223 static void *xlp_x_event_thread_start(void *arg)
224 {
225     xlp_t *xlp = (xlp_t *) arg;
226     struct timespec ts = { 0, 100000};
227     while (! xlp->exit_requested)  {
228 	XEvent xev;
229 	xlp_callback_t *cb;
230 
231 	pthread_mutex_lock(&xlp->dpy_mutex);
232 	while (! XPending(xlp->dpy)) {
233 	    pthread_mutex_unlock(&xlp->dpy_mutex);
234 	    nanosleep(&ts, NULL);
235 	    pthread_mutex_lock(&xlp->dpy_mutex);
236 	}
237 	XNextEvent(xlp->dpy, &xev);
238 	pthread_mutex_unlock(&xlp->dpy_mutex);
239 	for (cb = xlp->callbacks[xev.type]; cb; cb = cb->next) {
240 	    if (xev.xany.window == cb->window) {
241 		pthread_mutex_lock(cb->mutex);
242 		(cb->cb_fun)(&xev, cb->data);
243 		XSync(xlp->dpy, False);
244 		pthread_mutex_unlock(cb->mutex);
245 	    }
246 	}
247     }
248     return (void *) xlp->exit_code;
249 }
250 
xlp_timeout_thread_start(void * arg)251 static void *xlp_timeout_thread_start(void *arg)
252 {
253     xlp_t *xlp = (xlp_t *) arg;
254     xlp_timeout_t *to, *min;
255     xlp_timeout_t long_max;
256     sigset_t alrmset;
257     struct itimerval timerval;
258     int sig, err;
259 
260     long_max.delay_ms = LONG_MAX;
261 
262     sigemptyset(&alrmset);
263     sigaddset(&alrmset, SIGALRM | SIGINT);
264 
265     while(1) {
266 	pthread_mutex_lock(&xlp->timeouts_mutex);
267 
268 	min = &long_max;
269 	for (to = xlp->timeouts; to; to = to->next) {
270 	    if (to->delay_ms < min->delay_ms) {
271 		min = to;
272 	    }
273 	}
274 	if (min == &long_max) {
275 	    pthread_cond_wait(&xlp->timeouts_cond, &xlp->timeouts_mutex);
276 	    pthread_mutex_unlock(&xlp->timeouts_mutex);
277 	} else {
278 	    pthread_mutex_unlock(&xlp->timeouts_mutex);
279 	    timerval.it_interval.tv_sec = 0;
280 	    timerval.it_interval.tv_usec = 0;
281 	    timerval.it_value.tv_sec = min->delay_ms / 1000;
282 	    timerval.it_value.tv_usec = (min->delay_ms & 1000) * 1000;
283 	    setitimer(ITIMER_REAL, &timerval, NULL);
284 	    err = sigwait(&alrmset, &sig);
285 	    if (err == 0 && sig == SIGALRM) {
286 		pthread_mutex_lock(min->mutex);
287 		(min->to_fun)(min->start_tv, min->data);
288 		XSync(xlp->dpy, False);
289 		pthread_mutex_unlock(min->mutex);
290 	    } else if (sig == SIGINT) {
291 		break;
292 	    }
293 	    remove_timeout_and_adjust_others(xlp, min);
294 	}
295     }
296 
297     return (void *) 0;
298 }
299 
300 /* XXX: UGH! What was I thinking when I wrote this???  This is about
301    the most horrid timeout handling I could imagine.  Instead, each
302    timeout should make a start_tv and end_tv as soon as possible after
303    creation. Then, eliminate all this arithmetic which is accumulating
304    timing errors.
305 */
remove_timeout_and_adjust_others(xlp_t * xlp,xlp_timeout_t * timeout)306 static void remove_timeout_and_adjust_others(xlp_t *xlp, xlp_timeout_t *timeout)
307 {
308     xlp_timeout_t *to, *to_last;
309 
310     pthread_mutex_lock(&xlp->timeouts_mutex);
311 
312     to_last = NULL;
313     for (to = xlp->timeouts; to; to = to->next) {
314 	if (to == timeout) {
315 	    if (to_last) {
316 		to_last->next = to->next;
317 	    } else {
318 		xlp->timeouts = to->next;
319 	    }
320 	} else {
321 	    to->delay_ms -= timeout->delay_ms;
322 	}
323     }
324     free(to);
325 
326     pthread_mutex_unlock(&xlp->timeouts_mutex);
327 }
328 
329 /*
330 static char *event_name_for_type(int type)
331 {
332     switch(type) {
333     case KeyPress:
334 	return "KeyPress";
335     case KeyRelease:
336 	return "KeyRelease";
337     case ButtonPress:
338 	return "ButtonPress";
339     case ButtonRelease:
340 	return "ButtonRelease";
341     case MotionNotify:
342 	return "MotionNotify";
343     case EnterNotify:
344 	return "EnterNotify";
345     case LeaveNotify:
346 	return "LeaveNotify";
347     case FocusIn:
348 	return "FocusIn";
349     case FocusOut:
350 	return "FocusOut";
351     case KeymapNotify:
352 	return "KeymapNotify";
353     case Expose:
354 	return "Expose";
355     case GraphicsExpose:
356 	return "GraphicsExpose";
357     case NoExpose:
358 	return "NoExpose";
359     case VisibilityNotify:
360 	return "VisibilityNotify";
361     case CreateNotify:
362 	return "CreateNotify";
363     case DestroyNotify:
364 	return "DestroyNotify";
365     case UnmapNotify:
366 	return "UnmapNotify";
367     case MapNotify:
368 	return "MapNotify";
369     case ReparentNotify:
370 	return "ReparentNotify";
371     case ConfigureNotify:
372 	return "ConfigureNotify";
373     case ConfigureRequest:
374 	return "ConfigureRequest";
375     case GravityNotify:
376 	return "GravityNotify";
377     case ResizeRequest:
378 	return "ResizeRequest";
379     case CirculateNotify:
380 	return "CirculateNotify";
381     case CirculateRequest:
382 	return "CirculateRequest";
383     case PropertyNotify:
384 	return "PropertyNotify";
385     case SelectionClear:
386 	return "SelectionClear";
387     case SelectionRequest:
388 	return "SelectionRequest";
389     case SelectionNotify:
390 	return "SelectionNotify";
391     case ColormapNotify:
392 	return "ColorMapNotify";
393     case ClientMessage:
394 	return "ClientMessage";
395     case MappingNotify:
396 	return "MappingNotify";
397     default:
398 	fprintf(stderr, "%s:%d: ERROR: Unknown event type %d\n", __FILE__, __LINE__, type);
399 	return "(ERROR: Unknown event type)";
400     }
401 
402 }
403 */
404 
event_mask_for_type(int type)405 static long event_mask_for_type(int type)
406 {
407     switch(type) {
408     case KeyPress:
409 	return KeyPressMask;
410     case KeyRelease:
411 	return KeyReleaseMask;
412     case ButtonPress:
413 	return ButtonPressMask;
414     case ButtonRelease:
415 	return ButtonReleaseMask;
416     case MotionNotify:
417 	return PointerMotionMask | ButtonMotionMask;
418     case EnterNotify:
419 	return EnterWindowMask;
420     case LeaveNotify:
421 	return LeaveWindowMask;
422     case FocusIn:
423     case FocusOut:
424 	return FocusChangeMask;
425     case KeymapNotify:
426 	return KeymapStateMask;
427     case Expose:
428     case GraphicsExpose:
429     case NoExpose:
430 	return ExposureMask;
431     case VisibilityNotify:
432 	return VisibilityChangeMask;
433     case CreateNotify:
434 	return SubstructureNotifyMask;
435     case DestroyNotify:
436     case UnmapNotify:
437     case MapNotify:
438 	return StructureNotifyMask;
439     case MapRequest:
440 	return SubstructureRedirectMask;
441     case ReparentNotify:
442     case ConfigureNotify:
443 	return StructureNotifyMask;
444     case ConfigureRequest:
445 	return SubstructureRedirectMask;
446     case GravityNotify:
447 	return StructureNotifyMask;
448     case ResizeRequest:
449 	return ResizeRedirectMask;
450     case CirculateNotify:
451 	return StructureNotifyMask;
452     case CirculateRequest:
453 	return SubstructureRedirectMask;
454     case PropertyNotify:
455 	return PropertyChangeMask;
456     case SelectionClear:
457     case SelectionRequest:
458     case SelectionNotify:
459 	return NoEventMask;
460     case ColormapNotify:
461 	return ColormapChangeMask;
462     case ClientMessage:
463     case MappingNotify:
464 	return NoEventMask;
465     default:
466 	fprintf(stderr, "%s:%d: ERROR: Unknown event type %d\n", __FILE__, __LINE__, type);
467 	return NoEventMask;
468     }
469 }
470