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