]> icculus.org git repositories - mikachu/openbox.git/blob - src/Workspace.cc
sync with blackbox
[mikachu/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
123
124 void Workspace::removeWindow(BlackboxWindow *w, bool sticky) {
125   assert(w != 0);
126
127   stackingList.remove(w);
128
129   // pass focus to the next appropriate window
130   if ((w->isFocused() || w == lastfocus) &&
131       ! screen->getBlackbox()->doShutdown()) {
132     focusFallback(w);
133   }
134     
135   if (! w->isNormal()) return;
136
137   BlackboxWindowList::iterator it, end = windowList.end();
138   int i;
139   for (i = 0, it = windowList.begin(); it != end; ++it, ++i)
140     if (*it == w)
141       break;
142   assert(it != end);
143   
144   windowList.erase(it);
145   clientmenu->remove(i);
146   clientmenu->update();
147
148   if (! sticky) {
149     screen->updateNetizenWindowDel(w->getClientWindow());
150
151     BlackboxWindowList::iterator it = windowList.begin();
152     const BlackboxWindowList::iterator end = windowList.end();
153     unsigned int i = 0;
154     for (; it != end; ++it, ++i)
155       (*it)->setWindowNumber(i);
156   }
157
158   if (i == 0) {
159     cascade_x = cascade_y = 0;
160 #ifdef    XINERAMA
161     cascade_region = 0;
162 #endif // XINERAMA
163   }
164 }
165
166
167 void Workspace::focusFallback(const BlackboxWindow *old_window) {
168   BlackboxWindow *newfocus = 0;
169
170   if (id == screen->getCurrentWorkspaceID()) {
171     // The window is on the visible workspace.
172
173     // if it's a transient, then try to focus its parent
174     if (old_window && old_window->isTransient()) {
175       newfocus = old_window->getTransientFor();
176
177       if (! newfocus ||
178           newfocus->isIconic() ||                  // do not focus icons
179           newfocus->getWorkspaceNumber() != id ||  // or other workspaces
180           ! newfocus->setInputFocus())
181         newfocus = 0;
182     }
183
184     if (! newfocus) {
185       BlackboxWindowList::iterator it = stackingList.begin(),
186                                   end = stackingList.end();
187       for (; it != end; ++it) {
188         BlackboxWindow *tmp = *it;
189         if (tmp && tmp->isNormal() && tmp->setInputFocus()) {
190           // we found our new focus target
191           newfocus = tmp;
192           break;
193         }
194       }
195     }
196
197     screen->getBlackbox()->setFocusedWindow(newfocus);
198   } else {
199     // The window is not on the visible workspace.
200
201     if (old_window && lastfocus == old_window) {
202       // The window was the last-focus target, so we need to replace it.
203       BlackboxWindow *win = (BlackboxWindow*) 0;
204       if (! stackingList.empty())
205         win = stackingList.front();
206       setLastFocusedWindow(win);
207     }
208   }
209 }
210
211
212 void Workspace::setFocused(const BlackboxWindow *w, bool focused) {
213   BlackboxWindowList::iterator it, end = windowList.end();
214   int i;
215   for (i = 0, it = windowList.begin(); it != end; ++it, ++i)
216     if (*it == w)
217       break;
218   // if its == end, then a window thats not in the windowList
219   // got focused, such as a !isNormal() window.
220   if (it != end)
221     clientmenu->setItemSelected(i, focused);
222 }
223
224
225 void Workspace::removeAll(void) {
226   while (! windowList.empty())
227     windowList.front()->iconify();
228 }
229
230
231 /*
232  * returns the number of transients for win, plus the number of transients
233  * associated with each transient of win
234  */
235 static unsigned int countTransients(const BlackboxWindow * const win) {
236   BlackboxWindowList transients = win->getTransients();
237   if (transients.empty()) return 0;
238
239   unsigned int ret = transients.size();
240   BlackboxWindowList::const_iterator it = transients.begin(),
241     end = transients.end();
242   for (; it != end; ++it)
243     ret += countTransients(*it);
244
245   return ret;
246 }
247
248
249 /*
250  * puts the transients of win into the stack. windows are stacked above
251  * the window before it in the stackvector being iterated, meaning
252  * stack[0] is on bottom, stack[1] is above stack[0], stack[2] is above
253  * stack[1], etc...
254  */
255 void Workspace::raiseTransients(const BlackboxWindow * const win,
256                                 StackVector::iterator &stack) {
257   if (win->getTransients().empty()) return; // nothing to do
258
259   // put win's transients in the stack
260   BlackboxWindowList::const_iterator it, end = win->getTransients().end();
261   for (it = win->getTransients().begin(); it != end; ++it) {
262     BlackboxWindow *w = *it;
263     *stack++ = w->getFrameWindow();
264     screen->updateNetizenWindowRaise(w->getClientWindow());
265
266     if (! w->isIconic()) {
267       Workspace *wkspc = screen->getWorkspace(w->getWorkspaceNumber());
268       wkspc->stackingList.remove(w);
269       wkspc->stackingList.push_front(w);
270     }
271   }
272
273   // put transients of win's transients in the stack
274   for (it = win->getTransients().begin(); it != end; ++it)
275     raiseTransients(*it, stack);
276 }
277
278
279 void Workspace::lowerTransients(const BlackboxWindow * const win,
280                                 StackVector::iterator &stack) {
281   if (win->getTransients().empty()) return; // nothing to do
282
283   // put transients of win's transients in the stack
284   BlackboxWindowList::const_reverse_iterator it,
285     end = win->getTransients().rend();
286   for (it = win->getTransients().rbegin(); it != end; ++it)
287     lowerTransients(*it, stack);
288
289   // put win's transients in the stack
290   for (it = win->getTransients().rbegin(); it != end; ++it) {
291     BlackboxWindow *w = *it;
292     *stack++ = w->getFrameWindow();
293     screen->updateNetizenWindowLower(w->getClientWindow());
294
295     if (! w->isIconic()) {
296       Workspace *wkspc = screen->getWorkspace(w->getWorkspaceNumber());
297       wkspc->stackingList.remove(w);
298       wkspc->stackingList.push_back(w);
299     }
300   }
301 }
302
303
304 void Workspace::raiseWindow(BlackboxWindow *w) {
305   BlackboxWindow *win = w;
306
307   if (win->isDesktop()) return;
308
309   // walk up the transient_for's to the window that is not a transient
310   while (win->isTransient() && win->getTransientFor())
311     win = win->getTransientFor();
312
313   // get the total window count (win and all transients)
314   unsigned int i = 1 + countTransients(win);
315
316   // stack the window with all transients above
317   StackVector stack_vector(i);
318   StackVector::iterator stack = stack_vector.begin();
319
320   *(stack++) = win->getFrameWindow();
321   screen->updateNetizenWindowRaise(win->getClientWindow());
322   if (! (win->isIconic() || win->isDesktop())) {
323     Workspace *wkspc = screen->getWorkspace(win->getWorkspaceNumber());
324     wkspc->stackingList.remove(win);
325     wkspc->stackingList.push_front(win);
326   }
327
328   raiseTransients(win, stack);
329
330   screen->raiseWindows(&stack_vector[0], stack_vector.size());
331 }
332
333
334 void Workspace::lowerWindow(BlackboxWindow *w) {
335   BlackboxWindow *win = w;
336
337   // walk up the transient_for's to the window that is not a transient
338   while (win->isTransient() && win->getTransientFor())
339     win = win->getTransientFor();
340
341   // get the total window count (win and all transients)
342   unsigned int i = 1 + countTransients(win);
343
344   // stack the window with all transients above
345   StackVector stack_vector(i);
346   StackVector::iterator stack = stack_vector.begin();
347
348   lowerTransients(win, stack);
349
350   *(stack++) = win->getFrameWindow();
351   screen->updateNetizenWindowLower(win->getClientWindow());
352   if (! (win->isIconic() || win->isDesktop())) {
353     Workspace *wkspc = screen->getWorkspace(win->getWorkspaceNumber());
354     wkspc->stackingList.remove(win);
355     wkspc->stackingList.push_back(win);
356   }
357
358   screen->lowerWindows(&stack_vector[0], stack_vector.size());
359 }
360
361
362 void Workspace::reconfigure(void) {
363   clientmenu->reconfigure();
364   std::for_each(windowList.begin(), windowList.end(),
365                 std::mem_fun(&BlackboxWindow::reconfigure));
366 }
367
368
369 BlackboxWindow *Workspace::getWindow(unsigned int index) {
370   if (index < windowList.size()) {
371     BlackboxWindowList::iterator it = windowList.begin();
372     while (index-- > 0) // increment to index
373       ++it;
374     return *it;
375   }
376
377   return 0;
378 }
379
380
381 BlackboxWindow*
382 Workspace::getNextWindowInList(BlackboxWindow *w) {
383   BlackboxWindowList::iterator it = std::find(windowList.begin(),
384                                               windowList.end(),
385                                               w);
386   assert(it != windowList.end());   // window must be in list
387   ++it;                             // next window
388   if (it == windowList.end())
389     return windowList.front();      // if we walked off the end, wrap around
390
391   return *it;
392 }
393
394
395 BlackboxWindow* Workspace::getPrevWindowInList(BlackboxWindow *w) {
396   BlackboxWindowList::iterator it = std::find(windowList.begin(),
397                                               windowList.end(),
398                                               w);
399   assert(it != windowList.end()); // window must be in list
400   if (it == windowList.begin())
401     return windowList.back();     // if we walked of the front, wrap around
402
403   return *(--it);
404 }
405
406
407 BlackboxWindow* Workspace::getTopWindowOnStack(void) const {
408   assert(! stackingList.empty());
409   return stackingList.front();
410 }
411
412
413 void Workspace::sendWindowList(Netizen &n) {
414   BlackboxWindowList::iterator it = windowList.begin(),
415     end = windowList.end();
416   for(; it != end; ++it)
417     n.sendWindowAdd((*it)->getClientWindow(), getID());
418 }
419
420
421 unsigned int Workspace::getCount(void) const {
422   return windowList.size();
423 }
424
425
426 void Workspace::appendStackOrder(BlackboxWindowList &stack_order) const {
427   BlackboxWindowList::const_reverse_iterator it = stackingList.rbegin();
428   const BlackboxWindowList::const_reverse_iterator end = stackingList.rend();
429   for (; it != end; ++it)
430     if ((*it)->isNormal())
431       stack_order.push_back(*it);
432 }
433   
434
435 void Workspace::hide(void) {
436   BlackboxWindow *focused = screen->getBlackbox()->getFocusedWindow();
437   if (focused && focused->getScreen() == screen) {
438     assert(focused->isStuck() || focused->getWorkspaceNumber() == id);
439
440     lastfocus = focused;
441   } else {
442     // if no window had focus, no need to store a last focus
443     lastfocus = (BlackboxWindow *) 0;
444   }
445
446   // when we switch workspaces, unfocus whatever was focused
447   screen->getBlackbox()->setFocusedWindow((BlackboxWindow *) 0);
448
449   // withdraw windows in reverse order to minimize the number of Expose events
450
451   BlackboxWindowList::reverse_iterator it = stackingList.rbegin();
452   const BlackboxWindowList::reverse_iterator end = stackingList.rend();
453   for (; it != end; ++it) {
454     BlackboxWindow *bw = *it;
455     // not normal windows cant focus from mouse enters anyways, so we dont
456     // need to unmap/remap them on workspace changes
457     if (! bw->isStuck() || bw->isNormal())
458       bw->withdraw();
459   }
460 }
461
462
463 void Workspace::show(void) {
464   std::for_each(stackingList.begin(), stackingList.end(),
465                 std::mem_fun(&BlackboxWindow::show));
466
467   XSync(screen->getBlackbox()->getXDisplay(), False);
468
469   if (screen->doFocusLast()) {
470     if (! screen->isSloppyFocus() && ! lastfocus && ! stackingList.empty())
471       lastfocus = stackingList.front();
472
473     if (lastfocus)
474       lastfocus->setInputFocus();
475   }
476 }
477
478
479 bool Workspace::isCurrent(void) const {
480   return (id == screen->getCurrentWorkspaceID());
481 }
482
483
484 bool Workspace::isLastWindow(const BlackboxWindow* const w) const {
485   return (w == windowList.back());
486 }
487
488
489 void Workspace::setCurrent(void) {
490   screen->changeWorkspaceID(id);
491 }
492
493
494 void Workspace::readName(void) {
495   XAtom::StringVect namesList;
496   unsigned long numnames = id + 1;
497     
498   // attempt to get from the _NET_WM_DESKTOP_NAMES property
499   if (xatom->getValue(screen->getRootWindow(), XAtom::net_desktop_names,
500                       XAtom::utf8, numnames, namesList) &&
501       namesList.size() > id) {
502     name = namesList[id];
503   
504     clientmenu->setLabel(name);
505     clientmenu->update();
506   } else {
507     /*
508        Use a default name. This doesn't actually change the class. That will
509        happen after the setName changes the root property, and that change
510        makes its way back to this function.
511     */
512     string tmp =i18n(WorkspaceSet, WorkspaceDefaultNameFormat,
513                      "Workspace %d");
514     assert(tmp.length() < 32);
515     char default_name[32];
516     sprintf(default_name, tmp.c_str(), id + 1);
517     
518     setName(default_name);  // save this into the _NET_WM_DESKTOP_NAMES property
519   }
520 }
521
522
523 void Workspace::setName(const string& new_name) {
524   // set the _NET_WM_DESKTOP_NAMES property with the new name
525   XAtom::StringVect namesList;
526   unsigned long numnames = (unsigned) -1;
527   if (xatom->getValue(screen->getRootWindow(), XAtom::net_desktop_names,
528                       XAtom::utf8, numnames, namesList) &&
529       namesList.size() > id)
530     namesList[id] = new_name;
531   else
532     namesList.push_back(new_name);
533
534   xatom->setValue(screen->getRootWindow(), XAtom::net_desktop_names,
535                   XAtom::utf8, namesList);
536 }
537
538
539 /*
540  * Calculate free space available for window placement.
541  */
542 typedef std::vector<Rect> rectList;
543
544 static rectList calcSpace(const Rect &win, const rectList &spaces) {
545   Rect isect, extra;
546   rectList result;
547   rectList::const_iterator siter, end = spaces.end();
548   for (siter = spaces.begin(); siter != end; ++siter) {
549     const Rect &curr = *siter;
550
551     if(! win.intersects(curr)) {
552       result.push_back(curr);
553       continue;
554     }
555
556     /* Use an intersection of win and curr to determine the space around
557      * curr that we can use.
558      *
559      * NOTE: the spaces calculated can overlap.
560      */
561     isect = curr & win;
562
563     // left
564     extra.setCoords(curr.left(), curr.top(),
565                     isect.left() - 1, curr.bottom());
566     if (extra.valid()) result.push_back(extra);
567
568     // top
569     extra.setCoords(curr.left(), curr.top(),
570                     curr.right(), isect.top() - 1);
571     if (extra.valid()) result.push_back(extra);
572
573     // right
574     extra.setCoords(isect.right() + 1, curr.top(),
575                     curr.right(), curr.bottom());
576     if (extra.valid()) result.push_back(extra);
577
578     // bottom
579     extra.setCoords(curr.left(), isect.bottom() + 1,
580                     curr.right(), curr.bottom());
581     if (extra.valid()) result.push_back(extra);
582   }
583   return result;
584 }
585
586
587 static bool rowRLBT(const Rect &first, const Rect &second) {
588   if (first.bottom() == second.bottom())
589     return first.right() > second.right();
590   return first.bottom() > second.bottom();
591 }
592
593 static bool rowRLTB(const Rect &first, const Rect &second) {
594   if (first.y() == second.y())
595     return first.right() > second.right();
596   return first.y() < second.y();
597 }
598
599 static bool rowLRBT(const Rect &first, const Rect &second) {
600   if (first.bottom() == second.bottom())
601     return first.x() < second.x();
602   return first.bottom() > second.bottom();
603 }
604
605 static bool rowLRTB(const Rect &first, const Rect &second) {
606   if (first.y() == second.y())
607     return first.x() < second.x();
608   return first.y() < second.y();
609 }
610
611 static bool colLRTB(const Rect &first, const Rect &second) {
612   if (first.x() == second.x())
613     return first.y() < second.y();
614   return first.x() < second.x();
615 }
616
617 static bool colLRBT(const Rect &first, const Rect &second) {
618   if (first.x() == second.x())
619     return first.bottom() > second.bottom();
620   return first.x() < second.x();
621 }
622
623 static bool colRLTB(const Rect &first, const Rect &second) {
624   if (first.right() == second.right())
625     return first.y() < second.y();
626   return first.right() > second.right();
627 }
628
629 static bool colRLBT(const Rect &first, const Rect &second) {
630   if (first.right() == second.right())
631     return first.bottom() > second.bottom();
632   return first.right() > second.right();
633 }
634
635
636 bool Workspace::smartPlacement(Rect& win) {
637   rectList spaces;
638  
639   //initially the entire screen is free
640 #ifdef    XINERAMA
641   if (screen->isXineramaActive() &&
642       screen->getBlackbox()->doXineramaPlacement()) {
643     RectList availableAreas = screen->allAvailableAreas();
644     RectList::iterator it, end = availableAreas.end();
645
646     for (it = availableAreas.begin(); it != end; ++it)
647       spaces.push_back(*it);
648   } else
649 #endif // XINERAMA
650     spaces.push_back(screen->availableArea());
651
652   //Find Free Spaces
653   BlackboxWindowList::const_iterator wit = windowList.begin(),
654     end = windowList.end();
655   Rect tmp;
656   for (; wit != end; ++wit) {
657     const BlackboxWindow* const curr = *wit;
658
659     // watch for shaded windows and full-maxed windows
660     if (curr->isShaded()) {
661       if (screen->getPlaceIgnoreShaded()) continue;
662     } else if (curr->isMaximizedFull()) {
663       if (screen->getPlaceIgnoreMaximized()) continue;
664     }
665
666     tmp.setRect(curr->frameRect().x(), curr->frameRect().y(),
667                 curr->frameRect().width() + screen->getBorderWidth(),
668                 curr->frameRect().height() + screen->getBorderWidth());
669
670     spaces = calcSpace(tmp, spaces);
671   }
672
673   if (screen->getPlacementPolicy() == BScreen::RowSmartPlacement) {
674     if(screen->getRowPlacementDirection() == BScreen::LeftRight) {
675       if(screen->getColPlacementDirection() == BScreen::TopBottom)
676         std::sort(spaces.begin(), spaces.end(), rowLRTB);
677       else
678         std::sort(spaces.begin(), spaces.end(), rowLRBT);
679     } else {
680       if(screen->getColPlacementDirection() == BScreen::TopBottom)
681         std::sort(spaces.begin(), spaces.end(), rowRLTB);
682       else
683         std::sort(spaces.begin(), spaces.end(), rowRLBT);
684     }
685   } else {
686     if(screen->getColPlacementDirection() == BScreen::TopBottom) {
687       if(screen->getRowPlacementDirection() == BScreen::LeftRight)
688         std::sort(spaces.begin(), spaces.end(), colLRTB);
689       else
690         std::sort(spaces.begin(), spaces.end(), colRLTB);
691     } else {
692       if(screen->getRowPlacementDirection() == BScreen::LeftRight)
693         std::sort(spaces.begin(), spaces.end(), colLRBT);
694       else
695         std::sort(spaces.begin(), spaces.end(), colRLBT);
696     }
697   }
698
699   rectList::const_iterator sit = spaces.begin(), spaces_end = spaces.end();
700   for(; sit != spaces_end; ++sit) {
701     if (sit->width() >= win.width() && sit->height() >= win.height())
702       break;
703   }
704
705   if (sit == spaces_end)
706     return False;
707
708   //set new position based on the empty space found
709   const Rect& where = *sit;
710   win.setX(where.x());
711   win.setY(where.y());
712
713   // adjust the location() based on left/right and top/bottom placement
714   if (screen->getPlacementPolicy() == BScreen::RowSmartPlacement) {
715     if (screen->getRowPlacementDirection() == BScreen::RightLeft)
716       win.setX(where.right() - win.width());
717     if (screen->getColPlacementDirection() == BScreen::BottomTop)
718       win.setY(where.bottom() - win.height());
719   } else {
720     if (screen->getColPlacementDirection() == BScreen::BottomTop)
721       win.setY(win.y() + where.height() - win.height());
722     if (screen->getRowPlacementDirection() == BScreen::RightLeft)
723       win.setX(win.x() + where.width() - win.width());
724   }
725   return True;
726 }
727
728
729 bool Workspace::underMousePlacement(Rect &win) {
730   int x, y, rx, ry;
731   Window c, r;
732   unsigned int m;
733   XQueryPointer(screen->getBlackbox()->getXDisplay(), screen->getRootWindow(),
734                 &r, &c, &rx, &ry, &x, &y, &m);
735
736   Rect area;
737 #ifdef    XINERAMA
738   if (screen->isXineramaActive() &&
739       screen->getBlackbox()->doXineramaPlacement()) {
740     RectList availableAreas = screen->allAvailableAreas();
741     RectList::iterator it, end = availableAreas.end();
742
743     for (it = availableAreas.begin(); it != end; ++it)
744       if (it->contains(rx, ry)) break;
745     assert(it != end);  // the mouse isn't inside an area?
746     area = *it;
747   } else
748 #endif // XINERAMA
749     area = screen->availableArea();
750   
751   x = rx - win.width() / 2;
752   y = ry - win.height() / 2;
753
754   if (x < area.x())
755     x = area.x();
756   if (y < area.y())
757     y = area.y();
758   if (x + win.width() > area.x() + area.width())
759     x = area.x() + area.width() - win.width();
760   if (y + win.height() > area.y() + area.height())
761     y = area.y() + area.height() - win.height();
762
763   win.setX(x);
764   win.setY(y);
765
766   return True;
767 }
768
769
770 bool Workspace::cascadePlacement(Rect &win, const int offset) {
771   Rect area;
772   
773 #ifdef    XINERAMA
774   if (screen->isXineramaActive() &&
775       screen->getBlackbox()->doXineramaPlacement()) {
776     area = screen->allAvailableAreas()[cascade_region];
777   } else
778 #endif // XINERAMA
779     area = screen->availableArea();
780
781   if ((static_cast<signed>(cascade_x + win.width()) > area.right() + 1) ||
782       (static_cast<signed>(cascade_y + win.height()) > area.bottom() + 1)) {
783     cascade_x = cascade_y = 0;
784 #ifdef    XINERAMA
785     if (screen->isXineramaActive() &&
786         screen->getBlackbox()->doXineramaPlacement()) {
787       // go to the next xinerama region, and use its area
788       if (++cascade_region >= screen->allAvailableAreas().size())
789         cascade_region = 0;
790       area = screen->allAvailableAreas()[cascade_region];
791     }
792 #endif // XINERAMA
793   }
794
795   if (cascade_x == 0) {
796     cascade_x = area.x() + offset;
797     cascade_y = area.y() + offset;
798   }
799
800   win.setPos(cascade_x, cascade_y);
801
802   cascade_x += offset;
803   cascade_y += offset;
804
805   return True;
806 }
807
808
809 void Workspace::placeWindow(BlackboxWindow *win) {
810   Rect new_win(0, 0, win->frameRect().width(), win->frameRect().height());
811   bool placed = False;
812
813   switch (screen->getPlacementPolicy()) {
814   case BScreen::RowSmartPlacement:
815   case BScreen::ColSmartPlacement:
816     placed = smartPlacement(new_win);
817     break;
818   case BScreen::UnderMousePlacement:
819   case BScreen::ClickMousePlacement:
820     placed = underMousePlacement(new_win);
821   default:
822     break; // handled below
823   } // switch
824
825   if (placed == False)
826     cascadePlacement(new_win, (win->getTitleHeight() +
827                                screen->getBorderWidth() * 2));
828
829   if (new_win.right() > screen->availableArea().right())
830     new_win.setX(screen->availableArea().left());
831   if (new_win.bottom() > screen->availableArea().bottom())
832     new_win.setY(screen->availableArea().top());
833
834   win->configure(new_win.x(), new_win.y(), new_win.width(), new_win.height());
835 }