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>
39 # include <X11/extensions/Xinerama.h>
44 #endif // HAVE_FCNTL_H
48 #endif // HAVE_STDIO_H
52 #endif // HAVE_STDLIB_H
56 #endif // HAVE_STRING_H
59 # include <sys/types.h>
61 #endif // HAVE_UNISTD_H
63 #ifdef HAVE_SYS_SELECT_H
64 # include <sys/select.h>
65 #endif // HAVE_SYS_SELECT_H
69 #endif // HAVE_SIGNAL_H
73 # define SA_NODEFER SA_INTERRUPT
74 # else // !SA_INTERRUPT
75 # define SA_NODEFER (0)
76 # endif // SA_INTERRUPT
79 #ifdef HAVE_SYS_WAIT_H
80 # include <sys/types.h>
81 # include <sys/wait.h>
82 #endif // HAVE_SYS_WAIT_H
89 #include "BaseDisplay.hh"
95 // X error handler to handle any and all X errors while the application is
97 static bool internal_error
= False
;
99 BaseDisplay
*base_display
;
101 static int handleXErrors(Display
*d
, XErrorEvent
*e
) {
105 XGetErrorText(d
, e
->error_code
, errtxt
, 128);
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
);
117 if (internal_error
) abort();
123 // signal handler to allow for proper and gentle shutdown
125 #ifndef HAVE_SIGACTION
126 static RETSIGTYPE
signalhandler(int sig
) {
127 #else // HAVE_SIGACTION
128 static void signalhandler(int sig
) {
129 #endif // HAVE_SIGACTION
131 static int re_enter
= 0;
136 waitpid(-1, &status
, WNOHANG
| WUNTRACED
);
138 #ifndef HAVE_SIGACTION
139 // assume broken, braindead sysv signal semantics
140 signal(SIGCHLD
, (RETSIGTYPE (*)(int)) signalhandler
);
141 #endif // HAVE_SIGACTION
146 if (base_display
->handleSignal(sig
)) {
148 #ifndef HAVE_SIGACTION
149 // assume broken, braindead sysv signal semantics
150 signal(sig
, (RETSIGTYPE (*)(int)) signalhandler
);
151 #endif // HAVE_SIGACTION
156 fprintf(stderr
, i18n(BaseDisplaySet
, BaseDisplaySignalCaught
,
157 "%s: signal %d caught\n"),
158 base_display
->getApplicationName(), sig
);
160 if (! base_display
->isStartup() && ! re_enter
) {
161 internal_error
= True
;
164 fprintf(stderr
, i18n(BaseDisplaySet
, BaseDisplayShuttingDown
,
166 base_display
->shutdown();
169 if (sig
!= SIGTERM
&& sig
!= SIGINT
) {
170 fprintf(stderr
, i18n(BaseDisplaySet
, BaseDisplayAborting
,
171 "aborting... dumping core\n"));
182 BaseDisplay::BaseDisplay(const char *app_name
, const char *dpy_name
) {
183 application_name
= app_name
;
187 ::base_display
= this;
189 #ifdef HAVE_SIGACTION
190 struct sigaction action
;
192 action
.sa_handler
= signalhandler
;
193 action
.sa_mask
= sigset_t();
194 action
.sa_flags
= SA_NOCLDSTOP
| SA_NODEFER
;
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
217 if (! (display
= XOpenDisplay(dpy_name
))) {
219 i18n(BaseDisplaySet
, BaseDisplayXConnectFail
,
220 "BaseDisplay::BaseDisplay: connection to X server failed.\n"));
222 } else if (fcntl(ConnectionNumber(display
), F_SETFD
, 1) == -1) {
224 i18n(BaseDisplaySet
, BaseDisplayCloseOnExecFail
,
225 "BaseDisplay::BaseDisplay: couldn't mark display connection "
226 "as close-on-exec\n"));
230 display_name
= XDisplayName(dpy_name
);
233 shape
.extensions
= XShapeQueryExtension(display
, &shape
.event_basep
,
236 shape
.extensions
= False
;
239 xinerama
.extensions
= False
;
241 if (XineramaQueryExtension(display
, &xinerama
.event_basep
,
242 &xinerama
.error_basep
)) {
243 if (XineramaQueryVersion(display
, &xinerama
.major
,
247 "BaseDisplay::BaseDisplay: Found Xinerama version %d.%d\n",
248 xinerama
.major
, xinerama
.minor
);
250 xinerama
.extensions
= True
;
255 XSetErrorHandler((XErrorHandler
) handleXErrors
);
257 screenInfoList
.reserve(ScreenCount(display
));
258 for (int i
= 0; i
< ScreenCount(display
); ++i
)
259 screenInfoList
.push_back(ScreenInfo(this, i
));
261 NumLockMask
= ScrollLockMask
= 0;
263 const XModifierKeymap
* const modmap
= XGetModifierMapping(display
);
264 if (modmap
&& modmap
->max_keypermod
> 0) {
265 const int mask_table
[] = {
266 ShiftMask
, LockMask
, ControlMask
, Mod1Mask
,
267 Mod2Mask
, Mod3Mask
, Mod4Mask
, Mod5Mask
269 const size_t size
= (sizeof(mask_table
) / sizeof(mask_table
[0])) *
270 modmap
->max_keypermod
;
271 // get the values of the keyboard lock modifiers
272 // Note: Caps lock is not retrieved the same way as Scroll and Num lock
273 // since it doesn't need to be.
274 const KeyCode num_lock
= XKeysymToKeycode(display
, XK_Num_Lock
);
275 const KeyCode scroll_lock
= XKeysymToKeycode(display
, XK_Scroll_Lock
);
277 for (size_t cnt
= 0; cnt
< size
; ++cnt
) {
278 if (! modmap
->modifiermap
[cnt
]) continue;
280 if (num_lock
== modmap
->modifiermap
[cnt
])
281 NumLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
282 if (scroll_lock
== modmap
->modifiermap
[cnt
])
283 ScrollLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
288 MaskList
[1] = LockMask
;
289 MaskList
[2] = NumLockMask
;
290 MaskList
[3] = LockMask
| NumLockMask
;
291 MaskList
[4] = ScrollLockMask
;
292 MaskList
[5] = ScrollLockMask
| LockMask
;
293 MaskList
[6] = ScrollLockMask
| NumLockMask
;
294 MaskList
[7] = ScrollLockMask
| LockMask
| NumLockMask
;
295 MaskListLength
= sizeof(MaskList
) / sizeof(MaskList
[0]);
297 if (modmap
) XFreeModifiermap(const_cast<XModifierKeymap
*>(modmap
));
299 gccache
= (BGCCache
*) 0;
303 BaseDisplay::~BaseDisplay(void) {
306 XCloseDisplay(display
);
310 void BaseDisplay::eventLoop(void) {
313 const int xfd
= ConnectionNumber(display
);
315 while (run_state
== RUNNING
&& ! internal_error
) {
316 if (XPending(display
)) {
318 XNextEvent(display
, &e
);
322 timeval now
, tm
, *timeout
= (timeval
*) 0;
327 if (! timerList
.empty()) {
328 const BTimer
* const timer
= timerList
.top();
330 gettimeofday(&now
, 0);
331 tm
= timer
->timeRemaining(now
);
336 select(xfd
+ 1, &rfds
, 0, 0, timeout
);
338 // check for timer timeout
339 gettimeofday(&now
, 0);
341 // there is a small chance for deadlock here:
342 // *IF* the timer list keeps getting refreshed *AND* the time between
343 // timer->start() and timer->shouldFire() is within the timer's period
344 // then the timer will keep firing. This should be VERY near impossible.
345 while (! timerList
.empty()) {
346 BTimer
*timer
= timerList
.top();
347 if (! timer
->shouldFire(now
))
352 timer
->fireTimeout();
354 if (timer
->isRecurring())
362 void BaseDisplay::addTimer(BTimer
*timer
) {
365 timerList
.push(timer
);
369 void BaseDisplay::removeTimer(BTimer
*timer
) {
370 timerList
.release(timer
);
375 * Grabs a button, but also grabs the button in every possible combination
376 * with the keyboard lock keys, so that they do not cancel out the event.
378 * if allow_scroll_lock is true then only the top half of the lock mask
379 * table is used and scroll lock is ignored. This value defaults to false.
381 void BaseDisplay::grabButton(unsigned int button
, unsigned int modifiers
,
382 Window grab_window
, bool owner_events
,
383 unsigned int event_mask
, int pointer_mode
,
384 int keyboard_mode
, Window confine_to
,
385 Cursor cursor
, bool allow_scroll_lock
) const {
386 unsigned int length
= (allow_scroll_lock
) ? MaskListLength
/ 2:
388 for (size_t cnt
= 0; cnt
< length
; ++cnt
)
389 XGrabButton(display
, button
, modifiers
| MaskList
[cnt
], grab_window
,
390 owner_events
, event_mask
, pointer_mode
, keyboard_mode
,
396 * Releases the grab on a button, and ungrabs all possible combinations of the
397 * keyboard lock keys.
399 void BaseDisplay::ungrabButton(unsigned int button
, unsigned int modifiers
,
400 Window grab_window
) const {
401 for (size_t cnt
= 0; cnt
< MaskListLength
; ++cnt
)
402 XUngrabButton(display
, button
, modifiers
| MaskList
[cnt
], grab_window
);
406 const ScreenInfo
* BaseDisplay::getScreenInfo(unsigned int s
) const {
407 if (s
< screenInfoList
.size())
408 return &screenInfoList
[s
];
409 return (const ScreenInfo
*) 0;
413 BGCCache
* BaseDisplay::gcCache(void) const {
415 gccache
= new BGCCache(this);
421 ScreenInfo::ScreenInfo(BaseDisplay
*d
, unsigned int num
) {
425 root_window
= RootWindow(basedisplay
->getXDisplay(), screen_number
);
427 rect
.setSize(WidthOfScreen(ScreenOfDisplay(basedisplay
->getXDisplay(),
429 HeightOfScreen(ScreenOfDisplay(basedisplay
->getXDisplay(),
432 If the default depth is at least 8 we will use that,
433 otherwise we try to find the largest TrueColor visual.
434 Preference is given to 24 bit over larger depths if 24 bit is an option.
437 depth
= DefaultDepth(basedisplay
->getXDisplay(), screen_number
);
438 visual
= DefaultVisual(basedisplay
->getXDisplay(), screen_number
);
439 colormap
= DefaultColormap(basedisplay
->getXDisplay(), screen_number
);
442 // search for a TrueColor Visual... if we can't find one...
443 // we will use the default visual for the screen
444 XVisualInfo vinfo_template
, *vinfo_return
;
448 vinfo_template
.screen
= screen_number
;
449 vinfo_template
.c_class
= TrueColor
;
451 vinfo_return
= XGetVisualInfo(basedisplay
->getXDisplay(),
452 VisualScreenMask
| VisualClassMask
,
453 &vinfo_template
, &vinfo_nitems
);
456 for (int i
= 0; i
< vinfo_nitems
; ++i
) {
457 if (vinfo_return
[i
].depth
> max_depth
) {
458 if (max_depth
== 24 && vinfo_return
[i
].depth
> 24)
459 break; // prefer 24 bit over 32
460 max_depth
= vinfo_return
[i
].depth
;
464 if (max_depth
< depth
) best
= -1;
468 depth
= vinfo_return
[best
].depth
;
469 visual
= vinfo_return
[best
].visual
;
470 colormap
= XCreateColormap(basedisplay
->getXDisplay(), root_window
,
477 // get the default display string and strip the screen number
478 string default_string
= DisplayString(basedisplay
->getXDisplay());
479 const string::size_type pos
= default_string
.rfind(".");
480 if (pos
!= string::npos
)
481 default_string
.resize(pos
);
483 display_string
= string("DISPLAY=") + default_string
+ '.' +
484 itostring(static_cast<unsigned long>(screen_number
));
487 if (d
->hasXineramaExtensions()) {
488 if (d
->getXineramaMajorVersion() == 1) {
489 // we know the version 1(.1?) protocol
492 in this version of Xinerama, we can't query on a per-screen basis, but
493 in future versions we should be able, so the 'activeness' is checked
494 on a pre-screen basis anyways.
496 xinerama_active
= XineramaIsActive(d
->getXDisplay());
498 If Xinerama is being used, there there is only going to be one screen
499 present. We still, of course, want to use the screen class, but that is
500 why no screen number is used in this function call. There should never
501 be more than one screen present with Xinerama active.
504 XineramaScreenInfo
*info
= XineramaQueryScreens(d
->getXDisplay(), &num
);
505 if (num
> 0 && info
) {
506 for (int i
= 0; i
< num
; ++i
) {
507 xinerama_areas
.push_back(Rect(info
[i
].x_org
, info
[i
].y_org
,
508 info
[i
].width
, info
[i
].height
));