merge in netwm branch at tag netwm-merge2
[mikachu/openbox.git] / src / blackbox.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2 // blackbox.cc for Blackbox - an X11 Window manager
3 // Copyright (c) 2001 - 2002 Sean 'Shaleh' Perry <shaleh@debian.org>
4 // Copyright (c) 1997 - 2000 Brad Hughes (bhughes@tcac.net)
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a
7 // copy of this software and associated documentation files (the "Software"),
8 // to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 // and/or sell copies of the Software, and to permit persons to whom the
11 // Software is furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 // DEALINGS IN THE SOFTWARE.
23
24 #ifdef    HAVE_CONFIG_H
25 #  include "../config.h"
26 #endif // HAVE_CONFIG_H
27
28 extern "C" {
29 #include <X11/Xlib.h>
30 #include <X11/Xutil.h>
31 #include <X11/Xatom.h>
32 #include <X11/cursorfont.h>
33 #include <X11/keysym.h>
34
35 #ifdef    SHAPE
36 #include <X11/extensions/shape.h>
37 #endif // SHAPE
38
39 #ifdef    HAVE_STDIO_H
40 #  include <stdio.h>
41 #endif // HAVE_STDIO_H
42
43 #ifdef HAVE_STDLIB_H
44 #  include <stdlib.h>
45 #endif // HAVE_STDLIB_H
46
47 #ifdef HAVE_STRING_H
48 #  include <string.h>
49 #endif // HAVE_STRING_H
50
51 #ifdef    HAVE_UNISTD_H
52 #  include <sys/types.h>
53 #  include <unistd.h>
54 #endif // HAVE_UNISTD_H
55
56 #ifdef    HAVE_SYS_PARAM_H
57 #  include <sys/param.h>
58 #endif // HAVE_SYS_PARAM_H
59
60 #ifdef    HAVE_SYS_SELECT_H
61 #  include <sys/select.h>
62 #endif // HAVE_SYS_SELECT_H
63
64 #ifdef    HAVE_SIGNAL_H
65 #  include <signal.h>
66 #endif // HAVE_SIGNAL_H
67
68 #ifdef    HAVE_SYS_SIGNAL_H
69 #  include <sys/signal.h>
70 #endif // HAVE_SYS_SIGNAL_H
71
72 #ifdef    HAVE_SYS_STAT_H
73 #  include <sys/types.h>
74 #  include <sys/stat.h>
75 #endif // HAVE_SYS_STAT_H
76
77 #ifdef    TIME_WITH_SYS_TIME
78 #  include <sys/time.h>
79 #  include <time.h>
80 #else // !TIME_WITH_SYS_TIME
81 #  ifdef    HAVE_SYS_TIME_H
82 #    include <sys/time.h>
83 #  else // !HAVE_SYS_TIME_H
84 #    include <time.h>
85 #  endif // HAVE_SYS_TIME_H
86 #endif // TIME_WITH_SYS_TIME
87
88 #ifdef    HAVE_LIBGEN_H
89 #  include <libgen.h>
90 #endif // HAVE_LIBGEN_H
91 }
92
93 #include <algorithm>
94 #include <string>
95 using std::string;
96
97 #include "i18n.hh"
98 #include "blackbox.hh"
99 #include "Basemenu.hh"
100 #include "Clientmenu.hh"
101 #include "GCCache.hh"
102 #include "Image.hh"
103 #include "Rootmenu.hh"
104 #include "Screen.hh"
105 #include "Slit.hh"
106 #include "Toolbar.hh"
107 #include "Util.hh"
108 #include "Window.hh"
109 #include "Workspace.hh"
110 #include "Workspacemenu.hh"
111 #include "XAtom.hh"
112
113 // X event scanner for enter/leave notifies - adapted from twm
114 struct scanargs {
115   Window w;
116   bool leave, inferior, enter;
117 };
118
119 static Bool queueScanner(Display *, XEvent *e, char *args) {
120   scanargs *scan = (scanargs *) args;
121   if ((e->type == LeaveNotify) &&
122       (e->xcrossing.window == scan->w) &&
123       (e->xcrossing.mode == NotifyNormal)) {
124     scan->leave = True;
125     scan->inferior = (e->xcrossing.detail == NotifyInferior);
126   } else if ((e->type == EnterNotify) && (e->xcrossing.mode == NotifyUngrab)) {
127     scan->enter = True;
128   }
129
130   return False;
131 }
132
133 Blackbox *blackbox;
134
135
136 Blackbox::Blackbox(char **m_argv, char *dpy_name, char *rc, char *menu)
137   : BaseDisplay(m_argv[0], dpy_name) {
138   if (! XSupportsLocale())
139     fprintf(stderr, "X server does not support locale\n");
140
141   if (XSetLocaleModifiers("") == NULL)
142     fprintf(stderr, "cannot set locale modifiers\n");
143
144   ::blackbox = this;
145   argv = m_argv;
146   if (! rc) rc = "~/.openbox/rc";
147   rc_file = expandTilde(rc);
148   config.setFile(rc_file);  
149   if (! menu) menu = "~/.openbox/menu";
150   menu_file = expandTilde(menu);
151
152   no_focus = False;
153
154   resource.auto_raise_delay.tv_sec = resource.auto_raise_delay.tv_usec = 0;
155
156   active_screen = 0;
157   focused_window = (BlackboxWindow *) 0;
158
159   XrmInitialize();
160   load_rc();
161
162   xatom = new XAtom(this);
163
164   cursor.session = XCreateFontCursor(getXDisplay(), XC_left_ptr);
165   cursor.move = XCreateFontCursor(getXDisplay(), XC_fleur);
166   cursor.ll_angle = XCreateFontCursor(getXDisplay(), XC_ll_angle);
167   cursor.lr_angle = XCreateFontCursor(getXDisplay(), XC_lr_angle);
168
169   for (unsigned int i = 0; i < getNumberOfScreens(); i++) {
170     BScreen *screen = new BScreen(this, i);
171
172     if (! screen->isScreenManaged()) {
173       delete screen;
174       continue;
175     }
176
177     screenList.push_back(screen);
178   }
179
180   if (screenList.empty()) {
181     fprintf(stderr,
182             i18n(blackboxSet, blackboxNoManagableScreens,
183               "Blackbox::Blackbox: no managable screens found, aborting.\n"));
184     ::exit(3);
185   }
186
187   // save current settings and default values
188   save_rc();
189
190   // set the screen with mouse to the first managed screen
191   active_screen = screenList.front();
192   setFocusedWindow(0);
193
194   XSynchronize(getXDisplay(), False);
195   XSync(getXDisplay(), False);
196
197   reconfigure_wait = reread_menu_wait = False;
198
199   timer = new BTimer(this, this);
200   timer->setTimeout(0l);
201 }
202
203
204 Blackbox::~Blackbox(void) {
205   std::for_each(screenList.begin(), screenList.end(), PointerAssassin());
206
207   std::for_each(menuTimestamps.begin(), menuTimestamps.end(),
208                 PointerAssassin());
209
210   delete xatom;
211
212   delete timer;
213 }
214
215
216 void Blackbox::process_event(XEvent *e) {
217   switch (e->type) {
218   case ButtonPress: {
219     // strip the lock key modifiers
220     e->xbutton.state &= ~(NumLockMask | ScrollLockMask | LockMask);
221
222     last_time = e->xbutton.time;
223
224     BlackboxWindow *win = (BlackboxWindow *) 0;
225     Basemenu *menu = (Basemenu *) 0;
226     Slit *slit = (Slit *) 0;
227     Toolbar *tbar = (Toolbar *) 0;
228     BScreen *scrn = (BScreen *) 0;
229
230     if ((win = searchWindow(e->xbutton.window))) {
231       win->buttonPressEvent(&e->xbutton);
232
233       /* XXX: is this sane on low colour desktops? */
234       if (e->xbutton.button == 1)
235         win->installColormap(True);
236     } else if ((menu = searchMenu(e->xbutton.window))) {
237       menu->buttonPressEvent(&e->xbutton);
238     } else if ((slit = searchSlit(e->xbutton.window))) {
239       slit->buttonPressEvent(&e->xbutton);
240     } else if ((tbar = searchToolbar(e->xbutton.window))) {
241       tbar->buttonPressEvent(&e->xbutton);
242     } else if ((scrn = searchScreen(e->xbutton.window))) {
243       scrn->buttonPressEvent(&e->xbutton);
244       if (active_screen != scrn) {
245         active_screen = scrn;
246         // first, set no focus window on the old screen
247         setFocusedWindow(0);
248         // and move focus to this screen
249         setFocusedWindow(0);
250       }
251     }
252     break;
253   }
254
255   case ButtonRelease: {
256     // strip the lock key modifiers
257     e->xbutton.state &= ~(NumLockMask | ScrollLockMask | LockMask);
258
259     last_time = e->xbutton.time;
260
261     BlackboxWindow *win = (BlackboxWindow *) 0;
262     Basemenu *menu = (Basemenu *) 0;
263     Toolbar *tbar = (Toolbar *) 0;
264
265     if ((win = searchWindow(e->xbutton.window)))
266       win->buttonReleaseEvent(&e->xbutton);
267     else if ((menu = searchMenu(e->xbutton.window)))
268       menu->buttonReleaseEvent(&e->xbutton);
269     else if ((tbar = searchToolbar(e->xbutton.window)))
270       tbar->buttonReleaseEvent(&e->xbutton);
271
272     break;
273   }
274
275   case ConfigureRequest: {
276     // compress configure requests...
277     XEvent realevent;
278     unsigned int i = 0;
279     while(XCheckTypedWindowEvent(getXDisplay(), e->xconfigurerequest.window,
280                                  ConfigureRequest, &realevent)) {
281       i++;
282     }
283     if ( i > 0 )
284       e = &realevent;
285
286     BlackboxWindow *win = (BlackboxWindow *) 0;
287     Slit *slit = (Slit *) 0;
288
289     if ((win = searchWindow(e->xconfigurerequest.window))) {
290       win->configureRequestEvent(&e->xconfigurerequest);
291     } else if ((slit = searchSlit(e->xconfigurerequest.window))) {
292       slit->configureRequestEvent(&e->xconfigurerequest);
293     } else {
294       if (validateWindow(e->xconfigurerequest.window)) {
295         XWindowChanges xwc;
296
297         xwc.x = e->xconfigurerequest.x;
298         xwc.y = e->xconfigurerequest.y;
299         xwc.width = e->xconfigurerequest.width;
300         xwc.height = e->xconfigurerequest.height;
301         xwc.border_width = e->xconfigurerequest.border_width;
302         xwc.sibling = e->xconfigurerequest.above;
303         xwc.stack_mode = e->xconfigurerequest.detail;
304
305         XConfigureWindow(getXDisplay(), e->xconfigurerequest.window,
306                          e->xconfigurerequest.value_mask, &xwc);
307       }
308     }
309
310     break;
311   }
312
313   case MapRequest: {
314 #ifdef    DEBUG
315     fprintf(stderr, "Blackbox::process_event(): MapRequest for 0x%lx\n",
316             e->xmaprequest.window);
317 #endif // DEBUG
318
319     BlackboxWindow *win = searchWindow(e->xmaprequest.window);
320
321     if (! win) {
322       BScreen *screen = searchScreen(e->xmaprequest.parent);
323
324       if (! screen) {
325         /*
326           we got a map request for a window who's parent isn't root. this
327           can happen in only one circumstance:
328
329             a client window unmapped a managed window, and then remapped it
330             somewhere between unmapping the client window and reparenting it
331             to root.
332
333           regardless of how it happens, we need to find the screen that
334           the window is on
335         */
336         XWindowAttributes wattrib;
337         if (! XGetWindowAttributes(getXDisplay(), e->xmaprequest.window,
338                                    &wattrib)) {
339           // failed to get the window attributes, perhaps the window has
340           // now been destroyed?
341           break;
342         }
343
344         screen = searchScreen(wattrib.root);
345         assert(screen != 0); // this should never happen
346       }
347
348       screen->manageWindow(e->xmaprequest.window);
349     }
350
351     break;
352   }
353
354   case UnmapNotify: {
355     BlackboxWindow *win = (BlackboxWindow *) 0;
356     Slit *slit = (Slit *) 0;
357
358     if ((win = searchWindow(e->xunmap.window))) {
359       win->unmapNotifyEvent(&e->xunmap);
360     } else if ((slit = searchSlit(e->xunmap.window))) {
361       slit->unmapNotifyEvent(&e->xunmap);
362     }
363
364     break;
365   }
366
367   case DestroyNotify: {
368     BlackboxWindow *win = (BlackboxWindow *) 0;
369     Slit *slit = (Slit *) 0;
370     BWindowGroup *group = (BWindowGroup *) 0;
371
372     if ((win = searchWindow(e->xdestroywindow.window))) {
373       win->destroyNotifyEvent(&e->xdestroywindow);
374     } else if ((slit = searchSlit(e->xdestroywindow.window))) {
375       slit->removeClient(e->xdestroywindow.window, False);
376     } else if ((group = searchGroup(e->xdestroywindow.window))) {
377       delete group;
378     }
379
380     break;
381   }
382
383   case ReparentNotify: {
384     /*
385       this event is quite rare and is usually handled in unmapNotify
386       however, if the window is unmapped when the reparent event occurs
387       the window manager never sees it because an unmap event is not sent
388       to an already unmapped window.
389     */
390     BlackboxWindow *win = searchWindow(e->xreparent.window);
391     if (win) {
392       win->reparentNotifyEvent(&e->xreparent);
393     } else {
394       Slit *slit = searchSlit(e->xreparent.window);
395       if (slit && slit->getWindowID() != e->xreparent.parent)
396         slit->removeClient(e->xreparent.window, True);
397     }
398     break;
399   }
400
401   case MotionNotify: {
402     // motion notify compression...
403     XEvent realevent;
404     unsigned int i = 0;
405     while (XCheckTypedWindowEvent(getXDisplay(), e->xmotion.window,
406                                   MotionNotify, &realevent)) {
407       i++;
408     }
409
410     // if we have compressed some motion events, use the last one
411     if ( i > 0 )
412       e = &realevent;
413
414     // strip the lock key modifiers
415     e->xbutton.state &= ~(NumLockMask | ScrollLockMask | LockMask);
416
417     last_time = e->xmotion.time;
418
419     BlackboxWindow *win = (BlackboxWindow *) 0;
420     Basemenu *menu = (Basemenu *) 0;
421
422     if ((win = searchWindow(e->xmotion.window)))
423       win->motionNotifyEvent(&e->xmotion);
424     else if ((menu = searchMenu(e->xmotion.window)))
425       menu->motionNotifyEvent(&e->xmotion);
426
427     break;
428   }
429
430   case PropertyNotify: {
431     last_time = e->xproperty.time;
432
433     if (e->xproperty.state != PropertyDelete) {
434       BlackboxWindow *win = searchWindow(e->xproperty.window);
435
436       if (win)
437         win->propertyNotifyEvent(e->xproperty.atom);
438     }
439
440     break;
441   }
442
443   case EnterNotify: {
444     last_time = e->xcrossing.time;
445
446     BScreen *screen = (BScreen *) 0;
447     BlackboxWindow *win = (BlackboxWindow *) 0;
448     Basemenu *menu = (Basemenu *) 0;
449     Toolbar *tbar = (Toolbar *) 0;
450     Slit *slit = (Slit *) 0;
451
452     if (e->xcrossing.mode == NotifyGrab) break;
453
454     XEvent dummy;
455     scanargs sa;
456     sa.w = e->xcrossing.window;
457     sa.enter = sa.leave = False;
458     XCheckIfEvent(getXDisplay(), &dummy, queueScanner, (char *) &sa);
459
460     if ((e->xcrossing.window == e->xcrossing.root) &&
461         (screen = searchScreen(e->xcrossing.window))) {
462       screen->getImageControl()->installRootColormap();
463     } else if ((win = searchWindow(e->xcrossing.window))) {
464       if (win->getScreen()->isSloppyFocus() &&
465           (! win->isFocused()) && (! no_focus)) {
466         if (((! sa.leave) || sa.inferior) && win->isVisible()) {
467           if (win->setInputFocus())
468             win->installColormap(True); // XXX: shouldnt we honour no install?
469         }
470       }
471     } else if ((menu = searchMenu(e->xcrossing.window))) {
472       menu->enterNotifyEvent(&e->xcrossing);
473     } else if ((tbar = searchToolbar(e->xcrossing.window))) {
474       tbar->enterNotifyEvent(&e->xcrossing);
475     } else if ((slit = searchSlit(e->xcrossing.window))) {
476       slit->enterNotifyEvent(&e->xcrossing);
477     }
478     break;
479   }
480
481   case LeaveNotify: {
482     last_time = e->xcrossing.time;
483
484     BlackboxWindow *win = (BlackboxWindow *) 0;
485     Basemenu *menu = (Basemenu *) 0;
486     Toolbar *tbar = (Toolbar *) 0;
487     Slit *slit = (Slit *) 0;
488
489     if ((menu = searchMenu(e->xcrossing.window)))
490       menu->leaveNotifyEvent(&e->xcrossing);
491     else if ((win = searchWindow(e->xcrossing.window)))
492       win->installColormap(False);
493     else if ((tbar = searchToolbar(e->xcrossing.window)))
494       tbar->leaveNotifyEvent(&e->xcrossing);
495     else if ((slit = searchSlit(e->xcrossing.window)))
496       slit->leaveNotifyEvent(&e->xcrossing);
497     break;
498   }
499
500   case Expose: {
501     // compress expose events
502     XEvent realevent;
503     unsigned int i = 0;
504     int ex1, ey1, ex2, ey2;
505     ex1 = e->xexpose.x;
506     ey1 = e->xexpose.y;
507     ex2 = ex1 + e->xexpose.width - 1;
508     ey2 = ey1 + e->xexpose.height - 1;
509     while (XCheckTypedWindowEvent(getXDisplay(), e->xexpose.window,
510                                   Expose, &realevent)) {
511       i++;
512
513       // merge expose area
514       ex1 = std::min(realevent.xexpose.x, ex1);
515       ey1 = std::min(realevent.xexpose.y, ey1);
516       ex2 = std::max(realevent.xexpose.x + realevent.xexpose.width - 1, ex2);
517       ey2 = std::max(realevent.xexpose.y + realevent.xexpose.height - 1, ey2);
518     }
519     if ( i > 0 )
520       e = &realevent;
521
522     // use the merged area
523     e->xexpose.x = ex1;
524     e->xexpose.y = ey1;
525     e->xexpose.width = ex2 - ex1 + 1;
526     e->xexpose.height = ey2 - ey1 + 1;
527
528     BlackboxWindow *win = (BlackboxWindow *) 0;
529     Basemenu *menu = (Basemenu *) 0;
530     Toolbar *tbar = (Toolbar *) 0;
531
532     if ((win = searchWindow(e->xexpose.window)))
533       win->exposeEvent(&e->xexpose);
534     else if ((menu = searchMenu(e->xexpose.window)))
535       menu->exposeEvent(&e->xexpose);
536     else if ((tbar = searchToolbar(e->xexpose.window)))
537       tbar->exposeEvent(&e->xexpose);
538
539     break;
540   }
541
542   case KeyPress: {
543     Toolbar *tbar = searchToolbar(e->xkey.window);
544
545     if (tbar && tbar->isEditing())
546       tbar->keyPressEvent(&e->xkey);
547
548     break;
549   }
550
551   case ColormapNotify: {
552     BScreen *screen = searchScreen(e->xcolormap.window);
553
554     if (screen)
555       screen->setRootColormapInstalled((e->xcolormap.state ==
556                                         ColormapInstalled) ? True : False);
557
558     break;
559   }
560
561   case FocusIn: {
562     if (e->xfocus.detail != NotifyNonlinear &&
563         e->xfocus.detail != NotifyAncestor) {
564       /*
565         don't process FocusIns when:
566         1. the new focus window isn't an ancestor or inferior of the old
567         focus window (NotifyNonlinear)
568         make sure to allow the FocusIn when the old focus window was an
569         ancestor but didn't have a parent, such as root (NotifyAncestor)
570       */
571       break;
572     }
573
574     BlackboxWindow *win = searchWindow(e->xfocus.window);
575     if (win) {
576       if (! win->isFocused())
577         win->setFocusFlag(True);
578
579       /*
580         set the event window to None.  when the FocusOut event handler calls
581         this function recursively, it uses this as an indication that focus
582         has moved to a known window.
583       */
584       e->xfocus.window = None;
585     }
586
587     break;
588   }
589
590   case FocusOut: {
591     if (e->xfocus.detail != NotifyNonlinear) {
592       /*
593         don't process FocusOuts when:
594         2. the new focus window isn't an ancestor or inferior of the old
595         focus window (NotifyNonlinear)
596       */
597       break;
598     }
599
600     BlackboxWindow *win = searchWindow(e->xfocus.window);
601     if (win && win->isFocused()) {
602       /*
603         before we mark "win" as unfocused, we need to verify that focus is
604         going to a known location, is in a known location, or set focus
605         to a known location.
606       */
607
608       XEvent event;
609       // don't check the current focus if FocusOut was generated during a grab
610       bool check_focus = (e->xfocus.mode == NotifyNormal);
611
612       /*
613         First, check if there is a pending FocusIn event waiting.  if there
614         is, process it and determine if focus has moved to another window
615         (the FocusIn event handler sets the window in the event
616         structure to None to indicate this).
617       */
618       if (XCheckTypedEvent(getXDisplay(), FocusIn, &event)) {
619
620         process_event(&event);
621         if (event.xfocus.window == None) {
622           // focus has moved
623           check_focus = False;
624         }
625       }
626
627       if (check_focus) {
628         /*
629           Second, we query the X server for the current input focus.
630           to make sure that we keep a consistent state.
631         */
632         BlackboxWindow *focus;
633         Window w;
634         int revert;
635         XGetInputFocus(getXDisplay(), &w, &revert);
636         focus = searchWindow(w);
637         if (focus) {
638           /*
639             focus got from "win" to "focus" under some very strange
640             circumstances, and we need to make sure that the focus indication
641             is correct.
642           */
643           setFocusedWindow(focus);
644         } else {
645           // we have no idea where focus went... so we set it to somewhere
646           setFocusedWindow(0);
647         }
648       }
649     }
650
651     break;
652   }
653
654   case ClientMessage: {
655     if (e->xclient.format == 32) {
656       if (e->xclient.message_type == xatom->getAtom(XAtom::wm_change_state)) {
657         BlackboxWindow *win = searchWindow(e->xclient.window);
658         if (! win || ! win->validateClient()) return;
659
660         if (e->xclient.data.l[0] == IconicState)
661           win->iconify();
662         if (e->xclient.data.l[0] == NormalState)
663           win->deiconify();
664       } else if(e->xclient.message_type == getBlackboxChangeWorkspaceAtom()) {
665         BScreen *screen = searchScreen(e->xclient.window);
666
667         if (screen && e->xclient.data.l[0] >= 0 &&
668             e->xclient.data.l[0] <
669             static_cast<signed>(screen->getWorkspaceCount()))
670           screen->changeWorkspaceID(e->xclient.data.l[0]);
671       } else if (e->xclient.message_type == getBlackboxChangeWindowFocusAtom()) {
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 == getBlackboxCycleWindowFocusAtom()) {
677         BScreen *screen = searchScreen(e->xclient.window);
678
679         if (screen) {
680           if (! e->xclient.data.l[0])
681             screen->prevFocus();
682           else
683             screen->nextFocus();
684         }
685       } else if (e->xclient.message_type == getBlackboxChangeAttributesAtom()) {
686         BlackboxWindow *win = searchWindow(e->xclient.window);
687
688         if (win && win->validateClient()) {
689           BlackboxHints net;
690           net.flags = e->xclient.data.l[0];
691           net.attrib = e->xclient.data.l[1];
692           net.workspace = e->xclient.data.l[2];
693           net.stack = e->xclient.data.l[3];
694           net.decoration = e->xclient.data.l[4];
695
696           win->changeBlackboxHints(&net);
697         }
698       }
699     }
700
701     break;
702   }
703
704   case NoExpose:
705   case ConfigureNotify:
706   case MapNotify:
707     break; // not handled, just ignore
708
709   default: {
710 #ifdef    SHAPE
711     if (e->type == getShapeEventBase()) {
712       XShapeEvent *shape_event = (XShapeEvent *) e;
713       BlackboxWindow *win = searchWindow(e->xany.window);
714
715       if (win)
716         win->shapeEvent(shape_event);
717     }
718 #endif // SHAPE
719   }
720   } // switch
721 }
722
723
724 bool Blackbox::handleSignal(int sig) {
725   switch (sig) {
726   case SIGHUP:
727   case SIGUSR1:
728     reconfigure();
729     break;
730
731   case SIGUSR2:
732     rereadMenu();
733     break;
734
735   case SIGPIPE:
736   case SIGSEGV:
737   case SIGFPE:
738   case SIGINT:
739   case SIGTERM:
740     shutdown();
741
742   default:
743     return False;
744   }
745
746   return True;
747 }
748
749
750 bool Blackbox::validateWindow(Window window) {
751   XEvent event;
752   if (XCheckTypedWindowEvent(getXDisplay(), window, DestroyNotify, &event)) {
753     XPutBackEvent(getXDisplay(), &event);
754
755     return False;
756   }
757
758   return True;
759 }
760
761
762 BScreen *Blackbox::searchScreen(Window window) {
763   ScreenList::iterator it = screenList.begin();
764
765   for (; it != screenList.end(); ++it) {
766     BScreen *s = *it;
767     if (s->getRootWindow() == window)
768       return s;
769   }
770
771   return (BScreen *) 0;
772 }
773
774
775 BlackboxWindow *Blackbox::searchWindow(Window window) {
776   WindowLookup::iterator it = windowSearchList.find(window);
777   if (it != windowSearchList.end())
778     return it->second;
779
780   return (BlackboxWindow*) 0;
781 }
782
783
784 BWindowGroup *Blackbox::searchGroup(Window window) {
785   GroupLookup::iterator it = groupSearchList.find(window);
786   if (it != groupSearchList.end())
787     return it->second;
788
789   return (BWindowGroup *) 0;
790 }
791
792
793 Basemenu *Blackbox::searchMenu(Window window) {
794   MenuLookup::iterator it = menuSearchList.find(window);
795   if (it != menuSearchList.end())
796     return it->second;
797
798   return (Basemenu*) 0;
799 }
800
801
802 Toolbar *Blackbox::searchToolbar(Window window) {
803   ToolbarLookup::iterator it = toolbarSearchList.find(window);
804   if (it != toolbarSearchList.end())
805     return it->second;
806
807   return (Toolbar*) 0;
808 }
809
810
811 Slit *Blackbox::searchSlit(Window window) {
812   SlitLookup::iterator it = slitSearchList.find(window);
813   if (it != slitSearchList.end())
814     return it->second;
815
816   return (Slit*) 0;
817 }
818
819
820 void Blackbox::saveWindowSearch(Window window, BlackboxWindow *data) {
821   windowSearchList.insert(WindowLookupPair(window, data));
822 }
823
824
825 void Blackbox::saveGroupSearch(Window window, BWindowGroup *data) {
826   groupSearchList.insert(GroupLookupPair(window, data));
827 }
828
829
830 void Blackbox::saveMenuSearch(Window window, Basemenu *data) {
831   menuSearchList.insert(MenuLookupPair(window, data));
832 }
833
834
835 void Blackbox::saveToolbarSearch(Window window, Toolbar *data) {
836   toolbarSearchList.insert(ToolbarLookupPair(window, data));
837 }
838
839
840 void Blackbox::saveSlitSearch(Window window, Slit *data) {
841   slitSearchList.insert(SlitLookupPair(window, data));
842 }
843
844
845 void Blackbox::removeWindowSearch(Window window) {
846   windowSearchList.erase(window);
847 }
848
849
850 void Blackbox::removeGroupSearch(Window window) {
851   groupSearchList.erase(window);
852 }
853
854
855 void Blackbox::removeMenuSearch(Window window) {
856   menuSearchList.erase(window);
857 }
858
859
860 void Blackbox::removeToolbarSearch(Window window) {
861   toolbarSearchList.erase(window);
862 }
863
864
865 void Blackbox::removeSlitSearch(Window window) {
866   slitSearchList.erase(window);
867 }
868
869
870 void Blackbox::restart(const char *prog) {
871   shutdown();
872
873   if (prog) {
874     execlp(prog, prog, NULL);
875     perror(prog);
876   }
877
878   // fall back in case the above execlp doesn't work
879   execvp(argv[0], argv);
880   string name = basename(argv[0]);
881   execvp(name.c_str(), argv);
882 }
883
884
885 void Blackbox::shutdown(void) {
886   BaseDisplay::shutdown();
887
888   XSetInputFocus(getXDisplay(), PointerRoot, None, CurrentTime);
889
890   std::for_each(screenList.begin(), screenList.end(),
891                 std::mem_fun(&BScreen::shutdown));
892
893   XSync(getXDisplay(), False);
894 }
895
896
897 /*
898  * Save all values as they are so that the defaults will be written to the rc
899  * file
900  */
901 void Blackbox::save_rc(void) {
902   config.setAutoSave(false);
903
904   config.setValue("session.colorsPerChannel", resource.colors_per_channel);
905   config.setValue("session.doubleClickInterval",
906                   resource.double_click_interval);
907   config.setValue("session.autoRaiseDelay",
908                   ((resource.auto_raise_delay.tv_sec * 1000) +
909                    (resource.auto_raise_delay.tv_usec / 1000)));
910   config.setValue("session.cacheLife", resource.cache_life / 60000);
911   config.setValue("session.cacheMax", resource.cache_max);
912   config.setValue("session.styleFile", resource.style_file);
913   config.setValue("session.titlebarLayout", resource.titlebar_layout);
914   
915   std::for_each(screenList.begin(), screenList.end(),
916                 std::mem_fun(&BScreen::save_rc));
917  
918   config.setAutoSave(true);
919   config.save();
920 }
921
922
923 void Blackbox::load_rc(void) {
924   if (! config.load())
925         config.create();
926   
927   string s;
928
929   if (! config.getValue("session.colorsPerChannel",
930                         resource.colors_per_channel))
931     resource.colors_per_channel = 4;
932   if (resource.colors_per_channel < 2) resource.colors_per_channel = 2;
933   else if (resource.colors_per_channel > 6) resource.colors_per_channel = 6;
934
935   if (config.getValue("session.styleFile", s))
936     resource.style_file = expandTilde(s);
937   else
938     resource.style_file = DEFAULTSTYLE;
939
940   if (! config.getValue("session.doubleClickInterval",
941                        resource.double_click_interval));
942     resource.double_click_interval = 250;
943
944   if (! config.getValue("session.autoRaiseDelay",
945                        resource.auto_raise_delay.tv_usec))
946     resource.auto_raise_delay.tv_usec = 400;
947   resource.auto_raise_delay.tv_sec = resource.auto_raise_delay.tv_usec / 1000;
948   resource.auto_raise_delay.tv_usec -=
949     (resource.auto_raise_delay.tv_sec * 1000);
950   resource.auto_raise_delay.tv_usec *= 1000;
951
952   if (! config.getValue("session.cacheLife", resource.cache_life))
953     resource.cache_life = 5;
954   resource.cache_life *= 60000;
955
956   if (! config.getValue("session.cacheMax", resource.cache_max))
957     resource.cache_max = 200;
958   
959   if (! config.getValue("session.titlebarLayout", resource.titlebar_layout))
960     resource.titlebar_layout = "ILMC";
961 }
962
963
964 void Blackbox::reconfigure(void) {
965   reconfigure_wait = True;
966
967   if (! timer->isTiming()) timer->start();
968 }
969
970
971 void Blackbox::real_reconfigure(void) {
972   load_rc();
973   
974   std::for_each(menuTimestamps.begin(), menuTimestamps.end(),
975                 PointerAssassin());
976   menuTimestamps.clear();
977
978   gcCache()->purge();
979
980   std::for_each(screenList.begin(), screenList.end(),
981                 std::mem_fun(&BScreen::reconfigure));
982 }
983
984
985 void Blackbox::checkMenu(void) {
986   bool reread = False;
987   MenuTimestampList::iterator it = menuTimestamps.begin();
988   for(; it != menuTimestamps.end(); ++it) {
989     MenuTimestamp *tmp = *it;
990     struct stat buf;
991
992     if (! stat(tmp->filename.c_str(), &buf)) {
993       if (tmp->timestamp != buf.st_ctime)
994         reread = True;
995     } else {
996       reread = True;
997     }
998   }
999
1000   if (reread) rereadMenu();
1001 }
1002
1003
1004 void Blackbox::rereadMenu(void) {
1005   reread_menu_wait = True;
1006
1007   if (! timer->isTiming()) timer->start();
1008 }
1009
1010
1011 void Blackbox::real_rereadMenu(void) {
1012   std::for_each(menuTimestamps.begin(), menuTimestamps.end(),
1013                 PointerAssassin());
1014   menuTimestamps.clear();
1015
1016   std::for_each(screenList.begin(), screenList.end(),
1017                 std::mem_fun(&BScreen::rereadMenu));
1018 }
1019
1020
1021 void Blackbox::saveStyleFilename(const string& filename) {
1022   assert(! filename.empty());
1023   resource.style_file = filename;
1024   config.setValue("session.styleFile", resource.style_file);
1025 }
1026
1027
1028 void Blackbox::addMenuTimestamp(const string& filename) {
1029   assert(! filename.empty());
1030   bool found = False;
1031
1032   MenuTimestampList::iterator it = menuTimestamps.begin();
1033   for (; it != menuTimestamps.end() && ! found; ++it) {
1034     if ((*it)->filename == filename) found = True;
1035   }
1036   if (! found) {
1037     struct stat buf;
1038
1039     if (! stat(filename.c_str(), &buf)) {
1040       MenuTimestamp *ts = new MenuTimestamp;
1041
1042       ts->filename = filename;
1043       ts->timestamp = buf.st_ctime;
1044
1045       menuTimestamps.push_back(ts);
1046     }
1047   }
1048 }
1049
1050
1051 void Blackbox::timeout(void) {
1052   if (reconfigure_wait)
1053     real_reconfigure();
1054
1055   if (reread_menu_wait)
1056     real_rereadMenu();
1057
1058   reconfigure_wait = reread_menu_wait = False;
1059 }
1060
1061
1062 void Blackbox::setFocusedWindow(BlackboxWindow *win) {
1063   if (focused_window && focused_window == win) // nothing to do
1064     return;
1065
1066   BScreen *old_screen = 0;
1067
1068   if (focused_window) {
1069     focused_window->setFocusFlag(False);
1070     old_screen = focused_window->getScreen();
1071   }
1072
1073   if (win && ! win->isIconic()) {
1074     // the active screen is the one with the last focused window...
1075     // this will keep focus on this screen no matter where the mouse goes,
1076     // so multihead keybindings will continue to work on that screen until the
1077     // user focuses a window on a different screen.
1078     active_screen = win->getScreen();
1079     focused_window = win;
1080   } else {
1081     focused_window = 0;
1082     if (! old_screen) {
1083       if (active_screen) {
1084         // set input focus to the toolbar of the screen with mouse
1085         XSetInputFocus(getXDisplay(),
1086                        active_screen->getRootWindow(),
1087                        RevertToPointerRoot, CurrentTime);
1088       } else {
1089         // set input focus to the toolbar of the first managed screen
1090         XSetInputFocus(getXDisplay(),
1091                        screenList.front()->getRootWindow(),
1092                        RevertToPointerRoot, CurrentTime);
1093       }
1094     } else {
1095       // set input focus to the toolbar of the last screen
1096       XSetInputFocus(getXDisplay(), old_screen->getRootWindow(),
1097                      RevertToPointerRoot, CurrentTime);
1098     }
1099   }
1100
1101   if (active_screen && active_screen->isScreenManaged()) {
1102     active_screen->getToolbar()->redrawWindowLabel(True);
1103     active_screen->updateNetizenWindowFocus();
1104   }
1105
1106   if (old_screen && old_screen != active_screen) {
1107     old_screen->getToolbar()->redrawWindowLabel(True);
1108     old_screen->updateNetizenWindowFocus();
1109   }
1110 }