]> icculus.org git repositories - mikachu/openbox.git/blob - src/blackbox.cc
xinerama support for window placement
[mikachu/openbox.git] / src / blackbox.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2 // blackbox.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)
5 //
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:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
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.
23
24 #ifdef    HAVE_CONFIG_H
25 #  include "../config.h"
26 #endif // HAVE_CONFIG_H
27
28 extern "C" {
29 #include <X11/Xlib.h>
30 #include <X11/Xutil.h>
31 #include <X11/Xatom.h>
32 #include <X11/cursorfont.h>
33 #include <X11/keysym.h>
34
35 #ifdef    SHAPE
36 #include <X11/extensions/shape.h>
37 #endif // SHAPE
38
39 #ifdef    HAVE_STDIO_H
40 #  include <stdio.h>
41 #endif // HAVE_STDIO_H
42
43 #ifdef HAVE_STDLIB_H
44 #  include <stdlib.h>
45 #endif // HAVE_STDLIB_H
46
47 #ifdef HAVE_STRING_H
48 #  include <string.h>
49 #endif // HAVE_STRING_H
50
51 #ifdef    HAVE_UNISTD_H
52 #  include <sys/types.h>
53 #  include <unistd.h>
54 #endif // HAVE_UNISTD_H
55
56 #ifdef    HAVE_SYS_PARAM_H
57 #  include <sys/param.h>
58 #endif // HAVE_SYS_PARAM_H
59
60 #ifdef    HAVE_SYS_SELECT_H
61 #  include <sys/select.h>
62 #endif // HAVE_SYS_SELECT_H
63
64 #ifdef    HAVE_SIGNAL_H
65 #  include <signal.h>
66 #endif // HAVE_SIGNAL_H
67
68 #ifdef    HAVE_SYS_SIGNAL_H
69 #  include <sys/signal.h>
70 #endif // HAVE_SYS_SIGNAL_H
71
72 #ifdef    HAVE_SYS_STAT_H
73 #  include <sys/types.h>
74 #  include <sys/stat.h>
75 #endif // HAVE_SYS_STAT_H
76
77 #ifdef    TIME_WITH_SYS_TIME
78 #  include <sys/time.h>
79 #  include <time.h>
80 #else // !TIME_WITH_SYS_TIME
81 #  ifdef    HAVE_SYS_TIME_H
82 #    include <sys/time.h>
83 #  else // !HAVE_SYS_TIME_H
84 #    include <time.h>
85 #  endif // HAVE_SYS_TIME_H
86 #endif // TIME_WITH_SYS_TIME
87
88 #ifdef    HAVE_LIBGEN_H
89 #  include <libgen.h>
90 #endif // HAVE_LIBGEN_H
91 }
92
93 #include <assert.h>
94
95 #include <algorithm>
96 #include <string>
97 using std::string;
98
99 #include "i18n.hh"
100 #include "blackbox.hh"
101 #include "Basemenu.hh"
102 #include "Clientmenu.hh"
103 #include "GCCache.hh"
104 #include "Image.hh"
105 #include "Rootmenu.hh"
106 #include "Screen.hh"
107 #include "Slit.hh"
108 #include "Toolbar.hh"
109 #include "Util.hh"
110 #include "Window.hh"
111 #include "Workspace.hh"
112 #include "Workspacemenu.hh"
113 #include "XAtom.hh"
114
115 // X event scanner for enter/leave notifies - adapted from twm
116 struct scanargs {
117   Window w;
118   bool leave, inferior, enter;
119 };
120
121 static Bool queueScanner(Display *, XEvent *e, char *args) {
122   scanargs *scan = (scanargs *) args;
123   if ((e->type == LeaveNotify) &&
124       (e->xcrossing.window == scan->w) &&
125       (e->xcrossing.mode == NotifyNormal)) {
126     scan->leave = True;
127     scan->inferior = (e->xcrossing.detail == NotifyInferior);
128   } else if ((e->type == EnterNotify) && (e->xcrossing.mode == NotifyUngrab)) {
129     scan->enter = True;
130   }
131
132   return False;
133 }
134
135 Blackbox *blackbox;
136
137
138 Blackbox::Blackbox(char **m_argv, char *dpy_name, char *rc, char *menu)
139   : BaseDisplay(m_argv[0], dpy_name) {
140   if (! XSupportsLocale())
141     fprintf(stderr, "X server does not support locale\n");
142
143   if (XSetLocaleModifiers("") == NULL)
144     fprintf(stderr, "cannot set locale modifiers\n");
145
146   ::blackbox = this;
147   argv = m_argv;
148
149   // try to make sure the ~/.openbox directory exists
150   mkdir(expandTilde("~/.openbox").c_str(), S_IREAD | S_IWRITE | S_IEXEC |
151                                            S_IRGRP | S_IWGRP | S_IXGRP |
152                                            S_IROTH | S_IWOTH | S_IXOTH);
153   
154   if (! rc) rc = "~/.openbox/rc";
155   rc_file = expandTilde(rc);
156   config.setFile(rc_file);  
157   if (! menu) menu = "~/.openbox/menu";
158   menu_file = expandTilde(menu);
159
160   no_focus = False;
161
162   resource.auto_raise_delay.tv_sec = resource.auto_raise_delay.tv_usec = 0;
163
164   active_screen = 0;
165   focused_window = changing_window = (BlackboxWindow *) 0;
166
167   load_rc();
168
169   xatom = new XAtom(getXDisplay());
170
171   cursor.session = XCreateFontCursor(getXDisplay(), XC_left_ptr);
172   cursor.move = XCreateFontCursor(getXDisplay(), XC_fleur);
173   cursor.ll_angle = XCreateFontCursor(getXDisplay(), XC_ll_angle);
174   cursor.lr_angle = XCreateFontCursor(getXDisplay(), XC_lr_angle);
175   cursor.ul_angle = XCreateFontCursor(getXDisplay(), XC_ul_angle);
176   cursor.ur_angle = XCreateFontCursor(getXDisplay(), XC_ur_angle);
177
178   for (unsigned int i = 0; i < getNumberOfScreens(); i++) {
179     BScreen *screen = new BScreen(this, i);
180
181     if (! screen->isScreenManaged()) {
182       delete screen;
183       continue;
184     }
185
186     screenList.push_back(screen);
187   }
188
189   if (screenList.empty()) {
190     fprintf(stderr,
191             i18n(blackboxSet, blackboxNoManagableScreens,
192               "Blackbox::Blackbox: no managable screens found, aborting.\n"));
193     ::exit(3);
194   }
195
196   // save current settings and default values
197   save_rc();
198
199   // set the screen with mouse to the first managed screen
200   active_screen = screenList.front();
201   setFocusedWindow(0);
202
203   XSynchronize(getXDisplay(), False);
204   XSync(getXDisplay(), False);
205
206   reconfigure_wait = reread_menu_wait = False;
207
208   timer = new BTimer(this, this);
209   timer->setTimeout(0l);
210 }
211
212
213 Blackbox::~Blackbox(void) {
214   std::for_each(screenList.begin(), screenList.end(), PointerAssassin());
215
216   std::for_each(menuTimestamps.begin(), menuTimestamps.end(),
217                 PointerAssassin());
218
219   delete xatom;
220
221   delete timer;
222 }
223
224
225 void Blackbox::process_event(XEvent *e) {
226   switch (e->type) {
227   case ButtonPress: {
228     // strip the lock key modifiers
229     e->xbutton.state &= ~(NumLockMask | ScrollLockMask | LockMask);
230
231     last_time = e->xbutton.time;
232
233     BlackboxWindow *win = (BlackboxWindow *) 0;
234     Basemenu *menu = (Basemenu *) 0;
235     Slit *slit = (Slit *) 0;
236     Toolbar *tbar = (Toolbar *) 0;
237     BScreen *scrn = (BScreen *) 0;
238
239     if ((win = searchWindow(e->xbutton.window))) {
240       win->buttonPressEvent(&e->xbutton);
241
242       /* XXX: is this sane on low colour desktops? */
243       if (e->xbutton.button == 1)
244         win->installColormap(True);
245     } else if ((menu = searchMenu(e->xbutton.window))) {
246       menu->buttonPressEvent(&e->xbutton);
247     } else if ((slit = searchSlit(e->xbutton.window))) {
248       slit->buttonPressEvent(&e->xbutton);
249     } else if ((tbar = searchToolbar(e->xbutton.window))) {
250       tbar->buttonPressEvent(&e->xbutton);
251     } else if ((scrn = searchScreen(e->xbutton.window))) {
252       scrn->buttonPressEvent(&e->xbutton);
253       if (active_screen != scrn) {
254         active_screen = scrn;
255         // first, set no focus window on the old screen
256         setFocusedWindow(0);
257         // and move focus to this screen
258         setFocusedWindow(0);
259       }
260     }
261     break;
262   }
263
264   case ButtonRelease: {
265     // strip the lock key modifiers
266     e->xbutton.state &= ~(NumLockMask | ScrollLockMask | LockMask);
267
268     last_time = e->xbutton.time;
269
270     BlackboxWindow *win = (BlackboxWindow *) 0;
271     Basemenu *menu = (Basemenu *) 0;
272     Toolbar *tbar = (Toolbar *) 0;
273
274     if ((win = searchWindow(e->xbutton.window)))
275       win->buttonReleaseEvent(&e->xbutton);
276     else if ((menu = searchMenu(e->xbutton.window)))
277       menu->buttonReleaseEvent(&e->xbutton);
278     else if ((tbar = searchToolbar(e->xbutton.window)))
279       tbar->buttonReleaseEvent(&e->xbutton);
280
281     break;
282   }
283
284   case ConfigureRequest: {
285     // compress configure requests...
286     XEvent realevent;
287     unsigned int i = 0;
288     while(XCheckTypedWindowEvent(getXDisplay(), e->xconfigurerequest.window,
289                                  ConfigureRequest, &realevent)) {
290       i++;
291     }
292     if ( i > 0 )
293       e = &realevent;
294
295     BlackboxWindow *win = (BlackboxWindow *) 0;
296     Slit *slit = (Slit *) 0;
297
298     if ((win = searchWindow(e->xconfigurerequest.window))) {
299       win->configureRequestEvent(&e->xconfigurerequest);
300     } else if ((slit = searchSlit(e->xconfigurerequest.window))) {
301       slit->configureRequestEvent(&e->xconfigurerequest);
302     } else {
303       if (validateWindow(e->xconfigurerequest.window)) {
304         XWindowChanges xwc;
305
306         xwc.x = e->xconfigurerequest.x;
307         xwc.y = e->xconfigurerequest.y;
308         xwc.width = e->xconfigurerequest.width;
309         xwc.height = e->xconfigurerequest.height;
310         xwc.border_width = e->xconfigurerequest.border_width;
311         xwc.sibling = e->xconfigurerequest.above;
312         xwc.stack_mode = e->xconfigurerequest.detail;
313
314         XConfigureWindow(getXDisplay(), e->xconfigurerequest.window,
315                          e->xconfigurerequest.value_mask, &xwc);
316       }
317     }
318
319     break;
320   }
321
322   case MapRequest: {
323 #ifdef    DEBUG
324     fprintf(stderr, "Blackbox::process_event(): MapRequest for 0x%lx\n",
325             e->xmaprequest.window);
326 #endif // DEBUG
327
328     BlackboxWindow *win = searchWindow(e->xmaprequest.window);
329
330     if (win) {
331       bool focus = False;
332       if (win->isIconic()) {
333         win->deiconify();
334         focus = True;
335       }
336       if (win->isShaded()) {
337         win->shade();
338         focus = True;
339       }
340
341       if (focus && (win->isTransient() || win->getScreen()->doFocusNew()))
342         win->setInputFocus();
343     } else {
344       BScreen *screen = searchScreen(e->xmaprequest.parent);
345
346       if (! screen) {
347         /*
348           we got a map request for a window who's parent isn't root. this
349           can happen in only one circumstance:
350
351             a client window unmapped a managed window, and then remapped it
352             somewhere between unmapping the client window and reparenting it
353             to root.
354
355           regardless of how it happens, we need to find the screen that
356           the window is on
357         */
358         XWindowAttributes wattrib;
359         if (! XGetWindowAttributes(getXDisplay(), e->xmaprequest.window,
360                                    &wattrib)) {
361           // failed to get the window attributes, perhaps the window has
362           // now been destroyed?
363           break;
364         }
365
366         screen = searchScreen(wattrib.root);
367         assert(screen != 0); // this should never happen
368       }
369
370       screen->manageWindow(e->xmaprequest.window);
371     }
372
373     break;
374   }
375
376   case UnmapNotify: {
377     BlackboxWindow *win = (BlackboxWindow *) 0;
378     Slit *slit = (Slit *) 0;
379     BScreen *screen = (BScreen *) 0;
380
381     if ((win = searchWindow(e->xunmap.window))) {
382       win->unmapNotifyEvent(&e->xunmap);
383     } else if ((slit = searchSlit(e->xunmap.window))) {
384       slit->unmapNotifyEvent(&e->xunmap);
385     } else if ((screen = searchSystrayWindow(e->xunmap.window))) {
386       screen->removeSystrayWindow(e->xunmap.window);
387     }
388
389     break;
390   }
391
392   case DestroyNotify: {
393     BlackboxWindow *win = (BlackboxWindow *) 0;
394     Slit *slit = (Slit *) 0;
395     BScreen *screen = (BScreen *) 0;
396     BWindowGroup *group = (BWindowGroup *) 0;
397
398     if ((win = searchWindow(e->xdestroywindow.window))) {
399       win->destroyNotifyEvent(&e->xdestroywindow);
400     } else if ((slit = searchSlit(e->xdestroywindow.window))) {
401       slit->removeClient(e->xdestroywindow.window, False);
402     } else if ((group = searchGroup(e->xdestroywindow.window))) {
403       delete group;
404     } else if ((screen = searchSystrayWindow(e->xunmap.window))) {
405       screen->removeSystrayWindow(e->xunmap.window);
406     }
407
408     break;
409   }
410
411   case ReparentNotify: {
412     /*
413       this event is quite rare and is usually handled in unmapNotify
414       however, if the window is unmapped when the reparent event occurs
415       the window manager never sees it because an unmap event is not sent
416       to an already unmapped window.
417     */
418     BlackboxWindow *win = searchWindow(e->xreparent.window);
419     if (win) {
420       win->reparentNotifyEvent(&e->xreparent);
421     } else {
422       Slit *slit = searchSlit(e->xreparent.window);
423       if (slit && slit->getWindowID() != e->xreparent.parent)
424         slit->removeClient(e->xreparent.window, True);
425     }
426     break;
427   }
428
429   case MotionNotify: {
430     // motion notify compression...
431     XEvent realevent;
432     unsigned int i = 0;
433     while (XCheckTypedWindowEvent(getXDisplay(), e->xmotion.window,
434                                   MotionNotify, &realevent)) {
435       i++;
436     }
437
438     // if we have compressed some motion events, use the last one
439     if ( i > 0 )
440       e = &realevent;
441
442     // strip the lock key modifiers
443     e->xbutton.state &= ~(NumLockMask | ScrollLockMask | LockMask);
444
445     last_time = e->xmotion.time;
446
447     BlackboxWindow *win = (BlackboxWindow *) 0;
448     Basemenu *menu = (Basemenu *) 0;
449
450     if ((win = searchWindow(e->xmotion.window)))
451       win->motionNotifyEvent(&e->xmotion);
452     else if ((menu = searchMenu(e->xmotion.window)))
453       menu->motionNotifyEvent(&e->xmotion);
454
455     break;
456   }
457
458   case PropertyNotify: {
459     last_time = e->xproperty.time;
460
461     BlackboxWindow *win = (BlackboxWindow *) 0;
462     BScreen *screen = (BScreen *) 0;
463
464     if ((win = searchWindow(e->xproperty.window)))
465       win->propertyNotifyEvent(&e->xproperty);
466     else if ((screen = searchScreen(e->xproperty.window)))
467       screen->propertyNotifyEvent(&e->xproperty);
468     break;
469   }
470
471   case EnterNotify: {
472     last_time = e->xcrossing.time;
473
474     BScreen *screen = (BScreen *) 0;
475     BlackboxWindow *win = (BlackboxWindow *) 0;
476     Basemenu *menu = (Basemenu *) 0;
477     Toolbar *tbar = (Toolbar *) 0;
478     Slit *slit = (Slit *) 0;
479
480     if (e->xcrossing.mode == NotifyGrab) break;
481
482     XEvent dummy;
483     scanargs sa;
484     sa.w = e->xcrossing.window;
485     sa.enter = sa.leave = False;
486     XCheckIfEvent(getXDisplay(), &dummy, queueScanner, (char *) &sa);
487
488     if ((e->xcrossing.window == e->xcrossing.root) &&
489         (screen = searchScreen(e->xcrossing.window))) {
490       screen->getImageControl()->installRootColormap();
491     } else if ((win = searchWindow(e->xcrossing.window))) {
492       if (win->getScreen()->isSloppyFocus() &&
493           (! win->isFocused()) && (! no_focus) &&
494           win->isNormal()) {  // don't focus non-normal windows with mouseover
495         if (((! sa.leave) || sa.inferior) && win->isVisible()) {
496           if (win->setInputFocus())
497             win->installColormap(True); // XXX: shouldnt we honour no install?
498         }
499       }
500     } else if ((menu = searchMenu(e->xcrossing.window))) {
501       menu->enterNotifyEvent(&e->xcrossing);
502     } else if ((tbar = searchToolbar(e->xcrossing.window))) {
503       tbar->enterNotifyEvent(&e->xcrossing);
504     } else if ((slit = searchSlit(e->xcrossing.window))) {
505       slit->enterNotifyEvent(&e->xcrossing);
506     }
507     break;
508   }
509
510   case LeaveNotify: {
511     last_time = e->xcrossing.time;
512
513     BlackboxWindow *win = (BlackboxWindow *) 0;
514     Basemenu *menu = (Basemenu *) 0;
515     Toolbar *tbar = (Toolbar *) 0;
516     Slit *slit = (Slit *) 0;
517
518     if ((menu = searchMenu(e->xcrossing.window)))
519       menu->leaveNotifyEvent(&e->xcrossing);
520     else if ((win = searchWindow(e->xcrossing.window)))
521       win->installColormap(False);
522     else if ((tbar = searchToolbar(e->xcrossing.window)))
523       tbar->leaveNotifyEvent(&e->xcrossing);
524     else if ((slit = searchSlit(e->xcrossing.window)))
525       slit->leaveNotifyEvent(&e->xcrossing);
526     break;
527   }
528
529   case Expose: {
530     // compress expose events
531     XEvent realevent;
532     unsigned int i = 0;
533     int ex1, ey1, ex2, ey2;
534     ex1 = e->xexpose.x;
535     ey1 = e->xexpose.y;
536     ex2 = ex1 + e->xexpose.width - 1;
537     ey2 = ey1 + e->xexpose.height - 1;
538     while (XCheckTypedWindowEvent(getXDisplay(), e->xexpose.window,
539                                   Expose, &realevent)) {
540       i++;
541
542       // merge expose area
543       ex1 = std::min(realevent.xexpose.x, ex1);
544       ey1 = std::min(realevent.xexpose.y, ey1);
545       ex2 = std::max(realevent.xexpose.x + realevent.xexpose.width - 1, ex2);
546       ey2 = std::max(realevent.xexpose.y + realevent.xexpose.height - 1, ey2);
547     }
548     if ( i > 0 )
549       e = &realevent;
550
551     // use the merged area
552     e->xexpose.x = ex1;
553     e->xexpose.y = ey1;
554     e->xexpose.width = ex2 - ex1 + 1;
555     e->xexpose.height = ey2 - ey1 + 1;
556
557     BlackboxWindow *win = (BlackboxWindow *) 0;
558     Basemenu *menu = (Basemenu *) 0;
559     Toolbar *tbar = (Toolbar *) 0;
560
561     if ((win = searchWindow(e->xexpose.window)))
562       win->exposeEvent(&e->xexpose);
563     else if ((menu = searchMenu(e->xexpose.window)))
564       menu->exposeEvent(&e->xexpose);
565     else if ((tbar = searchToolbar(e->xexpose.window)))
566       tbar->exposeEvent(&e->xexpose);
567
568     break;
569   }
570
571   case KeyPress: {
572     Toolbar *tbar = searchToolbar(e->xkey.window);
573
574     if (tbar && tbar->isEditing())
575       tbar->keyPressEvent(&e->xkey);
576
577     break;
578   }
579
580   case ColormapNotify: {
581     BScreen *screen = searchScreen(e->xcolormap.window);
582
583     if (screen)
584       screen->setRootColormapInstalled((e->xcolormap.state ==
585                                         ColormapInstalled) ? True : False);
586
587     break;
588   }
589
590   case FocusIn: {
591     if (e->xfocus.detail != NotifyNonlinear &&
592         e->xfocus.detail != NotifyAncestor) {
593       /*
594         don't process FocusIns when:
595         1. the new focus window isn't an ancestor or inferior of the old
596         focus window (NotifyNonlinear)
597         make sure to allow the FocusIn when the old focus window was an
598         ancestor but didn't have a parent, such as root (NotifyAncestor)
599       */
600       break;
601     }
602
603     BlackboxWindow *win = searchWindow(e->xfocus.window);
604     if (win) {
605       if (! win->isFocused())
606         win->setFocusFlag(True);
607
608       /*
609         set the event window to None.  when the FocusOut event handler calls
610         this function recursively, it uses this as an indication that focus
611         has moved to a known window.
612       */
613       e->xfocus.window = None;
614     }
615
616     break;
617   }
618
619   case FocusOut: {
620     if (e->xfocus.detail != NotifyNonlinear) {
621       /*
622         don't process FocusOuts when:
623         2. the new focus window isn't an ancestor or inferior of the old
624         focus window (NotifyNonlinear)
625       */
626       break;
627     }
628
629     BlackboxWindow *win = searchWindow(e->xfocus.window);
630     if (win && win->isFocused()) {
631       /*
632         before we mark "win" as unfocused, we need to verify that focus is
633         going to a known location, is in a known location, or set focus
634         to a known location.
635       */
636
637       XEvent event;
638       // don't check the current focus if FocusOut was generated during a grab
639       bool check_focus = (e->xfocus.mode == NotifyNormal);
640
641       /*
642         First, check if there is a pending FocusIn event waiting.  if there
643         is, process it and determine if focus has moved to another window
644         (the FocusIn event handler sets the window in the event
645         structure to None to indicate this).
646       */
647       if (XCheckTypedEvent(getXDisplay(), FocusIn, &event)) {
648
649         process_event(&event);
650         if (event.xfocus.window == None) {
651           // focus has moved
652           check_focus = False;
653         }
654       }
655
656       if (check_focus) {
657         /*
658           Second, we query the X server for the current input focus.
659           to make sure that we keep a consistent state.
660         */
661         BlackboxWindow *focus;
662         Window w;
663         int revert;
664         XGetInputFocus(getXDisplay(), &w, &revert);
665         focus = searchWindow(w);
666         if (focus) {
667           /*
668             focus got from "win" to "focus" under some very strange
669             circumstances, and we need to make sure that the focus indication
670             is correct.
671           */
672           setFocusedWindow(focus);
673         } else {
674           // we have no idea where focus went... so we set it to somewhere
675           setFocusedWindow(0);
676         }
677       }
678     }
679
680     break;
681   }
682
683   case ClientMessage: {
684     if (e->xclient.format == 32) {
685       if (e->xclient.message_type == xatom->getAtom(XAtom::wm_change_state)) {
686         // WM_CHANGE_STATE message
687         BlackboxWindow *win = searchWindow(e->xclient.window);
688         if (! win || ! win->validateClient()) return;
689
690         if (e->xclient.data.l[0] == IconicState)
691           win->iconify();
692         if (e->xclient.data.l[0] == NormalState)
693           win->deiconify();
694       } else if (e->xclient.message_type == 
695                  xatom->getAtom(XAtom::blackbox_change_workspace) || 
696                  e->xclient.message_type == 
697                  xatom->getAtom(XAtom::net_current_desktop)) {
698         // NET_CURRENT_DESKTOP message
699         BScreen *screen = searchScreen(e->xclient.window);
700
701         unsigned int workspace = e->xclient.data.l[0];
702         if (screen && workspace < screen->getWorkspaceCount())
703           screen->changeWorkspaceID(workspace);
704       } else if (e->xclient.message_type == 
705                  xatom->getAtom(XAtom::blackbox_change_window_focus)) {
706         // TEMP HACK TO KEEP BBKEYS WORKING
707         BlackboxWindow *win = searchWindow(e->xclient.window);
708
709         if (win && win->isVisible() && win->setInputFocus())
710           win->installColormap(True);
711       } else if (e->xclient.message_type == 
712                  xatom->getAtom(XAtom::net_active_window)) {
713         // NET_ACTIVE_WINDOW
714         BlackboxWindow *win = searchWindow(e->xclient.window);
715
716         if (win) {
717           BScreen *screen = win->getScreen();
718
719           if (win->isIconic())
720             win->deiconify(False, True);
721           if (win->isShaded())
722             win->shade();
723           if (win->getWorkspaceNumber() != screen->getCurrentWorkspaceID())
724             screen->changeWorkspaceID(win->getWorkspaceNumber());
725           if (win->isVisible() && win->setInputFocus()) {
726             win->getScreen()->getWorkspace(win->getWorkspaceNumber())->
727               raiseWindow(win);
728             win->installColormap(True);
729           }
730         }
731       } else if (e->xclient.message_type == 
732                  xatom->getAtom(XAtom::blackbox_cycle_window_focus)) {
733         // BLACKBOX_CYCLE_WINDOW_FOCUS
734         BScreen *screen = searchScreen(e->xclient.window);
735
736         if (screen) {
737           if (! e->xclient.data.l[0])
738             screen->prevFocus();
739           else
740             screen->nextFocus();
741         }
742       } else if (e->xclient.message_type == 
743                  xatom->getAtom(XAtom::net_wm_desktop)) {
744         // NET_WM_DESKTOP
745         BlackboxWindow *win = searchWindow(e->xclient.window);
746
747         if (win) {
748           BScreen *screen = win->getScreen();
749           unsigned long wksp = (unsigned) e->xclient.data.l[0];
750           if (wksp < screen->getWorkspaceCount()) {
751             if (win->isIconic()) win->deiconify(False, True);
752             if (win->isStuck()) win->stick();
753             if (wksp != screen->getCurrentWorkspaceID())
754               win->withdraw();
755             else
756               win->show();
757             screen->reassociateWindow(win, wksp, True);
758           } else if (wksp == 0xfffffffe || // XXX: BUG, BUT DOING THIS SO KDE WORKS FOR NOW!!
759                      wksp == 0xffffffff) {
760             if (win->isIconic()) win->deiconify(False, True);
761             if (! win->isStuck()) win->stick();
762             if (! win->isVisible()) win->show();
763           }
764         }
765       } else if (e->xclient.message_type == 
766                  xatom->getAtom(XAtom::blackbox_change_attributes)) {
767         // BLACKBOX_CHANGE_ATTRIBUTES
768         BlackboxWindow *win = searchWindow(e->xclient.window);
769
770         if (win && win->validateClient()) {
771           BlackboxHints net;
772           net.flags = e->xclient.data.l[0];
773           net.attrib = e->xclient.data.l[1];
774           net.workspace = e->xclient.data.l[2];
775           net.stack = e->xclient.data.l[3];
776           net.decoration = e->xclient.data.l[4];
777
778           win->changeBlackboxHints(&net);
779         }
780       } else if (e->xclient.message_type == 
781                 xatom->getAtom(XAtom::net_number_of_desktops)) {
782         // NET_NUMBER_OF_DESKTOPS
783         BScreen *screen = searchScreen(e->xclient.window);
784         
785         if (e->xclient.data.l[0] > 0)
786           screen->changeWorkspaceCount((unsigned) e->xclient.data.l[0]);
787       } else if (e->xclient.message_type ==
788                  xatom->getAtom(XAtom::net_close_window)) {
789         // NET_CLOSE_WINDOW
790         BlackboxWindow *win = searchWindow(e->xclient.window);
791         if (win && win->validateClient())
792           win->close(); // could this be smarter?
793       } else if (e->xclient.message_type ==
794                  xatom->getAtom(XAtom::net_wm_moveresize)) {
795         // NET_WM_MOVERESIZE
796         BlackboxWindow *win = searchWindow(e->xclient.window);
797         if (win && win->validateClient()) {
798           int x_root = e->xclient.data.l[0],
799               y_root = e->xclient.data.l[1];
800           if ((Atom) e->xclient.data.l[2] ==
801               xatom->getAtom(XAtom::net_wm_moveresize_move)) {
802             win->beginMove(x_root, y_root);
803           } else {
804             if ((Atom) e->xclient.data.l[2] ==
805                 xatom->getAtom(XAtom::net_wm_moveresize_size_topleft))
806               win->beginResize(x_root, y_root, BlackboxWindow::TopLeft);
807             else if ((Atom) e->xclient.data.l[2] ==
808                      xatom->getAtom(XAtom::net_wm_moveresize_size_topright))
809               win->beginResize(x_root, y_root, BlackboxWindow::TopRight);
810             else if ((Atom) e->xclient.data.l[2] ==
811                      xatom->getAtom(XAtom::net_wm_moveresize_size_bottomleft))
812               win->beginResize(x_root, y_root, BlackboxWindow::BottomLeft);
813             else if ((Atom) e->xclient.data.l[2] ==
814                 xatom->getAtom(XAtom::net_wm_moveresize_size_bottomright))
815               win->beginResize(x_root, y_root, BlackboxWindow::BottomRight);
816           }
817         }
818       } else if (e->xclient.message_type ==
819                  xatom->getAtom(XAtom::net_wm_state)) {
820         // NET_WM_STATE
821         BlackboxWindow *win = searchWindow(e->xclient.window);
822         if (win && win->validateClient()) {
823           const Atom action = (Atom) e->xclient.data.l[0];
824           const Atom state[] = { (Atom) e->xclient.data.l[1],
825                                  (Atom) e->xclient.data.l[2] };
826           
827           for (int i = 0; i < 2; ++i) {
828             if (! state[i])
829               continue;
830
831             if ((Atom) e->xclient.data.l[0] == 1) {
832               // ADD
833               if (state[i] == xatom->getAtom(XAtom::net_wm_state_modal)) {
834                 win->setModal(True);
835               } else if (state[i] ==
836                          xatom->getAtom(XAtom::net_wm_state_maximized_vert)) {
837                 if (win->isMaximizedHoriz()) {
838                   win->maximize(0); // unmaximize
839                   win->maximize(1); // full
840                 } else if (! win->isMaximized()) {
841                   win->maximize(2); // vert
842                 }
843               } else if (state[i] ==
844                          xatom->getAtom(XAtom::net_wm_state_maximized_horz)) {
845                 if (win->isMaximizedVert()) {
846                   win->maximize(0); // unmaximize
847                   win->maximize(1); // full
848                 } else if (! win->isMaximized()) {
849                   win->maximize(3); // horiz
850                 }
851               } else if (state[i] ==
852                          xatom->getAtom(XAtom::net_wm_state_shaded)) {
853                 if (! win->isShaded())
854                   win->shade();
855               } else if (state[i] ==
856                          xatom->getAtom(XAtom::net_wm_state_skip_taskbar)) {
857                 win->setSkipTaskbar(True);
858               } else if (state[i] ==
859                          xatom->getAtom(XAtom::net_wm_state_skip_pager)) {
860                 win->setSkipPager(True);
861               } else if (state[i] ==
862                          xatom->getAtom(XAtom::net_wm_state_fullscreen)) {
863                 win->setFullscreen(True);
864               }
865             } else if (action == 0) {
866               // REMOVE
867               if (state[i] == xatom->getAtom(XAtom::net_wm_state_modal)) {
868                 win->setModal(False);
869               } else if (state[i] ==
870                          xatom->getAtom(XAtom::net_wm_state_maximized_vert)) {
871                 if (win->isMaximizedFull()) {
872                   win->maximize(0); // unmaximize
873                   win->maximize(3); // horiz
874                 } else if (win->isMaximizedVert()) {
875                   win->maximize(0); // unmaximize
876                 }
877               } else if (state[i] ==
878                          xatom->getAtom(XAtom::net_wm_state_maximized_horz)) {
879                 if (win->isMaximizedFull()) {
880                   win->maximize(0); // unmaximize
881                   win->maximize(2); // vert
882                 } else if (win->isMaximizedHoriz()) {
883                   win->maximize(0); // unmaximize
884                 }
885               } else if (state[i] ==
886                          xatom->getAtom(XAtom::net_wm_state_shaded)) {
887                 if (win->isShaded())
888                   win->shade();
889               } else if (state[i] ==
890                          xatom->getAtom(XAtom::net_wm_state_skip_taskbar)) {
891                 win->setSkipTaskbar(False);
892               } else if (state[i] ==
893                          xatom->getAtom(XAtom::net_wm_state_skip_pager)) {
894                 win->setSkipPager(False);
895               } else if (state[i] ==
896                          xatom->getAtom(XAtom::net_wm_state_fullscreen)) {
897                 win->setFullscreen(False);
898               }
899             } else if (action == 2) {
900               // TOGGLE
901               if (state[i] == xatom->getAtom(XAtom::net_wm_state_modal)) {
902                 win->setModal(! win->isModal());
903               } else if (state[i] ==
904                          xatom->getAtom(XAtom::net_wm_state_maximized_vert)) {
905                 if (win->isMaximizedFull()) {
906                   win->maximize(0); // unmaximize
907                   win->maximize(3); // horiz
908                 } else if (win->isMaximizedVert()) {
909                   win->maximize(0); // unmaximize
910                 } else if (win->isMaximizedHoriz()) {
911                   win->maximize(0); // unmaximize
912                   win->maximize(1); // full
913                 } else {
914                   win->maximize(2); // vert
915                 }
916               } else if (state[i] ==
917                          xatom->getAtom(XAtom::net_wm_state_maximized_horz)) {
918                 if (win->isMaximizedFull()) {
919                   win->maximize(0); // unmaximize
920                   win->maximize(2); // vert
921                 } else if (win->isMaximizedHoriz()) {
922                   win->maximize(0); // unmaximize
923                 } else if (win->isMaximizedVert()) {
924                   win->maximize(0); // unmaximize
925                   win->maximize(1); // full
926                 } else {
927                   win->maximize(3); // horiz
928                 }
929               } else if (state[i] ==
930                          xatom->getAtom(XAtom::net_wm_state_shaded)) {
931                 win->shade();
932               } else if (state[i] ==
933                          xatom->getAtom(XAtom::net_wm_state_skip_taskbar)) {
934                 win->setSkipTaskbar(! win->skipTaskbar());
935               } else if (state[i] ==
936                          xatom->getAtom(XAtom::net_wm_state_skip_pager)) {
937                 win->setSkipPager(! win->skipPager());
938               } else if (state[i] ==
939                          xatom->getAtom(XAtom::net_wm_state_fullscreen)) {
940                 win->setFullscreen(! win->isFullscreen());
941               }
942             }
943           }
944         }
945       }
946     }
947
948     break;
949   }
950
951   case NoExpose:
952   case ConfigureNotify:
953   case MapNotify:
954     break; // not handled, just ignore
955
956   default: {
957 #ifdef    SHAPE
958     if (e->type == getShapeEventBase()) {
959       XShapeEvent *shape_event = (XShapeEvent *) e;
960       BlackboxWindow *win = searchWindow(e->xany.window);
961
962       if (win)
963         win->shapeEvent(shape_event);
964     }
965 #endif // SHAPE
966   }
967   } // switch
968 }
969
970
971 bool Blackbox::handleSignal(int sig) {
972   switch (sig) {
973   case SIGHUP:
974     reconfigure();
975     break;
976
977   case SIGUSR1:
978     restart();
979     break;
980
981   case SIGUSR2:
982     rereadMenu();
983     break;
984
985   case SIGPIPE:
986   case SIGSEGV:
987   case SIGFPE:
988   case SIGINT:
989   case SIGTERM:
990     shutdown();
991
992   default:
993     return False;
994   }
995
996   return True;
997 }
998
999
1000 bool Blackbox::validateWindow(Window window) {
1001   XEvent event;
1002   if (XCheckTypedWindowEvent(getXDisplay(), window, DestroyNotify, &event)) {
1003     XPutBackEvent(getXDisplay(), &event);
1004
1005     return False;
1006   }
1007
1008   return True;
1009 }
1010
1011
1012 BScreen *Blackbox::searchScreen(Window window) {
1013   ScreenList::iterator it = screenList.begin();
1014
1015   for (; it != screenList.end(); ++it) {
1016     BScreen *s = *it;
1017     if (s->getRootWindow() == window)
1018       return s;
1019   }
1020
1021   return (BScreen *) 0;
1022 }
1023
1024
1025 BScreen *Blackbox::searchSystrayWindow(Window window) {
1026   WindowScreenLookup::iterator it = systraySearchList.find(window);
1027   if (it != systraySearchList.end())
1028     return it->second;
1029
1030   return (BScreen*) 0;
1031 }
1032
1033
1034 BlackboxWindow *Blackbox::searchWindow(Window window) {
1035   WindowLookup::iterator it = windowSearchList.find(window);
1036   if (it != windowSearchList.end())
1037     return it->second;
1038
1039   return (BlackboxWindow*) 0;
1040 }
1041
1042
1043 BWindowGroup *Blackbox::searchGroup(Window window) {
1044   GroupLookup::iterator it = groupSearchList.find(window);
1045   if (it != groupSearchList.end())
1046     return it->second;
1047
1048   return (BWindowGroup *) 0;
1049 }
1050
1051
1052 Basemenu *Blackbox::searchMenu(Window window) {
1053   MenuLookup::iterator it = menuSearchList.find(window);
1054   if (it != menuSearchList.end())
1055     return it->second;
1056
1057   return (Basemenu*) 0;
1058 }
1059
1060
1061 Toolbar *Blackbox::searchToolbar(Window window) {
1062   ToolbarLookup::iterator it = toolbarSearchList.find(window);
1063   if (it != toolbarSearchList.end())
1064     return it->second;
1065
1066   return (Toolbar*) 0;
1067 }
1068
1069
1070 Slit *Blackbox::searchSlit(Window window) {
1071   SlitLookup::iterator it = slitSearchList.find(window);
1072   if (it != slitSearchList.end())
1073     return it->second;
1074
1075   return (Slit*) 0;
1076 }
1077
1078
1079 void Blackbox::saveSystrayWindowSearch(Window window, BScreen *screen) {
1080   systraySearchList.insert(WindowScreenLookupPair(window, screen));
1081 }
1082
1083
1084 void Blackbox::saveWindowSearch(Window window, BlackboxWindow *data) {
1085   windowSearchList.insert(WindowLookupPair(window, data));
1086 }
1087
1088
1089 void Blackbox::saveGroupSearch(Window window, BWindowGroup *data) {
1090   groupSearchList.insert(GroupLookupPair(window, data));
1091 }
1092
1093
1094 void Blackbox::saveMenuSearch(Window window, Basemenu *data) {
1095   menuSearchList.insert(MenuLookupPair(window, data));
1096 }
1097
1098
1099 void Blackbox::saveToolbarSearch(Window window, Toolbar *data) {
1100   toolbarSearchList.insert(ToolbarLookupPair(window, data));
1101 }
1102
1103
1104 void Blackbox::saveSlitSearch(Window window, Slit *data) {
1105   slitSearchList.insert(SlitLookupPair(window, data));
1106 }
1107
1108
1109 void Blackbox::removeSystrayWindowSearch(Window window) {
1110   systraySearchList.erase(window);
1111 }
1112
1113
1114 void Blackbox::removeWindowSearch(Window window) {
1115   windowSearchList.erase(window);
1116 }
1117
1118
1119 void Blackbox::removeGroupSearch(Window window) {
1120   groupSearchList.erase(window);
1121 }
1122
1123
1124 void Blackbox::removeMenuSearch(Window window) {
1125   menuSearchList.erase(window);
1126 }
1127
1128
1129 void Blackbox::removeToolbarSearch(Window window) {
1130   toolbarSearchList.erase(window);
1131 }
1132
1133
1134 void Blackbox::removeSlitSearch(Window window) {
1135   slitSearchList.erase(window);
1136 }
1137
1138
1139 void Blackbox::restart(const char *prog) {
1140   shutdown();
1141
1142   if (prog) {
1143     putenv(const_cast<char *>(screenList.front()->displayString().c_str()));
1144     execlp(prog, prog, NULL);
1145     perror(prog);
1146   }
1147
1148   // fall back in case the above execlp doesn't work
1149   execvp(argv[0], argv);
1150   string name = basename(argv[0]);
1151   execvp(name.c_str(), argv);
1152 }
1153
1154
1155 void Blackbox::shutdown(void) {
1156   BaseDisplay::shutdown();
1157
1158   XSetInputFocus(getXDisplay(), PointerRoot, None, CurrentTime);
1159
1160   std::for_each(screenList.begin(), screenList.end(),
1161                 std::mem_fun(&BScreen::shutdown));
1162
1163   XSync(getXDisplay(), False);
1164 }
1165
1166
1167 #ifdef    XINERAMA
1168 void Blackbox::saveXineramaPlacement(bool x) {
1169   resource.xinerama_placement = x;
1170   config.setValue("session.xineramaSupport.windowPlacement",
1171                   resource.xinerama_placement);
1172   reconfigure();  // make sure all screens get this change
1173 }
1174
1175
1176 void Blackbox::saveXineramaMaximizing(bool x) {
1177   resource.xinerama_maximize = x;
1178   config.setValue("session.xineramaSupport.windowMaximizing",
1179                   resource.xinerama_maximize);
1180   reconfigure();  // make sure all screens get this change
1181 }
1182
1183
1184 void Blackbox::saveXineramaSnapping(bool x) {
1185   resource.xinerama_snap = x;
1186   config.setValue("session.xineramaSupport.windowSnapping",
1187                   resource.xinerama_snap);
1188   reconfigure();  // make sure all screens get this change
1189 }
1190 #endif // XINERAMA
1191
1192   
1193 /*
1194  * Save all values as they are so that the defaults will be written to the rc
1195  * file
1196  */
1197 void Blackbox::save_rc(void) {
1198   config.setAutoSave(false);
1199
1200   config.setValue("session.colorsPerChannel", resource.colors_per_channel);
1201   config.setValue("session.doubleClickInterval",
1202                   resource.double_click_interval);
1203   config.setValue("session.autoRaiseDelay",
1204                   ((resource.auto_raise_delay.tv_sec * 1000) +
1205                    (resource.auto_raise_delay.tv_usec / 1000)));
1206   config.setValue("session.cacheLife", resource.cache_life / 60000);
1207   config.setValue("session.cacheMax", resource.cache_max);
1208   config.setValue("session.styleFile", resource.style_file);
1209   config.setValue("session.titlebarLayout", resource.titlebar_layout);
1210   
1211 #ifdef    XINERAMA
1212   saveXineramaPlacement(resource.xinerama_placement);
1213   saveXineramaMaximizing(resource.xinerama_maximize);
1214   saveXineramaSnapping(resource.xinerama_snap);
1215 #endif // XINERAMA
1216
1217   std::for_each(screenList.begin(), screenList.end(),
1218                 std::mem_fun(&BScreen::save_rc));
1219  
1220   config.setAutoSave(true);
1221   config.save();
1222 }
1223
1224
1225 void Blackbox::load_rc(void) {
1226   if (! config.load())
1227     config.create();
1228   
1229   string s;
1230
1231   if (! config.getValue("session.colorsPerChannel",
1232                         resource.colors_per_channel))
1233     resource.colors_per_channel = 4;
1234   if (resource.colors_per_channel < 2) resource.colors_per_channel = 2;
1235   else if (resource.colors_per_channel > 6) resource.colors_per_channel = 6;
1236
1237   if (config.getValue("session.styleFile", s))
1238     resource.style_file = expandTilde(s);
1239   else
1240     resource.style_file = DEFAULTSTYLE;
1241
1242   if (! config.getValue("session.doubleClickInterval",
1243                        resource.double_click_interval));
1244     resource.double_click_interval = 250;
1245
1246   if (! config.getValue("session.autoRaiseDelay",
1247                        resource.auto_raise_delay.tv_usec))
1248     resource.auto_raise_delay.tv_usec = 400;
1249   resource.auto_raise_delay.tv_sec = resource.auto_raise_delay.tv_usec / 1000;
1250   resource.auto_raise_delay.tv_usec -=
1251     (resource.auto_raise_delay.tv_sec * 1000);
1252   resource.auto_raise_delay.tv_usec *= 1000;
1253
1254   if (! config.getValue("session.cacheLife", resource.cache_life))
1255     resource.cache_life = 5;
1256   resource.cache_life *= 60000;
1257
1258   if (! config.getValue("session.cacheMax", resource.cache_max))
1259     resource.cache_max = 200;
1260   
1261   if (! config.getValue("session.titlebarLayout", resource.titlebar_layout))
1262     resource.titlebar_layout = "ILMC";
1263
1264 #ifdef    XINERAMA
1265   if (! config.getValue("session.xineramaSupport.windowPlacement",
1266                         resource.xinerama_placement))
1267     resource.xinerama_placement = true;
1268
1269   if (! config.getValue("session.xineramaSupport.windowMaximizing",
1270                         resource.xinerama_maximize))
1271     resource.xinerama_maximize = true;
1272
1273   if (! config.getValue("session.xineramaSupport.windowSnapping",
1274                         resource.xinerama_snap))
1275     resource.xinerama_snap = true;
1276 #endif // XINERAMA
1277 }
1278
1279
1280 void Blackbox::reconfigure(void) {
1281   // don't reconfigure while saving the initial rc file, it's a waste and it
1282   // breaks somethings (workspace names)
1283   if (isStartup()) return;
1284
1285   reconfigure_wait = True;
1286
1287   if (! timer->isTiming()) timer->start();
1288 }
1289
1290
1291 void Blackbox::real_reconfigure(void) {
1292   load_rc();
1293   
1294   std::for_each(menuTimestamps.begin(), menuTimestamps.end(),
1295                 PointerAssassin());
1296   menuTimestamps.clear();
1297
1298   gcCache()->purge();
1299
1300   std::for_each(screenList.begin(), screenList.end(),
1301                 std::mem_fun(&BScreen::reconfigure));
1302 }
1303
1304
1305 void Blackbox::checkMenu(void) {
1306   bool reread = False;
1307   MenuTimestampList::iterator it = menuTimestamps.begin();
1308   for(; it != menuTimestamps.end(); ++it) {
1309     MenuTimestamp *tmp = *it;
1310     struct stat buf;
1311
1312     if (! stat(tmp->filename.c_str(), &buf)) {
1313       if (tmp->timestamp != buf.st_ctime)
1314         reread = True;
1315     } else {
1316       reread = True;
1317     }
1318   }
1319
1320   if (reread) rereadMenu();
1321 }
1322
1323
1324 void Blackbox::rereadMenu(void) {
1325   reread_menu_wait = True;
1326
1327   if (! timer->isTiming()) timer->start();
1328 }
1329
1330
1331 void Blackbox::real_rereadMenu(void) {
1332   std::for_each(menuTimestamps.begin(), menuTimestamps.end(),
1333                 PointerAssassin());
1334   menuTimestamps.clear();
1335
1336   std::for_each(screenList.begin(), screenList.end(),
1337                 std::mem_fun(&BScreen::rereadMenu));
1338 }
1339
1340
1341 void Blackbox::saveStyleFilename(const string& filename) {
1342   assert(! filename.empty());
1343   resource.style_file = filename;
1344   config.setValue("session.styleFile", resource.style_file);
1345 }
1346
1347
1348 void Blackbox::addMenuTimestamp(const string& filename) {
1349   assert(! filename.empty());
1350   bool found = False;
1351
1352   MenuTimestampList::iterator it = menuTimestamps.begin();
1353   for (; it != menuTimestamps.end() && ! found; ++it) {
1354     if ((*it)->filename == filename) found = True;
1355   }
1356   if (! found) {
1357     struct stat buf;
1358
1359     if (! stat(filename.c_str(), &buf)) {
1360       MenuTimestamp *ts = new MenuTimestamp;
1361
1362       ts->filename = filename;
1363       ts->timestamp = buf.st_ctime;
1364
1365       menuTimestamps.push_back(ts);
1366     }
1367   }
1368 }
1369
1370
1371 void Blackbox::timeout(void) {
1372   if (reconfigure_wait)
1373     real_reconfigure();
1374
1375   if (reread_menu_wait)
1376     real_rereadMenu();
1377
1378   reconfigure_wait = reread_menu_wait = False;
1379 }
1380
1381
1382 void Blackbox::setChangingWindow(BlackboxWindow *win) {
1383   // make sure one of the two is null and the other isn't
1384   assert((! changing_window && win) || (! win && changing_window));
1385   changing_window = win;
1386 }
1387
1388
1389 void Blackbox::setFocusedWindow(BlackboxWindow *win) {
1390   if (focused_window && focused_window == win) // nothing to do
1391     return;
1392
1393   BScreen *old_screen = 0;
1394
1395   if (focused_window) {
1396     focused_window->setFocusFlag(False);
1397     old_screen = focused_window->getScreen();
1398   }
1399
1400   if (win && ! win->isIconic()) {
1401     // the active screen is the one with the last focused window...
1402     // this will keep focus on this screen no matter where the mouse goes,
1403     // so multihead keybindings will continue to work on that screen until the
1404     // user focuses a window on a different screen.
1405     active_screen = win->getScreen();
1406     focused_window = win;
1407   } else {
1408     focused_window = 0;
1409     if (! old_screen) {
1410       if (active_screen) {
1411         // set input focus to the toolbar of the screen with mouse
1412         XSetInputFocus(getXDisplay(),
1413                        active_screen->getRootWindow(),
1414                        RevertToPointerRoot, CurrentTime);
1415       } else {
1416         // set input focus to the toolbar of the first managed screen
1417         XSetInputFocus(getXDisplay(),
1418                        screenList.front()->getRootWindow(),
1419                        RevertToPointerRoot, CurrentTime);
1420       }
1421     } else {
1422       // set input focus to the toolbar of the last screen
1423       XSetInputFocus(getXDisplay(), old_screen->getRootWindow(),
1424                      RevertToPointerRoot, CurrentTime);
1425     }
1426   }
1427
1428   if (active_screen && active_screen->isScreenManaged()) {
1429     active_screen->getToolbar()->redrawWindowLabel(True);
1430     active_screen->updateNetizenWindowFocus();
1431   }
1432
1433   if (old_screen && old_screen != active_screen) {
1434     old_screen->getToolbar()->redrawWindowLabel(True);
1435     old_screen->updateNetizenWindowFocus();
1436   }
1437 }