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