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