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