]> Dogcows Code - chaz/openbox/blob - src/BaseDisplay.cc
sync with blackbox-cvs
[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 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 }
This page took 0.059728 seconds and 5 git commands to generate.