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