+++ /dev/null
-// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
-
-#ifdef HAVE_CONFIG_H
-# include "../config.h"
-#endif // HAVE_CONFIG_H
-
-extern "C" {
-#include <X11/Xlib.h>
-#include <X11/Xatom.h>
-
-#ifdef HAVE_STDIO_H
-# include <stdio.h>
-#endif // HAVE_STDIO_H
-
-#ifdef HAVE_STRING_H
-# include <string.h>
-#endif // HAVE_STRING_H
-}
-
-#include <assert.h>
-
-#include <functional>
-#include <string>
-
-using std::string;
-
-#include "blackbox.hh"
-#include "otk/font.hh"
-#include "otk/display.hh"
-#include "bbscreen.hh"
-#include "otk/util.hh"
-#include "bbwindow.hh"
-#include "workspace.hh"
-
-namespace ob {
-
-Workspace::Workspace(BScreen *scrn, unsigned int i) {
- screen = scrn;
- xatom = screen->getBlackbox()->getXAtom();
-
- cascade_x = cascade_y = 0;
-#ifdef XINERAMA
- cascade_region = 0;
-#endif // XINERAMA
-
- id = i;
-
- lastfocus = (BlackboxWindow *) 0;
-
- readName();
-}
-
-
-void Workspace::addWindow(BlackboxWindow *w, bool place, bool sticky) {
- assert(w != 0);
-
- if (place) placeWindow(w);
-
- stackingList.push_front(w);
-
- if (! sticky)
- w->setWorkspace(id);
-
- if (! w->isNormal()) {
- if (! sticky) {
- // just give it some number, else bad things happen as it is assumed to
- // not be on a workspace
- w->setWindowNumber(0);
- }
- } else {
- if (! sticky)
- w->setWindowNumber(windowList.size());
-
- windowList.push_back(w);
-
- if (screen->doFocusNew() || (w->isTransient() && w->getTransientFor() &&
- w->getTransientFor()->isFocused())) {
- if (id != screen->getCurrentWorkspaceID()) {
- /*
- not on the focused workspace, so the window is not going to get focus
- but if the user wants new windows focused, then it should get focus
- when this workspace does become focused.
- */
- lastfocus = w;
- }
- }
- }
-
- if (! w->isDesktop())
- raiseWindow(w);
- else
- lowerWindow(w);
-}
-
-
-void Workspace::removeWindow(BlackboxWindow *w, bool sticky) {
- assert(w != 0);
-
- stackingList.remove(w);
-
- // pass focus to the next appropriate window
- if ((w->isFocused() || w == lastfocus) &&
- screen->getBlackbox()->state() != Openbox::State_Exiting) {
- focusFallback(w);
- }
-
- if (! w->isNormal()) return;
-
- BlackboxWindowList::iterator it, end = windowList.end();
- int i;
- for (i = 0, it = windowList.begin(); it != end; ++it, ++i)
- if (*it == w)
- break;
- assert(it != end);
-
- windowList.erase(it);
- if (! sticky) {
- BlackboxWindowList::iterator it = windowList.begin();
- const BlackboxWindowList::iterator end = windowList.end();
- unsigned int i = 0;
- for (; it != end; ++it, ++i)
- (*it)->setWindowNumber(i);
- }
-
- if (i == 0) {
- cascade_x = cascade_y = 0;
-#ifdef XINERAMA
- cascade_region = 0;
-#endif // XINERAMA
- }
-}
-
-
-void Workspace::focusFallback(const BlackboxWindow *old_window) {
- BlackboxWindow *newfocus = 0;
-
- if (id == screen->getCurrentWorkspaceID()) {
- // The window is on the visible workspace.
-
- // if it's a transient, then try to focus its parent
- if (old_window && old_window->isTransient()) {
- newfocus = old_window->getTransientFor();
-
- if (! newfocus ||
- newfocus->isIconic() || // do not focus icons
- newfocus->getWorkspaceNumber() != id || // or other workspaces
- ! newfocus->setInputFocus())
- newfocus = 0;
- }
-
- if (! newfocus) {
- BlackboxWindowList::iterator it = stackingList.begin(),
- end = stackingList.end();
- for (; it != end; ++it) {
- BlackboxWindow *tmp = *it;
- if (tmp && tmp->isNormal() && tmp->setInputFocus()) {
- // we found our new focus target
- newfocus = tmp;
- break;
- }
- }
- }
-
- screen->getBlackbox()->setFocusedWindow(newfocus);
- } else {
- // The window is not on the visible workspace.
-
- if (old_window && lastfocus == old_window) {
- // The window was the last-focus target, so we need to replace it.
- BlackboxWindow *win = (BlackboxWindow*) 0;
- if (! stackingList.empty())
- win = stackingList.front();
- setLastFocusedWindow(win);
- }
- }
-}
-
-
-void Workspace::removeAll(void) {
- while (! windowList.empty())
- windowList.front()->iconify();
-}
-
-void Workspace::showAll(void) {
- BlackboxWindowList::iterator it = stackingList.begin();
- const BlackboxWindowList::iterator end = stackingList.end();
- for (; it != end; ++it) {
- BlackboxWindow *bw = *it;
- // sticky windows arent unmapped on a workspace change so we don't have ot
- // map them, but sometimes on a restart, another app can unmap our sticky
- // windows, so we map on startup always
- if (! bw->isStuck() ||
- screen->getBlackbox()->state() == Openbox::State_Starting)
- bw->show();
- }
-}
-
-
-void Workspace::hideAll(void) {
- // withdraw in reverse order to minimize the number of Expose events
-
- BlackboxWindowList lst(stackingList.rbegin(), stackingList.rend());
-
- BlackboxWindowList::iterator it = lst.begin();
- const BlackboxWindowList::iterator end = lst.end();
- for (; it != end; ++it) {
- BlackboxWindow *bw = *it;
- // don't hide sticky windows, or they'll end up flickering on a workspace
- // change
- if (! bw->isStuck())
- bw->withdraw();
- }
-}
-
-
-
-/*
- * returns the number of transients for win, plus the number of transients
- * associated with each transient of win
- */
-static unsigned int countTransients(const BlackboxWindow * const win) {
- BlackboxWindowList transients = win->getTransients();
- if (transients.empty()) return 0;
-
- unsigned int ret = transients.size();
- BlackboxWindowList::const_iterator it = transients.begin(),
- end = transients.end();
- for (; it != end; ++it)
- ret += countTransients(*it);
-
- return ret;
-}
-
-
-/*
- * puts the transients of win into the stack. windows are stacked above
- * the window before it in the stackvector being iterated, meaning
- * stack[0] is on bottom, stack[1] is above stack[0], stack[2] is above
- * stack[1], etc...
- */
-void Workspace::raiseTransients(const BlackboxWindow * const win,
- StackVector::iterator &stack) {
- if (win->getTransients().empty()) return; // nothing to do
-
- // put win's transients in the stack
- BlackboxWindowList::const_iterator it, end = win->getTransients().end();
- for (it = win->getTransients().begin(); it != end; ++it) {
- BlackboxWindow *w = *it;
- *stack++ = w->getFrameWindow();
-
- if (! w->isIconic()) {
- Workspace *wkspc = screen->getWorkspace(w->getWorkspaceNumber());
- wkspc->stackingList.remove(w);
- wkspc->stackingList.push_front(w);
- }
- }
-
- // put transients of win's transients in the stack
- for (it = win->getTransients().begin(); it != end; ++it)
- raiseTransients(*it, stack);
-}
-
-
-void Workspace::lowerTransients(const BlackboxWindow * const win,
- StackVector::iterator &stack) {
- if (win->getTransients().empty()) return; // nothing to do
-
- // put transients of win's transients in the stack
- BlackboxWindowList::const_reverse_iterator it,
- end = win->getTransients().rend();
- for (it = win->getTransients().rbegin(); it != end; ++it)
- lowerTransients(*it, stack);
-
- // put win's transients in the stack
- for (it = win->getTransients().rbegin(); it != end; ++it) {
- BlackboxWindow *w = *it;
- *stack++ = w->getFrameWindow();
-
- if (! w->isIconic()) {
- Workspace *wkspc = screen->getWorkspace(w->getWorkspaceNumber());
- wkspc->stackingList.remove(w);
- wkspc->stackingList.push_back(w);
- }
- }
-}
-
-
-void Workspace::raiseWindow(BlackboxWindow *w) {
- BlackboxWindow *win = w;
-
- if (win->isDesktop()) return;
-
- // walk up the transient_for's to the window that is not a transient
- while (win->isTransient() && win->getTransientFor())
- win = win->getTransientFor();
-
- // get the total window count (win and all transients)
- unsigned int i = 1 + countTransients(win);
-
- // stack the window with all transients above
- StackVector stack_vector(i);
- StackVector::iterator stack = stack_vector.begin();
-
- *(stack++) = win->getFrameWindow();
- if (! (win->isIconic() || win->isDesktop())) {
- Workspace *wkspc = screen->getWorkspace(win->getWorkspaceNumber());
- wkspc->stackingList.remove(win);
- wkspc->stackingList.push_front(win);
- }
-
- raiseTransients(win, stack);
-
- screen->raiseWindows(&stack_vector[0], stack_vector.size());
-}
-
-
-void Workspace::lowerWindow(BlackboxWindow *w) {
- BlackboxWindow *win = w;
-
- // walk up the transient_for's to the window that is not a transient
- while (win->isTransient() && win->getTransientFor())
- win = win->getTransientFor();
-
- // get the total window count (win and all transients)
- unsigned int i = 1 + countTransients(win);
-
- // stack the window with all transients above
- StackVector stack_vector(i);
- StackVector::iterator stack = stack_vector.begin();
-
- lowerTransients(win, stack);
-
- *(stack++) = win->getFrameWindow();
- if (! (win->isIconic() || win->isDesktop())) {
- Workspace *wkspc = screen->getWorkspace(win->getWorkspaceNumber());
- wkspc->stackingList.remove(win);
- wkspc->stackingList.push_back(win);
- }
-
- screen->lowerWindows(&stack_vector[0], stack_vector.size());
-}
-
-
-void Workspace::reconfigure(void) {
- std::for_each(windowList.begin(), windowList.end(),
- std::mem_fun(&BlackboxWindow::reconfigure));
-}
-
-
-BlackboxWindow *Workspace::getWindow(unsigned int index) {
- if (index < windowList.size()) {
- BlackboxWindowList::iterator it = windowList.begin();
- while (index-- > 0) // increment to index
- ++it;
- return *it;
- }
-
- return 0;
-}
-
-
-BlackboxWindow*
-Workspace::getNextWindowInList(BlackboxWindow *w) {
- BlackboxWindowList::iterator it = std::find(windowList.begin(),
- windowList.end(),
- w);
- assert(it != windowList.end()); // window must be in list
- ++it; // next window
- if (it == windowList.end())
- return windowList.front(); // if we walked off the end, wrap around
-
- return *it;
-}
-
-
-BlackboxWindow* Workspace::getPrevWindowInList(BlackboxWindow *w) {
- BlackboxWindowList::iterator it = std::find(windowList.begin(),
- windowList.end(),
- w);
- assert(it != windowList.end()); // window must be in list
- if (it == windowList.begin())
- return windowList.back(); // if we walked of the front, wrap around
-
- return *(--it);
-}
-
-
-BlackboxWindow* Workspace::getTopWindowOnStack(void) const {
- assert(! stackingList.empty());
- return stackingList.front();
-}
-
-
-unsigned int Workspace::getCount(void) const {
- return windowList.size();
-}
-
-
-void Workspace::appendStackOrder(BlackboxWindowList &stack_order) const {
- BlackboxWindowList::const_reverse_iterator it = stackingList.rbegin();
- const BlackboxWindowList::const_reverse_iterator end = stackingList.rend();
- for (; it != end; ++it)
- // don't add desktop wnidows, or sticky windows more than once
- if (! ( (*it)->isDesktop() ||
- ((*it)->isStuck() && id != screen->getCurrentWorkspaceID())))
- stack_order.push_back(*it);
-}
-
-
-bool Workspace::isCurrent(void) const {
- return (id == screen->getCurrentWorkspaceID());
-}
-
-
-bool Workspace::isLastWindow(const BlackboxWindow *w) const {
- return (w == windowList.back());
-}
-
-
-void Workspace::setCurrent(void) {
- screen->changeWorkspaceID(id);
-}
-
-
-void Workspace::readName(void) {
- otk::OBProperty::StringVect namesList;
- unsigned long numnames = id + 1;
-
- // attempt to get from the _NET_WM_DESKTOP_NAMES property
- if (xatom->get(screen->getRootWindow(), otk::OBProperty::net_desktop_names,
- otk::OBProperty::utf8, &numnames, &namesList) &&
- namesList.size() > id) {
- name = namesList[id];
-
- } else {
- /*
- Use a default name. This doesn't actually change the class. That will
- happen after the setName changes the root property, and that change
- makes its way back to this function.
- */
- string tmp = "Workspace %d";
- assert(tmp.length() < 32);
- char default_name[32];
- sprintf(default_name, tmp.c_str(), id + 1);
-
- setName(default_name); // save this into the _NET_WM_DESKTOP_NAMES property
- }
-}
-
-
-void Workspace::setName(const string& new_name) {
- // set the _NET_WM_DESKTOP_NAMES property with the new name
- otk::OBProperty::StringVect namesList;
- unsigned long numnames = (unsigned) -1;
- if (xatom->get(screen->getRootWindow(),
- otk::OBProperty::net_desktop_names,
- otk::OBProperty::utf8, &numnames, &namesList) &&
- namesList.size() > id)
- namesList[id] = new_name;
- else
- namesList.push_back(new_name);
-
- xatom->set(screen->getRootWindow(), otk::OBProperty::net_desktop_names,
- otk::OBProperty::utf8, namesList);
-}
-
-
-/*
- * Calculate free space available for window placement.
- */
-Workspace::rectList Workspace::calcSpace(const otk::Rect &win,
- const rectList &spaces) const {
- otk::Rect isect, extra;
- rectList result;
- rectList::const_iterator siter, end = spaces.end();
- for (siter = spaces.begin(); siter != end; ++siter) {
- const otk::Rect &curr = *siter;
-
- if(! win.intersects(curr)) {
- result.push_back(curr);
- continue;
- }
-
- /* Use an intersection of win and curr to determine the space around
- * curr that we can use.
- *
- * NOTE: the spaces calculated can overlap.
- */
- isect = curr & win;
-
- // left
- extra.setCoords(curr.left(), curr.top(),
- isect.left() - screen->getSnapOffset(), curr.bottom());
- if (extra.valid()) result.push_back(extra);
-
- // top
- extra.setCoords(curr.left(), curr.top(),
- curr.right(), isect.top() - screen->getSnapOffset());
- if (extra.valid()) result.push_back(extra);
-
- // right
- extra.setCoords(isect.right() + screen->getSnapOffset(), curr.top(),
- curr.right(), curr.bottom());
- if (extra.valid()) result.push_back(extra);
-
- // bottom
- extra.setCoords(curr.left(), isect.bottom() + screen->getSnapOffset(),
- curr.right(), curr.bottom());
- if (extra.valid()) result.push_back(extra);
- }
- return result;
-}
-
-
-static bool rowRLBT(const otk::Rect &first, const otk::Rect &second) {
- if (first.bottom() == second.bottom())
- return first.right() > second.right();
- return first.bottom() > second.bottom();
-}
-
-static bool rowRLTB(const otk::Rect &first, const otk::Rect &second) {
- if (first.y() == second.y())
- return first.right() > second.right();
- return first.y() < second.y();
-}
-
-static bool rowLRBT(const otk::Rect &first, const otk::Rect &second) {
- if (first.bottom() == second.bottom())
- return first.x() < second.x();
- return first.bottom() > second.bottom();
-}
-
-static bool rowLRTB(const otk::Rect &first, const otk::Rect &second) {
- if (first.y() == second.y())
- return first.x() < second.x();
- return first.y() < second.y();
-}
-
-static bool colLRTB(const otk::Rect &first, const otk::Rect &second) {
- if (first.x() == second.x())
- return first.y() < second.y();
- return first.x() < second.x();
-}
-
-static bool colLRBT(const otk::Rect &first, const otk::Rect &second) {
- if (first.x() == second.x())
- return first.bottom() > second.bottom();
- return first.x() < second.x();
-}
-
-static bool colRLTB(const otk::Rect &first, const otk::Rect &second) {
- if (first.right() == second.right())
- return first.y() < second.y();
- return first.right() > second.right();
-}
-
-static bool colRLBT(const otk::Rect &first, const otk::Rect &second) {
- if (first.right() == second.right())
- return first.bottom() > second.bottom();
- return first.right() > second.right();
-}
-
-
-bool Workspace::smartPlacement(otk::Rect& win) {
- rectList spaces;
-
- //initially the entire screen is free
-#ifdef XINERAMA
- if (screen->isXineramaActive() &&
- screen->getBlackbox()->doXineramaPlacement()) {
- RectList availableAreas = screen->allAvailableAreas();
- RectList::iterator it, end = availableAreas.end();
-
- for (it = availableAreas.begin(); it != end; ++it) {
- Rect r = *it;
- r.setRect(r.x() + screen->getSnapOffset(),
- r.y() + screen->getSnapOffset(),
- r.width() - screen->getSnapOffset(),
- r.height() - screen->getSnapOffset());
- spaces.push_back(*it);
- }
- } else
-#endif // XINERAMA
- {
- otk::Rect r = screen->availableArea();
- r.setRect(r.x() + screen->getSnapOffset(),
- r.y() + screen->getSnapOffset(),
- r.width() - screen->getSnapOffset(),
- r.height() - screen->getSnapOffset());
- spaces.push_back(r);
- }
-
- //Find Free Spaces
- BlackboxWindowList::const_iterator wit = windowList.begin(),
- end = windowList.end();
- otk::Rect tmp;
- for (; wit != end; ++wit) {
- const BlackboxWindow* const curr = *wit;
-
- // watch for shaded windows and full-maxed windows
- if (curr->isShaded()) {
- if (screen->getPlaceIgnoreShaded()) continue;
- } else if (curr->isMaximizedFull()) {
- if (screen->getPlaceIgnoreMaximized()) continue;
- }
-
- tmp.setRect(curr->frameRect().x(), curr->frameRect().y(),
- curr->frameRect().width() + screen->getWindowStyle()->getBorderWidth(),
- curr->frameRect().height() + screen->getWindowStyle()->getBorderWidth());
-
- spaces = calcSpace(tmp, spaces);
- }
-
- if (screen->getPlacementPolicy() == BScreen::RowSmartPlacement) {
- if(screen->getRowPlacementDirection() == BScreen::LeftRight) {
- if(screen->getColPlacementDirection() == BScreen::TopBottom)
- std::sort(spaces.begin(), spaces.end(), rowLRTB);
- else
- std::sort(spaces.begin(), spaces.end(), rowLRBT);
- } else {
- if(screen->getColPlacementDirection() == BScreen::TopBottom)
- std::sort(spaces.begin(), spaces.end(), rowRLTB);
- else
- std::sort(spaces.begin(), spaces.end(), rowRLBT);
- }
- } else {
- if(screen->getColPlacementDirection() == BScreen::TopBottom) {
- if(screen->getRowPlacementDirection() == BScreen::LeftRight)
- std::sort(spaces.begin(), spaces.end(), colLRTB);
- else
- std::sort(spaces.begin(), spaces.end(), colRLTB);
- } else {
- if(screen->getRowPlacementDirection() == BScreen::LeftRight)
- std::sort(spaces.begin(), spaces.end(), colLRBT);
- else
- std::sort(spaces.begin(), spaces.end(), colRLBT);
- }
- }
-
- rectList::const_iterator sit = spaces.begin(), spaces_end = spaces.end();
- for(; sit != spaces_end; ++sit) {
- if (sit->width() >= win.width() && sit->height() >= win.height())
- break;
- }
-
- if (sit == spaces_end)
- return False;
-
- //set new position based on the empty space found
- const otk::Rect& where = *sit;
- win.setX(where.x());
- win.setY(where.y());
-
- // adjust the location() based on left/right and top/bottom placement
- if (screen->getPlacementPolicy() == BScreen::RowSmartPlacement) {
- if (screen->getRowPlacementDirection() == BScreen::RightLeft)
- win.setX(where.right() - win.width());
- if (screen->getColPlacementDirection() == BScreen::BottomTop)
- win.setY(where.bottom() - win.height());
- } else {
- if (screen->getColPlacementDirection() == BScreen::BottomTop)
- win.setY(win.y() + where.height() - win.height());
- if (screen->getRowPlacementDirection() == BScreen::RightLeft)
- win.setX(win.x() + where.width() - win.width());
- }
- return True;
-}
-
-
-bool Workspace::underMousePlacement(otk::Rect &win) {
- int x, y, rx, ry;
- Window c, r;
- unsigned int m;
- XQueryPointer(otk::OBDisplay::display, screen->getRootWindow(),
- &r, &c, &rx, &ry, &x, &y, &m);
-
- otk::Rect area;
-#ifdef XINERAMA
- if (screen->isXineramaActive() &&
- screen->getBlackbox()->doXineramaPlacement()) {
- RectList availableAreas = screen->allAvailableAreas();
- RectList::iterator it, end = availableAreas.end();
-
- for (it = availableAreas.begin(); it != end; ++it)
- if (it->contains(rx, ry)) break;
- assert(it != end); // the mouse isn't inside an area?
- area = *it;
- } else
-#endif // XINERAMA
- area = screen->availableArea();
-
- x = rx - win.width() / 2;
- y = ry - win.height() / 2;
-
- if (x < area.x())
- x = area.x();
- if (y < area.y())
- y = area.y();
- if (x + win.width() > area.x() + area.width())
- x = area.x() + area.width() - win.width();
- if (y + win.height() > area.y() + area.height())
- y = area.y() + area.height() - win.height();
-
- win.setX(x);
- win.setY(y);
-
- return True;
-}
-
-
-bool Workspace::cascadePlacement(otk::Rect &win, const int offset) {
- otk::Rect area;
-
-#ifdef XINERAMA
- if (screen->isXineramaActive() &&
- screen->getBlackbox()->doXineramaPlacement()) {
- area = screen->allAvailableAreas()[cascade_region];
- } else
-#endif // XINERAMA
- area = screen->availableArea();
-
- if ((static_cast<signed>(cascade_x + win.width()) > area.right() + 1) ||
- (static_cast<signed>(cascade_y + win.height()) > area.bottom() + 1)) {
- cascade_x = cascade_y = 0;
-#ifdef XINERAMA
- if (screen->isXineramaActive() &&
- screen->getBlackbox()->doXineramaPlacement()) {
- // go to the next xinerama region, and use its area
- if (++cascade_region >= screen->allAvailableAreas().size())
- cascade_region = 0;
- area = screen->allAvailableAreas()[cascade_region];
- }
-#endif // XINERAMA
- }
-
- if (cascade_x == 0) {
- cascade_x = area.x() + offset;
- cascade_y = area.y() + offset;
- }
-
- win.setPos(cascade_x, cascade_y);
-
- cascade_x += offset;
- cascade_y += offset;
-
- return True;
-}
-
-
-void Workspace::placeWindow(BlackboxWindow *win) {
- otk::Rect new_win(0, 0, win->frameRect().width(), win->frameRect().height());
- bool placed = False;
-
- switch (screen->getPlacementPolicy()) {
- case BScreen::RowSmartPlacement:
- case BScreen::ColSmartPlacement:
- placed = smartPlacement(new_win);
- break;
- case BScreen::UnderMousePlacement:
- case BScreen::ClickMousePlacement:
- placed = underMousePlacement(new_win);
- default:
- break; // handled below
- } // switch
-
- if (placed == False)
- cascadePlacement(new_win, (win->getTitleHeight() +
- screen->getWindowStyle()->getBorderWidth() * 2));
-
- if (new_win.right() > screen->availableArea().right())
- new_win.setX(screen->availableArea().left());
- if (new_win.bottom() > screen->availableArea().bottom())
- new_win.setY(screen->availableArea().top());
-
- win->configure(new_win.x(), new_win.y(), new_win.width(), new_win.height());
-}
-
-}