]> icculus.org git repositories - dana/openbox.git/blob - openbox/mainloop.c
add resize corners to the bottom corners of the inner client border
[dana/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-2007   Dana 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 "event.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
93 struct _ObMainLoopTimer
94 {
95     gulong delay;
96     GSourceFunc func;
97     gpointer data;
98     GEqualFunc equal;
99     GDestroyNotify destroy;
100
101     /* The timer needs to be freed */
102     gboolean del_me;
103     /* The time the last fire should've been at */
104     GTimeVal last;
105     /* When this timer will next trigger */
106     GTimeVal timeout;
107 };
108
109 struct _ObMainLoopSignalHandlerType
110 {
111     ObMainLoop *loop;
112     gint signal;
113     gpointer data;
114     ObMainLoopSignalHandler func;
115     GDestroyNotify destroy;
116 };
117
118 struct _ObMainLoopXHandlerType
119 {
120     ObMainLoop *loop;
121     gpointer data;
122     ObMainLoopXHandler func;
123     GDestroyNotify destroy;
124 };
125
126 struct _ObMainLoopFdHandlerType
127 {
128     ObMainLoop *loop;
129     gint fd;
130     gpointer data;
131     ObMainLoopFdHandler func;
132     GDestroyNotify destroy;
133 };
134
135 ObMainLoop *ob_main_loop_new(Display *display)
136 {
137     ObMainLoop *loop;
138
139     loop = g_new0(ObMainLoop, 1);
140     loop->display = display;
141     loop->fd_x = ConnectionNumber(display);
142     FD_ZERO(&loop->fd_set);
143     FD_SET(loop->fd_x, &loop->fd_set);
144     loop->fd_max = loop->fd_x;
145
146     loop->fd_handlers = g_hash_table_new_full(g_int_hash, g_int_equal,
147                                               NULL, fd_handler_destroy);
148
149     g_get_current_time(&loop->now);
150
151     /* only do this if we're the first loop created */
152     if (!all_loops) {
153         guint i;
154         struct sigaction action;
155         sigset_t sigset;
156
157         /* initialize the all_signals_set */
158         sigfillset(&all_signals_set);
159
160         sigemptyset(&sigset);
161         action.sa_handler = sighandler;
162         action.sa_mask = sigset;
163         action.sa_flags = SA_NOCLDSTOP;
164
165         /* grab all the signals that cause core dumps */
166         for (i = 0; i < NUM_CORE_SIGNALS; ++i) {
167             /* SIGABRT is curiously not grabbed here!! that's because when we
168                get one of the core_signals, we use abort() to dump the core.
169                And having the abort() only go back to our signal handler again
170                is less than optimal */
171             if (core_signals[i] != SIGABRT) {
172                 sigaction(core_signals[i], &action,
173                           &all_signals[core_signals[i]].oldact);
174                 all_signals[core_signals[i]].installed++;
175             }
176         }
177     }
178
179     all_loops = g_slist_prepend(all_loops, loop);
180
181     return loop;
182 }
183
184 void ob_main_loop_destroy(ObMainLoop *loop)
185 {
186     guint i;
187     GSList *it, *next;
188
189     if (loop) {
190         g_assert(loop->running == FALSE);
191
192         for (it = loop->x_handlers; it; it = next) {
193             ObMainLoopXHandlerType *h = it->data;
194             next = g_slist_next(it);
195             ob_main_loop_x_remove(loop, h->func);
196         }
197
198         g_hash_table_destroy(loop->fd_handlers);
199
200         for (it = loop->timers; it; it = g_slist_next(it)) {
201             ObMainLoopTimer *t = it->data;
202             if (t->destroy) t->destroy(t->data);
203             g_free(t);
204         }
205         g_slist_free(loop->timers);
206         loop->timers = NULL;
207
208         for (i = 0; i < NUM_SIGNALS; ++i)
209             for (it = loop->signal_handlers[i]; it; it = next) {
210                 ObMainLoopSignalHandlerType *h = it->data;
211                 next = g_slist_next(it);
212                 ob_main_loop_signal_remove(loop, h->func);
213             }
214
215         all_loops = g_slist_remove(all_loops, loop);
216
217         /* only do this if we're the last loop destroyed */
218         if (!all_loops) {
219             guint i;
220
221             /* grab all the signals that cause core dumps */
222             for (i = 0; i < NUM_CORE_SIGNALS; ++i) {
223                 if (all_signals[core_signals[i]].installed) {
224                     sigaction(core_signals[i],
225                               &all_signals[core_signals[i]].oldact, NULL);
226                     all_signals[core_signals[i]].installed--;
227                 }
228             }
229         }
230
231         g_free(loop);
232     }
233 }
234
235 static void fd_handle_foreach(gpointer key,
236                               gpointer value,
237                               gpointer data)
238 {
239     ObMainLoopFdHandlerType *h = value;
240     fd_set *set = data;
241
242     if (FD_ISSET(h->fd, set))
243         h->func(h->fd, h->data);
244 }
245
246 void ob_main_loop_run(ObMainLoop *loop)
247 {
248     XEvent e;
249     struct timeval *wait;
250     fd_set selset;
251     GSList *it;
252
253     loop->run = TRUE;
254     loop->running = TRUE;
255
256     while (loop->run) {
257         if (loop->signal_fired) {
258             guint i;
259             sigset_t oldset;
260
261             /* block signals so that we can do this without the data changing
262                on us */
263             sigprocmask(SIG_SETMASK, &all_signals_set, &oldset);
264
265             for (i = 0; i < NUM_SIGNALS; ++i) {
266                 while (loop->signals_fired[i]) {
267                     for (it = loop->signal_handlers[i];
268                             it; it = g_slist_next(it)) {
269                         ObMainLoopSignalHandlerType *h = it->data;
270                         h->func(i, h->data);
271                     }
272                     loop->signals_fired[i]--;
273                 }
274             }
275             loop->signal_fired = FALSE;
276
277             sigprocmask(SIG_SETMASK, &oldset, NULL);
278         } else if (XPending(loop->display)) {
279             do {
280                 XNextEvent(loop->display, &e);
281
282                 for (it = loop->x_handlers; it; it = g_slist_next(it)) {
283                     ObMainLoopXHandlerType *h = it->data;
284                     h->func(&e, h->data);
285                 }
286             } while (XPending(loop->display) && loop->run);
287         } else {
288             /* this only runs if there were no x events received */
289
290             timer_dispatch(loop, (GTimeVal**)&wait);
291
292             selset = loop->fd_set;
293             /* there is a small race condition here. if a signal occurs
294                between this if() and the select() then we will not process
295                the signal until 'wait' expires. possible solutions include
296                using GStaticMutex, and having the signal handler set 'wait'
297                to 0 */
298             if (!loop->signal_fired)
299                 select(loop->fd_max + 1, &selset, NULL, NULL, wait);
300
301             /* handle the X events with highest prioirity */
302             if (FD_ISSET(loop->fd_x, &selset))
303                 continue;
304
305             g_hash_table_foreach(loop->fd_handlers,
306                                  fd_handle_foreach, &selset);
307         }
308     }
309
310     loop->running = FALSE;
311 }
312
313 void ob_main_loop_exit(ObMainLoop *loop)
314 {
315     loop->run = FALSE;
316 }
317
318 /*** XEVENT WATCHERS ***/
319
320 void ob_main_loop_x_add(ObMainLoop *loop,
321                         ObMainLoopXHandler handler,
322                         gpointer data,
323                         GDestroyNotify notify)
324 {
325     ObMainLoopXHandlerType *h;
326
327     h = g_new(ObMainLoopXHandlerType, 1);
328     h->loop = loop;
329     h->func = handler;
330     h->data = data;
331     h->destroy = notify;
332     loop->x_handlers = g_slist_prepend(loop->x_handlers, h);
333 }
334
335 void ob_main_loop_x_remove(ObMainLoop *loop,
336                            ObMainLoopXHandler handler)
337 {
338     GSList *it, *next;
339
340     for (it = loop->x_handlers; it; it = next) {
341         ObMainLoopXHandlerType *h = it->data;
342         next = g_slist_next(it);
343         if (h->func == handler) {
344             loop->x_handlers = g_slist_delete_link(loop->x_handlers, it);
345             if (h->destroy) h->destroy(h->data);
346             g_free(h);
347         }
348     }
349 }
350
351 /*** SIGNAL WATCHERS ***/
352
353 static void sighandler(gint sig)
354 {
355     GSList *it;
356     guint i;
357
358     g_return_if_fail(sig < NUM_SIGNALS);
359
360     for (i = 0; i < NUM_CORE_SIGNALS; ++i)
361         if (sig == core_signals[i]) {
362             /* XXX special case for signals that default to core dump.
363                but throw some helpful output here... */
364
365             fprintf(stderr, "How are you gentlemen? All your base are"
366                     " belong to us. (Openbox received signal %d)\n", sig);
367
368             /* die with a core dump */
369             abort();
370         }
371
372     for (it = all_loops; it; it = g_slist_next(it)) {
373         ObMainLoop *loop = it->data;
374         loop->signal_fired = TRUE;
375         loop->signals_fired[sig]++;
376     }
377 }
378
379 void ob_main_loop_signal_add(ObMainLoop *loop,
380                              gint signal,
381                              ObMainLoopSignalHandler handler,
382                              gpointer data,
383                              GDestroyNotify notify)
384 {
385     ObMainLoopSignalHandlerType *h;
386
387     g_return_if_fail(signal < NUM_SIGNALS);
388
389     h = g_new(ObMainLoopSignalHandlerType, 1);
390     h->loop = loop;
391     h->signal = signal;
392     h->func = handler;
393     h->data = data;
394     h->destroy = notify;
395     loop->signal_handlers[h->signal] =
396         g_slist_prepend(loop->signal_handlers[h->signal], h);
397
398     if (!all_signals[signal].installed) {
399         struct sigaction action;
400         sigset_t sigset;
401
402         sigemptyset(&sigset);
403         action.sa_handler = sighandler;
404         action.sa_mask = sigset;
405         action.sa_flags = SA_NOCLDSTOP;
406
407         sigaction(signal, &action, &all_signals[signal].oldact);
408     }
409
410     all_signals[signal].installed++;
411 }
412
413 void ob_main_loop_signal_remove(ObMainLoop *loop,
414                                 ObMainLoopSignalHandler handler)
415 {
416     guint i;
417     GSList *it, *next;
418
419     for (i = 0; i < NUM_SIGNALS; ++i) {
420         for (it = loop->signal_handlers[i]; it; it = next) {
421             ObMainLoopSignalHandlerType *h = it->data;
422
423             next = g_slist_next(it);
424
425             if (h->func == handler) {
426                 g_assert(all_signals[h->signal].installed > 0);
427
428                 all_signals[h->signal].installed--;
429                 if (!all_signals[h->signal].installed) {
430                     sigaction(h->signal, &all_signals[h->signal].oldact, NULL);
431                 }
432
433                 loop->signal_handlers[i] =
434                     g_slist_delete_link(loop->signal_handlers[i], it);
435                 if (h->destroy) h->destroy(h->data);
436
437                 g_free(h);
438             }
439         }
440     }
441
442 }
443
444 /*** FILE DESCRIPTOR WATCHERS ***/
445
446 static void max_fd_func(gpointer key, gpointer value, gpointer data)
447 {
448     ObMainLoop *loop = data;
449
450     /* key is the fd */
451     loop->fd_max = MAX(loop->fd_max, *(gint*)key);
452 }
453
454 static void calc_max_fd(ObMainLoop *loop)
455 {
456     loop->fd_max = loop->fd_x;
457
458     g_hash_table_foreach(loop->fd_handlers, max_fd_func, loop);
459 }
460
461 void ob_main_loop_fd_add(ObMainLoop *loop,
462                          gint fd,
463                          ObMainLoopFdHandler handler,
464                          gpointer data,
465                          GDestroyNotify notify)
466 {
467     ObMainLoopFdHandlerType *h;
468
469     h = g_new(ObMainLoopFdHandlerType, 1);
470     h->loop = loop;
471     h->fd = fd;
472     h->func = handler;
473     h->data = data;
474     h->destroy = notify;
475
476     g_hash_table_replace(loop->fd_handlers, &h->fd, h);
477     FD_SET(h->fd, &loop->fd_set);
478     calc_max_fd(loop);
479 }
480
481 static void fd_handler_destroy(gpointer data)
482 {
483     ObMainLoopFdHandlerType *h = data;
484
485     FD_CLR(h->fd, &h->loop->fd_set);
486
487     if (h->destroy)
488         h->destroy(h->data);
489 }
490
491 void ob_main_loop_fd_remove(ObMainLoop *loop,
492                             gint fd)
493 {
494     g_hash_table_remove(loop->fd_handlers, &fd);
495 }
496
497 /*** TIMEOUTS ***/
498
499 #define NEAREST_TIMEOUT(loop) \
500     (((ObMainLoopTimer*)(loop)->timers->data)->timeout)
501
502 static glong timecompare(GTimeVal *a, GTimeVal *b)
503 {
504     glong r;
505
506     if ((r = b->tv_sec - a->tv_sec)) return r;
507     return b->tv_usec - a->tv_usec;
508
509 }
510
511 static void insert_timer(ObMainLoop *loop, ObMainLoopTimer *ins)
512 {
513     GSList *it;
514     for (it = loop->timers; it; it = g_slist_next(it)) {
515         ObMainLoopTimer *t = it->data;
516         if (timecompare(&ins->timeout, &t->timeout) >= 0) {
517             loop->timers = g_slist_insert_before(loop->timers, it, ins);
518             break;
519         }
520     }
521     if (it == NULL) /* didnt fit anywhere in the list */
522         loop->timers = g_slist_append(loop->timers, ins);
523 }
524
525 void ob_main_loop_timeout_add(ObMainLoop *loop,
526                               gulong microseconds,
527                               GSourceFunc handler,
528                               gpointer data,
529                               GEqualFunc cmp,
530                               GDestroyNotify notify)
531 {
532     ObMainLoopTimer *t = g_new(ObMainLoopTimer, 1);
533     t->delay = microseconds;
534     t->func = handler;
535     t->data = data;
536     t->equal = cmp;
537     t->destroy = notify;
538     t->del_me = FALSE;
539     g_get_current_time(&loop->now);
540     t->last = t->timeout = loop->now;
541     g_time_val_add(&t->timeout, t->delay);
542
543     insert_timer(loop, t);
544 }
545
546 void ob_main_loop_timeout_remove(ObMainLoop *loop,
547                                  GSourceFunc handler)
548 {
549     GSList *it;
550
551     for (it = loop->timers; it; it = g_slist_next(it)) {
552         ObMainLoopTimer *t = it->data;
553         if (t->func == handler)
554             t->del_me = TRUE;
555     }
556 }
557
558 void ob_main_loop_timeout_remove_data(ObMainLoop *loop, GSourceFunc handler,
559                                       gpointer data, gboolean cancel_dest)
560 {
561     GSList *it;
562
563     for (it = loop->timers; it; it = g_slist_next(it)) {
564         ObMainLoopTimer *t = it->data;
565         if (t->func == handler && t->equal(t->data, data)) {
566             t->del_me = TRUE;
567             if (cancel_dest)
568                 t->destroy = NULL;
569         }
570     }
571 }
572
573 /* find the time to wait for the nearest timeout */
574 static gboolean nearest_timeout_wait(ObMainLoop *loop, GTimeVal *tm)
575 {
576   if (loop->timers == NULL)
577     return FALSE;
578
579   tm->tv_sec = NEAREST_TIMEOUT(loop).tv_sec - loop->now.tv_sec;
580   tm->tv_usec = NEAREST_TIMEOUT(loop).tv_usec - loop->now.tv_usec;
581
582   while (tm->tv_usec < 0) {
583     tm->tv_usec += G_USEC_PER_SEC;
584     tm->tv_sec--;
585   }
586   tm->tv_sec += tm->tv_usec / G_USEC_PER_SEC;
587   tm->tv_usec %= G_USEC_PER_SEC;
588   if (tm->tv_sec < 0)
589     tm->tv_sec = 0;
590
591   return TRUE;
592 }
593
594 static void timer_dispatch(ObMainLoop *loop, GTimeVal **wait)
595 {
596     GSList *it, *next;
597
598     gboolean fired = FALSE;
599
600     g_get_current_time(&loop->now);
601
602     for (it = loop->timers; it; it = next) {
603         ObMainLoopTimer *curr;
604
605         next = g_slist_next(it);
606
607         curr = it->data;
608
609         /* since timer_stop doesn't actually free the timer, we have to do our
610            real freeing in here.
611         */
612         if (curr->del_me) {
613             /* delete the top */
614             loop->timers = g_slist_delete_link(loop->timers, it);
615             if (curr->destroy)
616                 curr->destroy(curr->data);
617             g_free(curr);
618             continue;
619         }
620
621         /* the queue is sorted, so if this timer shouldn't fire, none are
622            ready */
623         if (timecompare(&NEAREST_TIMEOUT(loop), &loop->now) < 0)
624             break;
625
626         /* we set the last fired time to delay msec after the previous firing,
627            then re-insert.  timers maintain their order and may trigger more
628            than once if they've waited more than one delay's worth of time.
629         */
630         loop->timers = g_slist_delete_link(loop->timers, it);
631         g_time_val_add(&curr->last, curr->delay);
632         if (curr->func(curr->data)) {
633             g_time_val_add(&curr->timeout, curr->delay);
634             insert_timer(loop, curr);
635         } else {
636             if (curr->destroy)
637                 curr->destroy(curr->data);
638             g_free(curr);
639         }
640
641         fired = TRUE;
642     }
643
644     if (fired) {
645         /* if at least one timer fires, then don't wait on X events, as there
646            may already be some in the queue from the timer callbacks.
647         */
648         loop->ret_wait.tv_sec = loop->ret_wait.tv_usec = 0;
649         *wait = &loop->ret_wait;
650     } else if (nearest_timeout_wait(loop, &loop->ret_wait))
651         *wait = &loop->ret_wait;
652     else
653         *wait = NULL;
654 }