summaryrefslogtreecommitdiff
path: root/src/tile.c
diff options
context:
space:
mode:
authorDanny Baumann <dannybaumann@web.de>2007-04-02 15:44:04 +0200
committerDanny Baumann <dannybaumann@web.de>2007-04-02 15:44:04 +0200
commitf4d1fd48ab08f4a8ac417cec5496c84b5f55afce (patch)
tree628a48a5ea754ed195342b4097767c33514e0be9 /src/tile.c
parent04d0f4a2ba324e37d320adcc9054ac3b2eb6b453 (diff)
downloadberyl-premerge-f4d1fd48ab08f4a8ac417cec5496c84b5f55afce.tar.gz
beryl-premerge-f4d1fd48ab08f4a8ac417cec5496c84b5f55afce.tar.bz2
added ported plugins (TODO: add build system ;-) )
Diffstat (limited to 'src/tile.c')
-rw-r--r--src/tile.c1277
1 files changed, 1277 insertions, 0 deletions
diff --git a/src/tile.c b/src/tile.c
new file mode 100644
index 0000000..a64a621
--- /dev/null
+++ b/src/tile.c
@@ -0,0 +1,1277 @@
+/**
+ *
+ * Beryl tile plugin
+ *
+ * tile.c
+ *
+ * Copyright (c) 2006 Atie H. <atie.at.matrix@gmail.com>
+ * Copyright (c) 2006 Michal Fojtik <pichalsi(at)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 2
+ * 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.
+ *
+ * TODO
+ * - change vertical and horizontal tiling to similar behavior as Left
+ * - fix bugs
+ * - make vertical and horizontal maximization be saved when tiling
+ *
+ **/
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include <time.h>
+
+#include <X11/Xatom.h>
+#include <X11/extensions/Xrender.h>
+
+#include <compiz.h>
+#include "tile_options.h"
+
+#define GET_TILE_DISPLAY(d) ((TileDisplay *) (d)->privates[displayPrivateIndex].ptr)
+#define TILE_DISPLAY(d) TileDisplay *td = GET_TILE_DISPLAY (d)
+
+#define GET_TILE_SCREEN(s, td) ((TileScreen *) (s)->privates[(td)->screenPrivateIndex].ptr)
+#define TILE_SCREEN(s) TileScreen *ts = GET_TILE_SCREEN (s, GET_TILE_DISPLAY (s->display))
+
+#define GET_TILE_WINDOW(w, ts) ((TileWindow *) (w)->privates[(ts)->windowPrivateIndex].ptr)
+#define TILE_WINDOW(w) TileWindow *tw = GET_TILE_WINDOW (w, GET_TILE_SCREEN (w->screen, GET_TILE_DISPLAY (w->screen->display)))
+
+#define THIS_VIEWPORT(s) (ts->viewports[(((s)->hsize * (s)->y) + (s)->x)])
+
+// Minimal width and height under which the window cant be resized
+#define MINIMUM_WIDTH 25
+#define MINIMUM_HEIGHT 10
+
+static int displayPrivateIndex = 0;
+
+typedef struct _TileDisplay {
+ int screenPrivateIndex;
+} TileDisplay;
+
+typedef struct _TileViewport {
+ TileTileToggleTypeEnum currentTileType; // which kind of tiling is applied to windows on viewport
+ int tiledCount; // number of windows
+ CompWindow *firstTiled; // pointer to first tiled window
+} TileViewport;
+
+typedef struct _TileScreen {
+ TileViewport *viewports;
+ Bool isResizing; // whether there any windows on screen being resized
+ int oneDuration; // duration of animation for one window
+ int msResizing; // number of ms elapsed from start of resizing animation
+
+ PaintWindowProc paintWindow;
+ WindowResizeNotifyProc windowResizeNotify;
+ PreparePaintScreenProc preparePaintScreen;
+ DonePaintScreenProc donePaintScreen;
+ PaintScreenProc paintScreen;
+ SetScreenOptionProc setScreenOption;
+
+ int windowPrivateIndex;
+} TileScreen;
+
+typedef struct _TileWindow {
+ CompWindow *next; // next window for tiling
+ CompWindow *prev; // previous window for tiling
+
+ Bool resizedAlready; // whether the animation already resized the window
+
+ // coords used in animation, before last resize
+ int previousX;
+ int previousY;
+ int previousWidth;
+ int previousHeight;
+
+ // coords used when configuring window in middle of animation
+ int futureX;
+ int futureY;
+ int futureWidth;
+ int futureHeight;
+
+ Bool isResizing; // tells if the window is being resized
+
+ GLushort outlineColor[3];
+ int prevState;
+
+ // coords before whole tiling
+ int originalX;
+ int originalY;
+ int originalWidth;
+ int originalHeight;
+} TileWindow;
+
+static Bool placeWindow(CompWindow *w, int x, int y, int width, int height);
+static Bool isTileWindow(CompWindow *w);
+static Bool setWindowFutureSize(CompWindow *w);
+
+int current = 0;
+
+// window painting function, draws animation
+static Bool tilePaintWindow(CompWindow * w,
+ const WindowPaintAttrib * attrib,
+ const CompTransform * transform,
+ Region region, unsigned int mask)
+{
+ CompScreen *s = w->screen;
+ Bool status;
+ Bool dontDraw = FALSE;
+
+ TILE_WINDOW(w);
+ TILE_SCREEN(s);
+
+ if (tw->isResizing)
+ {
+ WindowPaintAttrib wAttrib = *attrib;
+ CompTransform wTransform = *transform;
+ float animationDuration = tileGetAnimationDuration(s->display);
+
+ switch (tileGetAnimateType(s->display))
+ {
+ /*
+ Drop animation
+ */
+ case AnimateTypeDrop:
+ matrixRotate(&wTransform,
+ 100.0f / animationDuration * ts->msResizing - 100,
+ 0.0f, 0.0f, 1.0f);
+ mask |= PAINT_WINDOW_TRANSFORMED_MASK;
+ break;
+
+ /*
+ Zoom animation
+ */
+ case AnimateTypeZoom:
+ matrixTranslate(&wTransform, 0, 0,
+ -1 + ts->msResizing / animationDuration);
+ mask |= PAINT_WINDOW_TRANSFORMED_MASK;
+ break;
+
+ /*
+ Slide animation
+ */
+ case AnimateTypeSlide:
+ if (ts->msResizing < 0.75 * animationDuration)
+ wAttrib.opacity = OPAQUE / 2;
+ else
+ wAttrib.opacity = OPAQUE / 2 + OPAQUE / 2 *
+ (ts->msResizing - 0.75 * animationDuration) /
+ (0.25 * animationDuration);
+
+ if (ts->msResizing > current * ts->oneDuration)
+ // windows that have animation finished already
+ {
+ mask |= PAINT_WINDOW_TRANSFORMED_MASK;
+ }
+ else if (ts->msResizing > (current-1)*ts->oneDuration && ts->msResizing < current*ts->oneDuration)
+ // animation in progress
+ {
+ int thisDur; // ms spent animating this window
+ for(thisDur = ts->msResizing;thisDur > ts->oneDuration;thisDur -= ts->oneDuration);
+
+ if (current % 2)
+ matrixTranslate(&wTransform, -s->width +
+ s->width * (float)thisDur / ts->oneDuration,
+ 0, 0);
+ else
+ matrixTranslate(&wTransform, s->width -
+ s->width * (float)thisDur / ts->oneDuration,
+ 0, 0);
+
+ mask |= PAINT_WINDOW_TRANSFORMED_MASK;
+ }
+ else
+ dontDraw = TRUE;
+ break;
+
+ /*
+ Outline animation
+ */
+ case AnimateTypeOutline:
+ dontDraw = TRUE;
+ break;
+
+ /*
+ Fade animation
+ */
+ case AnimateTypeFade:
+ // first half of the animation, fade out
+ if (ts->msResizing < 0.40f * animationDuration)
+ {
+ wAttrib.opacity = OPAQUE - OPAQUE *
+ ts->msResizing / (0.40f * animationDuration);
+ mask |= PAINT_WINDOW_TRANSFORMED_MASK;
+ }
+ else if (ts->msResizing > 0.40f * animationDuration && !tw->resizedAlready)
+ {
+ // resize window right after first half
+ setWindowFutureSize(w);
+ dontDraw = TRUE;
+ }
+ else if (ts->msResizing > 0.60f * animationDuration)
+ // second half of animation, fade in
+ {
+ wAttrib.opacity = OPAQUE *
+ (ts->msResizing - 0.60f * animationDuration) /
+ (0.40f * animationDuration);
+ mask |= PAINT_WINDOW_TRANSFORMED_MASK;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (dontDraw)
+ mask |= PAINT_WINDOW_NO_CORE_INSTANCE_MASK;
+
+ UNWRAP(ts, s, paintWindow);
+ status = (*s->paintWindow) (w, &wAttrib, &wTransform, region, mask);
+ WRAP(ts, s, paintWindow, tilePaintWindow);
+
+ current -= 1;
+ }
+ else // paint window as always
+ {
+ UNWRAP(ts, s, paintWindow);
+ status = (*s->paintWindow) (w, attrib, transform, region, mask);
+ WRAP(ts, s, paintWindow, tilePaintWindow);
+ }
+
+ return status;
+}
+
+static void tilePreparePaintScreen(CompScreen * s, int msSinceLastPaint)
+{
+ TILE_SCREEN(s);
+ current = THIS_VIEWPORT(s).tiledCount;
+
+ // add spent time
+ if (ts->isResizing)
+ ts->msResizing += msSinceLastPaint;
+
+ // Check if the animation hasnt finished yet
+ if (ts->isResizing && ts->msResizing > tileGetAnimationDuration(s->display))
+ {
+ CompWindow *w = THIS_VIEWPORT(s).firstTiled;
+ while (w)
+ {
+ TILE_WINDOW(w);
+ if (tw->isResizing)
+ tw->isResizing = FALSE;
+
+ w = tw->next;
+ }
+ ts->isResizing = FALSE;
+ ts->msResizing = 0;
+ }
+
+ UNWRAP(ts, s, preparePaintScreen);
+ (*s->preparePaintScreen) (s, msSinceLastPaint);
+ WRAP(ts, s, preparePaintScreen, tilePreparePaintScreen);
+}
+
+static void tileDonePaintScreen(CompScreen * s)
+{
+ TILE_SCREEN(s);
+
+ if (ts->isResizing)
+ damageScreen(s);
+
+ UNWRAP(ts, s, donePaintScreen);
+ (*s->donePaintScreen) (s);
+ WRAP(ts, s, donePaintScreen, tileDonePaintScreen);
+}
+
+static Bool tilePaintScreen(CompScreen * s,
+ const ScreenPaintAttrib * sa,
+ const CompTransform * transform,
+ Region region, int output, unsigned int mask)
+{
+ Bool status;
+ CompTransform sTransform = *transform;
+
+ TILE_SCREEN(s);
+
+ if (ts->isResizing)
+ mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK;
+
+ UNWRAP(ts, s, paintScreen);
+ status = (*s->paintScreen) (s, sa, transform, region, output, mask);
+ WRAP(ts, s, paintScreen, tilePaintScreen);
+
+ // Check if animation is enabled, there is resizing on screen and only outline should be drawn
+
+ if (ts->isResizing && (tileGetAnimateType(s->display) == AnimateTypeOutline))
+ {
+ transformToScreenSpace(s, output, -DEFAULT_Z_CAMERA, &sTransform);
+ glPushMatrix();
+ glLoadMatrixf(sTransform.m);
+
+ glLineWidth(4.0f);
+
+ CompWindow *w = THIS_VIEWPORT(s).firstTiled;
+ float animationDuration = tileGetAnimationDuration(s->display);
+
+ while (w && isTileWindow(w))
+ {
+ TILE_WINDOW(w);
+
+ if (tw->isResizing)
+ {
+ // Coordinate = start + speed * elapsedTime
+ // Coordinate = start + (target - start)/interval * elapsedTime
+ // Draw outline
+
+ int x = tw->previousX - w->input.left +
+ (((float)(w->attrib.x - tw->previousX)) * ts->msResizing / animationDuration);
+ int y = tw->previousY - w->input.top +
+ (((float)(w->attrib.y - tw->previousY)) * ts->msResizing / animationDuration);
+ int width = tw->previousWidth + w->input.left + w->input.right +
+ (((float)(w->attrib.width - tw->previousWidth)) * ts->msResizing / animationDuration);
+ int height = tw->previousHeight + w->input.top + w->input.bottom +
+ (((float)(w->attrib.height - tw->previousHeight)) * ts->msResizing / animationDuration);
+
+ glColor3us(tw->outlineColor[0] * 0.66, tw->outlineColor[1] * 0.66,
+ tw->outlineColor[2]*0.66);
+ glRecti(x, y + height, x + width, y);
+
+ glColor3usv(tw->outlineColor);
+
+ glBegin(GL_LINE_LOOP);
+ glVertex3f(x, y, 0.0f);
+ glVertex3f(x + width, y, 0.0f);
+ glVertex3f(x + width, y + height, 0.0f);
+ glVertex3f(x, y + height, 0.0f);
+ glEnd();
+
+ glColor4usv(defaultColor);
+ }
+
+ w = tw->next;
+ }
+
+ glPopMatrix();
+ glColor4usv(defaultColor);
+ glLineWidth(1.0f);
+ }
+
+ return status;
+}
+
+static Bool tileSetCoreScreenOption(CompScreen *s, char *name, CompOptionValue *value)
+{
+ TILE_SCREEN(s);
+ Bool status;
+
+ UNWRAP(ts, s, setScreenOption);
+ status = (*s->setScreenOption) (s, name, value);
+ WRAP(ts, s, setScreenOption, tileSetCoreScreenOption);
+
+ if (status && ((strcmp(name, "size") == 0) || (strcmp(name, "vsize") == 0)))
+ {
+ ts->viewports = realloc(ts->viewports, sizeof(TileViewport) *
+ (s->hsize * s->vsize));
+ }
+
+ return status;
+}
+
+// Resize notify used when windows are tiled horizontally or vertically
+static void tileResizeNotify(CompWindow * w, int dx, int dy, int dwidth, int dheight)
+{
+ TILE_SCREEN(w->screen);
+ TILE_WINDOW(w);
+
+ UNWRAP(ts, w->screen, windowResizeNotify);
+ (*w->screen->windowResizeNotify) (w, dx, dy, dwidth, dheight);
+ WRAP(ts, w->screen, windowResizeNotify, tileResizeNotify);
+
+ if (!tw->resizedAlready)
+ {
+ tw->resizedAlready = True; // window is resized now
+ return;
+ }
+
+ // Dont do anything if joining is disabled or windows are being resized
+ if (!tileGetTileJoin(w->screen->display) || ts->isResizing)
+ return;
+
+ switch (THIS_VIEWPORT(w->screen).currentTileType)
+ {
+ case TileToggleTypeTile:
+ if (tw->prev)
+ placeWindow(tw->prev,
+ tw->prev->attrib.x, tw->prev->attrib.y,
+ w->attrib.x - tw->prev->attrib.x - w->input.left - tw->prev->input.right,
+ tw->prev->height);
+
+ if (tw->next)
+ {
+ int currentX = w->attrib.x + w->width + w->input.right + tw->next->input.left;
+ placeWindow(tw->next, currentX, tw->next->attrib.y,
+ tw->next->width + tw->next->attrib.x - currentX, tw->next->height);
+ }
+ break;
+
+ case TileToggleTypeHorizontal:
+ if (tw->prev)
+ placeWindow(tw->prev,
+ tw->prev->attrib.x, tw->prev->attrib.y,
+ tw->prev->width,
+ w->attrib.y - tw->prev->attrib.y - w->input.top - tw->prev->input.bottom);
+
+ if (tw->next)
+ {
+ int currentY = w->attrib.y + w->height + w->input.bottom + tw->next->input.top;
+ placeWindow(tw->next, tw->next->attrib.x, currentY,
+ tw->next->width, tw->next->height + tw->next->attrib.y - currentY);
+ }
+ break;
+
+ case TileToggleTypeLeft:
+ if (!tw->next && tw->prev && dwidth) // last window - on the left
+ {
+ CompWindow *temp = THIS_VIEWPORT(w->screen).firstTiled;
+ while (temp)
+ {
+ TILE_WINDOW(temp);
+
+ if (!tw->next)
+ break;
+
+ XRectangle workArea;
+ getWorkareaForOutput(w->screen, w->screen->currentOutputDev, &workArea);
+
+ int currentX = workArea.x + w->serverX + w->serverWidth + w->input.right + temp->input.left;
+
+ placeWindow(temp, currentX, temp->attrib.y,
+ workArea.width - currentX - w->input.right, temp->attrib.height);
+
+ temp = tw->next;
+ }
+ }
+ else if (tw->next) // windows on the right
+ {
+ XRectangle workArea;
+ getWorkareaForOutput(w->screen, w->screen->currentOutputDev, &workArea);
+
+ CompWindow *temp = THIS_VIEWPORT(w->screen).firstTiled;
+ while (temp)
+ {
+ TILE_WINDOW(temp);
+ if (!tw->next) // left window, last window
+ {
+ placeWindow(temp,
+ workArea.x + temp->input.left, temp->attrib.y,
+ w->serverX - w->input.left - temp->input.left - temp->input.right - workArea.x,
+ temp->attrib.height);
+ break;
+ }
+
+ if (w->id != temp->id)
+ {
+ int x = temp->attrib.x;
+ int y = temp->attrib.y;
+ int width = temp->attrib.width;
+ int height = temp->attrib.height;
+
+ /* tileWindow of the resized window, tw is the tileWindow of temp */
+ TileWindow * otw = GET_TILE_WINDOW(w, ts);
+
+ if (otw->prev && (temp->id == otw->prev->id))
+ height = w->serverY - temp->attrib.y - w->input.top - temp->input.bottom;
+ else if (otw->next && (temp->id == otw->next->id))
+ y = w->serverY + w->serverHeight + w->input.bottom + temp->input.top;
+
+ x = w->serverX;
+ width = workArea.width + workArea.x - w->serverX - w->input.right;
+
+ placeWindow(temp, x, y, width, height);
+ }
+ temp = tw->next;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static Bool tileInitScreen(CompPlugin * p, CompScreen * s)
+{
+ TILE_DISPLAY(s->display);
+
+ TileScreen *ts = (TileScreen *) calloc(1, sizeof(TileScreen));
+
+ ts->windowPrivateIndex = allocateWindowPrivateIndex(s);
+ if (ts->windowPrivateIndex < 0)
+ {
+ free(ts);
+ return FALSE;
+ }
+ srand(time(0));
+
+ s->privates[td->screenPrivateIndex].ptr = ts;
+
+ ts->isResizing = FALSE;
+ ts->msResizing = 0;
+ ts->oneDuration = 0;
+
+ ts->viewports = calloc(s->hsize * s->vsize, sizeof(TileViewport));
+ int i;
+ for(i = 0;i < (s->hsize * s->vsize); i++)
+ ts->viewports[i].currentTileType = TileToggleTypeNum;
+
+ // Wrap plugin functions
+ WRAP(ts, s, paintScreen, tilePaintScreen);
+ WRAP(ts, s, preparePaintScreen, tilePreparePaintScreen);
+ WRAP(ts, s, donePaintScreen, tileDonePaintScreen);
+ WRAP(ts, s, windowResizeNotify, tileResizeNotify);
+ WRAP(ts, s, paintWindow, tilePaintWindow);
+ WRAP(ts, s, setScreenOption, tileSetCoreScreenOption);
+
+ return TRUE;
+}
+
+static void tileFiniScreen(CompPlugin * p, CompScreen * s)
+{
+ TILE_SCREEN(s);
+
+ freeWindowPrivateIndex(s, ts->windowPrivateIndex);
+ free(ts->viewports);
+
+ //Restore the original function
+ UNWRAP(ts, s, paintScreen);
+ UNWRAP(ts, s, preparePaintScreen);
+ UNWRAP(ts, s, donePaintScreen);
+ UNWRAP(ts, s, windowResizeNotify);
+ UNWRAP(ts, s, paintWindow);
+ UNWRAP(ts, s, setScreenOption);
+
+ //Free the pointer
+ free(ts);
+}
+
+// this is resizeConstrainMinMax from resize.c, thanks to David Reveman/Nigel Cunningham
+static void constrainMinMax(CompWindow * w, int width, int height, int *newWidth, int *newHeight)
+{
+ const XSizeHints *hints = &w->sizeHints;
+ int min_width = 0;
+ int min_height = 0;
+ int max_width = MAXSHORT;
+ int max_height = MAXSHORT;
+
+ if ((hints->flags & PBaseSize) && (hints->flags & PMinSize))
+ {
+ min_width = hints->min_width;
+ min_height = hints->min_height;
+ }
+ else if (hints->flags & PBaseSize)
+ {
+ min_width = hints->base_width;
+ min_height = hints->base_height;
+ }
+ else if (hints->flags & PMinSize)
+ {
+ min_width = hints->min_width;
+ min_height = hints->min_height;
+ }
+
+ if (hints->flags & PMaxSize)
+ {
+ max_width = hints->max_width;
+ max_height = hints->max_height;
+ }
+#define CLAMP(v, min, max) ((v) <= (min) ? (min) : (v) >= (max) ? (max) : (v))
+
+ /* clamp width and height to min and max values */
+ width = CLAMP(width, min_width, max_width);
+ height = CLAMP(height, min_height, max_height);
+
+#undef CLAMP
+
+ *newWidth = width;
+ *newHeight = height;
+}
+
+// Moves window to [x,y] and resizes to width x height if no animation or starts animation
+static Bool placeWindow(CompWindow *w, int x, int y, int width, int height)
+{
+ // window existence check
+ if (!w)
+ return FALSE;
+
+ // this checks if the window isnt smaller than minimum size it has defined
+ constrainMinMax(w, width, height, &width, &height);
+
+ // check if the window isnt already where its going to be
+ if (x == w->attrib.x && y == w->attrib.y && width == w->attrib.width && height == w->attrib.height)
+ return TRUE;
+
+ TILE_WINDOW(w);
+ TILE_SCREEN(w->screen);
+
+ // set previous coordinates for animation
+ tw->previousX = w->attrib.x;
+ tw->previousY = w->attrib.y;
+ tw->previousWidth = w->attrib.width;
+ tw->previousHeight = w->attrib.height;
+
+ // set future coordinates for animation
+ tw->futureX = x;
+ tw->futureY = y;
+ tw->futureWidth = width;
+ tw->futureHeight = height;
+
+ tw->resizedAlready = False; // window is not resized now
+
+ switch (tileGetAnimateType(w->screen->display))
+ {
+ case AnimateTypeNone:
+ setWindowFutureSize(w);
+ break;
+ case AnimateTypeOutline:
+ case AnimateTypeSlide:
+ case AnimateTypeZoom:
+ case AnimateTypeDrop:
+ setWindowFutureSize(w);
+ /* fall-through */
+ case AnimateTypeFade:
+ ts->isResizing = TRUE;
+ tw->isResizing = TRUE;
+ ts->msResizing = 0;
+ ts->oneDuration = tileGetAnimationDuration(w->screen->display) /
+ THIS_VIEWPORT(w->screen).tiledCount;
+ break;
+ default:
+ break;
+ }
+
+ return TRUE;
+}
+
+static Bool setWindowFutureSize(CompWindow *w)
+{
+ TILE_WINDOW(w);
+ TILE_SCREEN(w->screen);
+ XWindowChanges xwc;
+
+ xwc.x = tw->futureX;
+ xwc.y = tw->futureY;
+ xwc.width = tw->futureWidth;
+ xwc.height = tw->futureHeight;
+
+ if (THIS_VIEWPORT(w->screen).currentTileType == TileToggleTypeNum)
+ maximizeWindow(w, tw->prevState);
+ else
+ maximizeWindow(w, 0);
+
+ // FIXME
+ // restack window where it should be, under next window because maximization/restoration breaks it somehow
+ if (tw->prevState & MAXIMIZE_STATE && tw->next)
+ restackWindowBelow(w, tw->next);
+
+ /* TODO */
+#if 0
+ // if after restoring the window is maximized, then restore "restore data" :P
+ if ((w->state & MAXIMIZE_STATE) && THIS_VIEWPORT(w->screen).currentTileType == none)
+ saveWindowGeometry(w, &xwc, CWX | CWY | CWWidth | CWHeight);
+ else
+ configureXWindow (w, CWX | CWY | CWWidth | CWHeight, &xwc);
+#endif
+ return TRUE;
+}
+
+// Heavily inspired by windowIs3D and isSDWindow, returns TRUE if the window is usable for tiling
+static Bool isTileWindow(CompWindow * w)
+{
+ if (matchEval(tileGetExcludeMatch(w->screen->display), w))
+ return FALSE;
+
+ if (w->attrib.override_redirect)
+ return FALSE;
+
+ if (w->wmType & (CompWindowTypeDockMask | CompWindowTypeDesktopMask))
+ return FALSE;
+
+ if (w->state & CompWindowStateSkipPagerMask)
+ return FALSE;
+
+ // Normal window
+ if ((w->type & CompWindowTypeNormalMask)==CompWindowTypeNormalMask && !w->minimized && w->placed)
+ return TRUE;
+
+ return FALSE;
+}
+
+// Finds windows on desktop that are to be tiled
+static Bool loadTiledWindows(CompScreen *s)
+{
+ TILE_SCREEN(s);
+
+ int count = 0;
+ CompWindow *first = 0, *previous = 0;
+
+ CompWindow *w;
+ if (THIS_VIEWPORT(s).currentTileType != TileToggleTypeNum)
+ for (w = s->windows; w; w = w->next)
+ {
+ int x,y;
+ defaultViewportForWindow(w, &x, &y);
+ if (isTileWindow(w) && (x == s->x) && (y == s->y))
+ {
+ if (previous)
+ {
+ TILE_WINDOW(previous);
+ tw->next = w;
+ }
+
+ TILE_WINDOW(w);
+ if (!first)
+ first = w;
+
+ tw->prev = previous;
+ previous = w;
+ tw->next = 0;
+ count++;
+ }
+ }
+ else // when reloading windows for restoration, in case window order was changed since last tiling
+ for (w = s->windows; w; w = w->next)
+ {
+ TILE_WINDOW(w);
+ int x,y;
+ defaultViewportForWindow(w,&x,&y);
+ if ((tw->originalWidth || tw->originalHeight) && (x == s->x) && (y == s->y))
+ {
+ if (previous)
+ {
+ TILE_WINDOW(previous);
+ tw->next = w;
+ }
+
+ if (!first)
+ first = w;
+
+ tw->prev = previous;
+ previous = w;
+ tw->next = 0;
+ count++;
+ }
+ }
+
+ THIS_VIEWPORT(s).firstTiled = first;
+ THIS_VIEWPORT(s).tiledCount = count;
+
+ return TRUE;
+}
+
+// save window coordinates to use for restore
+static Bool saveCoords(CompWindow *w)
+{
+ TILE_WINDOW(w);
+
+ // if the window was maximized save "restore data" for later restoring
+ if (w->state & MAXIMIZE_STATE)
+ {
+ if (w->saveMask & CWX)
+ tw->originalX = w->saveWc.x;
+ else
+ tw->originalX = w->serverX;
+
+ if (w->saveMask & CWY)
+ tw->originalY = w->saveWc.y;
+ else
+ tw->originalY = w->serverY;
+
+ if (w->saveMask & CWWidth)
+ tw->originalWidth = w->saveWc.width;
+ else
+ tw->originalWidth = w->serverWidth;
+
+ if (w->saveMask & CWHeight)
+ tw->originalHeight = w->saveWc.height;
+ else
+ tw->originalHeight = w->serverHeight;
+ }
+ else
+ {
+ tw->originalX = w->serverX;
+ tw->originalY = w->serverY;
+ tw->originalWidth = w->serverWidth;
+ tw->originalHeight = w->serverHeight;
+ }
+
+ // save state
+ tw->prevState = w->state;
+
+ return TRUE;
+}
+
+// Applies tiling/restoring
+static Bool applyTiling(CompScreen *s)
+{
+ TILE_SCREEN(s);
+
+ if (ts->isResizing)
+ return FALSE;
+
+ loadTiledWindows(s);
+ if (THIS_VIEWPORT(s).tiledCount > 1)
+ {
+ int i = 0;
+ XRectangle workArea;
+ CompWindow *w;
+ CompWindowExtents border;
+
+ memset(&border, 0, sizeof(CompWindowExtents));
+ /* first get the largest border of the windows on this
+ screen - some of the windows in our list might be
+ maximized now and not be maximized later, so
+ their border information may be inaccurate */
+ for (w = s->windows; w; w = w->next)
+ {
+ if (w->input.left > border.left)
+ border.left = w->input.left;
+ if (w->input.right > border.right)
+ border.right = w->input.right;
+ if (w->input.top > border.top)
+ border.top = w->input.top;
+ if (w->input.bottom > border.bottom)
+ border.bottom = w->input.bottom;
+ }
+
+ w = THIS_VIEWPORT(s).firstTiled;
+
+ /* FIXME */
+ getWorkareaForOutput(s, s->currentOutputDev, &workArea);
+
+ switch(THIS_VIEWPORT(s).currentTileType)
+ {
+ /*
+ Tile into grid
+ */
+ case TileToggleTypeTile :
+ {
+ int countX = ceil(sqrt(THIS_VIEWPORT(s).tiledCount));
+ int countY = ceil((float)THIS_VIEWPORT(s).tiledCount / countX);
+
+ int currentX = workArea.x;
+ int currentY = workArea.y;
+
+ int winWidth = workArea.width / countX;
+ int winHeight = workArea.height / countY;
+
+ while (w)
+ {
+ TILE_WINDOW(w);
+ if (!tw->originalWidth || !tw->originalHeight)
+ saveCoords(w);
+
+ placeWindow(w, currentX + border.left,
+ currentY + border.top,
+ winWidth - (border.left + border.right),
+ winHeight - (border.top + border.bottom));
+ i++;
+
+ if (!(i % countX))
+ {
+ currentX = workArea.x;
+ currentY += winHeight;
+ }
+ else
+ currentX += winWidth;
+
+ w = tw->next;
+ }
+ }
+ break;
+
+ /*
+ Tile left
+ */
+ case TileToggleTypeLeft:
+ {
+ int x = 0, y = 0, winWidth = 0, winHeight = 0;
+ int height = workArea.height / (THIS_VIEWPORT(s).tiledCount - 1);
+ int occupancy = tileGetTileLeftOccupancy(s->display);
+
+ while (w)
+ {
+ TILE_WINDOW(w);
+ if (!tw->next) // this is the last window in the list - the active/topmost window
+ {
+ x = workArea.x;
+ y = workArea.y;
+ winWidth = workArea.width * occupancy / 100;
+ winHeight = workArea.height;
+ }
+ else
+ {
+ x = workArea.x + (workArea.width * occupancy / 100);
+ y = workArea.y + (i * height);
+ winWidth = (workArea.width * (100 - occupancy) / 100);
+ winHeight = height;
+ }
+ if (!tw->originalWidth || !tw->originalHeight)
+ saveCoords(w);
+
+ placeWindow(w, x + border.left, y + border.top,
+ winWidth - (border.left + border.right),
+ winHeight - (border.top + border.bottom));
+ i++;
+ w = tw->next;
+ }
+ }
+ break;
+
+ /*
+ Tile vertically
+ */
+ case TileToggleTypeVertical:
+ {
+ int winWidth = workArea.width / THIS_VIEWPORT(s).tiledCount;
+ int winHeight = workArea.height;
+ int y = workArea.y;
+
+ while (w)
+ {
+ TILE_WINDOW(w);
+
+ int x = workArea.x + (winWidth * i);
+ if (!tw->originalWidth || !tw->originalHeight)
+ saveCoords(w);
+
+ placeWindow(w, x + border.left, y + border.top,
+ winWidth - (border.left + border.right),
+ winHeight - (border.top + border.bottom));
+ i++;
+ w = tw->next;
+ }
+ }
+ break;
+
+ /*
+ Tile horizontally
+ */
+ case TileToggleTypeHorizontal:
+ {
+ int winWidth = workArea.width;
+ int winHeight = workArea.height / THIS_VIEWPORT(s).tiledCount;
+ int x = workArea.x;
+
+ while (w)
+ {
+ TILE_WINDOW(w);
+
+ int y = workArea.y + (winHeight * i);
+ if (!tw->originalWidth || !tw->originalHeight)
+ saveCoords(w);
+
+ placeWindow(w, x + border.left, y + border.top,
+ winWidth - (border.left + border.right),
+ winHeight - (border.top + border.bottom));
+ i++;
+ w = tw->next;
+ }
+ }
+ break;
+
+ /*
+ Cascade
+ */
+ case TileToggleTypeCascade:
+ {
+ int delta = tileGetTileDelta(s->display);
+ int currentX = workArea.x;
+ int currentY = workArea.y;
+ int winHeight = workArea.height - delta * (THIS_VIEWPORT(s).tiledCount - 1);
+ int winWidth = workArea.width - delta * (THIS_VIEWPORT(s).tiledCount - 1);
+
+ while (w)
+ {
+ TILE_WINDOW(w);
+ if (!tw->originalWidth || !tw->originalHeight)
+ saveCoords(w);
+
+ placeWindow(w, currentX + border.left,
+ currentY + border.top,
+ winWidth - (border.left + border.right),
+ winHeight - (border.top + border.bottom));
+
+ currentX += delta;
+ currentY += delta;
+ w = tw->next;
+ }
+ }
+ break;
+
+ /*
+ Restore
+ */
+ case TileToggleTypeNum:
+ {
+ while (w)
+ {
+ TILE_WINDOW(w);
+
+ placeWindow(w, tw->originalX, tw->originalY, tw->originalWidth, tw->originalHeight);
+
+ tw->originalX = 0;
+ tw->originalY = 0;
+ tw->originalWidth = 0;
+ tw->originalHeight = 0;
+
+ w = tw->next;
+ }
+ }
+ break;
+ }
+ }
+
+ return TRUE;
+}
+
+static Bool tileTile(CompDisplay * d, CompAction * ac, CompActionState state, CompOption * option, int nOption)
+{
+ CompScreen *s;
+ s = findScreenAtDisplay(d, getIntOptionNamed(option, nOption, "root", 0));
+
+ if (s) {
+ TILE_SCREEN(s);
+ THIS_VIEWPORT(s).currentTileType = TileToggleTypeTile;
+ applyTiling(s);
+ }
+ return FALSE;
+}
+
+static Bool tileVertically(CompDisplay * d, CompAction * ac, CompActionState state,CompOption * option, int nOption)
+{
+ CompScreen *s;
+ s = findScreenAtDisplay(d, getIntOptionNamed(option, nOption, "root", 0));
+
+ if (s) {
+ TILE_SCREEN(s);
+ THIS_VIEWPORT(s).currentTileType = TileToggleTypeVertical;
+ applyTiling(s);
+ }
+ return FALSE;
+}
+
+static Bool tileHorizontally(CompDisplay * d, CompAction * ac, CompActionState state, CompOption * option, int nOption)
+{
+ CompScreen *s;
+ s = findScreenAtDisplay(d, getIntOptionNamed(option, nOption, "root", 0));
+
+ if (s) {
+ TILE_SCREEN(s);
+ THIS_VIEWPORT(s).currentTileType = TileToggleTypeHorizontal;
+ applyTiling(s);
+ }
+ return FALSE;
+}
+
+static Bool tileCascade(CompDisplay * d, CompAction * ac, CompActionState state, CompOption * option, int nOption)
+{
+ CompScreen *s;
+ s = findScreenAtDisplay(d, getIntOptionNamed(option, nOption, "root", 0));
+
+ if (s) {
+ TILE_SCREEN(s);
+ THIS_VIEWPORT(s).currentTileType = TileToggleTypeCascade;
+ applyTiling(s);
+ }
+ return FALSE;
+}
+
+static Bool tileRestore(CompDisplay * d, CompAction * ac, CompActionState state, CompOption * option, int nOption)
+{
+ CompScreen *s;
+ s = findScreenAtDisplay(d, getIntOptionNamed(option, nOption, "root", 0));
+
+ if (s) {
+ TILE_SCREEN(s);
+ if(THIS_VIEWPORT(s).currentTileType != TileToggleTypeNum)
+ {
+ THIS_VIEWPORT(s).currentTileType = TileToggleTypeNum;
+ applyTiling(s);
+ }
+ }
+ return FALSE;
+}
+
+static Bool tileToggle(CompDisplay * d, CompAction * ac, CompActionState state, CompOption * option, int nOption)
+{
+ CompScreen *s;
+ s = findScreenAtDisplay(d, getIntOptionNamed(option, nOption, "root", 0));
+
+ if (s) {
+ TILE_SCREEN(s);
+ if(THIS_VIEWPORT(s).currentTileType != TileToggleTypeNum)
+ {
+ THIS_VIEWPORT(s).currentTileType = TileToggleTypeNum;
+ applyTiling(s);
+ }
+ else
+ {
+ THIS_VIEWPORT(s).currentTileType = tileGetTileToggleType(d);
+ applyTiling(s);
+ }
+ }
+
+ return FALSE;
+}
+
+static Bool tileInitDisplay(CompPlugin * p, CompDisplay * d)
+{
+ //Generate a tile display
+ TileDisplay *td = (TileDisplay *) malloc(sizeof(TileDisplay));
+ //Allocate a private index
+ td->screenPrivateIndex = allocateScreenPrivateIndex(d);
+
+ //Check if its valid
+ if (td->screenPrivateIndex < 0) {
+ free(td);
+ return FALSE;
+ }
+
+ tileSetTileVerticallyInitiate(d, tileVertically);
+ tileSetTileHorizontallyInitiate(d, tileHorizontally);
+ tileSetTileTileInitiate(d, tileTile);
+ tileSetTileCascadeInitiate(d, tileCascade);
+ tileSetTileRestoreInitiate(d, tileRestore);
+ tileSetTileToggleInitiate(d, tileToggle);
+
+ //Record the display
+ d->privates[displayPrivateIndex].ptr = td;
+
+ return TRUE;
+}
+
+static void tileFiniDisplay(CompPlugin * p, CompDisplay * d)
+{
+ TILE_DISPLAY(d);
+
+ //Free the private index
+ freeScreenPrivateIndex(d, td->screenPrivateIndex);
+ //Free the pointer
+ free(td);
+}
+
+static Bool tileInitWindow(CompPlugin * p, CompWindow * w)
+{
+ TileWindow *tw;
+
+ TILE_SCREEN(w->screen);
+
+ tw = malloc(sizeof(TileWindow));
+ if (!tw)
+ return FALSE;
+
+ tw->next = 0;
+ tw->prev = 0;
+
+ tw->originalX = 0;
+ tw->originalY = 0;
+ tw->originalWidth = 0;
+ tw->originalHeight = 0;
+
+ tw->previousX = 0;
+ tw->previousY = 0;
+ tw->previousWidth = 0;
+ tw->previousHeight = 0;
+ tw->isResizing = FALSE;
+ tw->prevState = 0;
+
+ // random color, from group.c thanks to the author for doing it
+ tw->outlineColor[0] = rand() % 0xFFFF;
+ tw->outlineColor[1] = rand() % 0xFFFF;
+ tw->outlineColor[2] = rand() % 0xFFFF;
+
+ w->privates[ts->windowPrivateIndex].ptr = tw;
+
+ return TRUE;
+}
+
+static void tileFiniWindow(CompPlugin * p, CompWindow * w)
+{
+ TILE_WINDOW(w);
+ TILE_SCREEN(w->screen);
+
+ if (tw->originalWidth && tw->originalHeight)
+ {
+ // when one window is destroyed, join the linked list
+ CompWindow *prev = tw->prev;
+ CompWindow *next = tw->next;
+
+ if (prev)
+ {
+ TileWindow *twprev = GET_TILE_WINDOW(prev, GET_TILE_SCREEN (prev->screen, GET_TILE_DISPLAY (prev->screen->display)));
+ twprev->next = next;
+ }
+ else
+ {
+ THIS_VIEWPORT(w->screen).firstTiled = next;
+ }
+ }
+
+ free(tw);
+}
+
+static Bool tileInit(CompPlugin * p)
+{
+ displayPrivateIndex = allocateDisplayPrivateIndex();
+
+ if (displayPrivateIndex < 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void tileFini(CompPlugin * p)
+{
+ if (displayPrivateIndex >= 0)
+ freeDisplayPrivateIndex(displayPrivateIndex);
+}
+
+static int tileGetVersion(CompPlugin *p, int version)
+{
+ return ABIVERSION;
+}
+
+CompPluginVTable tileVTable = {
+ "tile",
+ N_("Tile"),
+ N_("Tile windows"),
+ tileGetVersion,
+ tileInit,
+ tileFini,
+ tileInitDisplay,
+ tileFiniDisplay,
+ tileInitScreen,
+ tileFiniScreen,
+ tileInitWindow,
+ tileFiniWindow,
+ 0,
+ 0,
+ 0, /*tileGetScreenOptions */
+ 0, /*tileSetScreenOption */
+ 0,
+ 0,
+ 0,
+ 0
+};
+
+CompPluginVTable *getCompPluginInfo(void)
+{
+ return &tileVTable;
+}