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
50 #ifdef HAVE_SYS_WAIT_H
51 # include <sys/types.h>
52 # include <sys/wait.h>
53 #endif // HAVE_SYS_WAIT_H
59 #include "basedisplay.hh"
65 // X error handler to handle any and all X errors while the application is
67 static bool internal_error
= False
;
69 BaseDisplay
*base_display
;
71 static int handleXErrors(Display
*d
, XErrorEvent
*e
) {
75 XGetErrorText(d
, e
->error_code
, errtxt
, 128); // XXX: use this!!
76 fprintf(stderr
, "%s: X error: %s(%d) opcodes %d/%d\n resource 0x%lx\n",
77 base_display
->getApplicationName(), errtxt
, e
->error_code
,
78 e
->request_code
, e
->minor_code
, e
->resourceid
);
85 if (internal_error
) abort();
91 // signal handler to allow for proper and gentle shutdown
93 static void signalhandler(int sig
) {
95 static int re_enter
= 0;
100 waitpid(-1, &status
, WNOHANG
| WUNTRACED
);
104 if (base_display
->handleSignal(sig
))
107 fprintf(stderr
, "%s: signal %d caught\n",
108 base_display
->getApplicationName(), sig
);
110 if (! base_display
->isStartup() && ! re_enter
) {
111 internal_error
= True
;
114 fprintf(stderr
, "shutting down\n");
115 base_display
->shutdown();
118 if (sig
!= SIGTERM
&& sig
!= SIGINT
) {
119 fprintf(stderr
, "aborting... dumping core\n");
130 BaseDisplay::BaseDisplay(const char *app_name
, const char *dpy_name
) {
131 application_name
= app_name
;
135 ::base_display
= this;
137 struct sigaction action
;
139 action
.sa_handler
= signalhandler
;
140 action
.sa_mask
= sigset_t();
141 action
.sa_flags
= SA_NOCLDSTOP
| SA_NODEFER
;
143 sigaction(SIGPIPE
, &action
, NULL
);
144 sigaction(SIGSEGV
, &action
, NULL
);
145 sigaction(SIGFPE
, &action
, NULL
);
146 sigaction(SIGTERM
, &action
, NULL
);
147 sigaction(SIGINT
, &action
, NULL
);
148 sigaction(SIGCHLD
, &action
, NULL
);
149 sigaction(SIGHUP
, &action
, NULL
);
150 sigaction(SIGUSR1
, &action
, NULL
);
151 sigaction(SIGUSR2
, &action
, NULL
);
153 if (! (display
= XOpenDisplay(dpy_name
))) {
155 "BaseDisplay::BaseDisplay: connection to X server failed.\n");
157 } else if (fcntl(ConnectionNumber(display
), F_SETFD
, 1) == -1) {
159 "BaseDisplay::BaseDisplay: couldn't mark display connection "
160 "as close-on-exec\n");
164 display_name
= XDisplayName(dpy_name
);
167 shape
.extensions
= XShapeQueryExtension(display
, &shape
.event_basep
,
170 shape
.extensions
= False
;
174 if (XineramaQueryExtension(display
, &xinerama
.event_basep
,
175 &xinerama
.error_basep
) &&
176 XineramaQueryVersion(display
, &xinerama
.major
, &xinerama
.minor
)) {
179 "BaseDisplay::BaseDisplay: Found Xinerama version %d.%d\n",
180 xinerama
.major
, xinerama
.minor
);
182 xinerama
.extensions
= True
;
184 xinerama
.extensions
= False
;
188 XSetErrorHandler((XErrorHandler
) handleXErrors
);
190 screenInfoList
.reserve(ScreenCount(display
));
191 for (int i
= 0; i
< ScreenCount(display
); ++i
)
192 screenInfoList
.push_back(ScreenInfo(this, i
));
194 NumLockMask
= ScrollLockMask
= 0;
196 const XModifierKeymap
* const modmap
= XGetModifierMapping(display
);
197 if (modmap
&& modmap
->max_keypermod
> 0) {
198 const int mask_table
[] = {
199 ShiftMask
, LockMask
, ControlMask
, Mod1Mask
,
200 Mod2Mask
, Mod3Mask
, Mod4Mask
, Mod5Mask
202 const size_t size
= (sizeof(mask_table
) / sizeof(mask_table
[0])) *
203 modmap
->max_keypermod
;
204 // get the values of the keyboard lock modifiers
205 // Note: Caps lock is not retrieved the same way as Scroll and Num lock
206 // since it doesn't need to be.
207 const KeyCode num_lock
= XKeysymToKeycode(display
, XK_Num_Lock
);
208 const KeyCode scroll_lock
= XKeysymToKeycode(display
, XK_Scroll_Lock
);
210 for (size_t cnt
= 0; cnt
< size
; ++cnt
) {
211 if (! modmap
->modifiermap
[cnt
]) continue;
213 if (num_lock
== modmap
->modifiermap
[cnt
])
214 NumLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
215 if (scroll_lock
== modmap
->modifiermap
[cnt
])
216 ScrollLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
221 MaskList
[1] = LockMask
;
222 MaskList
[2] = NumLockMask
;
223 MaskList
[3] = LockMask
| NumLockMask
;
224 MaskList
[4] = ScrollLockMask
;
225 MaskList
[5] = ScrollLockMask
| LockMask
;
226 MaskList
[6] = ScrollLockMask
| NumLockMask
;
227 MaskList
[7] = ScrollLockMask
| LockMask
| NumLockMask
;
228 MaskListLength
= sizeof(MaskList
) / sizeof(MaskList
[0]);
230 if (modmap
) XFreeModifiermap(const_cast<XModifierKeymap
*>(modmap
));
232 gccache
= (BGCCache
*) 0;
236 BaseDisplay::~BaseDisplay(void) {
239 XCloseDisplay(display
);
243 void BaseDisplay::eventLoop(void) {
246 const int xfd
= ConnectionNumber(display
);
248 while (run_state
== RUNNING
&& ! internal_error
) {
249 if (XPending(display
)) {
251 XNextEvent(display
, &e
);
255 timeval now
, tm
, *timeout
= (timeval
*) 0;
260 if (! timerList
.empty()) {
261 const BTimer
* const timer
= timerList
.top();
263 gettimeofday(&now
, 0);
264 tm
= timer
->timeRemaining(now
);
269 select(xfd
+ 1, &rfds
, 0, 0, timeout
);
271 // check for timer timeout
272 gettimeofday(&now
, 0);
274 // there is a small chance for deadlock here:
275 // *IF* the timer list keeps getting refreshed *AND* the time between
276 // timer->start() and timer->shouldFire() is within the timer's period
277 // then the timer will keep firing. This should be VERY near impossible.
278 while (! timerList
.empty()) {
279 BTimer
*timer
= timerList
.top();
280 if (! timer
->shouldFire(now
))
285 timer
->fireTimeout();
287 if (timer
->isRecurring())
295 void BaseDisplay::addTimer(BTimer
*timer
) {
298 timerList
.push(timer
);
302 void BaseDisplay::removeTimer(BTimer
*timer
) {
303 timerList
.release(timer
);
308 * Grabs a button, but also grabs the button in every possible combination
309 * with the keyboard lock keys, so that they do not cancel out the event.
311 * if allow_scroll_lock is true then only the top half of the lock mask
312 * table is used and scroll lock is ignored. This value defaults to false.
314 void BaseDisplay::grabButton(unsigned int button
, unsigned int modifiers
,
315 Window grab_window
, bool owner_events
,
316 unsigned int event_mask
, int pointer_mode
,
317 int keyboard_mode
, Window confine_to
,
318 Cursor cursor
, bool allow_scroll_lock
) const {
319 unsigned int length
= (allow_scroll_lock
) ? MaskListLength
/ 2:
321 for (size_t cnt
= 0; cnt
< length
; ++cnt
)
322 XGrabButton(display
, button
, modifiers
| MaskList
[cnt
], grab_window
,
323 owner_events
, event_mask
, pointer_mode
, keyboard_mode
,
329 * Releases the grab on a button, and ungrabs all possible combinations of the
330 * keyboard lock keys.
332 void BaseDisplay::ungrabButton(unsigned int button
, unsigned int modifiers
,
333 Window grab_window
) const {
334 for (size_t cnt
= 0; cnt
< MaskListLength
; ++cnt
)
335 XUngrabButton(display
, button
, modifiers
| MaskList
[cnt
], grab_window
);
339 const ScreenInfo
* BaseDisplay::getScreenInfo(unsigned int s
) const {
340 if (s
< screenInfoList
.size())
341 return &screenInfoList
[s
];
342 return (const ScreenInfo
*) 0;
346 BGCCache
* BaseDisplay::gcCache(void) const {
348 gccache
= new BGCCache(this, screenInfoList
.size());
354 ScreenInfo::ScreenInfo(BaseDisplay
*d
, unsigned int num
) {
358 root_window
= RootWindow(basedisplay
->getXDisplay(), screen_number
);
360 rect
.setSize(WidthOfScreen(ScreenOfDisplay(basedisplay
->getXDisplay(),
362 HeightOfScreen(ScreenOfDisplay(basedisplay
->getXDisplay(),
365 If the default depth is at least 8 we will use that,
366 otherwise we try to find the largest TrueColor visual.
367 Preference is given to 24 bit over larger depths if 24 bit is an option.
370 depth
= DefaultDepth(basedisplay
->getXDisplay(), screen_number
);
371 visual
= DefaultVisual(basedisplay
->getXDisplay(), screen_number
);
372 colormap
= DefaultColormap(basedisplay
->getXDisplay(), screen_number
);
375 // search for a TrueColor Visual... if we can't find one...
376 // we will use the default visual for the screen
377 XVisualInfo vinfo_template
, *vinfo_return
;
381 vinfo_template
.screen
= screen_number
;
382 vinfo_template
.c_class
= TrueColor
;
384 vinfo_return
= XGetVisualInfo(basedisplay
->getXDisplay(),
385 VisualScreenMask
| VisualClassMask
,
386 &vinfo_template
, &vinfo_nitems
);
389 for (int i
= 0; i
< vinfo_nitems
; ++i
) {
390 if (vinfo_return
[i
].depth
> max_depth
) {
391 if (max_depth
== 24 && vinfo_return
[i
].depth
> 24)
392 break; // prefer 24 bit over 32
393 max_depth
= vinfo_return
[i
].depth
;
397 if (max_depth
< depth
) best
= -1;
401 depth
= vinfo_return
[best
].depth
;
402 visual
= vinfo_return
[best
].visual
;
403 colormap
= XCreateColormap(basedisplay
->getXDisplay(), root_window
,
410 // get the default display string and strip the screen number
411 string default_string
= DisplayString(basedisplay
->getXDisplay());
412 const string::size_type pos
= default_string
.rfind(".");
413 if (pos
!= string::npos
)
414 default_string
.resize(pos
);
416 display_string
= string("DISPLAY=") + default_string
+ '.' +
417 itostring(static_cast<unsigned long>(screen_number
));
420 xinerama_active
= False
;
422 if (d
->hasXineramaExtensions()) {
423 if (d
->getXineramaMajorVersion() == 1) {
424 // we know the version 1(.1?) protocol
427 in this version of Xinerama, we can't query on a per-screen basis, but
428 in future versions we should be able, so the 'activeness' is checked
429 on a pre-screen basis anyways.
431 if (XineramaIsActive(d
->getXDisplay())) {
433 If Xinerama is being used, there there is only going to be one screen
434 present. We still, of course, want to use the screen class, but that
435 is why no screen number is used in this function call. There should
436 never be more than one screen present with Xinerama active.
439 XineramaScreenInfo
*info
= XineramaQueryScreens(d
->getXDisplay(), &num
);
440 if (num
> 0 && info
) {
441 xinerama_areas
.reserve(num
);
442 for (int i
= 0; i
< num
; ++i
) {
443 xinerama_areas
.push_back(Rect(info
[i
].x_org
, info
[i
].y_org
,
444 info
[i
].width
, info
[i
].height
));
448 // if we can't find any xinerama regions, then we act as if it is not
449 // active, even though it said it was
450 xinerama_active
= True
;