1 // -*- mode: C++; indent-tabs-mode: nil; -*-
4 # include "../config.h"
5 #endif // HAVE_CONFIG_H
10 #include <X11/Xutil.h>
11 #include <X11/keysym.h>
14 # include <X11/extensions/shape.h>
18 # include <X11/extensions/Xinerama.h>
23 #endif // HAVE_FCNTL_H
27 #endif // HAVE_STDIO_H
31 #endif // HAVE_STDLIB_H
35 #endif // HAVE_STRING_H
38 # include <sys/types.h>
40 #endif // HAVE_UNISTD_H
42 #ifdef HAVE_SYS_SELECT_H
43 # include <sys/select.h>
44 #endif // HAVE_SYS_SELECT_H
48 #endif // HAVE_SIGNAL_H
52 # define SA_NODEFER SA_INTERRUPT
53 # else // !SA_INTERRUPT
54 # define SA_NODEFER (0)
55 # endif // SA_INTERRUPT
58 #ifdef HAVE_SYS_WAIT_H
59 # include <sys/types.h>
60 # include <sys/wait.h>
61 #endif // HAVE_SYS_WAIT_H
67 #include "basedisplay.hh"
73 // X error handler to handle any and all X errors while the application is
75 static bool internal_error
= False
;
77 BaseDisplay
*base_display
;
79 static int handleXErrors(Display
*d
, XErrorEvent
*e
) {
83 XGetErrorText(d
, e
->error_code
, errtxt
, 128);
84 fprintf(stderr
, "%s: X error: %s(%d) opcodes %d/%d\n resource 0x%lx\n",
85 base_display
->getApplicationName(), errtxt
, e
->error_code
,
86 e
->request_code
, e
->minor_code
, e
->resourceid
);
93 if (internal_error
) abort();
99 // signal handler to allow for proper and gentle shutdown
101 static void signalhandler(int sig
) {
103 static int re_enter
= 0;
108 waitpid(-1, &status
, WNOHANG
| WUNTRACED
);
112 if (base_display
->handleSignal(sig
))
115 fprintf(stderr
, "%s: signal %d caught\n",
116 base_display
->getApplicationName(), sig
);
118 if (! base_display
->isStartup() && ! re_enter
) {
119 internal_error
= True
;
122 fprintf(stderr
, "shutting down\n");
123 base_display
->shutdown();
126 if (sig
!= SIGTERM
&& sig
!= SIGINT
) {
127 fprintf(stderr
, "aborting... dumping core\n");
138 BaseDisplay::BaseDisplay(const char *app_name
, const char *dpy_name
) {
139 application_name
= app_name
;
143 ::base_display
= this;
145 struct sigaction action
;
147 action
.sa_handler
= signalhandler
;
148 action
.sa_mask
= sigset_t();
149 action
.sa_flags
= SA_NOCLDSTOP
| SA_NODEFER
;
151 sigaction(SIGPIPE
, &action
, NULL
);
152 sigaction(SIGSEGV
, &action
, NULL
);
153 sigaction(SIGFPE
, &action
, NULL
);
154 sigaction(SIGTERM
, &action
, NULL
);
155 sigaction(SIGINT
, &action
, NULL
);
156 sigaction(SIGCHLD
, &action
, NULL
);
157 sigaction(SIGHUP
, &action
, NULL
);
158 sigaction(SIGUSR1
, &action
, NULL
);
159 sigaction(SIGUSR2
, &action
, NULL
);
161 if (! (display
= XOpenDisplay(dpy_name
))) {
163 "BaseDisplay::BaseDisplay: connection to X server failed.\n");
165 } else if (fcntl(ConnectionNumber(display
), F_SETFD
, 1) == -1) {
167 "BaseDisplay::BaseDisplay: couldn't mark display connection "
168 "as close-on-exec\n");
172 display_name
= XDisplayName(dpy_name
);
175 shape
.extensions
= XShapeQueryExtension(display
, &shape
.event_basep
,
178 shape
.extensions
= False
;
182 if (XineramaQueryExtension(display
, &xinerama
.event_basep
,
183 &xinerama
.error_basep
) &&
184 XineramaQueryVersion(display
, &xinerama
.major
, &xinerama
.minor
)) {
187 "BaseDisplay::BaseDisplay: Found Xinerama version %d.%d\n",
188 xinerama
.major
, xinerama
.minor
);
190 xinerama
.extensions
= True
;
192 xinerama
.extensions
= False
;
196 XSetErrorHandler((XErrorHandler
) handleXErrors
);
198 screenInfoList
.reserve(ScreenCount(display
));
199 for (int i
= 0; i
< ScreenCount(display
); ++i
)
200 screenInfoList
.push_back(ScreenInfo(this, i
));
202 NumLockMask
= ScrollLockMask
= 0;
204 const XModifierKeymap
* const modmap
= XGetModifierMapping(display
);
205 if (modmap
&& modmap
->max_keypermod
> 0) {
206 const int mask_table
[] = {
207 ShiftMask
, LockMask
, ControlMask
, Mod1Mask
,
208 Mod2Mask
, Mod3Mask
, Mod4Mask
, Mod5Mask
210 const size_t size
= (sizeof(mask_table
) / sizeof(mask_table
[0])) *
211 modmap
->max_keypermod
;
212 // get the values of the keyboard lock modifiers
213 // Note: Caps lock is not retrieved the same way as Scroll and Num lock
214 // since it doesn't need to be.
215 const KeyCode num_lock
= XKeysymToKeycode(display
, XK_Num_Lock
);
216 const KeyCode scroll_lock
= XKeysymToKeycode(display
, XK_Scroll_Lock
);
218 for (size_t cnt
= 0; cnt
< size
; ++cnt
) {
219 if (! modmap
->modifiermap
[cnt
]) continue;
221 if (num_lock
== modmap
->modifiermap
[cnt
])
222 NumLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
223 if (scroll_lock
== modmap
->modifiermap
[cnt
])
224 ScrollLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
229 MaskList
[1] = LockMask
;
230 MaskList
[2] = NumLockMask
;
231 MaskList
[3] = LockMask
| NumLockMask
;
232 MaskList
[4] = ScrollLockMask
;
233 MaskList
[5] = ScrollLockMask
| LockMask
;
234 MaskList
[6] = ScrollLockMask
| NumLockMask
;
235 MaskList
[7] = ScrollLockMask
| LockMask
| NumLockMask
;
236 MaskListLength
= sizeof(MaskList
) / sizeof(MaskList
[0]);
238 if (modmap
) XFreeModifiermap(const_cast<XModifierKeymap
*>(modmap
));
240 gccache
= (BGCCache
*) 0;
244 BaseDisplay::~BaseDisplay(void) {
247 XCloseDisplay(display
);
251 void BaseDisplay::eventLoop(void) {
254 const int xfd
= ConnectionNumber(display
);
256 while (run_state
== RUNNING
&& ! internal_error
) {
257 if (XPending(display
)) {
259 XNextEvent(display
, &e
);
263 timeval now
, tm
, *timeout
= (timeval
*) 0;
268 if (! timerList
.empty()) {
269 const BTimer
* const timer
= timerList
.top();
271 gettimeofday(&now
, 0);
272 tm
= timer
->timeRemaining(now
);
277 select(xfd
+ 1, &rfds
, 0, 0, timeout
);
279 // check for timer timeout
280 gettimeofday(&now
, 0);
282 // there is a small chance for deadlock here:
283 // *IF* the timer list keeps getting refreshed *AND* the time between
284 // timer->start() and timer->shouldFire() is within the timer's period
285 // then the timer will keep firing. This should be VERY near impossible.
286 while (! timerList
.empty()) {
287 BTimer
*timer
= timerList
.top();
288 if (! timer
->shouldFire(now
))
293 timer
->fireTimeout();
295 if (timer
->isRecurring())
303 void BaseDisplay::addTimer(BTimer
*timer
) {
306 timerList
.push(timer
);
310 void BaseDisplay::removeTimer(BTimer
*timer
) {
311 timerList
.release(timer
);
316 * Grabs a button, but also grabs the button in every possible combination
317 * with the keyboard lock keys, so that they do not cancel out the event.
319 * if allow_scroll_lock is true then only the top half of the lock mask
320 * table is used and scroll lock is ignored. This value defaults to false.
322 void BaseDisplay::grabButton(unsigned int button
, unsigned int modifiers
,
323 Window grab_window
, bool owner_events
,
324 unsigned int event_mask
, int pointer_mode
,
325 int keyboard_mode
, Window confine_to
,
326 Cursor cursor
, bool allow_scroll_lock
) const {
327 unsigned int length
= (allow_scroll_lock
) ? MaskListLength
/ 2:
329 for (size_t cnt
= 0; cnt
< length
; ++cnt
)
330 XGrabButton(display
, button
, modifiers
| MaskList
[cnt
], grab_window
,
331 owner_events
, event_mask
, pointer_mode
, keyboard_mode
,
337 * Releases the grab on a button, and ungrabs all possible combinations of the
338 * keyboard lock keys.
340 void BaseDisplay::ungrabButton(unsigned int button
, unsigned int modifiers
,
341 Window grab_window
) const {
342 for (size_t cnt
= 0; cnt
< MaskListLength
; ++cnt
)
343 XUngrabButton(display
, button
, modifiers
| MaskList
[cnt
], grab_window
);
347 const ScreenInfo
* BaseDisplay::getScreenInfo(unsigned int s
) const {
348 if (s
< screenInfoList
.size())
349 return &screenInfoList
[s
];
350 return (const ScreenInfo
*) 0;
354 BGCCache
* BaseDisplay::gcCache(void) const {
356 gccache
= new BGCCache(this, screenInfoList
.size());
362 ScreenInfo::ScreenInfo(BaseDisplay
*d
, unsigned int num
) {
366 root_window
= RootWindow(basedisplay
->getXDisplay(), screen_number
);
368 rect
.setSize(WidthOfScreen(ScreenOfDisplay(basedisplay
->getXDisplay(),
370 HeightOfScreen(ScreenOfDisplay(basedisplay
->getXDisplay(),
373 If the default depth is at least 8 we will use that,
374 otherwise we try to find the largest TrueColor visual.
375 Preference is given to 24 bit over larger depths if 24 bit is an option.
378 depth
= DefaultDepth(basedisplay
->getXDisplay(), screen_number
);
379 visual
= DefaultVisual(basedisplay
->getXDisplay(), screen_number
);
380 colormap
= DefaultColormap(basedisplay
->getXDisplay(), screen_number
);
383 // search for a TrueColor Visual... if we can't find one...
384 // we will use the default visual for the screen
385 XVisualInfo vinfo_template
, *vinfo_return
;
389 vinfo_template
.screen
= screen_number
;
390 vinfo_template
.c_class
= TrueColor
;
392 vinfo_return
= XGetVisualInfo(basedisplay
->getXDisplay(),
393 VisualScreenMask
| VisualClassMask
,
394 &vinfo_template
, &vinfo_nitems
);
397 for (int i
= 0; i
< vinfo_nitems
; ++i
) {
398 if (vinfo_return
[i
].depth
> max_depth
) {
399 if (max_depth
== 24 && vinfo_return
[i
].depth
> 24)
400 break; // prefer 24 bit over 32
401 max_depth
= vinfo_return
[i
].depth
;
405 if (max_depth
< depth
) best
= -1;
409 depth
= vinfo_return
[best
].depth
;
410 visual
= vinfo_return
[best
].visual
;
411 colormap
= XCreateColormap(basedisplay
->getXDisplay(), root_window
,
418 // get the default display string and strip the screen number
419 string default_string
= DisplayString(basedisplay
->getXDisplay());
420 const string::size_type pos
= default_string
.rfind(".");
421 if (pos
!= string::npos
)
422 default_string
.resize(pos
);
424 display_string
= string("DISPLAY=") + default_string
+ '.' +
425 itostring(static_cast<unsigned long>(screen_number
));
428 xinerama_active
= False
;
430 if (d
->hasXineramaExtensions()) {
431 if (d
->getXineramaMajorVersion() == 1) {
432 // we know the version 1(.1?) protocol
435 in this version of Xinerama, we can't query on a per-screen basis, but
436 in future versions we should be able, so the 'activeness' is checked
437 on a pre-screen basis anyways.
439 if (XineramaIsActive(d
->getXDisplay())) {
441 If Xinerama is being used, there there is only going to be one screen
442 present. We still, of course, want to use the screen class, but that
443 is why no screen number is used in this function call. There should
444 never be more than one screen present with Xinerama active.
447 XineramaScreenInfo
*info
= XineramaQueryScreens(d
->getXDisplay(), &num
);
448 if (num
> 0 && info
) {
449 xinerama_areas
.reserve(num
);
450 for (int i
= 0; i
< num
; ++i
) {
451 xinerama_areas
.push_back(Rect(info
[i
].x_org
, info
[i
].y_org
,
452 info
[i
].width
, info
[i
].height
));
456 // if we can't find any xinerama regions, then we act as if it is not
457 // active, even though it said it was
458 xinerama_active
= True
;