]> icculus.org git repositories - dana/openbox.git/blob - src/BaseDisplay.cc
bindable/disableable root/workspace menus
[dana/openbox.git] / src / BaseDisplay.cc
1 // -*- mode: C++; indent-tabs-mode: nil; -*-
2 // BaseDisplay.cc for Blackbox - an X11 Window manager
3 // Copyright (c) 2001 - 2002 Sean 'Shaleh' Perry <shaleh@debian.org>
4 // Copyright (c) 1997 - 2000 Brad Hughes (bhughes@tcac.net)
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a
7 // copy of this software and associated documentation files (the "Software"),
8 // to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 // and/or sell copies of the Software, and to permit persons to whom the
11 // Software is furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 // DEALINGS IN THE SOFTWARE.
23
24 #ifdef    HAVE_CONFIG_H
25 #  include "../config.h"
26 #endif // HAVE_CONFIG_H
27
28 extern "C" {
29 #include <X11/Xlib.h>
30 #include <X11/Xatom.h>
31 #include <X11/Xutil.h>
32 #include <X11/keysym.h>
33
34 #ifdef    SHAPE
35 #  include <X11/extensions/shape.h>
36 #endif // SHAPE
37
38 #ifdef    XINERAMA
39 #  include <X11/extensions/Xinerama.h>
40 #endif // XINERAMA
41
42 #ifdef    HAVE_FCNTL_H
43 #  include <fcntl.h>
44 #endif // HAVE_FCNTL_H
45
46 #ifdef    HAVE_STDIO_H
47 #  include <stdio.h>
48 #endif // HAVE_STDIO_H
49
50 #ifdef HAVE_STDLIB_H
51 #  include <stdlib.h>
52 #endif // HAVE_STDLIB_H
53
54 #ifdef HAVE_STRING_H
55 #  include <string.h>
56 #endif // HAVE_STRING_H
57
58 #ifdef    HAVE_UNISTD_H
59 #  include <sys/types.h>
60 #  include <unistd.h>
61 #endif // HAVE_UNISTD_H
62
63 #ifdef    HAVE_SYS_SELECT_H
64 #  include <sys/select.h>
65 #endif // HAVE_SYS_SELECT_H
66
67 #ifdef    HAVE_SIGNAL_H
68 #  include <signal.h>
69 #endif // HAVE_SIGNAL_H
70
71 #ifndef   SA_NODEFER
72 #  ifdef   SA_INTERRUPT
73 #    define SA_NODEFER SA_INTERRUPT
74 #  else // !SA_INTERRUPT
75 #    define SA_NODEFER (0)
76 #  endif // SA_INTERRUPT
77 #endif // SA_NODEFER
78
79 #ifdef    HAVE_SYS_WAIT_H
80 #  include <sys/types.h>
81 #  include <sys/wait.h>
82 #endif // HAVE_SYS_WAIT_H
83 }
84
85 #include <string>
86 using std::string;
87
88 #include "i18n.hh"
89 #include "BaseDisplay.hh"
90 #include "GCCache.hh"
91 #include "Timer.hh"
92 #include "Util.hh"
93
94
95 // X error handler to handle any and all X errors while the application is
96 // running
97 static bool internal_error = False;
98
99 BaseDisplay *base_display;
100
101 static int handleXErrors(Display *d, XErrorEvent *e) {
102 #ifdef    DEBUG
103   char errtxt[128];
104
105   XGetErrorText(d, e->error_code, errtxt, 128);
106   fprintf(stderr,
107           i18n(BaseDisplaySet, BaseDisplayXError,
108                "%s:  X error: %s(%d) opcodes %d/%d\n  resource 0x%lx\n"),
109           base_display->getApplicationName(), errtxt, e->error_code,
110           e->request_code, e->minor_code, e->resourceid);
111 #else
112   // shutup gcc
113   (void) d;
114   (void) e;
115 #endif // DEBUG
116
117   if (internal_error) abort();
118
119   return(False);
120 }
121
122
123 // signal handler to allow for proper and gentle shutdown
124
125 #ifndef   HAVE_SIGACTION
126 static RETSIGTYPE signalhandler(int sig) {
127 #else //  HAVE_SIGACTION
128 static void signalhandler(int sig) {
129 #endif // HAVE_SIGACTION
130
131   static int re_enter = 0;
132
133   switch (sig) {
134   case SIGCHLD:
135     int status;
136     waitpid(-1, &status, WNOHANG | WUNTRACED);
137
138 #ifndef   HAVE_SIGACTION
139     // assume broken, braindead sysv signal semantics
140     signal(SIGCHLD, (RETSIGTYPE (*)(int)) signalhandler);
141 #endif // HAVE_SIGACTION
142
143     break;
144
145   default:
146     if (base_display->handleSignal(sig)) {
147
148 #ifndef   HAVE_SIGACTION
149       // assume broken, braindead sysv signal semantics
150       signal(sig, (RETSIGTYPE (*)(int)) signalhandler);
151 #endif // HAVE_SIGACTION
152
153       return;
154     }
155
156     fprintf(stderr, i18n(BaseDisplaySet, BaseDisplaySignalCaught,
157                          "%s:  signal %d caught\n"),
158             base_display->getApplicationName(), sig);
159
160     if (! base_display->isStartup() && ! re_enter) {
161       internal_error = True;
162
163       re_enter = 1;
164       fprintf(stderr, i18n(BaseDisplaySet, BaseDisplayShuttingDown,
165                            "shutting down\n"));
166       base_display->shutdown();
167     }
168
169     if (sig != SIGTERM && sig != SIGINT) {
170       fprintf(stderr, i18n(BaseDisplaySet, BaseDisplayAborting,
171                            "aborting... dumping core\n"));
172       abort();
173     }
174
175     exit(0);
176
177     break;
178   }
179 }
180
181
182 BaseDisplay::BaseDisplay(const char *app_name, const char *dpy_name) {
183   application_name = app_name;
184
185   run_state = STARTUP;
186
187   ::base_display = this;
188
189 #ifdef    HAVE_SIGACTION
190   struct sigaction action;
191
192   action.sa_handler = signalhandler;
193   action.sa_mask = sigset_t();
194   action.sa_flags = SA_NOCLDSTOP | SA_NODEFER;
195
196   sigaction(SIGPIPE, &action, NULL);
197   sigaction(SIGSEGV, &action, NULL);
198   sigaction(SIGFPE, &action, NULL);
199   sigaction(SIGTERM, &action, NULL);
200   sigaction(SIGINT, &action, NULL);
201   sigaction(SIGCHLD, &action, NULL);
202   sigaction(SIGHUP, &action, NULL);
203   sigaction(SIGUSR1, &action, NULL);
204   sigaction(SIGUSR2, &action, NULL);
205 #else // !HAVE_SIGACTION
206   signal(SIGPIPE, (RETSIGTYPE (*)(int)) signalhandler);
207   signal(SIGSEGV, (RETSIGTYPE (*)(int)) signalhandler);
208   signal(SIGFPE, (RETSIGTYPE (*)(int)) signalhandler);
209   signal(SIGTERM, (RETSIGTYPE (*)(int)) signalhandler);
210   signal(SIGINT, (RETSIGTYPE (*)(int)) signalhandler);
211   signal(SIGUSR1, (RETSIGTYPE (*)(int)) signalhandler);
212   signal(SIGUSR2, (RETSIGTYPE (*)(int)) signalhandler);
213   signal(SIGHUP, (RETSIGTYPE (*)(int)) signalhandler);
214   signal(SIGCHLD, (RETSIGTYPE (*)(int)) signalhandler);
215 #endif // HAVE_SIGACTION
216
217   if (! (display = XOpenDisplay(dpy_name))) {
218     fprintf(stderr,
219             i18n(BaseDisplaySet, BaseDisplayXConnectFail,
220                "BaseDisplay::BaseDisplay: connection to X server failed.\n"));
221     ::exit(2);
222   } else if (fcntl(ConnectionNumber(display), F_SETFD, 1) == -1) {
223     fprintf(stderr,
224             i18n(BaseDisplaySet, BaseDisplayCloseOnExecFail,
225                  "BaseDisplay::BaseDisplay: couldn't mark display connection "
226                  "as close-on-exec\n"));
227     ::exit(2);
228   }
229
230   display_name = XDisplayName(dpy_name);
231
232 #ifdef    SHAPE
233   shape.extensions = XShapeQueryExtension(display, &shape.event_basep,
234                                           &shape.error_basep);
235 #else // !SHAPE
236   shape.extensions = False;
237 #endif // SHAPE
238
239 #ifdef    XINERAMA
240   if (XineramaQueryExtension(display, &xinerama.event_basep,
241                              &xinerama.error_basep) &&
242       XineramaQueryVersion(display, &xinerama.major, &xinerama.minor)) {
243 #ifdef    DEBUG
244     fprintf(stderr,
245             "BaseDisplay::BaseDisplay: Found Xinerama version %d.%d\n",
246             xinerama.major, xinerama.minor);
247 #endif // DEBUG
248     xinerama.extensions = True;
249   } else {
250     xinerama.extensions = False;
251   }
252 #endif // XINERAMA
253
254   XSetErrorHandler((XErrorHandler) handleXErrors);
255
256   screenInfoList.reserve(ScreenCount(display));
257   for (int i = 0; i < ScreenCount(display); ++i)
258     screenInfoList.push_back(ScreenInfo(this, i));
259
260   NumLockMask = ScrollLockMask = 0;
261
262   const XModifierKeymap* const modmap = XGetModifierMapping(display);
263   if (modmap && modmap->max_keypermod > 0) {
264     const int mask_table[] = {
265       ShiftMask, LockMask, ControlMask, Mod1Mask,
266       Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
267     };
268     const size_t size = (sizeof(mask_table) / sizeof(mask_table[0])) *
269       modmap->max_keypermod;
270     // get the values of the keyboard lock modifiers
271     // Note: Caps lock is not retrieved the same way as Scroll and Num lock
272     // since it doesn't need to be.
273     const KeyCode num_lock = XKeysymToKeycode(display, XK_Num_Lock);
274     const KeyCode scroll_lock = XKeysymToKeycode(display, XK_Scroll_Lock);
275
276     for (size_t cnt = 0; cnt < size; ++cnt) {
277       if (! modmap->modifiermap[cnt]) continue;
278
279       if (num_lock == modmap->modifiermap[cnt])
280         NumLockMask = mask_table[cnt / modmap->max_keypermod];
281       if (scroll_lock == modmap->modifiermap[cnt])
282         ScrollLockMask = mask_table[cnt / modmap->max_keypermod];
283     }
284   }
285
286   MaskList[0] = 0;
287   MaskList[1] = LockMask;
288   MaskList[2] = NumLockMask;
289   MaskList[3] = LockMask | NumLockMask;
290   MaskList[4] = ScrollLockMask;
291   MaskList[5] = ScrollLockMask | LockMask;
292   MaskList[6] = ScrollLockMask | NumLockMask;
293   MaskList[7] = ScrollLockMask | LockMask | NumLockMask;
294   MaskListLength = sizeof(MaskList) / sizeof(MaskList[0]);
295
296   if (modmap) XFreeModifiermap(const_cast<XModifierKeymap*>(modmap));
297
298   gccache = (BGCCache *) 0;
299 }
300
301
302 BaseDisplay::~BaseDisplay(void) {
303   delete gccache;
304
305   XCloseDisplay(display);
306 }
307
308
309 void BaseDisplay::eventLoop(void) {
310   run();
311
312   const int xfd = ConnectionNumber(display);
313
314   while (run_state == RUNNING && ! internal_error) {
315     if (XPending(display)) {
316       XEvent e;
317       XNextEvent(display, &e);
318       process_event(&e);
319     } else {
320       fd_set rfds;
321       timeval now, tm, *timeout = (timeval *) 0;
322
323       FD_ZERO(&rfds);
324       FD_SET(xfd, &rfds);
325
326       if (! timerList.empty()) {
327         const BTimer* const timer = timerList.top();
328
329         gettimeofday(&now, 0);
330         tm = timer->timeRemaining(now);
331
332         timeout = &tm;
333       }
334
335       select(xfd + 1, &rfds, 0, 0, timeout);
336
337       // check for timer timeout
338       gettimeofday(&now, 0);
339
340       // there is a small chance for deadlock here:
341       // *IF* the timer list keeps getting refreshed *AND* the time between
342       // timer->start() and timer->shouldFire() is within the timer's period
343       // then the timer will keep firing.  This should be VERY near impossible.
344       while (! timerList.empty()) {
345         BTimer *timer = timerList.top();
346         if (! timer->shouldFire(now))
347           break;
348
349         timerList.pop();
350
351         timer->fireTimeout();
352         timer->halt();
353         if (timer->isRecurring())
354           timer->start();
355       }
356     }
357   }
358 }
359
360
361 void BaseDisplay::addTimer(BTimer *timer) {
362   if (! timer) return;
363
364   timerList.push(timer);
365 }
366
367
368 void BaseDisplay::removeTimer(BTimer *timer) {
369   timerList.release(timer);
370 }
371
372
373 /*
374  * Grabs a button, but also grabs the button in every possible combination
375  * with the keyboard lock keys, so that they do not cancel out the event.
376
377  * if allow_scroll_lock is true then only the top half of the lock mask
378  * table is used and scroll lock is ignored.  This value defaults to false.
379  */
380 void BaseDisplay::grabButton(unsigned int button, unsigned int modifiers,
381                              Window grab_window, bool owner_events,
382                              unsigned int event_mask, int pointer_mode,
383                              int keyboard_mode, Window confine_to,
384                              Cursor cursor, bool allow_scroll_lock) const {
385   unsigned int length = (allow_scroll_lock) ? MaskListLength / 2:
386                                               MaskListLength;
387   for (size_t cnt = 0; cnt < length; ++cnt)
388     XGrabButton(display, button, modifiers | MaskList[cnt], grab_window,
389                 owner_events, event_mask, pointer_mode, keyboard_mode,
390                 confine_to, cursor);
391 }
392
393
394 /*
395  * Releases the grab on a button, and ungrabs all possible combinations of the
396  * keyboard lock keys.
397  */
398 void BaseDisplay::ungrabButton(unsigned int button, unsigned int modifiers,
399                                Window grab_window) const {
400   for (size_t cnt = 0; cnt < MaskListLength; ++cnt)
401     XUngrabButton(display, button, modifiers | MaskList[cnt], grab_window);
402 }
403
404
405 const ScreenInfo* BaseDisplay::getScreenInfo(unsigned int s) const {
406   if (s < screenInfoList.size())
407     return &screenInfoList[s];
408   return (const ScreenInfo*) 0;
409 }
410
411
412 BGCCache* BaseDisplay::gcCache(void) const {
413   if (! gccache)
414     gccache = new BGCCache(this, screenInfoList.size());
415   
416   return gccache;
417 }
418
419
420 ScreenInfo::ScreenInfo(BaseDisplay *d, unsigned int num) {
421   basedisplay = d;
422   screen_number = num;
423
424   root_window = RootWindow(basedisplay->getXDisplay(), screen_number);
425
426   rect.setSize(WidthOfScreen(ScreenOfDisplay(basedisplay->getXDisplay(),
427                                              screen_number)),
428                HeightOfScreen(ScreenOfDisplay(basedisplay->getXDisplay(),
429                                               screen_number)));
430   /*
431     If the default depth is at least 8 we will use that,
432     otherwise we try to find the largest TrueColor visual.
433     Preference is given to 24 bit over larger depths if 24 bit is an option.
434   */
435
436   depth = DefaultDepth(basedisplay->getXDisplay(), screen_number);
437   visual = DefaultVisual(basedisplay->getXDisplay(), screen_number);
438   colormap = DefaultColormap(basedisplay->getXDisplay(), screen_number);
439   
440   if (depth < 8) {
441     // search for a TrueColor Visual... if we can't find one...
442     // we will use the default visual for the screen
443     XVisualInfo vinfo_template, *vinfo_return;
444     int vinfo_nitems;
445     int best = -1;
446
447     vinfo_template.screen = screen_number;
448     vinfo_template.c_class = TrueColor;
449
450     vinfo_return = XGetVisualInfo(basedisplay->getXDisplay(),
451                                   VisualScreenMask | VisualClassMask,
452                                   &vinfo_template, &vinfo_nitems);
453     if (vinfo_return) {
454       int max_depth = 1;
455       for (int i = 0; i < vinfo_nitems; ++i) {
456         if (vinfo_return[i].depth > max_depth) {
457           if (max_depth == 24 && vinfo_return[i].depth > 24)
458             break;          // prefer 24 bit over 32
459           max_depth = vinfo_return[i].depth;
460           best = i;
461         }
462       }
463       if (max_depth < depth) best = -1;
464     }
465
466     if (best != -1) {
467       depth = vinfo_return[best].depth;
468       visual = vinfo_return[best].visual;
469       colormap = XCreateColormap(basedisplay->getXDisplay(), root_window,
470                                  visual, AllocNone);
471     }
472
473     XFree(vinfo_return);
474   }
475
476   // get the default display string and strip the screen number
477   string default_string = DisplayString(basedisplay->getXDisplay());
478   const string::size_type pos = default_string.rfind(".");
479   if (pos != string::npos)
480     default_string.resize(pos);
481
482   display_string = string("DISPLAY=") + default_string + '.' +
483     itostring(static_cast<unsigned long>(screen_number));
484   
485 #ifdef    XINERAMA
486   xinerama_active = False;
487
488   if (d->hasXineramaExtensions()) {
489     if (d->getXineramaMajorVersion() == 1) {
490       // we know the version 1(.1?) protocol
491
492       /*
493          in this version of Xinerama, we can't query on a per-screen basis, but
494          in future versions we should be able, so the 'activeness' is checked
495          on a pre-screen basis anyways.
496       */
497       if (XineramaIsActive(d->getXDisplay())) {
498         /*
499            If Xinerama is being used, there there is only going to be one screen
500            present. We still, of course, want to use the screen class, but that
501            is why no screen number is used in this function call. There should
502            never be more than one screen present with Xinerama active.
503         */
504         int num;
505         XineramaScreenInfo *info = XineramaQueryScreens(d->getXDisplay(), &num);
506         if (num > 0 && info) {
507           xinerama_areas.reserve(num);
508           for (int i = 0; i < num; ++i) {
509             xinerama_areas.push_back(Rect(info[i].x_org, info[i].y_org,
510                                           info[i].width, info[i].height));
511           }
512           XFree(info);
513
514           // if we can't find any xinerama regions, then we act as if it is not
515           // active, even though it said it was
516           xinerama_active = True;
517         }
518       }
519     }
520   }
521 #endif // XINERAMA
522 }