]> icculus.org git repositories - mikachu/openbox.git/blob - src/basedisplay.cc
add comment for later
[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 "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 }
352
353
354 ScreenInfo::ScreenInfo(BaseDisplay *d, unsigned int num) {
355   basedisplay = d;
356   screen_number = num;
357
358   root_window = RootWindow(basedisplay->getXDisplay(), screen_number);
359
360   rect.setSize(WidthOfScreen(ScreenOfDisplay(basedisplay->getXDisplay(),
361                                              screen_number)),
362                HeightOfScreen(ScreenOfDisplay(basedisplay->getXDisplay(),
363                                               screen_number)));
364   /*
365     If the default depth is at least 8 we will use that,
366     otherwise we try to find the largest TrueColor visual.
367     Preference is given to 24 bit over larger depths if 24 bit is an option.
368   */
369
370   depth = DefaultDepth(basedisplay->getXDisplay(), screen_number);
371   visual = DefaultVisual(basedisplay->getXDisplay(), screen_number);
372   colormap = DefaultColormap(basedisplay->getXDisplay(), screen_number);
373   
374   if (depth < 8) {
375     // search for a TrueColor Visual... if we can't find one...
376     // we will use the default visual for the screen
377     XVisualInfo vinfo_template, *vinfo_return;
378     int vinfo_nitems;
379     int best = -1;
380
381     vinfo_template.screen = screen_number;
382     vinfo_template.c_class = TrueColor;
383
384     vinfo_return = XGetVisualInfo(basedisplay->getXDisplay(),
385                                   VisualScreenMask | VisualClassMask,
386                                   &vinfo_template, &vinfo_nitems);
387     if (vinfo_return) {
388       int max_depth = 1;
389       for (int i = 0; i < vinfo_nitems; ++i) {
390         if (vinfo_return[i].depth > max_depth) {
391           if (max_depth == 24 && vinfo_return[i].depth > 24)
392             break;          // prefer 24 bit over 32
393           max_depth = vinfo_return[i].depth;
394           best = i;
395         }
396       }
397       if (max_depth < depth) best = -1;
398     }
399
400     if (best != -1) {
401       depth = vinfo_return[best].depth;
402       visual = vinfo_return[best].visual;
403       colormap = XCreateColormap(basedisplay->getXDisplay(), root_window,
404                                  visual, AllocNone);
405     }
406
407     XFree(vinfo_return);
408   }
409
410   // get the default display string and strip the screen number
411   string default_string = DisplayString(basedisplay->getXDisplay());
412   const string::size_type pos = default_string.rfind(".");
413   if (pos != string::npos)
414     default_string.resize(pos);
415
416   display_string = string("DISPLAY=") + default_string + '.' +
417     itostring(static_cast<unsigned long>(screen_number));
418   
419 #ifdef    XINERAMA
420   xinerama_active = False;
421
422   if (d->hasXineramaExtensions()) {
423     if (d->getXineramaMajorVersion() == 1) {
424       // we know the version 1(.1?) protocol
425
426       /*
427          in this version of Xinerama, we can't query on a per-screen basis, but
428          in future versions we should be able, so the 'activeness' is checked
429          on a pre-screen basis anyways.
430       */
431       if (XineramaIsActive(d->getXDisplay())) {
432         /*
433            If Xinerama is being used, there there is only going to be one screen
434            present. We still, of course, want to use the screen class, but that
435            is why no screen number is used in this function call. There should
436            never be more than one screen present with Xinerama active.
437         */
438         int num;
439         XineramaScreenInfo *info = XineramaQueryScreens(d->getXDisplay(), &num);
440         if (num > 0 && info) {
441           xinerama_areas.reserve(num);
442           for (int i = 0; i < num; ++i) {
443             xinerama_areas.push_back(Rect(info[i].x_org, info[i].y_org,
444                                           info[i].width, info[i].height));
445           }
446           XFree(info);
447
448           // if we can't find any xinerama regions, then we act as if it is not
449           // active, even though it said it was
450           xinerama_active = True;
451         }
452       }
453     }
454   }
455 #endif // XINERAMA
456 }