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)
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:
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
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.
25 # include "../config.h"
26 #endif // HAVE_CONFIG_H
30 #include <X11/Xatom.h>
31 #include <X11/Xutil.h>
32 #include <X11/keysym.h>
35 # include <X11/extensions/shape.h>
40 #endif // HAVE_FCNTL_H
44 #endif // HAVE_STDIO_H
48 #endif // HAVE_STDLIB_H
52 #endif // HAVE_STRING_H
55 # include <sys/types.h>
57 #endif // HAVE_UNISTD_H
59 #ifdef HAVE_SYS_SELECT_H
60 # include <sys/select.h>
61 #endif // HAVE_SYS_SELECT_H
65 #endif // HAVE_SIGNAL_H
69 # define SA_NODEFER SA_INTERRUPT
70 # else // !SA_INTERRUPT
71 # define SA_NODEFER (0)
72 # endif // SA_INTERRUPT
75 #ifdef HAVE_SYS_WAIT_H
76 # include <sys/types.h>
77 # include <sys/wait.h>
78 #endif // HAVE_SYS_WAIT_H
85 #include "BaseDisplay.hh"
91 // X error handler to handle any and all X errors while the application is
93 static bool internal_error
= False
;
95 BaseDisplay
*base_display
;
97 static int handleXErrors(Display
*d
, XErrorEvent
*e
) {
101 XGetErrorText(d
, e
->error_code
, errtxt
, 128);
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
);
113 if (internal_error
) abort();
119 // signal handler to allow for proper and gentle shutdown
121 #ifndef HAVE_SIGACTION
122 static RETSIGTYPE
signalhandler(int sig
) {
123 #else // HAVE_SIGACTION
124 static void signalhandler(int sig
) {
125 #endif // HAVE_SIGACTION
127 static int re_enter
= 0;
132 waitpid(-1, &status
, WNOHANG
| WUNTRACED
);
134 #ifndef HAVE_SIGACTION
135 // assume broken, braindead sysv signal semantics
136 signal(SIGCHLD
, (RETSIGTYPE (*)(int)) signalhandler
);
137 #endif // HAVE_SIGACTION
142 if (base_display
->handleSignal(sig
)) {
144 #ifndef HAVE_SIGACTION
145 // assume broken, braindead sysv signal semantics
146 signal(sig
, (RETSIGTYPE (*)(int)) signalhandler
);
147 #endif // HAVE_SIGACTION
152 fprintf(stderr
, i18n(BaseDisplaySet
, BaseDisplaySignalCaught
,
153 "%s: signal %d caught\n"),
154 base_display
->getApplicationName(), sig
);
156 if (! base_display
->isStartup() && ! re_enter
) {
157 internal_error
= True
;
160 fprintf(stderr
, i18n(BaseDisplaySet
, BaseDisplayShuttingDown
,
162 base_display
->shutdown();
165 if (sig
!= SIGTERM
&& sig
!= SIGINT
) {
166 fprintf(stderr
, i18n(BaseDisplaySet
, BaseDisplayAborting
,
167 "aborting... dumping core\n"));
178 BaseDisplay::BaseDisplay(const char *app_name
, const char *dpy_name
) {
179 application_name
= app_name
;
183 ::base_display
= this;
185 #ifdef HAVE_SIGACTION
186 struct sigaction action
;
188 action
.sa_handler
= signalhandler
;
189 action
.sa_mask
= sigset_t();
190 action
.sa_flags
= SA_NOCLDSTOP
| SA_NODEFER
;
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
213 if (! (display
= XOpenDisplay(dpy_name
))) {
215 i18n(BaseDisplaySet
, BaseDisplayXConnectFail
,
216 "BaseDisplay::BaseDisplay: connection to X server failed.\n"));
218 } else if (fcntl(ConnectionNumber(display
), F_SETFD
, 1) == -1) {
220 i18n(BaseDisplaySet
, BaseDisplayCloseOnExecFail
,
221 "BaseDisplay::BaseDisplay: couldn't mark display connection "
222 "as close-on-exec\n"));
226 display_name
= XDisplayName(dpy_name
);
229 shape
.extensions
= XShapeQueryExtension(display
, &shape
.event_basep
,
232 shape
.extensions
= False
;
235 XSetErrorHandler((XErrorHandler
) handleXErrors
);
237 screenInfoList
.reserve(ScreenCount(display
));
238 for (int i
= 0; i
< ScreenCount(display
); ++i
)
239 screenInfoList
.push_back(ScreenInfo(this, i
));
242 NumLockMask
= ScrollLockMask
= 0;
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
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
);
258 for (size_t cnt
= 0; cnt
< size
; ++cnt
) {
259 if (! modmap
->modifiermap
[cnt
]) continue;
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
];
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]);
278 if (modmap
) XFreeModifiermap(const_cast<XModifierKeymap
*>(modmap
));
288 BaseDisplay::~BaseDisplay(void) {
291 XCloseDisplay(display
);
295 void BaseDisplay::eventLoop(void) {
298 const int xfd
= ConnectionNumber(display
);
300 while (run_state
== RUNNING
&& ! internal_error
) {
301 if (XPending(display
)) {
303 XNextEvent(display
, &e
);
307 timeval now
, tm
, *timeout
= (timeval
*) 0;
312 if (! timerList
.empty()) {
313 const BTimer
* const timer
= timerList
.top();
315 gettimeofday(&now
, 0);
316 tm
= timer
->timeRemaining(now
);
321 select(xfd
+ 1, &rfds
, 0, 0, timeout
);
323 // check for timer timeout
324 gettimeofday(&now
, 0);
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
))
337 timer
->fireTimeout();
339 if (timer
->isRecurring())
347 void BaseDisplay::addTimer(BTimer
*timer
) {
350 timerList
.push(timer
);
354 void BaseDisplay::removeTimer(BTimer
*timer
) {
355 timerList
.release(timer
);
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.
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 {
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
,
374 XGrabButton(display
, button
, modifiers
, grab_window
,
375 owner_events
, event_mask
, pointer_mode
, keyboard_mode
,
381 * Releases the grab on a button, and ungrabs all possible combinations of the
382 * keyboard lock keys.
384 void BaseDisplay::ungrabButton(unsigned int button
, unsigned int modifiers
,
385 Window grab_window
) const {
387 for (size_t cnt
= 0; cnt
< MaskListLength
; ++cnt
)
388 XUngrabButton(display
, button
, modifiers
| MaskList
[cnt
], grab_window
);
390 XUngrabButton(display
, button
, modifiers
, grab_window
);
395 const ScreenInfo
* BaseDisplay::getScreenInfo(unsigned int s
) const {
396 if (s
< screenInfoList
.size())
397 return &screenInfoList
[s
];
398 return (const ScreenInfo
*) 0;
402 BGCCache
*BaseDisplay::gcCache(void) const
404 if (! gccache
) gccache
= new BGCCache(this);
409 ScreenInfo::ScreenInfo(BaseDisplay
*d
, unsigned int num
) {
413 root_window
= RootWindow(basedisplay
->getXDisplay(), screen_number
);
415 rect
.setSize(WidthOfScreen(ScreenOfDisplay(basedisplay
->getXDisplay(),
417 HeightOfScreen(ScreenOfDisplay(basedisplay
->getXDisplay(),
420 If the default depth is at least 8 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.
425 depth
= DefaultDepth(basedisplay
->getXDisplay(), screen_number
);
426 visual
= DefaultVisual(basedisplay
->getXDisplay(), screen_number
);
427 colormap
= DefaultColormap(basedisplay
->getXDisplay(), screen_number
);
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
;
436 vinfo_template
.screen
= screen_number
;
437 vinfo_template
.c_class
= TrueColor
;
439 vinfo_return
= XGetVisualInfo(basedisplay
->getXDisplay(),
440 VisualScreenMask
| VisualClassMask
,
441 &vinfo_template
, &vinfo_nitems
);
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
;
452 if (max_depth
< depth
) best
= -1;
456 depth
= vinfo_return
[best
].depth
;
457 visual
= vinfo_return
[best
].visual
;
458 colormap
= XCreateColormap(basedisplay
->getXDisplay(), root_window
,
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
);
471 std::ostringstream formatter
;
472 formatter
<< "DISPLAY=" << default_string
<< '.' << screen_number
;
473 display_string
= formatter
.str();