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