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