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