]> icculus.org git repositories - dana/openbox.git/blob - src/blackbox.cc
add comment
[dana/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     // the pointer is on the wrong screen
443     if (! e->xmotion.same_screen)
444       break;
445
446     // strip the lock key modifiers
447     e->xmotion.state &= ~(NumLockMask | ScrollLockMask | LockMask);
448
449     last_time = e->xmotion.time;
450
451     BlackboxWindow *win = (BlackboxWindow *) 0;
452     Basemenu *menu = (Basemenu *) 0;
453
454     if ((win = searchWindow(e->xmotion.window)))
455       win->motionNotifyEvent(&e->xmotion);
456     else if ((menu = searchMenu(e->xmotion.window)))
457       menu->motionNotifyEvent(&e->xmotion);
458
459     break;
460   }
461
462   case PropertyNotify: {
463     last_time = e->xproperty.time;
464
465     BlackboxWindow *win = (BlackboxWindow *) 0;
466     BScreen *screen = (BScreen *) 0;
467
468     if ((win = searchWindow(e->xproperty.window)))
469       win->propertyNotifyEvent(&e->xproperty);
470     else if ((screen = searchScreen(e->xproperty.window)))
471       screen->propertyNotifyEvent(&e->xproperty);
472     break;
473   }
474
475   case EnterNotify: {
476     last_time = e->xcrossing.time;
477
478     BScreen *screen = (BScreen *) 0;
479     BlackboxWindow *win = (BlackboxWindow *) 0;
480     Basemenu *menu = (Basemenu *) 0;
481     Toolbar *tbar = (Toolbar *) 0;
482     Slit *slit = (Slit *) 0;
483
484     if (e->xcrossing.mode == NotifyGrab) break;
485
486     XEvent dummy;
487     scanargs sa;
488     sa.w = e->xcrossing.window;
489     sa.enter = sa.leave = False;
490     XCheckIfEvent(getXDisplay(), &dummy, queueScanner, (char *) &sa);
491
492     if ((e->xcrossing.window == e->xcrossing.root) &&
493         (screen = searchScreen(e->xcrossing.window))) {
494       screen->getImageControl()->installRootColormap();
495     } else if ((win = searchWindow(e->xcrossing.window))) {
496       if (win->getScreen()->isSloppyFocus() &&
497           (! win->isFocused()) && (! no_focus) &&
498           win->isNormal()) {  // don't focus non-normal windows with mouseover
499         if (((! sa.leave) || sa.inferior) && win->isVisible()) {
500           if (win->setInputFocus())
501             win->installColormap(True); // XXX: shouldnt we honour no install?
502         }
503       }
504     } else if ((menu = searchMenu(e->xcrossing.window))) {
505       menu->enterNotifyEvent(&e->xcrossing);
506     } else if ((tbar = searchToolbar(e->xcrossing.window))) {
507       tbar->enterNotifyEvent(&e->xcrossing);
508     } else if ((slit = searchSlit(e->xcrossing.window))) {
509       slit->enterNotifyEvent(&e->xcrossing);
510     }
511     break;
512   }
513
514   case LeaveNotify: {
515     last_time = e->xcrossing.time;
516
517     BlackboxWindow *win = (BlackboxWindow *) 0;
518     Basemenu *menu = (Basemenu *) 0;
519     Toolbar *tbar = (Toolbar *) 0;
520     Slit *slit = (Slit *) 0;
521
522     if ((menu = searchMenu(e->xcrossing.window)))
523       menu->leaveNotifyEvent(&e->xcrossing);
524     else if ((win = searchWindow(e->xcrossing.window)))
525       win->installColormap(False);
526     else if ((tbar = searchToolbar(e->xcrossing.window)))
527       tbar->leaveNotifyEvent(&e->xcrossing);
528     else if ((slit = searchSlit(e->xcrossing.window)))
529       slit->leaveNotifyEvent(&e->xcrossing);
530     break;
531   }
532
533   case Expose: {
534     // compress expose events
535     XEvent realevent;
536     unsigned int i = 0;
537     int ex1, ey1, ex2, ey2;
538     ex1 = e->xexpose.x;
539     ey1 = e->xexpose.y;
540     ex2 = ex1 + e->xexpose.width - 1;
541     ey2 = ey1 + e->xexpose.height - 1;
542     while (XCheckTypedWindowEvent(getXDisplay(), e->xexpose.window,
543                                   Expose, &realevent)) {
544       i++;
545
546       // merge expose area
547       ex1 = std::min(realevent.xexpose.x, ex1);
548       ey1 = std::min(realevent.xexpose.y, ey1);
549       ex2 = std::max(realevent.xexpose.x + realevent.xexpose.width - 1, ex2);
550       ey2 = std::max(realevent.xexpose.y + realevent.xexpose.height - 1, ey2);
551     }
552     if ( i > 0 )
553       e = &realevent;
554
555     // use the merged area
556     e->xexpose.x = ex1;
557     e->xexpose.y = ey1;
558     e->xexpose.width = ex2 - ex1 + 1;
559     e->xexpose.height = ey2 - ey1 + 1;
560
561     BlackboxWindow *win = (BlackboxWindow *) 0;
562     Basemenu *menu = (Basemenu *) 0;
563     Toolbar *tbar = (Toolbar *) 0;
564
565     if ((win = searchWindow(e->xexpose.window)))
566       win->exposeEvent(&e->xexpose);
567     else if ((menu = searchMenu(e->xexpose.window)))
568       menu->exposeEvent(&e->xexpose);
569     else if ((tbar = searchToolbar(e->xexpose.window)))
570       tbar->exposeEvent(&e->xexpose);
571
572     break;
573   }
574
575   case KeyPress: {
576     Toolbar *tbar = searchToolbar(e->xkey.window);
577
578     if (tbar && tbar->isEditing())
579       tbar->keyPressEvent(&e->xkey);
580
581     break;
582   }
583
584   case ColormapNotify: {
585     BScreen *screen = searchScreen(e->xcolormap.window);
586
587     if (screen)
588       screen->setRootColormapInstalled((e->xcolormap.state ==
589                                         ColormapInstalled) ? True : False);
590
591     break;
592   }
593
594   case FocusIn: {
595     if (e->xfocus.detail != NotifyNonlinear &&
596         e->xfocus.detail != NotifyAncestor) {
597       /*
598         don't process FocusIns when:
599         1. the new focus window isn't an ancestor or inferior of the old
600         focus window (NotifyNonlinear)
601         make sure to allow the FocusIn when the old focus window was an
602         ancestor but didn't have a parent, such as root (NotifyAncestor)
603       */
604       break;
605     }
606
607     BlackboxWindow *win = searchWindow(e->xfocus.window);
608     if (win) {
609       if (! win->isFocused())
610         win->setFocusFlag(True);
611
612       /*
613         set the event window to None.  when the FocusOut event handler calls
614         this function recursively, it uses this as an indication that focus
615         has moved to a known window.
616       */
617       e->xfocus.window = None;
618     }
619
620     break;
621   }
622
623   case FocusOut: {
624     if (e->xfocus.detail != NotifyNonlinear) {
625       /*
626         don't process FocusOuts when:
627         2. the new focus window isn't an ancestor or inferior of the old
628         focus window (NotifyNonlinear)
629       */
630       break;
631     }
632
633     BlackboxWindow *win = searchWindow(e->xfocus.window);
634     if (win && win->isFocused()) {
635       /*
636         before we mark "win" as unfocused, we need to verify that focus is
637         going to a known location, is in a known location, or set focus
638         to a known location.
639       */
640
641       XEvent event;
642       // don't check the current focus if FocusOut was generated during a grab
643       bool check_focus = (e->xfocus.mode == NotifyNormal);
644
645       /*
646         First, check if there is a pending FocusIn event waiting.  if there
647         is, process it and determine if focus has moved to another window
648         (the FocusIn event handler sets the window in the event
649         structure to None to indicate this).
650       */
651       if (XCheckTypedEvent(getXDisplay(), FocusIn, &event)) {
652
653         process_event(&event);
654         if (event.xfocus.window == None) {
655           // focus has moved
656           check_focus = False;
657         }
658       }
659
660       if (check_focus) {
661         /*
662           Second, we query the X server for the current input focus.
663           to make sure that we keep a consistent state.
664         */
665         BlackboxWindow *focus;
666         Window w;
667         int revert;
668         XGetInputFocus(getXDisplay(), &w, &revert);
669         focus = searchWindow(w);
670         if (focus) {
671           /*
672             focus got from "win" to "focus" under some very strange
673             circumstances, and we need to make sure that the focus indication
674             is correct.
675           */
676           setFocusedWindow(focus);
677         } else {
678           // we have no idea where focus went... so we set it to somewhere
679           setFocusedWindow(0);
680         }
681       }
682     }
683
684     break;
685   }
686
687   case ClientMessage: {
688     if (e->xclient.format == 32) {
689       if (e->xclient.message_type == xatom->getAtom(XAtom::wm_change_state)) {
690         // WM_CHANGE_STATE message
691         BlackboxWindow *win = searchWindow(e->xclient.window);
692         if (! win || ! win->validateClient()) return;
693
694         if (e->xclient.data.l[0] == IconicState)
695           win->iconify();
696         if (e->xclient.data.l[0] == NormalState)
697           win->deiconify();
698       } else if (e->xclient.message_type == 
699                  xatom->getAtom(XAtom::blackbox_change_workspace) || 
700                  e->xclient.message_type == 
701                  xatom->getAtom(XAtom::net_current_desktop)) {
702         // NET_CURRENT_DESKTOP message
703         BScreen *screen = searchScreen(e->xclient.window);
704
705         unsigned int workspace = e->xclient.data.l[0];
706         if (screen && workspace < screen->getWorkspaceCount())
707           screen->changeWorkspaceID(workspace);
708       } else if (e->xclient.message_type == 
709                  xatom->getAtom(XAtom::blackbox_change_window_focus)) {
710         // TEMP HACK TO KEEP BBKEYS WORKING
711         BlackboxWindow *win = searchWindow(e->xclient.window);
712
713         if (win && win->isVisible() && win->setInputFocus())
714           win->installColormap(True);
715       } else if (e->xclient.message_type == 
716                  xatom->getAtom(XAtom::net_active_window)) {
717         // NET_ACTIVE_WINDOW
718         BlackboxWindow *win = searchWindow(e->xclient.window);
719
720         if (win) {
721           BScreen *screen = win->getScreen();
722
723           if (win->isIconic())
724             win->deiconify(False, True);
725           if (win->isShaded())
726             win->shade();
727           if (win->getWorkspaceNumber() != screen->getCurrentWorkspaceID())
728             screen->changeWorkspaceID(win->getWorkspaceNumber());
729           if (win->isVisible() && win->setInputFocus()) {
730             win->getScreen()->getWorkspace(win->getWorkspaceNumber())->
731               raiseWindow(win);
732             win->installColormap(True);
733           }
734         }
735       } else if (e->xclient.message_type == 
736                  xatom->getAtom(XAtom::blackbox_cycle_window_focus)) {
737         // BLACKBOX_CYCLE_WINDOW_FOCUS
738         BScreen *screen = searchScreen(e->xclient.window);
739
740         if (screen) {
741           if (! e->xclient.data.l[0])
742             screen->prevFocus();
743           else
744             screen->nextFocus();
745         }
746       } else if (e->xclient.message_type == 
747                  xatom->getAtom(XAtom::net_wm_desktop)) {
748         // NET_WM_DESKTOP
749         BlackboxWindow *win = searchWindow(e->xclient.window);
750
751         if (win) {
752           BScreen *screen = win->getScreen();
753           unsigned long wksp = (unsigned) e->xclient.data.l[0];
754           if (wksp < screen->getWorkspaceCount()) {
755             if (win->isIconic()) win->deiconify(False, True);
756             if (win->isStuck()) win->stick();
757             if (wksp != screen->getCurrentWorkspaceID())
758               win->withdraw();
759             else
760               win->show();
761             screen->reassociateWindow(win, wksp, True);
762           } else if (wksp == 0xfffffffe || // XXX: BUG, BUT DOING THIS SO KDE WORKS FOR NOW!!
763                      wksp == 0xffffffff) {
764             if (win->isIconic()) win->deiconify(False, True);
765             if (! win->isStuck()) win->stick();
766             if (! win->isVisible()) win->show();
767           }
768         }
769       } else if (e->xclient.message_type == 
770                  xatom->getAtom(XAtom::blackbox_change_attributes)) {
771         // BLACKBOX_CHANGE_ATTRIBUTES
772         BlackboxWindow *win = searchWindow(e->xclient.window);
773
774         if (win && win->validateClient()) {
775           BlackboxHints net;
776           net.flags = e->xclient.data.l[0];
777           net.attrib = e->xclient.data.l[1];
778           net.workspace = e->xclient.data.l[2];
779           net.stack = e->xclient.data.l[3];
780           net.decoration = e->xclient.data.l[4];
781
782           win->changeBlackboxHints(&net);
783         }
784       } else if (e->xclient.message_type == 
785                 xatom->getAtom(XAtom::net_number_of_desktops)) {
786         // NET_NUMBER_OF_DESKTOPS
787         BScreen *screen = searchScreen(e->xclient.window);
788         
789         if (e->xclient.data.l[0] > 0)
790           screen->changeWorkspaceCount((unsigned) e->xclient.data.l[0]);
791       } else if (e->xclient.message_type ==
792                  xatom->getAtom(XAtom::net_close_window)) {
793         // NET_CLOSE_WINDOW
794         BlackboxWindow *win = searchWindow(e->xclient.window);
795         if (win && win->validateClient())
796           win->close(); // could this be smarter?
797       } else if (e->xclient.message_type ==
798                  xatom->getAtom(XAtom::net_wm_moveresize)) {
799         // NET_WM_MOVERESIZE
800         BlackboxWindow *win = searchWindow(e->xclient.window);
801         if (win && win->validateClient()) {
802           int x_root = e->xclient.data.l[0],
803               y_root = e->xclient.data.l[1];
804           if ((Atom) e->xclient.data.l[2] ==
805               xatom->getAtom(XAtom::net_wm_moveresize_move)) {
806             win->beginMove(x_root, y_root);
807           } else {
808             if ((Atom) e->xclient.data.l[2] ==
809                 xatom->getAtom(XAtom::net_wm_moveresize_size_topleft))
810               win->beginResize(x_root, y_root, BlackboxWindow::TopLeft);
811             else if ((Atom) e->xclient.data.l[2] ==
812                      xatom->getAtom(XAtom::net_wm_moveresize_size_topright))
813               win->beginResize(x_root, y_root, BlackboxWindow::TopRight);
814             else if ((Atom) e->xclient.data.l[2] ==
815                      xatom->getAtom(XAtom::net_wm_moveresize_size_bottomleft))
816               win->beginResize(x_root, y_root, BlackboxWindow::BottomLeft);
817             else if ((Atom) e->xclient.data.l[2] ==
818                 xatom->getAtom(XAtom::net_wm_moveresize_size_bottomright))
819               win->beginResize(x_root, y_root, BlackboxWindow::BottomRight);
820           }
821         }
822       } else if (e->xclient.message_type ==
823                  xatom->getAtom(XAtom::net_wm_state)) {
824         // NET_WM_STATE
825         BlackboxWindow *win = searchWindow(e->xclient.window);
826         if (win && win->validateClient()) {
827           const Atom action = (Atom) e->xclient.data.l[0];
828           const Atom state[] = { (Atom) e->xclient.data.l[1],
829                                  (Atom) e->xclient.data.l[2] };
830           
831           for (int i = 0; i < 2; ++i) {
832             if (! state[i])
833               continue;
834
835             if ((Atom) e->xclient.data.l[0] == 1) {
836               // ADD
837               if (state[i] == xatom->getAtom(XAtom::net_wm_state_modal)) {
838                 win->setModal(True);
839               } else if (state[i] ==
840                          xatom->getAtom(XAtom::net_wm_state_maximized_vert)) {
841                 if (win->isMaximizedHoriz()) {
842                   win->maximize(0); // unmaximize
843                   win->maximize(1); // full
844                 } else if (! win->isMaximized()) {
845                   win->maximize(2); // vert
846                 }
847               } else if (state[i] ==
848                          xatom->getAtom(XAtom::net_wm_state_maximized_horz)) {
849                 if (win->isMaximizedVert()) {
850                   win->maximize(0); // unmaximize
851                   win->maximize(1); // full
852                 } else if (! win->isMaximized()) {
853                   win->maximize(3); // horiz
854                 }
855               } else if (state[i] ==
856                          xatom->getAtom(XAtom::net_wm_state_shaded)) {
857                 if (! win->isShaded())
858                   win->shade();
859               } else if (state[i] ==
860                          xatom->getAtom(XAtom::net_wm_state_skip_taskbar)) {
861                 win->setSkipTaskbar(True);
862               } else if (state[i] ==
863                          xatom->getAtom(XAtom::net_wm_state_skip_pager)) {
864                 win->setSkipPager(True);
865               } else if (state[i] ==
866                          xatom->getAtom(XAtom::net_wm_state_fullscreen)) {
867                 win->setFullscreen(True);
868               }
869             } else if (action == 0) {
870               // REMOVE
871               if (state[i] == xatom->getAtom(XAtom::net_wm_state_modal)) {
872                 win->setModal(False);
873               } else if (state[i] ==
874                          xatom->getAtom(XAtom::net_wm_state_maximized_vert)) {
875                 if (win->isMaximizedFull()) {
876                   win->maximize(0); // unmaximize
877                   win->maximize(3); // horiz
878                 } else if (win->isMaximizedVert()) {
879                   win->maximize(0); // unmaximize
880                 }
881               } else if (state[i] ==
882                          xatom->getAtom(XAtom::net_wm_state_maximized_horz)) {
883                 if (win->isMaximizedFull()) {
884                   win->maximize(0); // unmaximize
885                   win->maximize(2); // vert
886                 } else if (win->isMaximizedHoriz()) {
887                   win->maximize(0); // unmaximize
888                 }
889               } else if (state[i] ==
890                          xatom->getAtom(XAtom::net_wm_state_shaded)) {
891                 if (win->isShaded())
892                   win->shade();
893               } else if (state[i] ==
894                          xatom->getAtom(XAtom::net_wm_state_skip_taskbar)) {
895                 win->setSkipTaskbar(False);
896               } else if (state[i] ==
897                          xatom->getAtom(XAtom::net_wm_state_skip_pager)) {
898                 win->setSkipPager(False);
899               } else if (state[i] ==
900                          xatom->getAtom(XAtom::net_wm_state_fullscreen)) {
901                 win->setFullscreen(False);
902               }
903             } else if (action == 2) {
904               // TOGGLE
905               if (state[i] == xatom->getAtom(XAtom::net_wm_state_modal)) {
906                 win->setModal(! win->isModal());
907               } else if (state[i] ==
908                          xatom->getAtom(XAtom::net_wm_state_maximized_vert)) {
909                 if (win->isMaximizedFull()) {
910                   win->maximize(0); // unmaximize
911                   win->maximize(3); // horiz
912                 } else if (win->isMaximizedVert()) {
913                   win->maximize(0); // unmaximize
914                 } else if (win->isMaximizedHoriz()) {
915                   win->maximize(0); // unmaximize
916                   win->maximize(1); // full
917                 } else {
918                   win->maximize(2); // vert
919                 }
920               } else if (state[i] ==
921                          xatom->getAtom(XAtom::net_wm_state_maximized_horz)) {
922                 if (win->isMaximizedFull()) {
923                   win->maximize(0); // unmaximize
924                   win->maximize(2); // vert
925                 } else if (win->isMaximizedHoriz()) {
926                   win->maximize(0); // unmaximize
927                 } else if (win->isMaximizedVert()) {
928                   win->maximize(0); // unmaximize
929                   win->maximize(1); // full
930                 } else {
931                   win->maximize(3); // horiz
932                 }
933               } else if (state[i] ==
934                          xatom->getAtom(XAtom::net_wm_state_shaded)) {
935                 win->shade();
936               } else if (state[i] ==
937                          xatom->getAtom(XAtom::net_wm_state_skip_taskbar)) {
938                 win->setSkipTaskbar(! win->skipTaskbar());
939               } else if (state[i] ==
940                          xatom->getAtom(XAtom::net_wm_state_skip_pager)) {
941                 win->setSkipPager(! win->skipPager());
942               } else if (state[i] ==
943                          xatom->getAtom(XAtom::net_wm_state_fullscreen)) {
944                 win->setFullscreen(! win->isFullscreen());
945               }
946             }
947           }
948         }
949       }
950     }
951
952     break;
953   }
954
955   case NoExpose:
956   case ConfigureNotify:
957   case MapNotify:
958     break; // not handled, just ignore
959
960   default: {
961 #ifdef    SHAPE
962     if (e->type == getShapeEventBase()) {
963       XShapeEvent *shape_event = (XShapeEvent *) e;
964       BlackboxWindow *win = searchWindow(e->xany.window);
965
966       if (win)
967         win->shapeEvent(shape_event);
968     }
969 #endif // SHAPE
970   }
971   } // switch
972 }
973
974
975 bool Blackbox::handleSignal(int sig) {
976   switch (sig) {
977   case SIGHUP:
978     reconfigure();
979     break;
980
981   case SIGUSR1:
982     restart();
983     break;
984
985   case SIGUSR2:
986     rereadMenu();
987     break;
988
989   case SIGPIPE:
990   case SIGSEGV:
991   case SIGFPE:
992   case SIGINT:
993   case SIGTERM:
994     shutdown();
995
996   default:
997     return False;
998   }
999
1000   return True;
1001 }
1002
1003
1004 bool Blackbox::validateWindow(Window window) {
1005   XEvent event;
1006   if (XCheckTypedWindowEvent(getXDisplay(), window, DestroyNotify, &event)) {
1007     XPutBackEvent(getXDisplay(), &event);
1008
1009     return False;
1010   }
1011
1012   return True;
1013 }
1014
1015
1016 BScreen *Blackbox::searchScreen(Window window) {
1017   ScreenList::iterator it = screenList.begin();
1018
1019   for (; it != screenList.end(); ++it) {
1020     BScreen *s = *it;
1021     if (s->getRootWindow() == window)
1022       return s;
1023   }
1024
1025   return (BScreen *) 0;
1026 }
1027
1028
1029 BScreen *Blackbox::searchSystrayWindow(Window window) {
1030   WindowScreenLookup::iterator it = systraySearchList.find(window);
1031   if (it != systraySearchList.end())
1032     return it->second;
1033
1034   return (BScreen*) 0;
1035 }
1036
1037
1038 BlackboxWindow *Blackbox::searchWindow(Window window) {
1039   WindowLookup::iterator it = windowSearchList.find(window);
1040   if (it != windowSearchList.end())
1041     return it->second;
1042
1043   return (BlackboxWindow*) 0;
1044 }
1045
1046
1047 BWindowGroup *Blackbox::searchGroup(Window window) {
1048   GroupLookup::iterator it = groupSearchList.find(window);
1049   if (it != groupSearchList.end())
1050     return it->second;
1051
1052   return (BWindowGroup *) 0;
1053 }
1054
1055
1056 Basemenu *Blackbox::searchMenu(Window window) {
1057   MenuLookup::iterator it = menuSearchList.find(window);
1058   if (it != menuSearchList.end())
1059     return it->second;
1060
1061   return (Basemenu*) 0;
1062 }
1063
1064
1065 Toolbar *Blackbox::searchToolbar(Window window) {
1066   ToolbarLookup::iterator it = toolbarSearchList.find(window);
1067   if (it != toolbarSearchList.end())
1068     return it->second;
1069
1070   return (Toolbar*) 0;
1071 }
1072
1073
1074 Slit *Blackbox::searchSlit(Window window) {
1075   SlitLookup::iterator it = slitSearchList.find(window);
1076   if (it != slitSearchList.end())
1077     return it->second;
1078
1079   return (Slit*) 0;
1080 }
1081
1082
1083 void Blackbox::saveSystrayWindowSearch(Window window, BScreen *screen) {
1084   systraySearchList.insert(WindowScreenLookupPair(window, screen));
1085 }
1086
1087
1088 void Blackbox::saveWindowSearch(Window window, BlackboxWindow *data) {
1089   windowSearchList.insert(WindowLookupPair(window, data));
1090 }
1091
1092
1093 void Blackbox::saveGroupSearch(Window window, BWindowGroup *data) {
1094   groupSearchList.insert(GroupLookupPair(window, data));
1095 }
1096
1097
1098 void Blackbox::saveMenuSearch(Window window, Basemenu *data) {
1099   menuSearchList.insert(MenuLookupPair(window, data));
1100 }
1101
1102
1103 void Blackbox::saveToolbarSearch(Window window, Toolbar *data) {
1104   toolbarSearchList.insert(ToolbarLookupPair(window, data));
1105 }
1106
1107
1108 void Blackbox::saveSlitSearch(Window window, Slit *data) {
1109   slitSearchList.insert(SlitLookupPair(window, data));
1110 }
1111
1112
1113 void Blackbox::removeSystrayWindowSearch(Window window) {
1114   systraySearchList.erase(window);
1115 }
1116
1117
1118 void Blackbox::removeWindowSearch(Window window) {
1119   windowSearchList.erase(window);
1120 }
1121
1122
1123 void Blackbox::removeGroupSearch(Window window) {
1124   groupSearchList.erase(window);
1125 }
1126
1127
1128 void Blackbox::removeMenuSearch(Window window) {
1129   menuSearchList.erase(window);
1130 }
1131
1132
1133 void Blackbox::removeToolbarSearch(Window window) {
1134   toolbarSearchList.erase(window);
1135 }
1136
1137
1138 void Blackbox::removeSlitSearch(Window window) {
1139   slitSearchList.erase(window);
1140 }
1141
1142
1143 void Blackbox::restart(const char *prog) {
1144   shutdown();
1145
1146   if (prog) {
1147     putenv(const_cast<char *>(screenList.front()->displayString().c_str()));
1148     execlp(prog, prog, NULL);
1149     perror(prog);
1150   }
1151
1152   // fall back in case the above execlp doesn't work
1153   execvp(argv[0], argv);
1154   string name = basename(argv[0]);
1155   execvp(name.c_str(), argv);
1156 }
1157
1158
1159 void Blackbox::shutdown(void) {
1160   BaseDisplay::shutdown();
1161
1162   XSetInputFocus(getXDisplay(), PointerRoot, None, CurrentTime);
1163
1164   std::for_each(screenList.begin(), screenList.end(),
1165                 std::mem_fun(&BScreen::shutdown));
1166
1167   XSync(getXDisplay(), False);
1168 }
1169
1170
1171 #ifdef    XINERAMA
1172 void Blackbox::saveXineramaPlacement(bool x) {
1173   resource.xinerama_placement = x;
1174   config.setValue("session.xineramaSupport.windowPlacement",
1175                   resource.xinerama_placement);
1176   reconfigure();  // make sure all screens get this change
1177 }
1178
1179
1180 void Blackbox::saveXineramaMaximizing(bool x) {
1181   resource.xinerama_maximize = x;
1182   config.setValue("session.xineramaSupport.windowMaximizing",
1183                   resource.xinerama_maximize);
1184   reconfigure();  // make sure all screens get this change
1185 }
1186
1187
1188 void Blackbox::saveXineramaSnapping(bool x) {
1189   resource.xinerama_snap = x;
1190   config.setValue("session.xineramaSupport.windowSnapping",
1191                   resource.xinerama_snap);
1192   reconfigure();  // make sure all screens get this change
1193 }
1194 #endif // XINERAMA
1195
1196   
1197 /*
1198  * Save all values as they are so that the defaults will be written to the rc
1199  * file
1200  */
1201 void Blackbox::save_rc(void) {
1202   config.setAutoSave(false);
1203
1204   config.setValue("session.colorsPerChannel", resource.colors_per_channel);
1205   config.setValue("session.doubleClickInterval",
1206                   resource.double_click_interval);
1207   config.setValue("session.autoRaiseDelay",
1208                   ((resource.auto_raise_delay.tv_sec * 1000) +
1209                    (resource.auto_raise_delay.tv_usec / 1000)));
1210   config.setValue("session.cacheLife", resource.cache_life / 60000);
1211   config.setValue("session.cacheMax", resource.cache_max);
1212   config.setValue("session.styleFile", resource.style_file);
1213   config.setValue("session.titlebarLayout", resource.titlebar_layout);
1214   
1215 #ifdef    XINERAMA
1216   saveXineramaPlacement(resource.xinerama_placement);
1217   saveXineramaMaximizing(resource.xinerama_maximize);
1218   saveXineramaSnapping(resource.xinerama_snap);
1219 #endif // XINERAMA
1220
1221   std::for_each(screenList.begin(), screenList.end(),
1222                 std::mem_fun(&BScreen::save_rc));
1223  
1224   config.setAutoSave(true);
1225   config.save();
1226 }
1227
1228
1229 void Blackbox::load_rc(void) {
1230   if (! config.load())
1231     config.create();
1232   
1233   string s;
1234
1235   if (! config.getValue("session.colorsPerChannel",
1236                         resource.colors_per_channel))
1237     resource.colors_per_channel = 4;
1238   if (resource.colors_per_channel < 2) resource.colors_per_channel = 2;
1239   else if (resource.colors_per_channel > 6) resource.colors_per_channel = 6;
1240
1241   if (config.getValue("session.styleFile", s))
1242     resource.style_file = expandTilde(s);
1243   else
1244     resource.style_file = DEFAULTSTYLE;
1245
1246   if (! config.getValue("session.doubleClickInterval",
1247                        resource.double_click_interval));
1248     resource.double_click_interval = 250;
1249
1250   if (! config.getValue("session.autoRaiseDelay",
1251                        resource.auto_raise_delay.tv_usec))
1252     resource.auto_raise_delay.tv_usec = 400;
1253   resource.auto_raise_delay.tv_sec = resource.auto_raise_delay.tv_usec / 1000;
1254   resource.auto_raise_delay.tv_usec -=
1255     (resource.auto_raise_delay.tv_sec * 1000);
1256   resource.auto_raise_delay.tv_usec *= 1000;
1257
1258   if (! config.getValue("session.cacheLife", resource.cache_life))
1259     resource.cache_life = 5;
1260   resource.cache_life *= 60000;
1261
1262   if (! config.getValue("session.cacheMax", resource.cache_max))
1263     resource.cache_max = 200;
1264   
1265   if (! config.getValue("session.titlebarLayout", resource.titlebar_layout))
1266     resource.titlebar_layout = "ILMC";
1267
1268 #ifdef    XINERAMA
1269   if (! config.getValue("session.xineramaSupport.windowPlacement",
1270                         resource.xinerama_placement))
1271     resource.xinerama_placement = false;
1272
1273   if (! config.getValue("session.xineramaSupport.windowMaximizing",
1274                         resource.xinerama_maximize))
1275     resource.xinerama_maximize = false;
1276
1277   if (! config.getValue("session.xineramaSupport.windowSnapping",
1278                         resource.xinerama_snap))
1279     resource.xinerama_snap = false;
1280 #endif // XINERAMA
1281 }
1282
1283
1284 void Blackbox::reconfigure(void) {
1285   // don't reconfigure while saving the initial rc file, it's a waste and it
1286   // breaks somethings (workspace names)
1287   if (isStartup()) return;
1288
1289   reconfigure_wait = True;
1290
1291   if (! timer->isTiming()) timer->start();
1292 }
1293
1294
1295 void Blackbox::real_reconfigure(void) {
1296   load_rc();
1297   
1298   std::for_each(menuTimestamps.begin(), menuTimestamps.end(),
1299                 PointerAssassin());
1300   menuTimestamps.clear();
1301
1302   gcCache()->purge();
1303
1304   std::for_each(screenList.begin(), screenList.end(),
1305                 std::mem_fun(&BScreen::reconfigure));
1306 }
1307
1308
1309 void Blackbox::checkMenu(void) {
1310   bool reread = False;
1311   MenuTimestampList::iterator it = menuTimestamps.begin();
1312   for(; it != menuTimestamps.end(); ++it) {
1313     MenuTimestamp *tmp = *it;
1314     struct stat buf;
1315
1316     if (! stat(tmp->filename.c_str(), &buf)) {
1317       if (tmp->timestamp != buf.st_ctime)
1318         reread = True;
1319     } else {
1320       reread = True;
1321     }
1322   }
1323
1324   if (reread) rereadMenu();
1325 }
1326
1327
1328 void Blackbox::rereadMenu(void) {
1329   reread_menu_wait = True;
1330
1331   if (! timer->isTiming()) timer->start();
1332 }
1333
1334
1335 void Blackbox::real_rereadMenu(void) {
1336   std::for_each(menuTimestamps.begin(), menuTimestamps.end(),
1337                 PointerAssassin());
1338   menuTimestamps.clear();
1339
1340   std::for_each(screenList.begin(), screenList.end(),
1341                 std::mem_fun(&BScreen::rereadMenu));
1342 }
1343
1344
1345 void Blackbox::saveStyleFilename(const string& filename) {
1346   assert(! filename.empty());
1347   resource.style_file = filename;
1348   config.setValue("session.styleFile", resource.style_file);
1349 }
1350
1351
1352 void Blackbox::addMenuTimestamp(const string& filename) {
1353   assert(! filename.empty());
1354   bool found = False;
1355
1356   MenuTimestampList::iterator it = menuTimestamps.begin();
1357   for (; it != menuTimestamps.end() && ! found; ++it) {
1358     if ((*it)->filename == filename) found = True;
1359   }
1360   if (! found) {
1361     struct stat buf;
1362
1363     if (! stat(filename.c_str(), &buf)) {
1364       MenuTimestamp *ts = new MenuTimestamp;
1365
1366       ts->filename = filename;
1367       ts->timestamp = buf.st_ctime;
1368
1369       menuTimestamps.push_back(ts);
1370     }
1371   }
1372 }
1373
1374
1375 void Blackbox::timeout(void) {
1376   if (reconfigure_wait)
1377     real_reconfigure();
1378
1379   if (reread_menu_wait)
1380     real_rereadMenu();
1381
1382   reconfigure_wait = reread_menu_wait = False;
1383 }
1384
1385
1386 void Blackbox::setChangingWindow(BlackboxWindow *win) {
1387   // make sure one of the two is null and the other isn't
1388   assert((! changing_window && win) || (! win && changing_window));
1389   changing_window = win;
1390 }
1391
1392
1393 void Blackbox::setFocusedWindow(BlackboxWindow *win) {
1394   if (focused_window && focused_window == win) // nothing to do
1395     return;
1396
1397   BScreen *old_screen = 0;
1398
1399   if (focused_window) {
1400     focused_window->setFocusFlag(False);
1401     old_screen = focused_window->getScreen();
1402   }
1403
1404   if (win && ! win->isIconic()) {
1405     // the active screen is the one with the last focused window...
1406     // this will keep focus on this screen no matter where the mouse goes,
1407     // so multihead keybindings will continue to work on that screen until the
1408     // user focuses a window on a different screen.
1409     active_screen = win->getScreen();
1410     focused_window = win;
1411   } else {
1412     focused_window = 0;
1413     if (! old_screen) {
1414       if (active_screen) {
1415         // set input focus to the toolbar of the screen with mouse
1416         XSetInputFocus(getXDisplay(),
1417                        active_screen->getRootWindow(),
1418                        RevertToPointerRoot, CurrentTime);
1419       } else {
1420         // set input focus to the toolbar of the first managed screen
1421         XSetInputFocus(getXDisplay(),
1422                        screenList.front()->getRootWindow(),
1423                        RevertToPointerRoot, CurrentTime);
1424       }
1425     } else {
1426       // set input focus to the toolbar of the last screen
1427       XSetInputFocus(getXDisplay(), old_screen->getRootWindow(),
1428                      RevertToPointerRoot, CurrentTime);
1429     }
1430   }
1431
1432   if (active_screen && active_screen->isScreenManaged()) {
1433     active_screen->getToolbar()->redrawWindowLabel(True);
1434     active_screen->updateNetizenWindowFocus();
1435   }
1436
1437   if (old_screen && old_screen != active_screen) {
1438     old_screen->getToolbar()->redrawWindowLabel(True);
1439     old_screen->updateNetizenWindowFocus();
1440   }
1441 }