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