big fat commit..
[mikachu/openbox.git] / src / blackbox.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2
3 #ifdef    HAVE_CONFIG_H
4 #  include "../config.h"
5 #endif // HAVE_CONFIG_H
6
7 extern "C" {
8 #include <X11/Xlib.h>
9 #include <X11/Xutil.h>
10 #include <X11/Xatom.h>
11 #include <X11/cursorfont.h>
12 #include <X11/keysym.h>
13
14 #ifdef    SHAPE
15 #include <X11/extensions/shape.h>
16 #endif // SHAPE
17
18 #ifdef    HAVE_STDIO_H
19 #  include <stdio.h>
20 #endif // HAVE_STDIO_H
21
22 #ifdef HAVE_STDLIB_H
23 #  include <stdlib.h>
24 #endif // HAVE_STDLIB_H
25
26 #ifdef HAVE_STRING_H
27 #  include <string.h>
28 #endif // HAVE_STRING_H
29
30 #ifdef    HAVE_UNISTD_H
31 #  include <sys/types.h>
32 #  include <unistd.h>
33 #endif // HAVE_UNISTD_H
34
35 #ifdef    HAVE_SYS_PARAM_H
36 #  include <sys/param.h>
37 #endif // HAVE_SYS_PARAM_H
38
39 #ifdef    HAVE_SYS_SELECT_H
40 #  include <sys/select.h>
41 #endif // HAVE_SYS_SELECT_H
42
43 #ifdef    HAVE_SIGNAL_H
44 #  include <signal.h>
45 #endif // HAVE_SIGNAL_H
46
47 #ifdef    HAVE_SYS_SIGNAL_H
48 #  include <sys/signal.h>
49 #endif // HAVE_SYS_SIGNAL_H
50
51 #ifdef    HAVE_SYS_STAT_H
52 #  include <sys/types.h>
53 #  include <sys/stat.h>
54 #endif // HAVE_SYS_STAT_H
55
56 #ifdef    TIME_WITH_SYS_TIME
57 #  include <sys/time.h>
58 #  include <time.h>
59 #else // !TIME_WITH_SYS_TIME
60 #  ifdef    HAVE_SYS_TIME_H
61 #    include <sys/time.h>
62 #  else // !HAVE_SYS_TIME_H
63 #    include <time.h>
64 #  endif // HAVE_SYS_TIME_H
65 #endif // TIME_WITH_SYS_TIME
66
67 #ifdef    HAVE_LIBGEN_H
68 #  include <libgen.h>
69 #endif // HAVE_LIBGEN_H
70 }
71
72 #include <assert.h>
73
74 #include <algorithm>
75 #include <string>
76 using std::string;
77
78 #include "blackbox.hh"
79 #include "gccache.hh"
80 #include "image.hh"
81 #include "screen.hh"
82 #include "util.hh"
83 #include "window.hh"
84 #include "workspace.hh"
85 #include "xatom.hh"
86
87 Blackbox *blackbox;
88
89
90 Blackbox::Blackbox(char **m_argv, char *dpy_name, char *rc)
91   : BaseDisplay(m_argv[0], dpy_name) {
92   if (! XSupportsLocale())
93     fprintf(stderr, "X server does not support locale\n");
94
95   if (XSetLocaleModifiers("") == NULL)
96     fprintf(stderr, "cannot set locale modifiers\n");
97
98   ::blackbox = this;
99   argv = m_argv;
100
101   // try to make sure the ~/.openbox directory exists
102   mkdir(expandTilde("~/.openbox").c_str(), S_IREAD | S_IWRITE | S_IEXEC |
103                                            S_IRGRP | S_IWGRP | S_IXGRP |
104                                            S_IROTH | S_IWOTH | S_IXOTH);
105   
106   if (! rc) rc = "~/.openbox/rc";
107   rc_file = expandTilde(rc);
108   config.setFile(rc_file);  
109
110   no_focus = False;
111
112   resource.auto_raise_delay.tv_sec = resource.auto_raise_delay.tv_usec = 0;
113
114   active_screen = 0;
115   focused_window = changing_window = (BlackboxWindow *) 0;
116
117   load_rc();
118
119   xatom = new XAtom(getXDisplay());
120
121   cursor.session = XCreateFontCursor(getXDisplay(), XC_left_ptr);
122   cursor.move = XCreateFontCursor(getXDisplay(), XC_fleur);
123   cursor.ll_angle = XCreateFontCursor(getXDisplay(), XC_ll_angle);
124   cursor.lr_angle = XCreateFontCursor(getXDisplay(), XC_lr_angle);
125   cursor.ul_angle = XCreateFontCursor(getXDisplay(), XC_ul_angle);
126   cursor.ur_angle = XCreateFontCursor(getXDisplay(), XC_ur_angle);
127
128   for (unsigned int i = 0; i < getNumberOfScreens(); i++) {
129     BScreen *screen = new BScreen(this, i);
130
131     if (! screen->isScreenManaged()) {
132       delete screen;
133       continue;
134     }
135
136     screenList.push_back(screen);
137   }
138
139   if (screenList.empty()) {
140     fprintf(stderr,
141             "Blackbox::Blackbox: no managable screens found, aborting.\n");
142     ::exit(3);
143   }
144
145   // save current settings and default values
146   save_rc();
147
148   // set the screen with mouse to the first managed screen
149   active_screen = screenList.front();
150   setFocusedWindow(0);
151
152   XSynchronize(getXDisplay(), False);
153   XSync(getXDisplay(), False);
154
155   reconfigure_wait = False;
156
157   timer = new BTimer(this, this);
158   timer->setTimeout(0l);
159 }
160
161
162 Blackbox::~Blackbox(void) {
163   std::for_each(screenList.begin(), screenList.end(), PointerAssassin());
164
165   delete xatom;
166
167   delete timer;
168 }
169
170
171 void Blackbox::process_event(XEvent *e) {
172   switch (e->type) {
173   case ButtonPress: {
174     // strip the lock key modifiers
175     e->xbutton.state &= ~(NumLockMask | ScrollLockMask | LockMask);
176
177     last_time = e->xbutton.time;
178
179     BlackboxWindow *win = (BlackboxWindow *) 0;
180     BScreen *scrn = (BScreen *) 0;
181
182     if ((win = searchWindow(e->xbutton.window))) {
183       win->buttonPressEvent(&e->xbutton);
184
185       /* XXX: is this sane on low colour desktops? */
186       if (e->xbutton.button == 1)
187         win->installColormap(True);
188     } else if ((scrn = searchScreen(e->xbutton.window))) {
189       scrn->buttonPressEvent(&e->xbutton);
190       if (active_screen != scrn) {
191         active_screen = scrn;
192         // first, set no focus window on the old screen
193         setFocusedWindow(0);
194         // and move focus to this screen
195         setFocusedWindow(0);
196       }
197     }
198     break;
199   }
200
201   case ButtonRelease: {
202     // strip the lock key modifiers
203     e->xbutton.state &= ~(NumLockMask | ScrollLockMask | LockMask);
204
205     last_time = e->xbutton.time;
206
207     BlackboxWindow *win = (BlackboxWindow *) 0;
208
209     if ((win = searchWindow(e->xbutton.window)))
210       win->buttonReleaseEvent(&e->xbutton);
211
212     break;
213   }
214
215   case ConfigureRequest: {
216     BlackboxWindow *win = (BlackboxWindow *) 0;
217
218     if ((win = searchWindow(e->xconfigurerequest.window))) {
219       win->configureRequestEvent(&e->xconfigurerequest);
220     } else {
221       if (validateWindow(e->xconfigurerequest.window)) {
222         XWindowChanges xwc;
223
224         xwc.x = e->xconfigurerequest.x;
225         xwc.y = e->xconfigurerequest.y;
226         xwc.width = e->xconfigurerequest.width;
227         xwc.height = e->xconfigurerequest.height;
228         xwc.border_width = e->xconfigurerequest.border_width;
229         xwc.sibling = e->xconfigurerequest.above;
230         xwc.stack_mode = e->xconfigurerequest.detail;
231
232         XConfigureWindow(getXDisplay(), e->xconfigurerequest.window,
233                          e->xconfigurerequest.value_mask, &xwc);
234       }
235     }
236
237     break;
238   }
239
240   case MapRequest: {
241 #ifdef    DEBUG
242     fprintf(stderr, "Blackbox::process_event(): MapRequest for 0x%lx\n",
243             e->xmaprequest.window);
244 #endif // DEBUG
245
246     BlackboxWindow *win = searchWindow(e->xmaprequest.window);
247
248     if (win) {
249       bool focus = False;
250       if (win->isIconic()) {
251         win->deiconify();
252         focus = True;
253       }
254       if (win->isShaded()) {
255         win->shade();
256         focus = True;
257       }
258
259       if (focus && (win->isTransient() || win->getScreen()->doFocusNew()) &&
260           win->isVisible())
261         win->setInputFocus();
262     } else {
263       BScreen *screen = searchScreen(e->xmaprequest.parent);
264
265       if (! screen) {
266         /*
267           we got a map request for a window who's parent isn't root. this
268           can happen in only one circumstance:
269
270             a client window unmapped a managed window, and then remapped it
271             somewhere between unmapping the client window and reparenting it
272             to root.
273
274           regardless of how it happens, we need to find the screen that
275           the window is on
276         */
277         XWindowAttributes wattrib;
278         if (! XGetWindowAttributes(getXDisplay(), e->xmaprequest.window,
279                                    &wattrib)) {
280           // failed to get the window attributes, perhaps the window has
281           // now been destroyed?
282           break;
283         }
284
285         screen = searchScreen(wattrib.root);
286         assert(screen != 0); // this should never happen
287       }
288
289       screen->manageWindow(e->xmaprequest.window);
290     }
291
292     break;
293   }
294
295   case UnmapNotify: {
296     BlackboxWindow *win = (BlackboxWindow *) 0;
297     BScreen *screen = (BScreen *) 0;
298
299     if ((win = searchWindow(e->xunmap.window))) {
300       win->unmapNotifyEvent(&e->xunmap);
301     } else if ((screen = searchSystrayWindow(e->xunmap.window))) {
302       screen->removeSystrayWindow(e->xunmap.window);
303     }
304
305     break;
306   }
307
308   case DestroyNotify: {
309     BlackboxWindow *win = (BlackboxWindow *) 0;
310     BScreen *screen = (BScreen *) 0;
311     BWindowGroup *group = (BWindowGroup *) 0;
312
313     if ((win = searchWindow(e->xdestroywindow.window))) {
314       win->destroyNotifyEvent(&e->xdestroywindow);
315     } else if ((group = searchGroup(e->xdestroywindow.window))) {
316       delete group;
317     } else if ((screen = searchSystrayWindow(e->xunmap.window))) {
318       screen->removeSystrayWindow(e->xunmap.window);
319     }
320
321     break;
322   }
323
324   case ReparentNotify: {
325     /*
326       this event is quite rare and is usually handled in unmapNotify
327       however, if the window is unmapped when the reparent event occurs
328       the window manager never sees it because an unmap event is not sent
329       to an already unmapped window.
330     */
331     BlackboxWindow *win = searchWindow(e->xreparent.window);
332     if (win)
333       win->reparentNotifyEvent(&e->xreparent);
334     break;
335   }
336
337   case MotionNotify: {
338     // motion notify compression...
339     XEvent realevent;
340     unsigned int i = 0;
341     while (XCheckTypedWindowEvent(getXDisplay(), e->xmotion.window,
342                                   MotionNotify, &realevent)) {
343       i++;
344     }
345
346     // if we have compressed some motion events, use the last one
347     if ( i > 0 )
348       e = &realevent;
349
350     // the pointer is on the wrong screen
351     if (! e->xmotion.same_screen)
352       break;
353
354     // strip the lock key modifiers
355     e->xmotion.state &= ~(NumLockMask | ScrollLockMask | LockMask);
356
357     last_time = e->xmotion.time;
358
359     BlackboxWindow *win = (BlackboxWindow *) 0;
360
361     if ((win = searchWindow(e->xmotion.window)))
362       win->motionNotifyEvent(&e->xmotion);
363
364     break;
365   }
366
367   case PropertyNotify: {
368     last_time = e->xproperty.time;
369
370     BlackboxWindow *win = (BlackboxWindow *) 0;
371     BScreen *screen = (BScreen *) 0;
372
373     if ((win = searchWindow(e->xproperty.window)))
374       win->propertyNotifyEvent(&e->xproperty);
375     else if ((screen = searchScreen(e->xproperty.window)))
376       screen->propertyNotifyEvent(&e->xproperty);
377     break;
378   }
379
380   case EnterNotify: {
381     last_time = e->xcrossing.time;
382
383     BScreen *screen = (BScreen *) 0;
384     BlackboxWindow *win = (BlackboxWindow *) 0;
385
386     if (e->xcrossing.mode == NotifyGrab) break;
387
388     if ((e->xcrossing.window == e->xcrossing.root) &&
389         (screen = searchScreen(e->xcrossing.window))) {
390       screen->getImageControl()->installRootColormap();
391     } else if ((win = searchWindow(e->xcrossing.window))) {
392       if (! no_focus)
393         win->enterNotifyEvent(&e->xcrossing);
394     }
395     break;
396   }
397
398   case LeaveNotify: {
399     last_time = e->xcrossing.time;
400
401     BlackboxWindow *win = (BlackboxWindow *) 0;
402
403     if ((win = searchWindow(e->xcrossing.window)))
404       win->leaveNotifyEvent(&e->xcrossing);
405     break;
406   }
407
408   case Expose: {
409     // compress expose events
410     XEvent realevent;
411     unsigned int i = 0;
412     int ex1, ey1, ex2, ey2;
413     ex1 = e->xexpose.x;
414     ey1 = e->xexpose.y;
415     ex2 = ex1 + e->xexpose.width - 1;
416     ey2 = ey1 + e->xexpose.height - 1;
417     while (XCheckTypedWindowEvent(getXDisplay(), e->xexpose.window,
418                                   Expose, &realevent)) {
419       i++;
420
421       // merge expose area
422       ex1 = std::min(realevent.xexpose.x, ex1);
423       ey1 = std::min(realevent.xexpose.y, ey1);
424       ex2 = std::max(realevent.xexpose.x + realevent.xexpose.width - 1, ex2);
425       ey2 = std::max(realevent.xexpose.y + realevent.xexpose.height - 1, ey2);
426     }
427     if ( i > 0 )
428       e = &realevent;
429
430     // use the merged area
431     e->xexpose.x = ex1;
432     e->xexpose.y = ey1;
433     e->xexpose.width = ex2 - ex1 + 1;
434     e->xexpose.height = ey2 - ey1 + 1;
435
436     BlackboxWindow *win = (BlackboxWindow *) 0;
437
438     if ((win = searchWindow(e->xexpose.window)))
439       win->exposeEvent(&e->xexpose);
440
441     break;
442   }
443
444   case KeyPress: {
445     break;
446   }
447
448   case ColormapNotify: {
449     BScreen *screen = searchScreen(e->xcolormap.window);
450
451     if (screen)
452       screen->setRootColormapInstalled((e->xcolormap.state ==
453                                         ColormapInstalled) ? True : False);
454
455     break;
456   }
457
458   case FocusIn: {
459     if (e->xfocus.detail != NotifyNonlinear &&
460         e->xfocus.detail != NotifyAncestor) {
461       /*
462         don't process FocusIns when:
463         1. the new focus window isn't an ancestor or inferior of the old
464         focus window (NotifyNonlinear)
465         make sure to allow the FocusIn when the old focus window was an
466         ancestor but didn't have a parent, such as root (NotifyAncestor)
467       */
468       break;
469     }
470
471     BlackboxWindow *win = searchWindow(e->xfocus.window);
472     if (win) {
473       if (! win->isFocused())
474         win->setFocusFlag(True);
475
476       /*
477         set the event window to None.  when the FocusOut event handler calls
478         this function recursively, it uses this as an indication that focus
479         has moved to a known window.
480       */
481       e->xfocus.window = None;
482
483       no_focus = False;   // focusing is back on
484     }
485
486     break;
487   }
488
489   case FocusOut: {
490     if (e->xfocus.detail != NotifyNonlinear) {
491       /*
492         don't process FocusOuts when:
493         2. the new focus window isn't an ancestor or inferior of the old
494         focus window (NotifyNonlinear)
495       */
496       break;
497     }
498
499     BlackboxWindow *win = searchWindow(e->xfocus.window);
500     if (win && win->isFocused()) {
501       /*
502         before we mark "win" as unfocused, we need to verify that focus is
503         going to a known location, is in a known location, or set focus
504         to a known location.
505       */
506
507       XEvent event;
508       // don't check the current focus if FocusOut was generated during a grab
509       bool check_focus = (e->xfocus.mode == NotifyNormal);
510
511       /*
512         First, check if there is a pending FocusIn event waiting.  if there
513         is, process it and determine if focus has moved to another window
514         (the FocusIn event handler sets the window in the event
515         structure to None to indicate this).
516       */
517       if (XCheckTypedEvent(getXDisplay(), FocusIn, &event)) {
518
519         process_event(&event);
520         if (event.xfocus.window == None) {
521           // focus has moved
522           check_focus = False;
523         }
524       }
525
526       if (check_focus) {
527         /*
528           Second, we query the X server for the current input focus.
529           to make sure that we keep a consistent state.
530         */
531         BlackboxWindow *focus;
532         Window w;
533         int revert;
534         XGetInputFocus(getXDisplay(), &w, &revert);
535         focus = searchWindow(w);
536         if (focus) {
537           /*
538             focus got from "win" to "focus" under some very strange
539             circumstances, and we need to make sure that the focus indication
540             is correct.
541           */
542           setFocusedWindow(focus);
543         } else {
544           // we have no idea where focus went... so we set it to somewhere
545           setFocusedWindow(0);
546         }
547       }
548     }
549
550     break;
551   }
552
553   case ClientMessage: {
554     if (e->xclient.format == 32) {
555       if (e->xclient.message_type == xatom->getAtom(XAtom::wm_change_state)) {
556         // WM_CHANGE_STATE message
557         BlackboxWindow *win = searchWindow(e->xclient.window);
558         if (! win || ! win->validateClient()) return;
559
560         if (e->xclient.data.l[0] == IconicState)
561           win->iconify();
562         if (e->xclient.data.l[0] == NormalState)
563           win->deiconify();
564       } else if (e->xclient.message_type == 
565                  xatom->getAtom(XAtom::blackbox_change_workspace) || 
566                  e->xclient.message_type == 
567                  xatom->getAtom(XAtom::net_current_desktop)) {
568         // NET_CURRENT_DESKTOP message
569         BScreen *screen = searchScreen(e->xclient.window);
570
571         unsigned int workspace = e->xclient.data.l[0];
572         if (screen && workspace < screen->getWorkspaceCount())
573           screen->changeWorkspaceID(workspace);
574       } else if (e->xclient.message_type == 
575                  xatom->getAtom(XAtom::blackbox_change_window_focus)) {
576         // TEMP HACK TO KEEP BBKEYS WORKING
577         BlackboxWindow *win = searchWindow(e->xclient.window);
578
579         if (win && win->isVisible() && win->setInputFocus())
580           win->installColormap(True);
581       } else if (e->xclient.message_type == 
582                  xatom->getAtom(XAtom::net_active_window)) {
583         // NET_ACTIVE_WINDOW
584         BlackboxWindow *win = searchWindow(e->xclient.window);
585
586         if (win) {
587           BScreen *screen = win->getScreen();
588
589           if (win->isIconic())
590             win->deiconify(False, False);
591           if (! win->isStuck() &&
592               (win->getWorkspaceNumber() != screen->getCurrentWorkspaceID())) {
593             no_focus = True;
594             screen->changeWorkspaceID(win->getWorkspaceNumber());
595           }
596           if (win->isVisible() && win->setInputFocus()) {
597             win->getScreen()->getWorkspace(win->getWorkspaceNumber())->
598               raiseWindow(win);
599             win->installColormap(True);
600           }
601         }
602       } else if (e->xclient.message_type == 
603                  xatom->getAtom(XAtom::blackbox_cycle_window_focus)) {
604         // BLACKBOX_CYCLE_WINDOW_FOCUS
605         BScreen *screen = searchScreen(e->xclient.window);
606
607         if (screen) {
608           if (! e->xclient.data.l[0])
609             screen->prevFocus();
610           else
611             screen->nextFocus();
612         }
613       } else if (e->xclient.message_type == 
614                  xatom->getAtom(XAtom::net_wm_desktop)) {
615         // NET_WM_DESKTOP
616         BlackboxWindow *win = searchWindow(e->xclient.window);
617
618         if (win) {
619           BScreen *screen = win->getScreen();
620           unsigned long wksp = (unsigned) e->xclient.data.l[0];
621           if (wksp < screen->getWorkspaceCount()) {
622             if (win->isIconic()) win->deiconify(False, True);
623             if (win->isStuck()) win->stick();
624             if (wksp != screen->getCurrentWorkspaceID())
625               win->withdraw();
626             else
627               win->show();
628             screen->reassociateWindow(win, wksp, True);
629           } else if (wksp == 0xfffffffe || // XXX: BUG, BUT DOING THIS SO KDE WORKS FOR NOW!!
630                      wksp == 0xffffffff) {
631             if (win->isIconic()) win->deiconify(False, True);
632             if (! win->isStuck()) win->stick();
633             if (! win->isVisible()) win->show();
634           }
635         }
636       } else if (e->xclient.message_type == 
637                  xatom->getAtom(XAtom::blackbox_change_attributes)) {
638         // BLACKBOX_CHANGE_ATTRIBUTES
639         BlackboxWindow *win = searchWindow(e->xclient.window);
640
641         if (win && win->validateClient()) {
642           BlackboxHints net;
643           net.flags = e->xclient.data.l[0];
644           net.attrib = e->xclient.data.l[1];
645           net.workspace = e->xclient.data.l[2];
646           net.stack = e->xclient.data.l[3];
647           net.decoration = e->xclient.data.l[4];
648
649           win->changeBlackboxHints(&net);
650         }
651       } else if (e->xclient.message_type == 
652                 xatom->getAtom(XAtom::net_number_of_desktops)) {
653         // NET_NUMBER_OF_DESKTOPS
654         BScreen *screen = searchScreen(e->xclient.window);
655         
656         if (e->xclient.data.l[0] > 0)
657           screen->changeWorkspaceCount((unsigned) e->xclient.data.l[0]);
658       } else if (e->xclient.message_type ==
659                  xatom->getAtom(XAtom::net_close_window)) {
660         // NET_CLOSE_WINDOW
661         BlackboxWindow *win = searchWindow(e->xclient.window);
662         if (win && win->validateClient())
663           win->close(); // could this be smarter?
664       } else if (e->xclient.message_type ==
665                  xatom->getAtom(XAtom::net_wm_moveresize)) {
666         // NET_WM_MOVERESIZE
667         BlackboxWindow *win = searchWindow(e->xclient.window);
668         if (win && win->validateClient()) {
669           int x_root = e->xclient.data.l[0],
670               y_root = e->xclient.data.l[1];
671           if ((Atom) e->xclient.data.l[2] ==
672               xatom->getAtom(XAtom::net_wm_moveresize_move)) {
673             win->beginMove(x_root, y_root);
674           } else {
675             if ((Atom) e->xclient.data.l[2] ==
676                 xatom->getAtom(XAtom::net_wm_moveresize_size_topleft))
677               win->beginResize(x_root, y_root, BlackboxWindow::TopLeft);
678             else if ((Atom) e->xclient.data.l[2] ==
679                      xatom->getAtom(XAtom::net_wm_moveresize_size_topright))
680               win->beginResize(x_root, y_root, BlackboxWindow::TopRight);
681             else if ((Atom) e->xclient.data.l[2] ==
682                      xatom->getAtom(XAtom::net_wm_moveresize_size_bottomleft))
683               win->beginResize(x_root, y_root, BlackboxWindow::BottomLeft);
684             else if ((Atom) e->xclient.data.l[2] ==
685                 xatom->getAtom(XAtom::net_wm_moveresize_size_bottomright))
686               win->beginResize(x_root, y_root, BlackboxWindow::BottomRight);
687           }
688         }
689       } else if (e->xclient.message_type ==
690                  xatom->getAtom(XAtom::net_wm_state)) {
691         // NET_WM_STATE
692         BlackboxWindow *win = searchWindow(e->xclient.window);
693         if (win && win->validateClient()) {
694           const Atom action = (Atom) e->xclient.data.l[0];
695           const Atom state[] = { (Atom) e->xclient.data.l[1],
696                                  (Atom) e->xclient.data.l[2] };
697           
698           for (int i = 0; i < 2; ++i) {
699             if (! state[i])
700               continue;
701
702             if ((Atom) e->xclient.data.l[0] == 1) {
703               // ADD
704               if (state[i] == xatom->getAtom(XAtom::net_wm_state_modal)) {
705                 win->setModal(True);
706               } else if (state[i] ==
707                          xatom->getAtom(XAtom::net_wm_state_maximized_vert)) {
708                 if (win->isMaximizedHoriz()) {
709                   win->maximize(0); // unmaximize
710                   win->maximize(1); // full
711                 } else if (! win->isMaximized()) {
712                   win->maximize(2); // vert
713                 }
714               } else if (state[i] ==
715                          xatom->getAtom(XAtom::net_wm_state_maximized_horz)) {
716                 if (win->isMaximizedVert()) {
717                   win->maximize(0); // unmaximize
718                   win->maximize(1); // full
719                 } else if (! win->isMaximized()) {
720                   win->maximize(3); // horiz
721                 }
722               } else if (state[i] ==
723                          xatom->getAtom(XAtom::net_wm_state_shaded)) {
724                 if (! win->isShaded())
725                   win->shade();
726               } else if (state[i] ==
727                          xatom->getAtom(XAtom::net_wm_state_skip_taskbar)) {
728                 win->setSkipTaskbar(True);
729               } else if (state[i] ==
730                          xatom->getAtom(XAtom::net_wm_state_skip_pager)) {
731                 win->setSkipPager(True);
732               } else if (state[i] ==
733                          xatom->getAtom(XAtom::net_wm_state_fullscreen)) {
734                 win->setFullscreen(True);
735               }
736             } else if (action == 0) {
737               // REMOVE
738               if (state[i] == xatom->getAtom(XAtom::net_wm_state_modal)) {
739                 win->setModal(False);
740               } else if (state[i] ==
741                          xatom->getAtom(XAtom::net_wm_state_maximized_vert)) {
742                 if (win->isMaximizedFull()) {
743                   win->maximize(0); // unmaximize
744                   win->maximize(3); // horiz
745                 } else if (win->isMaximizedVert()) {
746                   win->maximize(0); // unmaximize
747                 }
748               } else if (state[i] ==
749                          xatom->getAtom(XAtom::net_wm_state_maximized_horz)) {
750                 if (win->isMaximizedFull()) {
751                   win->maximize(0); // unmaximize
752                   win->maximize(2); // vert
753                 } else if (win->isMaximizedHoriz()) {
754                   win->maximize(0); // unmaximize
755                 }
756               } else if (state[i] ==
757                          xatom->getAtom(XAtom::net_wm_state_shaded)) {
758                 if (win->isShaded())
759                   win->shade();
760               } else if (state[i] ==
761                          xatom->getAtom(XAtom::net_wm_state_skip_taskbar)) {
762                 win->setSkipTaskbar(False);
763               } else if (state[i] ==
764                          xatom->getAtom(XAtom::net_wm_state_skip_pager)) {
765                 win->setSkipPager(False);
766               } else if (state[i] ==
767                          xatom->getAtom(XAtom::net_wm_state_fullscreen)) {
768                 win->setFullscreen(False);
769               }
770             } else if (action == 2) {
771               // TOGGLE
772               if (state[i] == xatom->getAtom(XAtom::net_wm_state_modal)) {
773                 win->setModal(! win->isModal());
774               } else if (state[i] ==
775                          xatom->getAtom(XAtom::net_wm_state_maximized_vert)) {
776                 if (win->isMaximizedFull()) {
777                   win->maximize(0); // unmaximize
778                   win->maximize(3); // horiz
779                 } else if (win->isMaximizedVert()) {
780                   win->maximize(0); // unmaximize
781                 } else if (win->isMaximizedHoriz()) {
782                   win->maximize(0); // unmaximize
783                   win->maximize(1); // full
784                 } else {
785                   win->maximize(2); // vert
786                 }
787               } else if (state[i] ==
788                          xatom->getAtom(XAtom::net_wm_state_maximized_horz)) {
789                 if (win->isMaximizedFull()) {
790                   win->maximize(0); // unmaximize
791                   win->maximize(2); // vert
792                 } else if (win->isMaximizedHoriz()) {
793                   win->maximize(0); // unmaximize
794                 } else if (win->isMaximizedVert()) {
795                   win->maximize(0); // unmaximize
796                   win->maximize(1); // full
797                 } else {
798                   win->maximize(3); // horiz
799                 }
800               } else if (state[i] ==
801                          xatom->getAtom(XAtom::net_wm_state_shaded)) {
802                 win->shade();
803               } else if (state[i] ==
804                          xatom->getAtom(XAtom::net_wm_state_skip_taskbar)) {
805                 win->setSkipTaskbar(! win->skipTaskbar());
806               } else if (state[i] ==
807                          xatom->getAtom(XAtom::net_wm_state_skip_pager)) {
808                 win->setSkipPager(! win->skipPager());
809               } else if (state[i] ==
810                          xatom->getAtom(XAtom::net_wm_state_fullscreen)) {
811                 win->setFullscreen(! win->isFullscreen());
812               }
813             }
814           }
815         }
816       }
817     }
818
819     break;
820   }
821
822   case NoExpose:
823   case ConfigureNotify:
824   case MapNotify:
825     break; // not handled, just ignore
826
827   default: {
828 #ifdef    SHAPE
829     if (e->type == getShapeEventBase()) {
830       XShapeEvent *shape_event = (XShapeEvent *) e;
831       BlackboxWindow *win = searchWindow(e->xany.window);
832
833       if (win && shape_event->kind == ShapeBounding)
834         win->shapeEvent(shape_event);
835     }
836 #endif // SHAPE
837   }
838   } // switch
839 }
840
841
842 bool Blackbox::handleSignal(int sig) {
843   switch (sig) {
844   case SIGHUP:
845     reconfigure();
846     break;
847
848   case SIGUSR1:
849     restart();
850     break;
851
852   case SIGUSR2:
853     break;
854
855   case SIGPIPE:
856   case SIGSEGV:
857   case SIGFPE:
858   case SIGINT:
859   case SIGTERM:
860     shutdown();
861
862   default:
863     return False;
864   }
865
866   return True;
867 }
868
869
870 bool Blackbox::validateWindow(Window window) {
871   XEvent event;
872   if (XCheckTypedWindowEvent(getXDisplay(), window, DestroyNotify, &event)) {
873     XPutBackEvent(getXDisplay(), &event);
874
875     return False;
876   }
877
878   return True;
879 }
880
881
882 BScreen *Blackbox::searchScreen(Window window) {
883   ScreenList::iterator it = screenList.begin();
884
885   for (; it != screenList.end(); ++it) {
886     BScreen *s = *it;
887     if (s->getRootWindow() == window)
888       return s;
889   }
890
891   return (BScreen *) 0;
892 }
893
894
895 BScreen *Blackbox::searchSystrayWindow(Window window) {
896   WindowScreenLookup::iterator it = systraySearchList.find(window);
897   if (it != systraySearchList.end())
898     return it->second;
899
900   return (BScreen*) 0;
901 }
902
903
904 BlackboxWindow *Blackbox::searchWindow(Window window) {
905   WindowLookup::iterator it = windowSearchList.find(window);
906   if (it != windowSearchList.end())
907     return it->second;
908
909   return (BlackboxWindow*) 0;
910 }
911
912
913 BWindowGroup *Blackbox::searchGroup(Window window) {
914   GroupLookup::iterator it = groupSearchList.find(window);
915   if (it != groupSearchList.end())
916     return it->second;
917
918   return (BWindowGroup *) 0;
919 }
920
921
922 void Blackbox::saveSystrayWindowSearch(Window window, BScreen *screen) {
923   systraySearchList.insert(WindowScreenLookupPair(window, screen));
924 }
925
926
927 void Blackbox::saveWindowSearch(Window window, BlackboxWindow *data) {
928   windowSearchList.insert(WindowLookupPair(window, data));
929 }
930
931
932 void Blackbox::saveGroupSearch(Window window, BWindowGroup *data) {
933   groupSearchList.insert(GroupLookupPair(window, data));
934 }
935
936
937 void Blackbox::removeSystrayWindowSearch(Window window) {
938   systraySearchList.erase(window);
939 }
940
941
942 void Blackbox::removeWindowSearch(Window window) {
943   windowSearchList.erase(window);
944 }
945
946
947 void Blackbox::removeGroupSearch(Window window) {
948   groupSearchList.erase(window);
949 }
950
951
952 void Blackbox::restart(const char *prog) {
953   shutdown();
954
955   if (prog) {
956     putenv(const_cast<char *>(screenList.front()->displayString().c_str()));
957     execlp(prog, prog, NULL);
958     perror(prog);
959   }
960
961   // fall back in case the above execlp doesn't work
962   execvp(argv[0], argv);
963   string name = basename(argv[0]);
964   execvp(name.c_str(), argv);
965 }
966
967
968 void Blackbox::shutdown(void) {
969   BaseDisplay::shutdown();
970
971   XSetInputFocus(getXDisplay(), PointerRoot, None, CurrentTime);
972
973   std::for_each(screenList.begin(), screenList.end(),
974                 std::mem_fun(&BScreen::shutdown));
975
976   XSync(getXDisplay(), False);
977 }
978
979
980 #ifdef    XINERAMA
981 void Blackbox::saveXineramaPlacement(bool x) {
982   resource.xinerama_placement = x;
983   config.setValue("session.xineramaSupport.windowPlacement",
984                   resource.xinerama_placement);
985   reconfigure();  // make sure all screens get this change
986 }
987
988
989 void Blackbox::saveXineramaMaximizing(bool x) {
990   resource.xinerama_maximize = x;
991   config.setValue("session.xineramaSupport.windowMaximizing",
992                   resource.xinerama_maximize);
993   reconfigure();  // make sure all screens get this change
994 }
995
996
997 void Blackbox::saveXineramaSnapping(bool x) {
998   resource.xinerama_snap = x;
999   config.setValue("session.xineramaSupport.windowSnapping",
1000                   resource.xinerama_snap);
1001   reconfigure();  // make sure all screens get this change
1002 }
1003 #endif // XINERAMA
1004
1005   
1006 /*
1007  * Save all values as they are so that the defaults will be written to the rc
1008  * file
1009  */
1010 void Blackbox::save_rc(void) {
1011   config.setAutoSave(false);
1012
1013   config.setValue("session.colorsPerChannel", resource.colors_per_channel);
1014   config.setValue("session.doubleClickInterval",
1015                   resource.double_click_interval);
1016   config.setValue("session.autoRaiseDelay",
1017                   ((resource.auto_raise_delay.tv_sec * 1000) +
1018                    (resource.auto_raise_delay.tv_usec / 1000)));
1019   config.setValue("session.cacheLife", resource.cache_life / 60000);
1020   config.setValue("session.cacheMax", resource.cache_max);
1021   config.setValue("session.styleFile", resource.style_file);
1022   config.setValue("session.titlebarLayout", resource.titlebar_layout);
1023
1024   string s;
1025   if (resource.mod_mask & Mod1Mask) s += "Mod1-";
1026   if (resource.mod_mask & Mod2Mask) s += "Mod2-";
1027   if (resource.mod_mask & Mod3Mask) s += "Mod3-";
1028   if (resource.mod_mask & Mod4Mask) s += "Mod4-";
1029   if (resource.mod_mask & Mod5Mask) s += "Mod5-";
1030   if (resource.mod_mask & ShiftMask) s += "Shift-";
1031   if (resource.mod_mask & ControlMask) s += "Control-";
1032   s.resize(s.size() - 1); // drop the last '-'
1033   config.setValue("session.modifierMask", s);
1034   
1035 #ifdef    XINERAMA
1036   saveXineramaPlacement(resource.xinerama_placement);
1037   saveXineramaMaximizing(resource.xinerama_maximize);
1038   saveXineramaSnapping(resource.xinerama_snap);
1039 #endif // XINERAMA
1040
1041   std::for_each(screenList.begin(), screenList.end(),
1042                 std::mem_fun(&BScreen::save_rc));
1043  
1044   config.setAutoSave(true);
1045   config.save();
1046 }
1047
1048
1049 void Blackbox::load_rc(void) {
1050   if (! config.load())
1051     config.create();
1052   
1053   string s;
1054
1055   if (! config.getValue("session.colorsPerChannel",
1056                         resource.colors_per_channel))
1057     resource.colors_per_channel = 4;
1058   if (resource.colors_per_channel < 2) resource.colors_per_channel = 2;
1059   else if (resource.colors_per_channel > 6) resource.colors_per_channel = 6;
1060
1061   if (config.getValue("session.styleFile", s))
1062     resource.style_file = expandTilde(s);
1063   else
1064     resource.style_file = DEFAULTSTYLE;
1065
1066   if (! config.getValue("session.doubleClickInterval",
1067                        resource.double_click_interval));
1068     resource.double_click_interval = 250;
1069
1070   if (! config.getValue("session.autoRaiseDelay",
1071                        resource.auto_raise_delay.tv_usec))
1072     resource.auto_raise_delay.tv_usec = 400;
1073   resource.auto_raise_delay.tv_sec = resource.auto_raise_delay.tv_usec / 1000;
1074   resource.auto_raise_delay.tv_usec -=
1075     (resource.auto_raise_delay.tv_sec * 1000);
1076   resource.auto_raise_delay.tv_usec *= 1000;
1077
1078   if (! config.getValue("session.cacheLife", resource.cache_life))
1079     resource.cache_life = 5;
1080   resource.cache_life *= 60000;
1081
1082   if (! config.getValue("session.cacheMax", resource.cache_max))
1083     resource.cache_max = 200;
1084   
1085   if (! config.getValue("session.titlebarLayout", resource.titlebar_layout))
1086     resource.titlebar_layout = "ILMC";
1087
1088 #ifdef    XINERAMA
1089   if (! config.getValue("session.xineramaSupport.windowPlacement",
1090                         resource.xinerama_placement))
1091     resource.xinerama_placement = false;
1092
1093   if (! config.getValue("session.xineramaSupport.windowMaximizing",
1094                         resource.xinerama_maximize))
1095     resource.xinerama_maximize = false;
1096
1097   if (! config.getValue("session.xineramaSupport.windowSnapping",
1098                         resource.xinerama_snap))
1099     resource.xinerama_snap = false;
1100 #endif // XINERAMA
1101   
1102   resource.mod_mask = 0;
1103   if (config.getValue("session.modifierMask", s)) {
1104     if (s.find("Mod1") != string::npos)
1105       resource.mod_mask |= Mod1Mask;
1106     if (s.find("Mod2") != string::npos)
1107       resource.mod_mask |= Mod2Mask;
1108     if (s.find("Mod3") != string::npos)
1109       resource.mod_mask |= Mod3Mask;
1110     if (s.find("Mod4") != string::npos)
1111       resource.mod_mask |= Mod4Mask;
1112     if (s.find("Mod5") != string::npos)
1113       resource.mod_mask |= Mod5Mask;
1114     if (s.find("Shift") != string::npos)
1115       resource.mod_mask |= ShiftMask;
1116     if (s.find("Control") != string::npos)
1117       resource.mod_mask |= ControlMask;
1118   }
1119   if (! resource.mod_mask)
1120     resource.mod_mask = Mod1Mask;
1121 }
1122
1123
1124 void Blackbox::reconfigure(void) {
1125   // don't reconfigure while saving the initial rc file, it's a waste and it
1126   // breaks somethings (workspace names)
1127   if (isStartup()) return;
1128
1129   reconfigure_wait = True;
1130
1131   if (! timer->isTiming()) timer->start();
1132 }
1133
1134
1135 void Blackbox::real_reconfigure(void) {
1136   load_rc();
1137   
1138   gcCache()->purge();
1139
1140   std::for_each(screenList.begin(), screenList.end(),
1141                 std::mem_fun(&BScreen::reconfigure));
1142 }
1143
1144
1145 void Blackbox::saveStyleFilename(const string& filename) {
1146   assert(! filename.empty());
1147   resource.style_file = filename;
1148   config.setValue("session.styleFile", resource.style_file);
1149 }
1150
1151
1152 void Blackbox::timeout(void) {
1153   if (reconfigure_wait)
1154     real_reconfigure();
1155
1156   reconfigure_wait = False;
1157 }
1158
1159
1160 void Blackbox::setChangingWindow(BlackboxWindow *win) {
1161   // make sure one of the two is null and the other isn't
1162   assert((! changing_window && win) || (! win && changing_window));
1163   changing_window = win;
1164 }
1165
1166
1167 void Blackbox::setFocusedWindow(BlackboxWindow *win) {
1168   if (focused_window && focused_window == win) // nothing to do
1169     return;
1170
1171   BScreen *old_screen = 0;
1172
1173   if (focused_window) {
1174     focused_window->setFocusFlag(False);
1175     old_screen = focused_window->getScreen();
1176   }
1177
1178   if (win && ! win->isIconic()) {
1179     // the active screen is the one with the last focused window...
1180     // this will keep focus on this screen no matter where the mouse goes,
1181     // so multihead keybindings will continue to work on that screen until the
1182     // user focuses a window on a different screen.
1183     active_screen = win->getScreen();
1184     focused_window = win;
1185   } else {
1186     focused_window = 0;
1187     if (! old_screen) {
1188       if (active_screen) {
1189         // set input focus to the toolbar of the screen with mouse
1190         XSetInputFocus(getXDisplay(),
1191                        active_screen->getRootWindow(),
1192                        RevertToPointerRoot, CurrentTime);
1193       } else {
1194         // set input focus to the toolbar of the first managed screen
1195         XSetInputFocus(getXDisplay(),
1196                        screenList.front()->getRootWindow(),
1197                        RevertToPointerRoot, CurrentTime);
1198       }
1199     } else {
1200       // set input focus to the toolbar of the last screen
1201       XSetInputFocus(getXDisplay(), old_screen->getRootWindow(),
1202                      RevertToPointerRoot, CurrentTime);
1203     }
1204   }
1205
1206   if (active_screen && active_screen->isScreenManaged()) {
1207     active_screen->updateNetizenWindowFocus();
1208   }
1209
1210   if (old_screen && old_screen != active_screen) {
1211     old_screen->updateNetizenWindowFocus();
1212   }
1213 }