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