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