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