]> icculus.org git repositories - mikachu/openbox.git/blob - src/openbox.cc
if user is moving a window and switches workspaces, stop moving that window
[mikachu/openbox.git] / src / openbox.cc
1 // openbox.cc for Openbox
2 // Copyright (c) 2001 Sean 'Shaleh' Perry <shaleh@debian.org>
3 // Copyright (c) 1997 - 2000 Brad Hughes (bhughes@tcac.net)
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a
6 // copy of this software and associated documentation files (the "Software"),
7 // to deal in the Software without restriction, including without limitation
8 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 // and/or sell copies of the Software, and to permit persons to whom the
10 // Software is furnished to do so, subject to the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be included in
13 // all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 // DEALINGS IN THE SOFTWARE.
22
23 // stupid macros needed to access some functions in version 2 of the GNU C
24 // library
25 #ifndef   _GNU_SOURCE
26 #define   _GNU_SOURCE
27 #endif // _GNU_SOURCE
28
29 #ifdef    HAVE_CONFIG_H
30 #  include "../config.h"
31 #endif // HAVE_CONFIG_H
32
33 #include <X11/Xlib.h>
34 #include <X11/Xutil.h>
35 #include <X11/Xresource.h>
36 #include <X11/Xatom.h>
37 #include <X11/keysym.h>
38
39 #ifdef    SHAPE
40 #include <X11/extensions/shape.h>
41 #endif // SHAPE
42
43 #include "i18n.h"
44 #include "openbox.h"
45 #include "Basemenu.h"
46 #include "Clientmenu.h"
47 #include "Rootmenu.h"
48 #include "Screen.h"
49
50 #ifdef    SLIT
51 #include "Slit.h"
52 #endif // SLIT
53
54 #include "Toolbar.h"
55 #include "Window.h"
56 #include "Workspace.h"
57 #include "Workspacemenu.h"
58 #include "Util.h"
59
60 #include <string>
61 #include <algorithm>
62
63 #ifdef    HAVE_STDIO_H
64 #  include <stdio.h>
65 #endif // HAVE_STDIO_H
66
67 #ifdef    HAVE_STDLIB_H
68 #  include <stdlib.h>
69 #endif // HAVE_STDLIB_H
70
71 #ifdef    HAVE_STRING_H
72 #  include <string.h>
73 #endif // HAVE_STRING_H
74
75 #ifdef    HAVE_UNISTD_H
76 #  include <sys/types.h>
77 #  include <unistd.h>
78 #endif // HAVE_UNISTD_H
79
80 #ifdef    HAVE_SYS_PARAM_H
81 #  include <sys/param.h>
82 #endif // HAVE_SYS_PARAM_H
83
84 #ifndef   MAXPATHLEN
85 #define   MAXPATHLEN 255
86 #endif // MAXPATHLEN
87
88 #ifdef    HAVE_SYS_SELECT_H
89 #  include <sys/select.h>
90 #endif // HAVE_SYS_SELECT_H
91
92 #ifdef    HAVE_SIGNAL_H
93 #  include <signal.h>
94 #endif // HAVE_SIGNAL_H
95
96 #ifdef    HAVE_SYS_SIGNAL_H
97 #  include <sys/signal.h>
98 #endif // HAVE_SYS_SIGNAL_H
99
100 #ifdef    HAVE_SYS_STAT_H
101 #  include <sys/types.h>
102 #  include <sys/stat.h>
103 #endif // HAVE_SYS_STAT_H
104
105 #ifdef    TIME_WITH_SYS_TIME
106 #  include <sys/time.h>
107 #  include <time.h>
108 #else // !TIME_WITH_SYS_TIME
109 #  ifdef    HAVE_SYS_TIME_H
110 #    include <sys/time.h>
111 #  else // !HAVE_SYS_TIME_H
112 #    include <time.h>
113 #  endif // HAVE_SYS_TIME_H
114 #endif // TIME_WITH_SYS_TIME
115
116 #ifdef    HAVE_LIBGEN_H
117 #  include <libgen.h>
118 #endif // HAVE_LIBGEN_H
119
120 #ifndef   HAVE_BASENAME
121 static inline char *basename (char *s) {
122   char *save = s;
123
124   while (*s) if (*s++ == '/') save = s;
125
126   return save;
127 }
128 #endif // HAVE_BASENAME
129
130
131 // X event scanner for enter/leave notifies - adapted from twm
132 typedef struct scanargs {
133   Window w;
134   Bool leave, inferior, enter;
135 } scanargs;
136
137 static Bool queueScanner(Display *, XEvent *e, char *args) {
138   if ((e->type == LeaveNotify) &&
139       (e->xcrossing.window == ((scanargs *) args)->w) &&
140       (e->xcrossing.mode == NotifyNormal)) {
141     ((scanargs *) args)->leave = True;
142     ((scanargs *) args)->inferior = (e->xcrossing.detail == NotifyInferior);
143   } else if ((e->type == EnterNotify) &&
144              (e->xcrossing.mode == NotifyUngrab)) {
145     ((scanargs *) args)->enter = True;
146   }
147
148   return False;
149 }
150
151 Openbox *openbox;
152
153
154 Openbox::Openbox(int m_argc, char **m_argv, char *dpy_name, char *rc)
155   : BaseDisplay(m_argv[0], dpy_name) {
156   grab();
157
158   if (! XSupportsLocale())
159     fprintf(stderr, "X server does not support locale\n");
160
161   if (XSetLocaleModifiers("") == NULL)
162     fprintf(stderr, "cannot set locale modifiers\n");
163
164   ::openbox = this;
165   argc = m_argc;
166   argv = m_argv;
167   if (rc == NULL) {
168     char *homedir = getenv("HOME");
169
170     rc_file = new char[strlen(homedir) + strlen("/.openbox/rc") + 1];
171     sprintf(rc_file, "%s/.openbox", homedir);
172
173     // try to make sure the ~/.openbox directory exists
174     mkdir(rc_file, S_IREAD | S_IWRITE | S_IEXEC | S_IRGRP | S_IWGRP | S_IXGRP |
175           S_IROTH | S_IWOTH | S_IXOTH);
176     
177     sprintf(rc_file, "%s/.openbox/rc", homedir);
178   } else {
179     rc_file = bstrdup(rc);
180   }
181   config.setFile(rc_file);
182
183   no_focus = False;
184
185   resource.menu_file = resource.style_file = NULL;
186   resource.titlebar_layout = NULL;
187   resource.auto_raise_delay.tv_sec = resource.auto_raise_delay.tv_usec = 0;
188
189   focused_window = masked_window = NULL;
190   masked = None;
191
192   windowSearchList = new LinkedList<WindowSearch>;
193   menuSearchList = new LinkedList<MenuSearch>;
194
195 #ifdef    SLIT
196   slitSearchList = new LinkedList<SlitSearch>;
197 #endif // SLIT
198
199   toolbarSearchList = new LinkedList<ToolbarSearch>;
200   groupSearchList = new LinkedList<WindowSearch>;
201
202   menuTimestamps = new LinkedList<MenuTimestamp>;
203
204   load();
205
206 #ifdef    HAVE_GETPID
207   openbox_pid = XInternAtom(getXDisplay(), "_BLACKBOX_PID", False);
208 #endif // HAVE_GETPID
209
210   screenList = new LinkedList<BScreen>;
211   for (int i = 0; i < getNumberOfScreens(); i++) {
212     BScreen *screen = new BScreen(*this, i, config);
213
214     if (! screen->isScreenManaged()) {
215       delete screen;
216       continue;
217     }
218
219     screenList->insert(screen);
220   }
221
222   if (! screenList->count()) {
223     fprintf(stderr,
224             i18n->getMessage(openboxSet, openboxNoManagableScreens,
225                "Openbox::Openbox: no managable screens found, aborting.\n"));
226     ::exit(3);
227   }
228
229   // save current settings and default values
230   save();
231   
232   XSynchronize(getXDisplay(), False);
233   XSync(getXDisplay(), False);
234
235   reconfigure_wait = reread_menu_wait = False;
236
237   timer = new BTimer(*this, *this);
238   timer->setTimeout(0);
239   timer->fireOnce(True);
240
241   ungrab();
242 }
243
244
245 Openbox::~Openbox() {
246   while (screenList->count())
247     delete screenList->remove(0);
248
249   while (menuTimestamps->count()) {
250     MenuTimestamp *ts = menuTimestamps->remove(0);
251
252     if (ts->filename)
253       delete [] ts->filename;
254
255     delete ts;
256   }
257
258   if (resource.menu_file)
259     delete [] resource.menu_file;
260
261   if (resource.style_file)
262     delete [] resource.style_file;
263
264   if (resource.titlebar_layout)
265     delete [] resource.titlebar_layout;
266
267   delete timer;
268
269   delete screenList;
270   delete menuTimestamps;
271
272   delete windowSearchList;
273   delete menuSearchList;
274   delete toolbarSearchList;
275   delete groupSearchList;
276
277   delete [] rc_file;
278
279 #ifdef    SLIT
280   delete slitSearchList;
281 #endif // SLIT
282 }
283
284
285 void Openbox::process_event(XEvent *e) {
286   if ((masked == e->xany.window) && masked_window &&
287       (e->type == MotionNotify)) {
288     last_time = e->xmotion.time;
289     masked_window->motionNotifyEvent(&e->xmotion);
290
291     return;
292   }
293
294   switch (e->type) {
295   case ButtonPress: {
296     // strip the lock key modifiers
297     e->xbutton.state &= ~(NumLockMask | ScrollLockMask | LockMask);
298
299     last_time = e->xbutton.time;
300
301     OpenboxWindow *win = (OpenboxWindow *) 0;
302     Basemenu *menu = (Basemenu *) 0;
303
304 #ifdef    SLIT
305     Slit *slit = (Slit *) 0;
306 #endif // SLIT
307
308     Toolbar *tbar = (Toolbar *) 0;
309
310     if ((win = searchWindow(e->xbutton.window))) {
311       win->buttonPressEvent(&e->xbutton);
312
313       if (e->xbutton.button == 1)
314         win->installColormap(True);
315     } else if ((menu = searchMenu(e->xbutton.window))) {
316       menu->buttonPressEvent(&e->xbutton);
317
318 #ifdef    SLIT
319     } else if ((slit = searchSlit(e->xbutton.window))) {
320       slit->buttonPressEvent(&e->xbutton);
321 #endif // SLIT
322
323     } else if ((tbar = searchToolbar(e->xbutton.window))) {
324       tbar->buttonPressEvent(&e->xbutton);
325     } else {
326       LinkedListIterator<BScreen> it(screenList);
327       BScreen *screen = it.current();
328       for (; screen; it++, screen = it.current()) {
329         if (e->xbutton.window == screen->getRootWindow()) {
330           if (e->xbutton.button == 1) {
331             if (! screen->isRootColormapInstalled())
332               screen->getImageControl()->installRootColormap();
333
334             if (screen->getWorkspacemenu()->isVisible())
335               screen->getWorkspacemenu()->hide();
336
337             if (screen->getRootmenu()->isVisible())
338               screen->getRootmenu()->hide();
339           } else if (e->xbutton.button == 2) {
340             int mx = e->xbutton.x_root -
341               (screen->getWorkspacemenu()->getWidth() / 2);
342             int my = e->xbutton.y_root -
343               (screen->getWorkspacemenu()->getTitleHeight() / 2);
344
345             if (mx < 0) mx = 0;
346             if (my < 0) my = 0;
347
348             if (mx + screen->getWorkspacemenu()->getWidth() >
349                 screen->size().w())
350               mx = screen->size().w() -
351                 screen->getWorkspacemenu()->getWidth() -
352                 screen->getBorderWidth();
353
354             if (my + screen->getWorkspacemenu()->getHeight() >
355                 screen->size().h())
356               my = screen->size().h() -
357                 screen->getWorkspacemenu()->getHeight() -
358                 screen->getBorderWidth();
359
360             screen->getWorkspacemenu()->move(mx, my);
361
362             if (! screen->getWorkspacemenu()->isVisible()) {
363               screen->getWorkspacemenu()->removeParent();
364               screen->getWorkspacemenu()->show();
365             }
366           } else if (e->xbutton.button == 3) {
367             int mx = e->xbutton.x_root -
368               (screen->getRootmenu()->getWidth() / 2);
369             int my = e->xbutton.y_root -
370               (screen->getRootmenu()->getTitleHeight() / 2);
371
372             if (mx < 0) mx = 0;
373             if (my < 0) my = 0;
374
375             if (mx + screen->getRootmenu()->getWidth() > screen->size().w())
376               mx = screen->size().w() -
377                 screen->getRootmenu()->getWidth() -
378                 screen->getBorderWidth();
379
380             if (my + screen->getRootmenu()->getHeight() > screen->size().h())
381                 my = screen->size().h() -
382                   screen->getRootmenu()->getHeight() -
383                   screen->getBorderWidth();
384
385             screen->getRootmenu()->move(mx, my);
386
387             if (! screen->getRootmenu()->isVisible()) {
388               checkMenu();
389               screen->getRootmenu()->show();
390             }
391           } else if (e->xbutton.button == 4) {
392             if ((screen->getCurrentWorkspaceID() + 1) >
393                 screen->getWorkspaceCount() - 1)
394               screen->changeWorkspaceID(0);
395             else
396               screen->changeWorkspaceID(screen->getCurrentWorkspaceID() + 1);
397           } else if (e->xbutton.button == 5) {
398             if ((screen->getCurrentWorkspaceID() - 1) < 0)
399               screen->changeWorkspaceID(screen->getWorkspaceCount() - 1);
400             else
401               screen->changeWorkspaceID(screen->getCurrentWorkspaceID() - 1);
402           }
403         }
404       }
405     }
406
407     break;
408   }
409
410   case ButtonRelease: {
411     // strip the lock key modifiers
412     e->xbutton.state &= ~(NumLockMask | ScrollLockMask | LockMask);
413
414     last_time = e->xbutton.time;
415
416     OpenboxWindow *win = (OpenboxWindow *) 0;
417     Basemenu *menu = (Basemenu *) 0;
418     Toolbar *tbar = (Toolbar *) 0;
419
420     if ((win = searchWindow(e->xbutton.window)))
421       win->buttonReleaseEvent(&e->xbutton);
422     else if ((menu = searchMenu(e->xbutton.window)))
423       menu->buttonReleaseEvent(&e->xbutton);
424     else if ((tbar = searchToolbar(e->xbutton.window)))
425       tbar->buttonReleaseEvent(&e->xbutton);
426
427     break;
428   }
429
430   case ConfigureRequest: {
431     OpenboxWindow *win = (OpenboxWindow *) 0;
432
433 #ifdef    SLIT
434     Slit *slit = (Slit *) 0;
435 #endif // SLIT
436
437     if ((win = searchWindow(e->xconfigurerequest.window))) {
438       win->configureRequestEvent(&e->xconfigurerequest);
439
440 #ifdef    SLIT
441     } else if ((slit = searchSlit(e->xconfigurerequest.window))) {
442       slit->configureRequestEvent(&e->xconfigurerequest);
443 #endif // SLIT
444
445     } else {
446       grab();
447
448       if (validateWindow(e->xconfigurerequest.window)) {
449         XWindowChanges xwc;
450
451         xwc.x = e->xconfigurerequest.x;
452         xwc.y = e->xconfigurerequest.y;
453         xwc.width = e->xconfigurerequest.width;
454         xwc.height = e->xconfigurerequest.height;
455         xwc.border_width = e->xconfigurerequest.border_width;
456         xwc.sibling = e->xconfigurerequest.above;
457         xwc.stack_mode = e->xconfigurerequest.detail;
458
459         XConfigureWindow(getXDisplay(), e->xconfigurerequest.window,
460                          e->xconfigurerequest.value_mask, &xwc);
461       }
462
463       ungrab();
464     }
465
466     break;
467   }
468
469   case MapRequest: {
470 #ifdef    DEBUG
471     fprintf(stderr,
472             i18n->getMessage(openboxSet, openboxMapRequest,
473                  "Openbox::process_event(): MapRequest for 0x%lx\n"),
474             e->xmaprequest.window);
475 #endif // DEBUG
476
477     OpenboxWindow *win = searchWindow(e->xmaprequest.window);
478
479     if (! win)
480       win = new OpenboxWindow(*this, e->xmaprequest.window);
481
482     if ((win = searchWindow(e->xmaprequest.window))) {
483       win->mapRequestEvent(&e->xmaprequest);
484       // if we're using the click to place placement type, then immediately
485       // after the window is mapped, we need to start interactively moving it
486       if (win->getScreen()->placementPolicy() == BScreen::ClickMousePlacement) {
487         int x, y, rx, ry;
488         Window c, r;
489         unsigned int m;
490         XQueryPointer(getXDisplay(), win->getScreen()->getRootWindow(),
491                       &r, &c, &rx, &ry, &x, &y, &m);
492         win->startMove(rx, ry);
493       }
494     }
495     break;
496   }
497
498   case MapNotify: {
499     OpenboxWindow *win = searchWindow(e->xmap.window);
500
501     if (win)
502       win->mapNotifyEvent(&e->xmap);
503
504       break;
505   }
506
507   case UnmapNotify: {
508     OpenboxWindow *win = (OpenboxWindow *) 0;
509
510 #ifdef    SLIT
511     Slit *slit = (Slit *) 0;
512 #endif // SLIT
513
514     if ((win = searchWindow(e->xunmap.window))) {
515       win->unmapNotifyEvent(&e->xunmap);
516       if (focused_window == win)
517         focused_window = (OpenboxWindow *) 0;
518 #ifdef    SLIT
519     } else if ((slit = searchSlit(e->xunmap.window))) {
520       slit->removeClient(e->xunmap.window);
521 #endif // SLIT
522
523     }
524
525     break;
526   }
527
528   case DestroyNotify: {
529     OpenboxWindow *win = (OpenboxWindow *) 0;
530
531 #ifdef    SLIT
532     Slit *slit = (Slit *) 0;
533 #endif // SLIT
534
535     if ((win = searchWindow(e->xdestroywindow.window))) {
536       win->destroyNotifyEvent(&e->xdestroywindow);
537       if (focused_window == win)
538         focused_window = (OpenboxWindow *) 0;
539 #ifdef    SLIT
540     } else if ((slit = searchSlit(e->xdestroywindow.window))) {
541       slit->removeClient(e->xdestroywindow.window, False);
542 #endif // SLIT
543     }
544
545     break;
546   }
547
548   case MotionNotify: {
549     // strip the lock key modifiers
550     e->xbutton.state &= ~(NumLockMask | ScrollLockMask | LockMask);
551     
552     last_time = e->xmotion.time;
553
554     OpenboxWindow *win = (OpenboxWindow *) 0;
555     Basemenu *menu = (Basemenu *) 0;
556
557     if ((win = searchWindow(e->xmotion.window)))
558       win->motionNotifyEvent(&e->xmotion);
559     else if ((menu = searchMenu(e->xmotion.window)))
560       menu->motionNotifyEvent(&e->xmotion);
561
562     break;
563   }
564
565   case PropertyNotify: {
566     last_time = e->xproperty.time;
567
568     if (e->xproperty.state != PropertyDelete) {
569       OpenboxWindow *win = searchWindow(e->xproperty.window);
570
571       if (win)
572         win->propertyNotifyEvent(e->xproperty.atom);
573     }
574
575     break;
576   }
577
578   case EnterNotify: {
579     last_time = e->xcrossing.time;
580
581     BScreen *screen = (BScreen *) 0;
582     OpenboxWindow *win = (OpenboxWindow *) 0;
583     Basemenu *menu = (Basemenu *) 0;
584     Toolbar *tbar = (Toolbar *) 0;
585
586 #ifdef    SLIT
587     Slit *slit = (Slit *) 0;
588 #endif // SLIT
589
590     if (e->xcrossing.mode == NotifyGrab) break;
591
592     XEvent dummy;
593     scanargs sa;
594     sa.w = e->xcrossing.window;
595     sa.enter = sa.leave = False;
596     XCheckIfEvent(getXDisplay(), &dummy, queueScanner, (char *) &sa);
597
598     if ((e->xcrossing.window == e->xcrossing.root) &&
599         (screen = searchScreen(e->xcrossing.window))) {
600       screen->getImageControl()->installRootColormap();
601     } else if ((win = searchWindow(e->xcrossing.window))) {
602       if (win->getScreen()->sloppyFocus() &&
603           (! win->isFocused()) && (! no_focus)) {
604         grab();
605
606         if (((! sa.leave) || sa.inferior) && win->isVisible() &&
607             win->setInputFocus())
608           win->installColormap(True);
609
610         ungrab();
611       }
612     } else if ((menu = searchMenu(e->xcrossing.window))) {
613       menu->enterNotifyEvent(&e->xcrossing);
614     } else if ((tbar = searchToolbar(e->xcrossing.window))) {
615       tbar->enterNotifyEvent(&e->xcrossing);
616 #ifdef    SLIT
617     } else if ((slit = searchSlit(e->xcrossing.window))) {
618       slit->enterNotifyEvent(&e->xcrossing);
619 #endif // SLIT
620     }
621     break;
622   }
623
624   case LeaveNotify: {
625     last_time = e->xcrossing.time;
626
627     OpenboxWindow *win = (OpenboxWindow *) 0;
628     Basemenu *menu = (Basemenu *) 0;
629     Toolbar *tbar = (Toolbar *) 0;
630
631 #ifdef    SLIT
632     Slit *slit = (Slit *) 0;
633 #endif // SLIT
634
635     if ((menu = searchMenu(e->xcrossing.window)))
636       menu->leaveNotifyEvent(&e->xcrossing);
637     else if ((win = searchWindow(e->xcrossing.window)))
638       win->installColormap(False);
639     else if ((tbar = searchToolbar(e->xcrossing.window)))
640       tbar->leaveNotifyEvent(&e->xcrossing);
641 #ifdef    SLIT
642     else if ((slit = searchSlit(e->xcrossing.window)))
643       slit->leaveNotifyEvent(&e->xcrossing);
644 #endif // SLIT
645
646     break;
647   }
648
649   case Expose: {
650     OpenboxWindow *win = (OpenboxWindow *) 0;
651     Basemenu *menu = (Basemenu *) 0;
652     Toolbar *tbar = (Toolbar *) 0;
653
654     if ((win = searchWindow(e->xexpose.window)))
655       win->exposeEvent(&e->xexpose);
656     else if ((menu = searchMenu(e->xexpose.window)))
657       menu->exposeEvent(&e->xexpose);
658     else if ((tbar = searchToolbar(e->xexpose.window)))
659       tbar->exposeEvent(&e->xexpose);
660
661     break;
662   }
663
664   case KeyPress: {
665     Toolbar *tbar = searchToolbar(e->xkey.window);
666
667     if (tbar && tbar->isEditing())
668       tbar->keyPressEvent(&e->xkey);
669
670     break;
671   }
672
673   case ColormapNotify: {
674     BScreen *screen = searchScreen(e->xcolormap.window);
675
676     if (screen)
677       screen->setRootColormapInstalled((e->xcolormap.state ==
678                                         ColormapInstalled) ? True : False);
679
680     break;
681   }
682
683   case FocusIn: {
684     if (e->xfocus.mode == NotifyUngrab || e->xfocus.detail == NotifyPointer)
685       break;
686
687     OpenboxWindow *win = searchWindow(e->xfocus.window);
688     if (win && ! win->isFocused())
689       setFocusedWindow(win);
690
691     break;
692   }
693
694   case FocusOut:
695     break;
696
697   case ClientMessage: {
698     if (e->xclient.format == 32) {
699       if (e->xclient.message_type == getWMChangeStateAtom()) {
700         OpenboxWindow *win = searchWindow(e->xclient.window);
701         if (! win || ! win->validateClient()) return;
702
703         if (e->xclient.data.l[0] == IconicState)
704           win->iconify();
705         if (e->xclient.data.l[0] == NormalState)
706           win->deiconify();
707       } else if (e->xclient.message_type == getOpenboxChangeWorkspaceAtom()) {
708         BScreen *screen = searchScreen(e->xclient.window);
709
710         if (screen && e->xclient.data.l[0] >= 0 &&
711             e->xclient.data.l[0] < screen->getWorkspaceCount())
712           screen->changeWorkspaceID(e->xclient.data.l[0]);
713       } else if (e->xclient.message_type == getOpenboxChangeWindowFocusAtom()) {
714         OpenboxWindow *win = searchWindow(e->xclient.window);
715
716         if (win && win->isVisible() && win->setInputFocus())
717           win->installColormap(True);
718       } else if (e->xclient.message_type == getOpenboxCycleWindowFocusAtom()) {
719         BScreen *screen = searchScreen(e->xclient.window);
720
721         if (screen) {
722           if (! e->xclient.data.l[0])
723             screen->prevFocus();
724           else
725             screen->nextFocus();
726         }
727       } else if (e->xclient.message_type == getOpenboxChangeAttributesAtom()) {
728         OpenboxWindow *win = searchWindow(e->xclient.window);
729
730         if (win && win->validateClient()) {
731           OpenboxHints net;
732           net.flags = e->xclient.data.l[0];
733           net.attrib = e->xclient.data.l[1];
734           net.workspace = e->xclient.data.l[2];
735           net.stack = e->xclient.data.l[3];
736           net.decoration = e->xclient.data.l[4];
737
738           win->changeOpenboxHints(&net);
739         }
740       }
741     }
742
743     break;
744   }
745
746
747   default: {
748 #ifdef    SHAPE
749     if (e->type == getShapeEventBase()) {
750       XShapeEvent *shape_event = (XShapeEvent *) e;
751       OpenboxWindow *win = (OpenboxWindow *) 0;
752
753       if ((win = searchWindow(e->xany.window)) ||
754           (shape_event->kind != ShapeBounding))
755         win->shapeEvent(shape_event);
756     }
757 #endif // SHAPE
758
759   }
760   } // switch
761 }
762
763
764 Bool Openbox::handleSignal(int sig) {
765   switch (sig) {
766   case SIGHUP:
767   case SIGUSR1:
768     reconfigure();
769     break;
770
771   case SIGUSR2:
772     rereadMenu();
773     break;
774
775   case SIGPIPE:
776   case SIGSEGV:
777   case SIGFPE:
778   case SIGINT:
779   case SIGTERM:
780     shutdown();
781
782   default:
783     return False;
784   }
785
786   return True;
787 }
788
789
790 BScreen *Openbox::searchScreen(Window window) {
791   LinkedListIterator<BScreen> it(screenList);
792
793   for (BScreen *curr = it.current(); curr; it++, curr = it.current()) {
794     if (curr->getRootWindow() == window) {
795       return curr;
796     }
797   }
798
799   return (BScreen *) 0;
800 }
801
802
803 OpenboxWindow *Openbox::searchWindow(Window window) {
804   LinkedListIterator<WindowSearch> it(windowSearchList);
805
806   for (WindowSearch *tmp = it.current(); tmp; it++, tmp = it.current()) {
807       if (tmp->getWindow() == window) {
808         return tmp->getData();
809       }
810   }
811
812   return (OpenboxWindow *) 0;
813 }
814
815
816 OpenboxWindow *Openbox::searchGroup(Window window, OpenboxWindow *win) {
817   OpenboxWindow *w = (OpenboxWindow *) 0;
818   LinkedListIterator<WindowSearch> it(groupSearchList);
819
820   for (WindowSearch *tmp = it.current(); tmp; it++, tmp = it.current()) {
821     if (tmp->getWindow() == window) {
822       w = tmp->getData();
823       if (w->getClientWindow() != win->getClientWindow())
824         return win;
825     }
826   }
827
828   return (OpenboxWindow *) 0;
829 }
830
831
832 Basemenu *Openbox::searchMenu(Window window) {
833   LinkedListIterator<MenuSearch> it(menuSearchList);
834
835   for (MenuSearch *tmp = it.current(); tmp; it++, tmp = it.current()) {
836     if (tmp->getWindow() == window)
837       return tmp->getData();
838   }
839
840   return (Basemenu *) 0;
841 }
842
843
844 Toolbar *Openbox::searchToolbar(Window window) {
845   LinkedListIterator<ToolbarSearch> it(toolbarSearchList);
846
847   for (ToolbarSearch *tmp = it.current(); tmp; it++, tmp = it.current()) {
848     if (tmp->getWindow() == window)
849       return tmp->getData();
850   }
851
852   return (Toolbar *) 0;
853 }
854
855
856 #ifdef    SLIT
857 Slit *Openbox::searchSlit(Window window) {
858   LinkedListIterator<SlitSearch> it(slitSearchList);
859
860   for (SlitSearch *tmp = it.current(); tmp; it++, tmp = it.current()) {
861     if (tmp->getWindow() == window)
862       return tmp->getData();
863   }
864
865   return (Slit *) 0;
866 }
867 #endif // SLIT
868
869
870 void Openbox::saveWindowSearch(Window window, OpenboxWindow *data) {
871   windowSearchList->insert(new WindowSearch(window, data));
872 }
873
874
875 void Openbox::saveGroupSearch(Window window, OpenboxWindow *data) {
876   groupSearchList->insert(new WindowSearch(window, data));
877 }
878
879
880 void Openbox::saveMenuSearch(Window window, Basemenu *data) {
881   menuSearchList->insert(new MenuSearch(window, data));
882 }
883
884
885 void Openbox::saveToolbarSearch(Window window, Toolbar *data) {
886   toolbarSearchList->insert(new ToolbarSearch(window, data));
887 }
888
889
890 #ifdef    SLIT
891 void Openbox::saveSlitSearch(Window window, Slit *data) {
892   slitSearchList->insert(new SlitSearch(window, data));
893 }
894 #endif // SLIT
895
896
897 void Openbox::removeWindowSearch(Window window) {
898   LinkedListIterator<WindowSearch> it(windowSearchList);
899   for (WindowSearch *tmp = it.current(); tmp; it++, tmp = it.current()) {
900     if (tmp->getWindow() == window) {
901       windowSearchList->remove(tmp);
902       delete tmp;
903       break;
904     }
905   }
906 }
907
908
909 void Openbox::removeGroupSearch(Window window) {
910   LinkedListIterator<WindowSearch> it(groupSearchList);
911   for (WindowSearch *tmp = it.current(); tmp; it++, tmp = it.current()) {
912     if (tmp->getWindow() == window) {
913       groupSearchList->remove(tmp);
914       delete tmp;
915       break;
916     }
917   }
918 }
919
920
921 void Openbox::removeMenuSearch(Window window) {
922   LinkedListIterator<MenuSearch> it(menuSearchList);
923   for (MenuSearch *tmp = it.current(); tmp; it++, tmp = it.current()) {
924     if (tmp->getWindow() == window) {
925       menuSearchList->remove(tmp);
926       delete tmp;
927       break;
928     }
929   }
930 }
931
932
933 void Openbox::removeToolbarSearch(Window window) {
934   LinkedListIterator<ToolbarSearch> it(toolbarSearchList);
935   for (ToolbarSearch *tmp = it.current(); tmp; it++, tmp = it.current()) {
936     if (tmp->getWindow() == window) {
937       toolbarSearchList->remove(tmp);
938       delete tmp;
939       break;
940     }
941   }
942 }
943
944
945 #ifdef    SLIT
946 void Openbox::removeSlitSearch(Window window) {
947   LinkedListIterator<SlitSearch> it(slitSearchList);
948   for (SlitSearch *tmp = it.current(); tmp; it++, tmp = it.current()) {
949     if (tmp->getWindow() == window) {
950       slitSearchList->remove(tmp);
951       delete tmp;
952       break;
953     }
954   }
955 }
956 #endif // SLIT
957
958
959 void Openbox::restart(const char *prog) {
960   shutdown();
961
962   if (prog) {
963     execlp(prog, prog, NULL);
964     perror(prog);
965   }
966
967   // fall back in case the above execlp doesn't work
968   execvp(argv[0], argv);
969   execvp(basename(argv[0]), argv);
970 }
971
972
973 void Openbox::shutdown() {
974   BaseDisplay::shutdown();
975
976   XSetInputFocus(getXDisplay(), PointerRoot, None, CurrentTime);
977
978   LinkedListIterator<BScreen> it(screenList);
979   for (BScreen *s = it.current(); s; it++, s = it.current())
980     s->shutdown();
981
982   XSync(getXDisplay(), False);
983 }
984
985
986 void Openbox::save() {
987   config.setAutoSave(false);
988   
989   // save all values as they are so that the defaults will be written to the rc
990   // file
991   
992   config.setValue("session.menuFile", getMenuFilename());
993   config.setValue("session.colorsPerChannel",
994                   resource.colors_per_channel);
995   config.setValue("session.doubleClickInterval",
996                   (long)resource.double_click_interval);
997   config.setValue("session.autoRaiseDelay",
998           ((resource.auto_raise_delay.tv_sec * 1000) +
999            (resource.auto_raise_delay.tv_usec / 1000)));
1000   config.setValue("session.cacheLife", (long)resource.cache_life / 60000);
1001   config.setValue("session.cacheMax", (long)resource.cache_max);
1002   config.setValue("session.styleFile", resource.style_file);
1003
1004   LinkedListIterator<BScreen> it(screenList);
1005   for (BScreen *s = it.current(); s != NULL; it++, s = it.current()) {
1006     s->save();
1007     s->getToolbar()->save();
1008 #ifdef    SLIT
1009     s->getSlit()->save();
1010 #endif // SLIT
1011   }
1012
1013   config.setAutoSave(true);
1014   config.save();
1015 }
1016
1017 void Openbox::load() {
1018   if (!config.load())
1019     config.create();
1020
1021   std::string s;
1022   long l;
1023   
1024   if (resource.menu_file)
1025     delete [] resource.menu_file;
1026   if (config.getValue("session.menuFile", "Session.MenuFile", s))
1027     resource.menu_file = bstrdup(s.c_str());
1028   else
1029     resource.menu_file = bstrdup(DEFAULTMENU);
1030
1031   if (config.getValue("session.colorsPerChannel", "Session.ColorsPerChannel",
1032                       l))
1033     resource.colors_per_channel = (l < 2 ? 2 : (l > 6 ? 6 : l)); // >= 2, <= 6
1034   else
1035     resource.colors_per_channel = 4;
1036
1037   if (resource.style_file)
1038     delete [] resource.style_file;
1039   if (config.getValue("session.styleFile", "Session.StyleFile", s))
1040     resource.style_file = bstrdup(s.c_str());
1041   else
1042     resource.style_file = bstrdup(DEFAULTSTYLE);
1043
1044   if (resource.titlebar_layout)
1045     delete [] resource.titlebar_layout;
1046   if (config.getValue("session.titlebarLayout", "Session.TitlebarLayout", s))
1047     resource.titlebar_layout = bstrdup(s.c_str());
1048   else
1049     resource.titlebar_layout = bstrdup("ILMC");
1050
1051   if (config.getValue("session.doubleClickInterval",
1052                       "Session.DoubleClickInterval", l))
1053     resource.double_click_interval = l;
1054   else
1055     resource.double_click_interval = 250;
1056
1057   if (!config.getValue("session.autoRaiseDelay", "Session.AutoRaiseDelay", l))
1058     resource.auto_raise_delay.tv_usec = l;
1059   else
1060     resource.auto_raise_delay.tv_usec = 400;
1061   resource.auto_raise_delay.tv_sec = resource.auto_raise_delay.tv_usec / 1000;
1062   resource.auto_raise_delay.tv_usec -=
1063     (resource.auto_raise_delay.tv_sec * 1000);
1064   resource.auto_raise_delay.tv_usec *= 1000;
1065
1066   if (config.getValue("session.cacheLife", "Session.CacheLife", l))
1067     resource.cache_life = l;
1068   else
1069     resource.cache_life = 51;
1070   resource.cache_life *= 60000;
1071
1072   if (config.getValue("session.cacheMax", "Session.CacheMax", l))
1073     resource.cache_max = l;
1074   else
1075     resource.cache_max = 200;
1076 }
1077
1078
1079 void Openbox::reconfigure() {
1080   reconfigure_wait = True;
1081
1082   if (! timer->isTiming()) timer->start();
1083 }
1084
1085
1086 void Openbox::real_reconfigure() {
1087   grab();
1088
1089   load();
1090   
1091   for (int i = 0, n = menuTimestamps->count(); i < n; i++) {
1092     MenuTimestamp *ts = menuTimestamps->remove(0);
1093
1094     if (ts) {
1095       if (ts->filename)
1096         delete [] ts->filename;
1097
1098       delete ts;
1099     }
1100   }
1101
1102   LinkedListIterator<BScreen> it(screenList);
1103   for (BScreen *screen = it.current(); screen; it++, screen = it.current()) {
1104     screen->reconfigure();
1105   }
1106
1107   ungrab();
1108 }
1109
1110
1111 void Openbox::checkMenu() {
1112   Bool reread = False;
1113   LinkedListIterator<MenuTimestamp> it(menuTimestamps);
1114   for (MenuTimestamp *tmp = it.current(); tmp && (! reread);
1115        it++, tmp = it.current()) {
1116     struct stat buf;
1117
1118     if (! stat(tmp->filename, &buf)) {
1119       if (tmp->timestamp != buf.st_ctime)
1120         reread = True;
1121     } else {
1122       reread = True;
1123     }
1124   }
1125
1126   if (reread) rereadMenu();
1127 }
1128
1129
1130 void Openbox::rereadMenu() {
1131   reread_menu_wait = True;
1132
1133   if (! timer->isTiming()) timer->start();
1134 }
1135
1136
1137 void Openbox::real_rereadMenu() {
1138   for (int i = 0, n = menuTimestamps->count(); i < n; i++) {
1139     MenuTimestamp *ts = menuTimestamps->remove(0);
1140
1141     if (ts) {
1142       if (ts->filename)
1143         delete [] ts->filename;
1144
1145       delete ts;
1146     }
1147   }
1148
1149   LinkedListIterator<BScreen> it(screenList);
1150   for (BScreen *screen = it.current(); screen; it++, screen = it.current())
1151     screen->rereadMenu();
1152 }
1153
1154
1155 void Openbox::setStyleFilename(const char *filename) {
1156   if (resource.style_file)
1157     delete [] resource.style_file;
1158
1159   resource.style_file = bstrdup(filename);
1160   config.setValue("session.styleFile", resource.style_file);
1161 }
1162
1163
1164 void Openbox::setMenuFilename(const char *filename) {
1165   Bool found = False;
1166
1167   LinkedListIterator<MenuTimestamp> it(menuTimestamps);
1168   for (MenuTimestamp *tmp = it.current(); tmp && (! found);
1169        it++, tmp = it.current()) {
1170     if (! strcmp(tmp->filename, filename)) found = True;
1171   }
1172   if (! found) {
1173     struct stat buf;
1174
1175     if (! stat(filename, &buf)) {
1176       MenuTimestamp *ts = new MenuTimestamp;
1177
1178       ts->filename = bstrdup(filename);
1179       ts->timestamp = buf.st_ctime;
1180
1181       menuTimestamps->insert(ts);
1182     }
1183   }
1184 }
1185
1186
1187 void Openbox::timeout() {
1188   if (reconfigure_wait)
1189     real_reconfigure();
1190
1191   if (reread_menu_wait)
1192     real_rereadMenu();
1193
1194   reconfigure_wait = reread_menu_wait = False;
1195 }
1196
1197
1198 void Openbox::setFocusedWindow(OpenboxWindow *win) {
1199   BScreen *old_screen = (BScreen *) 0, *screen = (BScreen *) 0;
1200   OpenboxWindow *old_win = (OpenboxWindow *) 0;
1201   Toolbar *old_tbar = (Toolbar *) 0, *tbar = (Toolbar *) 0;
1202   Workspace *old_wkspc = (Workspace *) 0, *wkspc = (Workspace *) 0;
1203
1204   if (focused_window) {
1205     old_win = focused_window;
1206     old_screen = old_win->getScreen();
1207     old_tbar = old_screen->getToolbar();
1208     old_wkspc = old_screen->getWorkspace(old_win->getWorkspaceNumber());
1209
1210     old_win->setFocusFlag(False);
1211     old_wkspc->getMenu()->setItemSelected(old_win->getWindowNumber(), False);
1212   }
1213
1214   if (win && ! win->isIconic()) {
1215     screen = win->getScreen();
1216     tbar = screen->getToolbar();
1217     wkspc = screen->getWorkspace(win->getWorkspaceNumber());
1218
1219     focused_window = win;
1220
1221     win->setFocusFlag(True);
1222     wkspc->getMenu()->setItemSelected(win->getWindowNumber(), True);
1223   } else {
1224     focused_window = (OpenboxWindow *) 0;
1225   }
1226
1227   if (tbar)
1228     tbar->redrawWindowLabel(True);
1229   if (screen)
1230     screen->updateNetizenWindowFocus();
1231
1232   if (old_tbar && old_tbar != tbar)
1233     old_tbar->redrawWindowLabel(True);
1234   if (old_screen && old_screen != screen)
1235     old_screen->updateNetizenWindowFocus();
1236 }