the old event code
[mikachu/openbox.git] / openbox / mainloop.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    mainloop.c for the Openbox window manager
4    Copyright (c) 2003        Ben Jansens
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    See the COPYING file for a copy of the GNU General Public License.
17 */
18
19 #include "mainloop.h"
20 #include "action.h"
21 #include "client.h"
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <sys/select.h>
26 #include <signal.h>
27
28 typedef struct _ObMainLoopTimer             ObMainLoopTimer;
29 typedef struct _ObMainLoopSignal            ObMainLoopSignal;
30 typedef struct _ObMainLoopSignalHandlerType ObMainLoopSignalHandlerType;
31 typedef struct _ObMainLoopXHandlerType      ObMainLoopXHandlerType;
32 typedef struct _ObMainLoopFdHandlerType     ObMainLoopFdHandlerType;
33
34 /* this should be more than the number of possible signals on any
35    architecture... */
36 #define NUM_SIGNALS 99
37
38 /* all created ObMainLoops. Used by the signal handler to pass along signals */
39 static GSList *all_loops;
40
41 /* signals are global to all loops */
42 struct {
43     guint installed; /* a ref count */
44     struct sigaction oldact;
45 } all_signals[NUM_SIGNALS];
46
47 /* a set of all possible signals */
48 sigset_t all_signals_set;
49
50 /* signals which cause a core dump, these can't be used for callbacks */
51 static gint core_signals[] =
52 {
53     SIGABRT,
54     SIGSEGV,
55     SIGFPE,
56     SIGILL,
57     SIGQUIT,
58     SIGTRAP,
59     SIGSYS,
60     SIGBUS,
61     SIGXCPU,
62     SIGXFSZ
63 };
64 #define NUM_CORE_SIGNALS (sizeof(core_signals) / sizeof(core_signals[0]))
65
66 static void sighandler(gint sig);
67 static void timer_dispatch(ObMainLoop *loop, GTimeVal **wait);
68 static void fd_handler_destroy(gpointer data);
69
70 struct _ObMainLoop
71 {
72     Display *display;
73
74     gboolean run;     /* do keep running */
75     gboolean running; /* is still running */
76
77     GSList *x_handlers;
78
79     gint fd_x; /* The X fd is a special case! */
80     gint fd_max;
81     GHashTable *fd_handlers;
82     fd_set fd_set;
83
84     GSList *timers;
85     GTimeVal now;
86     GTimeVal ret_wait;
87
88     gboolean signal_fired;
89     guint signals_fired[NUM_SIGNALS];
90     GSList *signal_handlers[NUM_SIGNALS];
91
92     GSList *action_queue;
93 };
94
95 struct _ObMainLoopTimer
96 {
97     gulong delay;
98     GSourceFunc func;
99     gpointer data;
100     GDestroyNotify destroy;
101
102     /* The timer needs to be freed */
103     gboolean del_me;
104     /* The time the last fire should've been at */
105     GTimeVal last;
106     /* When this timer will next trigger */
107     GTimeVal timeout;
108 };
109
110 struct _ObMainLoopSignalHandlerType
111 {
112     ObMainLoop *loop;
113     gint signal;
114     gpointer data;
115     ObMainLoopSignalHandler func;
116     GDestroyNotify destroy;
117 };
118
119 struct _ObMainLoopXHandlerType
120 {
121     ObMainLoop *loop;
122     gpointer data;
123     ObMainLoopXHandler func;
124     GDestroyNotify destroy;
125 };
126
127 struct _ObMainLoopFdHandlerType
128 {
129     ObMainLoop *loop;
130     gint fd;
131     gpointer data;
132     ObMainLoopFdHandler func;
133     GDestroyNotify destroy;
134 };
135
136 ObMainLoop *ob_main_loop_new(Display *display)
137 {
138     ObMainLoop *loop;
139
140     loop = g_new0(ObMainLoop, 1);
141     loop->display = display;
142     loop->fd_x = ConnectionNumber(display);
143     FD_ZERO(&loop->fd_set);
144     FD_SET(loop->fd_x, &loop->fd_set);
145     loop->fd_max = loop->fd_x;
146
147     loop->fd_handlers = g_hash_table_new_full(g_int_hash, g_int_equal,
148                                               NULL, fd_handler_destroy);
149
150     g_get_current_time(&loop->now);
151
152     /* only do this if we're the first loop created */
153     if (!all_loops) {
154         guint i;
155         struct sigaction action;
156         sigset_t sigset;
157
158         /* initialize the all_signals_set */
159         sigfillset(&all_signals_set);
160
161         sigemptyset(&sigset);
162         action.sa_handler = sighandler;
163         action.sa_mask = sigset;
164         action.sa_flags = SA_NOCLDSTOP;
165
166         /* grab all the signals that cause core dumps */
167         for (i = 0; i < NUM_CORE_SIGNALS; ++i) {
168             /* SIGABRT is curiously not grabbed here!! that's because when we
169                get one of the core_signals, we use abort() to dump the core.
170                And having the abort() only go back to our signal handler again
171                is less than optimal */
172             if (core_signals[i] != SIGABRT) {
173                 sigaction(core_signals[i], &action,
174                           &all_signals[core_signals[i]].oldact);
175                 all_signals[core_signals[i]].installed++;
176             }
177         }
178     }
179
180     all_loops = g_slist_prepend(all_loops, loop);
181
182     loop->action_queue = NULL;
183
184     return loop;
185 }
186
187 void ob_main_loop_destroy(ObMainLoop *loop)
188 {
189     guint i;
190     GSList *it, *next;
191
192     if (loop) {
193         g_assert(loop->running == FALSE);
194
195         for (it = loop->x_handlers; it; it = next) {
196             ObMainLoopXHandlerType *h = it->data;
197             next = g_slist_next(it);
198             ob_main_loop_x_remove(loop, h->func);
199         }
200
201         g_hash_table_destroy(loop->fd_handlers);
202
203         for (it = loop->timers; it; it = g_slist_next(it)) {
204             ObMainLoopTimer *t = it->data;
205             if (t->destroy) t->destroy(t->data);
206             g_free(t);
207         }
208         g_slist_free(loop->timers);
209         loop->timers = NULL;
210
211         for (i = 0; i < NUM_SIGNALS; ++i)
212             for (it = loop->signal_handlers[i]; it; it = next) {
213                 ObMainLoopSignalHandlerType *h = it->data;
214                 next = g_slist_next(it);
215                 ob_main_loop_signal_remove(loop, h->func);
216             }
217
218         all_loops = g_slist_remove(all_loops, loop);
219
220         /* only do this if we're the last loop destroyed */
221         if (!all_loops) {
222             guint i;
223
224             /* grab all the signals that cause core dumps */
225             for (i = 0; i < NUM_CORE_SIGNALS; ++i) {
226                 if (all_signals[core_signals[i]].installed) {
227                     sigaction(core_signals[i],
228                               &all_signals[core_signals[i]].oldact, NULL);
229                     all_signals[core_signals[i]].installed--;
230                 }
231             }
232         }
233
234         for (it = loop->action_queue; it; it = g_slist_next(it))
235             action_unref(it->data);
236         g_slist_free(loop->action_queue);
237
238         g_free(loop);
239     }
240 }
241
242 static void fd_handle_foreach(gpointer key,
243                               gpointer value,
244                               gpointer data)
245 {
246     ObMainLoopFdHandlerType *h = value;
247     fd_set *set = data;
248
249     if (FD_ISSET(h->fd, set))
250         h->func(h->fd, h->data);
251 }
252
253 void ob_main_loop_queue_action(ObMainLoop *loop, ObAction *act)
254 {
255     loop->action_queue = g_slist_append(loop->action_queue, action_copy(act));
256 }
257
258 static void ob_main_loop_client_destroy(ObClient *client, gpointer data)
259 {
260     ObMainLoop *loop = data;
261     GSList *it;
262
263     for (it = loop->action_queue; it; it = g_slist_next(it)) {
264         ObAction *act = it->data;
265
266         if (act->data.any.c == client)
267             act->data.any.c = NULL;
268     }
269 }
270
271 void ob_main_loop_run(ObMainLoop *loop)
272 {
273     XEvent e;
274     struct timeval *wait;
275     fd_set selset;
276     GSList *it;
277     ObAction *act;
278
279     loop->run = TRUE;
280     loop->running = TRUE;
281
282     client_add_destructor(ob_main_loop_client_destroy, loop);
283
284     while (loop->run) {
285         if (loop->signal_fired) {
286             guint i;
287             sigset_t oldset;
288
289             /* block signals so that we can do this without the data changing
290                on us */
291             sigprocmask(SIG_SETMASK, &all_signals_set, &oldset);
292
293             for (i = 0; i < NUM_SIGNALS; ++i) {
294                 while (loop->signals_fired[i]) {
295                     for (it = loop->signal_handlers[i];
296                             it; it = g_slist_next(it)) {
297                         ObMainLoopSignalHandlerType *h = it->data;
298                         h->func(i, h->data);
299                     }
300                     loop->signals_fired[i]--;
301                 }
302             }
303             loop->signal_fired = FALSE;
304
305             sigprocmask(SIG_SETMASK, &oldset, NULL);
306         } else if (XPending(loop->display)) {
307             do {
308                 XNextEvent(loop->display, &e);
309
310                 for (it = loop->x_handlers; it; it = g_slist_next(it)) {
311                     ObMainLoopXHandlerType *h = it->data;
312                     h->func(&e, h->data);
313                 }
314             } while (XPending(loop->display));
315         } else if (loop->action_queue) {
316             /* only fire off one action at a time, then go back for more
317                X events, since the action might cause some X events (like
318                FocusIn :) */
319
320             do {
321                 act = loop->action_queue->data;
322                 if (act->data.any.client_action == OB_CLIENT_ACTION_ALWAYS &&
323                     !act->data.any.c)
324                 {
325                     loop->action_queue =
326                         g_slist_delete_link(loop->action_queue,
327                                 loop->action_queue);
328                     action_unref(act);
329                     act = NULL;
330                 }
331             } while (!act && loop->action_queue);
332
333             if  (act) {
334                 act->func(&act->data);
335                 loop->action_queue =
336                     g_slist_delete_link(loop->action_queue,
337                                         loop->action_queue);
338                 action_unref(act);
339             }
340         } else {
341             /* this only runs if there were no x events received */
342
343             timer_dispatch(loop, (GTimeVal**)&wait);
344
345             selset = loop->fd_set;
346             /* there is a small race condition here. if a signal occurs
347                between this if() and the select() then we will not process
348                the signal until 'wait' expires. possible solutions include
349                using GStaticMutex, and having the signal handler set 'wait'
350                to 0 */
351             if (!loop->signal_fired)
352                 select(loop->fd_max + 1, &selset, NULL, NULL, wait);
353
354             /* handle the X events with highest prioirity */
355             if (FD_ISSET(loop->fd_x, &selset))
356                 continue;
357
358             g_hash_table_foreach(loop->fd_handlers,
359                                  fd_handle_foreach, &selset);
360         }
361     }
362
363     client_remove_destructor(ob_main_loop_client_destroy);
364
365     loop->running = FALSE;
366 }
367
368 void ob_main_loop_exit(ObMainLoop *loop)
369 {
370     loop->run = FALSE;
371 }
372
373 /*** XEVENT WATCHERS ***/
374
375 void ob_main_loop_x_add(ObMainLoop *loop,
376                         ObMainLoopXHandler handler,
377                         gpointer data,
378                         GDestroyNotify notify)
379 {
380     ObMainLoopXHandlerType *h;
381
382     h = g_new(ObMainLoopXHandlerType, 1);
383     h->loop = loop;
384     h->func = handler;
385     h->data = data;
386     h->destroy = notify;
387     loop->x_handlers = g_slist_prepend(loop->x_handlers, h);
388 }
389
390 void ob_main_loop_x_remove(ObMainLoop *loop,
391                            ObMainLoopXHandler handler)
392 {
393     GSList *it, *next;
394
395     for (it = loop->x_handlers; it; it = next) {
396         ObMainLoopXHandlerType *h = it->data;
397         next = g_slist_next(it);
398         if (h->func == handler) {
399             loop->x_handlers = g_slist_delete_link(loop->x_handlers, it);
400             if (h->destroy) h->destroy(h->data);
401             g_free(h);
402         }
403     }
404 }
405
406 /*** SIGNAL WATCHERS ***/
407
408 static void sighandler(gint sig)
409 {
410     GSList *it;
411     guint i;
412
413     g_return_if_fail(sig < NUM_SIGNALS);
414
415     for (i = 0; i < NUM_CORE_SIGNALS; ++i)
416         if (sig == core_signals[i]) {
417             /* XXX special case for signals that default to core dump.
418                but throw some helpful output here... */
419
420             fprintf(stderr, "Fuck yah. Core dump. (Signal=%d)\n", sig);
421
422             /* die with a core dump */
423             abort();
424         }
425
426     for (it = all_loops; it; it = g_slist_next(it)) {
427         ObMainLoop *loop = it->data;
428         loop->signal_fired = TRUE;
429         loop->signals_fired[sig]++;
430     }
431 }
432
433 void ob_main_loop_signal_add(ObMainLoop *loop,
434                              gint signal,
435                              ObMainLoopSignalHandler handler,
436                              gpointer data,
437                              GDestroyNotify notify)
438 {
439     ObMainLoopSignalHandlerType *h;
440
441     g_return_if_fail(signal < NUM_SIGNALS);
442
443     h = g_new(ObMainLoopSignalHandlerType, 1);
444     h->loop = loop;
445     h->signal = signal;
446     h->func = handler;
447     h->data = data;
448     h->destroy = notify;
449     loop->signal_handlers[h->signal] =
450         g_slist_prepend(loop->signal_handlers[h->signal], h);
451
452     if (!all_signals[signal].installed) {
453         struct sigaction action;
454         sigset_t sigset;
455
456         sigemptyset(&sigset);
457         action.sa_handler = sighandler;
458         action.sa_mask = sigset;
459         action.sa_flags = SA_NOCLDSTOP;
460
461         sigaction(signal, &action, &all_signals[signal].oldact);
462     }
463
464     all_signals[signal].installed++;
465 }
466
467 void ob_main_loop_signal_remove(ObMainLoop *loop,
468                                 ObMainLoopSignalHandler handler)
469 {
470     guint i;
471     GSList *it, *next;
472
473     for (i = 0; i < NUM_SIGNALS; ++i) {
474         for (it = loop->signal_handlers[i]; it; it = next) {
475             ObMainLoopSignalHandlerType *h = it->data;
476
477             next = g_slist_next(it);
478
479             if (h->func == handler) {
480                 g_assert(all_signals[h->signal].installed > 0);
481
482                 all_signals[h->signal].installed--;
483                 if (!all_signals[h->signal].installed) {
484                     sigaction(h->signal, &all_signals[h->signal].oldact, NULL);
485                 }
486
487                 loop->signal_handlers[i] =
488                     g_slist_delete_link(loop->signal_handlers[i], it);
489                 if (h->destroy) h->destroy(h->data);
490
491                 g_free(h);
492             }
493         }
494     }
495
496 }
497
498 /*** FILE DESCRIPTOR WATCHERS ***/
499
500 static void max_fd_func(gpointer key, gpointer value, gpointer data)
501 {
502     ObMainLoop *loop = data;
503
504     /* key is the fd */
505     loop->fd_max = MAX(loop->fd_max, *(gint*)key);
506 }
507
508 static void calc_max_fd(ObMainLoop *loop)
509 {
510     loop->fd_max = loop->fd_x;
511
512     g_hash_table_foreach(loop->fd_handlers, max_fd_func, loop);
513 }
514
515 void ob_main_loop_fd_add(ObMainLoop *loop,
516                          gint fd,
517                          ObMainLoopFdHandler handler,
518                          gpointer data,
519                          GDestroyNotify notify)
520 {
521     ObMainLoopFdHandlerType *h;
522
523     h = g_new(ObMainLoopFdHandlerType, 1);
524     h->loop = loop;
525     h->fd = fd;
526     h->func = handler;
527     h->data = data;
528     h->destroy = notify;
529
530     g_hash_table_replace(loop->fd_handlers, &h->fd, h);
531     FD_SET(h->fd, &loop->fd_set);
532     calc_max_fd(loop);
533 }
534
535 static void fd_handler_destroy(gpointer data)
536 {
537     ObMainLoopFdHandlerType *h = data;
538
539     FD_CLR(h->fd, &h->loop->fd_set);
540
541     if (h->destroy)
542         h->destroy(h->data);
543 }
544
545 void ob_main_loop_fd_remove(ObMainLoop *loop,
546                             gint fd)
547 {
548     g_hash_table_remove(loop->fd_handlers, &fd);
549 }
550
551 /*** TIMEOUTS ***/
552
553 #define NEAREST_TIMEOUT(loop) \
554     (((ObMainLoopTimer*)(loop)->timers->data)->timeout)
555
556 static glong timecompare(GTimeVal *a, GTimeVal *b)
557 {
558     glong r;
559
560     if ((r = b->tv_sec - a->tv_sec)) return r;
561     return b->tv_usec - a->tv_usec;
562     
563 }
564
565 static void insert_timer(ObMainLoop *loop, ObMainLoopTimer *ins)
566 {
567     GSList *it;
568     for (it = loop->timers; it; it = g_slist_next(it)) {
569         ObMainLoopTimer *t = it->data;
570         if (timecompare(&ins->timeout, &t->timeout) >= 0) {
571             loop->timers = g_slist_insert_before(loop->timers, it, ins);
572             break;
573         }
574     }
575     if (it == NULL) /* didnt fit anywhere in the list */
576         loop->timers = g_slist_append(loop->timers, ins);
577 }
578
579 void ob_main_loop_timeout_add(ObMainLoop *loop,
580                               gulong microseconds,
581                               GSourceFunc handler,
582                               gpointer data,
583                               GDestroyNotify notify)
584 {
585     ObMainLoopTimer *t = g_new(ObMainLoopTimer, 1);
586     t->delay = microseconds;
587     t->func = handler;
588     t->data = data;
589     t->destroy = notify;
590     t->del_me = FALSE;
591     g_get_current_time(&loop->now);
592     t->last = t->timeout = loop->now;
593     g_time_val_add(&t->timeout, t->delay);
594
595     insert_timer(loop, t);
596 }
597
598 void ob_main_loop_timeout_remove(ObMainLoop *loop,
599                                  GSourceFunc handler)
600 {
601     GSList *it;
602
603     for (it = loop->timers; it; it = g_slist_next(it)) {
604         ObMainLoopTimer *t = it->data;
605         if (t->func == handler)
606             t->del_me = TRUE;
607     }
608 }
609
610 void ob_main_loop_timeout_remove_data(ObMainLoop *loop,
611                                       GSourceFunc handler,
612                                       gpointer data)
613 {
614     GSList *it;
615
616     for (it = loop->timers; it; it = g_slist_next(it)) {
617         ObMainLoopTimer *t = it->data;
618         if (t->func == handler && t->data == data)
619             t->del_me = TRUE;
620     }
621 }
622
623 /* find the time to wait for the nearest timeout */
624 static gboolean nearest_timeout_wait(ObMainLoop *loop, GTimeVal *tm)
625 {
626   if (loop->timers == NULL)
627     return FALSE;
628
629   tm->tv_sec = NEAREST_TIMEOUT(loop).tv_sec - loop->now.tv_sec;
630   tm->tv_usec = NEAREST_TIMEOUT(loop).tv_usec - loop->now.tv_usec;
631
632   while (tm->tv_usec < 0) {
633     tm->tv_usec += G_USEC_PER_SEC;
634     tm->tv_sec--;
635   }
636   tm->tv_sec += tm->tv_usec / G_USEC_PER_SEC;
637   tm->tv_usec %= G_USEC_PER_SEC;
638   if (tm->tv_sec < 0)
639     tm->tv_sec = 0;
640
641   return TRUE;
642 }
643
644 static void timer_dispatch(ObMainLoop *loop, GTimeVal **wait)
645 {
646     GSList *it, *next;
647
648     gboolean fired = FALSE;
649
650     g_get_current_time(&loop->now);
651
652     for (it = loop->timers; it; it = next) {
653         ObMainLoopTimer *curr;
654         
655         next = g_slist_next(it);
656
657         curr = it->data;
658
659         /* since timer_stop doesn't actually free the timer, we have to do our
660            real freeing in here.
661         */
662         if (curr->del_me) {
663             /* delete the top */
664             loop->timers = g_slist_delete_link(loop->timers, it); 
665             if (curr->destroy)
666                 curr->destroy(curr->data);
667             g_free(curr);
668             continue;
669         }
670         
671         /* the queue is sorted, so if this timer shouldn't fire, none are 
672            ready */
673         if (timecompare(&NEAREST_TIMEOUT(loop), &loop->now) < 0)
674             break;
675
676         /* we set the last fired time to delay msec after the previous firing,
677            then re-insert.  timers maintain their order and may trigger more
678            than once if they've waited more than one delay's worth of time.
679         */
680         loop->timers = g_slist_delete_link(loop->timers, it);
681         g_time_val_add(&curr->last, curr->delay);
682         if (curr->func(curr->data)) {
683             g_time_val_add(&curr->timeout, curr->delay);
684             insert_timer(loop, curr);
685         } else {
686             if (curr->destroy)
687                 curr->destroy(curr->data);
688             g_free(curr);
689         }
690
691         fired = TRUE;
692     }
693
694     if (fired) {
695         /* if at least one timer fires, then don't wait on X events, as there
696            may already be some in the queue from the timer callbacks.
697         */
698         loop->ret_wait.tv_sec = loop->ret_wait.tv_usec = 0;
699         *wait = &loop->ret_wait;
700     } else if (nearest_timeout_wait(loop, &loop->ret_wait))
701         *wait = &loop->ret_wait;
702     else
703         *wait = NULL;
704 }