1 // BaseDisplay.cc for Blackbox - an X11 Window manager
2 // Copyright (c) 1997 - 2000 Brad Hughes (bhughes@tcac.net)
4 // Permission is hereby granted, free of charge, to any person obtaining a
5 // copy of this software and associated documentation files (the "Software"),
6 // to deal in the Software without restriction, including without limitation
7 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 // and/or sell copies of the Software, and to permit persons to whom the
9 // Software is furnished to do so, subject to the following conditions:
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 // DEALINGS IN THE SOFTWARE.
24 // stupid macros needed to access some functions in version 2 of the GNU C
32 # include "../config.h"
33 #endif // HAVE_CONFIG_H
36 #include <X11/Xatom.h>
37 #include <X11/Xutil.h>
38 #include <X11/cursorfont.h>
41 # include <X11/extensions/shape.h>
44 #include "BaseDisplay.hh"
45 #include "LinkedList.hh"
50 #endif // HAVE_FCNTL_H
54 #endif // HAVE_STDIO_H
59 #endif // STDC_HEADERS
66 # include <sys/types.h>
68 #endif // HAVE_UNISTD_H
70 #ifdef HAVE_SYS_SELECT_H
71 # include <sys/select.h>
72 #endif // HAVE_SYS_SELECT_H
76 #endif // HAVE_SIGNAL_H
78 #ifdef HAVE_SYS_SIGNAL_H
79 // # include <sys/signal.h>
80 #endif // HAVE_SYS_SIGNAL_H
84 # define SA_NODEFER SA_INTERRUPT
85 # else // !SA_INTERRUPT
86 # define SA_NODEFER (0)
87 # endif // SA_INTERRUPT
90 #ifdef HAVE_SYS_WAIT_H
91 # include <sys/types.h>
92 # include <sys/wait.h>
93 #endif // HAVE_SYS_WAIT_H
96 // X error handler to handle any and all X errors while the application is
98 static Bool internal_error
= False
;
99 static Window last_bad_window
= None
;
101 BaseDisplay
*base_display
;
104 static int handleXErrors(Display
* d
, XErrorEvent
* e
)
108 XGetErrorText(d
, e
->error_code
, errtxt
, 128);
109 fprintf(stderr
, "%s: X error: %s(%d) opcodes %d/%d\n"
111 base_display
->getApplicationName(), errtxt
, e
->error_code
,
112 e
->request_code
, e
->minor_code
, e
->resourceid
);
114 static int handleXErrors(Display
*, XErrorEvent
* e
)
118 if (e
->error_code
== BadWindow
)
119 last_bad_window
= e
->resourceid
;
127 // signal handler to allow for proper and gentle shutdown
129 #ifndef HAVE_SIGACTION
130 static RETSIGTYPE
signalhandler(int sig
)
132 #else // HAVE_SIGACTION
133 static void signalhandler(int sig
)
135 #endif // HAVE_SIGACTION
137 static int re_enter
= 0;
142 waitpid(-1, &status
, WNOHANG
| WUNTRACED
);
144 #ifndef HAVE_SIGACTION
145 // assume broken, braindead sysv signal semantics
146 signal(SIGCHLD
, (RETSIGTYPE(*)(int)) signalhandler
);
147 #endif // HAVE_SIGACTION
152 if (base_display
->handleSignal(sig
)) {
154 #ifndef HAVE_SIGACTION
155 // assume broken, braindead sysv signal semantics
156 signal(sig
, (RETSIGTYPE(*)(int)) signalhandler
);
157 #endif // HAVE_SIGACTION
162 fprintf(stderr
, "%s: signal %d caught\n",
163 base_display
->getApplicationName(), sig
);
165 if (!base_display
->isStartup() && !re_enter
) {
166 internal_error
= True
;
169 fprintf(stderr
, "shutting down\n");
170 base_display
->shutdown();
173 if (sig
!= SIGTERM
&& sig
!= SIGINT
) {
174 fprintf(stderr
, "aborting... dumping core\n");
185 BaseDisplay::BaseDisplay(char *app_name
, char *dpy_name
)
188 application_name
= app_name
;
193 last_bad_window
= None
;
195 ::base_display
= this;
197 #ifdef HAVE_SIGACTION
198 struct sigaction action
;
200 action
.sa_handler
= signalhandler
;
201 action
.sa_mask
= sigset_t();
202 action
.sa_flags
= SA_NOCLDSTOP
| SA_NODEFER
;
204 sigaction(SIGSEGV
, &action
, NULL
);
205 sigaction(SIGFPE
, &action
, NULL
);
206 sigaction(SIGTERM
, &action
, NULL
);
207 sigaction(SIGINT
, &action
, NULL
);
208 sigaction(SIGCHLD
, &action
, NULL
);
209 sigaction(SIGHUP
, &action
, NULL
);
210 sigaction(SIGUSR1
, &action
, NULL
);
211 sigaction(SIGUSR2
, &action
, NULL
);
212 #else // !HAVE_SIGACTION
213 signal(SIGSEGV
, (RETSIGTYPE(*)(int)) signalhandler
);
214 signal(SIGFPE
, (RETSIGTYPE(*)(int)) signalhandler
);
215 signal(SIGTERM
, (RETSIGTYPE(*)(int)) signalhandler
);
216 signal(SIGINT
, (RETSIGTYPE(*)(int)) signalhandler
);
217 signal(SIGUSR1
, (RETSIGTYPE(*)(int)) signalhandler
);
218 signal(SIGUSR2
, (RETSIGTYPE(*)(int)) signalhandler
);
219 signal(SIGHUP
, (RETSIGTYPE(*)(int)) signalhandler
);
220 signal(SIGCHLD
, (RETSIGTYPE(*)(int)) signalhandler
);
221 #endif // HAVE_SIGACTION
223 if (!(display
= XOpenDisplay(dpy_name
))) {
225 "BaseDisplay::BaseDisplay: connection to X server failed.\n");
227 } else if (fcntl(ConnectionNumber(display
), F_SETFD
, 1) == -1) {
229 "BaseDisplay::BaseDisplay: couldn't mark display connection "
230 "as close-on-exec\n");
234 number_of_screens
= ScreenCount(display
);
235 display_name
= XDisplayName(dpy_name
);
238 shape
.extensions
= XShapeQueryExtension(display
, &shape
.event_basep
,
241 shape
.extensions
= False
;
244 xa_wm_colormap_windows
=
245 XInternAtom(display
, "WM_COLORMAP_WINDOWS", False
);
246 xa_wm_protocols
= XInternAtom(display
, "WM_PROTOCOLS", False
);
247 xa_wm_state
= XInternAtom(display
, "WM_STATE", False
);
248 xa_wm_change_state
= XInternAtom(display
, "WM_CHANGE_STATE", False
);
249 xa_wm_delete_window
= XInternAtom(display
, "WM_DELETE_WINDOW", False
);
250 xa_wm_take_focus
= XInternAtom(display
, "WM_TAKE_FOCUS", False
);
251 motif_wm_hints
= XInternAtom(display
, "_MOTIF_WM_HINTS", False
);
253 blackbox_hints
= XInternAtom(display
, "_BLACKBOX_HINTS", False
);
254 blackbox_attributes
=
255 XInternAtom(display
, "_BLACKBOX_ATTRIBUTES", False
);
256 blackbox_change_attributes
=
257 XInternAtom(display
, "_BLACKBOX_CHANGE_ATTRIBUTES", False
);
259 blackbox_structure_messages
=
260 XInternAtom(display
, "_BLACKBOX_STRUCTURE_MESSAGES", False
);
261 blackbox_notify_startup
=
262 XInternAtom(display
, "_BLACKBOX_NOTIFY_STARTUP", False
);
263 blackbox_notify_window_add
=
264 XInternAtom(display
, "_BLACKBOX_NOTIFY_WINDOW_ADD", False
);
265 blackbox_notify_window_del
=
266 XInternAtom(display
, "_BLACKBOX_NOTIFY_WINDOW_DEL", False
);
267 blackbox_notify_current_workspace
=
268 XInternAtom(display
, "_BLACKBOX_NOTIFY_CURRENT_WORKSPACE", False
);
269 blackbox_notify_workspace_count
=
270 XInternAtom(display
, "_BLACKBOX_NOTIFY_WORKSPACE_COUNT", False
);
271 blackbox_notify_window_focus
=
272 XInternAtom(display
, "_BLACKBOX_NOTIFY_WINDOW_FOCUS", False
);
273 blackbox_notify_window_raise
=
274 XInternAtom(display
, "_BLACKBOX_NOTIFY_WINDOW_RAISE", False
);
275 blackbox_notify_window_lower
=
276 XInternAtom(display
, "_BLACKBOX_NOTIFY_WINDOW_LOWER", False
);
278 blackbox_change_workspace
=
279 XInternAtom(display
, "_BLACKBOX_CHANGE_WORKSPACE", False
);
280 blackbox_change_window_focus
=
281 XInternAtom(display
, "_BLACKBOX_CHANGE_WINDOW_FOCUS", False
);
282 blackbox_cycle_window_focus
=
283 XInternAtom(display
, "_BLACKBOX_CYCLE_WINDOW_FOCUS", False
);
287 net_supported
= XInternAtom(display
, "_NET_SUPPORTED", False
);
288 net_client_list
= XInternAtom(display
, "_NET_CLIENT_LIST", False
);
289 net_client_list_stacking
=
290 XInternAtom(display
, "_NET_CLIENT_LIST_STACKING", False
);
291 net_number_of_desktops
=
292 XInternAtom(display
, "_NET_NUMBER_OF_DESKTOPS", False
);
293 net_desktop_geometry
=
294 XInternAtom(display
, "_NET_DESKTOP_GEOMETRY", False
);
295 net_desktop_viewport
=
296 XInternAtom(display
, "_NET_DESKTOP_VIEWPORT", False
);
297 net_current_desktop
=
298 XInternAtom(display
, "_NET_CURRENT_DESKTOP", False
);
299 net_desktop_names
= XInternAtom(display
, "_NET_DESKTOP_NAMES", False
);
300 net_active_window
= XInternAtom(display
, "_NET_ACTIVE_WINDOW", False
);
301 net_workarea
= XInternAtom(display
, "_NET_WORKAREA", False
);
302 net_supporting_wm_check
=
303 XInternAtom(display
, "_NET_SUPPORTING_WM_CHECK", False
);
304 net_virtual_roots
= XInternAtom(display
, "_NET_VIRTUAL_ROOTS", False
);
306 net_close_window
= XInternAtom(display
, "_NET_CLOSE_WINDOW", False
);
307 net_wm_moveresize
= XInternAtom(display
, "_NET_WM_MOVERESIZE", False
);
309 net_properties
= XInternAtom(display
, "_NET_PROPERTIES", False
);
310 net_wm_name
= XInternAtom(display
, "_NET_WM_NAME", False
);
311 net_wm_desktop
= XInternAtom(display
, "_NET_WM_DESKTOP", False
);
312 net_wm_window_type
= XInternAtom(display
, "_NET_WM_WINDOW_TYPE", False
);
313 net_wm_state
= XInternAtom(display
, "_NET_WM_STATE", False
);
314 net_wm_strut
= XInternAtom(display
, "_NET_WM_STRUT", False
);
315 net_wm_icon_geometry
=
316 XInternAtom(display
, "_NET_WM_ICON_GEOMETRY", False
);
317 net_wm_icon
= XInternAtom(display
, "_NET_WM_ICON", False
);
318 net_wm_pid
= XInternAtom(display
, "_NET_WM_PID", False
);
319 net_wm_handled_icons
=
320 XInternAtom(display
, "_NET_WM_HANDLED_ICONS", False
);
322 net_wm_ping
= XInternAtom(display
, "_NET_WM_PING", False
);
326 cursor
.session
= XCreateFontCursor(display
, XC_left_ptr
);
327 cursor
.move
= XCreateFontCursor(display
, XC_fleur
);
328 cursor
.ll_angle
= XCreateFontCursor(display
, XC_ll_angle
);
329 cursor
.lr_angle
= XCreateFontCursor(display
, XC_lr_angle
);
331 XSetErrorHandler((XErrorHandler
) handleXErrors
);
333 timerList
= new LinkedList
< BTimer
>;
335 screenInfoList
= new LinkedList
< ScreenInfo
>;
337 for (i
= 0; i
< number_of_screens
; i
++) {
338 ScreenInfo
*screeninfo
= new ScreenInfo(this, i
);
339 screenInfoList
->insert(screeninfo
);
344 BaseDisplay::~BaseDisplay(void)
347 while (screenInfoList
->count()) {
348 ScreenInfo
*si
= screenInfoList
->first();
350 screenInfoList
->remove(si
);
354 delete screenInfoList
;
356 // we don't create the BTimers, we don't delete them
357 while (timerList
->count())
358 timerList
->remove(0);
362 XCloseDisplay(display
);
366 void BaseDisplay::eventLoop(void)
370 int xfd
= ConnectionNumber(display
);
371 time_t lastCheck
= time(NULL
);
373 while ((!_shutdown
) && (!internal_error
)) {
374 if (XPending(display
)) {
376 XNextEvent(display
, &e
);
378 if (last_bad_window
!= None
&& e
.xany
.window
== last_bad_window
) {
380 fprintf(stderr
, "BaseDisplay::eventLoop(): "
381 "removing bad window from event queue\n");
384 last_bad_window
= None
;
387 } else if (time(NULL
) - lastCheck
> 10) {
389 lastCheck
= time(NULL
);
393 timeval now
, tm
, *timeout
= (timeval
*) 0;
398 if (timerList
->count()) {
399 gettimeofday(&now
, 0);
401 tm
.tv_sec
= tm
.tv_usec
= 0l;
403 BTimer
*timer
= timerList
->first();
405 tm
.tv_sec
= timer
->getStartTime().tv_sec
+
406 timer
->getTimeout().tv_sec
- now
.tv_sec
;
407 tm
.tv_usec
= timer
->getStartTime().tv_usec
+
408 timer
->getTimeout().tv_usec
- now
.tv_usec
;
410 while (tm
.tv_usec
>= 1000000) {
412 tm
.tv_usec
-= 1000000;
415 while (tm
.tv_usec
< 0) {
418 tm
.tv_usec
+= 1000000;
428 select(xfd
+ 1, &rfds
, 0, 0, timeout
);
430 // check for timer timeout
431 gettimeofday(&now
, 0);
433 LinkedListIterator
< BTimer
> it(timerList
);
434 for (; it
.current(); it
++) {
435 tm
.tv_sec
= it
.current()->getStartTime().tv_sec
+
436 it
.current()->getTimeout().tv_sec
;
437 tm
.tv_usec
= it
.current()->getStartTime().tv_usec
+
438 it
.current()->getTimeout().tv_usec
;
440 if ((now
.tv_sec
< tm
.tv_sec
) ||
441 (now
.tv_sec
== tm
.tv_sec
&& now
.tv_usec
< tm
.tv_usec
))
444 it
.current()->fireTimeout();
446 // restart the current timer so that the start time is updated
447 if (!it
.current()->doOnce())
448 it
.current()->start();
450 it
.current()->stop();
457 const Bool
BaseDisplay::validateWindow(Window window
)
460 if (XCheckTypedWindowEvent(display
, window
, DestroyNotify
, &event
)) {
461 XPutBackEvent(display
, &event
);
470 void BaseDisplay::grab(void)
473 XGrabServer(display
);
477 void BaseDisplay::ungrab(void)
480 XUngrabServer(display
);
482 if (server_grabs
< 0)
487 void BaseDisplay::addTimer(BTimer
* timer
)
492 LinkedListIterator
< BTimer
> it(timerList
);
494 for (; it
.current(); it
++, index
++)
495 if ((it
.current()->getTimeout().tv_sec
> timer
->getTimeout().tv_sec
) ||
496 ((it
.current()->getTimeout().tv_sec
== timer
->getTimeout().tv_sec
)
497 && (it
.current()->getTimeout().tv_usec
>=
498 timer
->getTimeout().tv_usec
))) break;
500 timerList
->insert(timer
, index
);
504 void BaseDisplay::removeTimer(BTimer
* timer
)
506 timerList
->remove(timer
);
510 ScreenInfo::ScreenInfo(BaseDisplay
* d
, int num
)
516 root_window
= RootWindow(basedisplay
->getXDisplay(), screen_number
);
517 depth
= DefaultDepth(basedisplay
->getXDisplay(), screen_number
);
520 WidthOfScreen(ScreenOfDisplay
521 (basedisplay
->getXDisplay(), screen_number
));
523 HeightOfScreen(ScreenOfDisplay
524 (basedisplay
->getXDisplay(), screen_number
));
526 // search for a TrueColor Visual... if we can't find one... we will use the
527 // default visual for the screen
528 XVisualInfo vinfo_template
, *vinfo_return
;
531 vinfo_template
.screen
= screen_number
;
532 vinfo_template
.c_class
= TrueColor
;
534 visual
= (Visual
*) 0;
536 if ((vinfo_return
= XGetVisualInfo(basedisplay
->getXDisplay(),
537 VisualScreenMask
| VisualClassMask
,
538 &vinfo_template
, &vinfo_nitems
)) &&
540 for (int i
= 0; i
< vinfo_nitems
; i
++) {
541 if (depth
< (vinfo_return
+ i
)->depth
) {
542 depth
= (vinfo_return
+ i
)->depth
;
543 visual
= (vinfo_return
+ i
)->visual
;
551 visual
= DefaultVisual(basedisplay
->getXDisplay(), screen_number
);