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