]> Dogcows Code - chaz/openbox/blob - src/BaseDisplay.cc
sync with the 2.0 branch
[chaz/openbox] / 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 }
This page took 0.059757 seconds and 4 git commands to generate.