compiles now. uses xft2
[mikachu/openbox.git] / src / screen.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2 // Screen.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/Xatom.h>
30 #include <X11/keysym.h>
31
32 #ifdef    XINERAMA
33 #  include <X11/Xlib.h>
34 #  include <X11/extensions/Xinerama.h>
35 #endif // XINERAMA
36
37 #ifdef HAVE_STDLIB_H
38 #  include <stdlib.h>
39 #endif // HAVE_STDLIB_H
40
41 #ifdef HAVE_STRING_H
42 #  include <string.h>
43 #endif // HAVE_STRING_H
44
45 #ifdef    HAVE_CTYPE_H
46 #  include <ctype.h>
47 #endif // HAVE_CTYPE_H
48
49 #ifdef    HAVE_UNISTD_H
50 #  include <sys/types.h>
51 #  include <unistd.h>
52 #endif // HAVE_UNISTD_H
53
54 #ifdef    HAVE_DIRENT_H
55 #  include <dirent.h>
56 #endif // HAVE_DIRENT_H
57
58 #ifdef    HAVE_LOCALE_H
59 #  include <locale.h>
60 #endif // HAVE_LOCALE_H
61
62 #ifdef    HAVE_SYS_STAT_H
63 #  include <sys/stat.h>
64 #endif // HAVE_SYS_STAT_H
65
66 #ifdef    HAVE_STDARG_H
67 #  include <stdarg.h>
68 #endif // HAVE_STDARG_H
69 }
70
71 #include <assert.h>
72
73 #include <algorithm>
74 #include <functional>
75 #include <string>
76 using std::string;
77
78 #include "blackbox.hh"
79 #include "font.hh"
80 #include "gccache.hh"
81 #include "image.hh"
82 #include "screen.hh"
83 #include "util.hh"
84 #include "window.hh"
85 #include "workspace.hh"
86 #include "util.hh"
87 #include "xatom.hh"
88
89 #ifndef   FONT_ELEMENT_SIZE
90 #define   FONT_ELEMENT_SIZE 50
91 #endif // FONT_ELEMENT_SIZE
92
93
94 static bool running = True;
95
96 static int anotherWMRunning(Display *display, XErrorEvent *) {
97   fprintf(stderr,
98           "BScreen::BScreen: an error occured while querying the X server.\n"
99           "  another window manager already running on display %s.\n",
100           DisplayString(display));
101
102   running = False;
103
104   return(-1);
105 }
106
107
108 BScreen::BScreen(Blackbox *bb, unsigned int scrn) : ScreenInfo(bb, scrn) {
109   blackbox = bb;
110   screenstr = "session.screen" + itostring(scrn) + '.';
111   config = blackbox->getConfig();
112   xatom = blackbox->getXAtom();
113
114   event_mask = ColormapChangeMask | EnterWindowMask | PropertyChangeMask |
115     SubstructureRedirectMask | ButtonPressMask | ButtonReleaseMask;
116
117   XErrorHandler old = XSetErrorHandler((XErrorHandler) anotherWMRunning);
118   XSelectInput(getBaseDisplay()->getXDisplay(), getRootWindow(), event_mask);
119   XSync(getBaseDisplay()->getXDisplay(), False);
120   XSetErrorHandler((XErrorHandler) old);
121
122   managed = running;
123   if (! managed) return;
124
125   fprintf(stderr, "BScreen::BScreen: managing screen %d "
126           "using visual 0x%lx, depth %d\n",
127           getScreenNumber(), XVisualIDFromVisual(getVisual()),
128           getDepth());
129
130   resource.wstyle.font = (BFont *) 0;
131
132   geom_pixmap = None;
133
134   xatom->setSupported(this);    // set-up netwm support
135 #ifdef    HAVE_GETPID
136   xatom->setValue(getRootWindow(), XAtom::blackbox_pid, XAtom::cardinal,
137                   (unsigned long) getpid());
138 #endif // HAVE_GETPID
139   unsigned long geometry[] = { getWidth(),
140                                getHeight()};
141   xatom->setValue(getRootWindow(), XAtom::net_desktop_geometry,
142                   XAtom::cardinal, geometry, 2);
143   unsigned long viewport[] = {0,0};
144   xatom->setValue(getRootWindow(), XAtom::net_desktop_viewport,
145                   XAtom::cardinal, viewport, 2);
146                   
147
148   XDefineCursor(blackbox->getXDisplay(), getRootWindow(),
149                 blackbox->getSessionCursor());
150
151   updateAvailableArea();
152
153   image_control =
154     new BImageControl(blackbox, this, True, blackbox->getColorsPerChannel(),
155                       blackbox->getCacheLife(), blackbox->getCacheMax());
156   image_control->installRootColormap();
157   root_colormap_installed = True;
158
159   load_rc();
160   LoadStyle();
161
162   XGCValues gcv;
163   gcv.foreground = WhitePixel(blackbox->getXDisplay(), getScreenNumber())
164     ^ BlackPixel(blackbox->getXDisplay(), getScreenNumber());
165   gcv.function = GXxor;
166   gcv.subwindow_mode = IncludeInferiors;
167   opGC = XCreateGC(blackbox->getXDisplay(), getRootWindow(),
168                    GCForeground | GCFunction | GCSubwindowMode, &gcv);
169
170   const char *s = "0: 0000 x 0: 0000";
171   geom_w = resource.wstyle.font->measureString(s) + resource.bevel_width * 2;
172   geom_h = resource.wstyle.font->height() + resource.bevel_width * 2;
173
174   XSetWindowAttributes attrib;
175   unsigned long mask = CWBorderPixel | CWColormap | CWSaveUnder;
176   attrib.border_pixel = getBorderColor()->pixel();
177   attrib.colormap = getColormap();
178   attrib.save_under = True;
179
180   geom_window = XCreateWindow(blackbox->getXDisplay(), getRootWindow(),
181                               0, 0, geom_w, geom_h, resource.border_width,
182                               getDepth(), InputOutput, getVisual(),
183                               mask, &attrib);
184   geom_visible = False;
185
186   BTexture* texture = &(resource.wstyle.l_focus);
187   geom_pixmap = texture->render(geom_w, geom_h, geom_pixmap);
188   if (geom_pixmap == ParentRelative) {
189     texture = &(resource.wstyle.t_focus);
190     geom_pixmap = texture->render(geom_w, geom_h, geom_pixmap);
191   }
192   if (! geom_pixmap)
193     XSetWindowBackground(blackbox->getXDisplay(), geom_window,
194                          texture->color().pixel());
195   else
196     XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
197                                geom_window, geom_pixmap);
198
199   if (resource.workspaces > 0) {
200     for (unsigned int i = 0; i < resource.workspaces; ++i) {
201       Workspace *wkspc = new Workspace(this, workspacesList.size());
202       workspacesList.push_back(wkspc);
203
204     }
205   } else {
206     Workspace *wkspc = new Workspace(this, workspacesList.size());
207     workspacesList.push_back(wkspc);
208   }
209   saveWorkspaceNames();
210
211   updateNetizenWorkspaceCount();
212
213   current_workspace = workspacesList.front();
214   
215   xatom->setValue(getRootWindow(), XAtom::net_current_desktop,
216                   XAtom::cardinal, 0); //first workspace
217
218   raiseWindows(0, 0);     // this also initializes the empty stacking list
219
220   updateClientList();     // initialize the client lists, which will be empty
221   updateAvailableArea();
222
223   changeWorkspaceID(0);
224
225   unsigned int i, j, nchild;
226   Window r, p, *children;
227   XQueryTree(blackbox->getXDisplay(), getRootWindow(), &r, &p,
228              &children, &nchild);
229
230   // preen the window list of all icon windows... for better dockapp support
231   for (i = 0; i < nchild; i++) {
232     if (children[i] == None) continue;
233
234     XWMHints *wmhints = XGetWMHints(blackbox->getXDisplay(),
235                                     children[i]);
236
237     if (wmhints) {
238       if ((wmhints->flags & IconWindowHint) &&
239           (wmhints->icon_window != children[i])) {
240         for (j = 0; j < nchild; j++) {
241           if (children[j] == wmhints->icon_window) {
242             children[j] = None;
243             break;
244           }
245         }
246       }
247
248       XFree(wmhints);
249     }
250   }
251
252   // manage shown windows
253   for (i = 0; i < nchild; ++i) {
254     if (children[i] == None || ! blackbox->validateWindow(children[i]))
255       continue;
256
257     XWindowAttributes attrib;
258     if (XGetWindowAttributes(blackbox->getXDisplay(), children[i], &attrib)) {
259       if (attrib.override_redirect) continue;
260
261       if (attrib.map_state != IsUnmapped) {
262         manageWindow(children[i]);
263       }
264     }
265   }
266
267   XFree(children);
268
269   // call this again just in case a window we found updates the Strut list
270   updateAvailableArea();
271 }
272
273
274 BScreen::~BScreen(void) {
275   if (! managed) return;
276
277   if (geom_pixmap != None)
278     image_control->removeImage(geom_pixmap);
279
280   if (geom_window != None)
281     XDestroyWindow(blackbox->getXDisplay(), geom_window);
282
283   std::for_each(workspacesList.begin(), workspacesList.end(),
284                 PointerAssassin());
285
286   std::for_each(iconList.begin(), iconList.end(), PointerAssassin());
287
288   while (! systrayWindowList.empty())
289     removeSystrayWindow(systrayWindowList[0]);
290
291   delete image_control;
292
293   if (resource.wstyle.font)
294     delete resource.wstyle.font;
295
296 #ifdef    BITMAPBUTTONS
297   if (resource.wstyle.close_button.mask != None)
298     XFreePixmap(blackbox->getXDisplay(), resource.wstyle.close_button.mask);
299   if (resource.wstyle.max_button.mask != None)
300     XFreePixmap(blackbox->getXDisplay(), resource.wstyle.max_button.mask);
301   if (resource.wstyle.icon_button.mask != None)
302     XFreePixmap(blackbox->getXDisplay(), resource.wstyle.icon_button.mask);
303   if (resource.wstyle.stick_button.mask != None)
304     XFreePixmap(blackbox->getXDisplay(), resource.wstyle.stick_button.mask);
305
306   resource.wstyle.max_button.mask = resource.wstyle.close_button.mask =
307     resource.wstyle.icon_button.mask =
308     resource.wstyle.stick_button.mask = None;
309 #endif // BITMAPBUTTONS
310   
311   XFreeGC(blackbox->getXDisplay(), opGC);
312 }
313
314
315 void BScreen::saveSloppyFocus(bool s) {
316   resource.sloppy_focus = s;
317
318   string fmodel;
319   if (resource.sloppy_focus) {
320     fmodel = "SloppyFocus";
321     if (resource.auto_raise) fmodel += " AutoRaise";
322     if (resource.click_raise) fmodel += " ClickRaise";
323   } else {
324     fmodel = "ClickToFocus";
325   }
326   config->setValue(screenstr + "focusModel", fmodel);
327 }
328
329
330 void BScreen::saveAutoRaise(bool a) {
331   resource.auto_raise = a;
332   saveSloppyFocus(resource.sloppy_focus);
333 }
334
335
336 void BScreen::saveClickRaise(bool c) {
337   resource.click_raise = c;
338   saveSloppyFocus(resource.sloppy_focus);
339 }
340
341
342 void BScreen::saveImageDither(bool d) {
343   image_control->setDither(d);
344   config->setValue(screenstr + "imageDither", doImageDither());
345 }
346
347
348 void BScreen::saveOpaqueMove(bool o) {
349   resource.opaque_move = o;
350   config->setValue(screenstr + "opaqueMove", resource.opaque_move);
351 }
352
353
354 void BScreen::saveFullMax(bool f) {
355   resource.full_max = f;
356   config->setValue(screenstr + "fullMaximization", resource.full_max);
357 }
358
359
360 void BScreen::saveFocusNew(bool f) {
361   resource.focus_new = f;
362   config->setValue(screenstr + "focusNewWindows", resource.focus_new);
363 }
364
365
366 void BScreen::saveFocusLast(bool f) {
367   resource.focus_last = f;
368   config->setValue(screenstr + "focusLastWindow", resource.focus_last);
369 }
370
371
372 void BScreen::saveAAFonts(bool f) {
373   resource.aa_fonts = f;
374   config->setValue(screenstr + "antialiasFonts", resource.aa_fonts);
375   reconfigure();
376 }
377
378
379 void BScreen::saveShadowFonts(bool f) {
380   resource.shadow_fonts = f;
381   config->setValue(screenstr + "dropShadowFonts", resource.shadow_fonts);
382   reconfigure();
383 }
384
385
386 void BScreen::saveWindowToEdgeSnap(int s) {
387   resource.snap_to_edges = s;
388
389   const char *snap;
390   switch (resource.snap_to_edges) {
391   case WindowNoSnap: snap = "NoSnap"; break;
392   case WindowResistance: snap = "Resistance"; break;
393   case WindowSnap: default: snap = "Snap"; break;
394   }
395   config->setValue(screenstr + "windowToEdgeSnap", snap);
396 }
397
398
399 void BScreen::saveWindowToWindowSnap(int s) {
400   resource.snap_to_windows = s;
401   
402   const char *snap;
403   switch (resource.snap_to_windows) {
404   case WindowNoSnap: snap = "NoSnap"; break;
405   case WindowResistance: snap = "Resistance"; break;
406   case WindowSnap: default: snap = "Snap"; break;
407   }
408   config->setValue(screenstr + "windowToWindowSnap", snap);
409 }
410
411
412 void BScreen::saveResizeZones(unsigned int z) {
413   resource.resize_zones = z;
414   config->setValue(screenstr + "resizeZones", resource.resize_zones);
415 }
416
417
418 void BScreen::saveWindowCornerSnap(bool s) {
419   resource.window_corner_snap = s;
420   config->setValue(screenstr + "windowCornerSnap",
421                    resource.window_corner_snap);
422 }
423
424
425 void BScreen::saveWorkspaces(unsigned int w) {
426   resource.workspaces = w;
427   config->setValue(screenstr + "workspaces", resource.workspaces);
428 }
429
430
431 void BScreen::savePlacementPolicy(int p) {
432   resource.placement_policy = p; 
433   const char *placement;
434   switch (resource.placement_policy) {
435   case CascadePlacement: placement = "CascadePlacement"; break;
436   case UnderMousePlacement: placement = "UnderMousePlacement"; break;
437   case ClickMousePlacement: placement = "ClickMousePlacement"; break;
438   case ColSmartPlacement: placement = "ColSmartPlacement"; break;
439   case RowSmartPlacement: default: placement = "RowSmartPlacement"; break;
440   }
441   config->setValue(screenstr + "windowPlacement", placement);
442 }
443
444
445 void BScreen::saveResistanceSize(int s) {
446   resource.resistance_size = s;
447   config->setValue(screenstr + "resistanceSize",
448                    resource.resistance_size);
449 }
450
451
452 void BScreen::saveSnapThreshold(int t) {
453   resource.snap_threshold = t;
454   config->setValue(screenstr + "edgeSnapThreshold",
455                    resource.snap_threshold);
456 }
457
458
459 void BScreen::saveSnapOffset(int t) {
460   resource.snap_offset = t;
461   config->setValue(screenstr + "edgeSnapOffset",
462                    resource.snap_offset);
463 }
464
465
466 void BScreen::saveRowPlacementDirection(int d) {
467   resource.row_direction = d;
468   config->setValue(screenstr + "rowPlacementDirection",
469                    resource.row_direction == LeftRight ?
470                    "LeftToRight" : "RightToLeft");
471 }
472
473
474 void BScreen::saveColPlacementDirection(int d) {
475   resource.col_direction = d;
476   config->setValue(screenstr + "colPlacementDirection",
477                    resource.col_direction == TopBottom ?
478                    "TopToBottom" : "BottomToTop");
479 }
480
481
482 void BScreen::saveStrftimeFormat(const std::string& format) {
483   resource.strftime_format = format;
484   config->setValue(screenstr + "strftimeFormat", resource.strftime_format);
485 }
486
487
488 void BScreen::saveWorkspaceNames() {
489   string names;
490  
491   for (unsigned int i = 0; i < workspacesList.size(); ++i) {
492     names += workspacesList[i]->getName();
493     if (i < workspacesList.size() - 1)
494       names += ',';
495   }
496
497   config->setValue(screenstr + "workspaceNames", names);
498 }
499
500
501 void BScreen::savePlaceIgnoreShaded(bool i) {
502   resource.ignore_shaded = i;
503   config->setValue(screenstr + "placementIgnoreShaded",
504                    resource.ignore_shaded);
505 }
506
507
508 void BScreen::savePlaceIgnoreMaximized(bool i) {
509   resource.ignore_maximized = i;
510   config->setValue(screenstr + "placementIgnoreMaximized",
511                    resource.ignore_maximized);
512 }
513
514
515 void BScreen::saveAllowScrollLock(bool a) {
516   resource.allow_scroll_lock = a;
517   config->setValue(screenstr + "disableBindingsWithScrollLock",
518                    resource.allow_scroll_lock);
519 }
520
521
522 void BScreen::saveWorkspaceWarping(bool w) {
523   resource.workspace_warping = w;
524   config->setValue(screenstr + "workspaceWarping",
525                    resource.workspace_warping);
526 }
527
528
529 void BScreen::saveRootScrollDirection(int d) {
530   resource.root_scroll = d;
531   const char *dir;
532   switch (resource.root_scroll) {
533   case NoScroll: dir = "None"; break;
534   case ReverseScroll: dir = "Reverse"; break;
535   case NormalScroll: default: dir = "Normal"; break;
536   }
537   config->setValue(screenstr + "rootScrollDirection", dir);
538 }
539
540
541 void BScreen::save_rc(void) {
542   saveSloppyFocus(resource.sloppy_focus);
543   saveAutoRaise(resource.auto_raise);
544   saveImageDither(doImageDither());
545   saveShadowFonts(resource.shadow_fonts);
546   saveAAFonts(resource.aa_fonts);
547   saveResizeZones(resource.resize_zones);
548   saveOpaqueMove(resource.opaque_move);
549   saveFullMax(resource.full_max);
550   saveFocusNew(resource.focus_new);
551   saveFocusLast(resource.focus_last);
552   saveWindowToWindowSnap(resource.snap_to_windows);
553   saveWindowToEdgeSnap(resource.snap_to_edges);
554   saveWindowCornerSnap(resource.window_corner_snap);
555   saveWorkspaces(resource.workspaces);
556   savePlacementPolicy(resource.placement_policy);
557   saveSnapThreshold(resource.snap_threshold);
558   saveSnapOffset(resource.snap_offset);
559   saveResistanceSize(resource.resistance_size);
560   saveRowPlacementDirection(resource.row_direction);
561   saveColPlacementDirection(resource.col_direction);
562   saveStrftimeFormat(resource.strftime_format); 
563   savePlaceIgnoreShaded(resource.ignore_shaded);
564   savePlaceIgnoreMaximized(resource.ignore_maximized);
565   saveAllowScrollLock(resource.allow_scroll_lock);
566   saveWorkspaceWarping(resource.workspace_warping);
567   saveRootScrollDirection(resource.root_scroll);
568 }
569
570
571 void BScreen::load_rc(void) {
572   std::string s;
573   bool b;
574
575   if (! config->getValue(screenstr + "fullMaximization", resource.full_max))
576     resource.full_max = false;
577
578   if (! config->getValue(screenstr + "focusNewWindows", resource.focus_new))
579     resource.focus_new = false;
580
581   if (! config->getValue(screenstr + "focusLastWindow", resource.focus_last))
582     resource.focus_last = false;
583
584   if (! config->getValue(screenstr + "workspaces", resource.workspaces))
585     resource.workspaces = 1;
586
587   if (! config->getValue(screenstr + "opaqueMove", resource.opaque_move))
588     resource.opaque_move = false;
589
590   if (! config->getValue(screenstr + "antialiasFonts", resource.aa_fonts))
591     resource.aa_fonts = true;
592
593   if (! resource.aa_fonts ||
594       ! config->getValue(screenstr + "dropShadowFonts", resource.shadow_fonts))
595     resource.shadow_fonts = false;
596
597   if (! config->getValue(screenstr + "resizeZones", resource.resize_zones) ||
598       (resource.resize_zones != 1 && resource.resize_zones != 2 &&
599        resource.resize_zones != 4))
600       resource.resize_zones = 4;
601
602   resource.snap_to_windows = WindowResistance;
603   if (config->getValue(screenstr + "windowToWindowSnap", s)) {
604     if (s == "NoSnap")
605       resource.snap_to_windows = WindowNoSnap;
606     else if (s == "Snap")
607       resource.snap_to_windows = WindowSnap;
608   }
609
610   resource.snap_to_edges = WindowResistance;
611   if (config->getValue(screenstr + "windowToEdgeSnap", s)) {
612     if (s == "NoSnap")
613       resource.snap_to_edges = WindowNoSnap;
614     else if (s == "Snap")
615       resource.snap_to_edges = WindowSnap;
616   }
617
618   if (! config->getValue(screenstr + "windowCornerSnap",
619                          resource.window_corner_snap))
620     resource.window_corner_snap = true;
621
622   if (! config->getValue(screenstr + "imageDither", b))
623     b = true;
624   image_control->setDither(b);
625
626   if (! config->getValue(screenstr + "edgeSnapOffset",
627                         resource.snap_offset))
628     resource.snap_offset = 0;
629   if (resource.snap_offset > 50)  // sanity check, setting this huge would
630     resource.snap_offset = 50;    // seriously suck.
631   
632   if (! config->getValue(screenstr + "edgeSnapThreshold",
633                         resource.snap_threshold))
634     resource.snap_threshold = 4;
635   
636   if (! config->getValue(screenstr + "resistanceSize",
637                         resource.resistance_size))
638     resource.resistance_size = 18;
639   
640   if (config->getValue(screenstr + "rowPlacementDirection", s) &&
641       s == "RightToLeft")
642     resource.row_direction = RightLeft;
643   else
644     resource.row_direction = LeftRight;
645
646   if (config->getValue(screenstr + "colPlacementDirection", s) &&
647       s == "BottomToTop")
648     resource.col_direction = BottomTop;
649   else
650     resource.col_direction = TopBottom;
651
652   if (config->getValue(screenstr + "workspaceNames", s)) {
653     XAtom::StringVect workspaceNames;
654
655     string::const_iterator it = s.begin(), end = s.end();
656     while(1) {
657       string::const_iterator tmp = it;     // current string.begin()
658       it = std::find(tmp, end, ',');       // look for comma between tmp and end
659       workspaceNames.push_back(string(tmp, it)); // s[tmp:it]
660       if (it == end)
661         break;
662       ++it;
663     }
664
665     xatom->setValue(getRootWindow(), XAtom::net_desktop_names, XAtom::utf8,
666                     workspaceNames);
667   }
668
669   resource.sloppy_focus = true;
670   resource.auto_raise = false;
671   resource.click_raise = false;
672   if (config->getValue(screenstr + "focusModel", s)) {
673     if (s.find("ClickToFocus") != string::npos) {
674       resource.sloppy_focus = false;
675     } else {
676       // must be sloppy
677       if (s.find("AutoRaise") != string::npos)
678         resource.auto_raise = true;
679       if (s.find("ClickRaise") != string::npos)
680         resource.click_raise = true;
681     }
682   }
683
684   if (config->getValue(screenstr + "windowPlacement", s)) {
685     if (s == "CascadePlacement")
686       resource.placement_policy = CascadePlacement;
687     else if (s == "UnderMousePlacement")
688       resource.placement_policy = UnderMousePlacement;
689     else if (s == "ClickMousePlacement")
690       resource.placement_policy = ClickMousePlacement;
691     else if (s == "ColSmartPlacement")
692       resource.placement_policy = ColSmartPlacement;
693     else //if (s == "RowSmartPlacement")
694       resource.placement_policy = RowSmartPlacement;
695   } else
696     resource.placement_policy = RowSmartPlacement;
697
698   if (! config->getValue(screenstr + "strftimeFormat",
699                          resource.strftime_format))
700     resource.strftime_format = "%I:%M %p";
701   
702   if (! config->getValue(screenstr + "placementIgnoreShaded",
703                          resource.ignore_shaded))
704     resource.ignore_shaded = true;
705
706   if (! config->getValue(screenstr + "placementIgnoreMaximized",
707                          resource.ignore_maximized))
708     resource.ignore_maximized = true;
709
710   if (! config->getValue(screenstr + "disableBindingsWithScrollLock",
711                        resource.allow_scroll_lock))
712     resource.allow_scroll_lock = false;
713
714   if (! config->getValue(screenstr + "workspaceWarping",
715                          resource.workspace_warping))
716     resource.workspace_warping = false;
717
718   resource.root_scroll = NormalScroll;
719   if (config->getValue(screenstr + "rootScrollDirection", s)) {
720     if (s == "None")
721       resource.root_scroll = NoScroll;
722     else if (s == "Reverse")
723       resource.root_scroll = ReverseScroll;
724   }
725 }
726
727
728 void BScreen::changeWorkspaceCount(unsigned int new_count) {
729   assert(new_count > 0);
730
731   if (new_count < workspacesList.size()) {
732     // shrink
733     for (unsigned int i = workspacesList.size(); i > new_count; --i)
734       removeLastWorkspace();
735     // removeLast already sets the current workspace to the 
736     // last available one.
737   } else if (new_count > workspacesList.size()) {
738     // grow
739     for(unsigned int i = workspacesList.size(); i < new_count; ++i)
740       addWorkspace();
741   }
742 }
743
744
745 void BScreen::reconfigure(void) {
746   // don't reconfigure while saving the initial rc file, it's a waste and it
747   // breaks somethings (workspace names)
748   if (blackbox->isStartup()) return;
749
750   load_rc();
751   LoadStyle();
752
753   // we need to do this explicitly, because just loading this value from the rc
754   // does nothing
755   changeWorkspaceCount(resource.workspaces);
756
757   XGCValues gcv;
758   gcv.foreground = WhitePixel(blackbox->getXDisplay(),
759                               getScreenNumber());
760   gcv.function = GXinvert;
761   gcv.subwindow_mode = IncludeInferiors;
762   XChangeGC(blackbox->getXDisplay(), opGC,
763             GCForeground | GCFunction | GCSubwindowMode, &gcv);
764
765   const char *s = "0: 0000 x 0: 0000";
766
767   geom_w = resource.wstyle.font->measureString(s) + resource.bevel_width * 2;
768   geom_h = resource.wstyle.font->height() + resource.bevel_width * 2;
769
770   BTexture* texture = &(resource.wstyle.l_focus);
771   geom_pixmap = texture->render(geom_w, geom_h, geom_pixmap);
772   if (geom_pixmap == ParentRelative) {
773     texture = &(resource.wstyle.t_focus);
774     geom_pixmap = texture->render(geom_w, geom_h, geom_pixmap);
775   }
776   if (! geom_pixmap)
777     XSetWindowBackground(blackbox->getXDisplay(), geom_window,
778                          texture->color().pixel());
779   else
780     XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
781                                geom_window, geom_pixmap);
782
783   XSetWindowBorderWidth(blackbox->getXDisplay(), geom_window,
784                         resource.border_width);
785   XSetWindowBorder(blackbox->getXDisplay(), geom_window,
786                    resource.border_color.pixel());
787
788   typedef std::vector<int> SubList;
789   SubList remember_subs;
790
791   raiseWindows(0, 0);
792
793   std::for_each(workspacesList.begin(), workspacesList.end(),
794                 std::mem_fun(&Workspace::reconfigure));
795
796   BlackboxWindowList::iterator iit = iconList.begin();
797   for (; iit != iconList.end(); ++iit) {
798     BlackboxWindow *bw = *iit;
799     if (bw->validateClient())
800       bw->reconfigure();
801   }
802
803   image_control->timeout();
804 }
805
806
807 void BScreen::LoadStyle(void) {
808   Configuration style(False);
809
810   const char *sfile = blackbox->getStyleFilename();
811   if (sfile != NULL) {
812     style.setFile(sfile);
813     if (! style.load()) {
814       style.setFile(DEFAULTSTYLE);
815       if (! style.load())
816         style.create();  // hardcoded default values will be used.
817     }
818   }
819
820   // merge in the rc file
821   style.merge(config->file(), True);
822
823   string s;
824
825   // load fonts/fontsets
826   if (resource.wstyle.font)
827     delete resource.wstyle.font;
828
829   resource.wstyle.font = readDatabaseFont("window.", style);
830
831   // load window config
832   resource.wstyle.t_focus =
833     readDatabaseTexture("window.title.focus", "white", style);
834   resource.wstyle.t_unfocus =
835     readDatabaseTexture("window.title.unfocus", "black", style);
836   resource.wstyle.l_focus =
837     readDatabaseTexture("window.label.focus", "white", style);
838   resource.wstyle.l_unfocus =
839     readDatabaseTexture("window.label.unfocus", "black", style);
840   resource.wstyle.h_focus =
841     readDatabaseTexture("window.handle.focus", "white", style);
842   resource.wstyle.h_unfocus =
843     readDatabaseTexture("window.handle.unfocus", "black", style);
844   resource.wstyle.g_focus =
845     readDatabaseTexture("window.grip.focus", "white", style);
846   resource.wstyle.g_unfocus =
847     readDatabaseTexture("window.grip.unfocus", "black", style);
848   resource.wstyle.b_focus =
849     readDatabaseTexture("window.button.focus", "white", style);
850   resource.wstyle.b_unfocus =
851     readDatabaseTexture("window.button.unfocus", "black", style);
852   resource.wstyle.b_pressed =
853     readDatabaseTexture("window.button.pressed", "black", style);
854
855   //if neither of these can be found, we will use the previous resource
856   resource.wstyle.b_pressed_focus =
857     readDatabaseTexture("window.button.pressed.focus", "black", style, true);
858   resource.wstyle.b_pressed_unfocus =
859     readDatabaseTexture("window.button.pressed.unfocus", "black", style, true);
860
861 #ifdef    BITMAPBUTTONS
862   if (resource.wstyle.close_button.mask != None)
863     XFreePixmap(blackbox->getXDisplay(), resource.wstyle.close_button.mask);
864   if (resource.wstyle.max_button.mask != None)
865     XFreePixmap(blackbox->getXDisplay(), resource.wstyle.max_button.mask);
866   if (resource.wstyle.icon_button.mask != None)
867     XFreePixmap(blackbox->getXDisplay(), resource.wstyle.icon_button.mask);
868   if (resource.wstyle.stick_button.mask != None)
869     XFreePixmap(blackbox->getXDisplay(), resource.wstyle.stick_button.mask);
870
871   resource.wstyle.close_button.mask = resource.wstyle.max_button.mask =
872     resource.wstyle.icon_button.mask =
873     resource.wstyle.icon_button.mask = None;
874   
875   readDatabaseMask("window.button.close.mask", resource.wstyle.close_button,
876                    style);
877   readDatabaseMask("window.button.max.mask", resource.wstyle.max_button,
878                    style);
879   readDatabaseMask("window.button.icon.mask", resource.wstyle.icon_button,
880                    style);
881   readDatabaseMask("window.button.stick.mask", resource.wstyle.stick_button,
882                    style);
883 #endif // BITMAPBUTTONS
884
885   // we create the window.frame texture by hand because it exists only to
886   // make the code cleaner and is not actually used for display
887   BColor color = readDatabaseColor("window.frame.focusColor", "white", style);
888   resource.wstyle.f_focus = BTexture("solid flat", getBaseDisplay(),
889                                      getScreenNumber(), image_control);
890   resource.wstyle.f_focus.setColor(color);
891
892   color = readDatabaseColor("window.frame.unfocusColor", "white", style);
893   resource.wstyle.f_unfocus = BTexture("solid flat", getBaseDisplay(),
894                                        getScreenNumber(), image_control);
895   resource.wstyle.f_unfocus.setColor(color);
896
897   resource.wstyle.l_text_focus =
898     readDatabaseColor("window.label.focus.textColor", "black", style);
899   resource.wstyle.l_text_unfocus =
900     readDatabaseColor("window.label.unfocus.textColor", "white", style);
901   resource.wstyle.b_pic_focus =
902     readDatabaseColor("window.button.focus.picColor", "black", style);
903   resource.wstyle.b_pic_unfocus =
904     readDatabaseColor("window.button.unfocus.picColor", "white", style);
905
906   resource.wstyle.justify = LeftJustify;
907   if (style.getValue("window.justify", s)) {
908     if (s == "right" || s == "Right")
909       resource.wstyle.justify = RightJustify;
910     else if (s == "center" || s == "Center")
911       resource.wstyle.justify = CenterJustify;
912   }
913
914   // sanity checks
915   if (resource.wstyle.t_focus.texture() == BTexture::Parent_Relative)
916     resource.wstyle.t_focus = resource.wstyle.f_focus;
917   if (resource.wstyle.t_unfocus.texture() == BTexture::Parent_Relative)
918     resource.wstyle.t_unfocus = resource.wstyle.f_unfocus;
919   if (resource.wstyle.h_focus.texture() == BTexture::Parent_Relative)
920     resource.wstyle.h_focus = resource.wstyle.f_focus;
921   if (resource.wstyle.h_unfocus.texture() == BTexture::Parent_Relative)
922     resource.wstyle.h_unfocus = resource.wstyle.f_unfocus;
923
924   resource.border_color =
925     readDatabaseColor("borderColor", "black", style);
926
927   // load bevel, border and handle widths
928   if (! style.getValue("handleWidth", resource.handle_width) ||
929       resource.handle_width > (getWidth() / 2) || resource.handle_width == 0)
930     resource.handle_width = 6;
931
932   if (! style.getValue("borderWidth", resource.border_width))
933     resource.border_width = 1;
934
935   if (! style.getValue("bevelWidth", resource.bevel_width) ||
936       resource.bevel_width > (getWidth() / 2) || resource.bevel_width == 0)
937     resource.bevel_width = 3;
938
939   if (! style.getValue("frameWidth", resource.frame_width) ||
940       resource.frame_width > (getWidth() / 2))
941     resource.frame_width = resource.bevel_width;
942
943   if (style.getValue("rootCommand", s))
944     bexec(s, displayString());
945 }
946
947
948 void BScreen::addIcon(BlackboxWindow *w) {
949   if (! w) return;
950
951   w->setWorkspace(BSENTINEL);
952   w->setWindowNumber(iconList.size());
953
954   iconList.push_back(w);
955 }
956
957
958 void BScreen::removeIcon(BlackboxWindow *w) {
959   if (! w) return;
960
961   iconList.remove(w);
962
963   BlackboxWindowList::iterator it = iconList.begin(),
964     end = iconList.end();
965   for (int i = 0; it != end; ++it)
966     (*it)->setWindowNumber(i++);
967 }
968
969
970 BlackboxWindow *BScreen::getIcon(unsigned int index) {
971   if (index < iconList.size()) {
972     BlackboxWindowList::iterator it = iconList.begin();
973     while (index-- > 0) // increment to index
974       ++it;
975     return *it;
976   }
977
978   return (BlackboxWindow *) 0;
979 }
980
981
982 unsigned int BScreen::addWorkspace(void) {
983   Workspace *wkspc = new Workspace(this, workspacesList.size());
984   workspacesList.push_back(wkspc);
985   saveWorkspaces(getWorkspaceCount());
986   saveWorkspaceNames();
987
988   return workspacesList.size();
989 }
990
991
992 unsigned int BScreen::removeLastWorkspace(void) {
993   if (workspacesList.size() == 1)
994     return 1;
995
996   Workspace *wkspc = workspacesList.back();
997
998   if (current_workspace->getID() == wkspc->getID())
999     changeWorkspaceID(current_workspace->getID() - 1);
1000
1001   wkspc->removeAll();
1002
1003   workspacesList.pop_back();
1004   delete wkspc;
1005
1006   saveWorkspaces(getWorkspaceCount());
1007   saveWorkspaceNames();
1008
1009   updateNetizenWorkspaceCount();
1010
1011   return workspacesList.size();
1012 }
1013
1014
1015 void BScreen::changeWorkspaceID(unsigned int id) {
1016   if (! current_workspace || id == current_workspace->getID()) return;
1017
1018   BlackboxWindow *focused = blackbox->getFocusedWindow();
1019   if (focused && focused->getScreen() == this) {
1020     assert(focused->isStuck() ||
1021            focused->getWorkspaceNumber() == current_workspace->getID());
1022
1023     current_workspace->setLastFocusedWindow(focused);
1024   } else {
1025     // if no window had focus, no need to store a last focus
1026     current_workspace->setLastFocusedWindow((BlackboxWindow *) 0);
1027   }
1028
1029   // when we switch workspaces, unfocus whatever was focused if it is going
1030   // to be unmapped
1031   if (focused && ! focused->isStuck())
1032     blackbox->setFocusedWindow((BlackboxWindow *) 0);
1033
1034   current_workspace->hideAll();
1035
1036   current_workspace = getWorkspace(id);
1037
1038   xatom->setValue(getRootWindow(), XAtom::net_current_desktop,
1039                   XAtom::cardinal, id);
1040
1041   current_workspace->showAll();
1042
1043   int x, y, rx, ry;
1044   Window c, r;
1045   unsigned int m;
1046   BlackboxWindow *win = (BlackboxWindow *) 0;
1047   bool f = False;
1048
1049   XSync(blackbox->getXDisplay(), False);
1050
1051   // If sloppy focus and we can find the client window under the pointer,
1052   // try to focus it.  
1053   if (resource.sloppy_focus &&
1054       XQueryPointer(blackbox->getXDisplay(), getRootWindow(), &r, &c,
1055                     &rx, &ry, &x, &y, &m) &&
1056       c != None) {
1057     if ( (win = blackbox->searchWindow(c)) )
1058       f = win->setInputFocus();
1059   }
1060
1061   // If that fails, and we're doing focus_last, try to focus the last window.
1062   if (! f && resource.focus_last &&
1063       (win = current_workspace->getLastFocusedWindow()))
1064     f = win->setInputFocus();
1065
1066   /*
1067     if we found a focus target, then we set the focused window explicitly
1068     because it is possible to switch off this workspace before the x server
1069     generates the FocusIn event for the window. if that happens, openbox would
1070     lose track of what window was the 'LastFocused' window on the workspace.
1071
1072     if we did not find a focus target, then set the current focused window to
1073     nothing.
1074   */
1075   if (f)
1076     blackbox->setFocusedWindow(win);
1077   else
1078     blackbox->setFocusedWindow((BlackboxWindow *) 0);
1079 }
1080
1081
1082 /*
1083  * Set the _NET_CLIENT_LIST root window property.
1084  */
1085 void BScreen::updateClientList(void) {
1086   if (windowList.size() > 0) {
1087     Window *windows = new Window[windowList.size()];
1088     Window *win_it = windows;
1089     BlackboxWindowList::iterator it = windowList.begin();
1090     const BlackboxWindowList::iterator end = windowList.end();
1091     for (; it != end; ++it, ++win_it)
1092       *win_it = (*it)->getClientWindow();
1093     xatom->setValue(getRootWindow(), XAtom::net_client_list, XAtom::window,
1094                     windows, windowList.size());
1095     delete [] windows;
1096   } else
1097     xatom->setValue(getRootWindow(), XAtom::net_client_list, XAtom::window,
1098                     0, 0);
1099
1100   updateStackingList();
1101 }
1102
1103
1104 /*
1105  * Set the _NET_CLIENT_LIST_STACKING root window property.
1106  */
1107 void BScreen::updateStackingList(void) {
1108
1109   BlackboxWindowList stack_order;
1110
1111   /*
1112    * Get the stacking order from all of the workspaces.
1113    * We start with the current workspace so that the sticky windows will be
1114    * in the right order on the current workspace.
1115    * XXX: Do we need to have sticky windows in the list once for each workspace?
1116    */
1117   getCurrentWorkspace()->appendStackOrder(stack_order);
1118   for (unsigned int i = 0; i < getWorkspaceCount(); ++i)
1119     if (i != getCurrentWorkspaceID())
1120       getWorkspace(i)->appendStackOrder(stack_order);
1121
1122   if (stack_order.size() > 0) {
1123     // set the client list atoms
1124     Window *windows = new Window[stack_order.size()];
1125     Window *win_it = windows;
1126     BlackboxWindowList::iterator it = stack_order.begin(),
1127                                  end = stack_order.end();
1128     for (; it != end; ++it, ++win_it)
1129       *win_it = (*it)->getClientWindow();
1130     xatom->setValue(getRootWindow(), XAtom::net_client_list_stacking,
1131                     XAtom::window, windows, stack_order.size());
1132     delete [] windows;
1133   } else
1134     xatom->setValue(getRootWindow(), XAtom::net_client_list_stacking,
1135                     XAtom::window, 0, 0);
1136 }
1137
1138
1139 void BScreen::addSystrayWindow(Window window) {
1140   XGrabServer(blackbox->getXDisplay());
1141   
1142   XSelectInput(blackbox->getXDisplay(), window, StructureNotifyMask);
1143   systrayWindowList.push_back(window);
1144   xatom->setValue(getRootWindow(), XAtom::kde_net_system_tray_windows,
1145                   XAtom::window,
1146                   &systrayWindowList[0], systrayWindowList.size());
1147   blackbox->saveSystrayWindowSearch(window, this);
1148
1149   XUngrabServer(blackbox->getXDisplay());
1150 }
1151
1152
1153 void BScreen::removeSystrayWindow(Window window) {
1154   XGrabServer(blackbox->getXDisplay());
1155   
1156   WindowList::iterator it = systrayWindowList.begin();
1157   const WindowList::iterator end = systrayWindowList.end();
1158   for (; it != end; ++it)
1159     if (*it == window) {
1160       systrayWindowList.erase(it);
1161       xatom->setValue(getRootWindow(), XAtom::kde_net_system_tray_windows,
1162                       XAtom::window,
1163                       &systrayWindowList[0], systrayWindowList.size());
1164       blackbox->removeSystrayWindowSearch(window);
1165       XSelectInput(blackbox->getXDisplay(), window, NoEventMask);
1166       break;
1167     }
1168
1169   assert(it != end);    // not a systray window
1170
1171   XUngrabServer(blackbox->getXDisplay());
1172 }
1173
1174
1175 void BScreen::manageWindow(Window w) {
1176   // is the window a KDE systray window?
1177   Window systray;
1178   if (xatom->getValue(w, XAtom::kde_net_wm_system_tray_window_for,
1179                       XAtom::window, systray) && systray != None) {
1180     addSystrayWindow(w);
1181     return;
1182   }
1183
1184   // is the window a docking app
1185   XWMHints *wmhint = XGetWMHints(blackbox->getXDisplay(), w);
1186   if (wmhint && (wmhint->flags & StateHint) &&
1187       wmhint->initial_state == WithdrawnState) {
1188     //slit->addClient(w);
1189     return;
1190   }
1191
1192   new BlackboxWindow(blackbox, w, this);
1193
1194   BlackboxWindow *win = blackbox->searchWindow(w);
1195   if (! win)
1196     return;
1197
1198   if (win->isDesktop()) {
1199     desktopWindowList.push_back(win->getFrameWindow());
1200   } else { // if (win->isNormal()) {
1201     // don't list desktop windows as managed windows
1202     windowList.push_back(win);
1203     updateClientList();
1204   
1205     if (win->isTopmost())
1206       specialWindowList.push_back(win->getFrameWindow());
1207   }
1208   
1209   XMapRequestEvent mre;
1210   mre.window = w;
1211   if (blackbox->isStartup() && win->isNormal()) win->restoreAttributes();
1212   win->mapRequestEvent(&mre);
1213 }
1214
1215
1216 void BScreen::unmanageWindow(BlackboxWindow *w, bool remap) {
1217   // is the window a KDE systray window?
1218   Window systray;
1219   if (xatom->getValue(w->getClientWindow(),
1220                       XAtom::kde_net_wm_system_tray_window_for,
1221                       XAtom::window, systray) && systray != None) {
1222     removeSystrayWindow(w->getClientWindow());
1223     return;
1224   }
1225
1226   w->restore(remap);
1227
1228   // Remove the modality so that its parent won't try to re-focus the window
1229   if (w->isModal()) w->setModal(False);
1230   
1231   if (w->getWorkspaceNumber() != BSENTINEL &&
1232       w->getWindowNumber() != BSENTINEL) {
1233     getWorkspace(w->getWorkspaceNumber())->removeWindow(w);
1234     if (w->isStuck()) {
1235       for (unsigned int i = 0; i < getNumberOfWorkspaces(); ++i)
1236         if (i != w->getWorkspaceNumber())
1237           getWorkspace(i)->removeWindow(w, True);
1238     }
1239   } else if (w->isIconic())
1240     removeIcon(w);
1241
1242   if (w->isDesktop()) {
1243     WindowList::iterator it = desktopWindowList.begin();
1244     const WindowList::iterator end = desktopWindowList.end();
1245     for (; it != end; ++it)
1246       if (*it == w->getFrameWindow()) {
1247         desktopWindowList.erase(it);
1248         break;
1249       }
1250     assert(it != end);  // the window wasnt a desktop window?
1251   } else { // if (w->isNormal()) {
1252     // we don't list desktop windows as managed windows
1253     windowList.remove(w);
1254     updateClientList();
1255
1256     if (w->isTopmost()) {
1257       WindowList::iterator it = specialWindowList.begin();
1258       const WindowList::iterator end = specialWindowList.end();
1259       for (; it != end; ++it)
1260         if (*it == w->getFrameWindow()) {
1261           specialWindowList.erase(it);
1262           break;
1263         }
1264       assert(it != end);  // the window wasnt a special window?
1265     }
1266   }
1267
1268   if (blackbox->getFocusedWindow() == w)
1269     blackbox->setFocusedWindow((BlackboxWindow *) 0);
1270
1271   /*
1272     some managed windows can also be window group controllers.  when
1273     unmanaging such windows, we should also delete the window group.
1274   */
1275   BWindowGroup *group = blackbox->searchGroup(w->getClientWindow());
1276   delete group;
1277
1278   delete w;
1279 }
1280
1281
1282 void BScreen::updateWorkArea(void) {
1283   if (workspacesList.size() > 0) {
1284     unsigned long *dims = new unsigned long[4 * workspacesList.size()];
1285     for (unsigned int i = 0, m = workspacesList.size(); i < m; ++i) {
1286       // XXX: this could be different for each workspace
1287       const Rect &area = availableArea();
1288       dims[(i * 4) + 0] = area.x();
1289       dims[(i * 4) + 1] = area.y();
1290       dims[(i * 4) + 2] = area.width();
1291       dims[(i * 4) + 3] = area.height();
1292     }
1293     xatom->setValue(getRootWindow(), XAtom::net_workarea, XAtom::cardinal,
1294                     dims, 4 * workspacesList.size());
1295     delete [] dims;
1296   } else
1297     xatom->setValue(getRootWindow(), XAtom::net_workarea, XAtom::cardinal,
1298                     0, 0);
1299 }
1300
1301
1302 void BScreen::updateNetizenWorkspaceCount(void) {
1303   xatom->setValue(getRootWindow(), XAtom::net_number_of_desktops,
1304                   XAtom::cardinal, workspacesList.size());
1305
1306   updateWorkArea();
1307 }
1308
1309
1310 void BScreen::updateNetizenWindowFocus(void) {
1311   Window f = ((blackbox->getFocusedWindow()) ?
1312               blackbox->getFocusedWindow()->getClientWindow() : None);
1313
1314   xatom->setValue(getRootWindow(), XAtom::net_active_window,
1315                   XAtom::window, f);
1316 }
1317
1318
1319 void BScreen::raiseWindows(Window *workspace_stack, unsigned int num) {
1320   // the 13 represents the number of blackbox windows such as menus
1321   int bbwins = 15;
1322 #ifdef    XINERAMA
1323   ++bbwins;
1324 #endif // XINERAMA
1325
1326   Window *session_stack = new
1327     Window[(num + specialWindowList.size() + bbwins)];
1328   unsigned int i = 0, k = num;
1329
1330   WindowList::iterator sit, send = specialWindowList.end();
1331   for (sit = specialWindowList.begin(); sit != send; ++sit)
1332     *(session_stack + i++) = *sit;
1333
1334   while (k--)
1335     *(session_stack + i++) = *(workspace_stack + k);
1336
1337   XRestackWindows(blackbox->getXDisplay(), session_stack, i);
1338
1339   delete [] session_stack;
1340
1341   updateStackingList();
1342 }
1343
1344
1345 void BScreen::lowerWindows(Window *workspace_stack, unsigned int num) {
1346   assert(num > 0);  // this would cause trouble in the XRaiseWindow call
1347
1348   Window *session_stack = new Window[(num + desktopWindowList.size())];
1349   unsigned int i = 0, k = num;
1350
1351   XLowerWindow(blackbox->getXDisplay(), workspace_stack[0]);
1352
1353   while (k--)
1354     *(session_stack + i++) = *(workspace_stack + k);
1355
1356   WindowList::iterator dit = desktopWindowList.begin();
1357   const WindowList::iterator d_end = desktopWindowList.end();
1358   for (; dit != d_end; ++dit)
1359     *(session_stack + i++) = *dit;
1360
1361   XRestackWindows(blackbox->getXDisplay(), session_stack, i);
1362
1363   delete [] session_stack;
1364
1365   updateStackingList();
1366 }
1367
1368
1369 void BScreen::reassociateWindow(BlackboxWindow *w, unsigned int wkspc_id,
1370                                 bool ignore_sticky) {
1371   if (! w) return;
1372
1373   if (wkspc_id == BSENTINEL)
1374     wkspc_id = current_workspace->getID();
1375
1376   if (w->getWorkspaceNumber() == wkspc_id)
1377     return;
1378
1379   if (w->isIconic()) {
1380     removeIcon(w);
1381     getWorkspace(wkspc_id)->addWindow(w);
1382     if (w->isStuck())
1383       for (unsigned int i = 0; i < getNumberOfWorkspaces(); ++i)
1384         if (i != w->getWorkspaceNumber())
1385           getWorkspace(i)->addWindow(w, True);
1386   } else if (ignore_sticky || ! w->isStuck()) {
1387     if (w->isStuck())
1388       w->stick();
1389     getWorkspace(w->getWorkspaceNumber())->removeWindow(w);
1390     getWorkspace(wkspc_id)->addWindow(w);
1391   }
1392   updateStackingList();
1393 }
1394
1395
1396 void BScreen::propagateWindowName(const BlackboxWindow *bw) {
1397   if (bw->isIconic()) {
1398   } else {
1399   }
1400 }
1401
1402
1403 void BScreen::nextFocus(void) const {
1404   BlackboxWindow *focused = blackbox->getFocusedWindow(),
1405     *next = focused;
1406
1407   if (focused &&
1408       focused->getScreen()->getScreenNumber() == getScreenNumber() &&
1409       current_workspace->getCount() > 1) {
1410     do {
1411       next = current_workspace->getNextWindowInList(next);
1412     } while (next != focused && ! next->setInputFocus());
1413
1414     if (next != focused)
1415       current_workspace->raiseWindow(next);
1416   } else if (current_workspace->getCount() > 0) {
1417     next = current_workspace->getTopWindowOnStack();
1418     next->setInputFocus();
1419     current_workspace->raiseWindow(next);
1420   }
1421 }
1422
1423
1424 void BScreen::prevFocus(void) const {
1425   BlackboxWindow *focused = blackbox->getFocusedWindow(),
1426     *next = focused;
1427
1428   if (focused) {
1429     // if window is not on this screen, ignore it
1430     if (focused->getScreen()->getScreenNumber() != getScreenNumber())
1431       focused = (BlackboxWindow*) 0;
1432   }
1433   
1434   if (focused &&
1435       focused->getScreen()->getScreenNumber() == getScreenNumber() &&
1436       current_workspace->getCount() > 1) {
1437     // next is the next window to receive focus, current is a place holder
1438     do {
1439       next = current_workspace->getPrevWindowInList(next);
1440     } while (next != focused && ! next->setInputFocus());
1441
1442     if (next != focused)
1443       current_workspace->raiseWindow(next);
1444   } else if (current_workspace->getCount() > 0) {
1445     next = current_workspace->getTopWindowOnStack();
1446     next->setInputFocus();
1447     current_workspace->raiseWindow(next);
1448   }
1449 }
1450
1451
1452 void BScreen::raiseFocus(void) const {
1453   BlackboxWindow *focused = blackbox->getFocusedWindow();
1454   if (! focused)
1455     return;
1456
1457   // if on this Screen, raise it
1458   if (focused->getScreen()->getScreenNumber() == getScreenNumber()) {
1459     Workspace *workspace = getWorkspace(focused->getWorkspaceNumber());
1460     workspace->raiseWindow(focused);
1461   }
1462 }
1463
1464
1465 void BScreen::shutdown(void) {
1466   XSelectInput(blackbox->getXDisplay(), getRootWindow(), NoEventMask);
1467   XSync(blackbox->getXDisplay(), False);
1468
1469   while(! windowList.empty())
1470     unmanageWindow(windowList.front(), True);
1471
1472   while(! desktopWindowList.empty()) {
1473     BlackboxWindow *win = blackbox->searchWindow(desktopWindowList.front());
1474     assert(win);
1475     unmanageWindow(win, True);
1476   }
1477 }
1478
1479
1480 void BScreen::showPosition(int x, int y) {
1481   if (! geom_visible) {
1482     XMoveResizeWindow(blackbox->getXDisplay(), geom_window,
1483                       (getWidth() - geom_w) / 2,
1484                       (getHeight() - geom_h) / 2, geom_w, geom_h);
1485     XMapWindow(blackbox->getXDisplay(), geom_window);
1486     XRaiseWindow(blackbox->getXDisplay(), geom_window);
1487
1488     geom_visible = True;
1489   }
1490
1491   char label[1024];
1492
1493   sprintf(label, "X: %4d x Y: %4d", x, y);
1494
1495   XClearWindow(blackbox->getXDisplay(), geom_window);
1496
1497   resource.wstyle.font->drawString(geom_window,
1498                                    resource.bevel_width, resource.bevel_width,
1499                                    resource.wstyle.l_text_focus,
1500                                    label);
1501 }
1502
1503
1504 void BScreen::showGeometry(unsigned int gx, unsigned int gy) {
1505   if (! geom_visible) {
1506     XMoveResizeWindow(blackbox->getXDisplay(), geom_window,
1507                       (getWidth() - geom_w) / 2,
1508                       (getHeight() - geom_h) / 2, geom_w, geom_h);
1509     XMapWindow(blackbox->getXDisplay(), geom_window);
1510     XRaiseWindow(blackbox->getXDisplay(), geom_window);
1511
1512     geom_visible = True;
1513   }
1514
1515   char label[1024];
1516
1517   sprintf(label, "W: %4d x H: %4d", gx, gy);
1518
1519   XClearWindow(blackbox->getXDisplay(), geom_window);
1520
1521   resource.wstyle.font->drawString(geom_window,
1522                                    resource.bevel_width, resource.bevel_width,
1523                                    resource.wstyle.l_text_focus,
1524                                    label);
1525 }
1526
1527
1528 void BScreen::hideGeometry(void) {
1529   if (geom_visible) {
1530     XUnmapWindow(blackbox->getXDisplay(), geom_window);
1531     geom_visible = False;
1532   }
1533 }
1534
1535
1536 void BScreen::addStrut(Strut *strut) {
1537   strutList.push_back(strut);
1538 }
1539
1540
1541 void BScreen::removeStrut(Strut *strut) {
1542   strutList.remove(strut);
1543 }
1544
1545
1546 const Rect& BScreen::availableArea(void) const {
1547   if (doFullMax())
1548     return getRect(); // return the full screen
1549   return usableArea;
1550 }
1551
1552
1553 #ifdef    XINERAMA
1554 const RectList& BScreen::allAvailableAreas(void) const {
1555   assert(isXineramaActive());
1556   assert(xineramaUsableArea.size() > 0);
1557   fprintf(stderr, "1found x %d y %d w %d h %d\n",
1558           xineramaUsableArea[0].x(), xineramaUsableArea[0].y(),
1559           xineramaUsableArea[0].width(), xineramaUsableArea[0].height());
1560   return xineramaUsableArea;
1561 }
1562 #endif // XINERAMA
1563
1564
1565 void BScreen::updateAvailableArea(void) {
1566   Rect old_area = usableArea;
1567   usableArea = getRect(); // reset to full screen
1568
1569 #ifdef    XINERAMA
1570   // reset to the full areas
1571   if (isXineramaActive())
1572     xineramaUsableArea = getXineramaAreas();
1573 #endif // XINERAMA
1574
1575   /* these values represent offsets from the screen edge
1576    * we look for the biggest offset on each edge and then apply them
1577    * all at once
1578    * do not be confused by the similarity to the names of Rect's members
1579    */
1580   unsigned int current_left = 0, current_right = 0, current_top = 0,
1581     current_bottom = 0;
1582
1583   StrutList::const_iterator it = strutList.begin(), end = strutList.end();
1584
1585   for(; it != end; ++it) {
1586     Strut *strut = *it;
1587     if (strut->left > current_left)
1588       current_left = strut->left;
1589     if (strut->top > current_top)
1590       current_top = strut->top;
1591     if (strut->right > current_right)
1592       current_right = strut->right;
1593     if (strut->bottom > current_bottom)
1594       current_bottom = strut->bottom;
1595   }
1596
1597   usableArea.setPos(current_left, current_top);
1598   usableArea.setSize(usableArea.width() - (current_left + current_right),
1599                      usableArea.height() - (current_top + current_bottom));
1600
1601 #ifdef    XINERAMA
1602   if (isXineramaActive()) {
1603     // keep each of the ximerama-defined areas inside the strut
1604     RectList::iterator xit, xend = xineramaUsableArea.end();
1605     for (xit = xineramaUsableArea.begin(); xit != xend; ++xit) {
1606       if (xit->x() < usableArea.x()) {
1607         xit->setX(usableArea.x());
1608         xit->setWidth(xit->width() - usableArea.x());
1609       }
1610       if (xit->y() < usableArea.y()) {
1611         xit->setY(usableArea.y());
1612         xit->setHeight(xit->height() - usableArea.y());
1613       }
1614       if (xit->x() + xit->width() > usableArea.width())
1615         xit->setWidth(usableArea.width() - xit->x());
1616       if (xit->y() + xit->height() > usableArea.height())
1617         xit->setHeight(usableArea.height() - xit->y());
1618     }
1619   }
1620 #endif // XINERAMA
1621
1622   if (old_area != usableArea) {
1623     BlackboxWindowList::iterator it = windowList.begin(),
1624       end = windowList.end();
1625     for (; it != end; ++it)
1626       if ((*it)->isMaximized()) (*it)->remaximize();
1627   }
1628
1629   updateWorkArea();  
1630 }
1631
1632
1633 Workspace* BScreen::getWorkspace(unsigned int index) const {
1634   assert(index < workspacesList.size());
1635   return workspacesList[index];
1636 }
1637
1638
1639 void BScreen::buttonPressEvent(const XButtonEvent *xbutton) {
1640   if (xbutton->button == 1) {
1641     if (! isRootColormapInstalled())
1642       image_control->installRootColormap();
1643
1644   // mouse wheel up
1645   } else if ((xbutton->button == 4 && resource.root_scroll == NormalScroll) ||
1646              (xbutton->button == 5 && resource.root_scroll == ReverseScroll)) {
1647     if (getCurrentWorkspaceID() >= getWorkspaceCount() - 1)
1648       changeWorkspaceID(0);
1649     else
1650       changeWorkspaceID(getCurrentWorkspaceID() + 1);
1651   // mouse wheel down
1652   } else if ((xbutton->button == 5 && resource.root_scroll == NormalScroll) ||
1653              (xbutton->button == 4 && resource.root_scroll == ReverseScroll)) {
1654     if (getCurrentWorkspaceID() == 0)
1655       changeWorkspaceID(getWorkspaceCount() - 1);
1656     else
1657       changeWorkspaceID(getCurrentWorkspaceID() - 1);
1658   }
1659 }
1660
1661
1662 void BScreen::propertyNotifyEvent(const XPropertyEvent *pe) {
1663   if (pe->atom == xatom->getAtom(XAtom::net_desktop_names)) {
1664     // _NET_WM_DESKTOP_NAMES
1665     WorkspaceList::iterator it = workspacesList.begin();
1666     const WorkspaceList::iterator end = workspacesList.end();
1667     for (; it != end; ++it) {
1668       (*it)->readName(); // re-read its name from the window property
1669       //workspacemenu->changeWorkspaceLabel((*it)->getID(), (*it)->getName());
1670     }
1671     //workspacemenu->update();
1672     saveWorkspaceNames();
1673   }
1674 }
1675
1676
1677 void BScreen::toggleFocusModel(FocusModel model) {
1678   std::for_each(windowList.begin(), windowList.end(),
1679                 std::mem_fun(&BlackboxWindow::ungrabButtons));
1680
1681   if (model == SloppyFocus) {
1682     saveSloppyFocus(True);
1683   } else {
1684     // we're cheating here to save writing the config file 3 times
1685     resource.auto_raise = False;
1686     resource.click_raise = False;
1687     saveSloppyFocus(False);
1688   }
1689
1690   std::for_each(windowList.begin(), windowList.end(),
1691                 std::mem_fun(&BlackboxWindow::grabButtons));
1692 }
1693
1694 #ifdef    BITMAPBUTTONS
1695 void BScreen::readDatabaseMask(const string &rname, PixmapMask &pixmapMask,
1696                                const Configuration &style) {
1697   string s;
1698   int hx, hy; //ignored
1699   int ret = BitmapOpenFailed; //default to failure.
1700   
1701   if (style.getValue(rname, s))
1702   {
1703     if (s[0] != '/' && s[0] != '~')
1704     {
1705       std::string xbmFile = std::string("~/.openbox/buttons/") + s;
1706       ret = XReadBitmapFile(blackbox->getXDisplay(), getRootWindow(),
1707                             expandTilde(xbmFile).c_str(), &pixmapMask.w,
1708                             &pixmapMask.h, &pixmapMask.mask, &hx, &hy);
1709     } else
1710       ret = XReadBitmapFile(blackbox->getXDisplay(), getRootWindow(),
1711                             expandTilde(s).c_str(), &pixmapMask.w,
1712                             &pixmapMask.h, &pixmapMask.mask, &hx, &hy);
1713     
1714     if (ret == BitmapSuccess)
1715       return;
1716   }
1717
1718   pixmapMask.mask = None;
1719   pixmapMask.w = pixmapMask.h = 0;
1720 }
1721 #endif // BITMAPSUCCESS
1722
1723 BTexture BScreen::readDatabaseTexture(const string &rname,
1724                                       const string &default_color,
1725                                       const Configuration &style, 
1726                                       bool allowNoTexture) {
1727   BTexture texture;
1728   string s;
1729
1730   if (style.getValue(rname, s))
1731     texture = BTexture(s);
1732   else if (allowNoTexture) //no default
1733     texture.setTexture(BTexture::NoTexture);
1734   else
1735     texture.setTexture(BTexture::Solid | BTexture::Flat);
1736
1737   // associate this texture with this screen
1738   texture.setDisplay(getBaseDisplay(), getScreenNumber());
1739   texture.setImageControl(image_control);
1740
1741   if (texture.texture() != BTexture::NoTexture) {
1742     texture.setColor(readDatabaseColor(rname + ".color", default_color,
1743                                        style));
1744     texture.setColorTo(readDatabaseColor(rname + ".colorTo", default_color,
1745                                          style));
1746     texture.setBorderColor(readDatabaseColor(rname + ".borderColor",
1747                                              default_color, style));
1748   }
1749
1750   return texture;
1751 }
1752
1753
1754 BColor BScreen::readDatabaseColor(const string &rname,
1755                                   const string &default_color,
1756                                   const Configuration &style) {
1757   BColor color;
1758   string s;
1759   if (style.getValue(rname, s))
1760     color = BColor(s, getBaseDisplay(), getScreenNumber());
1761   else
1762     color = BColor(default_color, getBaseDisplay(), getScreenNumber());
1763   return color;
1764 }
1765
1766
1767 BFont *BScreen::readDatabaseFont(const string &rbasename,
1768                                  const Configuration &style) {
1769   string fontname;
1770
1771   string s;
1772
1773   int i;
1774   if (style.getValue(rbasename + "xft.font", s) &&
1775       style.getValue(rbasename + "xft.size", i)) {
1776     string family = s;
1777     bool bold = False;
1778     bool italic = False;
1779     bool dropShadow = False;
1780
1781     if (style.getValue(rbasename + "xft.flags", s)) {
1782       if (s.find("bold") != string::npos)
1783         bold = True;
1784       if (s.find("italic") != string::npos)
1785         italic = True;
1786       if (s.find("shadow") != string::npos)
1787         dropShadow = True;
1788     }
1789     
1790     unsigned char offset = 1;
1791     if (style.getValue(rbasename + "xft.shadow.offset", s)) {
1792       offset = atoi(s.c_str()); //doesn't detect errors
1793       if (offset > CHAR_MAX)
1794         offset = 1;
1795     }
1796
1797     unsigned char tint = 0x40;
1798     if (style.getValue(rbasename + "xft.shadow.tint", s)) {
1799       tint = atoi(s.c_str());
1800     }
1801
1802     
1803     BFont *b = new BFont(blackbox->getXDisplay(), this, family, i, bold,
1804                          italic, dropShadow && resource.shadow_fonts, offset, 
1805                          tint, resource.aa_fonts);
1806     if (b->valid())
1807       return b;
1808     delete b;
1809   }
1810     
1811   exit(2);  // can't continue without a font
1812 }