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
88 #include "basedisplay.hh"
94 // X error handler to handle any and all X errors while the application is
96 static bool internal_error
= False
;
98 BaseDisplay
*base_display
;
100 static int handleXErrors(Display
*d
, XErrorEvent
*e
) {
104 XGetErrorText(d
, e
->error_code
, errtxt
, 128);
106 i18n(BaseDisplaySet
, BaseDisplayXError
,
107 "%s: X error: %s(%d) opcodes %d/%d\n resource 0x%lx\n"),
108 base_display
->getApplicationName(), errtxt
, e
->error_code
,
109 e
->request_code
, e
->minor_code
, e
->resourceid
);
116 if (internal_error
) abort();
122 // signal handler to allow for proper and gentle shutdown
124 #ifndef HAVE_SIGACTION
125 static RETSIGTYPE
signalhandler(int sig
) {
126 #else // HAVE_SIGACTION
127 static void signalhandler(int sig
) {
128 #endif // HAVE_SIGACTION
130 static int re_enter
= 0;
135 waitpid(-1, &status
, WNOHANG
| WUNTRACED
);
137 #ifndef HAVE_SIGACTION
138 // assume broken, braindead sysv signal semantics
139 signal(SIGCHLD
, (RETSIGTYPE (*)(int)) signalhandler
);
140 #endif // HAVE_SIGACTION
145 if (base_display
->handleSignal(sig
)) {
147 #ifndef HAVE_SIGACTION
148 // assume broken, braindead sysv signal semantics
149 signal(sig
, (RETSIGTYPE (*)(int)) signalhandler
);
150 #endif // HAVE_SIGACTION
155 fprintf(stderr
, i18n(BaseDisplaySet
, BaseDisplaySignalCaught
,
156 "%s: signal %d caught\n"),
157 base_display
->getApplicationName(), sig
);
159 if (! base_display
->isStartup() && ! re_enter
) {
160 internal_error
= True
;
163 fprintf(stderr
, i18n(BaseDisplaySet
, BaseDisplayShuttingDown
,
165 base_display
->shutdown();
168 if (sig
!= SIGTERM
&& sig
!= SIGINT
) {
169 fprintf(stderr
, i18n(BaseDisplaySet
, BaseDisplayAborting
,
170 "aborting... dumping core\n"));
181 BaseDisplay::BaseDisplay(const char *app_name
, const char *dpy_name
) {
182 application_name
= app_name
;
186 ::base_display
= this;
188 #ifdef HAVE_SIGACTION
189 struct sigaction action
;
191 action
.sa_handler
= signalhandler
;
192 action
.sa_mask
= sigset_t();
193 action
.sa_flags
= SA_NOCLDSTOP
| SA_NODEFER
;
195 sigaction(SIGPIPE
, &action
, NULL
);
196 sigaction(SIGSEGV
, &action
, NULL
);
197 sigaction(SIGFPE
, &action
, NULL
);
198 sigaction(SIGTERM
, &action
, NULL
);
199 sigaction(SIGINT
, &action
, NULL
);
200 sigaction(SIGCHLD
, &action
, NULL
);
201 sigaction(SIGHUP
, &action
, NULL
);
202 sigaction(SIGUSR1
, &action
, NULL
);
203 sigaction(SIGUSR2
, &action
, NULL
);
204 #else // !HAVE_SIGACTION
205 signal(SIGPIPE
, (RETSIGTYPE (*)(int)) signalhandler
);
206 signal(SIGSEGV
, (RETSIGTYPE (*)(int)) signalhandler
);
207 signal(SIGFPE
, (RETSIGTYPE (*)(int)) signalhandler
);
208 signal(SIGTERM
, (RETSIGTYPE (*)(int)) signalhandler
);
209 signal(SIGINT
, (RETSIGTYPE (*)(int)) signalhandler
);
210 signal(SIGUSR1
, (RETSIGTYPE (*)(int)) signalhandler
);
211 signal(SIGUSR2
, (RETSIGTYPE (*)(int)) signalhandler
);
212 signal(SIGHUP
, (RETSIGTYPE (*)(int)) signalhandler
);
213 signal(SIGCHLD
, (RETSIGTYPE (*)(int)) signalhandler
);
214 #endif // HAVE_SIGACTION
216 if (! (display
= XOpenDisplay(dpy_name
))) {
218 i18n(BaseDisplaySet
, BaseDisplayXConnectFail
,
219 "BaseDisplay::BaseDisplay: connection to X server failed.\n"));
221 } else if (fcntl(ConnectionNumber(display
), F_SETFD
, 1) == -1) {
223 i18n(BaseDisplaySet
, BaseDisplayCloseOnExecFail
,
224 "BaseDisplay::BaseDisplay: couldn't mark display connection "
225 "as close-on-exec\n"));
229 display_name
= XDisplayName(dpy_name
);
232 shape
.extensions
= XShapeQueryExtension(display
, &shape
.event_basep
,
235 shape
.extensions
= False
;
239 if (XineramaQueryExtension(display
, &xinerama
.event_basep
,
240 &xinerama
.error_basep
) &&
241 XineramaQueryVersion(display
, &xinerama
.major
, &xinerama
.minor
)) {
244 "BaseDisplay::BaseDisplay: Found Xinerama version %d.%d\n",
245 xinerama
.major
, xinerama
.minor
);
247 xinerama
.extensions
= True
;
249 xinerama
.extensions
= False
;
253 XSetErrorHandler((XErrorHandler
) handleXErrors
);
255 screenInfoList
.reserve(ScreenCount(display
));
256 for (int i
= 0; i
< ScreenCount(display
); ++i
)
257 screenInfoList
.push_back(ScreenInfo(this, i
));
259 NumLockMask
= ScrollLockMask
= 0;
261 const XModifierKeymap
* const modmap
= XGetModifierMapping(display
);
262 if (modmap
&& modmap
->max_keypermod
> 0) {
263 const int mask_table
[] = {
264 ShiftMask
, LockMask
, ControlMask
, Mod1Mask
,
265 Mod2Mask
, Mod3Mask
, Mod4Mask
, Mod5Mask
267 const size_t size
= (sizeof(mask_table
) / sizeof(mask_table
[0])) *
268 modmap
->max_keypermod
;
269 // get the values of the keyboard lock modifiers
270 // Note: Caps lock is not retrieved the same way as Scroll and Num lock
271 // since it doesn't need to be.
272 const KeyCode num_lock
= XKeysymToKeycode(display
, XK_Num_Lock
);
273 const KeyCode scroll_lock
= XKeysymToKeycode(display
, XK_Scroll_Lock
);
275 for (size_t cnt
= 0; cnt
< size
; ++cnt
) {
276 if (! modmap
->modifiermap
[cnt
]) continue;
278 if (num_lock
== modmap
->modifiermap
[cnt
])
279 NumLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
280 if (scroll_lock
== modmap
->modifiermap
[cnt
])
281 ScrollLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
286 MaskList
[1] = LockMask
;
287 MaskList
[2] = NumLockMask
;
288 MaskList
[3] = LockMask
| NumLockMask
;
289 MaskList
[4] = ScrollLockMask
;
290 MaskList
[5] = ScrollLockMask
| LockMask
;
291 MaskList
[6] = ScrollLockMask
| NumLockMask
;
292 MaskList
[7] = ScrollLockMask
| LockMask
| NumLockMask
;
293 MaskListLength
= sizeof(MaskList
) / sizeof(MaskList
[0]);
295 if (modmap
) XFreeModifiermap(const_cast<XModifierKeymap
*>(modmap
));
297 gccache
= (BGCCache
*) 0;
301 BaseDisplay::~BaseDisplay(void) {
304 XCloseDisplay(display
);
308 void BaseDisplay::eventLoop(void) {
311 const int xfd
= ConnectionNumber(display
);
313 while (run_state
== RUNNING
&& ! internal_error
) {
314 if (XPending(display
)) {
316 XNextEvent(display
, &e
);
320 timeval now
, tm
, *timeout
= (timeval
*) 0;
325 if (! timerList
.empty()) {
326 const BTimer
* const timer
= timerList
.top();
328 gettimeofday(&now
, 0);
329 tm
= timer
->timeRemaining(now
);
334 select(xfd
+ 1, &rfds
, 0, 0, timeout
);
336 // check for timer timeout
337 gettimeofday(&now
, 0);
339 // there is a small chance for deadlock here:
340 // *IF* the timer list keeps getting refreshed *AND* the time between
341 // timer->start() and timer->shouldFire() is within the timer's period
342 // then the timer will keep firing. This should be VERY near impossible.
343 while (! timerList
.empty()) {
344 BTimer
*timer
= timerList
.top();
345 if (! timer
->shouldFire(now
))
350 timer
->fireTimeout();
352 if (timer
->isRecurring())
360 void BaseDisplay::addTimer(BTimer
*timer
) {
363 timerList
.push(timer
);
367 void BaseDisplay::removeTimer(BTimer
*timer
) {
368 timerList
.release(timer
);
373 * Grabs a button, but also grabs the button in every possible combination
374 * with the keyboard lock keys, so that they do not cancel out the event.
376 * if allow_scroll_lock is true then only the top half of the lock mask
377 * table is used and scroll lock is ignored. This value defaults to false.
379 void BaseDisplay::grabButton(unsigned int button
, unsigned int modifiers
,
380 Window grab_window
, bool owner_events
,
381 unsigned int event_mask
, int pointer_mode
,
382 int keyboard_mode
, Window confine_to
,
383 Cursor cursor
, bool allow_scroll_lock
) const {
384 unsigned int length
= (allow_scroll_lock
) ? MaskListLength
/ 2:
386 for (size_t cnt
= 0; cnt
< length
; ++cnt
)
387 XGrabButton(display
, button
, modifiers
| MaskList
[cnt
], grab_window
,
388 owner_events
, event_mask
, pointer_mode
, keyboard_mode
,
394 * Releases the grab on a button, and ungrabs all possible combinations of the
395 * keyboard lock keys.
397 void BaseDisplay::ungrabButton(unsigned int button
, unsigned int modifiers
,
398 Window grab_window
) const {
399 for (size_t cnt
= 0; cnt
< MaskListLength
; ++cnt
)
400 XUngrabButton(display
, button
, modifiers
| MaskList
[cnt
], grab_window
);
404 const ScreenInfo
* BaseDisplay::getScreenInfo(unsigned int s
) const {
405 if (s
< screenInfoList
.size())
406 return &screenInfoList
[s
];
407 return (const ScreenInfo
*) 0;
411 BGCCache
* BaseDisplay::gcCache(void) const {
413 gccache
= new BGCCache(this, screenInfoList
.size());
419 ScreenInfo::ScreenInfo(BaseDisplay
*d
, unsigned int num
) {
423 root_window
= RootWindow(basedisplay
->getXDisplay(), screen_number
);
425 rect
.setSize(WidthOfScreen(ScreenOfDisplay(basedisplay
->getXDisplay(),
427 HeightOfScreen(ScreenOfDisplay(basedisplay
->getXDisplay(),
430 If the default depth is at least 8 we will use that,
431 otherwise we try to find the largest TrueColor visual.
432 Preference is given to 24 bit over larger depths if 24 bit is an option.
435 depth
= DefaultDepth(basedisplay
->getXDisplay(), screen_number
);
436 visual
= DefaultVisual(basedisplay
->getXDisplay(), screen_number
);
437 colormap
= DefaultColormap(basedisplay
->getXDisplay(), screen_number
);
440 // search for a TrueColor Visual... if we can't find one...
441 // we will use the default visual for the screen
442 XVisualInfo vinfo_template
, *vinfo_return
;
446 vinfo_template
.screen
= screen_number
;
447 vinfo_template
.c_class
= TrueColor
;
449 vinfo_return
= XGetVisualInfo(basedisplay
->getXDisplay(),
450 VisualScreenMask
| VisualClassMask
,
451 &vinfo_template
, &vinfo_nitems
);
454 for (int i
= 0; i
< vinfo_nitems
; ++i
) {
455 if (vinfo_return
[i
].depth
> max_depth
) {
456 if (max_depth
== 24 && vinfo_return
[i
].depth
> 24)
457 break; // prefer 24 bit over 32
458 max_depth
= vinfo_return
[i
].depth
;
462 if (max_depth
< depth
) best
= -1;
466 depth
= vinfo_return
[best
].depth
;
467 visual
= vinfo_return
[best
].visual
;
468 colormap
= XCreateColormap(basedisplay
->getXDisplay(), root_window
,
475 // get the default display string and strip the screen number
476 string default_string
= DisplayString(basedisplay
->getXDisplay());
477 const string::size_type pos
= default_string
.rfind(".");
478 if (pos
!= string::npos
)
479 default_string
.resize(pos
);
481 display_string
= string("DISPLAY=") + default_string
+ '.' +
482 itostring(static_cast<unsigned long>(screen_number
));
485 xinerama_active
= False
;
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 if (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
500 is why no screen number is used in this function call. There should
501 never be more than one screen present with Xinerama active.
504 XineramaScreenInfo
*info
= XineramaQueryScreens(d
->getXDisplay(), &num
);
505 if (num
> 0 && info
) {
506 xinerama_areas
.reserve(num
);
507 for (int i
= 0; i
< num
; ++i
) {
508 xinerama_areas
.push_back(Rect(info
[i
].x_org
, info
[i
].y_org
,
509 info
[i
].width
, info
[i
].height
));
513 // if we can't find any xinerama regions, then we act as if it is not
514 // active, even though it said it was
515 xinerama_active
= True
;