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