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