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