fix off-by-ones in window-to-window snapping. move the property from the blackbox...
[mikachu/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 "i18n.hh"
100 #include "blackbox.hh"
101 #include "Basemenu.hh"
102 #include "Clientmenu.hh"
103 #include "GCCache.hh"
104 #include "Image.hh"
105 #include "Rootmenu.hh"
106 #include "Screen.hh"
107 #include "Slit.hh"
108 #include "Toolbar.hh"
109 #include "Util.hh"
110 #include "Window.hh"
111 #include "Workspace.hh"
112 #include "Workspacemenu.hh"
113 #include "XAtom.hh"
114
115 // X event scanner for enter/leave notifies - adapted from twm
116 struct scanargs {
117   Window w;
118   bool leave, inferior, enter;
119 };
120
121 static Bool queueScanner(Display *, XEvent *e, char *args) {
122   scanargs *scan = (scanargs *) args;
123   if ((e->type == LeaveNotify) &&
124       (e->xcrossing.window == scan->w) &&
125       (e->xcrossing.mode == NotifyNormal)) {
126     scan->leave = True;
127     scan->inferior = (e->xcrossing.detail == NotifyInferior);
128   } else if ((e->type == EnterNotify) && (e->xcrossing.mode == NotifyUngrab)) {
129     scan->enter = True;
130   }
131
132   return False;
133 }
134
135 Blackbox *blackbox;
136
137
138 Blackbox::Blackbox(char **m_argv, char *dpy_name, char *rc, char *menu)
139   : BaseDisplay(m_argv[0], dpy_name) {
140   if (! XSupportsLocale())
141     fprintf(stderr, "X server does not support locale\n");
142
143   if (XSetLocaleModifiers("") == NULL)
144     fprintf(stderr, "cannot set locale modifiers\n");
145
146   ::blackbox = this;
147   argv = m_argv;
148   if (! rc) rc = "~/.openbox/rc";
149   rc_file = expandTilde(rc);
150   config.setFile(rc_file);  
151   if (! menu) menu = "~/.openbox/menu";
152   menu_file = expandTilde(menu);
153
154   no_focus = False;
155
156   resource.auto_raise_delay.tv_sec = resource.auto_raise_delay.tv_usec = 0;
157
158   active_screen = 0;
159   focused_window = (BlackboxWindow *) 0;
160
161   XrmInitialize();
162   load_rc();
163
164   xatom = new XAtom(this);
165
166   cursor.session = XCreateFontCursor(getXDisplay(), XC_left_ptr);
167   cursor.move = XCreateFontCursor(getXDisplay(), XC_fleur);
168   cursor.ll_angle = XCreateFontCursor(getXDisplay(), XC_ll_angle);
169   cursor.lr_angle = XCreateFontCursor(getXDisplay(), XC_lr_angle);
170
171   for (unsigned int i = 0; i < getNumberOfScreens(); i++) {
172     BScreen *screen = new BScreen(this, i);
173
174     if (! screen->isScreenManaged()) {
175       delete screen;
176       continue;
177     }
178
179     screenList.push_back(screen);
180   }
181
182   if (screenList.empty()) {
183     fprintf(stderr,
184             i18n(blackboxSet, blackboxNoManagableScreens,
185               "Blackbox::Blackbox: no managable screens found, aborting.\n"));
186     ::exit(3);
187   }
188
189   // save current settings and default values
190   save_rc();
191
192   // set the screen with mouse to the first managed screen
193   active_screen = screenList.front();
194   setFocusedWindow(0);
195
196   XSynchronize(getXDisplay(), False);
197   XSync(getXDisplay(), False);
198
199   reconfigure_wait = reread_menu_wait = False;
200
201   timer = new BTimer(this, this);
202   timer->setTimeout(0l);
203 }
204
205
206 Blackbox::~Blackbox(void) {
207   std::for_each(screenList.begin(), screenList.end(), PointerAssassin());
208
209   std::for_each(menuTimestamps.begin(), menuTimestamps.end(),
210                 PointerAssassin());
211
212   delete xatom;
213
214   delete timer;
215 }
216
217
218 void Blackbox::process_event(XEvent *e) {
219   switch (e->type) {
220   case ButtonPress: {
221     // strip the lock key modifiers
222     e->xbutton.state &= ~(NumLockMask | ScrollLockMask | LockMask);
223
224     last_time = e->xbutton.time;
225
226     BlackboxWindow *win = (BlackboxWindow *) 0;
227     Basemenu *menu = (Basemenu *) 0;
228     Slit *slit = (Slit *) 0;
229     Toolbar *tbar = (Toolbar *) 0;
230     BScreen *scrn = (BScreen *) 0;
231
232     if ((win = searchWindow(e->xbutton.window))) {
233       win->buttonPressEvent(&e->xbutton);
234
235       /* XXX: is this sane on low colour desktops? */
236       if (e->xbutton.button == 1)
237         win->installColormap(True);
238     } else if ((menu = searchMenu(e->xbutton.window))) {
239       menu->buttonPressEvent(&e->xbutton);
240     } else if ((slit = searchSlit(e->xbutton.window))) {
241       slit->buttonPressEvent(&e->xbutton);
242     } else if ((tbar = searchToolbar(e->xbutton.window))) {
243       tbar->buttonPressEvent(&e->xbutton);
244     } else if ((scrn = searchScreen(e->xbutton.window))) {
245       scrn->buttonPressEvent(&e->xbutton);
246       if (active_screen != scrn) {
247         active_screen = scrn;
248         // first, set no focus window on the old screen
249         setFocusedWindow(0);
250         // and move focus to this screen
251         setFocusedWindow(0);
252       }
253     }
254     break;
255   }
256
257   case ButtonRelease: {
258     // strip the lock key modifiers
259     e->xbutton.state &= ~(NumLockMask | ScrollLockMask | LockMask);
260
261     last_time = e->xbutton.time;
262
263     BlackboxWindow *win = (BlackboxWindow *) 0;
264     Basemenu *menu = (Basemenu *) 0;
265     Toolbar *tbar = (Toolbar *) 0;
266
267     if ((win = searchWindow(e->xbutton.window)))
268       win->buttonReleaseEvent(&e->xbutton);
269     else if ((menu = searchMenu(e->xbutton.window)))
270       menu->buttonReleaseEvent(&e->xbutton);
271     else if ((tbar = searchToolbar(e->xbutton.window)))
272       tbar->buttonReleaseEvent(&e->xbutton);
273
274     break;
275   }
276
277   case ConfigureRequest: {
278     // compress configure requests...
279     XEvent realevent;
280     unsigned int i = 0;
281     while(XCheckTypedWindowEvent(getXDisplay(), e->xconfigurerequest.window,
282                                  ConfigureRequest, &realevent)) {
283       i++;
284     }
285     if ( i > 0 )
286       e = &realevent;
287
288     BlackboxWindow *win = (BlackboxWindow *) 0;
289     Slit *slit = (Slit *) 0;
290
291     if ((win = searchWindow(e->xconfigurerequest.window))) {
292       win->configureRequestEvent(&e->xconfigurerequest);
293     } else if ((slit = searchSlit(e->xconfigurerequest.window))) {
294       slit->configureRequestEvent(&e->xconfigurerequest);
295     } else {
296       if (validateWindow(e->xconfigurerequest.window)) {
297         XWindowChanges xwc;
298
299         xwc.x = e->xconfigurerequest.x;
300         xwc.y = e->xconfigurerequest.y;
301         xwc.width = e->xconfigurerequest.width;
302         xwc.height = e->xconfigurerequest.height;
303         xwc.border_width = e->xconfigurerequest.border_width;
304         xwc.sibling = e->xconfigurerequest.above;
305         xwc.stack_mode = e->xconfigurerequest.detail;
306
307         XConfigureWindow(getXDisplay(), e->xconfigurerequest.window,
308                          e->xconfigurerequest.value_mask, &xwc);
309       }
310     }
311
312     break;
313   }
314
315   case MapRequest: {
316 #ifdef    DEBUG
317     fprintf(stderr, "Blackbox::process_event(): MapRequest for 0x%lx\n",
318             e->xmaprequest.window);
319 #endif // DEBUG
320
321     BlackboxWindow *win = searchWindow(e->xmaprequest.window);
322
323     if (! win) {
324       BScreen *screen = searchScreen(e->xmaprequest.parent);
325
326       if (! screen) {
327         /*
328           we got a map request for a window who's parent isn't root. this
329           can happen in only one circumstance:
330
331             a client window unmapped a managed window, and then remapped it
332             somewhere between unmapping the client window and reparenting it
333             to root.
334
335           regardless of how it happens, we need to find the screen that
336           the window is on
337         */
338         XWindowAttributes wattrib;
339         if (! XGetWindowAttributes(getXDisplay(), e->xmaprequest.window,
340                                    &wattrib)) {
341           // failed to get the window attributes, perhaps the window has
342           // now been destroyed?
343           break;
344         }
345
346         screen = searchScreen(wattrib.root);
347         assert(screen != 0); // this should never happen
348       }
349
350       screen->manageWindow(e->xmaprequest.window);
351     }
352
353     break;
354   }
355
356   case UnmapNotify: {
357     BlackboxWindow *win = (BlackboxWindow *) 0;
358     Slit *slit = (Slit *) 0;
359
360     if ((win = searchWindow(e->xunmap.window))) {
361       win->unmapNotifyEvent(&e->xunmap);
362     } else if ((slit = searchSlit(e->xunmap.window))) {
363       slit->unmapNotifyEvent(&e->xunmap);
364     }
365
366     break;
367   }
368
369   case DestroyNotify: {
370     BlackboxWindow *win = (BlackboxWindow *) 0;
371     Slit *slit = (Slit *) 0;
372     BWindowGroup *group = (BWindowGroup *) 0;
373
374     if ((win = searchWindow(e->xdestroywindow.window))) {
375       win->destroyNotifyEvent(&e->xdestroywindow);
376     } else if ((slit = searchSlit(e->xdestroywindow.window))) {
377       slit->removeClient(e->xdestroywindow.window, False);
378     } else if ((group = searchGroup(e->xdestroywindow.window))) {
379       delete group;
380     }
381
382     break;
383   }
384
385   case ReparentNotify: {
386     /*
387       this event is quite rare and is usually handled in unmapNotify
388       however, if the window is unmapped when the reparent event occurs
389       the window manager never sees it because an unmap event is not sent
390       to an already unmapped window.
391     */
392     BlackboxWindow *win = searchWindow(e->xreparent.window);
393     if (win) {
394       win->reparentNotifyEvent(&e->xreparent);
395     } else {
396       Slit *slit = searchSlit(e->xreparent.window);
397       if (slit && slit->getWindowID() != e->xreparent.parent)
398         slit->removeClient(e->xreparent.window, True);
399     }
400     break;
401   }
402
403   case MotionNotify: {
404     // motion notify compression...
405     XEvent realevent;
406     unsigned int i = 0;
407     while (XCheckTypedWindowEvent(getXDisplay(), e->xmotion.window,
408                                   MotionNotify, &realevent)) {
409       i++;
410     }
411
412     // if we have compressed some motion events, use the last one
413     if ( i > 0 )
414       e = &realevent;
415
416     // strip the lock key modifiers
417     e->xbutton.state &= ~(NumLockMask | ScrollLockMask | LockMask);
418
419     last_time = e->xmotion.time;
420
421     BlackboxWindow *win = (BlackboxWindow *) 0;
422     Basemenu *menu = (Basemenu *) 0;
423
424     if ((win = searchWindow(e->xmotion.window)))
425       win->motionNotifyEvent(&e->xmotion);
426     else if ((menu = searchMenu(e->xmotion.window)))
427       menu->motionNotifyEvent(&e->xmotion);
428
429     break;
430   }
431
432   case PropertyNotify: {
433     last_time = e->xproperty.time;
434
435     if (e->xproperty.state != PropertyDelete) {
436       BlackboxWindow *win = searchWindow(e->xproperty.window);
437
438       if (win)
439         win->propertyNotifyEvent(e->xproperty.atom);
440     }
441
442     break;
443   }
444
445   case EnterNotify: {
446     last_time = e->xcrossing.time;
447
448     BScreen *screen = (BScreen *) 0;
449     BlackboxWindow *win = (BlackboxWindow *) 0;
450     Basemenu *menu = (Basemenu *) 0;
451     Toolbar *tbar = (Toolbar *) 0;
452     Slit *slit = (Slit *) 0;
453
454     if (e->xcrossing.mode == NotifyGrab) break;
455
456     XEvent dummy;
457     scanargs sa;
458     sa.w = e->xcrossing.window;
459     sa.enter = sa.leave = False;
460     XCheckIfEvent(getXDisplay(), &dummy, queueScanner, (char *) &sa);
461
462     if ((e->xcrossing.window == e->xcrossing.root) &&
463         (screen = searchScreen(e->xcrossing.window))) {
464       screen->getImageControl()->installRootColormap();
465     } else if ((win = searchWindow(e->xcrossing.window))) {
466       if (win->getScreen()->isSloppyFocus() &&
467           (! win->isFocused()) && (! no_focus)) {
468         if (((! sa.leave) || sa.inferior) && win->isVisible()) {
469           if (win->setInputFocus())
470             win->installColormap(True); // XXX: shouldnt we honour no install?
471         }
472       }
473     } else if ((menu = searchMenu(e->xcrossing.window))) {
474       menu->enterNotifyEvent(&e->xcrossing);
475     } else if ((tbar = searchToolbar(e->xcrossing.window))) {
476       tbar->enterNotifyEvent(&e->xcrossing);
477     } else if ((slit = searchSlit(e->xcrossing.window))) {
478       slit->enterNotifyEvent(&e->xcrossing);
479     }
480     break;
481   }
482
483   case LeaveNotify: {
484     last_time = e->xcrossing.time;
485
486     BlackboxWindow *win = (BlackboxWindow *) 0;
487     Basemenu *menu = (Basemenu *) 0;
488     Toolbar *tbar = (Toolbar *) 0;
489     Slit *slit = (Slit *) 0;
490
491     if ((menu = searchMenu(e->xcrossing.window)))
492       menu->leaveNotifyEvent(&e->xcrossing);
493     else if ((win = searchWindow(e->xcrossing.window)))
494       win->installColormap(False);
495     else if ((tbar = searchToolbar(e->xcrossing.window)))
496       tbar->leaveNotifyEvent(&e->xcrossing);
497     else if ((slit = searchSlit(e->xcrossing.window)))
498       slit->leaveNotifyEvent(&e->xcrossing);
499     break;
500   }
501
502   case Expose: {
503     // compress expose events
504     XEvent realevent;
505     unsigned int i = 0;
506     int ex1, ey1, ex2, ey2;
507     ex1 = e->xexpose.x;
508     ey1 = e->xexpose.y;
509     ex2 = ex1 + e->xexpose.width - 1;
510     ey2 = ey1 + e->xexpose.height - 1;
511     while (XCheckTypedWindowEvent(getXDisplay(), e->xexpose.window,
512                                   Expose, &realevent)) {
513       i++;
514
515       // merge expose area
516       ex1 = std::min(realevent.xexpose.x, ex1);
517       ey1 = std::min(realevent.xexpose.y, ey1);
518       ex2 = std::max(realevent.xexpose.x + realevent.xexpose.width - 1, ex2);
519       ey2 = std::max(realevent.xexpose.y + realevent.xexpose.height - 1, ey2);
520     }
521     if ( i > 0 )
522       e = &realevent;
523
524     // use the merged area
525     e->xexpose.x = ex1;
526     e->xexpose.y = ey1;
527     e->xexpose.width = ex2 - ex1 + 1;
528     e->xexpose.height = ey2 - ey1 + 1;
529
530     BlackboxWindow *win = (BlackboxWindow *) 0;
531     Basemenu *menu = (Basemenu *) 0;
532     Toolbar *tbar = (Toolbar *) 0;
533
534     if ((win = searchWindow(e->xexpose.window)))
535       win->exposeEvent(&e->xexpose);
536     else if ((menu = searchMenu(e->xexpose.window)))
537       menu->exposeEvent(&e->xexpose);
538     else if ((tbar = searchToolbar(e->xexpose.window)))
539       tbar->exposeEvent(&e->xexpose);
540
541     break;
542   }
543
544   case KeyPress: {
545     Toolbar *tbar = searchToolbar(e->xkey.window);
546
547     if (tbar && tbar->isEditing())
548       tbar->keyPressEvent(&e->xkey);
549
550     break;
551   }
552
553   case ColormapNotify: {
554     BScreen *screen = searchScreen(e->xcolormap.window);
555
556     if (screen)
557       screen->setRootColormapInstalled((e->xcolormap.state ==
558                                         ColormapInstalled) ? True : False);
559
560     break;
561   }
562
563   case FocusIn: {
564     if (e->xfocus.detail != NotifyNonlinear &&
565         e->xfocus.detail != NotifyAncestor) {
566       /*
567         don't process FocusIns when:
568         1. the new focus window isn't an ancestor or inferior of the old
569         focus window (NotifyNonlinear)
570         make sure to allow the FocusIn when the old focus window was an
571         ancestor but didn't have a parent, such as root (NotifyAncestor)
572       */
573       break;
574     }
575
576     BlackboxWindow *win = searchWindow(e->xfocus.window);
577     if (win) {
578       if (! win->isFocused())
579         win->setFocusFlag(True);
580
581       /*
582         set the event window to None.  when the FocusOut event handler calls
583         this function recursively, it uses this as an indication that focus
584         has moved to a known window.
585       */
586       e->xfocus.window = None;
587     }
588
589     break;
590   }
591
592   case FocusOut: {
593     if (e->xfocus.detail != NotifyNonlinear) {
594       /*
595         don't process FocusOuts when:
596         2. the new focus window isn't an ancestor or inferior of the old
597         focus window (NotifyNonlinear)
598       */
599       break;
600     }
601
602     BlackboxWindow *win = searchWindow(e->xfocus.window);
603     if (win && win->isFocused()) {
604       /*
605         before we mark "win" as unfocused, we need to verify that focus is
606         going to a known location, is in a known location, or set focus
607         to a known location.
608       */
609
610       XEvent event;
611       // don't check the current focus if FocusOut was generated during a grab
612       bool check_focus = (e->xfocus.mode == NotifyNormal);
613
614       /*
615         First, check if there is a pending FocusIn event waiting.  if there
616         is, process it and determine if focus has moved to another window
617         (the FocusIn event handler sets the window in the event
618         structure to None to indicate this).
619       */
620       if (XCheckTypedEvent(getXDisplay(), FocusIn, &event)) {
621
622         process_event(&event);
623         if (event.xfocus.window == None) {
624           // focus has moved
625           check_focus = False;
626         }
627       }
628
629       if (check_focus) {
630         /*
631           Second, we query the X server for the current input focus.
632           to make sure that we keep a consistent state.
633         */
634         BlackboxWindow *focus;
635         Window w;
636         int revert;
637         XGetInputFocus(getXDisplay(), &w, &revert);
638         focus = searchWindow(w);
639         if (focus) {
640           /*
641             focus got from "win" to "focus" under some very strange
642             circumstances, and we need to make sure that the focus indication
643             is correct.
644           */
645           setFocusedWindow(focus);
646         } else {
647           // we have no idea where focus went... so we set it to somewhere
648           setFocusedWindow(0);
649         }
650       }
651     }
652
653     break;
654   }
655
656   case ClientMessage: {
657     if (e->xclient.format == 32) {
658       if (e->xclient.message_type == xatom->getAtom(XAtom::wm_change_state)) {
659         BlackboxWindow *win = searchWindow(e->xclient.window);
660         if (! win || ! win->validateClient()) return;
661
662         if (e->xclient.data.l[0] == IconicState)
663           win->iconify();
664         if (e->xclient.data.l[0] == NormalState)
665           win->deiconify();
666       } else if(e->xclient.message_type == getBlackboxChangeWorkspaceAtom()) {
667         BScreen *screen = searchScreen(e->xclient.window);
668
669         unsigned int workspace = e->xclient.data.l[0];
670         if (screen && workspace < screen->getWorkspaceCount())
671           screen->changeWorkspaceID(workspace);
672       } else if (e->xclient.message_type == getBlackboxChangeWindowFocusAtom()) {
673         BlackboxWindow *win = searchWindow(e->xclient.window);
674
675         if (win && win->isVisible() && win->setInputFocus())
676           win->installColormap(True);
677       } else if (e->xclient.message_type == getBlackboxCycleWindowFocusAtom()) {
678         BScreen *screen = searchScreen(e->xclient.window);
679
680         if (screen) {
681           if (! e->xclient.data.l[0])
682             screen->prevFocus();
683           else
684             screen->nextFocus();
685         }
686       } else if (e->xclient.message_type == getBlackboxChangeAttributesAtom()) {
687         BlackboxWindow *win = searchWindow(e->xclient.window);
688
689         if (win && win->validateClient()) {
690           BlackboxHints net;
691           net.flags = e->xclient.data.l[0];
692           net.attrib = e->xclient.data.l[1];
693           net.workspace = e->xclient.data.l[2];
694           net.stack = e->xclient.data.l[3];
695           net.decoration = e->xclient.data.l[4];
696
697           win->changeBlackboxHints(&net);
698         }
699       }
700     }
701
702     break;
703   }
704
705   case NoExpose:
706   case ConfigureNotify:
707   case MapNotify:
708     break; // not handled, just ignore
709
710   default: {
711 #ifdef    SHAPE
712     if (e->type == getShapeEventBase()) {
713       XShapeEvent *shape_event = (XShapeEvent *) e;
714       BlackboxWindow *win = searchWindow(e->xany.window);
715
716       if (win)
717         win->shapeEvent(shape_event);
718     }
719 #endif // SHAPE
720   }
721   } // switch
722 }
723
724
725 bool Blackbox::handleSignal(int sig) {
726   switch (sig) {
727   case SIGHUP:
728   case SIGUSR1:
729     reconfigure();
730     break;
731
732   case SIGUSR2:
733     rereadMenu();
734     break;
735
736   case SIGPIPE:
737   case SIGSEGV:
738   case SIGFPE:
739   case SIGINT:
740   case SIGTERM:
741     shutdown();
742
743   default:
744     return False;
745   }
746
747   return True;
748 }
749
750
751 bool Blackbox::validateWindow(Window window) {
752   XEvent event;
753   if (XCheckTypedWindowEvent(getXDisplay(), window, DestroyNotify, &event)) {
754     XPutBackEvent(getXDisplay(), &event);
755
756     return False;
757   }
758
759   return True;
760 }
761
762
763 BScreen *Blackbox::searchScreen(Window window) {
764   ScreenList::iterator it = screenList.begin();
765
766   for (; it != screenList.end(); ++it) {
767     BScreen *s = *it;
768     if (s->getRootWindow() == window)
769       return s;
770   }
771
772   return (BScreen *) 0;
773 }
774
775
776 BlackboxWindow *Blackbox::searchWindow(Window window) {
777   WindowLookup::iterator it = windowSearchList.find(window);
778   if (it != windowSearchList.end())
779     return it->second;
780
781   return (BlackboxWindow*) 0;
782 }
783
784
785 BWindowGroup *Blackbox::searchGroup(Window window) {
786   GroupLookup::iterator it = groupSearchList.find(window);
787   if (it != groupSearchList.end())
788     return it->second;
789
790   return (BWindowGroup *) 0;
791 }
792
793
794 Basemenu *Blackbox::searchMenu(Window window) {
795   MenuLookup::iterator it = menuSearchList.find(window);
796   if (it != menuSearchList.end())
797     return it->second;
798
799   return (Basemenu*) 0;
800 }
801
802
803 Toolbar *Blackbox::searchToolbar(Window window) {
804   ToolbarLookup::iterator it = toolbarSearchList.find(window);
805   if (it != toolbarSearchList.end())
806     return it->second;
807
808   return (Toolbar*) 0;
809 }
810
811
812 Slit *Blackbox::searchSlit(Window window) {
813   SlitLookup::iterator it = slitSearchList.find(window);
814   if (it != slitSearchList.end())
815     return it->second;
816
817   return (Slit*) 0;
818 }
819
820
821 void Blackbox::saveWindowSearch(Window window, BlackboxWindow *data) {
822   windowSearchList.insert(WindowLookupPair(window, data));
823 }
824
825
826 void Blackbox::saveGroupSearch(Window window, BWindowGroup *data) {
827   groupSearchList.insert(GroupLookupPair(window, data));
828 }
829
830
831 void Blackbox::saveMenuSearch(Window window, Basemenu *data) {
832   menuSearchList.insert(MenuLookupPair(window, data));
833 }
834
835
836 void Blackbox::saveToolbarSearch(Window window, Toolbar *data) {
837   toolbarSearchList.insert(ToolbarLookupPair(window, data));
838 }
839
840
841 void Blackbox::saveSlitSearch(Window window, Slit *data) {
842   slitSearchList.insert(SlitLookupPair(window, data));
843 }
844
845
846 void Blackbox::removeWindowSearch(Window window) {
847   windowSearchList.erase(window);
848 }
849
850
851 void Blackbox::removeGroupSearch(Window window) {
852   groupSearchList.erase(window);
853 }
854
855
856 void Blackbox::removeMenuSearch(Window window) {
857   menuSearchList.erase(window);
858 }
859
860
861 void Blackbox::removeToolbarSearch(Window window) {
862   toolbarSearchList.erase(window);
863 }
864
865
866 void Blackbox::removeSlitSearch(Window window) {
867   slitSearchList.erase(window);
868 }
869
870
871 void Blackbox::restart(const char *prog) {
872   shutdown();
873
874   if (prog) {
875     execlp(prog, prog, NULL);
876     perror(prog);
877   }
878
879   // fall back in case the above execlp doesn't work
880   execvp(argv[0], argv);
881   string name = basename(argv[0]);
882   execvp(name.c_str(), argv);
883 }
884
885
886 void Blackbox::shutdown(void) {
887   BaseDisplay::shutdown();
888
889   XSetInputFocus(getXDisplay(), PointerRoot, None, CurrentTime);
890
891   std::for_each(screenList.begin(), screenList.end(),
892                 std::mem_fun(&BScreen::shutdown));
893
894   XSync(getXDisplay(), False);
895 }
896
897
898 /*
899  * Save all values as they are so that the defaults will be written to the rc
900  * file
901  */
902 void Blackbox::save_rc(void) {
903   config.setAutoSave(false);
904
905   config.setValue("session.colorsPerChannel", resource.colors_per_channel);
906   config.setValue("session.doubleClickInterval",
907                   resource.double_click_interval);
908   config.setValue("session.autoRaiseDelay",
909                   ((resource.auto_raise_delay.tv_sec * 1000) +
910                    (resource.auto_raise_delay.tv_usec / 1000)));
911   config.setValue("session.cacheLife", resource.cache_life / 60000);
912   config.setValue("session.cacheMax", resource.cache_max);
913   config.setValue("session.styleFile", resource.style_file);
914   config.setValue("session.titlebarLayout", resource.titlebar_layout);
915   
916   std::for_each(screenList.begin(), screenList.end(),
917                 std::mem_fun(&BScreen::save_rc));
918  
919   config.setAutoSave(true);
920   config.save();
921 }
922
923
924 void Blackbox::load_rc(void) {
925   if (! config.load())
926         config.create();
927   
928   string s;
929
930   if (! config.getValue("session.colorsPerChannel",
931                         resource.colors_per_channel))
932     resource.colors_per_channel = 4;
933   if (resource.colors_per_channel < 2) resource.colors_per_channel = 2;
934   else if (resource.colors_per_channel > 6) resource.colors_per_channel = 6;
935
936   if (config.getValue("session.styleFile", s))
937     resource.style_file = expandTilde(s);
938   else
939     resource.style_file = DEFAULTSTYLE;
940
941   if (! config.getValue("session.doubleClickInterval",
942                        resource.double_click_interval));
943     resource.double_click_interval = 250;
944
945   if (! config.getValue("session.autoRaiseDelay",
946                        resource.auto_raise_delay.tv_usec))
947     resource.auto_raise_delay.tv_usec = 400;
948   resource.auto_raise_delay.tv_sec = resource.auto_raise_delay.tv_usec / 1000;
949   resource.auto_raise_delay.tv_usec -=
950     (resource.auto_raise_delay.tv_sec * 1000);
951   resource.auto_raise_delay.tv_usec *= 1000;
952
953   if (! config.getValue("session.cacheLife", resource.cache_life))
954     resource.cache_life = 5;
955   resource.cache_life *= 60000;
956
957   if (! config.getValue("session.cacheMax", resource.cache_max))
958     resource.cache_max = 200;
959   
960   if (! config.getValue("session.titlebarLayout", resource.titlebar_layout))
961     resource.titlebar_layout = "ILMC";
962 }
963
964
965 void Blackbox::reconfigure(void) {
966   reconfigure_wait = True;
967
968   if (! timer->isTiming()) timer->start();
969 }
970
971
972 void Blackbox::real_reconfigure(void) {
973   load_rc();
974   
975   std::for_each(menuTimestamps.begin(), menuTimestamps.end(),
976                 PointerAssassin());
977   menuTimestamps.clear();
978
979   gcCache()->purge();
980
981   std::for_each(screenList.begin(), screenList.end(),
982                 std::mem_fun(&BScreen::reconfigure));
983 }
984
985
986 void Blackbox::checkMenu(void) {
987   bool reread = False;
988   MenuTimestampList::iterator it = menuTimestamps.begin();
989   for(; it != menuTimestamps.end(); ++it) {
990     MenuTimestamp *tmp = *it;
991     struct stat buf;
992
993     if (! stat(tmp->filename.c_str(), &buf)) {
994       if (tmp->timestamp != buf.st_ctime)
995         reread = True;
996     } else {
997       reread = True;
998     }
999   }
1000
1001   if (reread) rereadMenu();
1002 }
1003
1004
1005 void Blackbox::rereadMenu(void) {
1006   reread_menu_wait = True;
1007
1008   if (! timer->isTiming()) timer->start();
1009 }
1010
1011
1012 void Blackbox::real_rereadMenu(void) {
1013   std::for_each(menuTimestamps.begin(), menuTimestamps.end(),
1014                 PointerAssassin());
1015   menuTimestamps.clear();
1016
1017   std::for_each(screenList.begin(), screenList.end(),
1018                 std::mem_fun(&BScreen::rereadMenu));
1019 }
1020
1021
1022 void Blackbox::saveStyleFilename(const string& filename) {
1023   assert(! filename.empty());
1024   resource.style_file = filename;
1025   config.setValue("session.styleFile", resource.style_file);
1026 }
1027
1028
1029 void Blackbox::addMenuTimestamp(const string& filename) {
1030   assert(! filename.empty());
1031   bool found = False;
1032
1033   MenuTimestampList::iterator it = menuTimestamps.begin();
1034   for (; it != menuTimestamps.end() && ! found; ++it) {
1035     if ((*it)->filename == filename) found = True;
1036   }
1037   if (! found) {
1038     struct stat buf;
1039
1040     if (! stat(filename.c_str(), &buf)) {
1041       MenuTimestamp *ts = new MenuTimestamp;
1042
1043       ts->filename = filename;
1044       ts->timestamp = buf.st_ctime;
1045
1046       menuTimestamps.push_back(ts);
1047     }
1048   }
1049 }
1050
1051
1052 void Blackbox::timeout(void) {
1053   if (reconfigure_wait)
1054     real_reconfigure();
1055
1056   if (reread_menu_wait)
1057     real_rereadMenu();
1058
1059   reconfigure_wait = reread_menu_wait = False;
1060 }
1061
1062
1063 void Blackbox::setFocusedWindow(BlackboxWindow *win) {
1064   if (focused_window && focused_window == win) // nothing to do
1065     return;
1066
1067   BScreen *old_screen = 0;
1068
1069   if (focused_window) {
1070     focused_window->setFocusFlag(False);
1071     old_screen = focused_window->getScreen();
1072   }
1073
1074   if (win && ! win->isIconic()) {
1075     // the active screen is the one with the last focused window...
1076     // this will keep focus on this screen no matter where the mouse goes,
1077     // so multihead keybindings will continue to work on that screen until the
1078     // user focuses a window on a different screen.
1079     active_screen = win->getScreen();
1080     focused_window = win;
1081   } else {
1082     focused_window = 0;
1083     if (! old_screen) {
1084       if (active_screen) {
1085         // set input focus to the toolbar of the screen with mouse
1086         XSetInputFocus(getXDisplay(),
1087                        active_screen->getRootWindow(),
1088                        RevertToPointerRoot, CurrentTime);
1089       } else {
1090         // set input focus to the toolbar of the first managed screen
1091         XSetInputFocus(getXDisplay(),
1092                        screenList.front()->getRootWindow(),
1093                        RevertToPointerRoot, CurrentTime);
1094       }
1095     } else {
1096       // set input focus to the toolbar of the last screen
1097       XSetInputFocus(getXDisplay(), old_screen->getRootWindow(),
1098                      RevertToPointerRoot, CurrentTime);
1099     }
1100   }
1101
1102   if (active_screen && active_screen->isScreenManaged()) {
1103     active_screen->getToolbar()->redrawWindowLabel(True);
1104     active_screen->updateNetizenWindowFocus();
1105   }
1106
1107   if (old_screen && old_screen != active_screen) {
1108     old_screen->getToolbar()->redrawWindowLabel(True);
1109     old_screen->updateNetizenWindowFocus();
1110   }
1111 }