use openbox/ dir for rc file and menu file. turn menu into a command line option...
[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/Xresource.h>
32 #include <X11/Xatom.h>
33 #include <X11/cursorfont.h>
34 #include <X11/keysym.h>
35
36 #ifdef    SHAPE
37 #include <X11/extensions/shape.h>
38 #endif // SHAPE
39
40 #ifdef    HAVE_STDIO_H
41 #  include <stdio.h>
42 #endif // HAVE_STDIO_H
43
44 #ifdef HAVE_STDLIB_H
45 #  include <stdlib.h>
46 #endif // HAVE_STDLIB_H
47
48 #ifdef HAVE_STRING_H
49 #  include <string.h>
50 #endif // HAVE_STRING_H
51
52 #ifdef    HAVE_UNISTD_H
53 #  include <sys/types.h>
54 #  include <unistd.h>
55 #endif // HAVE_UNISTD_H
56
57 #ifdef    HAVE_SYS_PARAM_H
58 #  include <sys/param.h>
59 #endif // HAVE_SYS_PARAM_H
60
61 #ifdef    HAVE_SYS_SELECT_H
62 #  include <sys/select.h>
63 #endif // HAVE_SYS_SELECT_H
64
65 #ifdef    HAVE_SIGNAL_H
66 #  include <signal.h>
67 #endif // HAVE_SIGNAL_H
68
69 #ifdef    HAVE_SYS_SIGNAL_H
70 #  include <sys/signal.h>
71 #endif // HAVE_SYS_SIGNAL_H
72
73 #ifdef    HAVE_SYS_STAT_H
74 #  include <sys/types.h>
75 #  include <sys/stat.h>
76 #endif // HAVE_SYS_STAT_H
77
78 #ifdef    TIME_WITH_SYS_TIME
79 #  include <sys/time.h>
80 #  include <time.h>
81 #else // !TIME_WITH_SYS_TIME
82 #  ifdef    HAVE_SYS_TIME_H
83 #    include <sys/time.h>
84 #  else // !HAVE_SYS_TIME_H
85 #    include <time.h>
86 #  endif // HAVE_SYS_TIME_H
87 #endif // TIME_WITH_SYS_TIME
88
89 #ifdef    HAVE_LIBGEN_H
90 #  include <libgen.h>
91 #endif // HAVE_LIBGEN_H
92 }
93
94 #include <algorithm>
95 #include <string>
96 using std::string;
97
98 #include "i18n.hh"
99 #include "blackbox.hh"
100 #include "Basemenu.hh"
101 #include "Clientmenu.hh"
102 #include "GCCache.hh"
103 #include "Image.hh"
104 #include "Rootmenu.hh"
105 #include "Screen.hh"
106 #include "Slit.hh"
107 #include "Toolbar.hh"
108 #include "Util.hh"
109 #include "Window.hh"
110 #include "Workspace.hh"
111 #include "Workspacemenu.hh"
112
113
114 // X event scanner for enter/leave notifies - adapted from twm
115 struct scanargs {
116   Window w;
117   bool leave, inferior, enter;
118 };
119
120 static Bool queueScanner(Display *, XEvent *e, char *args) {
121   scanargs *scan = (scanargs *) args;
122   if ((e->type == LeaveNotify) &&
123       (e->xcrossing.window == scan->w) &&
124       (e->xcrossing.mode == NotifyNormal)) {
125     scan->leave = True;
126     scan->inferior = (e->xcrossing.detail == NotifyInferior);
127   } else if ((e->type == EnterNotify) && (e->xcrossing.mode == NotifyUngrab)) {
128     scan->enter = True;
129   }
130
131   return False;
132 }
133
134 Blackbox *blackbox;
135
136
137 Blackbox::Blackbox(char **m_argv, char *dpy_name, char *rc, char *menu)
138   : BaseDisplay(m_argv[0], dpy_name) {
139   if (! XSupportsLocale())
140     fprintf(stderr, "X server does not support locale\n");
141
142   if (XSetLocaleModifiers("") == NULL)
143     fprintf(stderr, "cannot set locale modifiers\n");
144
145   ::blackbox = this;
146   argv = m_argv;
147   if (! rc) rc = "~/.openbox/rc";
148   rc_file = expandTilde(rc);
149   if (! menu) menu = "~/.openbox/menu";
150   menu_file = expandTilde(menu);
151
152   no_focus = False;
153
154   resource.auto_raise_delay.tv_sec = resource.auto_raise_delay.tv_usec = 0;
155
156   active_screen = 0;
157   focused_window = (BlackboxWindow *) 0;
158
159   XrmInitialize();
160   load_rc();
161
162   init_icccm();
163
164   cursor.session = XCreateFontCursor(getXDisplay(), XC_left_ptr);
165   cursor.move = XCreateFontCursor(getXDisplay(), XC_fleur);
166   cursor.ll_angle = XCreateFontCursor(getXDisplay(), XC_ll_angle);
167   cursor.lr_angle = XCreateFontCursor(getXDisplay(), XC_lr_angle);
168
169   for (unsigned int i = 0; i < getNumberOfScreens(); i++) {
170     BScreen *screen = new BScreen(this, i);
171
172     if (! screen->isScreenManaged()) {
173       delete screen;
174       continue;
175     }
176
177     screenList.push_back(screen);
178   }
179
180   if (screenList.empty()) {
181     fprintf(stderr,
182             i18n(blackboxSet, blackboxNoManagableScreens,
183               "Blackbox::Blackbox: no managable screens found, aborting.\n"));
184     ::exit(3);
185   }
186
187   // set the screen with mouse to the first managed screen
188   active_screen = screenList.front();
189   setFocusedWindow(0);
190
191   XSynchronize(getXDisplay(), False);
192   XSync(getXDisplay(), False);
193
194   reconfigure_wait = reread_menu_wait = False;
195
196   timer = new BTimer(this, this);
197   timer->setTimeout(0l);
198 }
199
200
201 Blackbox::~Blackbox(void) {
202   std::for_each(screenList.begin(), screenList.end(), PointerAssassin());
203
204   std::for_each(menuTimestamps.begin(), menuTimestamps.end(),
205                 PointerAssassin());
206
207   delete timer;
208 }
209
210
211 void Blackbox::process_event(XEvent *e) {
212   switch (e->type) {
213   case ButtonPress: {
214     // strip the lock key modifiers
215     e->xbutton.state &= ~(NumLockMask | ScrollLockMask | LockMask);
216
217     last_time = e->xbutton.time;
218
219     BlackboxWindow *win = (BlackboxWindow *) 0;
220     Basemenu *menu = (Basemenu *) 0;
221     Slit *slit = (Slit *) 0;
222     Toolbar *tbar = (Toolbar *) 0;
223     BScreen *scrn = (BScreen *) 0;
224
225     if ((win = searchWindow(e->xbutton.window))) {
226       win->buttonPressEvent(&e->xbutton);
227
228       /* XXX: is this sane on low colour desktops? */
229       if (e->xbutton.button == 1)
230         win->installColormap(True);
231     } else if ((menu = searchMenu(e->xbutton.window))) {
232       menu->buttonPressEvent(&e->xbutton);
233     } else if ((slit = searchSlit(e->xbutton.window))) {
234       slit->buttonPressEvent(&e->xbutton);
235     } else if ((tbar = searchToolbar(e->xbutton.window))) {
236       tbar->buttonPressEvent(&e->xbutton);
237     } else if ((scrn = searchScreen(e->xbutton.window))) {
238       scrn->buttonPressEvent(&e->xbutton);
239       if (active_screen != scrn) {
240         active_screen = scrn;
241         // first, set no focus window on the old screen
242         setFocusedWindow(0);
243         // and move focus to this screen
244         setFocusedWindow(0);
245       }
246     }
247     break;
248   }
249
250   case ButtonRelease: {
251     // strip the lock key modifiers
252     e->xbutton.state &= ~(NumLockMask | ScrollLockMask | LockMask);
253
254     last_time = e->xbutton.time;
255
256     BlackboxWindow *win = (BlackboxWindow *) 0;
257     Basemenu *menu = (Basemenu *) 0;
258     Toolbar *tbar = (Toolbar *) 0;
259
260     if ((win = searchWindow(e->xbutton.window)))
261       win->buttonReleaseEvent(&e->xbutton);
262     else if ((menu = searchMenu(e->xbutton.window)))
263       menu->buttonReleaseEvent(&e->xbutton);
264     else if ((tbar = searchToolbar(e->xbutton.window)))
265       tbar->buttonReleaseEvent(&e->xbutton);
266
267     break;
268   }
269
270   case ConfigureRequest: {
271     // compress configure requests...
272     XEvent realevent;
273     unsigned int i = 0;
274     while(XCheckTypedWindowEvent(getXDisplay(), e->xconfigurerequest.window,
275                                  ConfigureRequest, &realevent)) {
276       i++;
277     }
278     if ( i > 0 )
279       e = &realevent;
280
281     BlackboxWindow *win = (BlackboxWindow *) 0;
282     Slit *slit = (Slit *) 0;
283
284     if ((win = searchWindow(e->xconfigurerequest.window))) {
285       win->configureRequestEvent(&e->xconfigurerequest);
286     } else if ((slit = searchSlit(e->xconfigurerequest.window))) {
287       slit->configureRequestEvent(&e->xconfigurerequest);
288     } else {
289       if (validateWindow(e->xconfigurerequest.window)) {
290         XWindowChanges xwc;
291
292         xwc.x = e->xconfigurerequest.x;
293         xwc.y = e->xconfigurerequest.y;
294         xwc.width = e->xconfigurerequest.width;
295         xwc.height = e->xconfigurerequest.height;
296         xwc.border_width = e->xconfigurerequest.border_width;
297         xwc.sibling = e->xconfigurerequest.above;
298         xwc.stack_mode = e->xconfigurerequest.detail;
299
300         XConfigureWindow(getXDisplay(), e->xconfigurerequest.window,
301                          e->xconfigurerequest.value_mask, &xwc);
302       }
303     }
304
305     break;
306   }
307
308   case MapRequest: {
309 #ifdef    DEBUG
310     fprintf(stderr, "Blackbox::process_event(): MapRequest for 0x%lx\n",
311             e->xmaprequest.window);
312 #endif // DEBUG
313
314     BlackboxWindow *win = searchWindow(e->xmaprequest.window);
315
316     if (! win) {
317       BScreen *screen = searchScreen(e->xmaprequest.parent);
318
319       if (! screen) {
320         /*
321           we got a map request for a window who's parent isn't root. this
322           can happen in only one circumstance:
323
324             a client window unmapped a managed window, and then remapped it
325             somewhere between unmapping the client window and reparenting it
326             to root.
327
328           regardless of how it happens, we need to find the screen that
329           the window is on
330         */
331         XWindowAttributes wattrib;
332         if (! XGetWindowAttributes(getXDisplay(), e->xmaprequest.window,
333                                    &wattrib)) {
334           // failed to get the window attributes, perhaps the window has
335           // now been destroyed?
336           break;
337         }
338
339         screen = searchScreen(wattrib.root);
340         assert(screen != 0); // this should never happen
341       }
342
343       screen->manageWindow(e->xmaprequest.window);
344     }
345
346     break;
347   }
348
349   case UnmapNotify: {
350     BlackboxWindow *win = (BlackboxWindow *) 0;
351     Slit *slit = (Slit *) 0;
352
353     if ((win = searchWindow(e->xunmap.window))) {
354       win->unmapNotifyEvent(&e->xunmap);
355     } else if ((slit = searchSlit(e->xunmap.window))) {
356       slit->unmapNotifyEvent(&e->xunmap);
357     }
358
359     break;
360   }
361
362   case DestroyNotify: {
363     BlackboxWindow *win = (BlackboxWindow *) 0;
364     Slit *slit = (Slit *) 0;
365     BWindowGroup *group = (BWindowGroup *) 0;
366
367     if ((win = searchWindow(e->xdestroywindow.window))) {
368       win->destroyNotifyEvent(&e->xdestroywindow);
369     } else if ((slit = searchSlit(e->xdestroywindow.window))) {
370       slit->removeClient(e->xdestroywindow.window, False);
371     } else if ((group = searchGroup(e->xdestroywindow.window))) {
372       delete group;
373     }
374
375     break;
376   }
377
378   case ReparentNotify: {
379     /*
380       this event is quite rare and is usually handled in unmapNotify
381       however, if the window is unmapped when the reparent event occurs
382       the window manager never sees it because an unmap event is not sent
383       to an already unmapped window.
384     */
385     BlackboxWindow *win = searchWindow(e->xreparent.window);
386     if (win) {
387       win->reparentNotifyEvent(&e->xreparent);
388     } else {
389       Slit *slit = searchSlit(e->xreparent.window);
390       if (slit && slit->getWindowID() != e->xreparent.parent)
391         slit->removeClient(e->xreparent.window, True);
392     }
393     break;
394   }
395
396   case MotionNotify: {
397     // motion notify compression...
398     XEvent realevent;
399     unsigned int i = 0;
400     while (XCheckTypedWindowEvent(getXDisplay(), e->xmotion.window,
401                                   MotionNotify, &realevent)) {
402       i++;
403     }
404
405     // if we have compressed some motion events, use the last one
406     if ( i > 0 )
407       e = &realevent;
408
409     // strip the lock key modifiers
410     e->xbutton.state &= ~(NumLockMask | ScrollLockMask | LockMask);
411
412     last_time = e->xmotion.time;
413
414     BlackboxWindow *win = (BlackboxWindow *) 0;
415     Basemenu *menu = (Basemenu *) 0;
416
417     if ((win = searchWindow(e->xmotion.window)))
418       win->motionNotifyEvent(&e->xmotion);
419     else if ((menu = searchMenu(e->xmotion.window)))
420       menu->motionNotifyEvent(&e->xmotion);
421
422     break;
423   }
424
425   case PropertyNotify: {
426     last_time = e->xproperty.time;
427
428     if (e->xproperty.state != PropertyDelete) {
429       BlackboxWindow *win = searchWindow(e->xproperty.window);
430
431       if (win)
432         win->propertyNotifyEvent(e->xproperty.atom);
433     }
434
435     break;
436   }
437
438   case EnterNotify: {
439     last_time = e->xcrossing.time;
440
441     BScreen *screen = (BScreen *) 0;
442     BlackboxWindow *win = (BlackboxWindow *) 0;
443     Basemenu *menu = (Basemenu *) 0;
444     Toolbar *tbar = (Toolbar *) 0;
445     Slit *slit = (Slit *) 0;
446
447     if (e->xcrossing.mode == NotifyGrab) break;
448
449     XEvent dummy;
450     scanargs sa;
451     sa.w = e->xcrossing.window;
452     sa.enter = sa.leave = False;
453     XCheckIfEvent(getXDisplay(), &dummy, queueScanner, (char *) &sa);
454
455     if ((e->xcrossing.window == e->xcrossing.root) &&
456         (screen = searchScreen(e->xcrossing.window))) {
457       screen->getImageControl()->installRootColormap();
458     } else if ((win = searchWindow(e->xcrossing.window))) {
459       if (win->getScreen()->isSloppyFocus() &&
460           (! win->isFocused()) && (! no_focus)) {
461         if (((! sa.leave) || sa.inferior) && win->isVisible()) {
462           if (win->setInputFocus())
463             win->installColormap(True); // XXX: shouldnt we honour no install?
464         }
465       }
466     } else if ((menu = searchMenu(e->xcrossing.window))) {
467       menu->enterNotifyEvent(&e->xcrossing);
468     } else if ((tbar = searchToolbar(e->xcrossing.window))) {
469       tbar->enterNotifyEvent(&e->xcrossing);
470     } else if ((slit = searchSlit(e->xcrossing.window))) {
471       slit->enterNotifyEvent(&e->xcrossing);
472     }
473     break;
474   }
475
476   case LeaveNotify: {
477     last_time = e->xcrossing.time;
478
479     BlackboxWindow *win = (BlackboxWindow *) 0;
480     Basemenu *menu = (Basemenu *) 0;
481     Toolbar *tbar = (Toolbar *) 0;
482     Slit *slit = (Slit *) 0;
483
484     if ((menu = searchMenu(e->xcrossing.window)))
485       menu->leaveNotifyEvent(&e->xcrossing);
486     else if ((win = searchWindow(e->xcrossing.window)))
487       win->installColormap(False);
488     else if ((tbar = searchToolbar(e->xcrossing.window)))
489       tbar->leaveNotifyEvent(&e->xcrossing);
490     else if ((slit = searchSlit(e->xcrossing.window)))
491       slit->leaveNotifyEvent(&e->xcrossing);
492     break;
493   }
494
495   case Expose: {
496     // compress expose events
497     XEvent realevent;
498     unsigned int i = 0;
499     int ex1, ey1, ex2, ey2;
500     ex1 = e->xexpose.x;
501     ey1 = e->xexpose.y;
502     ex2 = ex1 + e->xexpose.width - 1;
503     ey2 = ey1 + e->xexpose.height - 1;
504     while (XCheckTypedWindowEvent(getXDisplay(), e->xexpose.window,
505                                   Expose, &realevent)) {
506       i++;
507
508       // merge expose area
509       ex1 = std::min(realevent.xexpose.x, ex1);
510       ey1 = std::min(realevent.xexpose.y, ey1);
511       ex2 = std::max(realevent.xexpose.x + realevent.xexpose.width - 1, ex2);
512       ey2 = std::max(realevent.xexpose.y + realevent.xexpose.height - 1, ey2);
513     }
514     if ( i > 0 )
515       e = &realevent;
516
517     // use the merged area
518     e->xexpose.x = ex1;
519     e->xexpose.y = ey1;
520     e->xexpose.width = ex2 - ex1 + 1;
521     e->xexpose.height = ey2 - ey1 + 1;
522
523     BlackboxWindow *win = (BlackboxWindow *) 0;
524     Basemenu *menu = (Basemenu *) 0;
525     Toolbar *tbar = (Toolbar *) 0;
526
527     if ((win = searchWindow(e->xexpose.window)))
528       win->exposeEvent(&e->xexpose);
529     else if ((menu = searchMenu(e->xexpose.window)))
530       menu->exposeEvent(&e->xexpose);
531     else if ((tbar = searchToolbar(e->xexpose.window)))
532       tbar->exposeEvent(&e->xexpose);
533
534     break;
535   }
536
537   case KeyPress: {
538     Toolbar *tbar = searchToolbar(e->xkey.window);
539
540     if (tbar && tbar->isEditing())
541       tbar->keyPressEvent(&e->xkey);
542
543     break;
544   }
545
546   case ColormapNotify: {
547     BScreen *screen = searchScreen(e->xcolormap.window);
548
549     if (screen)
550       screen->setRootColormapInstalled((e->xcolormap.state ==
551                                         ColormapInstalled) ? True : False);
552
553     break;
554   }
555
556   case FocusIn: {
557     if (e->xfocus.detail != NotifyNonlinear) {
558       /*
559         don't process FocusIns when:
560         1. the new focus window isn't an ancestor or inferior of the old
561         focus window (NotifyNonlinear)
562       */
563       break;
564     }
565
566     BlackboxWindow *win = searchWindow(e->xfocus.window);
567     if (win) {
568       if (! win->isFocused())
569         win->setFocusFlag(True);
570
571       /*
572         set the event window to None.  when the FocusOut event handler calls
573         this function recursively, it uses this as an indication that focus
574         has moved to a known window.
575       */
576       e->xfocus.window = None;
577     }
578
579     break;
580   }
581
582   case FocusOut: {
583     if (e->xfocus.detail != NotifyNonlinear) {
584       /*
585         don't process FocusOuts when:
586         2. the new focus window isn't an ancestor or inferior of the old
587         focus window (NotifyNonlinear)
588       */
589       break;
590     }
591
592     BlackboxWindow *win = searchWindow(e->xfocus.window);
593     if (win && win->isFocused()) {
594       /*
595         before we mark "win" as unfocused, we need to verify that focus is
596         going to a known location, is in a known location, or set focus
597         to a known location.
598       */
599
600       XEvent event;
601       // don't check the current focus if FocusOut was generated during a grab
602       bool check_focus = (e->xfocus.mode == NotifyNormal);
603
604       /*
605         First, check if there is a pending FocusIn event waiting.  if there
606         is, process it and determine if focus has moved to another window
607         (the FocusIn event handler sets the window in the event
608         structure to None to indicate this).
609       */
610       if (XCheckTypedEvent(getXDisplay(), FocusIn, &event)) {
611
612         process_event(&event);
613         if (event.xfocus.window == None) {
614           // focus has moved
615           check_focus = False;
616         }
617       }
618
619       if (check_focus) {
620         /*
621           Second, we query the X server for the current input focus.
622           to make sure that we keep a consistent state.
623         */
624         BlackboxWindow *focus;
625         Window w;
626         int revert;
627         XGetInputFocus(getXDisplay(), &w, &revert);
628         focus = searchWindow(w);
629         if (focus) {
630           /*
631             focus got from "win" to "focus" under some very strange
632             circumstances, and we need to make sure that the focus indication
633             is correct.
634           */
635           setFocusedWindow(focus);
636         } else {
637           // we have no idea where focus went... so we set it to somewhere
638           setFocusedWindow(0);
639         }
640       }
641     }
642
643     break;
644   }
645
646   case ClientMessage: {
647     if (e->xclient.format == 32) {
648       if (e->xclient.message_type == getWMChangeStateAtom()) {
649         BlackboxWindow *win = searchWindow(e->xclient.window);
650         if (! win || ! win->validateClient()) return;
651
652         if (e->xclient.data.l[0] == IconicState)
653           win->iconify();
654         if (e->xclient.data.l[0] == NormalState)
655           win->deiconify();
656       } else if(e->xclient.message_type == getBlackboxChangeWorkspaceAtom()) {
657         BScreen *screen = searchScreen(e->xclient.window);
658
659         if (screen && e->xclient.data.l[0] >= 0 &&
660             e->xclient.data.l[0] <
661             static_cast<signed>(screen->getWorkspaceCount()))
662           screen->changeWorkspaceID(e->xclient.data.l[0]);
663       } else if (e->xclient.message_type == getBlackboxChangeWindowFocusAtom()) {
664         BlackboxWindow *win = searchWindow(e->xclient.window);
665
666         if (win && win->isVisible() && win->setInputFocus())
667           win->installColormap(True);
668       } else if (e->xclient.message_type == getBlackboxCycleWindowFocusAtom()) {
669         BScreen *screen = searchScreen(e->xclient.window);
670
671         if (screen) {
672           if (! e->xclient.data.l[0])
673             screen->prevFocus();
674           else
675             screen->nextFocus();
676         }
677       } else if (e->xclient.message_type == getBlackboxChangeAttributesAtom()) {
678         BlackboxWindow *win = searchWindow(e->xclient.window);
679
680         if (win && win->validateClient()) {
681           BlackboxHints net;
682           net.flags = e->xclient.data.l[0];
683           net.attrib = e->xclient.data.l[1];
684           net.workspace = e->xclient.data.l[2];
685           net.stack = e->xclient.data.l[3];
686           net.decoration = e->xclient.data.l[4];
687
688           win->changeBlackboxHints(&net);
689         }
690       }
691     }
692
693     break;
694   }
695
696   case NoExpose:
697   case ConfigureNotify:
698   case MapNotify:
699     break; // not handled, just ignore
700
701   default: {
702 #ifdef    SHAPE
703     if (e->type == getShapeEventBase()) {
704       XShapeEvent *shape_event = (XShapeEvent *) e;
705       BlackboxWindow *win = searchWindow(e->xany.window);
706
707       if (win)
708         win->shapeEvent(shape_event);
709     }
710 #endif // SHAPE
711   }
712   } // switch
713 }
714
715
716 bool Blackbox::handleSignal(int sig) {
717   switch (sig) {
718   case SIGHUP:
719     reconfigure();
720     break;
721
722   case SIGUSR1:
723     reload_rc();
724     break;
725
726   case SIGUSR2:
727     rereadMenu();
728     break;
729
730   case SIGPIPE:
731   case SIGSEGV:
732   case SIGFPE:
733   case SIGINT:
734   case SIGTERM:
735     shutdown();
736
737   default:
738     return False;
739   }
740
741   return True;
742 }
743
744
745 void Blackbox::init_icccm(void) {
746   xa_wm_colormap_windows =
747     XInternAtom(getXDisplay(), "WM_COLORMAP_WINDOWS", False);
748   xa_wm_protocols = XInternAtom(getXDisplay(), "WM_PROTOCOLS", False);
749   xa_wm_state = XInternAtom(getXDisplay(), "WM_STATE", False);
750   xa_wm_change_state = XInternAtom(getXDisplay(), "WM_CHANGE_STATE", False);
751   xa_wm_delete_window = XInternAtom(getXDisplay(), "WM_DELETE_WINDOW", False);
752   xa_wm_take_focus = XInternAtom(getXDisplay(), "WM_TAKE_FOCUS", False);
753   motif_wm_hints = XInternAtom(getXDisplay(), "_MOTIF_WM_HINTS", False);
754
755   blackbox_hints = XInternAtom(getXDisplay(), "_BLACKBOX_HINTS", False);
756   blackbox_attributes =
757     XInternAtom(getXDisplay(), "_BLACKBOX_ATTRIBUTES", False);
758   blackbox_change_attributes =
759     XInternAtom(getXDisplay(), "_BLACKBOX_CHANGE_ATTRIBUTES", False);
760   blackbox_structure_messages =
761     XInternAtom(getXDisplay(), "_BLACKBOX_STRUCTURE_MESSAGES", False);
762   blackbox_notify_startup =
763     XInternAtom(getXDisplay(), "_BLACKBOX_NOTIFY_STARTUP", False);
764   blackbox_notify_window_add =
765     XInternAtom(getXDisplay(), "_BLACKBOX_NOTIFY_WINDOW_ADD", False);
766   blackbox_notify_window_del =
767     XInternAtom(getXDisplay(), "_BLACKBOX_NOTIFY_WINDOW_DEL", False);
768   blackbox_notify_current_workspace =
769     XInternAtom(getXDisplay(), "_BLACKBOX_NOTIFY_CURRENT_WORKSPACE", False);
770   blackbox_notify_workspace_count =
771     XInternAtom(getXDisplay(), "_BLACKBOX_NOTIFY_WORKSPACE_COUNT", False);
772   blackbox_notify_window_focus =
773     XInternAtom(getXDisplay(), "_BLACKBOX_NOTIFY_WINDOW_FOCUS", False);
774   blackbox_notify_window_raise =
775     XInternAtom(getXDisplay(), "_BLACKBOX_NOTIFY_WINDOW_RAISE", False);
776   blackbox_notify_window_lower =
777     XInternAtom(getXDisplay(), "_BLACKBOX_NOTIFY_WINDOW_LOWER", False);
778   blackbox_change_workspace =
779     XInternAtom(getXDisplay(), "_BLACKBOX_CHANGE_WORKSPACE", False);
780   blackbox_change_window_focus =
781     XInternAtom(getXDisplay(), "_BLACKBOX_CHANGE_WINDOW_FOCUS", False);
782   blackbox_cycle_window_focus =
783     XInternAtom(getXDisplay(), "_BLACKBOX_CYCLE_WINDOW_FOCUS", False);
784
785 #ifdef    NEWWMSPEC
786   net_supported = XInternAtom(getXDisplay(), "_NET_SUPPORTED", False);
787   net_client_list = XInternAtom(getXDisplay(), "_NET_CLIENT_LIST", False);
788   net_client_list_stacking =
789     XInternAtom(getXDisplay(), "_NET_CLIENT_LIST_STACKING", False);
790   net_number_of_desktops =
791     XInternAtom(getXDisplay(), "_NET_NUMBER_OF_DESKTOPS", False);
792   net_desktop_geometry =
793     XInternAtom(getXDisplay(), "_NET_DESKTOP_GEOMETRY", False);
794   net_desktop_viewport =
795     XInternAtom(getXDisplay(), "_NET_DESKTOP_VIEWPORT", False);
796   net_current_desktop =
797     XInternAtom(getXDisplay(), "_NET_CURRENT_DESKTOP", False);
798   net_desktop_names = XInternAtom(getXDisplay(), "_NET_DESKTOP_NAMES", False);
799   net_active_window = XInternAtom(getXDisplay(), "_NET_ACTIVE_WINDOW", False);
800   net_workarea = XInternAtom(getXDisplay(), "_NET_WORKAREA", False);
801   net_supporting_wm_check =
802     XInternAtom(getXDisplay(), "_NET_SUPPORTING_WM_CHECK", False);
803   net_virtual_roots = XInternAtom(getXDisplay(), "_NET_VIRTUAL_ROOTS", False);
804   net_close_window = XInternAtom(getXDisplay(), "_NET_CLOSE_WINDOW", False);
805   net_wm_moveresize = XInternAtom(getXDisplay(), "_NET_WM_MOVERESIZE", False);
806   net_properties = XInternAtom(getXDisplay(), "_NET_PROPERTIES", False);
807   net_wm_name = XInternAtom(getXDisplay(), "_NET_WM_NAME", False);
808   net_wm_desktop = XInternAtom(getXDisplay(), "_NET_WM_DESKTOP", False);
809   net_wm_window_type =
810     XInternAtom(getXDisplay(), "_NET_WM_WINDOW_TYPE", False);
811   net_wm_state = XInternAtom(getXDisplay(), "_NET_WM_STATE", False);
812   net_wm_strut = XInternAtom(getXDisplay(), "_NET_WM_STRUT", False);
813   net_wm_icon_geometry =
814     XInternAtom(getXDisplay(), "_NET_WM_ICON_GEOMETRY", False);
815   net_wm_icon = XInternAtom(getXDisplay(), "_NET_WM_ICON", False);
816   net_wm_pid = XInternAtom(getXDisplay(), "_NET_WM_PID", False);
817   net_wm_handled_icons =
818     XInternAtom(getXDisplay(), "_NET_WM_HANDLED_ICONS", False);
819   net_wm_ping = XInternAtom(getXDisplay(), "_NET_WM_PING", False);
820 #endif // NEWWMSPEC
821
822 #ifdef    HAVE_GETPID
823   blackbox_pid = XInternAtom(getXDisplay(), "_BLACKBOX_PID", False);
824 #endif // HAVE_GETPID
825 }
826
827
828 bool Blackbox::validateWindow(Window window) {
829   XEvent event;
830   if (XCheckTypedWindowEvent(getXDisplay(), window, DestroyNotify, &event)) {
831     XPutBackEvent(getXDisplay(), &event);
832
833     return False;
834   }
835
836   return True;
837 }
838
839
840 BScreen *Blackbox::searchScreen(Window window) {
841   ScreenList::iterator it = screenList.begin();
842
843   for (; it != screenList.end(); ++it) {
844     BScreen *s = *it;
845     if (s->getRootWindow() == window)
846       return s;
847   }
848
849   return (BScreen *) 0;
850 }
851
852
853 BlackboxWindow *Blackbox::searchWindow(Window window) {
854   WindowLookup::iterator it = windowSearchList.find(window);
855   if (it != windowSearchList.end())
856     return it->second;
857
858   return (BlackboxWindow*) 0;
859 }
860
861
862 BWindowGroup *Blackbox::searchGroup(Window window) {
863   GroupLookup::iterator it = groupSearchList.find(window);
864   if (it != groupSearchList.end())
865     return it->second;
866
867   return (BWindowGroup *) 0;
868 }
869
870
871 Basemenu *Blackbox::searchMenu(Window window) {
872   MenuLookup::iterator it = menuSearchList.find(window);
873   if (it != menuSearchList.end())
874     return it->second;
875
876   return (Basemenu*) 0;
877 }
878
879
880 Toolbar *Blackbox::searchToolbar(Window window) {
881   ToolbarLookup::iterator it = toolbarSearchList.find(window);
882   if (it != toolbarSearchList.end())
883     return it->second;
884
885   return (Toolbar*) 0;
886 }
887
888
889 Slit *Blackbox::searchSlit(Window window) {
890   SlitLookup::iterator it = slitSearchList.find(window);
891   if (it != slitSearchList.end())
892     return it->second;
893
894   return (Slit*) 0;
895 }
896
897
898 void Blackbox::saveWindowSearch(Window window, BlackboxWindow *data) {
899   windowSearchList.insert(WindowLookupPair(window, data));
900 }
901
902
903 void Blackbox::saveGroupSearch(Window window, BWindowGroup *data) {
904   groupSearchList.insert(GroupLookupPair(window, data));
905 }
906
907
908 void Blackbox::saveMenuSearch(Window window, Basemenu *data) {
909   menuSearchList.insert(MenuLookupPair(window, data));
910 }
911
912
913 void Blackbox::saveToolbarSearch(Window window, Toolbar *data) {
914   toolbarSearchList.insert(ToolbarLookupPair(window, data));
915 }
916
917
918 void Blackbox::saveSlitSearch(Window window, Slit *data) {
919   slitSearchList.insert(SlitLookupPair(window, data));
920 }
921
922
923 void Blackbox::removeWindowSearch(Window window) {
924   windowSearchList.erase(window);
925 }
926
927
928 void Blackbox::removeGroupSearch(Window window) {
929   groupSearchList.erase(window);
930 }
931
932
933 void Blackbox::removeMenuSearch(Window window) {
934   menuSearchList.erase(window);
935 }
936
937
938 void Blackbox::removeToolbarSearch(Window window) {
939   toolbarSearchList.erase(window);
940 }
941
942
943 void Blackbox::removeSlitSearch(Window window) {
944   slitSearchList.erase(window);
945 }
946
947
948 void Blackbox::restart(const char *prog) {
949   shutdown();
950
951   if (prog) {
952     execlp(prog, prog, NULL);
953     perror(prog);
954   }
955
956   // fall back in case the above execlp doesn't work
957   execvp(argv[0], argv);
958   string name = basename(argv[0]);
959   execvp(name.c_str(), argv);
960 }
961
962
963 void Blackbox::shutdown(void) {
964   BaseDisplay::shutdown();
965
966   XSetInputFocus(getXDisplay(), PointerRoot, None, CurrentTime);
967
968   std::for_each(screenList.begin(), screenList.end(),
969                 std::mem_fun(&BScreen::shutdown));
970
971   XSync(getXDisplay(), False);
972
973   save_rc();
974 }
975
976
977 void Blackbox::save_rc(void) {
978   XrmDatabase new_blackboxrc = (XrmDatabase) 0;
979   char rc_string[1024];
980
981   load_rc();
982
983   sprintf(rc_string, "session.colorsPerChannel:  %d",
984           resource.colors_per_channel);
985   XrmPutLineResource(&new_blackboxrc, rc_string);
986
987   sprintf(rc_string, "session.doubleClickInterval:  %lu",
988           resource.double_click_interval);
989   XrmPutLineResource(&new_blackboxrc, rc_string);
990
991   sprintf(rc_string, "session.autoRaiseDelay:  %lu",
992           ((resource.auto_raise_delay.tv_sec * 1000) +
993            (resource.auto_raise_delay.tv_usec / 1000)));
994   XrmPutLineResource(&new_blackboxrc, rc_string);
995
996   sprintf(rc_string, "session.cacheLife: %lu", resource.cache_life / 60000);
997   XrmPutLineResource(&new_blackboxrc, rc_string);
998
999   sprintf(rc_string, "session.cacheMax: %lu", resource.cache_max);
1000   XrmPutLineResource(&new_blackboxrc, rc_string);
1001
1002   ScreenList::iterator it = screenList.begin();
1003   for (; it != screenList.end(); ++it) {
1004     BScreen *screen = *it;
1005     int screen_number = screen->getScreenNumber();
1006
1007     char *placement = (char *) 0;
1008
1009     switch (screen->getSlitPlacement()) {
1010     case Slit::TopLeft: placement = "TopLeft"; break;
1011     case Slit::CenterLeft: placement = "CenterLeft"; break;
1012     case Slit::BottomLeft: placement = "BottomLeft"; break;
1013     case Slit::TopCenter: placement = "TopCenter"; break;
1014     case Slit::BottomCenter: placement = "BottomCenter"; break;
1015     case Slit::TopRight: placement = "TopRight"; break;
1016     case Slit::BottomRight: placement = "BottomRight"; break;
1017     case Slit::CenterRight: default: placement = "CenterRight"; break;
1018     }
1019
1020     sprintf(rc_string, "session.screen%d.slit.placement: %s", screen_number,
1021             placement);
1022     XrmPutLineResource(&new_blackboxrc, rc_string);
1023
1024     sprintf(rc_string, "session.screen%d.slit.direction: %s", screen_number,
1025             ((screen->getSlitDirection() == Slit::Horizontal) ? "Horizontal" :
1026              "Vertical"));
1027     XrmPutLineResource(&new_blackboxrc, rc_string);
1028
1029     sprintf(rc_string, "session.screen%d.slit.onTop: %s", screen_number,
1030             ((screen->getSlit()->isOnTop()) ? "True" : "False"));
1031     XrmPutLineResource(&new_blackboxrc, rc_string);
1032
1033     sprintf(rc_string, "session.screen%d.slit.autoHide: %s", screen_number,
1034             ((screen->getSlit()->doAutoHide()) ? "True" : "False"));
1035     XrmPutLineResource(&new_blackboxrc, rc_string);
1036
1037     sprintf(rc_string, "session.opaqueMove: %s",
1038             ((screen->doOpaqueMove()) ? "True" : "False"));
1039     XrmPutLineResource(&new_blackboxrc, rc_string);
1040
1041     sprintf(rc_string, "session.imageDither: %s",
1042             ((screen->getImageControl()->doDither()) ? "True" : "False"));
1043     XrmPutLineResource(&new_blackboxrc, rc_string);
1044
1045     sprintf(rc_string, "session.screen%d.fullMaximization: %s", screen_number,
1046             ((screen->doFullMax()) ? "True" : "False"));
1047     XrmPutLineResource(&new_blackboxrc, rc_string);
1048
1049     sprintf(rc_string, "session.screen%d.focusNewWindows: %s", screen_number,
1050             ((screen->doFocusNew()) ? "True" : "False"));
1051     XrmPutLineResource(&new_blackboxrc, rc_string);
1052
1053     sprintf(rc_string, "session.screen%d.focusLastWindow: %s", screen_number,
1054             ((screen->doFocusLast()) ? "True" : "False"));
1055     XrmPutLineResource(&new_blackboxrc, rc_string);
1056
1057     sprintf(rc_string, "session.screen%d.rowPlacementDirection: %s",
1058             screen_number,
1059             ((screen->getRowPlacementDirection() == BScreen::LeftRight) ?
1060              "LeftToRight" : "RightToLeft"));
1061     XrmPutLineResource(&new_blackboxrc, rc_string);
1062
1063     sprintf(rc_string, "session.screen%d.colPlacementDirection: %s",
1064             screen_number,
1065             ((screen->getColPlacementDirection() == BScreen::TopBottom) ?
1066              "TopToBottom" : "BottomToTop"));
1067     XrmPutLineResource(&new_blackboxrc, rc_string);
1068
1069     switch (screen->getPlacementPolicy()) {
1070     case BScreen::CascadePlacement:
1071       placement = "CascadePlacement";
1072       break;
1073     case BScreen::ColSmartPlacement:
1074       placement = "ColSmartPlacement";
1075       break;
1076
1077     case BScreen::RowSmartPlacement:
1078     default:
1079       placement = "RowSmartPlacement";
1080       break;
1081     }
1082     sprintf(rc_string, "session.screen%d.windowPlacement:  %s", screen_number,
1083             placement);
1084     XrmPutLineResource(&new_blackboxrc, rc_string);
1085
1086     string fmodel;
1087     if (screen->isSloppyFocus()) {
1088       fmodel = "SloppyFocus";
1089       if (screen->doAutoRaise()) fmodel += " AutoRaise";
1090       if (screen->doClickRaise()) fmodel += " ClickRaise";
1091     } else {
1092       fmodel = "ClickToFocus";
1093     }
1094     sprintf(rc_string, "session.screen%d.focusModel:  %s", screen_number,
1095             fmodel.c_str());
1096     XrmPutLineResource(&new_blackboxrc, rc_string);
1097
1098     sprintf(rc_string, "session.screen%d.workspaces:  %d", screen_number,
1099             screen->getWorkspaceCount());
1100     XrmPutLineResource(&new_blackboxrc, rc_string);
1101
1102     sprintf(rc_string, "session.screen%d.toolbar.onTop:  %s", screen_number,
1103             ((screen->getToolbar()->isOnTop()) ? "True" : "False"));
1104     XrmPutLineResource(&new_blackboxrc, rc_string);
1105
1106     sprintf(rc_string, "session.screen%d.toolbar.autoHide:  %s",
1107             screen_number,
1108             ((screen->getToolbar()->doAutoHide()) ? "True" : "False"));
1109     XrmPutLineResource(&new_blackboxrc, rc_string);
1110
1111     switch (screen->getToolbarPlacement()) {
1112     case Toolbar::TopLeft: placement = "TopLeft"; break;
1113     case Toolbar::BottomLeft: placement = "BottomLeft"; break;
1114     case Toolbar::TopCenter: placement = "TopCenter"; break;
1115     case Toolbar::TopRight: placement = "TopRight"; break;
1116     case Toolbar::BottomRight: placement = "BottomRight"; break;
1117     case Toolbar::BottomCenter: default: placement = "BottomCenter"; break;
1118     }
1119
1120     sprintf(rc_string, "session.screen%d.toolbar.placement: %s",
1121             screen_number, placement);
1122     XrmPutLineResource(&new_blackboxrc, rc_string);
1123
1124     load_rc(screen);
1125
1126     // these are static, but may not be saved in the users .blackboxrc,
1127     // writing these resources will allow the user to edit them at a later
1128     // time... but loading the defaults before saving allows us to rewrite the
1129     // users changes...
1130
1131 #ifdef    HAVE_STRFTIME
1132     sprintf(rc_string, "session.screen%d.strftimeFormat: %s", screen_number,
1133             screen->getStrftimeFormat());
1134     XrmPutLineResource(&new_blackboxrc, rc_string);
1135 #else // !HAVE_STRFTIME
1136     sprintf(rc_string, "session.screen%d.dateFormat:  %s", screen_number,
1137             ((screen->getDateFormat() == B_EuropeanDate) ?
1138              "European" : "American"));
1139     XrmPutLineResource(&new_blackboxrc, rc_string);
1140
1141     sprintf(rc_string, "session.screen%d.clockFormat:  %d", screen_number,
1142             ((screen->isClock24Hour()) ? 24 : 12));
1143     XrmPutLineResource(&new_blackboxrc, rc_string);
1144 #endif // HAVE_STRFTIME
1145
1146     sprintf(rc_string, "session.screen%d.edgeSnapThreshold: %d",
1147             screen_number, screen->getEdgeSnapThreshold());
1148     XrmPutLineResource(&new_blackboxrc, rc_string);
1149
1150     sprintf(rc_string, "session.screen%d.toolbar.widthPercent:  %d",
1151             screen_number, screen->getToolbarWidthPercent());
1152     XrmPutLineResource(&new_blackboxrc, rc_string);
1153
1154     // write out the user's workspace names
1155
1156     string save_string = screen->getWorkspace(0)->getName();
1157     for (unsigned int i = 1; i < screen->getWorkspaceCount(); ++i) {
1158       save_string += ',';
1159       save_string += screen->getWorkspace(i)->getName();
1160     }
1161
1162     char *resource_string = new char[save_string.length() + 48];
1163     sprintf(resource_string, "session.screen%d.workspaceNames:  %s",
1164             screen_number, save_string.c_str());
1165     XrmPutLineResource(&new_blackboxrc, resource_string);
1166
1167     delete [] resource_string;
1168   }
1169
1170   XrmDatabase old_blackboxrc = XrmGetFileDatabase(rc_file.c_str());
1171
1172   XrmMergeDatabases(new_blackboxrc, &old_blackboxrc);
1173   XrmPutFileDatabase(old_blackboxrc, rc_file.c_str());
1174   XrmDestroyDatabase(old_blackboxrc);
1175 }
1176
1177
1178 void Blackbox::load_rc(void) {
1179   XrmDatabase database = (XrmDatabase) 0;
1180
1181   database = XrmGetFileDatabase(rc_file.c_str());
1182
1183   XrmValue value;
1184   char *value_type;
1185   int int_value;
1186   unsigned long long_value;
1187
1188   resource.colors_per_channel = 4;
1189   if (XrmGetResource(database, "session.colorsPerChannel",
1190                      "Session.ColorsPerChannel", &value_type, &value) &&
1191       sscanf(value.addr, "%d", &int_value) == 1) {
1192     resource.colors_per_channel = int_value;
1193     if (resource.colors_per_channel < 2) resource.colors_per_channel = 2;
1194     if (resource.colors_per_channel > 6) resource.colors_per_channel = 6;
1195   }
1196
1197   if (XrmGetResource(database, "session.styleFile", "Session.StyleFile",
1198                      &value_type, &value))
1199     resource.style_file = expandTilde(value.addr);
1200   else
1201     resource.style_file = DEFAULTSTYLE;
1202
1203   resource.double_click_interval = 250;
1204   if (XrmGetResource(database, "session.doubleClickInterval",
1205                      "Session.DoubleClickInterval", &value_type, &value) &&
1206       sscanf(value.addr, "%lu", &long_value) == 1) {
1207     resource.double_click_interval = long_value;
1208   }
1209
1210   resource.auto_raise_delay.tv_usec = 400;
1211   if (XrmGetResource(database, "session.autoRaiseDelay",
1212                      "Session.AutoRaiseDelay", &value_type, &value) &&
1213       sscanf(value.addr, "%lu", &long_value) == 1) {
1214     resource.auto_raise_delay.tv_usec = long_value;
1215   }
1216
1217   resource.auto_raise_delay.tv_sec = resource.auto_raise_delay.tv_usec / 1000;
1218   resource.auto_raise_delay.tv_usec -=
1219     (resource.auto_raise_delay.tv_sec * 1000);
1220   resource.auto_raise_delay.tv_usec *= 1000;
1221
1222   resource.cache_life = 5l;
1223   if (XrmGetResource(database, "session.cacheLife", "Session.CacheLife",
1224                      &value_type, &value) &&
1225       sscanf(value.addr, "%lu", &long_value) == 1) {
1226     resource.cache_life = long_value;
1227   }
1228   resource.cache_life *= 60000;
1229
1230   resource.cache_max = 200;
1231   if (XrmGetResource(database, "session.cacheMax", "Session.CacheMax",
1232                      &value_type, &value) &&
1233       sscanf(value.addr, "%lu", &long_value) == 1) {
1234     resource.cache_max = long_value;
1235   }
1236 }
1237
1238
1239 void Blackbox::load_rc(BScreen *screen) {
1240   XrmDatabase database = (XrmDatabase) 0;
1241
1242   database = XrmGetFileDatabase(rc_file.c_str());
1243
1244   XrmValue value;
1245   char *value_type, name_lookup[1024], class_lookup[1024];
1246   int screen_number = screen->getScreenNumber();
1247   int int_value;
1248
1249   sprintf(name_lookup,  "session.screen%d.fullMaximization", screen_number);
1250   sprintf(class_lookup, "Session.Screen%d.FullMaximization", screen_number);
1251   screen->saveFullMax(False);
1252   if (XrmGetResource(database, name_lookup, class_lookup, &value_type,
1253                      &value) &&
1254       ! strncasecmp(value.addr, "true", value.size)) {
1255     screen->saveFullMax(True);
1256   }
1257
1258   sprintf(name_lookup,  "session.screen%d.focusNewWindows", screen_number);
1259   sprintf(class_lookup, "Session.Screen%d.FocusNewWindows", screen_number);
1260   screen->saveFocusNew(False);
1261   if (XrmGetResource(database, name_lookup, class_lookup, &value_type,
1262                      &value) &&
1263       ! strncasecmp(value.addr, "true", value.size)) {
1264     screen->saveFocusNew(True);
1265   }
1266
1267   sprintf(name_lookup,  "session.screen%d.focusLastWindow", screen_number);
1268   sprintf(class_lookup, "Session.Screen%d.focusLastWindow", screen_number);
1269   screen->saveFocusLast(False);
1270   if (XrmGetResource(database, name_lookup, class_lookup, &value_type,
1271                      &value) &&
1272       ! strncasecmp(value.addr, "true", value.size)) {
1273     screen->saveFocusLast(True);
1274   }
1275
1276   sprintf(name_lookup,  "session.screen%d.rowPlacementDirection",
1277           screen_number);
1278   sprintf(class_lookup, "Session.Screen%d.RowPlacementDirection",
1279           screen_number);
1280   screen->saveRowPlacementDirection(BScreen::LeftRight);
1281   if (XrmGetResource(database, name_lookup, class_lookup, &value_type,
1282                      &value) &&
1283       ! strncasecmp(value.addr, "righttoleft", value.size)) {
1284     screen->saveRowPlacementDirection(BScreen::RightLeft);
1285   }
1286
1287   sprintf(name_lookup,  "session.screen%d.colPlacementDirection",
1288           screen_number);
1289   sprintf(class_lookup, "Session.Screen%d.ColPlacementDirection",
1290           screen_number);
1291   screen->saveColPlacementDirection(BScreen::TopBottom);
1292   if (XrmGetResource(database, name_lookup, class_lookup, &value_type,
1293                      &value) &&
1294       ! strncasecmp(value.addr, "bottomtotop", value.size)) {
1295     screen->saveColPlacementDirection(BScreen::BottomTop);
1296   }
1297
1298   sprintf(name_lookup,  "session.screen%d.workspaces", screen_number);
1299   sprintf(class_lookup, "Session.Screen%d.Workspaces", screen_number);
1300   screen->saveWorkspaces(1);
1301   if (XrmGetResource(database, name_lookup, class_lookup,
1302                      &value_type, &value) &&
1303       sscanf(value.addr, "%d", &int_value) == 1 &&
1304       int_value > 0 && int_value < 128) {
1305     screen->saveWorkspaces(int_value);
1306   }
1307
1308   sprintf(name_lookup,  "session.screen%d.toolbar.widthPercent",
1309           screen_number);
1310   sprintf(class_lookup, "Session.Screen%d.Toolbar.WidthPercent",
1311           screen_number);
1312   screen->saveToolbarWidthPercent(66);
1313   if (XrmGetResource(database, name_lookup, class_lookup, &value_type,
1314                      &value) &&
1315       sscanf(value.addr, "%d", &int_value) == 1 &&
1316       int_value > 0 && int_value <= 100) {
1317     screen->saveToolbarWidthPercent(int_value);
1318   }
1319
1320   sprintf(name_lookup, "session.screen%d.toolbar.placement", screen_number);
1321   sprintf(class_lookup, "Session.Screen%d.Toolbar.Placement", screen_number);
1322   screen->saveToolbarPlacement(Toolbar::BottomCenter);
1323   if (XrmGetResource(database, name_lookup, class_lookup, &value_type,
1324                      &value)) {
1325     if (! strncasecmp(value.addr, "TopLeft", value.size))
1326       screen->saveToolbarPlacement(Toolbar::TopLeft);
1327     else if (! strncasecmp(value.addr, "BottomLeft", value.size))
1328       screen->saveToolbarPlacement(Toolbar::BottomLeft);
1329     else if (! strncasecmp(value.addr, "TopCenter", value.size))
1330       screen->saveToolbarPlacement(Toolbar::TopCenter);
1331     else if (! strncasecmp(value.addr, "TopRight", value.size))
1332       screen->saveToolbarPlacement(Toolbar::TopRight);
1333     else if (! strncasecmp(value.addr, "BottomRight", value.size))
1334       screen->saveToolbarPlacement(Toolbar::BottomRight);
1335   }
1336   screen->removeWorkspaceNames();
1337
1338   sprintf(name_lookup,  "session.screen%d.workspaceNames", screen_number);
1339   sprintf(class_lookup, "Session.Screen%d.WorkspaceNames", screen_number);
1340   if (XrmGetResource(database, name_lookup, class_lookup, &value_type,
1341                      &value)) {
1342     string search = value.addr;
1343     string::const_iterator it = search.begin(),
1344       end = search.end();
1345     while (1) {
1346       string::const_iterator tmp = it; // current string.begin()
1347       it = std::find(tmp, end, ',');   // look for comma between tmp and end
1348       screen->addWorkspaceName(string(tmp, it)); // string = search[tmp:it]
1349       if (it == end) break;
1350       ++it;
1351     }
1352   }
1353
1354   sprintf(name_lookup,  "session.screen%d.toolbar.onTop", screen_number);
1355   sprintf(class_lookup, "Session.Screen%d.Toolbar.OnTop", screen_number);
1356   screen->saveToolbarOnTop(False);
1357   if (XrmGetResource(database, name_lookup, class_lookup, &value_type,
1358                      &value) &&
1359       ! strncasecmp(value.addr, "true", value.size)) {
1360     screen->saveToolbarOnTop(True);
1361   }
1362
1363   sprintf(name_lookup,  "session.screen%d.toolbar.autoHide", screen_number);
1364   sprintf(class_lookup, "Session.Screen%d.Toolbar.autoHide", screen_number);
1365   screen->saveToolbarAutoHide(False);
1366   if (XrmGetResource(database, name_lookup, class_lookup, &value_type,
1367                      &value) &&
1368       ! strncasecmp(value.addr, "true", value.size)) {
1369     screen->saveToolbarAutoHide(True);
1370   }
1371
1372   sprintf(name_lookup,  "session.screen%d.focusModel", screen_number);
1373   sprintf(class_lookup, "Session.Screen%d.FocusModel", screen_number);
1374   screen->saveSloppyFocus(True);
1375   screen->saveAutoRaise(False);
1376   screen->saveClickRaise(False);
1377   if (XrmGetResource(database, name_lookup, class_lookup, &value_type,
1378                      &value)) {
1379     string fmodel = value.addr;
1380
1381     if (fmodel.find("ClickToFocus") != string::npos) {
1382       screen->saveSloppyFocus(False);
1383     } else {
1384       // must be sloppy
1385
1386       if (fmodel.find("AutoRaise") != string::npos)
1387         screen->saveAutoRaise(True);
1388       if (fmodel.find("ClickRaise") != string::npos)
1389         screen->saveClickRaise(True);
1390     }
1391   }
1392
1393   sprintf(name_lookup,  "session.screen%d.windowPlacement", screen_number);
1394   sprintf(class_lookup, "Session.Screen%d.WindowPlacement", screen_number);
1395   screen->savePlacementPolicy(BScreen::RowSmartPlacement);
1396   if (XrmGetResource(database, name_lookup, class_lookup, &value_type,
1397                      &value)) {
1398     if (! strncasecmp(value.addr, "RowSmartPlacement", value.size))
1399       /* pass */;
1400     else if (! strncasecmp(value.addr, "ColSmartPlacement", value.size))
1401       screen->savePlacementPolicy(BScreen::ColSmartPlacement);
1402     else if (! strncasecmp(value.addr, "CascadePlacement", value.size))
1403       screen->savePlacementPolicy(BScreen::CascadePlacement);
1404   }
1405
1406   sprintf(name_lookup, "session.screen%d.slit.placement", screen_number);
1407   sprintf(class_lookup, "Session.Screen%d.Slit.Placement", screen_number);
1408   screen->saveSlitPlacement(Slit::CenterRight);
1409   if (XrmGetResource(database, name_lookup, class_lookup, &value_type,
1410                      &value)) {
1411     if (! strncasecmp(value.addr, "TopLeft", value.size))
1412       screen->saveSlitPlacement(Slit::TopLeft);
1413     else if (! strncasecmp(value.addr, "CenterLeft", value.size))
1414       screen->saveSlitPlacement(Slit::CenterLeft);
1415     else if (! strncasecmp(value.addr, "BottomLeft", value.size))
1416       screen->saveSlitPlacement(Slit::BottomLeft);
1417     else if (! strncasecmp(value.addr, "TopCenter", value.size))
1418       screen->saveSlitPlacement(Slit::TopCenter);
1419     else if (! strncasecmp(value.addr, "BottomCenter", value.size))
1420       screen->saveSlitPlacement(Slit::BottomCenter);
1421     else if (! strncasecmp(value.addr, "TopRight", value.size))
1422       screen->saveSlitPlacement(Slit::TopRight);
1423     else if (! strncasecmp(value.addr, "BottomRight", value.size))
1424       screen->saveSlitPlacement(Slit::BottomRight);
1425   }
1426
1427   sprintf(name_lookup, "session.screen%d.slit.direction", screen_number);
1428   sprintf(class_lookup, "Session.Screen%d.Slit.Direction", screen_number);
1429   screen->saveSlitDirection(Slit::Vertical);
1430   if (XrmGetResource(database, name_lookup, class_lookup, &value_type,
1431                      &value) &&
1432       ! strncasecmp(value.addr, "Horizontal", value.size)) {
1433     screen->saveSlitDirection(Slit::Horizontal);
1434   }
1435
1436   sprintf(name_lookup, "session.screen%d.slit.onTop", screen_number);
1437   sprintf(class_lookup, "Session.Screen%d.Slit.OnTop", screen_number);
1438   screen->saveSlitOnTop(False);
1439   if (XrmGetResource(database, name_lookup, class_lookup, &value_type,
1440                      &value) &&
1441       ! strncasecmp(value.addr, "True", value.size)) {
1442     screen->saveSlitOnTop(True);
1443   }
1444
1445   sprintf(name_lookup, "session.screen%d.slit.autoHide", screen_number);
1446   sprintf(class_lookup, "Session.Screen%d.Slit.AutoHide", screen_number);
1447   screen->saveSlitAutoHide(False);
1448   if (XrmGetResource(database, name_lookup, class_lookup, &value_type,
1449                      &value) &&
1450       ! strncasecmp(value.addr, "true", value.size)) {
1451     screen->saveSlitAutoHide(True);
1452   }
1453
1454 #ifdef    HAVE_STRFTIME
1455   sprintf(name_lookup,  "session.screen%d.strftimeFormat", screen_number);
1456   sprintf(class_lookup, "Session.Screen%d.StrftimeFormat", screen_number);
1457   if (XrmGetResource(database, name_lookup, class_lookup, &value_type,
1458                      &value)) {
1459     screen->saveStrftimeFormat(value.addr);
1460   } else {
1461     screen->saveStrftimeFormat("%I:%M %p");
1462   }
1463 #else //  HAVE_STRFTIME
1464   sprintf(name_lookup,  "session.screen%d.dateFormat", screen_number);
1465   sprintf(class_lookup, "Session.Screen%d.DateFormat", screen_number);
1466   screen->saveDateFormat(B_AmericanDate);
1467   if (XrmGetResource(database, name_lookup, class_lookup, &value_type,
1468                      &value)) {
1469     if (! strncasecmp(value.addr, "european", value.size))
1470       screen->saveDateFormat(B_EuropeanDate);
1471   }
1472
1473   sprintf(name_lookup,  "session.screen%d.clockFormat", screen_number);
1474   sprintf(class_lookup, "Session.Screen%d.ClockFormat", screen_number);
1475   screen->saveClock24Hour(False);
1476   if (XrmGetResource(database, name_lookup, class_lookup, &value_type,
1477                      &value) &&
1478       sscanf(value.addr, "%d", &int_value) == 1 && int_value == 24) {
1479     screen->saveClock24Hour(True);
1480   }
1481 #endif // HAVE_STRFTIME
1482
1483   sprintf(name_lookup,  "session.screen%d.edgeSnapThreshold", screen_number);
1484   sprintf(class_lookup, "Session.Screen%d.EdgeSnapThreshold", screen_number);
1485   if (XrmGetResource(database, name_lookup, class_lookup, &value_type,
1486                      &value) &&
1487       sscanf(value.addr, "%d", &int_value) == 1) {
1488     screen->saveEdgeSnapThreshold(int_value);
1489   }
1490
1491   screen->saveImageDither(True);
1492   if (XrmGetResource(database, "session.imageDither", "Session.ImageDither",
1493                      &value_type, &value) &&
1494       ! strncasecmp("false", value.addr, value.size)) {
1495     screen->saveImageDither(False);
1496   }
1497
1498   screen->saveOpaqueMove(False);
1499   if (XrmGetResource(database, "session.opaqueMove", "Session.OpaqueMove",
1500                      &value_type, &value) &&
1501       ! strncasecmp("true", value.addr, value.size)) {
1502     screen->saveOpaqueMove(True);
1503   }
1504
1505   XrmDestroyDatabase(database);
1506 }
1507
1508
1509 void Blackbox::reload_rc(void) {
1510   load_rc();
1511   reconfigure();
1512 }
1513
1514
1515 void Blackbox::reconfigure(void) {
1516   reconfigure_wait = True;
1517
1518   if (! timer->isTiming()) timer->start();
1519 }
1520
1521
1522 void Blackbox::real_reconfigure(void) {
1523   XrmDatabase new_blackboxrc = (XrmDatabase) 0;
1524   char *style = new char[resource.style_file.length() + 20];
1525
1526   sprintf(style, "session.styleFile: %s", getStyleFilename());
1527   XrmPutLineResource(&new_blackboxrc, style);
1528
1529   delete [] style;
1530
1531   XrmDatabase old_blackboxrc = XrmGetFileDatabase(rc_file.c_str());
1532
1533   XrmMergeDatabases(new_blackboxrc, &old_blackboxrc);
1534   XrmPutFileDatabase(old_blackboxrc, rc_file.c_str());
1535   if (old_blackboxrc) XrmDestroyDatabase(old_blackboxrc);
1536
1537   std::for_each(menuTimestamps.begin(), menuTimestamps.end(),
1538                 PointerAssassin());
1539   menuTimestamps.clear();
1540
1541   gcCache()->purge();
1542
1543   std::for_each(screenList.begin(), screenList.end(),
1544                 std::mem_fun(&BScreen::reconfigure));
1545 }
1546
1547
1548 void Blackbox::checkMenu(void) {
1549   bool reread = False;
1550   MenuTimestampList::iterator it = menuTimestamps.begin();
1551   for(; it != menuTimestamps.end(); ++it) {
1552     MenuTimestamp *tmp = *it;
1553     struct stat buf;
1554
1555     if (! stat(tmp->filename.c_str(), &buf)) {
1556       if (tmp->timestamp != buf.st_ctime)
1557         reread = True;
1558     } else {
1559       reread = True;
1560     }
1561   }
1562
1563   if (reread) rereadMenu();
1564 }
1565
1566
1567 void Blackbox::rereadMenu(void) {
1568   reread_menu_wait = True;
1569
1570   if (! timer->isTiming()) timer->start();
1571 }
1572
1573
1574 void Blackbox::real_rereadMenu(void) {
1575   std::for_each(menuTimestamps.begin(), menuTimestamps.end(),
1576                 PointerAssassin());
1577   menuTimestamps.clear();
1578
1579   std::for_each(screenList.begin(), screenList.end(),
1580                 std::mem_fun(&BScreen::rereadMenu));
1581 }
1582
1583
1584 void Blackbox::saveStyleFilename(const string& filename) {
1585   assert(! filename.empty());
1586   resource.style_file = filename;
1587 }
1588
1589
1590 void Blackbox::addMenuTimestamp(const string& filename) {
1591   assert(! filename.empty());
1592   bool found = False;
1593
1594   MenuTimestampList::iterator it = menuTimestamps.begin();
1595   for (; it != menuTimestamps.end() && !found; ++it) {
1596     if ((*it)->filename == filename) found = True;
1597   }
1598   if (! found) {
1599     struct stat buf;
1600
1601     if (! stat(filename.c_str(), &buf)) {
1602       MenuTimestamp *ts = new MenuTimestamp;
1603
1604       ts->filename = filename;
1605       ts->timestamp = buf.st_ctime;
1606
1607       menuTimestamps.push_back(ts);
1608     }
1609   }
1610 }
1611
1612
1613 void Blackbox::timeout(void) {
1614   if (reconfigure_wait)
1615     real_reconfigure();
1616
1617   if (reread_menu_wait)
1618     real_rereadMenu();
1619
1620   reconfigure_wait = reread_menu_wait = False;
1621 }
1622
1623
1624 void Blackbox::setFocusedWindow(BlackboxWindow *win) {
1625   if (focused_window && focused_window == win) // nothing to do
1626     return;
1627
1628   BScreen *old_screen = 0;
1629
1630   if (focused_window) {
1631     focused_window->setFocusFlag(False);
1632     old_screen = focused_window->getScreen();
1633   }
1634
1635   if (win && ! win->isIconic()) {
1636     // the active screen is the one with the last focused window...
1637     // this will keep focus on this screen no matter where the mouse goes,
1638     // so multihead keybindings will continue to work on that screen until the
1639     // user focuses a window on a different screen.
1640     active_screen = win->getScreen();
1641     focused_window = win;
1642   } else {
1643     focused_window = 0;
1644     if (! old_screen) {
1645       if (active_screen) {
1646         // set input focus to the toolbar of the screen with mouse
1647         XSetInputFocus(getXDisplay(),
1648                        active_screen->getToolbar()->getWindowID(),
1649                        RevertToPointerRoot, CurrentTime);
1650       } else {
1651         // set input focus to the toolbar of the first managed screen
1652         XSetInputFocus(getXDisplay(),
1653                        screenList.front()->getToolbar()->getWindowID(),
1654                        RevertToPointerRoot, CurrentTime);
1655       }
1656     } else {
1657       // set input focus to the toolbar of the last screen
1658       XSetInputFocus(getXDisplay(), old_screen->getToolbar()->getWindowID(),
1659                      RevertToPointerRoot, CurrentTime);
1660     }
1661   }
1662
1663   if (active_screen && active_screen->isScreenManaged()) {
1664     active_screen->getToolbar()->redrawWindowLabel(True);
1665     active_screen->updateNetizenWindowFocus();
1666   }
1667
1668   if (old_screen && old_screen != active_screen) {
1669     old_screen->getToolbar()->redrawWindowLabel(True);
1670     old_screen->updateNetizenWindowFocus();
1671   }
1672 }