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