merging in netwm changes at merge point "netwm-merge1". This add the XAtom class...
[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       /*
564         don't process FocusIns when:
565         1. the new focus window isn't an ancestor or inferior of the old
566         focus window (NotifyNonlinear)
567       */
568       break;
569     }
570
571     BlackboxWindow *win = searchWindow(e->xfocus.window);
572     if (win) {
573       if (! win->isFocused())
574         win->setFocusFlag(True);
575
576       /*
577         set the event window to None.  when the FocusOut event handler calls
578         this function recursively, it uses this as an indication that focus
579         has moved to a known window.
580       */
581       e->xfocus.window = None;
582     }
583
584     break;
585   }
586
587   case FocusOut: {
588     if (e->xfocus.detail != NotifyNonlinear) {
589       /*
590         don't process FocusOuts when:
591         2. the new focus window isn't an ancestor or inferior of the old
592         focus window (NotifyNonlinear)
593       */
594       break;
595     }
596
597     BlackboxWindow *win = searchWindow(e->xfocus.window);
598     if (win && win->isFocused()) {
599       /*
600         before we mark "win" as unfocused, we need to verify that focus is
601         going to a known location, is in a known location, or set focus
602         to a known location.
603       */
604
605       XEvent event;
606       // don't check the current focus if FocusOut was generated during a grab
607       bool check_focus = (e->xfocus.mode == NotifyNormal);
608
609       /*
610         First, check if there is a pending FocusIn event waiting.  if there
611         is, process it and determine if focus has moved to another window
612         (the FocusIn event handler sets the window in the event
613         structure to None to indicate this).
614       */
615       if (XCheckTypedEvent(getXDisplay(), FocusIn, &event)) {
616
617         process_event(&event);
618         if (event.xfocus.window == None) {
619           // focus has moved
620           check_focus = False;
621         }
622       }
623
624       if (check_focus) {
625         /*
626           Second, we query the X server for the current input focus.
627           to make sure that we keep a consistent state.
628         */
629         BlackboxWindow *focus;
630         Window w;
631         int revert;
632         XGetInputFocus(getXDisplay(), &w, &revert);
633         focus = searchWindow(w);
634         if (focus) {
635           /*
636             focus got from "win" to "focus" under some very strange
637             circumstances, and we need to make sure that the focus indication
638             is correct.
639           */
640           setFocusedWindow(focus);
641         } else {
642           // we have no idea where focus went... so we set it to somewhere
643           setFocusedWindow(0);
644         }
645       }
646     }
647
648     break;
649   }
650
651   case ClientMessage: {
652     if (e->xclient.format == 32) {
653       if (e->xclient.message_type == getWMChangeStateAtom()) {
654         BlackboxWindow *win = searchWindow(e->xclient.window);
655         if (! win || ! win->validateClient()) return;
656
657         if (e->xclient.data.l[0] == IconicState)
658           win->iconify();
659         if (e->xclient.data.l[0] == NormalState)
660           win->deiconify();
661       } else if(e->xclient.message_type == getBlackboxChangeWorkspaceAtom()) {
662         BScreen *screen = searchScreen(e->xclient.window);
663
664         if (screen && e->xclient.data.l[0] >= 0 &&
665             e->xclient.data.l[0] <
666             static_cast<signed>(screen->getWorkspaceCount()))
667           screen->changeWorkspaceID(e->xclient.data.l[0]);
668       } else if (e->xclient.message_type == getBlackboxChangeWindowFocusAtom()) {
669         BlackboxWindow *win = searchWindow(e->xclient.window);
670
671         if (win && win->isVisible() && win->setInputFocus())
672           win->installColormap(True);
673       } else if (e->xclient.message_type == getBlackboxCycleWindowFocusAtom()) {
674         BScreen *screen = searchScreen(e->xclient.window);
675
676         if (screen) {
677           if (! e->xclient.data.l[0])
678             screen->prevFocus();
679           else
680             screen->nextFocus();
681         }
682       } else if (e->xclient.message_type == getBlackboxChangeAttributesAtom()) {
683         BlackboxWindow *win = searchWindow(e->xclient.window);
684
685         if (win && win->validateClient()) {
686           BlackboxHints net;
687           net.flags = e->xclient.data.l[0];
688           net.attrib = e->xclient.data.l[1];
689           net.workspace = e->xclient.data.l[2];
690           net.stack = e->xclient.data.l[3];
691           net.decoration = e->xclient.data.l[4];
692
693           win->changeBlackboxHints(&net);
694         }
695       }
696     }
697
698     break;
699   }
700
701   case NoExpose:
702   case ConfigureNotify:
703   case MapNotify:
704     break; // not handled, just ignore
705
706   default: {
707 #ifdef    SHAPE
708     if (e->type == getShapeEventBase()) {
709       XShapeEvent *shape_event = (XShapeEvent *) e;
710       BlackboxWindow *win = searchWindow(e->xany.window);
711
712       if (win)
713         win->shapeEvent(shape_event);
714     }
715 #endif // SHAPE
716   }
717   } // switch
718 }
719
720
721 bool Blackbox::handleSignal(int sig) {
722   switch (sig) {
723   case SIGHUP:
724   case SIGUSR1:
725     reconfigure();
726     break;
727
728   case SIGUSR2:
729     rereadMenu();
730     break;
731
732   case SIGPIPE:
733   case SIGSEGV:
734   case SIGFPE:
735   case SIGINT:
736   case SIGTERM:
737     shutdown();
738
739   default:
740     return False;
741   }
742
743   return True;
744 }
745
746
747 bool Blackbox::validateWindow(Window window) {
748   XEvent event;
749   if (XCheckTypedWindowEvent(getXDisplay(), window, DestroyNotify, &event)) {
750     XPutBackEvent(getXDisplay(), &event);
751
752     return False;
753   }
754
755   return True;
756 }
757
758
759 BScreen *Blackbox::searchScreen(Window window) {
760   ScreenList::iterator it = screenList.begin();
761
762   for (; it != screenList.end(); ++it) {
763     BScreen *s = *it;
764     if (s->getRootWindow() == window)
765       return s;
766   }
767
768   return (BScreen *) 0;
769 }
770
771
772 BlackboxWindow *Blackbox::searchWindow(Window window) {
773   WindowLookup::iterator it = windowSearchList.find(window);
774   if (it != windowSearchList.end())
775     return it->second;
776
777   return (BlackboxWindow*) 0;
778 }
779
780
781 BWindowGroup *Blackbox::searchGroup(Window window) {
782   GroupLookup::iterator it = groupSearchList.find(window);
783   if (it != groupSearchList.end())
784     return it->second;
785
786   return (BWindowGroup *) 0;
787 }
788
789
790 Basemenu *Blackbox::searchMenu(Window window) {
791   MenuLookup::iterator it = menuSearchList.find(window);
792   if (it != menuSearchList.end())
793     return it->second;
794
795   return (Basemenu*) 0;
796 }
797
798
799 Toolbar *Blackbox::searchToolbar(Window window) {
800   ToolbarLookup::iterator it = toolbarSearchList.find(window);
801   if (it != toolbarSearchList.end())
802     return it->second;
803
804   return (Toolbar*) 0;
805 }
806
807
808 Slit *Blackbox::searchSlit(Window window) {
809   SlitLookup::iterator it = slitSearchList.find(window);
810   if (it != slitSearchList.end())
811     return it->second;
812
813   return (Slit*) 0;
814 }
815
816
817 void Blackbox::saveWindowSearch(Window window, BlackboxWindow *data) {
818   windowSearchList.insert(WindowLookupPair(window, data));
819 }
820
821
822 void Blackbox::saveGroupSearch(Window window, BWindowGroup *data) {
823   groupSearchList.insert(GroupLookupPair(window, data));
824 }
825
826
827 void Blackbox::saveMenuSearch(Window window, Basemenu *data) {
828   menuSearchList.insert(MenuLookupPair(window, data));
829 }
830
831
832 void Blackbox::saveToolbarSearch(Window window, Toolbar *data) {
833   toolbarSearchList.insert(ToolbarLookupPair(window, data));
834 }
835
836
837 void Blackbox::saveSlitSearch(Window window, Slit *data) {
838   slitSearchList.insert(SlitLookupPair(window, data));
839 }
840
841
842 void Blackbox::removeWindowSearch(Window window) {
843   windowSearchList.erase(window);
844 }
845
846
847 void Blackbox::removeGroupSearch(Window window) {
848   groupSearchList.erase(window);
849 }
850
851
852 void Blackbox::removeMenuSearch(Window window) {
853   menuSearchList.erase(window);
854 }
855
856
857 void Blackbox::removeToolbarSearch(Window window) {
858   toolbarSearchList.erase(window);
859 }
860
861
862 void Blackbox::removeSlitSearch(Window window) {
863   slitSearchList.erase(window);
864 }
865
866
867 void Blackbox::restart(const char *prog) {
868   shutdown();
869
870   if (prog) {
871     execlp(prog, prog, NULL);
872     perror(prog);
873   }
874
875   // fall back in case the above execlp doesn't work
876   execvp(argv[0], argv);
877   string name = basename(argv[0]);
878   execvp(name.c_str(), argv);
879 }
880
881
882 void Blackbox::shutdown(void) {
883   BaseDisplay::shutdown();
884
885   XSetInputFocus(getXDisplay(), PointerRoot, None, CurrentTime);
886
887   std::for_each(screenList.begin(), screenList.end(),
888                 std::mem_fun(&BScreen::shutdown));
889
890   XSync(getXDisplay(), False);
891 }
892
893
894 /*
895  * Save all values as they are so that the defaults will be written to the rc
896  * file
897  */
898 void Blackbox::save_rc(void) {
899   config.setAutoSave(false);
900
901   config.setValue("session.colorsPerChannel", resource.colors_per_channel);
902   config.setValue("session.doubleClickInterval",
903                   resource.double_click_interval);
904   config.setValue("session.autoRaiseDelay",
905                   ((resource.auto_raise_delay.tv_sec * 1000) +
906                    (resource.auto_raise_delay.tv_usec / 1000)));
907   config.setValue("session.cacheLife", resource.cache_life / 60000);
908   config.setValue("session.cacheMax", resource.cache_max);
909   config.setValue("session.styleFile", resource.style_file);
910   config.setValue("session.titlebarLayout", resource.titlebar_layout);
911   
912   std::for_each(screenList.begin(), screenList.end(),
913                 std::mem_fun(&BScreen::save_rc));
914  
915   config.setAutoSave(true);
916   config.save();
917 }
918
919
920 void Blackbox::load_rc(void) {
921   if (! config.load())
922         config.create();
923   
924   string s;
925
926   if (! config.getValue("session.colorsPerChannel",
927                         resource.colors_per_channel))
928     resource.colors_per_channel = 4;
929   if (resource.colors_per_channel < 2) resource.colors_per_channel = 2;
930   else if (resource.colors_per_channel > 6) resource.colors_per_channel = 6;
931
932   if (config.getValue("session.styleFile", s))
933     resource.style_file = expandTilde(s);
934   else
935     resource.style_file = DEFAULTSTYLE;
936
937   if (! config.getValue("session.doubleClickInterval",
938                        resource.double_click_interval));
939     resource.double_click_interval = 250;
940
941   if (! config.getValue("session.autoRaiseDelay",
942                        resource.auto_raise_delay.tv_usec))
943     resource.auto_raise_delay.tv_usec = 400;
944   resource.auto_raise_delay.tv_sec = resource.auto_raise_delay.tv_usec / 1000;
945   resource.auto_raise_delay.tv_usec -=
946     (resource.auto_raise_delay.tv_sec * 1000);
947   resource.auto_raise_delay.tv_usec *= 1000;
948
949   if (! config.getValue("session.cacheLife", resource.cache_life))
950     resource.cache_life = 5;
951   resource.cache_life *= 60000;
952
953   if (! config.getValue("session.cacheMax", resource.cache_max))
954     resource.cache_max = 200;
955   
956   if (! config.getValue("session.titlebarLayout", resource.titlebar_layout))
957     resource.titlebar_layout = "ILMC";
958 }
959
960
961 void Blackbox::reconfigure(void) {
962   reconfigure_wait = True;
963
964   if (! timer->isTiming()) timer->start();
965 }
966
967
968 void Blackbox::real_reconfigure(void) {
969   load_rc();
970   
971   std::for_each(menuTimestamps.begin(), menuTimestamps.end(),
972                 PointerAssassin());
973   menuTimestamps.clear();
974
975   gcCache()->purge();
976
977   std::for_each(screenList.begin(), screenList.end(),
978                 std::mem_fun(&BScreen::reconfigure));
979 }
980
981
982 void Blackbox::checkMenu(void) {
983   bool reread = False;
984   MenuTimestampList::iterator it = menuTimestamps.begin();
985   for(; it != menuTimestamps.end(); ++it) {
986     MenuTimestamp *tmp = *it;
987     struct stat buf;
988
989     if (! stat(tmp->filename.c_str(), &buf)) {
990       if (tmp->timestamp != buf.st_ctime)
991         reread = True;
992     } else {
993       reread = True;
994     }
995   }
996
997   if (reread) rereadMenu();
998 }
999
1000
1001 void Blackbox::rereadMenu(void) {
1002   reread_menu_wait = True;
1003
1004   if (! timer->isTiming()) timer->start();
1005 }
1006
1007
1008 void Blackbox::real_rereadMenu(void) {
1009   std::for_each(menuTimestamps.begin(), menuTimestamps.end(),
1010                 PointerAssassin());
1011   menuTimestamps.clear();
1012
1013   std::for_each(screenList.begin(), screenList.end(),
1014                 std::mem_fun(&BScreen::rereadMenu));
1015 }
1016
1017
1018 void Blackbox::saveStyleFilename(const string& filename) {
1019   assert(! filename.empty());
1020   resource.style_file = filename;
1021   config.setValue("session.styleFile", resource.style_file);
1022 }
1023
1024
1025 void Blackbox::addMenuTimestamp(const string& filename) {
1026   assert(! filename.empty());
1027   bool found = False;
1028
1029   MenuTimestampList::iterator it = menuTimestamps.begin();
1030   for (; it != menuTimestamps.end() && ! found; ++it) {
1031     if ((*it)->filename == filename) found = True;
1032   }
1033   if (! found) {
1034     struct stat buf;
1035
1036     if (! stat(filename.c_str(), &buf)) {
1037       MenuTimestamp *ts = new MenuTimestamp;
1038
1039       ts->filename = filename;
1040       ts->timestamp = buf.st_ctime;
1041
1042       menuTimestamps.push_back(ts);
1043     }
1044   }
1045 }
1046
1047
1048 void Blackbox::timeout(void) {
1049   if (reconfigure_wait)
1050     real_reconfigure();
1051
1052   if (reread_menu_wait)
1053     real_rereadMenu();
1054
1055   reconfigure_wait = reread_menu_wait = False;
1056 }
1057
1058
1059 void Blackbox::setFocusedWindow(BlackboxWindow *win) {
1060   if (focused_window && focused_window == win) // nothing to do
1061     return;
1062
1063   BScreen *old_screen = 0;
1064
1065   if (focused_window) {
1066     focused_window->setFocusFlag(False);
1067     old_screen = focused_window->getScreen();
1068   }
1069
1070   if (win && ! win->isIconic()) {
1071     // the active screen is the one with the last focused window...
1072     // this will keep focus on this screen no matter where the mouse goes,
1073     // so multihead keybindings will continue to work on that screen until the
1074     // user focuses a window on a different screen.
1075     active_screen = win->getScreen();
1076     focused_window = win;
1077   } else {
1078     focused_window = 0;
1079     if (! old_screen) {
1080       if (active_screen) {
1081         // set input focus to the toolbar of the screen with mouse
1082         XSetInputFocus(getXDisplay(),
1083                        active_screen->getRootWindow(),
1084                        RevertToPointerRoot, CurrentTime);
1085       } else {
1086         // set input focus to the toolbar of the first managed screen
1087         XSetInputFocus(getXDisplay(),
1088                        screenList.front()->getRootWindow(),
1089                        RevertToPointerRoot, CurrentTime);
1090       }
1091     } else {
1092       // set input focus to the toolbar of the last screen
1093       XSetInputFocus(getXDisplay(), old_screen->getRootWindow(),
1094                      RevertToPointerRoot, CurrentTime);
1095     }
1096   }
1097
1098   if (active_screen && active_screen->isScreenManaged()) {
1099     active_screen->getToolbar()->redrawWindowLabel(True);
1100     active_screen->updateNetizenWindowFocus();
1101   }
1102
1103   if (old_screen && old_screen != active_screen) {
1104     old_screen->getToolbar()->redrawWindowLabel(True);
1105     old_screen->updateNetizenWindowFocus();
1106   }
1107 }