summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/tiles.cpp936
-rw-r--r--src/tiles.h245
2 files changed, 1181 insertions, 0 deletions
diff --git a/src/tiles.cpp b/src/tiles.cpp
new file mode 100644
index 0000000..5873ae1
--- /dev/null
+++ b/src/tiles.cpp
@@ -0,0 +1,936 @@
+/*
+ * Compiz Tiles Plugin
+ *
+ * tiles.cpp
+ *
+ * Copyright (c) 2011 Sam Spilsbury <smspillaz@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "tiles.h"
+
+COMPIZ_PLUGIN_20090315 (tiles, TilesPluginVTable);
+
+Tile::Tile (TileLayerPtr parent) :
+ priv (new PrivateTile)
+{
+ priv->mParent = parent;
+ priv->mProgress = 0.0f;
+ priv->mWindow = NULL;
+}
+
+Tile::~Tile()
+{
+ if (priv->mWindow)
+ {
+ XWindowChanges xwc;
+ xwc.x = priv->mSaveGeometry.x ();
+ xwc.y = priv->mSaveGeometry.y ();
+ xwc.width = priv->mSaveGeometry.width ();
+ xwc.height = priv->mSaveGeometry.height ();
+
+ priv->mWindow->configureXWindow (priv->mSaveMask, &xwc);
+ }
+
+ delete priv;
+}
+
+CompRect
+Tile::rect ()
+{
+ return priv->mRect;
+}
+
+CompWindow *
+Tile::window ()
+{
+ return priv->mWindow;
+}
+
+bool
+Tile::allocateWindow (CompWindow *w)
+{
+ if (priv->mWindow)
+ return false;
+
+ priv->mWindow = w;
+ priv->mSaveGeometry = priv->mWindow->serverGeometry ();
+ priv->mSaveMask = CWX | CWY | CWWidth | CWHeight;
+
+ return true;
+}
+
+const unsigned int anim_time = 300;
+
+bool
+Tile::animate (unsigned int ms)
+{
+ priv->mProgress += ms / static_cast <float> (anim_time);
+
+ if (priv->mProgress >= anim_time)
+ {
+ priv->mProgress = anim_time;
+ return false;
+ }
+
+ return true;
+}
+
+void
+Tile::lockTargetSize()
+{
+ CompRect resizeTarget = priv->mRect;
+
+ resized (resizeTarget);
+
+ XWindowChanges xwc;
+ unsigned int valueMask;
+
+ valueMask = CWX | CWY | CWWidth | CWHeight;
+ xwc.x = resizeTarget.x ();
+ xwc.y = resizeTarget.y ();
+ xwc.width = resizeTarget.width ();
+ xwc.height = resizeTarget.height ();
+
+ priv->mWindow->configureXWindow (valueMask, &xwc);
+}
+
+void
+Tile::resized (CompRect &r)
+{
+ r.setX (r.x () + priv->mWindow->border ().left);
+ r.setWidth (r.width () - priv->mWindow->border ().right
+ - priv->mWindow->border ().left);
+ r.setY (r.y () + priv->mWindow->border ().top);
+ r.setHeight (r.height () - priv->mWindow->border ().bottom
+ - priv->mWindow->border ().top);
+}
+
+GLVector
+Tile::scale ()
+{
+ GLVector v;
+
+ v[GLVector::x] = std::min (1.0f, priv->mWindow->serverBorderRect ().width () / static_cast <float> (priv->mRect.width ()));
+ v[GLVector::y] = std::min (1.0f, priv->mWindow->serverBorderRect ().height () / static_cast <float> (priv->mRect.height ()));
+ v[GLVector::z] = 1.0f;
+ v[GLVector::w] = 1.0f;
+
+ return v;
+}
+
+bool
+Tile::setTargetSize (CompRect &r)
+{
+ priv->mRect = r;
+ return true;
+}
+
+const std::vector<Tile::Ptr> &
+TileLayer::tiles ()
+{
+ return priv->mTiles;
+}
+
+void
+TileLayer::handleWindowResize (CompWindow *output)
+{
+ CompRegion available = priv->mOutput->workArea ();
+
+ /* Resize all the other tiles accordingly by
+ * calculating the gaps in the current tile
+ * layer and filling them
+ * FIXME: this could probably be a lot more
+ * optimized rather than crunching regions */
+
+ for (Tile::Ptr t : priv->mTiles)
+ {
+ available -= t->rect ();
+ }
+
+ /* Search tiles for available space, resize where possible */
+ for (const CompRect &r : available.rects ())
+ {
+ }
+}
+
+const unsigned int drop_zone_area = 20;
+
+void
+Tile::grab ()
+{
+}
+
+const unsigned int max_drag = 20;
+
+bool
+Tile::drag (CompPoint &p)
+{
+ priv->mDragBuffer += p;
+
+ float factor = sqrt (pow (abs (priv->mDragBuffer.x ()), 2) +
+ pow (abs (priv->mDragBuffer.y ()), 2));
+
+ if (factor >= max_drag)
+ return true;
+
+ return false;
+}
+
+void
+Tile::ungrab ()
+{
+ priv->mDragBuffer = CompPoint ();
+}
+
+void
+TileLayer::handleWindowGrabbed (CompWindow *w)
+{
+ priv->mPushedSpaces.clear();
+
+ /* Recalculate drop zones */
+ if (tiles ().empty ())
+ {
+ priv->mDropZones = CompRegion (*priv->mOutput) -
+ CompRegion (priv->mOutput->x () + drop_zone_area, priv->mOutput->y () + drop_zone_area,
+ priv->mOutput->width () - drop_zone_area * 2, priv->mOutput->height () - drop_zone_area * 2);
+ }
+ else
+ {
+ priv->mDropZones = CompRegion (*priv->mOutput);
+ for (const Tile::Ptr &tp : tiles ())
+ {
+ priv->mDropZones -= CompRegion (tp->rect ().x () + drop_zone_area, tp->rect ().y () + drop_zone_area,
+ tp->rect ().width () - drop_zone_area * 2, tp->rect ().height () - drop_zone_area * 2);
+ }
+ }
+
+ priv->mLastPointer = CompPoint (pointerX, pointerY);
+}
+
+void
+TileLayer::handleWindowUngrabbed (CompWindow *w)
+{
+ CompPoint p (pointerX, pointerY);
+ RectMap::Map::iterator it = priv->mPushedSpaces.begin ();
+ std::vector <Tile::Ptr>::iterator tpit = priv->mTiles.begin ();
+
+ for (; tpit != priv->mTiles.end (); tpit++)
+ {
+ if ((*tpit)->window () == w)
+ break;
+ }
+
+ if (tpit == priv->mTiles.end ())
+ {
+ for (; it != priv->mPushedSpaces.end (); it++)
+ {
+ if (it->first.contains (p))
+ {
+ addWindow (w, it->second);
+ break;
+ }
+ }
+ }
+ else
+ (*tpit)->ungrab ();
+}
+
+void
+PrivateTileLayer::pushSpace (CompRect r)
+{
+ CompRect available = mOutput->workArea ();
+ unsigned int nTiles = 0;
+
+ if (mPushedSpaces.find (r) != mPushedSpaces.end ())
+ return;
+
+ /* Calculate what this space expands to :
+ * If this stip is longer than it is wider,
+ * expand it horizontally, otherwise
+ * vertically */
+
+ if (r.height () > r.width ())
+ {
+ available.setHeight (r.height ());
+ available.setY (r.y ());
+
+ /* The way the region calculation works out
+ * means that there might be some extra space
+ * so work out if there are any other tiles partially
+ * overlapping workArea.height () * r.width ()
+ * and then add the empty spaces */
+ CompRegion bar = CompRegion (r.x (), mOutput->workArea ().y (), r.width (), mOutput->workArea ().height ());
+ CompRegion origBar = bar;
+
+ for (const Tile::Ptr &tp : mTiles)
+ {
+ bar -= tp->rect ();
+ if (tp->rect ().y () == origBar.boundingRect ().y ())
+ {
+ nTiles++;
+ }
+ }
+
+ if (bar.isEmpty () ||
+ (bar - CompRegion (r.x (), mOutput->workArea ().y (),
+ r.width (), mOutput->workArea ().height ())).isEmpty ())
+ {
+ available.setY (origBar.boundingRect ().y ());
+ available.setHeight (origBar.boundingRect ().height ());
+ }
+
+ /* Divide by number of tiles + 1, this will be
+ * the space we need to occupy */
+
+ unsigned int width = available.width () / (nTiles + 1);
+
+ /* Prefer going left to right unless we'd go offscreen that way */
+ if (r.x () + width > mOutput->width ())
+ available.setX (r.x2 () - width);
+ else
+ available.setX (r.x ());
+
+ available.setWidth (width);
+ }
+ else
+ {
+ available.setWidth (r.width ());
+ available.setX (r.x ());
+
+ /* The way the region calculation works out
+ * means that there might be some extra space
+ * so work out if there are any other tiles partially
+ * overlapping workArea.height () * r.width ()
+ * and then add the empty spaces */
+ CompRegion bar = CompRegion (r.x (), mOutput->workArea ().y (), r.width (), mOutput->workArea ().height ());
+ CompRegion origBar = bar;
+
+ for (const Tile::Ptr &tp : mTiles)
+ {
+ bar -= tp->rect ();
+ if (tp->rect ().x () == origBar.boundingRect ().x ())
+ nTiles++;
+ }
+
+ if (bar.isEmpty () ||
+ (bar - CompRegion (mOutput->workArea ().x (), r.y (),
+ mOutput->workArea ().width (), r.height ())).isEmpty ())
+ {
+ available.setX (origBar.boundingRect ().x ());
+ available.setWidth (origBar.boundingRect ().width ());
+ }
+
+ /* Divide by number of tiles + 1, this will be
+ * the space we need to occupy */
+
+ unsigned int height = available.height () / (nTiles + 1);
+
+ /* Prefer going top to bottom unless we'd go offscreen that way */
+ if (r.y () + height > mOutput->height ())
+ available.setY (r.y2 () - height);
+ else
+ available.setY (r.y ());
+
+ available.setHeight (height);
+ }
+
+ mPushedSpaces[r] = available;
+}
+
+void
+PrivateTileLayer::popSpaces ()
+{
+ mPushedSpaces.clear ();
+}
+
+void
+TileLayer::handleWindowGrabMovement (CompWindow *w, CompPoint &p)
+{
+ bool spaceFound = false;
+ std::vector <Tile::Ptr>::iterator tpit = priv->mTiles.begin ();
+
+ for (; tpit != priv->mTiles.end (); tpit++)
+ {
+ if ((*tpit)->window () == w)
+ break;
+ }
+
+ if (tpit == priv->mTiles.end ())
+ {
+ for (CompRect r : priv->mDropZones.rects ())
+ {
+ if (CompRegion (r).contains (p))
+ {
+ /* expand r to next tile space */
+ priv->pushSpace (r);
+ spaceFound = true;
+ }
+ }
+
+ if (!spaceFound)
+ priv->popSpaces ();
+ }
+ else
+ {
+ CompPoint dp = priv->mLastPointer - p;
+
+ if (!(*tpit)->drag (dp))
+ {
+ int dx = dp.x ();
+ int dy = dp.y ();
+
+ w->move (dx, dy, true);
+ w->syncPosition ();
+ }
+ else
+ removeWindow (w);
+ }
+
+ priv->mLastPointer = p;
+}
+
+bool
+TileLayer::tilesToRegion (std::vector <Tile::Ptr> tiles,
+ CompRegion tilesRegion,
+ CompRegion nextRegion)
+{
+ for (Tile::Ptr tp : tiles)
+ {
+ float xScale = nextRegion.boundingRect ().width () / static_cast <float> (tilesRegion.boundingRect ().width ());
+ float yScale = nextRegion.boundingRect ().height () / static_cast <float> (tilesRegion.boundingRect ().height ());
+
+ CompPoint pos = tp->rect ().pos () - tilesRegion.boundingRect ().pos ();
+ CompRect nextPosition;
+
+ /* Scale the new positions */
+ pos.setX (pos.x () * xScale);
+ pos.setY (pos.y () * yScale);
+
+ pos += nextRegion.boundingRect ().pos ();
+
+ /* Now scale the sizes */
+ nextPosition.setWidth (tp->rect ().width () * xScale);
+ nextPosition.setHeight (tp->rect ().height () * yScale);
+
+ nextPosition.setPos (pos);
+
+ if (!tp->setTargetSize (nextPosition))
+ return false;
+ }
+
+ return true;
+}
+
+bool
+TileLayer::addWindow (CompWindow *w, CompRect &r)
+{
+ Tile::Ptr t = Tile::Ptr (new Tile (shared_from_this ()));
+ std::vector <Tile::Ptr> intersectingTiles;
+ CompRegion intersectingTilesRegion;
+ CompRegion insertRegion (r);
+
+ /* Expand like this:
+ * -------------
+ * | |
+ * | |
+ * | |
+ * | |
+ * | |
+ * ------------------------------------
+ * | | | |
+ * | | | |
+ * | | | |
+ * | | | |
+ * |----------|-----------------------|
+ * | |
+ * | |
+ * | |
+ * | |
+ * | |
+ * -------------
+ */
+
+ insertRegion += CompRegion (r.x (), r.y (), priv->mOutput->workArea ().width () - r.x2 (), r.height ());
+ insertRegion += CompRegion (r.x (), r.y (), r.width (), priv->mOutput->workArea ().width () - r.y2 ());
+ insertRegion += CompRegion (priv->mOutput->workArea ().x (), r.y (), r.x () - priv->mOutput->workArea().x (), r.height ());
+ insertRegion += CompRegion (r.x (), priv->mOutput->workArea ().y (), r.width (), r.y () - priv->mOutput->workArea ().y ());
+
+
+ if (!t->allocateWindow (w))
+ return false;
+
+ /* Allocate a new space for this window
+ * and change the target size of other windows */
+ if (!t->setTargetSize (r))
+ return false;
+
+ /* Now reallocate the other tiles that intersect this one */
+ for (Tile::Ptr tp : priv->mTiles)
+ {
+ if (CompRegion (tp->rect ()).intersects (insertRegion))
+ {
+ intersectingTiles.push_back (tp);
+ intersectingTilesRegion += tp->rect ();
+ }
+ }
+
+ CompRegion allowedRegion = intersectingTilesRegion - r;
+
+ if (!tilesToRegion (intersectingTiles, intersectingTilesRegion, allowedRegion))
+ return false;
+
+ priv->mTiles.push_back (t);
+
+ for (Tile::Ptr tp : priv->mTiles)
+ tp->lockTargetSize ();
+
+ priv->popSpaces ();
+}
+
+void
+TileLayer::removeWindow (CompWindow *w)
+{
+ std::vector <Tile::Ptr>::iterator tpit = priv->mTiles.begin ();
+ std::vector <Tile::Ptr> resizeTiles;
+ CompRegion tilesRegion;
+
+ for (; tpit != priv->mTiles.end (); tpit++)
+ {
+ if ((*tpit)->window () == w)
+ {
+ priv->mTiles.erase (tpit);
+ break;
+ }
+ }
+
+ /* Now calculate the empty space and find any tiles contained
+ * in that space and expand them evenly to fill the empty space
+ * created by the newly removed tile */
+
+ CompRegion reg = priv->mOutput->workArea ();
+
+ for (Tile::Ptr tp : priv->mTiles)
+ reg -= tp->rect ();
+
+ CompRegion insertRegion = reg;
+ CompRect r = reg.boundingRect ();
+
+ /* Expand the rect towards screen edges (as illustrated above) */
+ insertRegion += CompRegion (r.x (), r.y (), priv->mOutput->workArea ().width () - r.x2 (), r.height ());
+ insertRegion += CompRegion (r.x (), r.y (), r.width (), priv->mOutput->workArea ().width () - r.y2 ());
+ insertRegion += CompRegion (priv->mOutput->workArea ().x (), r.y (), r.x () - priv->mOutput->workArea().x (), r.height ());
+ insertRegion += CompRegion (r.x (), priv->mOutput->workArea ().y (), r.width (), r.y () - priv->mOutput->workArea ().y ());
+
+ /* Now find any windows which are directly contained by this
+ * cross-shaped region */
+ for (Tile::Ptr tp : priv->mTiles)
+ {
+ if ((CompRegion (tp->rect ()) - insertRegion).isEmpty ())
+ {
+ resizeTiles.push_back (tp);
+ tilesRegion += tp->rect ();
+ }
+ }
+
+ reg += tilesRegion;
+
+ if (tilesToRegion (resizeTiles, tilesRegion, reg));
+ for (Tile::Ptr tp : resizeTiles)
+ tp->lockTargetSize ();
+}
+
+bool
+TileLayer::animate (unsigned int ms)
+{
+ bool done = false;
+
+ for (const Tile::Ptr &p : tiles ())
+ done |= p->animate (ms);
+
+ if (done)
+ {
+ /* Check the layer animation too */
+ // ??
+ }
+
+ return done;
+}
+
+void
+TileLayer::paintOutline(const GLMatrix &m)
+{
+ return;
+}
+
+void
+Tile::damage ()
+{
+ if (priv->mWindow)
+ {
+ std::vector <GLVector> vecs = { GLVector (priv->mWindow->serverBorderRect ().x (), priv->mWindow->serverBorderRect ().y (), 1.0f, 1.0f), // top left
+ GLVector (priv->mWindow->serverBorderRect ().x2 (), priv->mWindow->serverBorderRect ().y2 (), 1.0f, 1.0f) }; // bottom right
+
+ GLMatrix sMatrix;
+
+ sMatrix.scale (scale ());
+
+ for (GLVector &v : vecs)
+ {
+ v = sMatrix * v;
+ }
+
+ CompRegion reg (vecs[0][GLVector::x], vecs[0][GLVector::y],
+ vecs[1][GLVector::x] - vecs[0][GLVector::x],
+ vecs[1][GLVector::y] - vecs[0][GLVector::y]);
+
+ CompositeScreen::get (screen)->damageRegion (reg);
+ }
+}
+
+void
+TileLayer::damage ()
+{
+ for (const Tile::Ptr &p : tiles ())
+ p->damage ();
+}
+
+TileLayer::TileLayer (CompOutput *output) :
+ priv (new PrivateTileLayer)
+{
+ priv->mOutput = output;
+}
+
+TileLayer::~TileLayer ()
+{
+ delete priv;
+}
+
+void
+TilesWindow::grabNotify (int x, int y, unsigned int state, unsigned int mask)
+{
+ if (mask & (CompWindowGrabMoveMask | CompWindowGrabButtonMask))
+ {
+ if (!priv->mTile)
+ priv->window->moveNotifySetEnabled (this, true);
+ TilesScreen::get (screen)->addGrabbedWindow (this);
+ }
+
+ priv->window->grabNotify (x, y, state, mask);
+}
+
+void
+TilesWindow::ungrabNotify ()
+{
+ TilesScreen::get (screen)->removeGrabbedWindow (this);
+
+ if (!priv->mTile)
+ priv->window->moveNotifySetEnabled (this, false);
+
+ priv->window->ungrabNotify ();
+}
+
+void
+TilesWindow::moveNotify (int dx, int dy, bool immediate)
+{
+ TilesScreen::get (screen)->dragGrabbedWindow (this, CompPoint (pointerX, pointerY));
+
+ priv->window->moveNotify (dx, dy, immediate);
+}
+
+void
+TilesWindow::resizeNotify (int dx, int dy, int dwidth, int dheight)
+{
+ if (priv->mTile)
+ {
+ CompRect g (static_cast <CompRect &> (priv->window->geometry ()));
+ priv->mTile->resized (g);
+ }
+
+ priv->window->resizeNotify (dx, dy, dwidth, dheight);
+}
+
+void
+TilesWindow::validateResizeRequest (unsigned int &mask,
+ XWindowChanges *xwc,
+ unsigned int source)
+{
+ priv->window->validateResizeRequest (mask, xwc, source);
+
+ /* force xwc to be the current geometry */
+ xwc->x = priv->window->geometry().x ();
+ xwc->y = priv->window->geometry().y ();
+ xwc->width = priv->window->geometry().width ();
+ xwc->height = priv->window->geometry().height ();
+}
+
+bool
+TilesWindow::glPaint (const GLWindowPaintAttrib &attrib, const GLMatrix &matrix,
+ const CompRegion &region, unsigned int mask)
+{
+ if (priv->mTile)
+ {
+ GLVector scale = { 1.0f, 1.0f, 1.0f, 1.0f };
+ GLVector translation = { 0.0f, 0.0f, 0.0f, 0.0f };
+ GLMatrix wTransform (matrix);
+ const CompRect &rect = priv->mTile->rect ();
+
+ /* First translate to the target rectangle such
+ * that the center of the window geometry is the
+ * center of the target rectangle */
+
+ translation[GLVector::x] = rect.centerX () - priv->window->geometry ().centerX ();
+ translation[GLVector::y] = rect.centerY () - priv->window->geometry ().centerY ();
+
+ scale[GLVector::x] = rect.width () / static_cast <float> (priv->window->geometry ().width ());
+ scale[GLVector::y] = rect.height () / static_cast <float> (priv->window->geometry ().height ());
+
+ wTransform.translate (translation);
+ wTransform.scale (scale);
+
+ translation[GLVector::x] = -(rect.centerX () - priv->window->geometry ().centerX ());
+ translation[GLVector::y] = -(rect.centerY () - priv->window->geometry ().centerY ());
+
+ wTransform.translate (translation);
+
+ mask |= PAINT_WINDOW_TRANSFORMED_MASK;
+ }
+
+ priv->gWindow->glPaint (attrib, matrix, region, mask);
+}
+
+bool
+TilesWindow::damageRect (bool initial, const CompRect &rect)
+{
+ bool ret = false;
+
+ if (priv->mTile)
+ {
+ ret = true;
+
+ std::vector <GLVector> vec = { GLVector (rect.x1 (), rect.y1 (), 0.0, 1.0),
+ GLVector (rect.x2 (), rect.y1 (), 0.0, 1.0),
+ GLVector (rect.x1 (), rect.y2 (), 0.0, 1.0),
+ GLVector (rect.x2 (), rect.y2 (), 0.0, 1.0)
+ };
+
+ /* Determine scaling matrix */
+ GLMatrix matrix;
+ GLVector scale;
+ GLVector translation;
+
+ translation[GLVector::x] = rect.centerX () - priv->window->geometry ().centerX ();
+ translation[GLVector::y] = rect.centerY () - priv->window->geometry ().centerY ();
+
+ scale[GLVector::x] = rect.width () / static_cast <float> (priv->window->geometry ().width ());
+ scale[GLVector::y] = rect.height () / static_cast <float> (priv->window->geometry ().height ());
+
+ matrix.translate (translation);
+ matrix.scale (scale);
+
+ translation[GLVector::x] = -(rect.centerX () - priv->window->geometry ().centerX ());
+ translation[GLVector::y] = -(rect.centerY () - priv->window->geometry ().centerY ());
+
+ matrix.translate (translation);
+
+ /* Now multiply damage co-ords and damage region */
+ for (GLVector &v : vec)
+ v = matrix * v;
+
+ CompRect r = CompRect (vec[0][GLVector::x], vec[0][GLVector::y],
+ vec[1][GLVector::x] - vec[0][GLVector::x],
+ vec[2][GLVector::y] - vec[0][GLVector::y]);
+ CompRegion reg (r);
+
+ CompositeScreen::get (screen)->damageRegion (reg);
+ }
+
+ priv->cWindow->damageRect(initial, rect);
+
+ return ret;
+}
+
+bool
+TilesWindow::animate(unsigned int ms)
+{
+ return priv->mTile->animate (ms);
+}
+
+void
+TilesWindow::damage ()
+{
+ priv->cWindow->addDamage (true);
+}
+
+void
+TilesScreen::dragGrabbedWindow (TilesWindow *tw, CompPoint pointer)
+{
+ if (priv->mTileLayers.size ())
+ priv->mTileLayers.front ()->handleWindowGrabMovement (tw->window (), pointer);
+}
+
+void
+TilesScreen::removeGrabbedWindow (TilesWindow *tw)
+{
+ auto it = std::find (priv->mDraggedWindows.begin (), priv->mDraggedWindows.end (), tw);
+
+ if (it != priv->mDraggedWindows.end ())
+ priv->mDraggedWindows.erase (it);
+
+ if (!priv->mTileLayers.empty ())
+ priv->mTileLayers.back ()->handleWindowUngrabbed (tw->window ());
+
+ /* No more dragged windows and the last tile layer is empty,
+ * delete it */
+
+ if (priv->mTileLayers.size () == 1)
+ if (priv->mTileLayers.front ()->tiles ().empty ())
+ priv->mTileLayers.clear ();
+}
+
+void
+TilesScreen::addGrabbedWindow (TilesWindow *tw)
+{
+ TileLayer::Ptr tp;
+
+ /* If there is no layers then we should create one */
+ if (priv->mTileLayers.empty ())
+ {
+ tp = TileLayer::Ptr (new TileLayer (&screen->outputDevs ()[tw->window ()->outputDevice ()]));
+
+ priv->mTileLayers.push_back (tp);
+ }
+ else
+ tp = priv->mTileLayers.back ();
+
+ if (std::find (priv->mDraggedWindows.begin (), priv->mDraggedWindows.end (), tw)
+ != priv->mDraggedWindows.end ())
+ priv->mDraggedWindows.push_back (tw);
+
+ tp->handleWindowGrabbed (tw->window ());
+
+ //tw->priv->window->moveNotifySetEnabled (tw, true);
+}
+
+void
+TilesScreen::preparePaint (int ms)
+{
+ bool continueAnimation = false;
+
+ for (TileLayer::Ptr p : priv->mTileLayers)
+ continueAnimation = p->animate (ms);
+
+ for (TilesWindow *tw : priv->mDraggedWindows)
+ continueAnimation = tw->animate (ms);
+
+ priv->cScreen->preparePaint (ms);
+}
+
+bool
+TilesScreen::glPaintOutput (const GLScreenPaintAttrib &attrib, const GLMatrix &matrix,
+ const CompRegion &region, CompOutput *output, unsigned int mask)
+{
+ bool continueAnimation = priv->mDraggedWindows.size () ? true : false;
+
+ /* continueAnimation means that there are scaled windows
+ * around so we need to allow painting with custom vertex
+ * layouts */
+ for (TileLayer::Ptr p : priv->mTileLayers)
+ {
+ continueAnimation |= p->animate (0);
+ }
+
+ for (TilesWindow *tw : priv->mDraggedWindows)
+ continueAnimation |= tw->animate (0);
+
+ if (continueAnimation)
+ mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK;
+
+ priv->gScreen->glPaintOutput (attrib, matrix, region, output, mask);
+
+ /* continueAnimation means that there are scaled windows
+ * around so we need to allow painting with custom vertex
+ * layouts */
+ for (TileLayer::Ptr p : priv->mTileLayers)
+ {
+ bool animationsPending = p->animate (0);
+
+ if (animationsPending)
+ p->paintOutline (matrix);
+
+ continueAnimation |= animationsPending;
+ }
+}
+
+void
+TilesScreen::donePaint ()
+{
+ for (TileLayer::Ptr p : priv->mTileLayers)
+ {
+ if (p->animate (0))
+ p->damage ();
+ }
+
+ for (TilesWindow *tw : priv->mDraggedWindows)
+ {
+ if (tw->animate (0))
+ tw->damage ();
+ }
+}
+
+TilesWindow::TilesWindow (CompWindow *w) :
+ PluginClassHandler <TilesWindow, CompWindow> (w),
+ priv (new PrivateTilesWindow)
+{
+ priv->window = w;
+ priv->cWindow = CompositeWindow::get (w);
+ priv->gWindow = GLWindow::get (w);
+
+ WindowInterface::setHandler (priv->window);
+ CompositeWindowInterface::setHandler (priv->cWindow);
+ GLWindowInterface::setHandler (priv->gWindow);
+}
+
+TilesWindow::~TilesWindow ()
+{
+}
+
+TilesScreen::~TilesScreen ()
+{
+ delete priv;
+}
+
+TilesScreen::TilesScreen (CompScreen *s) :
+ PluginClassHandler <TilesScreen, CompScreen> (s),
+ priv (new PrivateTilesScreen)
+{
+ priv->cScreen = CompositeScreen::get (screen);
+ priv->gScreen = GLScreen::get (screen);
+
+ ScreenInterface::setHandler (screen);
+ CompositeScreenInterface::setHandler (priv->cScreen);
+ GLScreenInterface::setHandler (priv->gScreen);
+}
+
+
+bool
+TilesPluginVTable::init ()
+{
+ if (!CompPlugin::checkPluginABI ("core", CORE_ABIVERSION) ||
+ !CompPlugin::checkPluginABI ("composite", COMPIZ_COMPOSITE_ABI) ||
+ !CompPlugin::checkPluginABI ("opengl", COMPIZ_OPENGL_ABI))
+ {
+ return false;
+ }
+
+ return true;
+}
diff --git a/src/tiles.h b/src/tiles.h
new file mode 100644
index 0000000..2f0028c
--- /dev/null
+++ b/src/tiles.h
@@ -0,0 +1,245 @@
+/*
+ * Compiz Tiles Plugin
+ *
+ * tiles.h
+ *
+ * Copyright (c) 2011 Sam Spilsbury <smspillaz@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <core/core.h>
+#include <composite/composite.h>
+#include <opengl/opengl.h>
+#include <boost/shared_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
+#include <unordered_map>
+#include <hash_fun.h>
+
+#include "tiles_options.h"
+
+class TileLayer;
+typedef boost::shared_ptr <TileLayer> TileLayerPtr;
+
+namespace RectMap
+{
+ class Hash {
+ public:
+ bool operator() (const CompRect &r) const
+ {
+ std::hash <int> h;
+
+ return h (r.area ());
+ }
+ };
+
+ class Predicate {
+ public:
+ bool operator() (const CompRect &a, const CompRect &b) const
+ {
+ return (a == b);
+ }
+ };
+
+ typedef std::unordered_map<CompRect, CompRect, Hash, Predicate> Map;
+}
+
+class PrivateTile
+{
+ public:
+
+ TileLayerPtr mParent;
+ CompRect mRect;
+ CompWindow *mWindow;
+ CompWindow::Geometry mSaveGeometry;
+ unsigned int mSaveMask;
+ CompPoint mDragBuffer;
+ float mProgress;
+};
+
+class Tile :
+ public boost::enable_shared_from_this<Tile>
+{
+ public:
+
+ Tile (TileLayerPtr parent);
+ ~Tile ();
+
+ /* Target size */
+ CompRect rect ();
+ CompWindow * window ();
+ GLVector scale ();
+
+ /* Returns false if window cannot be put
+ * in this tile's allocation */
+ bool allocateWindow (CompWindow *);
+
+ /* setTargetSize will allocate a new target size
+ * for this tile, which will be animated to, while
+ * lockTargetSize actually resizes the window to that
+ * size */
+ bool setTargetSize (CompRect &);
+ void lockTargetSize ();
+
+ void resized (CompRect &);
+
+ bool animate (unsigned int ms);
+ void damage ();
+
+ void grab ();
+ bool drag (CompPoint &p);
+ void ungrab ();
+
+ typedef boost::shared_ptr<Tile> Ptr;
+ private:
+
+ PrivateTile *priv;
+};
+
+class PrivateTileLayer
+{
+ public:
+
+ void pushSpace (CompRect r);
+ void popSpaces ();
+
+ std::vector<Tile::Ptr> mTiles;
+ CompRegion mDropZones;
+ CompOutput *mOutput;
+ RectMap::Map mPushedSpaces;
+ CompPoint mLastPointer;
+};
+
+class TileLayer :
+ public boost::enable_shared_from_this <TileLayer>
+{
+ public:
+
+ TileLayer (CompOutput *output);
+ virtual ~TileLayer ();
+
+ const std::vector<Tile::Ptr> & tiles ();
+
+ void handleWindowResize (CompWindow *);
+ void handleWindowGrabbed (CompWindow *w);
+ void handleWindowGrabMovement (CompWindow *w, CompPoint &p);
+ void handleWindowUngrabbed (CompWindow *w);
+
+ bool addWindow (CompWindow *w, CompRect &);
+ void removeWindow (CompWindow *w);
+ void paintOutline (const GLMatrix &);
+
+ bool animate (unsigned int ms);
+ void damage ();
+
+ typedef TileLayerPtr Ptr;
+
+ protected:
+
+ bool tilesToRegion (std::vector <Tile::Ptr> tiles,
+ CompRegion tilesRegion,
+ CompRegion nextRegion);
+
+ private:
+
+ PrivateTileLayer *priv;
+};
+
+class PrivateTilesWindow
+{
+ public:
+ CompWindow *window;
+ CompositeWindow *cWindow;
+ GLWindow *gWindow;
+
+ Tile::Ptr mTile;
+};
+
+class TilesWindow :
+ public PluginClassHandler <TilesWindow, CompWindow>,
+ public WindowInterface,
+ public CompositeWindowInterface,
+ public GLWindowInterface
+{
+ public:
+
+ TilesWindow (CompWindow *w);
+ ~TilesWindow ();
+
+ void grabNotify (int x, int y, unsigned int state, unsigned int mask);
+ void ungrabNotify ();
+
+ void moveNotify (int dx, int dy, bool immediate);
+ void validateResizeRequest (unsigned int &mask,
+ XWindowChanges *xwc,
+ unsigned int source);
+ void resizeNotify (int dx, int dy, int dwidth, int dheight);
+
+ bool glPaint (const GLWindowPaintAttrib &attrib, const GLMatrix &matrix,
+ const CompRegion &region, unsigned int mask);
+ bool damageRect (bool initial, const CompRect &rect);
+
+ bool animate (unsigned int ms);
+
+ void damage ();
+
+ CompWindow * window () { return priv->window; } // XXX
+
+ private:
+
+ PrivateTilesWindow *priv;
+};
+
+class PrivateTilesScreen
+{
+ public:
+
+ CompositeScreen *cScreen;
+ GLScreen *gScreen;
+
+ std::vector <TilesWindow *> mDraggedWindows;
+ std::vector <TileLayer::Ptr> mTileLayers;
+};
+
+class TilesScreen :
+ public PluginClassHandler <TilesScreen, CompScreen>,
+ public ScreenInterface,
+ public CompositeScreenInterface,
+ public GLScreenInterface,
+ public TilesOptions
+{
+ public:
+
+ TilesScreen (CompScreen *);
+ ~TilesScreen ();
+
+ void preparePaint (int);
+ bool glPaintOutput (const GLScreenPaintAttrib &attrib, const GLMatrix &matrix,
+ const CompRegion &region, CompOutput *output, unsigned int mask);
+ void donePaint ();
+
+ void addGrabbedWindow (TilesWindow *tw);
+ void removeGrabbedWindow (TilesWindow *tw);
+ void dragGrabbedWindow (TilesWindow *tw, CompPoint pointer);
+ bool grabbedWindows ();
+
+ private:
+
+ PrivateTilesScreen *priv;
+};
+
+class TilesPluginVTable :
+ public CompPlugin::VTableForScreenAndWindow<TilesScreen, TilesWindow>
+{
+ public:
+
+ bool init ();
+};