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