]> icculus.org git repositories - dana/openbox.git/blob - src/Workspace.cc
added sticky windows in the wrong place, before the window was even fully initialized...
[dana/openbox.git] / src / Workspace.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2 // Workspace.cc for Blackbox - an X11 Window manager
3 // Copyright (c) 2001 - 2002 Sean 'Shaleh' Perry <shaleh@debian.org>
4 // Copyright (c) 1997 - 2000 Brad Hughes (bhughes@tcac.net)
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a
7 // copy of this software and associated documentation files (the "Software"),
8 // to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 // and/or sell copies of the Software, and to permit persons to whom the
11 // Software is furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 // DEALINGS IN THE SOFTWARE.
23
24 #ifdef    HAVE_CONFIG_H
25 #  include "../config.h"
26 #endif // HAVE_CONFIG_H
27
28 extern "C" {
29 #include <X11/Xlib.h>
30 #include <X11/Xatom.h>
31
32 #ifdef    HAVE_STDIO_H
33 #  include <stdio.h>
34 #endif // HAVE_STDIO_H
35
36 #ifdef HAVE_STRING_H
37 #  include <string.h>
38 #endif // HAVE_STRING_H
39 }
40
41 #include <assert.h>
42
43 #include <functional>
44 #include <string>
45
46 using std::string;
47
48 #include "i18n.hh"
49 #include "blackbox.hh"
50 #include "Clientmenu.hh"
51 #include "Font.hh"
52 #include "Netizen.hh"
53 #include "Screen.hh"
54 #include "Toolbar.hh"
55 #include "Util.hh"
56 #include "Window.hh"
57 #include "Workspace.hh"
58 #include "Windowmenu.hh"
59 #include "XAtom.hh"
60
61
62 Workspace::Workspace(BScreen *scrn, unsigned int i) {
63   screen = scrn;
64   xatom = screen->getBlackbox()->getXAtom();
65
66   cascade_x = cascade_y = 0;
67 #ifdef    XINERAMA
68   cascade_region = 0;
69 #endif // XINERAMA
70
71   id = i;
72
73   clientmenu = new Clientmenu(this);
74
75   lastfocus = (BlackboxWindow *) 0;
76
77   readName();
78 }
79
80
81 void Workspace::addWindow(BlackboxWindow *w, bool place, bool sticky) {
82   assert(w != 0);
83
84   if (place) placeWindow(w);
85
86   stackingList.push_front(w);
87     
88   if (w->isNormal()) {
89     if (! sticky) {
90       w->setWorkspace(id);
91       w->setWindowNumber(windowList.size());
92     }
93
94     windowList.push_back(w);
95
96     clientmenu->insert(w->getTitle());
97     clientmenu->update();
98
99     if (! sticky)
100       screen->updateNetizenWindowAdd(w->getClientWindow(), id);
101
102     if (screen->doFocusNew() || (w->isTransient() && w->getTransientFor() &&
103                                  w->getTransientFor()->isFocused())) {
104       if (id == screen->getCurrentWorkspaceID())
105         w->setInputFocus();
106       else {
107         /*
108            not on the focused workspace, so the window is not going to get focus
109            but if the user wants new windows focused, then it should get focus
110            when this workspace does become focused.
111         */
112         lastfocus = w;
113       }
114     }
115   }
116
117   if (! w->isDesktop())
118     raiseWindow(w);
119   else
120     lowerWindow(w);
121   
122   // if the window is sticky, then it needs to be added on all other
123   // workspaces too!
124   if (! sticky && w->isStuck()) {
125     for (unsigned int i = 0; i < screen->getWorkspaceCount(); ++i)
126       if (i != id)
127         screen->getWorkspace(i)->addWindow(w, place, True);
128   }
129 }
130
131
132 void Workspace::removeWindow(BlackboxWindow *w, bool sticky) {
133   assert(w != 0);
134
135   stackingList.remove(w);
136
137   // pass focus to the next appropriate window
138   if ((w->isFocused() || w == lastfocus) &&
139       ! screen->getBlackbox()->doShutdown()) {
140     focusFallback(w);
141   }
142     
143   // if the window is sticky, then it needs to be removed on all other
144   // workspaces too!
145   if (! sticky && w->isStuck()) {
146     for (unsigned int i = 0; i < screen->getWorkspaceCount(); ++i)
147       if (i != id)
148         screen->getWorkspace(i)->removeWindow(w, True);
149   }
150
151   if (! w->isNormal()) return;
152
153   BlackboxWindowList::iterator it, end = windowList.end();
154   int i;
155   for (i = 0, it = windowList.begin(); it != end; ++it, ++i)
156     if (*it == w)
157       break;
158   assert(it != end);
159   
160   windowList.erase(it);
161   clientmenu->remove(i);
162   clientmenu->update();
163
164   if (! sticky) {
165     screen->updateNetizenWindowDel(w->getClientWindow());
166
167     BlackboxWindowList::iterator it = windowList.begin();
168     const BlackboxWindowList::iterator end = windowList.end();
169     unsigned int i = 0;
170     for (; it != end; ++it, ++i)
171       (*it)->setWindowNumber(i);
172   }
173
174   if (i == 0) {
175     cascade_x = cascade_y = 0;
176 #ifdef    XINERAMA
177     cascade_region = 0;
178 #endif // XINERAMA
179   }
180 }
181
182
183 void Workspace::focusFallback(const BlackboxWindow *old_window) {
184   BlackboxWindow *newfocus = 0;
185
186   if (id == screen->getCurrentWorkspaceID()) {
187     // The window is on the visible workspace.
188
189     // if it's a transient, then try to focus its parent
190     if (old_window && old_window->isTransient()) {
191       newfocus = old_window->getTransientFor();
192
193       if (! newfocus ||
194           newfocus->isIconic() ||                  // do not focus icons
195           newfocus->getWorkspaceNumber() != id ||  // or other workspaces
196           ! newfocus->setInputFocus())
197         newfocus = 0;
198     }
199
200     if (! newfocus) {
201       BlackboxWindowList::iterator it = stackingList.begin(),
202                                   end = stackingList.end();
203       for (; it != end; ++it) {
204         BlackboxWindow *tmp = *it;
205         if (tmp && tmp->isNormal() && tmp->setInputFocus()) {
206           // we found our new focus target
207           newfocus = tmp;
208           break;
209         }
210       }
211     }
212
213     screen->getBlackbox()->setFocusedWindow(newfocus);
214   } else {
215     // The window is not on the visible workspace.
216
217     if (old_window && lastfocus == old_window) {
218       // The window was the last-focus target, so we need to replace it.
219       BlackboxWindow *win = (BlackboxWindow*) 0;
220       if (! stackingList.empty())
221         win = stackingList.front();
222       setLastFocusedWindow(win);
223     }
224   }
225 }
226
227
228 void Workspace::setFocused(const BlackboxWindow *w, bool focused) {
229   BlackboxWindowList::iterator it, end = windowList.end();
230   int i;
231   for (i = 0, it = windowList.begin(); it != end; ++it, ++i)
232     if (*it == w)
233       break;
234   // if its == end, then a window thats not in the windowList
235   // got focused, such as a !isNormal() window.
236   if (it != end)
237     clientmenu->setItemSelected(i, focused);
238 }
239
240
241 void Workspace::showAll(void) {
242   BlackboxWindowList::iterator it = stackingList.begin();
243   const BlackboxWindowList::iterator end = stackingList.end();
244   for (; it != end; ++it) {
245     BlackboxWindow *bw = *it;
246     bw->show();
247   }
248 }
249
250
251 void Workspace::hideAll(void) {
252   // withdraw in reverse order to minimize the number of Expose events
253   BlackboxWindowList::reverse_iterator it = stackingList.rbegin();
254   const BlackboxWindowList::reverse_iterator end = stackingList.rend();
255   while (it != end) {
256     BlackboxWindow *bw = *it;
257     ++it; // withdraw removes the current item from the list so we need the next
258           // iterator before that happens
259     bw->withdraw();
260   }
261 }
262
263
264 void Workspace::removeAll(void) {
265   while (! windowList.empty())
266     windowList.front()->iconify();
267 }
268
269
270 /*
271  * returns the number of transients for win, plus the number of transients
272  * associated with each transient of win
273  */
274 static int countTransients(const BlackboxWindow * const win) {
275   int ret = win->getTransients().size();
276   if (ret > 0) {
277     BlackboxWindowList::const_iterator it, end = win->getTransients().end();
278     for (it = win->getTransients().begin(); it != end; ++it) {
279       ret += countTransients(*it);
280     }
281   }
282   return ret;
283 }
284
285
286 /*
287  * puts the transients of win into the stack. windows are stacked above
288  * the window before it in the stackvector being iterated, meaning
289  * stack[0] is on bottom, stack[1] is above stack[0], stack[2] is above
290  * stack[1], etc...
291  */
292 void Workspace::raiseTransients(const BlackboxWindow * const win,
293                                 StackVector::iterator &stack) {
294   if (win->getTransients().size() == 0) return; // nothing to do
295
296   // put win's transients in the stack
297   BlackboxWindowList::const_iterator it, end = win->getTransients().end();
298   for (it = win->getTransients().begin(); it != end; ++it) {
299     *stack++ = (*it)->getFrameWindow();
300     screen->updateNetizenWindowRaise((*it)->getClientWindow());
301
302     if (! (*it)->isIconic()) {
303       Workspace *wkspc = screen->getWorkspace((*it)->getWorkspaceNumber());
304       wkspc->stackingList.remove((*it));
305       wkspc->stackingList.push_front((*it));
306     }
307   }
308
309   // put transients of win's transients in the stack
310   for (it = win->getTransients().begin(); it != end; ++it) {
311     raiseTransients(*it, stack);
312   }
313 }
314
315
316 void Workspace::lowerTransients(const BlackboxWindow * const win,
317                                 StackVector::iterator &stack) {
318   if (win->getTransients().size() == 0) return; // nothing to do
319
320   // put transients of win's transients in the stack
321   BlackboxWindowList::const_reverse_iterator it,
322     end = win->getTransients().rend();
323   for (it = win->getTransients().rbegin(); it != end; ++it) {
324     lowerTransients(*it, stack);
325   }
326
327   // put win's transients in the stack
328   for (it = win->getTransients().rbegin(); it != end; ++it) {
329     *stack++ = (*it)->getFrameWindow();
330     screen->updateNetizenWindowLower((*it)->getClientWindow());
331
332     if (! (*it)->isIconic()) {
333       Workspace *wkspc = screen->getWorkspace((*it)->getWorkspaceNumber());
334       wkspc->stackingList.remove((*it));
335       wkspc->stackingList.push_back((*it));
336     }
337   }
338 }
339
340
341 void Workspace::raiseWindow(BlackboxWindow *w) {
342   BlackboxWindow *win = w;
343
344   if (win->isDesktop()) return;
345
346   // walk up the transient_for's to the window that is not a transient
347   while (win->isTransient() && ! win->isDesktop()) {
348     if (! win->getTransientFor()) break;
349     win = win->getTransientFor();
350   }
351
352   // get the total window count (win and all transients)
353   unsigned int i = 1 + countTransients(win);
354
355   // stack the window with all transients above
356   StackVector stack_vector(i);
357   StackVector::iterator stack = stack_vector.begin();
358
359   *(stack++) = win->getFrameWindow();
360   screen->updateNetizenWindowRaise(win->getClientWindow());
361   if (! (win->isIconic() || win->isDesktop())) {
362     Workspace *wkspc = screen->getWorkspace(win->getWorkspaceNumber());
363     wkspc->stackingList.remove(win);
364     wkspc->stackingList.push_front(win);
365   }
366
367   raiseTransients(win, stack);
368
369   screen->raiseWindows(&stack_vector[0], stack_vector.size());
370 }
371
372
373 void Workspace::lowerWindow(BlackboxWindow *w) {
374   BlackboxWindow *win = w;
375
376   // walk up the transient_for's to the window that is not a transient
377   while (win->isTransient() && ! win->isDesktop()) {
378     if (! win->getTransientFor()) break;
379     win = win->getTransientFor();
380   }
381
382   // get the total window count (win and all transients)
383   unsigned int i = 1 + countTransients(win);
384
385   // stack the window with all transients above
386   StackVector stack_vector(i);
387   StackVector::iterator stack = stack_vector.begin();
388
389   lowerTransients(win, stack);
390
391   *(stack++) = win->getFrameWindow();
392   screen->updateNetizenWindowLower(win->getClientWindow());
393   if (! (win->isIconic() || win->isDesktop())) {
394     Workspace *wkspc = screen->getWorkspace(win->getWorkspaceNumber());
395     wkspc->stackingList.remove(win);
396     wkspc->stackingList.push_back(win);
397   }
398
399   screen->lowerWindows(&stack_vector[0], stack_vector.size());
400 }
401
402
403 void Workspace::reconfigure(void) {
404   clientmenu->reconfigure();
405   std::for_each(windowList.begin(), windowList.end(),
406                 std::mem_fun(&BlackboxWindow::reconfigure));
407 }
408
409
410 BlackboxWindow *Workspace::getWindow(unsigned int index) {
411   if (index < windowList.size()) {
412     BlackboxWindowList::iterator it = windowList.begin();
413     for(; index > 0; --index, ++it); /* increment to index */
414     return *it;
415   }
416   return 0;
417 }
418
419
420 BlackboxWindow*
421 Workspace::getNextWindowInList(BlackboxWindow *w) {
422   BlackboxWindowList::iterator it = std::find(windowList.begin(),
423                                               windowList.end(),
424                                               w);
425   assert(it != windowList.end());   // window must be in list
426   ++it;                             // next window
427   if (it == windowList.end())
428     return windowList.front();      // if we walked off the end, wrap around
429
430   return *it;
431 }
432
433
434 BlackboxWindow* Workspace::getPrevWindowInList(BlackboxWindow *w) {
435   BlackboxWindowList::iterator it = std::find(windowList.begin(),
436                                               windowList.end(),
437                                               w);
438   assert(it != windowList.end()); // window must be in list
439   if (it == windowList.begin())
440     return windowList.back();     // if we walked of the front, wrap around
441
442   return *(--it);
443 }
444
445
446 BlackboxWindow* Workspace::getTopWindowOnStack(void) const {
447   return stackingList.front();
448 }
449
450
451 void Workspace::sendWindowList(Netizen &n) {
452   BlackboxWindowList::iterator it = windowList.begin(),
453     end = windowList.end();
454   for(; it != end; ++it)
455     n.sendWindowAdd((*it)->getClientWindow(), getID());
456 }
457
458
459 unsigned int Workspace::getCount(void) const {
460   return windowList.size();
461 }
462
463
464 void Workspace::appendStackOrder(BlackboxWindowList &stack_order) const {
465   BlackboxWindowList::const_reverse_iterator it = stackingList.rbegin();
466   const BlackboxWindowList::const_reverse_iterator end = stackingList.rend();
467   for (; it != end; ++it)
468     if ((*it)->isNormal())
469       stack_order.push_back(*it);
470 }
471   
472
473 bool Workspace::isCurrent(void) const {
474   return (id == screen->getCurrentWorkspaceID());
475 }
476
477
478 bool Workspace::isLastWindow(const BlackboxWindow* const w) const {
479   return (w == windowList.back());
480 }
481
482
483 void Workspace::setCurrent(void) {
484   screen->changeWorkspaceID(id);
485 }
486
487
488 void Workspace::readName(void) {
489   XAtom::StringVect namesList;
490   unsigned long numnames = id + 1;
491     
492   // attempt to get from the _NET_WM_DESKTOP_NAMES property
493   if (xatom->getValue(screen->getRootWindow(), XAtom::net_desktop_names,
494                       XAtom::utf8, numnames, namesList) &&
495       namesList.size() > id) {
496     name = namesList[id];
497   
498     clientmenu->setLabel(name);
499     clientmenu->update();
500   } else {
501     /*
502        Use a default name. This doesn't actually change the class. That will
503        happen after the setName changes the root property, and that change
504        makes its way back to this function.
505     */
506     string tmp =i18n(WorkspaceSet, WorkspaceDefaultNameFormat,
507                      "Workspace %d");
508     assert(tmp.length() < 32);
509     char default_name[32];
510     sprintf(default_name, tmp.c_str(), id + 1);
511     
512     setName(default_name);  // save this into the _NET_WM_DESKTOP_NAMES property
513   }
514 }
515
516
517 void Workspace::setName(const string& new_name) {
518   // set the _NET_WM_DESKTOP_NAMES property with the new name
519   XAtom::StringVect namesList;
520   unsigned long numnames = (unsigned) -1;
521   if (xatom->getValue(screen->getRootWindow(), XAtom::net_desktop_names,
522                       XAtom::utf8, numnames, namesList) &&
523       namesList.size() > id)
524     namesList[id] = new_name;
525   else
526     namesList.push_back(new_name);
527
528   xatom->setValue(screen->getRootWindow(), XAtom::net_desktop_names,
529                   XAtom::utf8, namesList);
530 }
531
532
533 /*
534  * Calculate free space available for window placement.
535  */
536 typedef std::vector<Rect> rectList;
537
538 static rectList calcSpace(const Rect &win, const rectList &spaces) {
539   Rect isect, extra;
540   rectList result;
541   rectList::const_iterator siter, end = spaces.end();
542   for (siter = spaces.begin(); siter != end; ++siter) {
543     const Rect &curr = *siter;
544
545     if(! win.intersects(curr)) {
546       result.push_back(curr);
547       continue;
548     }
549
550     /* Use an intersection of win and curr to determine the space around
551      * curr that we can use.
552      *
553      * NOTE: the spaces calculated can overlap.
554      */
555     isect = curr & win;
556
557     // left
558     extra.setCoords(curr.left(), curr.top(),
559                     isect.left() - 1, curr.bottom());
560     if (extra.valid()) result.push_back(extra);
561
562     // top
563     extra.setCoords(curr.left(), curr.top(),
564                     curr.right(), isect.top() - 1);
565     if (extra.valid()) result.push_back(extra);
566
567     // right
568     extra.setCoords(isect.right() + 1, curr.top(),
569                     curr.right(), curr.bottom());
570     if (extra.valid()) result.push_back(extra);
571
572     // bottom
573     extra.setCoords(curr.left(), isect.bottom() + 1,
574                     curr.right(), curr.bottom());
575     if (extra.valid()) result.push_back(extra);
576   }
577   return result;
578 }
579
580
581 static bool rowRLBT(const Rect &first, const Rect &second) {
582   if (first.bottom() == second.bottom())
583     return first.right() > second.right();
584   return first.bottom() > second.bottom();
585 }
586
587 static bool rowRLTB(const Rect &first, const Rect &second) {
588   if (first.y() == second.y())
589     return first.right() > second.right();
590   return first.y() < second.y();
591 }
592
593 static bool rowLRBT(const Rect &first, const Rect &second) {
594   if (first.bottom() == second.bottom())
595     return first.x() < second.x();
596   return first.bottom() > second.bottom();
597 }
598
599 static bool rowLRTB(const Rect &first, const Rect &second) {
600   if (first.y() == second.y())
601     return first.x() < second.x();
602   return first.y() < second.y();
603 }
604
605 static bool colLRTB(const Rect &first, const Rect &second) {
606   if (first.x() == second.x())
607     return first.y() < second.y();
608   return first.x() < second.x();
609 }
610
611 static bool colLRBT(const Rect &first, const Rect &second) {
612   if (first.x() == second.x())
613     return first.bottom() > second.bottom();
614   return first.x() < second.x();
615 }
616
617 static bool colRLTB(const Rect &first, const Rect &second) {
618   if (first.right() == second.right())
619     return first.y() < second.y();
620   return first.right() > second.right();
621 }
622
623 static bool colRLBT(const Rect &first, const Rect &second) {
624   if (first.right() == second.right())
625     return first.bottom() > second.bottom();
626   return first.right() > second.right();
627 }
628
629
630 bool Workspace::smartPlacement(Rect& win) {
631   rectList spaces;
632  
633   //initially the entire screen is free
634 #ifdef    XINERAMA
635   if (screen->isXineramaActive() &&
636       screen->getBlackbox()->doXineramaPlacement()) {
637     RectList availableAreas = screen->allAvailableAreas();
638     RectList::iterator it, end = availableAreas.end();
639
640     for (it = availableAreas.begin(); it != end; ++it)
641       spaces.push_back(*it);
642   } else
643 #endif // XINERAMA
644     spaces.push_back(screen->availableArea());
645
646   //Find Free Spaces
647   BlackboxWindowList::const_iterator wit = windowList.begin(),
648     end = windowList.end();
649   Rect tmp;
650   for (; wit != end; ++wit) {
651     const BlackboxWindow* const curr = *wit;
652
653     // watch for shaded windows and full-maxed windows
654     if (curr->isShaded()) {
655       if (screen->getPlaceIgnoreShaded()) continue;
656     } else if (curr->isMaximizedFull()) {
657       if (screen->getPlaceIgnoreMaximized()) continue;
658     }
659
660     tmp.setRect(curr->frameRect().x(), curr->frameRect().y(),
661                 curr->frameRect().width() + screen->getBorderWidth(),
662                 curr->frameRect().height() + screen->getBorderWidth());
663
664     spaces = calcSpace(tmp, spaces);
665   }
666
667   if (screen->getPlacementPolicy() == BScreen::RowSmartPlacement) {
668     if(screen->getRowPlacementDirection() == BScreen::LeftRight) {
669       if(screen->getColPlacementDirection() == BScreen::TopBottom)
670         std::sort(spaces.begin(), spaces.end(), rowLRTB);
671       else
672         std::sort(spaces.begin(), spaces.end(), rowLRBT);
673     } else {
674       if(screen->getColPlacementDirection() == BScreen::TopBottom)
675         std::sort(spaces.begin(), spaces.end(), rowRLTB);
676       else
677         std::sort(spaces.begin(), spaces.end(), rowRLBT);
678     }
679   } else {
680     if(screen->getColPlacementDirection() == BScreen::TopBottom) {
681       if(screen->getRowPlacementDirection() == BScreen::LeftRight)
682         std::sort(spaces.begin(), spaces.end(), colLRTB);
683       else
684         std::sort(spaces.begin(), spaces.end(), colRLTB);
685     } else {
686       if(screen->getRowPlacementDirection() == BScreen::LeftRight)
687         std::sort(spaces.begin(), spaces.end(), colLRBT);
688       else
689         std::sort(spaces.begin(), spaces.end(), colRLBT);
690     }
691   }
692
693   rectList::const_iterator sit = spaces.begin(), spaces_end = spaces.end();
694   for(; sit != spaces_end; ++sit) {
695     if (sit->width() >= win.width() && sit->height() >= win.height())
696       break;
697   }
698
699   if (sit == spaces_end)
700     return False;
701
702   //set new position based on the empty space found
703   const Rect& where = *sit;
704   win.setX(where.x());
705   win.setY(where.y());
706
707   // adjust the location() based on left/right and top/bottom placement
708   if (screen->getPlacementPolicy() == BScreen::RowSmartPlacement) {
709     if (screen->getRowPlacementDirection() == BScreen::RightLeft)
710       win.setX(where.right() - win.width());
711     if (screen->getColPlacementDirection() == BScreen::BottomTop)
712       win.setY(where.bottom() - win.height());
713   } else {
714     if (screen->getColPlacementDirection() == BScreen::BottomTop)
715       win.setY(win.y() + where.height() - win.height());
716     if (screen->getRowPlacementDirection() == BScreen::RightLeft)
717       win.setX(win.x() + where.width() - win.width());
718   }
719   return True;
720 }
721
722
723 bool Workspace::underMousePlacement(Rect &win) {
724   int x, y, rx, ry;
725   Window c, r;
726   unsigned int m;
727   XQueryPointer(screen->getBlackbox()->getXDisplay(), screen->getRootWindow(),
728                 &r, &c, &rx, &ry, &x, &y, &m);
729
730   Rect area;
731 #ifdef    XINERAMA
732   if (screen->isXineramaActive() &&
733       screen->getBlackbox()->doXineramaPlacement()) {
734     RectList availableAreas = screen->allAvailableAreas();
735     RectList::iterator it, end = availableAreas.end();
736
737     for (it = availableAreas.begin(); it != end; ++it)
738       if (it->contains(rx, ry)) break;
739     assert(it != end);  // the mouse isn't inside an area?
740     area = *it;
741   } else
742 #endif // XINERAMA
743     area = screen->availableArea();
744   
745   x = rx - win.width() / 2;
746   y = ry - win.height() / 2;
747
748   if (x < area.x())
749     x = area.x();
750   if (y < area.y())
751     y = area.y();
752   if (x + win.width() > area.x() + area.width())
753     x = area.x() + area.width() - win.width();
754   if (y + win.height() > area.y() + area.height())
755     y = area.y() + area.height() - win.height();
756
757   win.setX(x);
758   win.setY(y);
759
760   return True;
761 }
762
763
764 bool Workspace::cascadePlacement(Rect &win, const int offset) {
765   Rect area;
766   
767 #ifdef    XINERAMA
768   if (screen->isXineramaActive() &&
769       screen->getBlackbox()->doXineramaPlacement()) {
770     area = screen->allAvailableAreas()[cascade_region];
771   } else
772 #endif // XINERAMA
773     area = screen->availableArea();
774
775   if ((static_cast<signed>(cascade_x + win.width()) > area.right() + 1) ||
776       (static_cast<signed>(cascade_y + win.height()) > area.bottom() + 1)) {
777     cascade_x = cascade_y = 0;
778 #ifdef    XINERAMA
779     if (screen->isXineramaActive() &&
780         screen->getBlackbox()->doXineramaPlacement()) {
781       // go to the next xinerama region, and use its area
782       if (++cascade_region >= screen->allAvailableAreas().size())
783         cascade_region = 0;
784       area = screen->allAvailableAreas()[cascade_region];
785     }
786 #endif // XINERAMA
787   }
788
789   if (cascade_x == 0) {
790     cascade_x = area.x() + offset;
791     cascade_y = area.y() + offset;
792   }
793
794   win.setPos(cascade_x, cascade_y);
795
796   cascade_x += offset;
797   cascade_y += offset;
798
799   return True;
800 }
801
802
803 void Workspace::placeWindow(BlackboxWindow *win) {
804   Rect new_win(0, 0, win->frameRect().width(), win->frameRect().height());
805   bool placed = False;
806
807   switch (screen->getPlacementPolicy()) {
808   case BScreen::RowSmartPlacement:
809   case BScreen::ColSmartPlacement:
810     placed = smartPlacement(new_win);
811     break;
812   case BScreen::UnderMousePlacement:
813   case BScreen::ClickMousePlacement:
814     placed = underMousePlacement(new_win);
815   default:
816     break; // handled below
817   } // switch
818
819   if (placed == False)
820     cascadePlacement(new_win, (win->getTitleHeight() +
821                                screen->getBorderWidth() * 2));
822
823   if (new_win.right() > screen->availableArea().right())
824     new_win.setX(screen->availableArea().left());
825   if (new_win.bottom() > screen->availableArea().bottom())
826     new_win.setY(screen->availableArea().top());
827
828   win->configure(new_win.x(), new_win.y(), new_win.width(), new_win.height());
829 }