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