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