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