]> icculus.org git repositories - mikachu/openbox.git/blob - src/BaseDisplay.cc
make desktop windows get lowered properly
[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    HAVE_FCNTL_H
39 #  include <fcntl.h>
40 #endif // HAVE_FCNTL_H
41
42 #ifdef    HAVE_STDIO_H
43 #  include <stdio.h>
44 #endif // HAVE_STDIO_H
45
46 #ifdef HAVE_STDLIB_H
47 #  include <stdlib.h>
48 #endif // HAVE_STDLIB_H
49
50 #ifdef HAVE_STRING_H
51 #  include <string.h>
52 #endif // HAVE_STRING_H
53
54 #ifdef    HAVE_UNISTD_H
55 #  include <sys/types.h>
56 #  include <unistd.h>
57 #endif // HAVE_UNISTD_H
58
59 #ifdef    HAVE_SYS_SELECT_H
60 #  include <sys/select.h>
61 #endif // HAVE_SYS_SELECT_H
62
63 #ifdef    HAVE_SIGNAL_H
64 #  include <signal.h>
65 #endif // HAVE_SIGNAL_H
66
67 #ifndef   SA_NODEFER
68 #  ifdef   SA_INTERRUPT
69 #    define SA_NODEFER SA_INTERRUPT
70 #  else // !SA_INTERRUPT
71 #    define SA_NODEFER (0)
72 #  endif // SA_INTERRUPT
73 #endif // SA_NODEFER
74
75 #ifdef    HAVE_SYS_WAIT_H
76 #  include <sys/types.h>
77 #  include <sys/wait.h>
78 #endif // HAVE_SYS_WAIT_H
79 }
80
81 #include <sstream>
82 using std::string;
83
84 #include "i18n.hh"
85 #include "BaseDisplay.hh"
86 #include "GCCache.hh"
87 #include "Timer.hh"
88 #include "Util.hh"
89
90
91 // X error handler to handle any and all X errors while the application is
92 // running
93 static bool internal_error = False;
94
95 BaseDisplay *base_display;
96
97 static int handleXErrors(Display *d, XErrorEvent *e) {
98 #ifdef    DEBUG
99   char errtxt[128];
100
101   XGetErrorText(d, e->error_code, errtxt, 128);
102   fprintf(stderr,
103           i18n(BaseDisplaySet, BaseDisplayXError,
104                "%s:  X error: %s(%d) opcodes %d/%d\n  resource 0x%lx\n"),
105           base_display->getApplicationName(), errtxt, e->error_code,
106           e->request_code, e->minor_code, e->resourceid);
107 #else
108   // shutup gcc
109   (void) d;
110   (void) e;
111 #endif // DEBUG
112
113   if (internal_error) abort();
114
115   return(False);
116 }
117
118
119 // signal handler to allow for proper and gentle shutdown
120
121 #ifndef   HAVE_SIGACTION
122 static RETSIGTYPE signalhandler(int sig) {
123 #else //  HAVE_SIGACTION
124 static void signalhandler(int sig) {
125 #endif // HAVE_SIGACTION
126
127   static int re_enter = 0;
128
129   switch (sig) {
130   case SIGCHLD:
131     int status;
132     waitpid(-1, &status, WNOHANG | WUNTRACED);
133
134 #ifndef   HAVE_SIGACTION
135     // assume broken, braindead sysv signal semantics
136     signal(SIGCHLD, (RETSIGTYPE (*)(int)) signalhandler);
137 #endif // HAVE_SIGACTION
138
139     break;
140
141   default:
142     if (base_display->handleSignal(sig)) {
143
144 #ifndef   HAVE_SIGACTION
145       // assume broken, braindead sysv signal semantics
146       signal(sig, (RETSIGTYPE (*)(int)) signalhandler);
147 #endif // HAVE_SIGACTION
148
149       return;
150     }
151
152     fprintf(stderr, i18n(BaseDisplaySet, BaseDisplaySignalCaught,
153                          "%s:  signal %d caught\n"),
154             base_display->getApplicationName(), sig);
155
156     if (! base_display->isStartup() && ! re_enter) {
157       internal_error = True;
158
159       re_enter = 1;
160       fprintf(stderr, i18n(BaseDisplaySet, BaseDisplayShuttingDown,
161                            "shutting down\n"));
162       base_display->shutdown();
163     }
164
165     if (sig != SIGTERM && sig != SIGINT) {
166       fprintf(stderr, i18n(BaseDisplaySet, BaseDisplayAborting,
167                            "aborting... dumping core\n"));
168       abort();
169     }
170
171     exit(0);
172
173     break;
174   }
175 }
176
177
178 BaseDisplay::BaseDisplay(const char *app_name, const char *dpy_name) {
179   application_name = app_name;
180
181   run_state = STARTUP;
182
183   ::base_display = this;
184
185 #ifdef    HAVE_SIGACTION
186   struct sigaction action;
187
188   action.sa_handler = signalhandler;
189   action.sa_mask = sigset_t();
190   action.sa_flags = SA_NOCLDSTOP | SA_NODEFER;
191
192   sigaction(SIGPIPE, &action, NULL);
193   sigaction(SIGSEGV, &action, NULL);
194   sigaction(SIGFPE, &action, NULL);
195   sigaction(SIGTERM, &action, NULL);
196   sigaction(SIGINT, &action, NULL);
197   sigaction(SIGCHLD, &action, NULL);
198   sigaction(SIGHUP, &action, NULL);
199   sigaction(SIGUSR1, &action, NULL);
200   sigaction(SIGUSR2, &action, NULL);
201 #else // !HAVE_SIGACTION
202   signal(SIGPIPE, (RETSIGTYPE (*)(int)) signalhandler);
203   signal(SIGSEGV, (RETSIGTYPE (*)(int)) signalhandler);
204   signal(SIGFPE, (RETSIGTYPE (*)(int)) signalhandler);
205   signal(SIGTERM, (RETSIGTYPE (*)(int)) signalhandler);
206   signal(SIGINT, (RETSIGTYPE (*)(int)) signalhandler);
207   signal(SIGUSR1, (RETSIGTYPE (*)(int)) signalhandler);
208   signal(SIGUSR2, (RETSIGTYPE (*)(int)) signalhandler);
209   signal(SIGHUP, (RETSIGTYPE (*)(int)) signalhandler);
210   signal(SIGCHLD, (RETSIGTYPE (*)(int)) signalhandler);
211 #endif // HAVE_SIGACTION
212
213   if (! (display = XOpenDisplay(dpy_name))) {
214     fprintf(stderr,
215             i18n(BaseDisplaySet, BaseDisplayXConnectFail,
216                "BaseDisplay::BaseDisplay: connection to X server failed.\n"));
217     ::exit(2);
218   } else if (fcntl(ConnectionNumber(display), F_SETFD, 1) == -1) {
219     fprintf(stderr,
220             i18n(BaseDisplaySet, BaseDisplayCloseOnExecFail,
221                  "BaseDisplay::BaseDisplay: couldn't mark display connection "
222                  "as close-on-exec\n"));
223     ::exit(2);
224   }
225
226   display_name = XDisplayName(dpy_name);
227
228 #ifdef    SHAPE
229   shape.extensions = XShapeQueryExtension(display, &shape.event_basep,
230                                           &shape.error_basep);
231 #else // !SHAPE
232   shape.extensions = False;
233 #endif // SHAPE
234
235   XSetErrorHandler((XErrorHandler) handleXErrors);
236
237   screenInfoList.reserve(ScreenCount(display));
238   for (int i = 0; i < ScreenCount(display); ++i)
239     screenInfoList.push_back(ScreenInfo(this, i));
240
241 #ifndef   NOCLOBBER
242   NumLockMask = ScrollLockMask = 0;
243
244   const XModifierKeymap* const modmap = XGetModifierMapping(display);
245   if (modmap && modmap->max_keypermod > 0) {
246     const int mask_table[] = {
247       ShiftMask, LockMask, ControlMask, Mod1Mask,
248       Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
249     };
250     const size_t size = (sizeof(mask_table) / sizeof(mask_table[0])) *
251       modmap->max_keypermod;
252     // get the values of the keyboard lock modifiers
253     // Note: Caps lock is not retrieved the same way as Scroll and Num lock
254     // since it doesn't need to be.
255     const KeyCode num_lock = XKeysymToKeycode(display, XK_Num_Lock);
256     const KeyCode scroll_lock = XKeysymToKeycode(display, XK_Scroll_Lock);
257
258     for (size_t cnt = 0; cnt < size; ++cnt) {
259       if (! modmap->modifiermap[cnt]) continue;
260
261       if (num_lock == modmap->modifiermap[cnt])
262         NumLockMask = mask_table[cnt / modmap->max_keypermod];
263       if (scroll_lock == modmap->modifiermap[cnt])
264         ScrollLockMask = mask_table[cnt / modmap->max_keypermod];
265     }
266   }
267
268   MaskList[0] = 0;
269   MaskList[1] = LockMask;
270   MaskList[2] = NumLockMask;
271   MaskList[3] = ScrollLockMask;
272   MaskList[4] = LockMask | NumLockMask;
273   MaskList[5] = NumLockMask  | ScrollLockMask;
274   MaskList[6] = LockMask | ScrollLockMask;
275   MaskList[7] = LockMask | NumLockMask | ScrollLockMask;
276   MaskListLength = sizeof(MaskList) / sizeof(MaskList[0]);
277
278   if (modmap) XFreeModifiermap(const_cast<XModifierKeymap*>(modmap));
279 #else  // NOCLOBBER
280   NumLockMask = 0;
281   ScrollLockMask = 0;
282 #endif // NOCLOBBER
283
284   gccache = 0;
285 }
286
287
288 BaseDisplay::~BaseDisplay(void) {
289   delete gccache;
290
291   XCloseDisplay(display);
292 }
293
294
295 void BaseDisplay::eventLoop(void) {
296   run();
297
298   const int xfd = ConnectionNumber(display);
299
300   while (run_state == RUNNING && ! internal_error) {
301     if (XPending(display)) {
302       XEvent e;
303       XNextEvent(display, &e);
304       process_event(&e);
305     } else {
306       fd_set rfds;
307       timeval now, tm, *timeout = (timeval *) 0;
308
309       FD_ZERO(&rfds);
310       FD_SET(xfd, &rfds);
311
312       if (! timerList.empty()) {
313         const BTimer* const timer = timerList.top();
314
315         gettimeofday(&now, 0);
316         tm = timer->timeRemaining(now);
317
318         timeout = &tm;
319       }
320
321       select(xfd + 1, &rfds, 0, 0, timeout);
322
323       // check for timer timeout
324       gettimeofday(&now, 0);
325
326       // there is a small chance for deadlock here:
327       // *IF* the timer list keeps getting refreshed *AND* the time between
328       // timer->start() and timer->shouldFire() is within the timer's period
329       // then the timer will keep firing.  This should be VERY near impossible.
330       while (! timerList.empty()) {
331         BTimer *timer = timerList.top();
332         if (! timer->shouldFire(now))
333           break;
334
335         timerList.pop();
336
337         timer->fireTimeout();
338         timer->halt();
339         if (timer->isRecurring())
340           timer->start();
341       }
342     }
343   }
344 }
345
346
347 void BaseDisplay::addTimer(BTimer *timer) {
348   if (! timer) return;
349
350   timerList.push(timer);
351 }
352
353
354 void BaseDisplay::removeTimer(BTimer *timer) {
355   timerList.release(timer);
356 }
357
358
359 /*
360  * Grabs a button, but also grabs the button in every possible combination
361  * with the keyboard lock keys, so that they do not cancel out the event.
362  */
363 void BaseDisplay::grabButton(unsigned int button, unsigned int modifiers,
364                              Window grab_window, bool owner_events,
365                              unsigned int event_mask, int pointer_mode,
366                              int keyboard_mode, Window confine_to,
367                              Cursor cursor) const {
368 #ifndef   NOCLOBBER
369   for (size_t cnt = 0; cnt < MaskListLength; ++cnt)
370     XGrabButton(display, button, modifiers | MaskList[cnt], grab_window,
371                 owner_events, event_mask, pointer_mode, keyboard_mode,
372                 confine_to, cursor);
373 #else  // NOCLOBBER
374   XGrabButton(display, button, modifiers, grab_window,
375               owner_events, event_mask, pointer_mode, keyboard_mode,
376               confine_to, cursor);
377 #endif // NOCLOBBER
378 }
379
380 /*
381  * Releases the grab on a button, and ungrabs all possible combinations of the
382  * keyboard lock keys.
383  */
384 void BaseDisplay::ungrabButton(unsigned int button, unsigned int modifiers,
385                                Window grab_window) const {
386 #ifndef   NOCLOBBER
387   for (size_t cnt = 0; cnt < MaskListLength; ++cnt)
388     XUngrabButton(display, button, modifiers | MaskList[cnt], grab_window);
389 #else  // NOCLOBBER
390   XUngrabButton(display, button, modifiers, grab_window);
391 #endif // NOCLOBBER
392 }
393
394
395 const ScreenInfo* BaseDisplay::getScreenInfo(unsigned int s) const {
396   if (s < screenInfoList.size())
397     return &screenInfoList[s];
398   return (const ScreenInfo*) 0;
399 }
400
401
402 BGCCache *BaseDisplay::gcCache(void) const
403 {
404     if (! gccache) gccache = new BGCCache(this);
405     return gccache;
406 }
407
408
409 ScreenInfo::ScreenInfo(BaseDisplay *d, unsigned int num) {
410   basedisplay = d;
411   screen_number = num;
412
413   root_window = RootWindow(basedisplay->getXDisplay(), screen_number);
414
415   rect.setSize(WidthOfScreen(ScreenOfDisplay(basedisplay->getXDisplay(),
416                                              screen_number)),
417                HeightOfScreen(ScreenOfDisplay(basedisplay->getXDisplay(),
418                                               screen_number)));
419   /*
420     If the default depth is at least 15 we will use that,
421     otherwise we try to find the largest TrueColor visual.
422     Preference is given to 24 bit over larger depths if 24 bit is an option.
423   */
424
425   depth = DefaultDepth(basedisplay->getXDisplay(), screen_number);
426   visual = DefaultVisual(basedisplay->getXDisplay(), screen_number);
427   colormap = DefaultColormap(basedisplay->getXDisplay(), screen_number);
428   
429   if (depth < 15) {
430     // search for a TrueColor Visual... if we can't find one...
431     // we will use the default visual for the screen
432     XVisualInfo vinfo_template, *vinfo_return;
433     int vinfo_nitems;
434     int best = -1;
435
436     vinfo_template.screen = screen_number;
437     vinfo_template.c_class = TrueColor;
438
439     vinfo_return = XGetVisualInfo(basedisplay->getXDisplay(),
440                                   VisualScreenMask | VisualClassMask,
441                                   &vinfo_template, &vinfo_nitems);
442     if (vinfo_return) {
443       int max_depth = 1;
444       for (int i = 0; i < vinfo_nitems; ++i) {
445         if (vinfo_return[i].depth > max_depth) {
446           if (max_depth == 24 && vinfo_return[i].depth > 24)
447             break;          // prefer 24 bit over 32
448           max_depth = vinfo_return[i].depth;
449           best = i;
450         }
451       }
452       if (max_depth < depth) best = -1;
453     }
454
455     if (best != -1) {
456       depth = vinfo_return[best].depth;
457       visual = vinfo_return[best].visual;
458       colormap = XCreateColormap(basedisplay->getXDisplay(), root_window,
459                                  visual, AllocNone);
460     }
461
462     XFree(vinfo_return);
463   }
464
465   // get the default display string and strip the screen number
466   string default_string = DisplayString(basedisplay->getXDisplay());
467   const string::size_type pos = default_string.rfind(".");
468   if (pos != string::npos)
469     default_string.resize(pos);
470
471   std::ostringstream formatter;
472   formatter << "DISPLAY=" << default_string << '.' << screen_number;
473   display_string = formatter.str();
474 }