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