change how the widgets' _dirty flag works so that all inheritence levels of the widge...
[mikachu/openbox.git] / src / basedisplay.cc
1 // -*- mode: C++; indent-tabs-mode: nil; -*-
2
3 #ifdef    HAVE_CONFIG_H
4 #  include "../config.h"
5 #endif // HAVE_CONFIG_H
6
7 extern "C" {
8 #include <X11/Xlib.h>
9 #include <X11/Xatom.h>
10 #include <X11/Xutil.h>
11 #include <X11/keysym.h>
12
13 #ifdef    SHAPE
14 #  include <X11/extensions/shape.h>
15 #endif // SHAPE
16
17 #ifdef    XINERAMA
18 #  include <X11/extensions/Xinerama.h>
19 #endif // XINERAMA
20
21 #ifdef    HAVE_FCNTL_H
22 #  include <fcntl.h>
23 #endif // HAVE_FCNTL_H
24
25 #ifdef    HAVE_STDIO_H
26 #  include <stdio.h>
27 #endif // HAVE_STDIO_H
28
29 #ifdef HAVE_STDLIB_H
30 #  include <stdlib.h>
31 #endif // HAVE_STDLIB_H
32
33 #ifdef HAVE_STRING_H
34 #  include <string.h>
35 #endif // HAVE_STRING_H
36
37 #ifdef    HAVE_UNISTD_H
38 #  include <sys/types.h>
39 #  include <unistd.h>
40 #endif // HAVE_UNISTD_H
41
42 #ifdef    HAVE_SYS_SELECT_H
43 #  include <sys/select.h>
44 #endif // HAVE_SYS_SELECT_H
45
46 #ifdef    HAVE_SIGNAL_H
47 #  include <signal.h>
48 #endif // HAVE_SIGNAL_H
49
50 #ifdef    HAVE_SYS_WAIT_H
51 #  include <sys/types.h>
52 #  include <sys/wait.h>
53 #endif // HAVE_SYS_WAIT_H
54 }
55
56 #include <string>
57 using std::string;
58
59 #include "basedisplay.hh"
60 #include "gccache.hh"
61 #include "timer.hh"
62 #include "otk/util.hh"
63
64
65 // X error handler to handle any and all X errors while the application is
66 // running
67 static bool internal_error = False;
68
69 BaseDisplay *base_display;
70
71 static int handleXErrors(Display *d, XErrorEvent *e) {
72 #ifdef    DEBUG
73   char errtxt[128];
74
75   XGetErrorText(d, e->error_code, errtxt, 128); // XXX: use this!!
76   fprintf(stderr, "%s:  X error: %s(%d) opcodes %d/%d\n  resource 0x%lx\n",
77           base_display->getApplicationName(), errtxt, e->error_code,
78           e->request_code, e->minor_code, e->resourceid);
79 #else
80   // shutup gcc
81   (void) d;
82   (void) e;
83 #endif // DEBUG
84
85   if (internal_error) abort();
86
87   return(False);
88 }
89
90
91 // signal handler to allow for proper and gentle shutdown
92
93 static void signalhandler(int sig) {
94
95   static int re_enter = 0;
96
97   switch (sig) {
98   case SIGCHLD:
99     int status;
100     waitpid(-1, &status, WNOHANG | WUNTRACED);
101     break;
102
103   default:
104     if (base_display->handleSignal(sig))
105       return;
106
107     fprintf(stderr, "%s:  signal %d caught\n",
108             base_display->getApplicationName(), sig);
109
110     if (! base_display->isStartup() && ! re_enter) {
111       internal_error = True;
112
113       re_enter = 1;
114       fprintf(stderr, "shutting down\n");
115       base_display->shutdown();
116     }
117
118     if (sig != SIGTERM && sig != SIGINT) {
119       fprintf(stderr, "aborting... dumping core\n");
120       abort();
121     }
122
123     exit(0);
124
125     break;
126   }
127 }
128
129
130 BaseDisplay::BaseDisplay(const char *app_name, const char *dpy_name) {
131   application_name = app_name;
132
133   run_state = STARTUP;
134
135   ::base_display = this;
136
137   struct sigaction action;
138
139   action.sa_handler = signalhandler;
140   action.sa_mask = sigset_t();
141   action.sa_flags = SA_NOCLDSTOP | SA_NODEFER;
142
143   sigaction(SIGPIPE, &action, NULL);
144   sigaction(SIGSEGV, &action, NULL);
145   sigaction(SIGFPE, &action, NULL);
146   sigaction(SIGTERM, &action, NULL);
147   sigaction(SIGINT, &action, NULL);
148   sigaction(SIGCHLD, &action, NULL);
149   sigaction(SIGHUP, &action, NULL);
150   sigaction(SIGUSR1, &action, NULL);
151   sigaction(SIGUSR2, &action, NULL);
152
153   if (! (display = XOpenDisplay(dpy_name))) {
154     fprintf(stderr,
155             "BaseDisplay::BaseDisplay: connection to X server failed.\n");
156     ::exit(2);
157   } else if (fcntl(ConnectionNumber(display), F_SETFD, 1) == -1) {
158     fprintf(stderr,
159             "BaseDisplay::BaseDisplay: couldn't mark display connection "
160             "as close-on-exec\n");
161     ::exit(2);
162   }
163
164   display_name = XDisplayName(dpy_name);
165
166 #ifdef    SHAPE
167   shape.extensions = XShapeQueryExtension(display, &shape.event_basep,
168                                           &shape.error_basep);
169 #else // !SHAPE
170   shape.extensions = False;
171 #endif // SHAPE
172
173 #ifdef    XINERAMA
174   if (XineramaQueryExtension(display, &xinerama.event_basep,
175                              &xinerama.error_basep) &&
176       XineramaQueryVersion(display, &xinerama.major, &xinerama.minor)) {
177 #ifdef    DEBUG
178     fprintf(stderr,
179             "BaseDisplay::BaseDisplay: Found Xinerama version %d.%d\n",
180             xinerama.major, xinerama.minor);
181 #endif // DEBUG
182     xinerama.extensions = True;
183   } else {
184     xinerama.extensions = False;
185   }
186 #endif // XINERAMA
187
188   XSetErrorHandler((XErrorHandler) handleXErrors);
189
190   screenInfoList.reserve(ScreenCount(display));
191   for (int i = 0; i < ScreenCount(display); ++i)
192     screenInfoList.push_back(ScreenInfo(this, i));
193
194   NumLockMask = ScrollLockMask = 0;
195
196   const XModifierKeymap* const modmap = XGetModifierMapping(display);
197   if (modmap && modmap->max_keypermod > 0) {
198     const int mask_table[] = {
199       ShiftMask, LockMask, ControlMask, Mod1Mask,
200       Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
201     };
202     const size_t size = (sizeof(mask_table) / sizeof(mask_table[0])) *
203       modmap->max_keypermod;
204     // get the values of the keyboard lock modifiers
205     // Note: Caps lock is not retrieved the same way as Scroll and Num lock
206     // since it doesn't need to be.
207     const KeyCode num_lock = XKeysymToKeycode(display, XK_Num_Lock);
208     const KeyCode scroll_lock = XKeysymToKeycode(display, XK_Scroll_Lock);
209
210     for (size_t cnt = 0; cnt < size; ++cnt) {
211       if (! modmap->modifiermap[cnt]) continue;
212
213       if (num_lock == modmap->modifiermap[cnt])
214         NumLockMask = mask_table[cnt / modmap->max_keypermod];
215       if (scroll_lock == modmap->modifiermap[cnt])
216         ScrollLockMask = mask_table[cnt / modmap->max_keypermod];
217     }
218   }
219
220   MaskList[0] = 0;
221   MaskList[1] = LockMask;
222   MaskList[2] = NumLockMask;
223   MaskList[3] = LockMask | NumLockMask;
224   MaskList[4] = ScrollLockMask;
225   MaskList[5] = ScrollLockMask | LockMask;
226   MaskList[6] = ScrollLockMask | NumLockMask;
227   MaskList[7] = ScrollLockMask | LockMask | NumLockMask;
228   MaskListLength = sizeof(MaskList) / sizeof(MaskList[0]);
229
230   if (modmap) XFreeModifiermap(const_cast<XModifierKeymap*>(modmap));
231
232   gccache = (BGCCache *) 0;
233 }
234
235
236 BaseDisplay::~BaseDisplay(void) {
237   delete gccache;
238
239   XCloseDisplay(display);
240 }
241
242
243 void BaseDisplay::eventLoop(void) {
244   run();
245
246   const int xfd = ConnectionNumber(display);
247
248   while (run_state == RUNNING && ! internal_error) {
249     if (XPending(display)) {
250       XEvent e;
251       XNextEvent(display, &e);
252       process_event(&e);
253     } else {
254       fd_set rfds;
255       timeval now, tm, *timeout = (timeval *) 0;
256
257       FD_ZERO(&rfds);
258       FD_SET(xfd, &rfds);
259
260       if (! timerList.empty()) {
261         const BTimer* const timer = timerList.top();
262
263         gettimeofday(&now, 0);
264         tm = timer->timeRemaining(now);
265
266         timeout = &tm;
267       }
268
269       select(xfd + 1, &rfds, 0, 0, timeout);
270
271       // check for timer timeout
272       gettimeofday(&now, 0);
273
274       // there is a small chance for deadlock here:
275       // *IF* the timer list keeps getting refreshed *AND* the time between
276       // timer->start() and timer->shouldFire() is within the timer's period
277       // then the timer will keep firing.  This should be VERY near impossible.
278       while (! timerList.empty()) {
279         BTimer *timer = timerList.top();
280         if (! timer->shouldFire(now))
281           break;
282
283         timerList.pop();
284
285         timer->fireTimeout();
286         timer->halt();
287         if (timer->isRecurring())
288           timer->start();
289       }
290     }
291   }
292 }
293
294
295 void BaseDisplay::addTimer(BTimer *timer) {
296   if (! timer) return;
297
298   timerList.push(timer);
299 }
300
301
302 void BaseDisplay::removeTimer(BTimer *timer) {
303   timerList.release(timer);
304 }
305
306
307 /*
308  * Grabs a button, but also grabs the button in every possible combination
309  * with the keyboard lock keys, so that they do not cancel out the event.
310
311  * if allow_scroll_lock is true then only the top half of the lock mask
312  * table is used and scroll lock is ignored.  This value defaults to false.
313  */
314 void BaseDisplay::grabButton(unsigned int button, unsigned int modifiers,
315                              Window grab_window, bool owner_events,
316                              unsigned int event_mask, int pointer_mode,
317                              int keyboard_mode, Window confine_to,
318                              Cursor cursor, bool allow_scroll_lock) const {
319   unsigned int length = (allow_scroll_lock) ? MaskListLength / 2:
320                                               MaskListLength;
321   for (size_t cnt = 0; cnt < length; ++cnt)
322     XGrabButton(display, button, modifiers | MaskList[cnt], grab_window,
323                 owner_events, event_mask, pointer_mode, keyboard_mode,
324                 confine_to, cursor);
325 }
326
327
328 /*
329  * Releases the grab on a button, and ungrabs all possible combinations of the
330  * keyboard lock keys.
331  */
332 void BaseDisplay::ungrabButton(unsigned int button, unsigned int modifiers,
333                                Window grab_window) const {
334   for (size_t cnt = 0; cnt < MaskListLength; ++cnt)
335     XUngrabButton(display, button, modifiers | MaskList[cnt], grab_window);
336 }
337
338
339 const ScreenInfo* BaseDisplay::getScreenInfo(unsigned int s) const {
340   if (s < screenInfoList.size())
341     return &screenInfoList[s];
342   return (const ScreenInfo*) 0;
343 }
344
345
346 BGCCache* BaseDisplay::gcCache(void) const {
347   if (! gccache)
348     gccache = new BGCCache(this, screenInfoList.size());
349   
350   return gccache;
351 }