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