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
));
241 NumLockMask
= ScrollLockMask
= 0;
243 const XModifierKeymap
* const modmap
= XGetModifierMapping(display
);
244 if (modmap
&& modmap
->max_keypermod
> 0) {
245 const int mask_table
[] = {
246 ShiftMask
, LockMask
, ControlMask
, Mod1Mask
,
247 Mod2Mask
, Mod3Mask
, Mod4Mask
, Mod5Mask
249 const size_t size
= (sizeof(mask_table
) / sizeof(mask_table
[0])) *
250 modmap
->max_keypermod
;
251 // get the values of the keyboard lock modifiers
252 // Note: Caps lock is not retrieved the same way as Scroll and Num lock
253 // since it doesn't need to be.
254 const KeyCode num_lock
= XKeysymToKeycode(display
, XK_Num_Lock
);
255 const KeyCode scroll_lock
= XKeysymToKeycode(display
, XK_Scroll_Lock
);
257 for (size_t cnt
= 0; cnt
< size
; ++cnt
) {
258 if (! modmap
->modifiermap
[cnt
]) continue;
260 if (num_lock
== modmap
->modifiermap
[cnt
])
261 NumLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
262 if (scroll_lock
== modmap
->modifiermap
[cnt
])
263 ScrollLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
268 MaskList
[1] = LockMask
;
269 MaskList
[2] = NumLockMask
;
270 MaskList
[3] = LockMask
| NumLockMask
;
271 MaskList
[4] = ScrollLockMask
;
272 MaskList
[5] = ScrollLockMask
| LockMask
;
273 MaskList
[6] = ScrollLockMask
| NumLockMask
;
274 MaskList
[7] = ScrollLockMask
| LockMask
| NumLockMask
;
275 MaskListLength
= sizeof(MaskList
) / sizeof(MaskList
[0]);
277 if (modmap
) XFreeModifiermap(const_cast<XModifierKeymap
*>(modmap
));
279 gccache
= (BGCCache
*) 0;
283 BaseDisplay::~BaseDisplay(void) {
286 XCloseDisplay(display
);
290 void BaseDisplay::eventLoop(void) {
293 const int xfd
= ConnectionNumber(display
);
295 while (run_state
== RUNNING
&& ! internal_error
) {
296 if (XPending(display
)) {
298 XNextEvent(display
, &e
);
302 timeval now
, tm
, *timeout
= (timeval
*) 0;
307 if (! timerList
.empty()) {
308 const BTimer
* const timer
= timerList
.top();
310 gettimeofday(&now
, 0);
311 tm
= timer
->timeRemaining(now
);
316 select(xfd
+ 1, &rfds
, 0, 0, timeout
);
318 // check for timer timeout
319 gettimeofday(&now
, 0);
321 // there is a small chance for deadlock here:
322 // *IF* the timer list keeps getting refreshed *AND* the time between
323 // timer->start() and timer->shouldFire() is within the timer's period
324 // then the timer will keep firing. This should be VERY near impossible.
325 while (! timerList
.empty()) {
326 BTimer
*timer
= timerList
.top();
327 if (! timer
->shouldFire(now
))
332 timer
->fireTimeout();
334 if (timer
->isRecurring())
342 void BaseDisplay::addTimer(BTimer
*timer
) {
345 timerList
.push(timer
);
349 void BaseDisplay::removeTimer(BTimer
*timer
) {
350 timerList
.release(timer
);
355 * Grabs a button, but also grabs the button in every possible combination
356 * with the keyboard lock keys, so that they do not cancel out the event.
358 * if allow_scroll_lock is true then only the top half of the lock mask
359 * table is used and scroll lock is ignored. This value defaults to false.
361 void BaseDisplay::grabButton(unsigned int button
, unsigned int modifiers
,
362 Window grab_window
, bool owner_events
,
363 unsigned int event_mask
, int pointer_mode
,
364 int keyboard_mode
, Window confine_to
,
365 Cursor cursor
, bool allow_scroll_lock
) const {
366 unsigned int length
= (allow_scroll_lock
) ? MaskListLength
/ 2:
368 for (size_t cnt
= 0; cnt
< length
; ++cnt
)
369 XGrabButton(display
, button
, modifiers
| MaskList
[cnt
], grab_window
,
370 owner_events
, event_mask
, pointer_mode
, keyboard_mode
,
376 * Releases the grab on a button, and ungrabs all possible combinations of the
377 * keyboard lock keys.
379 void BaseDisplay::ungrabButton(unsigned int button
, unsigned int modifiers
,
380 Window grab_window
) const {
381 for (size_t cnt
= 0; cnt
< MaskListLength
; ++cnt
)
382 XUngrabButton(display
, button
, modifiers
| MaskList
[cnt
], grab_window
);
386 const ScreenInfo
* BaseDisplay::getScreenInfo(unsigned int s
) const {
387 if (s
< screenInfoList
.size())
388 return &screenInfoList
[s
];
389 return (const ScreenInfo
*) 0;
393 BGCCache
* BaseDisplay::gcCache(void) const {
395 gccache
= new BGCCache(this);
401 ScreenInfo::ScreenInfo(BaseDisplay
*d
, unsigned int num
) {
405 root_window
= RootWindow(basedisplay
->getXDisplay(), screen_number
);
407 rect
.setSize(WidthOfScreen(ScreenOfDisplay(basedisplay
->getXDisplay(),
409 HeightOfScreen(ScreenOfDisplay(basedisplay
->getXDisplay(),
412 If the default depth is at least 8 we will use that,
413 otherwise we try to find the largest TrueColor visual.
414 Preference is given to 24 bit over larger depths if 24 bit is an option.
417 depth
= DefaultDepth(basedisplay
->getXDisplay(), screen_number
);
418 visual
= DefaultVisual(basedisplay
->getXDisplay(), screen_number
);
419 colormap
= DefaultColormap(basedisplay
->getXDisplay(), screen_number
);
422 // search for a TrueColor Visual... if we can't find one...
423 // we will use the default visual for the screen
424 XVisualInfo vinfo_template
, *vinfo_return
;
428 vinfo_template
.screen
= screen_number
;
429 vinfo_template
.c_class
= TrueColor
;
431 vinfo_return
= XGetVisualInfo(basedisplay
->getXDisplay(),
432 VisualScreenMask
| VisualClassMask
,
433 &vinfo_template
, &vinfo_nitems
);
436 for (int i
= 0; i
< vinfo_nitems
; ++i
) {
437 if (vinfo_return
[i
].depth
> max_depth
) {
438 if (max_depth
== 24 && vinfo_return
[i
].depth
> 24)
439 break; // prefer 24 bit over 32
440 max_depth
= vinfo_return
[i
].depth
;
444 if (max_depth
< depth
) best
= -1;
448 depth
= vinfo_return
[best
].depth
;
449 visual
= vinfo_return
[best
].visual
;
450 colormap
= XCreateColormap(basedisplay
->getXDisplay(), root_window
,
457 // get the default display string and strip the screen number
458 string default_string
= DisplayString(basedisplay
->getXDisplay());
459 const string::size_type pos
= default_string
.rfind(".");
460 if (pos
!= string::npos
)
461 default_string
.resize(pos
);
463 display_string
= string("DISPLAY=") + default_string
+ '.' +
464 itostring(static_cast<unsigned long>(screen_number
));