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