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