]> Dogcows Code - chaz/openbox/blob - src/basedisplay.cc
make strings in main.cc translatable
[chaz/openbox] / src / basedisplay.cc
1 // -*- mode: C++; indent-tabs-mode: nil; -*-
2
3 #ifdef HAVE_CONFIG_H
4 # include "../config.h"
5 #endif // HAVE_CONFIG_H
6
7 extern "C" {
8 #include <X11/Xlib.h>
9 #include <X11/Xatom.h>
10 #include <X11/Xutil.h>
11 #include <X11/keysym.h>
12
13 #ifdef SHAPE
14 # include <X11/extensions/shape.h>
15 #endif // SHAPE
16
17 #ifdef XINERAMA
18 # include <X11/extensions/Xinerama.h>
19 #endif // XINERAMA
20
21 #ifdef HAVE_FCNTL_H
22 # include <fcntl.h>
23 #endif // HAVE_FCNTL_H
24
25 #ifdef HAVE_STDIO_H
26 # include <stdio.h>
27 #endif // HAVE_STDIO_H
28
29 #ifdef HAVE_STDLIB_H
30 # include <stdlib.h>
31 #endif // HAVE_STDLIB_H
32
33 #ifdef HAVE_STRING_H
34 # include <string.h>
35 #endif // HAVE_STRING_H
36
37 #ifdef HAVE_UNISTD_H
38 # include <sys/types.h>
39 # include <unistd.h>
40 #endif // HAVE_UNISTD_H
41
42 #ifdef HAVE_SYS_SELECT_H
43 # include <sys/select.h>
44 #endif // HAVE_SYS_SELECT_H
45
46 #ifdef HAVE_SIGNAL_H
47 # include <signal.h>
48 #endif // HAVE_SIGNAL_H
49
50 #ifdef HAVE_SYS_WAIT_H
51 # include <sys/types.h>
52 # include <sys/wait.h>
53 #endif // HAVE_SYS_WAIT_H
54 }
55
56 #include <string>
57 using std::string;
58
59 #include "basedisplay.hh"
60 #include "gccache.hh"
61 #include "timer.hh"
62 #include "util.hh"
63
64
65 // X error handler to handle any and all X errors while the application is
66 // running
67 static bool internal_error = False;
68
69 BaseDisplay *base_display;
70
71 static int handleXErrors(Display *d, XErrorEvent *e) {
72 #ifdef DEBUG
73 char errtxt[128];
74
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);
79 #else
80 // shutup gcc
81 (void) d;
82 (void) e;
83 #endif // DEBUG
84
85 if (internal_error) abort();
86
87 return(False);
88 }
89
90
91 // signal handler to allow for proper and gentle shutdown
92
93 static void signalhandler(int sig) {
94
95 static int re_enter = 0;
96
97 switch (sig) {
98 case SIGCHLD:
99 int status;
100 waitpid(-1, &status, WNOHANG | WUNTRACED);
101 break;
102
103 default:
104 if (base_display->handleSignal(sig))
105 return;
106
107 fprintf(stderr, "%s: signal %d caught\n",
108 base_display->getApplicationName(), sig);
109
110 if (! base_display->isStartup() && ! re_enter) {
111 internal_error = True;
112
113 re_enter = 1;
114 fprintf(stderr, "shutting down\n");
115 base_display->shutdown();
116 }
117
118 if (sig != SIGTERM && sig != SIGINT) {
119 fprintf(stderr, "aborting... dumping core\n");
120 abort();
121 }
122
123 exit(0);
124
125 break;
126 }
127 }
128
129
130 BaseDisplay::BaseDisplay(const char *app_name, const char *dpy_name) {
131 application_name = app_name;
132
133 run_state = STARTUP;
134
135 ::base_display = this;
136
137 struct sigaction action;
138
139 action.sa_handler = signalhandler;
140 action.sa_mask = sigset_t();
141 action.sa_flags = SA_NOCLDSTOP | SA_NODEFER;
142
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);
152
153 if (! (display = XOpenDisplay(dpy_name))) {
154 fprintf(stderr,
155 "BaseDisplay::BaseDisplay: connection to X server failed.\n");
156 ::exit(2);
157 } else if (fcntl(ConnectionNumber(display), F_SETFD, 1) == -1) {
158 fprintf(stderr,
159 "BaseDisplay::BaseDisplay: couldn't mark display connection "
160 "as close-on-exec\n");
161 ::exit(2);
162 }
163
164 display_name = XDisplayName(dpy_name);
165
166 #ifdef SHAPE
167 shape.extensions = XShapeQueryExtension(display, &shape.event_basep,
168 &shape.error_basep);
169 #else // !SHAPE
170 shape.extensions = False;
171 #endif // SHAPE
172
173 #ifdef XINERAMA
174 if (XineramaQueryExtension(display, &xinerama.event_basep,
175 &xinerama.error_basep) &&
176 XineramaQueryVersion(display, &xinerama.major, &xinerama.minor)) {
177 #ifdef DEBUG
178 fprintf(stderr,
179 "BaseDisplay::BaseDisplay: Found Xinerama version %d.%d\n",
180 xinerama.major, xinerama.minor);
181 #endif // DEBUG
182 xinerama.extensions = True;
183 } else {
184 xinerama.extensions = False;
185 }
186 #endif // XINERAMA
187
188 XSetErrorHandler((XErrorHandler) handleXErrors);
189
190 screenInfoList.reserve(ScreenCount(display));
191 for (int i = 0; i < ScreenCount(display); ++i)
192 screenInfoList.push_back(ScreenInfo(this, i));
193
194 NumLockMask = ScrollLockMask = 0;
195
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
201 };
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);
209
210 for (size_t cnt = 0; cnt < size; ++cnt) {
211 if (! modmap->modifiermap[cnt]) continue;
212
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];
217 }
218 }
219
220 MaskList[0] = 0;
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]);
229
230 if (modmap) XFreeModifiermap(const_cast<XModifierKeymap*>(modmap));
231
232 gccache = (BGCCache *) 0;
233 }
234
235
236 BaseDisplay::~BaseDisplay(void) {
237 delete gccache;
238
239 XCloseDisplay(display);
240 }
241
242
243 void BaseDisplay::eventLoop(void) {
244 run();
245
246 const int xfd = ConnectionNumber(display);
247
248 while (run_state == RUNNING && ! internal_error) {
249 if (XPending(display)) {
250 XEvent e;
251 XNextEvent(display, &e);
252 process_event(&e);
253 } else {
254 fd_set rfds;
255 timeval now, tm, *timeout = (timeval *) 0;
256
257 FD_ZERO(&rfds);
258 FD_SET(xfd, &rfds);
259
260 if (! timerList.empty()) {
261 const BTimer* const timer = timerList.top();
262
263 gettimeofday(&now, 0);
264 tm = timer->timeRemaining(now);
265
266 timeout = &tm;
267 }
268
269 select(xfd + 1, &rfds, 0, 0, timeout);
270
271 // check for timer timeout
272 gettimeofday(&now, 0);
273
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))
281 break;
282
283 timerList.pop();
284
285 timer->fireTimeout();
286 timer->halt();
287 if (timer->isRecurring())
288 timer->start();
289 }
290 }
291 }
292 }
293
294
295 void BaseDisplay::addTimer(BTimer *timer) {
296 if (! timer) return;
297
298 timerList.push(timer);
299 }
300
301
302 void BaseDisplay::removeTimer(BTimer *timer) {
303 timerList.release(timer);
304 }
305
306
307 /*
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.
310
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.
313 */
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:
320 MaskListLength;
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,
324 confine_to, cursor);
325 }
326
327
328 /*
329 * Releases the grab on a button, and ungrabs all possible combinations of the
330 * keyboard lock keys.
331 */
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);
336 }
337
338
339 const ScreenInfo* BaseDisplay::getScreenInfo(unsigned int s) const {
340 if (s < screenInfoList.size())
341 return &screenInfoList[s];
342 return (const ScreenInfo*) 0;
343 }
344
345
346 BGCCache* BaseDisplay::gcCache(void) const {
347 if (! gccache)
348 gccache = new BGCCache(this, screenInfoList.size());
349
350 return gccache;
351 }
352
353
354 ScreenInfo::ScreenInfo(BaseDisplay *d, unsigned int num) {
355 basedisplay = d;
356 screen_number = num;
357
358 root_window = RootWindow(basedisplay->getXDisplay(), screen_number);
359
360 rect.setSize(WidthOfScreen(ScreenOfDisplay(basedisplay->getXDisplay(),
361 screen_number)),
362 HeightOfScreen(ScreenOfDisplay(basedisplay->getXDisplay(),
363 screen_number)));
364 /*
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.
368 */
369
370 depth = DefaultDepth(basedisplay->getXDisplay(), screen_number);
371 visual = DefaultVisual(basedisplay->getXDisplay(), screen_number);
372 colormap = DefaultColormap(basedisplay->getXDisplay(), screen_number);
373
374 if (depth < 8) {
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;
378 int vinfo_nitems;
379 int best = -1;
380
381 vinfo_template.screen = screen_number;
382 vinfo_template.c_class = TrueColor;
383
384 vinfo_return = XGetVisualInfo(basedisplay->getXDisplay(),
385 VisualScreenMask | VisualClassMask,
386 &vinfo_template, &vinfo_nitems);
387 if (vinfo_return) {
388 int max_depth = 1;
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;
394 best = i;
395 }
396 }
397 if (max_depth < depth) best = -1;
398 }
399
400 if (best != -1) {
401 depth = vinfo_return[best].depth;
402 visual = vinfo_return[best].visual;
403 colormap = XCreateColormap(basedisplay->getXDisplay(), root_window,
404 visual, AllocNone);
405 }
406
407 XFree(vinfo_return);
408 }
409
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);
415
416 display_string = string("DISPLAY=") + default_string + '.' +
417 itostring(static_cast<unsigned long>(screen_number));
418
419 #ifdef XINERAMA
420 xinerama_active = False;
421
422 if (d->hasXineramaExtensions()) {
423 if (d->getXineramaMajorVersion() == 1) {
424 // we know the version 1(.1?) protocol
425
426 /*
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.
430 */
431 if (XineramaIsActive(d->getXDisplay())) {
432 /*
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.
437 */
438 int num;
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));
445 }
446 XFree(info);
447
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;
451 }
452 }
453 }
454 }
455 #endif // XINERAMA
456 }
This page took 0.052502 seconds and 4 git commands to generate.