]> icculus.org git repositories - mikachu/openbox.git/blob - src/blackbox.cc
don't focus non-visible windows
[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->getWorkspaceNumber() != screen->getCurrentWorkspaceID())
717             screen->changeWorkspaceID(win->getWorkspaceNumber());
718           if (win->isVisible() && win->setInputFocus()) {
719             win->getScreen()->getWorkspace(win->getWorkspaceNumber())->
720               raiseWindow(win);
721             win->installColormap(True);
722           }
723         }
724       } else if (e->xclient.message_type == 
725                  xatom->getAtom(XAtom::blackbox_cycle_window_focus)) {
726         // BLACKBOX_CYCLE_WINDOW_FOCUS
727         BScreen *screen = searchScreen(e->xclient.window);
728
729         if (screen) {
730           if (! e->xclient.data.l[0])
731             screen->prevFocus();
732           else
733             screen->nextFocus();
734         }
735       } else if (e->xclient.message_type == 
736                  xatom->getAtom(XAtom::net_wm_desktop)) {
737         // NET_WM_DESKTOP
738         BlackboxWindow *win = searchWindow(e->xclient.window);
739
740         if (win) {
741           BScreen *screen = win->getScreen();
742           unsigned long wksp = (unsigned) e->xclient.data.l[0];
743           if (wksp < screen->getWorkspaceCount()) {
744             if (win->isIconic()) win->deiconify(False, True);
745             if (win->isStuck()) win->stick();
746             if (wksp != screen->getCurrentWorkspaceID())
747               win->withdraw();
748             else
749               win->show();
750             screen->reassociateWindow(win, wksp, True);
751           } else if (wksp == 0xfffffffe || // XXX: BUG, BUT DOING THIS SO KDE WORKS FOR NOW!!
752                      wksp == 0xffffffff) {
753             if (win->isIconic()) win->deiconify(False, True);
754             if (! win->isStuck()) win->stick();
755             if (! win->isVisible()) win->show();
756           }
757         }
758       } else if (e->xclient.message_type == 
759                  xatom->getAtom(XAtom::blackbox_change_attributes)) {
760         // BLACKBOX_CHANGE_ATTRIBUTES
761         BlackboxWindow *win = searchWindow(e->xclient.window);
762
763         if (win && win->validateClient()) {
764           BlackboxHints net;
765           net.flags = e->xclient.data.l[0];
766           net.attrib = e->xclient.data.l[1];
767           net.workspace = e->xclient.data.l[2];
768           net.stack = e->xclient.data.l[3];
769           net.decoration = e->xclient.data.l[4];
770
771           win->changeBlackboxHints(&net);
772         }
773       } else if (e->xclient.message_type == 
774                 xatom->getAtom(XAtom::net_number_of_desktops)) {
775         // NET_NUMBER_OF_DESKTOPS
776         BScreen *screen = searchScreen(e->xclient.window);
777         
778         if (e->xclient.data.l[0] > 0)
779           screen->changeWorkspaceCount((unsigned) e->xclient.data.l[0]);
780       } else if (e->xclient.message_type ==
781                  xatom->getAtom(XAtom::net_close_window)) {
782         // NET_CLOSE_WINDOW
783         BlackboxWindow *win = searchWindow(e->xclient.window);
784         if (win && win->validateClient())
785           win->close(); // could this be smarter?
786       } else if (e->xclient.message_type ==
787                  xatom->getAtom(XAtom::net_wm_moveresize)) {
788         // NET_WM_MOVERESIZE
789         BlackboxWindow *win = searchWindow(e->xclient.window);
790         if (win && win->validateClient()) {
791           int x_root = e->xclient.data.l[0],
792               y_root = e->xclient.data.l[1];
793           if ((Atom) e->xclient.data.l[2] ==
794               xatom->getAtom(XAtom::net_wm_moveresize_move)) {
795             win->beginMove(x_root, y_root);
796           } else {
797             if ((Atom) e->xclient.data.l[2] ==
798                 xatom->getAtom(XAtom::net_wm_moveresize_size_topleft))
799               win->beginResize(x_root, y_root, BlackboxWindow::TopLeft);
800             else if ((Atom) e->xclient.data.l[2] ==
801                      xatom->getAtom(XAtom::net_wm_moveresize_size_topright))
802               win->beginResize(x_root, y_root, BlackboxWindow::TopRight);
803             else if ((Atom) e->xclient.data.l[2] ==
804                      xatom->getAtom(XAtom::net_wm_moveresize_size_bottomleft))
805               win->beginResize(x_root, y_root, BlackboxWindow::BottomLeft);
806             else if ((Atom) e->xclient.data.l[2] ==
807                 xatom->getAtom(XAtom::net_wm_moveresize_size_bottomright))
808               win->beginResize(x_root, y_root, BlackboxWindow::BottomRight);
809           }
810         }
811       } else if (e->xclient.message_type ==
812                  xatom->getAtom(XAtom::net_wm_state)) {
813         // NET_WM_STATE
814         BlackboxWindow *win = searchWindow(e->xclient.window);
815         if (win && win->validateClient()) {
816           const Atom action = (Atom) e->xclient.data.l[0];
817           const Atom state[] = { (Atom) e->xclient.data.l[1],
818                                  (Atom) e->xclient.data.l[2] };
819           
820           for (int i = 0; i < 2; ++i) {
821             if (! state[i])
822               continue;
823
824             if ((Atom) e->xclient.data.l[0] == 1) {
825               // ADD
826               if (state[i] == xatom->getAtom(XAtom::net_wm_state_modal)) {
827                 win->setModal(True);
828               } else if (state[i] ==
829                          xatom->getAtom(XAtom::net_wm_state_maximized_vert)) {
830                 if (win->isMaximizedHoriz()) {
831                   win->maximize(0); // unmaximize
832                   win->maximize(1); // full
833                 } else if (! win->isMaximized()) {
834                   win->maximize(2); // vert
835                 }
836               } else if (state[i] ==
837                          xatom->getAtom(XAtom::net_wm_state_maximized_horz)) {
838                 if (win->isMaximizedVert()) {
839                   win->maximize(0); // unmaximize
840                   win->maximize(1); // full
841                 } else if (! win->isMaximized()) {
842                   win->maximize(3); // horiz
843                 }
844               } else if (state[i] ==
845                          xatom->getAtom(XAtom::net_wm_state_shaded)) {
846                 if (! win->isShaded())
847                   win->shade();
848               } else if (state[i] ==
849                          xatom->getAtom(XAtom::net_wm_state_skip_taskbar)) {
850                 win->setSkipTaskbar(True);
851               } else if (state[i] ==
852                          xatom->getAtom(XAtom::net_wm_state_skip_pager)) {
853                 win->setSkipPager(True);
854               } else if (state[i] ==
855                          xatom->getAtom(XAtom::net_wm_state_fullscreen)) {
856                 win->setFullscreen(True);
857               }
858             } else if (action == 0) {
859               // REMOVE
860               if (state[i] == xatom->getAtom(XAtom::net_wm_state_modal)) {
861                 win->setModal(False);
862               } else if (state[i] ==
863                          xatom->getAtom(XAtom::net_wm_state_maximized_vert)) {
864                 if (win->isMaximizedFull()) {
865                   win->maximize(0); // unmaximize
866                   win->maximize(3); // horiz
867                 } else if (win->isMaximizedVert()) {
868                   win->maximize(0); // unmaximize
869                 }
870               } else if (state[i] ==
871                          xatom->getAtom(XAtom::net_wm_state_maximized_horz)) {
872                 if (win->isMaximizedFull()) {
873                   win->maximize(0); // unmaximize
874                   win->maximize(2); // vert
875                 } else if (win->isMaximizedHoriz()) {
876                   win->maximize(0); // unmaximize
877                 }
878               } else if (state[i] ==
879                          xatom->getAtom(XAtom::net_wm_state_shaded)) {
880                 if (win->isShaded())
881                   win->shade();
882               } else if (state[i] ==
883                          xatom->getAtom(XAtom::net_wm_state_skip_taskbar)) {
884                 win->setSkipTaskbar(False);
885               } else if (state[i] ==
886                          xatom->getAtom(XAtom::net_wm_state_skip_pager)) {
887                 win->setSkipPager(False);
888               } else if (state[i] ==
889                          xatom->getAtom(XAtom::net_wm_state_fullscreen)) {
890                 win->setFullscreen(False);
891               }
892             } else if (action == 2) {
893               // TOGGLE
894               if (state[i] == xatom->getAtom(XAtom::net_wm_state_modal)) {
895                 win->setModal(! win->isModal());
896               } else if (state[i] ==
897                          xatom->getAtom(XAtom::net_wm_state_maximized_vert)) {
898                 if (win->isMaximizedFull()) {
899                   win->maximize(0); // unmaximize
900                   win->maximize(3); // horiz
901                 } else if (win->isMaximizedVert()) {
902                   win->maximize(0); // unmaximize
903                 } else if (win->isMaximizedHoriz()) {
904                   win->maximize(0); // unmaximize
905                   win->maximize(1); // full
906                 } else {
907                   win->maximize(2); // vert
908                 }
909               } else if (state[i] ==
910                          xatom->getAtom(XAtom::net_wm_state_maximized_horz)) {
911                 if (win->isMaximizedFull()) {
912                   win->maximize(0); // unmaximize
913                   win->maximize(2); // vert
914                 } else if (win->isMaximizedHoriz()) {
915                   win->maximize(0); // unmaximize
916                 } else if (win->isMaximizedVert()) {
917                   win->maximize(0); // unmaximize
918                   win->maximize(1); // full
919                 } else {
920                   win->maximize(3); // horiz
921                 }
922               } else if (state[i] ==
923                          xatom->getAtom(XAtom::net_wm_state_shaded)) {
924                 win->shade();
925               } else if (state[i] ==
926                          xatom->getAtom(XAtom::net_wm_state_skip_taskbar)) {
927                 win->setSkipTaskbar(! win->skipTaskbar());
928               } else if (state[i] ==
929                          xatom->getAtom(XAtom::net_wm_state_skip_pager)) {
930                 win->setSkipPager(! win->skipPager());
931               } else if (state[i] ==
932                          xatom->getAtom(XAtom::net_wm_state_fullscreen)) {
933                 win->setFullscreen(! win->isFullscreen());
934               }
935             }
936           }
937         }
938       }
939     }
940
941     break;
942   }
943
944   case NoExpose:
945   case ConfigureNotify:
946   case MapNotify:
947     break; // not handled, just ignore
948
949   default: {
950 #ifdef    SHAPE
951     if (e->type == getShapeEventBase()) {
952       XShapeEvent *shape_event = (XShapeEvent *) e;
953       BlackboxWindow *win = searchWindow(e->xany.window);
954
955       if (win)
956         win->shapeEvent(shape_event);
957     }
958 #endif // SHAPE
959   }
960   } // switch
961 }
962
963
964 bool Blackbox::handleSignal(int sig) {
965   switch (sig) {
966   case SIGHUP:
967     reconfigure();
968     break;
969
970   case SIGUSR1:
971     restart();
972     break;
973
974   case SIGUSR2:
975     rereadMenu();
976     break;
977
978   case SIGPIPE:
979   case SIGSEGV:
980   case SIGFPE:
981   case SIGINT:
982   case SIGTERM:
983     shutdown();
984
985   default:
986     return False;
987   }
988
989   return True;
990 }
991
992
993 bool Blackbox::validateWindow(Window window) {
994   XEvent event;
995   if (XCheckTypedWindowEvent(getXDisplay(), window, DestroyNotify, &event)) {
996     XPutBackEvent(getXDisplay(), &event);
997
998     return False;
999   }
1000
1001   return True;
1002 }
1003
1004
1005 BScreen *Blackbox::searchScreen(Window window) {
1006   ScreenList::iterator it = screenList.begin();
1007
1008   for (; it != screenList.end(); ++it) {
1009     BScreen *s = *it;
1010     if (s->getRootWindow() == window)
1011       return s;
1012   }
1013
1014   return (BScreen *) 0;
1015 }
1016
1017
1018 BScreen *Blackbox::searchSystrayWindow(Window window) {
1019   WindowScreenLookup::iterator it = systraySearchList.find(window);
1020   if (it != systraySearchList.end())
1021     return it->second;
1022
1023   return (BScreen*) 0;
1024 }
1025
1026
1027 BlackboxWindow *Blackbox::searchWindow(Window window) {
1028   WindowLookup::iterator it = windowSearchList.find(window);
1029   if (it != windowSearchList.end())
1030     return it->second;
1031
1032   return (BlackboxWindow*) 0;
1033 }
1034
1035
1036 BWindowGroup *Blackbox::searchGroup(Window window) {
1037   GroupLookup::iterator it = groupSearchList.find(window);
1038   if (it != groupSearchList.end())
1039     return it->second;
1040
1041   return (BWindowGroup *) 0;
1042 }
1043
1044
1045 Basemenu *Blackbox::searchMenu(Window window) {
1046   MenuLookup::iterator it = menuSearchList.find(window);
1047   if (it != menuSearchList.end())
1048     return it->second;
1049
1050   return (Basemenu*) 0;
1051 }
1052
1053
1054 Toolbar *Blackbox::searchToolbar(Window window) {
1055   ToolbarLookup::iterator it = toolbarSearchList.find(window);
1056   if (it != toolbarSearchList.end())
1057     return it->second;
1058
1059   return (Toolbar*) 0;
1060 }
1061
1062
1063 Slit *Blackbox::searchSlit(Window window) {
1064   SlitLookup::iterator it = slitSearchList.find(window);
1065   if (it != slitSearchList.end())
1066     return it->second;
1067
1068   return (Slit*) 0;
1069 }
1070
1071
1072 void Blackbox::saveSystrayWindowSearch(Window window, BScreen *screen) {
1073   systraySearchList.insert(WindowScreenLookupPair(window, screen));
1074 }
1075
1076
1077 void Blackbox::saveWindowSearch(Window window, BlackboxWindow *data) {
1078   windowSearchList.insert(WindowLookupPair(window, data));
1079 }
1080
1081
1082 void Blackbox::saveGroupSearch(Window window, BWindowGroup *data) {
1083   groupSearchList.insert(GroupLookupPair(window, data));
1084 }
1085
1086
1087 void Blackbox::saveMenuSearch(Window window, Basemenu *data) {
1088   menuSearchList.insert(MenuLookupPair(window, data));
1089 }
1090
1091
1092 void Blackbox::saveToolbarSearch(Window window, Toolbar *data) {
1093   toolbarSearchList.insert(ToolbarLookupPair(window, data));
1094 }
1095
1096
1097 void Blackbox::saveSlitSearch(Window window, Slit *data) {
1098   slitSearchList.insert(SlitLookupPair(window, data));
1099 }
1100
1101
1102 void Blackbox::removeSystrayWindowSearch(Window window) {
1103   systraySearchList.erase(window);
1104 }
1105
1106
1107 void Blackbox::removeWindowSearch(Window window) {
1108   windowSearchList.erase(window);
1109 }
1110
1111
1112 void Blackbox::removeGroupSearch(Window window) {
1113   groupSearchList.erase(window);
1114 }
1115
1116
1117 void Blackbox::removeMenuSearch(Window window) {
1118   menuSearchList.erase(window);
1119 }
1120
1121
1122 void Blackbox::removeToolbarSearch(Window window) {
1123   toolbarSearchList.erase(window);
1124 }
1125
1126
1127 void Blackbox::removeSlitSearch(Window window) {
1128   slitSearchList.erase(window);
1129 }
1130
1131
1132 void Blackbox::restart(const char *prog) {
1133   shutdown();
1134
1135   if (prog) {
1136     putenv(const_cast<char *>(screenList.front()->displayString().c_str()));
1137     execlp(prog, prog, NULL);
1138     perror(prog);
1139   }
1140
1141   // fall back in case the above execlp doesn't work
1142   execvp(argv[0], argv);
1143   string name = basename(argv[0]);
1144   execvp(name.c_str(), argv);
1145 }
1146
1147
1148 void Blackbox::shutdown(void) {
1149   BaseDisplay::shutdown();
1150
1151   XSetInputFocus(getXDisplay(), PointerRoot, None, CurrentTime);
1152
1153   std::for_each(screenList.begin(), screenList.end(),
1154                 std::mem_fun(&BScreen::shutdown));
1155
1156   XSync(getXDisplay(), False);
1157 }
1158
1159
1160 #ifdef    XINERAMA
1161 void Blackbox::saveXineramaPlacement(bool x) {
1162   resource.xinerama_placement = x;
1163   config.setValue("session.xineramaSupport.windowPlacement",
1164                   resource.xinerama_placement);
1165   reconfigure();  // make sure all screens get this change
1166 }
1167
1168
1169 void Blackbox::saveXineramaMaximizing(bool x) {
1170   resource.xinerama_maximize = x;
1171   config.setValue("session.xineramaSupport.windowMaximizing",
1172                   resource.xinerama_maximize);
1173   reconfigure();  // make sure all screens get this change
1174 }
1175
1176
1177 void Blackbox::saveXineramaSnapping(bool x) {
1178   resource.xinerama_snap = x;
1179   config.setValue("session.xineramaSupport.windowSnapping",
1180                   resource.xinerama_snap);
1181   reconfigure();  // make sure all screens get this change
1182 }
1183 #endif // XINERAMA
1184
1185   
1186 /*
1187  * Save all values as they are so that the defaults will be written to the rc
1188  * file
1189  */
1190 void Blackbox::save_rc(void) {
1191   config.setAutoSave(false);
1192
1193   config.setValue("session.colorsPerChannel", resource.colors_per_channel);
1194   config.setValue("session.doubleClickInterval",
1195                   resource.double_click_interval);
1196   config.setValue("session.autoRaiseDelay",
1197                   ((resource.auto_raise_delay.tv_sec * 1000) +
1198                    (resource.auto_raise_delay.tv_usec / 1000)));
1199   config.setValue("session.cacheLife", resource.cache_life / 60000);
1200   config.setValue("session.cacheMax", resource.cache_max);
1201   config.setValue("session.styleFile", resource.style_file);
1202   config.setValue("session.titlebarLayout", resource.titlebar_layout);
1203   
1204 #ifdef    XINERAMA
1205   saveXineramaPlacement(resource.xinerama_placement);
1206   saveXineramaMaximizing(resource.xinerama_maximize);
1207   saveXineramaSnapping(resource.xinerama_snap);
1208 #endif // XINERAMA
1209
1210   std::for_each(screenList.begin(), screenList.end(),
1211                 std::mem_fun(&BScreen::save_rc));
1212  
1213   config.setAutoSave(true);
1214   config.save();
1215 }
1216
1217
1218 void Blackbox::load_rc(void) {
1219   if (! config.load())
1220     config.create();
1221   
1222   string s;
1223
1224   if (! config.getValue("session.colorsPerChannel",
1225                         resource.colors_per_channel))
1226     resource.colors_per_channel = 4;
1227   if (resource.colors_per_channel < 2) resource.colors_per_channel = 2;
1228   else if (resource.colors_per_channel > 6) resource.colors_per_channel = 6;
1229
1230   if (config.getValue("session.styleFile", s))
1231     resource.style_file = expandTilde(s);
1232   else
1233     resource.style_file = DEFAULTSTYLE;
1234
1235   if (! config.getValue("session.doubleClickInterval",
1236                        resource.double_click_interval));
1237     resource.double_click_interval = 250;
1238
1239   if (! config.getValue("session.autoRaiseDelay",
1240                        resource.auto_raise_delay.tv_usec))
1241     resource.auto_raise_delay.tv_usec = 400;
1242   resource.auto_raise_delay.tv_sec = resource.auto_raise_delay.tv_usec / 1000;
1243   resource.auto_raise_delay.tv_usec -=
1244     (resource.auto_raise_delay.tv_sec * 1000);
1245   resource.auto_raise_delay.tv_usec *= 1000;
1246
1247   if (! config.getValue("session.cacheLife", resource.cache_life))
1248     resource.cache_life = 5;
1249   resource.cache_life *= 60000;
1250
1251   if (! config.getValue("session.cacheMax", resource.cache_max))
1252     resource.cache_max = 200;
1253   
1254   if (! config.getValue("session.titlebarLayout", resource.titlebar_layout))
1255     resource.titlebar_layout = "ILMC";
1256
1257 #ifdef    XINERAMA
1258   if (! config.getValue("session.xineramaSupport.windowPlacement",
1259                         resource.xinerama_placement))
1260     resource.xinerama_placement = false;
1261
1262   if (! config.getValue("session.xineramaSupport.windowMaximizing",
1263                         resource.xinerama_maximize))
1264     resource.xinerama_maximize = false;
1265
1266   if (! config.getValue("session.xineramaSupport.windowSnapping",
1267                         resource.xinerama_snap))
1268     resource.xinerama_snap = false;
1269 #endif // XINERAMA
1270 }
1271
1272
1273 void Blackbox::reconfigure(void) {
1274   // don't reconfigure while saving the initial rc file, it's a waste and it
1275   // breaks somethings (workspace names)
1276   if (isStartup()) return;
1277
1278   reconfigure_wait = True;
1279
1280   if (! timer->isTiming()) timer->start();
1281 }
1282
1283
1284 void Blackbox::real_reconfigure(void) {
1285   load_rc();
1286   
1287   std::for_each(menuTimestamps.begin(), menuTimestamps.end(),
1288                 PointerAssassin());
1289   menuTimestamps.clear();
1290
1291   gcCache()->purge();
1292
1293   std::for_each(screenList.begin(), screenList.end(),
1294                 std::mem_fun(&BScreen::reconfigure));
1295 }
1296
1297
1298 void Blackbox::checkMenu(void) {
1299   bool reread = False;
1300   MenuTimestampList::iterator it = menuTimestamps.begin();
1301   for(; it != menuTimestamps.end(); ++it) {
1302     MenuTimestamp *tmp = *it;
1303     struct stat buf;
1304
1305     if (! stat(tmp->filename.c_str(), &buf)) {
1306       if (tmp->timestamp != buf.st_ctime)
1307         reread = True;
1308     } else {
1309       reread = True;
1310     }
1311   }
1312
1313   if (reread) rereadMenu();
1314 }
1315
1316
1317 void Blackbox::rereadMenu(void) {
1318   reread_menu_wait = True;
1319
1320   if (! timer->isTiming()) timer->start();
1321 }
1322
1323
1324 void Blackbox::real_rereadMenu(void) {
1325   std::for_each(menuTimestamps.begin(), menuTimestamps.end(),
1326                 PointerAssassin());
1327   menuTimestamps.clear();
1328
1329   std::for_each(screenList.begin(), screenList.end(),
1330                 std::mem_fun(&BScreen::rereadMenu));
1331 }
1332
1333
1334 void Blackbox::saveStyleFilename(const string& filename) {
1335   assert(! filename.empty());
1336   resource.style_file = filename;
1337   config.setValue("session.styleFile", resource.style_file);
1338 }
1339
1340
1341 void Blackbox::addMenuTimestamp(const string& filename) {
1342   assert(! filename.empty());
1343   bool found = False;
1344
1345   MenuTimestampList::iterator it = menuTimestamps.begin();
1346   for (; it != menuTimestamps.end() && ! found; ++it) {
1347     if ((*it)->filename == filename) found = True;
1348   }
1349   if (! found) {
1350     struct stat buf;
1351
1352     if (! stat(filename.c_str(), &buf)) {
1353       MenuTimestamp *ts = new MenuTimestamp;
1354
1355       ts->filename = filename;
1356       ts->timestamp = buf.st_ctime;
1357
1358       menuTimestamps.push_back(ts);
1359     }
1360   }
1361 }
1362
1363
1364 void Blackbox::timeout(void) {
1365   if (reconfigure_wait)
1366     real_reconfigure();
1367
1368   if (reread_menu_wait)
1369     real_rereadMenu();
1370
1371   reconfigure_wait = reread_menu_wait = False;
1372 }
1373
1374
1375 void Blackbox::setChangingWindow(BlackboxWindow *win) {
1376   // make sure one of the two is null and the other isn't
1377   assert((! changing_window && win) || (! win && changing_window));
1378   changing_window = win;
1379 }
1380
1381
1382 void Blackbox::setFocusedWindow(BlackboxWindow *win) {
1383   if (focused_window && focused_window == win) // nothing to do
1384     return;
1385
1386   BScreen *old_screen = 0;
1387
1388   if (focused_window) {
1389     focused_window->setFocusFlag(False);
1390     old_screen = focused_window->getScreen();
1391   }
1392
1393   if (win && ! win->isIconic()) {
1394     // the active screen is the one with the last focused window...
1395     // this will keep focus on this screen no matter where the mouse goes,
1396     // so multihead keybindings will continue to work on that screen until the
1397     // user focuses a window on a different screen.
1398     active_screen = win->getScreen();
1399     focused_window = win;
1400   } else {
1401     focused_window = 0;
1402     if (! old_screen) {
1403       if (active_screen) {
1404         // set input focus to the toolbar of the screen with mouse
1405         XSetInputFocus(getXDisplay(),
1406                        active_screen->getRootWindow(),
1407                        RevertToPointerRoot, CurrentTime);
1408       } else {
1409         // set input focus to the toolbar of the first managed screen
1410         XSetInputFocus(getXDisplay(),
1411                        screenList.front()->getRootWindow(),
1412                        RevertToPointerRoot, CurrentTime);
1413       }
1414     } else {
1415       // set input focus to the toolbar of the last screen
1416       XSetInputFocus(getXDisplay(), old_screen->getRootWindow(),
1417                      RevertToPointerRoot, CurrentTime);
1418     }
1419   }
1420
1421   if (active_screen && active_screen->isScreenManaged()) {
1422     active_screen->getToolbar()->redrawWindowLabel(True);
1423     active_screen->updateNetizenWindowFocus();
1424   }
1425
1426   if (old_screen && old_screen != active_screen) {
1427     old_screen->getToolbar()->redrawWindowLabel(True);
1428     old_screen->updateNetizenWindowFocus();
1429   }
1430 }