finish conversion to the new otk::OBProperty class with its new interface
[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, otk::OBProperty::cardinal,
117                   (unsigned long) getpid());
118 #endif // HAVE_GETPID
119   unsigned long geometry[] = { getWidth(),
120                                getHeight()};
121   xatom->set(getRootWindow(), otk::OBProperty::net_desktop_geometry,
122              otk::OBProperty::Atom_Cardinal, geometry, 2);
123   unsigned long viewport[] = {0,0};
124   xatom->set(getRootWindow(), otk::OBProperty::net_desktop_viewport,
125              otk::OBProperty::Atom_Cardinal, viewport, 2);
126                   
127
128   XDefineCursor(otk::OBDisplay::display, getRootWindow(),
129                 blackbox->getSessionCursor());
130
131   updateAvailableArea();
132
133   image_control =
134     new otk::BImageControl(Openbox::instance->timerManager(),
135                            this, True, blackbox->getColorsPerChannel(),
136                            blackbox->getCacheLife(), blackbox->getCacheMax());
137   image_control->installRootColormap();
138   root_colormap_installed = True;
139
140   load_rc();
141   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->set(getRootWindow(), otk::OBProperty::net_current_desktop,
197              otk::OBProperty::Atom_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     otk::OBProperty::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->set(getRootWindow(), otk::OBProperty::net_desktop_names,
645                otk::OBProperty::utf8, 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   otk::BImageControl::timeout(image_control);
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->set(getRootWindow(), otk::OBProperty::net_current_desktop,
1017              otk::OBProperty::Atom_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->set(getRootWindow(), otk::OBProperty::net_client_list,
1072                otk::OBProperty::Atom_Window, windows, windowList.size());
1073     delete [] windows;
1074   } else
1075     xatom->set(getRootWindow(), otk::OBProperty::net_client_list,
1076                otk::OBProperty::Atom_Window, 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->set(getRootWindow(), otk::OBProperty::net_client_list_stacking,
1109                otk::OBProperty::Atom_Window, windows, stack_order.size());
1110     delete [] windows;
1111   } else
1112     xatom->set(getRootWindow(), otk::OBProperty::net_client_list_stacking,
1113                otk::OBProperty::Atom_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->set(getRootWindow(), otk::OBProperty::kde_net_system_tray_windows,
1123              otk::OBProperty::Atom_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->set(getRootWindow(),
1140                  otk::OBProperty::kde_net_system_tray_windows,
1141                  otk::OBProperty::Atom_Window,
1142                  &systrayWindowList[0], systrayWindowList.size());
1143       blackbox->removeSystrayWindowSearch(window);
1144       XSelectInput(otk::OBDisplay::display, window, NoEventMask);
1145       break;
1146     }
1147
1148   assert(it != end);    // not a systray window
1149
1150   XUngrabServer(otk::OBDisplay::display);
1151 }
1152
1153
1154 void BScreen::manageWindow(Window w) {
1155   // is the window a KDE systray window?
1156   Window systray;
1157   if (xatom->get(w, otk::OBProperty::kde_net_wm_system_tray_window_for,
1158                  otk::OBProperty::Atom_Window, &systray) &&
1159       systray != None)
1160   {
1161     addSystrayWindow(w);
1162     return;
1163   }
1164
1165   // is the window a docking app
1166   XWMHints *wmhint = XGetWMHints(otk::OBDisplay::display, w);
1167   if (wmhint && (wmhint->flags & StateHint) &&
1168       wmhint->initial_state == WithdrawnState) {
1169     //slit->addClient(w);
1170     return;
1171   }
1172
1173   new BlackboxWindow(blackbox, w, this);
1174
1175   BlackboxWindow *win = blackbox->searchWindow(w);
1176   if (! win)
1177     return;
1178
1179   if (win->isDesktop()) {
1180     desktopWindowList.push_back(win->getFrameWindow());
1181   } else { // if (win->isNormal()) {
1182     // don't list desktop windows as managed windows
1183     windowList.push_back(win);
1184     updateClientList();
1185   
1186     if (win->isTopmost())
1187       specialWindowList.push_back(win->getFrameWindow());
1188   }
1189   
1190   XMapRequestEvent mre;
1191   mre.window = w;
1192   if (blackbox->state() == Openbox::State_Starting &&
1193       win->isNormal())
1194     win->restoreAttributes();
1195   win->mapRequestEvent(&mre);
1196 }
1197
1198
1199 void BScreen::unmanageWindow(BlackboxWindow *w, bool remap) {
1200   // is the window a KDE systray window?
1201   Window systray;
1202   if (xatom->get(w->getClientWindow(),
1203                  otk::OBProperty::kde_net_wm_system_tray_window_for,
1204                  otk::OBProperty::Atom_Window, &systray) &&
1205       systray != None)
1206   {
1207     removeSystrayWindow(w->getClientWindow());
1208     return;
1209   }
1210
1211   w->restore(remap);
1212
1213   // Remove the modality so that its parent won't try to re-focus the window
1214   if (w->isModal()) w->setModal(False);
1215   
1216   if (w->getWorkspaceNumber() != BSENTINEL &&
1217       w->getWindowNumber() != BSENTINEL) {
1218     getWorkspace(w->getWorkspaceNumber())->removeWindow(w);
1219     if (w->isStuck()) {
1220       for (unsigned int i = 0; i < getNumberOfWorkspaces(); ++i)
1221         if (i != w->getWorkspaceNumber())
1222           getWorkspace(i)->removeWindow(w, True);
1223     }
1224   } else if (w->isIconic())
1225     removeIcon(w);
1226
1227   if (w->isDesktop()) {
1228     WindowList::iterator it = desktopWindowList.begin();
1229     const WindowList::iterator end = desktopWindowList.end();
1230     for (; it != end; ++it)
1231       if (*it == w->getFrameWindow()) {
1232         desktopWindowList.erase(it);
1233         break;
1234       }
1235     assert(it != end);  // the window wasnt a desktop window?
1236   } else { // if (w->isNormal()) {
1237     // we don't list desktop windows as managed windows
1238     windowList.remove(w);
1239     updateClientList();
1240
1241     if (w->isTopmost()) {
1242       WindowList::iterator it = specialWindowList.begin();
1243       const WindowList::iterator end = specialWindowList.end();
1244       for (; it != end; ++it)
1245         if (*it == w->getFrameWindow()) {
1246           specialWindowList.erase(it);
1247           break;
1248         }
1249       assert(it != end);  // the window wasnt a special window?
1250     }
1251   }
1252
1253   if (blackbox->getFocusedWindow() == w)
1254     blackbox->setFocusedWindow((BlackboxWindow *) 0);
1255
1256   /*
1257     some managed windows can also be window group controllers.  when
1258     unmanaging such windows, we should also delete the window group.
1259   */
1260   BWindowGroup *group = blackbox->searchGroup(w->getClientWindow());
1261   delete group;
1262
1263   delete w;
1264 }
1265
1266
1267 void BScreen::updateWorkArea(void) {
1268   if (workspacesList.size() > 0) {
1269     unsigned long *dims = new unsigned long[4 * workspacesList.size()];
1270     for (unsigned int i = 0, m = workspacesList.size(); i < m; ++i) {
1271       // XXX: this could be different for each workspace
1272       const otk::Rect &area = availableArea();
1273       dims[(i * 4) + 0] = area.x();
1274       dims[(i * 4) + 1] = area.y();
1275       dims[(i * 4) + 2] = area.width();
1276       dims[(i * 4) + 3] = area.height();
1277     }
1278     xatom->set(getRootWindow(), otk::OBProperty::net_workarea,
1279                otk::OBProperty::Atom_Cardinal,
1280                dims, 4 * workspacesList.size());
1281     delete [] dims;
1282   } else
1283     xatom->set(getRootWindow(), otk::OBProperty::net_workarea,
1284                otk::OBProperty::Atom_Cardinal, 0, 0);
1285 }
1286
1287
1288 void BScreen::updateNetizenWorkspaceCount(void) {
1289   xatom->set(getRootWindow(), otk::OBProperty::net_number_of_desktops,
1290              otk::OBProperty::Atom_Cardinal, workspacesList.size());
1291
1292   updateWorkArea();
1293 }
1294
1295
1296 void BScreen::updateNetizenWindowFocus(void) {
1297   Window f = ((blackbox->getFocusedWindow()) ?
1298               blackbox->getFocusedWindow()->getClientWindow() : None);
1299
1300   xatom->set(getRootWindow(), otk::OBProperty::net_active_window,
1301              otk::OBProperty::Atom_Window, f);
1302 }
1303
1304
1305 void BScreen::raiseWindows(Window *workspace_stack, unsigned int num) {
1306   // the 13 represents the number of blackbox windows such as menus
1307   int bbwins = 15;
1308 #ifdef    XINERAMA
1309   ++bbwins;
1310 #endif // XINERAMA
1311
1312   Window *session_stack = new
1313     Window[(num + specialWindowList.size() + bbwins)];
1314   unsigned int i = 0, k = num;
1315
1316   WindowList::iterator sit, send = specialWindowList.end();
1317   for (sit = specialWindowList.begin(); sit != send; ++sit)
1318     *(session_stack + i++) = *sit;
1319
1320   while (k--)
1321     *(session_stack + i++) = *(workspace_stack + k);
1322
1323   XRestackWindows(otk::OBDisplay::display, session_stack, i);
1324
1325   delete [] session_stack;
1326
1327   updateStackingList();
1328 }
1329
1330
1331 void BScreen::lowerWindows(Window *workspace_stack, unsigned int num) {
1332   assert(num > 0);  // this would cause trouble in the XRaiseWindow call
1333
1334   Window *session_stack = new Window[(num + desktopWindowList.size())];
1335   unsigned int i = 0, k = num;
1336
1337   XLowerWindow(otk::OBDisplay::display, workspace_stack[0]);
1338
1339   while (k--)
1340     *(session_stack + i++) = *(workspace_stack + k);
1341
1342   WindowList::iterator dit = desktopWindowList.begin();
1343   const WindowList::iterator d_end = desktopWindowList.end();
1344   for (; dit != d_end; ++dit)
1345     *(session_stack + i++) = *dit;
1346
1347   XRestackWindows(otk::OBDisplay::display, session_stack, i);
1348
1349   delete [] session_stack;
1350
1351   updateStackingList();
1352 }
1353
1354
1355 void BScreen::reassociateWindow(BlackboxWindow *w, unsigned int wkspc_id,
1356                                 bool ignore_sticky) {
1357   if (! w) return;
1358
1359   if (wkspc_id == BSENTINEL)
1360     wkspc_id = current_workspace->getID();
1361
1362   if (w->getWorkspaceNumber() == wkspc_id)
1363     return;
1364
1365   if (w->isIconic()) {
1366     removeIcon(w);
1367     getWorkspace(wkspc_id)->addWindow(w);
1368     if (w->isStuck())
1369       for (unsigned int i = 0; i < getNumberOfWorkspaces(); ++i)
1370         if (i != w->getWorkspaceNumber())
1371           getWorkspace(i)->addWindow(w, True);
1372   } else if (ignore_sticky || ! w->isStuck()) {
1373     if (w->isStuck())
1374       w->stick();
1375     getWorkspace(w->getWorkspaceNumber())->removeWindow(w);
1376     getWorkspace(wkspc_id)->addWindow(w);
1377   }
1378   updateStackingList();
1379 }
1380
1381
1382 void BScreen::propagateWindowName(const BlackboxWindow *bw) {
1383   if (bw->isIconic()) {
1384   } else {
1385   }
1386 }
1387
1388
1389 void BScreen::nextFocus(void) const {
1390   BlackboxWindow *focused = blackbox->getFocusedWindow(),
1391     *next = focused;
1392
1393   if (focused &&
1394       focused->getScreen()->getScreenNumber() == getScreenNumber() &&
1395       current_workspace->getCount() > 1) {
1396     do {
1397       next = current_workspace->getNextWindowInList(next);
1398     } while (next != focused && ! next->setInputFocus());
1399
1400     if (next != focused)
1401       current_workspace->raiseWindow(next);
1402   } else if (current_workspace->getCount() > 0) {
1403     next = current_workspace->getTopWindowOnStack();
1404     next->setInputFocus();
1405     current_workspace->raiseWindow(next);
1406   }
1407 }
1408
1409
1410 void BScreen::prevFocus(void) const {
1411   BlackboxWindow *focused = blackbox->getFocusedWindow(),
1412     *next = focused;
1413
1414   if (focused) {
1415     // if window is not on this screen, ignore it
1416     if (focused->getScreen()->getScreenNumber() != getScreenNumber())
1417       focused = (BlackboxWindow*) 0;
1418   }
1419   
1420   if (focused &&
1421       focused->getScreen()->getScreenNumber() == getScreenNumber() &&
1422       current_workspace->getCount() > 1) {
1423     // next is the next window to receive focus, current is a place holder
1424     do {
1425       next = current_workspace->getPrevWindowInList(next);
1426     } while (next != focused && ! next->setInputFocus());
1427
1428     if (next != focused)
1429       current_workspace->raiseWindow(next);
1430   } else if (current_workspace->getCount() > 0) {
1431     next = current_workspace->getTopWindowOnStack();
1432     next->setInputFocus();
1433     current_workspace->raiseWindow(next);
1434   }
1435 }
1436
1437
1438 void BScreen::raiseFocus(void) const {
1439   BlackboxWindow *focused = blackbox->getFocusedWindow();
1440   if (! focused)
1441     return;
1442
1443   // if on this Screen, raise it
1444   if (focused->getScreen()->getScreenNumber() == getScreenNumber()) {
1445     Workspace *workspace = getWorkspace(focused->getWorkspaceNumber());
1446     workspace->raiseWindow(focused);
1447   }
1448 }
1449
1450
1451 void BScreen::shutdown(void) {
1452   XSelectInput(otk::OBDisplay::display, getRootWindow(), NoEventMask);
1453   XSync(otk::OBDisplay::display, False);
1454
1455   while(! windowList.empty())
1456     unmanageWindow(windowList.front(), True);
1457
1458   while(! desktopWindowList.empty()) {
1459     BlackboxWindow *win = blackbox->searchWindow(desktopWindowList.front());
1460     assert(win);
1461     unmanageWindow(win, True);
1462   }
1463 }
1464
1465
1466 void BScreen::showPosition(int x, int y) {
1467   if (! geom_visible) {
1468     XMoveResizeWindow(otk::OBDisplay::display, geom_window,
1469                       (getWidth() - geom_w) / 2,
1470                       (getHeight() - geom_h) / 2, geom_w, geom_h);
1471     XMapWindow(otk::OBDisplay::display, geom_window);
1472     XRaiseWindow(otk::OBDisplay::display, geom_window);
1473
1474     geom_visible = True;
1475   }
1476
1477   char label[1024];
1478
1479   sprintf(label, "X: %4d x Y: %4d", x, y);
1480
1481   XClearWindow(otk::OBDisplay::display, geom_window);
1482
1483   resource.wstyle.font->drawString(geom_window,
1484                                    resource.bevel_width, resource.bevel_width,
1485                                    resource.wstyle.l_text_focus,
1486                                    label);
1487 }
1488
1489
1490 void BScreen::showGeometry(unsigned int gx, unsigned int gy) {
1491   if (! geom_visible) {
1492     XMoveResizeWindow(otk::OBDisplay::display, geom_window,
1493                       (getWidth() - geom_w) / 2,
1494                       (getHeight() - geom_h) / 2, geom_w, geom_h);
1495     XMapWindow(otk::OBDisplay::display, geom_window);
1496     XRaiseWindow(otk::OBDisplay::display, geom_window);
1497
1498     geom_visible = True;
1499   }
1500
1501   char label[1024];
1502
1503   sprintf(label, "W: %4d x H: %4d", gx, gy);
1504
1505   XClearWindow(otk::OBDisplay::display, geom_window);
1506
1507   resource.wstyle.font->drawString(geom_window,
1508                                    resource.bevel_width, resource.bevel_width,
1509                                    resource.wstyle.l_text_focus,
1510                                    label);
1511 }
1512
1513
1514 void BScreen::hideGeometry(void) {
1515   if (geom_visible) {
1516     XUnmapWindow(otk::OBDisplay::display, geom_window);
1517     geom_visible = False;
1518   }
1519 }
1520
1521
1522 void BScreen::addStrut(Strut *strut) {
1523   strutList.push_back(strut);
1524 }
1525
1526
1527 void BScreen::removeStrut(Strut *strut) {
1528   strutList.remove(strut);
1529 }
1530
1531
1532 const otk::Rect& BScreen::availableArea(void) const {
1533   if (doFullMax())
1534     return getRect(); // return the full screen
1535   return usableArea;
1536 }
1537
1538
1539 #ifdef    XINERAMA
1540 const RectList& BScreen::allAvailableAreas(void) const {
1541   assert(isXineramaActive());
1542   assert(xineramaUsableArea.size() > 0);
1543   fprintf(stderr, "1found x %d y %d w %d h %d\n",
1544           xineramaUsableArea[0].x(), xineramaUsableArea[0].y(),
1545           xineramaUsableArea[0].width(), xineramaUsableArea[0].height());
1546   return xineramaUsableArea;
1547 }
1548 #endif // XINERAMA
1549
1550
1551 void BScreen::updateAvailableArea(void) {
1552   otk::Rect old_area = usableArea;
1553   usableArea = getRect(); // reset to full screen
1554
1555 #ifdef    XINERAMA
1556   // reset to the full areas
1557   if (isXineramaActive())
1558     xineramaUsableArea = getXineramaAreas();
1559 #endif // XINERAMA
1560
1561   /* these values represent offsets from the screen edge
1562    * we look for the biggest offset on each edge and then apply them
1563    * all at once
1564    * do not be confused by the similarity to the names of Rect's members
1565    */
1566   unsigned int current_left = 0, current_right = 0, current_top = 0,
1567     current_bottom = 0;
1568
1569   StrutList::const_iterator it = strutList.begin(), end = strutList.end();
1570
1571   for(; it != end; ++it) {
1572     Strut *strut = *it;
1573     if (strut->left > current_left)
1574       current_left = strut->left;
1575     if (strut->top > current_top)
1576       current_top = strut->top;
1577     if (strut->right > current_right)
1578       current_right = strut->right;
1579     if (strut->bottom > current_bottom)
1580       current_bottom = strut->bottom;
1581   }
1582
1583   usableArea.setPos(current_left, current_top);
1584   usableArea.setSize(usableArea.width() - (current_left + current_right),
1585                      usableArea.height() - (current_top + current_bottom));
1586
1587 #ifdef    XINERAMA
1588   if (isXineramaActive()) {
1589     // keep each of the ximerama-defined areas inside the strut
1590     RectList::iterator xit, xend = xineramaUsableArea.end();
1591     for (xit = xineramaUsableArea.begin(); xit != xend; ++xit) {
1592       if (xit->x() < usableArea.x()) {
1593         xit->setX(usableArea.x());
1594         xit->setWidth(xit->width() - usableArea.x());
1595       }
1596       if (xit->y() < usableArea.y()) {
1597         xit->setY(usableArea.y());
1598         xit->setHeight(xit->height() - usableArea.y());
1599       }
1600       if (xit->x() + xit->width() > usableArea.width())
1601         xit->setWidth(usableArea.width() - xit->x());
1602       if (xit->y() + xit->height() > usableArea.height())
1603         xit->setHeight(usableArea.height() - xit->y());
1604     }
1605   }
1606 #endif // XINERAMA
1607
1608   if (old_area != usableArea) {
1609     BlackboxWindowList::iterator it = windowList.begin(),
1610       end = windowList.end();
1611     for (; it != end; ++it)
1612       if ((*it)->isMaximized()) (*it)->remaximize();
1613   }
1614
1615   updateWorkArea();  
1616 }
1617
1618
1619 Workspace* BScreen::getWorkspace(unsigned int index) const {
1620   assert(index < workspacesList.size());
1621   return workspacesList[index];
1622 }
1623
1624
1625 void BScreen::buttonPressEvent(const XButtonEvent *xbutton) {
1626   if (xbutton->button == 1) {
1627     if (! isRootColormapInstalled())
1628       image_control->installRootColormap();
1629
1630   // mouse wheel up
1631   } else if ((xbutton->button == 4 && resource.root_scroll == NormalScroll) ||
1632              (xbutton->button == 5 && resource.root_scroll == ReverseScroll)) {
1633     if (getCurrentWorkspaceID() >= getWorkspaceCount() - 1)
1634       changeWorkspaceID(0);
1635     else
1636       changeWorkspaceID(getCurrentWorkspaceID() + 1);
1637   // mouse wheel down
1638   } else if ((xbutton->button == 5 && resource.root_scroll == NormalScroll) ||
1639              (xbutton->button == 4 && resource.root_scroll == ReverseScroll)) {
1640     if (getCurrentWorkspaceID() == 0)
1641       changeWorkspaceID(getWorkspaceCount() - 1);
1642     else
1643       changeWorkspaceID(getCurrentWorkspaceID() - 1);
1644   }
1645 }
1646
1647
1648 void BScreen::propertyNotifyEvent(const XPropertyEvent *pe) {
1649   if (pe->atom == xatom->atom(otk::OBProperty::net_desktop_names)) {
1650     // _NET_WM_DESKTOP_NAMES
1651     WorkspaceList::iterator it = workspacesList.begin();
1652     const WorkspaceList::iterator end = workspacesList.end();
1653     for (; it != end; ++it) {
1654       (*it)->readName(); // re-read its name from the window property
1655       //workspacemenu->changeWorkspaceLabel((*it)->getID(), (*it)->getName());
1656     }
1657     //workspacemenu->update();
1658     saveWorkspaceNames();
1659   }
1660 }
1661
1662
1663 void BScreen::toggleFocusModel(FocusModel model) {
1664   std::for_each(windowList.begin(), windowList.end(),
1665                 std::mem_fun(&BlackboxWindow::ungrabButtons));
1666
1667   if (model == SloppyFocus) {
1668     saveSloppyFocus(True);
1669   } else {
1670     // we're cheating here to save writing the config file 3 times
1671     resource.auto_raise = False;
1672     resource.click_raise = False;
1673     saveSloppyFocus(False);
1674   }
1675
1676   std::for_each(windowList.begin(), windowList.end(),
1677                 std::mem_fun(&BlackboxWindow::grabButtons));
1678 }
1679
1680 void BScreen::readDatabaseMask(const string &rname, PixmapMask &pixmapMask,
1681                                const Configuration &style) {
1682   string s;
1683   int hx, hy; //ignored
1684   int ret = BitmapOpenFailed; //default to failure.
1685   
1686   if (style.getValue(rname, s))
1687   {
1688     if (s[0] != '/' && s[0] != '~')
1689     {
1690       std::string xbmFile = std::string("~/.openbox/buttons/") + s;
1691       ret = XReadBitmapFile(otk::OBDisplay::display, getRootWindow(),
1692                             expandTilde(xbmFile).c_str(), &pixmapMask.w,
1693                             &pixmapMask.h, &pixmapMask.mask, &hx, &hy);
1694     } else
1695       ret = XReadBitmapFile(otk::OBDisplay::display, getRootWindow(),
1696                             expandTilde(s).c_str(), &pixmapMask.w,
1697                             &pixmapMask.h, &pixmapMask.mask, &hx, &hy);
1698     
1699     if (ret == BitmapSuccess)
1700       return;
1701   }
1702
1703   pixmapMask.mask = None;
1704   pixmapMask.w = pixmapMask.h = 0;
1705 }
1706
1707 otk::BTexture BScreen::readDatabaseTexture(const string &rname,
1708                                            const string &default_color,
1709                                            const Configuration &style, 
1710                                            bool allowNoTexture) {
1711   otk::BTexture texture;
1712   string s;
1713
1714   if (style.getValue(rname, s))
1715     texture = otk::BTexture(s);
1716   else if (allowNoTexture) //no default
1717     texture.setTexture(otk::BTexture::NoTexture);
1718   else
1719     texture.setTexture(otk::BTexture::Solid | otk::BTexture::Flat);
1720
1721   // associate this texture with this screen
1722   texture.setScreen(getScreenNumber());
1723   texture.setImageControl(image_control);
1724
1725   if (texture.texture() != otk::BTexture::NoTexture) {
1726     texture.setColor(readDatabaseColor(rname + ".color", default_color,
1727                                        style));
1728     texture.setColorTo(readDatabaseColor(rname + ".colorTo", default_color,
1729                                          style));
1730     texture.setBorderColor(readDatabaseColor(rname + ".borderColor",
1731                                              default_color, style));
1732   }
1733
1734   return texture;
1735 }
1736
1737
1738 otk::BColor BScreen::readDatabaseColor(const string &rname,
1739                                        const string &default_color,
1740                                        const Configuration &style) {
1741   otk::BColor color;
1742   string s;
1743   if (style.getValue(rname, s))
1744     color = otk::BColor(s, getScreenNumber());
1745   else
1746     color = otk::BColor(default_color, getScreenNumber());
1747   return color;
1748 }
1749
1750
1751 otk::BFont *BScreen::readDatabaseFont(const string &rbasename,
1752                                       const Configuration &style) {
1753   string fontname;
1754
1755   string s;
1756
1757   int i;
1758   if (style.getValue(rbasename + "xft.font", s) &&
1759       style.getValue(rbasename + "xft.size", i)) {
1760     string family = s;
1761     bool bold = False;
1762     bool italic = False;
1763     bool dropShadow = False;
1764
1765     if (style.getValue(rbasename + "xft.flags", s)) {
1766       if (s.find("bold") != string::npos)
1767         bold = True;
1768       if (s.find("italic") != string::npos)
1769         italic = True;
1770       if (s.find("shadow") != string::npos)
1771         dropShadow = True;
1772     }
1773     
1774     unsigned char offset = 1;
1775     if (style.getValue(rbasename + "xft.shadow.offset", s)) {
1776       offset = atoi(s.c_str()); //doesn't detect errors
1777       if (offset > CHAR_MAX)
1778         offset = 1;
1779     }
1780
1781     unsigned char tint = 0x40;
1782     if (style.getValue(rbasename + "xft.shadow.tint", s)) {
1783       tint = atoi(s.c_str());
1784     }
1785
1786     
1787     otk::BFont *b = new otk::BFont(getScreenNumber(), family, i, bold, italic,
1788                                    dropShadow && resource.shadow_fonts,
1789                                    offset, tint, resource.aa_fonts);
1790     if (b->valid())
1791       return b;
1792     delete b;
1793   }
1794     
1795   exit(2);  // can't continue without a font
1796 }
1797
1798 }