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