]> icculus.org git repositories - mikachu/openbox.git/blob - src/basedisplay.cc
rename, remove bullshit. ya
[mikachu/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 "basedisplay.hh"
89 #include "gccache.hh"
90 #include "timer.hh"
91 #include "util.hh"
92
93
94 // X error handler to handle any and all X errors while the application is
95 // running
96 static bool internal_error = False;
97
98 BaseDisplay *base_display;
99
100 static int handleXErrors(Display *d, XErrorEvent *e) {
101 #ifdef    DEBUG
102   char errtxt[128];
103
104   XGetErrorText(d, e->error_code, errtxt, 128);
105   fprintf(stderr,
106           i18n(BaseDisplaySet, BaseDisplayXError,
107                "%s:  X error: %s(%d) opcodes %d/%d\n  resource 0x%lx\n"),
108           base_display->getApplicationName(), errtxt, e->error_code,
109           e->request_code, e->minor_code, e->resourceid);
110 #else
111   // shutup gcc
112   (void) d;
113   (void) e;
114 #endif // DEBUG
115
116   if (internal_error) abort();
117
118   return(False);
119 }
120
121
122 // signal handler to allow for proper and gentle shutdown
123
124 #ifndef   HAVE_SIGACTION
125 static RETSIGTYPE signalhandler(int sig) {
126 #else //  HAVE_SIGACTION
127 static void signalhandler(int sig) {
128 #endif // HAVE_SIGACTION
129
130   static int re_enter = 0;
131
132   switch (sig) {
133   case SIGCHLD:
134     int status;
135     waitpid(-1, &status, WNOHANG | WUNTRACED);
136
137 #ifndef   HAVE_SIGACTION
138     // assume broken, braindead sysv signal semantics
139     signal(SIGCHLD, (RETSIGTYPE (*)(int)) signalhandler);
140 #endif // HAVE_SIGACTION
141
142     break;
143
144   default:
145     if (base_display->handleSignal(sig)) {
146
147 #ifndef   HAVE_SIGACTION
148       // assume broken, braindead sysv signal semantics
149       signal(sig, (RETSIGTYPE (*)(int)) signalhandler);
150 #endif // HAVE_SIGACTION
151
152       return;
153     }
154
155     fprintf(stderr, i18n(BaseDisplaySet, BaseDisplaySignalCaught,
156                          "%s:  signal %d caught\n"),
157             base_display->getApplicationName(), sig);
158
159     if (! base_display->isStartup() && ! re_enter) {
160       internal_error = True;
161
162       re_enter = 1;
163       fprintf(stderr, i18n(BaseDisplaySet, BaseDisplayShuttingDown,
164                            "shutting down\n"));
165       base_display->shutdown();
166     }
167
168     if (sig != SIGTERM && sig != SIGINT) {
169       fprintf(stderr, i18n(BaseDisplaySet, BaseDisplayAborting,
170                            "aborting... dumping core\n"));
171       abort();
172     }
173
174     exit(0);
175
176     break;
177   }
178 }
179
180
181 BaseDisplay::BaseDisplay(const char *app_name, const char *dpy_name) {
182   application_name = app_name;
183
184   run_state = STARTUP;
185
186   ::base_display = this;
187
188 #ifdef    HAVE_SIGACTION
189   struct sigaction action;
190
191   action.sa_handler = signalhandler;
192   action.sa_mask = sigset_t();
193   action.sa_flags = SA_NOCLDSTOP | SA_NODEFER;
194
195   sigaction(SIGPIPE, &action, NULL);
196   sigaction(SIGSEGV, &action, NULL);
197   sigaction(SIGFPE, &action, NULL);
198   sigaction(SIGTERM, &action, NULL);
199   sigaction(SIGINT, &action, NULL);
200   sigaction(SIGCHLD, &action, NULL);
201   sigaction(SIGHUP, &action, NULL);
202   sigaction(SIGUSR1, &action, NULL);
203   sigaction(SIGUSR2, &action, NULL);
204 #else // !HAVE_SIGACTION
205   signal(SIGPIPE, (RETSIGTYPE (*)(int)) signalhandler);
206   signal(SIGSEGV, (RETSIGTYPE (*)(int)) signalhandler);
207   signal(SIGFPE, (RETSIGTYPE (*)(int)) signalhandler);
208   signal(SIGTERM, (RETSIGTYPE (*)(int)) signalhandler);
209   signal(SIGINT, (RETSIGTYPE (*)(int)) signalhandler);
210   signal(SIGUSR1, (RETSIGTYPE (*)(int)) signalhandler);
211   signal(SIGUSR2, (RETSIGTYPE (*)(int)) signalhandler);
212   signal(SIGHUP, (RETSIGTYPE (*)(int)) signalhandler);
213   signal(SIGCHLD, (RETSIGTYPE (*)(int)) signalhandler);
214 #endif // HAVE_SIGACTION
215
216   if (! (display = XOpenDisplay(dpy_name))) {
217     fprintf(stderr,
218             i18n(BaseDisplaySet, BaseDisplayXConnectFail,
219                "BaseDisplay::BaseDisplay: connection to X server failed.\n"));
220     ::exit(2);
221   } else if (fcntl(ConnectionNumber(display), F_SETFD, 1) == -1) {
222     fprintf(stderr,
223             i18n(BaseDisplaySet, BaseDisplayCloseOnExecFail,
224                  "BaseDisplay::BaseDisplay: couldn't mark display connection "
225                  "as close-on-exec\n"));
226     ::exit(2);
227   }
228
229   display_name = XDisplayName(dpy_name);
230
231 #ifdef    SHAPE
232   shape.extensions = XShapeQueryExtension(display, &shape.event_basep,
233                                           &shape.error_basep);
234 #else // !SHAPE
235   shape.extensions = False;
236 #endif // SHAPE
237
238 #ifdef    XINERAMA
239   if (XineramaQueryExtension(display, &xinerama.event_basep,
240                              &xinerama.error_basep) &&
241       XineramaQueryVersion(display, &xinerama.major, &xinerama.minor)) {
242 #ifdef    DEBUG
243     fprintf(stderr,
244             "BaseDisplay::BaseDisplay: Found Xinerama version %d.%d\n",
245             xinerama.major, xinerama.minor);
246 #endif // DEBUG
247     xinerama.extensions = True;
248   } else {
249     xinerama.extensions = False;
250   }
251 #endif // XINERAMA
252
253   XSetErrorHandler((XErrorHandler) handleXErrors);
254
255   screenInfoList.reserve(ScreenCount(display));
256   for (int i = 0; i < ScreenCount(display); ++i)
257     screenInfoList.push_back(ScreenInfo(this, i));
258
259   NumLockMask = ScrollLockMask = 0;
260
261   const XModifierKeymap* const modmap = XGetModifierMapping(display);
262   if (modmap && modmap->max_keypermod > 0) {
263     const int mask_table[] = {
264       ShiftMask, LockMask, ControlMask, Mod1Mask,
265       Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
266     };
267     const size_t size = (sizeof(mask_table) / sizeof(mask_table[0])) *
268       modmap->max_keypermod;
269     // get the values of the keyboard lock modifiers
270     // Note: Caps lock is not retrieved the same way as Scroll and Num lock
271     // since it doesn't need to be.
272     const KeyCode num_lock = XKeysymToKeycode(display, XK_Num_Lock);
273     const KeyCode scroll_lock = XKeysymToKeycode(display, XK_Scroll_Lock);
274
275     for (size_t cnt = 0; cnt < size; ++cnt) {
276       if (! modmap->modifiermap[cnt]) continue;
277
278       if (num_lock == modmap->modifiermap[cnt])
279         NumLockMask = mask_table[cnt / modmap->max_keypermod];
280       if (scroll_lock == modmap->modifiermap[cnt])
281         ScrollLockMask = mask_table[cnt / modmap->max_keypermod];
282     }
283   }
284
285   MaskList[0] = 0;
286   MaskList[1] = LockMask;
287   MaskList[2] = NumLockMask;
288   MaskList[3] = LockMask | NumLockMask;
289   MaskList[4] = ScrollLockMask;
290   MaskList[5] = ScrollLockMask | LockMask;
291   MaskList[6] = ScrollLockMask | NumLockMask;
292   MaskList[7] = ScrollLockMask | LockMask | NumLockMask;
293   MaskListLength = sizeof(MaskList) / sizeof(MaskList[0]);
294
295   if (modmap) XFreeModifiermap(const_cast<XModifierKeymap*>(modmap));
296
297   gccache = (BGCCache *) 0;
298 }
299
300
301 BaseDisplay::~BaseDisplay(void) {
302   delete gccache;
303
304   XCloseDisplay(display);
305 }
306
307
308 void BaseDisplay::eventLoop(void) {
309   run();
310
311   const int xfd = ConnectionNumber(display);
312
313   while (run_state == RUNNING && ! internal_error) {
314     if (XPending(display)) {
315       XEvent e;
316       XNextEvent(display, &e);
317       process_event(&e);
318     } else {
319       fd_set rfds;
320       timeval now, tm, *timeout = (timeval *) 0;
321
322       FD_ZERO(&rfds);
323       FD_SET(xfd, &rfds);
324
325       if (! timerList.empty()) {
326         const BTimer* const timer = timerList.top();
327
328         gettimeofday(&now, 0);
329         tm = timer->timeRemaining(now);
330
331         timeout = &tm;
332       }
333
334       select(xfd + 1, &rfds, 0, 0, timeout);
335
336       // check for timer timeout
337       gettimeofday(&now, 0);
338
339       // there is a small chance for deadlock here:
340       // *IF* the timer list keeps getting refreshed *AND* the time between
341       // timer->start() and timer->shouldFire() is within the timer's period
342       // then the timer will keep firing.  This should be VERY near impossible.
343       while (! timerList.empty()) {
344         BTimer *timer = timerList.top();
345         if (! timer->shouldFire(now))
346           break;
347
348         timerList.pop();
349
350         timer->fireTimeout();
351         timer->halt();
352         if (timer->isRecurring())
353           timer->start();
354       }
355     }
356   }
357 }
358
359
360 void BaseDisplay::addTimer(BTimer *timer) {
361   if (! timer) return;
362
363   timerList.push(timer);
364 }
365
366
367 void BaseDisplay::removeTimer(BTimer *timer) {
368   timerList.release(timer);
369 }
370
371
372 /*
373  * Grabs a button, but also grabs the button in every possible combination
374  * with the keyboard lock keys, so that they do not cancel out the event.
375
376  * if allow_scroll_lock is true then only the top half of the lock mask
377  * table is used and scroll lock is ignored.  This value defaults to false.
378  */
379 void BaseDisplay::grabButton(unsigned int button, unsigned int modifiers,
380                              Window grab_window, bool owner_events,
381                              unsigned int event_mask, int pointer_mode,
382                              int keyboard_mode, Window confine_to,
383                              Cursor cursor, bool allow_scroll_lock) const {
384   unsigned int length = (allow_scroll_lock) ? MaskListLength / 2:
385                                               MaskListLength;
386   for (size_t cnt = 0; cnt < length; ++cnt)
387     XGrabButton(display, button, modifiers | MaskList[cnt], grab_window,
388                 owner_events, event_mask, pointer_mode, keyboard_mode,
389                 confine_to, cursor);
390 }
391
392
393 /*
394  * Releases the grab on a button, and ungrabs all possible combinations of the
395  * keyboard lock keys.
396  */
397 void BaseDisplay::ungrabButton(unsigned int button, unsigned int modifiers,
398                                Window grab_window) const {
399   for (size_t cnt = 0; cnt < MaskListLength; ++cnt)
400     XUngrabButton(display, button, modifiers | MaskList[cnt], grab_window);
401 }
402
403
404 const ScreenInfo* BaseDisplay::getScreenInfo(unsigned int s) const {
405   if (s < screenInfoList.size())
406     return &screenInfoList[s];
407   return (const ScreenInfo*) 0;
408 }
409
410
411 BGCCache* BaseDisplay::gcCache(void) const {
412   if (! gccache)
413     gccache = new BGCCache(this, screenInfoList.size());
414   
415   return gccache;
416 }
417
418
419 ScreenInfo::ScreenInfo(BaseDisplay *d, unsigned int num) {
420   basedisplay = d;
421   screen_number = num;
422
423   root_window = RootWindow(basedisplay->getXDisplay(), screen_number);
424
425   rect.setSize(WidthOfScreen(ScreenOfDisplay(basedisplay->getXDisplay(),
426                                              screen_number)),
427                HeightOfScreen(ScreenOfDisplay(basedisplay->getXDisplay(),
428                                               screen_number)));
429   /*
430     If the default depth is at least 8 we will use that,
431     otherwise we try to find the largest TrueColor visual.
432     Preference is given to 24 bit over larger depths if 24 bit is an option.
433   */
434
435   depth = DefaultDepth(basedisplay->getXDisplay(), screen_number);
436   visual = DefaultVisual(basedisplay->getXDisplay(), screen_number);
437   colormap = DefaultColormap(basedisplay->getXDisplay(), screen_number);
438   
439   if (depth < 8) {
440     // search for a TrueColor Visual... if we can't find one...
441     // we will use the default visual for the screen
442     XVisualInfo vinfo_template, *vinfo_return;
443     int vinfo_nitems;
444     int best = -1;
445
446     vinfo_template.screen = screen_number;
447     vinfo_template.c_class = TrueColor;
448
449     vinfo_return = XGetVisualInfo(basedisplay->getXDisplay(),
450                                   VisualScreenMask | VisualClassMask,
451                                   &vinfo_template, &vinfo_nitems);
452     if (vinfo_return) {
453       int max_depth = 1;
454       for (int i = 0; i < vinfo_nitems; ++i) {
455         if (vinfo_return[i].depth > max_depth) {
456           if (max_depth == 24 && vinfo_return[i].depth > 24)
457             break;          // prefer 24 bit over 32
458           max_depth = vinfo_return[i].depth;
459           best = i;
460         }
461       }
462       if (max_depth < depth) best = -1;
463     }
464
465     if (best != -1) {
466       depth = vinfo_return[best].depth;
467       visual = vinfo_return[best].visual;
468       colormap = XCreateColormap(basedisplay->getXDisplay(), root_window,
469                                  visual, AllocNone);
470     }
471
472     XFree(vinfo_return);
473   }
474
475   // get the default display string and strip the screen number
476   string default_string = DisplayString(basedisplay->getXDisplay());
477   const string::size_type pos = default_string.rfind(".");
478   if (pos != string::npos)
479     default_string.resize(pos);
480
481   display_string = string("DISPLAY=") + default_string + '.' +
482     itostring(static_cast<unsigned long>(screen_number));
483   
484 #ifdef    XINERAMA
485   xinerama_active = False;
486
487   if (d->hasXineramaExtensions()) {
488     if (d->getXineramaMajorVersion() == 1) {
489       // we know the version 1(.1?) protocol
490
491       /*
492          in this version of Xinerama, we can't query on a per-screen basis, but
493          in future versions we should be able, so the 'activeness' is checked
494          on a pre-screen basis anyways.
495       */
496       if (XineramaIsActive(d->getXDisplay())) {
497         /*
498            If Xinerama is being used, there there is only going to be one screen
499            present. We still, of course, want to use the screen class, but that
500            is why no screen number is used in this function call. There should
501            never be more than one screen present with Xinerama active.
502         */
503         int num;
504         XineramaScreenInfo *info = XineramaQueryScreens(d->getXDisplay(), &num);
505         if (num > 0 && info) {
506           xinerama_areas.reserve(num);
507           for (int i = 0; i < num; ++i) {
508             xinerama_areas.push_back(Rect(info[i].x_org, info[i].y_org,
509                                           info[i].width, info[i].height));
510           }
511           XFree(info);
512
513           // if we can't find any xinerama regions, then we act as if it is not
514           // active, even though it said it was
515           xinerama_active = True;
516         }
517       }
518     }
519   }
520 #endif // XINERAMA
521 }