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