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