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