summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDennis Kasprzyk <onestone@beryl-project.org>2007-05-17 13:05:52 +0200
committerDennis Kasprzyk <onestone@beryl-project.org>2007-05-17 13:05:52 +0200
commitfe4696a82d2d02f6e4b6ff82fa882953e58ff017 (patch)
treefa071c8b3950ea72cde39bcb610bd9f49a072f3b
parent3b5120c5e676960cc0569bfc4e3eeaff49f419f1 (diff)
downloadtile-fe4696a82d2d02f6e4b6ff82fa882953e58ff017.tar.gz
tile-fe4696a82d2d02f6e4b6ff82fa882953e58ff017.tar.bz2
initial commit
-rw-r--r--Makefile218
-rw-r--r--dummy0
-rw-r--r--tile.c1103
-rw-r--r--tile.xml149
4 files changed, 1470 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..80635d8
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,218 @@
+##
+#
+# Compiz plugin Makefile
+#
+# Copyright : (C) 2006 by Dennis Kasprzyk
+# E-mail : onestone@deltatauchi.de
+#
+#
+# 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.
+#
+##
+
+## configuration
+
+#enter plugin name here
+PLUGIN = tile
+
+#enter dependencies here
+PKG_DEP =
+
+## end of configuration
+
+#enter beryl or compiz here
+TARGET = compiz
+
+ifeq ($(BUILD_GLOBAL),true)
+ DESTDIR = $(shell pkg-config --variable=libdir compiz)/compiz
+ XMLDIR = $(shell pkg-config --variable=prefix compiz)/share/compiz
+else
+ DESTDIR = $(HOME)/.$(TARGET)/plugins
+ XMLDIR = $(HOME)/.$(TARGET)/metadata
+endif
+
+BUILDDIR = build
+
+CC = gcc
+LIBTOOL = libtool
+INSTALL = install
+
+BCOP = `pkg-config --variable=bin bcop`
+
+CFLAGS = -g -Wall `pkg-config --cflags $(PKG_DEP) $(TARGET) `
+LDFLAGS = `pkg-config --libs $(PKG_DEP) $(TARGET) `
+
+is-bcop-target := $(shell if [ -e $(PLUGIN).xml ]; then cat $(PLUGIN).xml | grep "useBcop=\"true\"";fi )
+
+bcop-target := $(shell if [ -n "$(is-bcop-target)" ]; then echo $(PLUGIN).xml; fi )
+bcop-target-src := $(shell if [ -n "$(is-bcop-target)" ]; then echo $(PLUGIN)_options.c; fi )
+bcop-target-hdr := $(shell if [ -n "$(is-bcop-target)" ]; then echo $(PLUGIN)_options.h; fi )
+
+gen-schemas := $(shell if [ -e $(PLUGIN).xml ] && [ -n `pkg-config --variable=xsltdir compiz-gconf` ]; then echo true; fi )
+schema-target := $(shell if [ -n "$(gen-schemas)" ]; then echo $(PLUGIN).xml; fi )
+schema-output := $(shell if [ -n "$(gen-schemas)" ]; then echo compiz-$(PLUGIN).schema; fi )
+
+# find all the object files (including those from .moc.cpp files)
+
+c-objs := $(patsubst %.c,%.lo,$(shell find -name '*.c' 2> /dev/null | grep -v "$(BUILDDIR)/" | sed -e 's/^.\///'))
+c-objs := $(filter-out $(bcop-target-src:.c=.lo),$(c-objs))
+c-objs += $(bcop-target-src:.c=.lo)
+
+# system include path parameter, -isystem doesn't work on old gcc's
+inc-path-param = $(shell if [ -z "`gcc --version | head -n 1 | grep ' 3'`" ]; then echo "-isystem"; else echo "-I"; fi)
+
+# default color settings
+color := $(shell if [ $$TERM = "dumb" ]; then echo "no"; else echo "yes"; fi)
+
+#
+# Do it.
+#
+
+.PHONY: $(BUILDDIR) build-dir bcop-build schema-creation c-build-objs c-link-plugin
+
+all: $(BUILDDIR) build-dir bcop-build schema-creation c-build-objs c-link-plugin
+
+bcop-build: $(bcop-target-hdr) $(bcop-target-src)
+
+schema-creation: $(schema-output)
+
+c-build-objs: $(addprefix $(BUILDDIR)/,$(cxx-objs))
+
+c-link-plugin: $(BUILDDIR)/lib$(PLUGIN).la
+
+#
+# Create build directory
+#
+
+$(BUILDDIR) :
+ @mkdir -p $(BUILDDIR)
+
+$(DESTDIR) :
+ @mkdir -p $(DESTDIR)
+
+#
+# BCOP'ing
+
+%_options.h: %.xml
+ @if [ '$(color)' != 'no' ]; then \
+ echo -e -n "\033[0;1;5mbcop'ing \033[0;1;37m: \033[0;32m$< \033[0;1;37m-> \033[0;31m$@\033[0m"; \
+ else \
+ echo "bcop'ing $< -> $@"; \
+ fi
+ @$(BCOP) --header=$@ $<
+ @if [ '$(color)' != 'no' ]; then \
+ echo -e "\r\033[0mbcop'ing : \033[34m$< -> $@\033[0m"; \
+ fi
+
+%_options.c: %.xml
+ @if [ '$(color)' != 'no' ]; then \
+ echo -e -n "\033[0;1;5mbcop'ing \033[0;1;37m: \033[0;32m$< \033[0;1;37m-> \033[0;31m$@\033[0m"; \
+ else \
+ echo "bcop'ing $< -> $@"; \
+ fi
+ @$(BCOP) --source=$@ $<
+ @if [ '$(color)' != 'no' ]; then \
+ echo -e "\r\033[0mbcop'ing : \033[34m$< -> $@\033[0m"; \
+ fi
+
+#
+# Schema generation
+
+compiz-%.schema: %.xml
+ @if [ '$(color)' != 'no' ]; then \
+ echo -e -n "\033[0;1;5mschema \033[0;1;37m: \033[0;32m$< \033[0;1;37m-> \033[0;31m$@\033[0m"; \
+ else \
+ echo "schema'ing $< -> $@"; \
+ fi
+ @xsltproc `pkg-config --variable=xsltdir compiz-gconf`/schemas.xslt $< > $@
+ @if [ '$(color)' != 'no' ]; then \
+ echo -e "\r\033[0mschema : \033[34m$< -> $@\033[0m"; \
+ fi
+
+
+
+#
+# Compiling
+#
+
+$(BUILDDIR)/%.lo: %.c
+ @if [ '$(color)' != 'no' ]; then \
+ echo -n -e "\033[0;1;5mcompiling \033[0;1;37m: \033[0;32m$< \033[0;1;37m-> \033[0;31m$@\033[0m"; \
+ else \
+ echo "compiling $< -> $@"; \
+ fi
+ @$(LIBTOOL) --quiet --mode=compile $(CC) $(CFLAGS) -c -o $@ $<
+ @if [ '$(color)' != 'no' ]; then \
+ echo -e "\r\033[0mcompiling : \033[34m$< -> $@\033[0m"; \
+ fi
+
+#
+# Linking
+#
+
+cxx-rpath-prefix := -Wl,-rpath,
+
+$(BUILDDIR)/lib$(PLUGIN).la: $(addprefix $(BUILDDIR)/,$(c-objs))
+ @if [ '$(color)' != 'no' ]; then \
+ echo -e -n "\033[0;1;5mlinking -> \033[0;31m$@\033[0m"; \
+ else \
+ echo "linking -> $@"; \
+ fi
+ @$(LIBTOOL) --quiet --mode=link $(CC) $(LDFLAGS) -rpath $(DESTDIR) -o $@ $(addprefix $(BUILDDIR)/,$(c-objs))
+ @if [ '$(color)' != 'no' ]; then \
+ echo -e "\r\033[0mlinking -> \033[34m$@\033[0m"; \
+ fi
+
+
+clean:
+ rm -rf $(BUILDDIR)
+ rm -f $(bcop-target-src)
+ rm -f $(bcop-target-hdr)
+ rm -f $(schema-output)
+
+install: $(DESTDIR) all
+ @if [ '$(color)' != 'no' ]; then \
+ echo -n -e "\033[0;1;5minstall \033[0;1;37m: \033[0;31m$(DESTDIR)/lib$(PLUGIN).so\033[0m"; \
+ else \
+ echo "install : $(DESTDIR)/lib$(PLUGIN).so"; \
+ fi
+ @mkdir -p $(DESTDIR)
+ @$(INSTALL) $(BUILDDIR)/.libs/lib$(PLUGIN).so $(DESTDIR)/lib$(PLUGIN).so
+ @if [ '$(color)' != 'no' ]; then \
+ echo -e "\r\033[0minstall : \033[34m$(DESTDIR)/lib$(PLUGIN).so\033[0m"; \
+ fi
+ @if [ -e $(PLUGIN).xml ]; then \
+ if [ '$(color)' != 'no' ]; then \
+ echo -n -e "\033[0;1;5minstall \033[0;1;37m: \033[0;31m$(XMLDIR)/$(PLUGIN).xml\033[0m"; \
+ else \
+ echo "install : $(XMLDIR)/$(PLUGIN).xml"; \
+ fi; \
+ mkdir -p $(XMLDIR); \
+ cp $(PLUGIN).xml $(XMLDIR)/$(PLUGIN).xml; \
+ if [ '$(color)' != 'no' ]; then \
+ echo -e "\r\033[0minstall : \033[34m$(XMLDIR)/$(PLUGIN).xml\033[0m"; \
+ fi; \
+ fi
+ @if [ -e $(schema-output) ]; then \
+ if [ '$(color)' != 'no' ]; then \
+ echo -n -e "\033[0;1;5minstall \033[0;1;37m: \033[0;31m$(schema-output)\033[0m"; \
+ else \
+ echo "install : $(schema-output)"; \
+ fi; \
+ gconftool-2 --install-schema-file=$(schema-output) > /dev/null; \
+ if [ '$(color)' != 'no' ]; then \
+ echo -e "\r\033[0minstall : \033[34m$(schema-output)\033[0m"; \
+ fi; \
+ fi
+
+
+
+
diff --git a/dummy b/dummy
deleted file mode 100644
index e69de29..0000000
--- a/dummy
+++ /dev/null
diff --git a/tile.c b/tile.c
new file mode 100644
index 0000000..77d49cd
--- /dev/null
+++ b/tile.c
@@ -0,0 +1,1103 @@
+/**
+ *
+ * Beryl tile plugin
+ *
+ * tile.c
+ *
+ * Copyright (c) 2006 Atie H. <atie.at.matrix@gmail.com>
+ * Copyright (c) 2006 Michal Fojtik <pichalsi(at)gmail.com>
+ * Copyright (c) 2007 Danny Baumann <maniac@beryl-project.org>
+ *
+ * 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"
+
+static int displayPrivateIndex = 0;
+
+typedef enum {
+ NoAnimation = 0,
+ Animating,
+ AnimationDone
+} WindowAnimationType;
+
+typedef struct _TileDisplay {
+ int screenPrivateIndex;
+} TileDisplay;
+
+typedef struct _TileScreen {
+ int windowPrivateIndex;
+
+ int grabIndex;
+ int oneDuration; // duration of animation for one window
+ int msResizing; // number of ms elapsed from start of resizing animation
+ TileTileToggleTypeEnum tileType;
+
+ PaintWindowProc paintWindow;
+ WindowResizeNotifyProc windowResizeNotify;
+ PreparePaintScreenProc preparePaintScreen;
+ DonePaintScreenProc donePaintScreen;
+ PaintScreenProc paintScreen;
+} TileScreen;
+
+typedef struct _TileWindow {
+ Bool isTiled;
+
+ XRectangle savedCoords;
+ XRectangle prevCoords;
+ XRectangle newCoords;
+ unsigned int savedMaxState;
+ Bool savedValid;
+
+ Bool alreadyResized;
+
+ WindowAnimationType animationType;
+ unsigned int animationNum;
+
+ GLushort outlineColor[3];
+} TileWindow;
+
+#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)))
+
+
+static Bool placeWin(CompWindow *w, int x, int y, int width, int height);
+static Bool tileSetNewWindowSize(CompWindow *w);
+
+// 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->animationType != NoAnimation)
+ {
+ WindowPaintAttrib wAttrib = *attrib;
+ CompTransform wTransform = *transform;
+ float animationDuration = tileGetAnimationDuration(s->display);
+
+ switch (tileGetAnimateType(s->display))
+ {
+ /*
+ Drop animation
+ */
+ case AnimateTypeDropFromTop:
+ 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 > tw->animationNum * ts->oneDuration)
+ {
+ /* animation finished */
+ tw->animationType = AnimationDone;
+ }
+ else if (ts->msResizing > (tw->animationNum - 1) * ts->oneDuration)
+ {
+ int thisDur; // ms spent animating this window
+ thisDur = ts->msResizing % ts->oneDuration;
+
+ if (tw->animationNum % 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 AnimateTypeFilledOutline:
+ 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->alreadyResized)
+ {
+ // resize window right after first half
+ tileSetNewWindowSize(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);
+ }
+ 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);
+
+ // add spent time
+ if (ts->grabIndex)
+ ts->msResizing += msSinceLastPaint;
+
+ UNWRAP(ts, s, preparePaintScreen);
+ (*s->preparePaintScreen) (s, msSinceLastPaint);
+ WRAP(ts, s, preparePaintScreen, tilePreparePaintScreen);
+}
+
+static void tileDonePaintScreen(CompScreen * s)
+{
+ TILE_SCREEN(s);
+
+ if (ts->grabIndex)
+ {
+ if (ts->msResizing > tileGetAnimationDuration(s->display))
+ {
+ CompWindow *w;
+ for (w = s->windows; w; w = w->next)
+ {
+ TILE_WINDOW(w);
+ tw->animationType = NoAnimation;
+ }
+
+ ts->msResizing = 0;
+
+ removeScreenGrab(s, ts->grabIndex, NULL);
+ ts->grabIndex = 0;
+ }
+ else
+ 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->grabIndex)
+ 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->grabIndex &&
+ (tileGetAnimateType(s->display) == AnimateTypeFilledOutline))
+ {
+ CompWindow *w;
+ float animationDuration = tileGetAnimationDuration(s->display);
+
+ transformToScreenSpace(s, output, -DEFAULT_Z_CAMERA, &sTransform);
+ glPushMatrix();
+ glLoadMatrixf(sTransform.m);
+
+ glLineWidth(4.0f);
+
+ for (w = s->windows; w; w = w->next)
+ {
+ TILE_WINDOW(w);
+
+ if (tw->animationType == Animating)
+ {
+ // Coordinate = start + speed * elapsedTime
+ // Coordinate = start + (target - start)/interval * elapsedTime
+ // Draw outline
+
+ int x = tw->prevCoords.x - w->input.left +
+ (((float)(w->attrib.x - tw->prevCoords.x)) * ts->msResizing / animationDuration);
+ int y = tw->prevCoords.y - w->input.top +
+ (((float)(w->attrib.y - tw->prevCoords.y)) * ts->msResizing / animationDuration);
+ int width = tw->prevCoords.width + w->input.left + w->input.right +
+ (((float)(w->attrib.width - tw->prevCoords.width)) * ts->msResizing / animationDuration);
+ int height = tw->prevCoords.height + w->input.top + w->input.bottom +
+ (((float)(w->attrib.height - tw->prevCoords.height)) * 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);
+ }
+ }
+
+ glPopMatrix();
+ glColor4usv(defaultColor);
+ glLineWidth(1.0f);
+ }
+
+ 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->alreadyResized)
+ {
+ tw->alreadyResized = TRUE;
+ return;
+ }
+
+ // Dont do anything if joining is disabled or windows are being resized
+ if (!tileGetTileJoin(w->screen->display) || ts->grabIndex)
+ return;
+
+ /* determine previous and next tiled window */
+ CompWindow *prev = NULL, *next = NULL, *cw;
+ Bool windowSeen = FALSE;
+ for (cw = w->screen->reverseWindows; cw; cw = cw->prev)
+ {
+ if (windowSeen)
+ {
+ next = cw;
+ break;
+ }
+ else
+ {
+ if (cw != w)
+ prev = cw;
+ else
+ windowSeen = TRUE;
+ }
+ }
+
+ switch (ts->tileType)
+ {
+ case TileToggleTypeTile:
+ if (prev)
+ placeWin(prev,
+ prev->attrib.x, prev->attrib.y,
+ w->attrib.x - prev->attrib.x - w->input.left - prev->input.right,
+ prev->height);
+
+ if (next)
+ {
+ int currentX = w->attrib.x + w->width + w->input.right + next->input.left;
+ placeWin(next, currentX, next->attrib.y,
+ next->width + next->attrib.x - currentX, next->height);
+ }
+ break;
+
+ case TileToggleTypeTileHorizontally:
+ if (prev)
+ placeWin(prev,
+ prev->attrib.x, prev->attrib.y,
+ prev->width,
+ w->attrib.y - prev->attrib.y - w->input.top - prev->input.bottom);
+
+ if (next)
+ {
+ int currentY = w->attrib.y + w->height + w->input.bottom + next->input.top;
+ placeWin(next, next->attrib.x, currentY,
+ next->width, next->height + next->attrib.y - currentY);
+ }
+ break;
+ case TileToggleTypeLeft:
+ if (!next && prev && dwidth) // last window - on the left
+ {
+ XRectangle workArea;
+ int currentX;
+
+ workArea = w->screen->workArea;
+
+ for (cw = w->screen->windows; cw; cw = cw->next)
+ {
+ TILE_WINDOW(cw);
+
+ if (!tw->isTiled || (cw->id == w->id))
+ continue;
+
+ currentX = workArea.x + w->serverX + w->serverWidth + w->input.right + cw->input.left;
+
+ placeWin(cw, currentX, cw->attrib.y,
+ workArea.width - currentX - w->input.right, cw->attrib.height);
+ }
+ }
+ else if (next) // windows on the right
+ {
+ XRectangle workArea;
+ Bool first = TRUE;
+
+ workArea = w->screen->workArea;
+
+ for (cw = w->screen->windows; cw; cw = cw->next)
+ {
+ TILE_WINDOW(cw);
+
+ if (!tw->isTiled || (cw->id == w->id))
+ continue;
+
+ if (first)
+ {
+ placeWin(cw,
+ workArea.x + cw->input.left, cw->attrib.y,
+ w->serverX - w->input.left - cw->input.left - cw->input.right - workArea.x,
+ cw->attrib.height);
+
+ first = FALSE;
+ }
+ else
+ {
+ int x = cw->attrib.x;
+ int y = cw->attrib.y;
+ int width = cw->attrib.width;
+ int height = cw->attrib.height;
+
+ if (prev && (cw->id == prev->id))
+ height = w->serverY - cw->attrib.y - w->input.top - cw->input.bottom;
+ else if (next && (cw->id == next->id))
+ y = w->serverY + w->serverHeight + w->input.bottom + cw->input.top;
+
+ x = w->serverX;
+ width = workArea.width + workArea.x - w->serverX - w->input.right;
+
+ placeWin(cw, x, y, width, height);
+ }
+ }
+ }
+ 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->grabIndex = 0;
+ ts->msResizing = 0;
+ ts->oneDuration = 0;
+
+ // 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);
+
+ return TRUE;
+}
+
+static void tileFiniScreen(CompPlugin * p, CompScreen * s)
+{
+ TILE_SCREEN(s);
+
+ freeWindowPrivateIndex(s, ts->windowPrivateIndex);
+
+ //Restore the original function
+ UNWRAP(ts, s, paintScreen);
+ UNWRAP(ts, s, preparePaintScreen);
+ UNWRAP(ts, s, donePaintScreen);
+ UNWRAP(ts, s, windowResizeNotify);
+ UNWRAP(ts, s, paintWindow);
+
+ //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 placeWin(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);
+
+ // set previous coordinates for animation
+ tw->prevCoords.x = w->attrib.x;
+ tw->prevCoords.y = w->attrib.y;
+ tw->prevCoords.width = w->attrib.width;
+ tw->prevCoords.height = w->attrib.height;
+
+ // set future coordinates for animation
+ tw->newCoords.x = x;
+ tw->newCoords.y = y;
+ tw->newCoords.width = width;
+ tw->newCoords.height = height;
+
+ tw->alreadyResized = FALSE; // window is not resized now
+
+ switch (tileGetAnimateType(w->screen->display))
+ {
+ case AnimateTypeNone:
+ tileSetNewWindowSize(w);
+ break;
+ case AnimateTypeFilledOutline:
+ case AnimateTypeSlide:
+ case AnimateTypeZoom:
+ case AnimateTypeDropFromTop:
+ tileSetNewWindowSize(w);
+ /* fall-through */
+ case AnimateTypeFade:
+ tw->animationType = Animating;
+ break;
+ default:
+ break;
+ }
+
+ return TRUE;
+}
+
+static Bool tileSetNewWindowSize(CompWindow *w)
+{
+ TILE_WINDOW(w);
+ TILE_SCREEN(w->screen);
+ XWindowChanges xwc;
+
+ xwc.x = tw->newCoords.x;
+ xwc.y = tw->newCoords.y;
+ xwc.width = tw->newCoords.width;
+ xwc.height = tw->newCoords.height;
+
+ if (ts->tileType == -1)
+ {
+ if (tw->savedValid)
+ maximizeWindow(w, tw->savedMaxState);
+ }
+ else
+ maximizeWindow(w, 0);
+
+ configureXWindow (w, CWX | CWY | CWWidth | CWHeight, &xwc);
+
+ 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->screen->focusWindow)(w)))
+ return FALSE;
+
+ if (w->wmType & (CompWindowTypeDockMask | CompWindowTypeDesktopMask))
+ return FALSE;
+
+ if (w->state & CompWindowStateSkipPagerMask)
+ return FALSE;
+
+ if (w->minimized || !w->placed)
+ return FALSE;
+
+ return TRUE;
+}
+
+// save window coordinates to use for restore
+static void saveCoords(CompWindow *w)
+{
+ TILE_WINDOW(w);
+
+ if (tw->savedValid)
+ return;
+
+ tw->savedCoords.x = w->serverX;
+ tw->savedCoords.y = w->serverY;
+ tw->savedCoords.width = w->serverWidth;
+ tw->savedCoords.height = w->serverHeight;
+
+ tw->savedMaxState = w->state & MAXIMIZE_STATE;
+
+ tw->savedValid = TRUE;
+
+ return;
+}
+
+// Applies tiling/restoring
+static Bool applyTiling(CompScreen *s)
+{
+ TILE_SCREEN(s);
+
+ if (ts->grabIndex)
+ return FALSE;
+
+ int count = 0;
+ CompWindow *w;
+
+ for (w = s->windows; w; w = w->next)
+ {
+ if (isTileWindow(w))
+ count++;
+ }
+
+ ts->oneDuration = tileGetAnimationDuration(s->display) / MAX(count, 1);
+
+ if (count > 1)
+ {
+ int i = 0;
+ XRectangle workArea;
+ 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;
+ }
+
+ int countX = 0, countY = 0;
+ int currentX = 0, currentY = 0;
+ int winWidth = 0, winHeight = 0;
+ int x = 0, y = 0;
+ int height = 0, occupancy = 0, delta = 0;
+ Bool first = TRUE;
+
+ workArea = s->workArea;
+
+ switch (ts->tileType)
+ {
+ case TileToggleTypeTile:
+ countX = ceil(sqrt(count));
+ countY = ceil((float)count / countX);
+ currentX = workArea.x;
+ currentY = workArea.y;
+ winWidth = workArea.width / countX;
+ winHeight = workArea.height / countY;
+ break;
+ case TileToggleTypeLeft:
+ height = workArea.height / (count - 1);
+ occupancy = tileGetTileLeftOccupancy(s->display);
+ break;
+ case TileToggleTypeTileVertically:
+ winWidth = workArea.width / count;
+ winHeight = workArea.height;
+ y = workArea.y;
+ break;
+ case TileToggleTypeTileHorizontally:
+ winWidth = workArea.width;
+ winHeight = workArea.height / count;
+ x = workArea.x;
+ break;
+ case TileToggleTypeCascade:
+ delta = tileGetTileDelta(s->display);
+ currentX = workArea.x;
+ currentY = workArea.y;
+ winHeight = workArea.height - delta * (count - 1);
+ winWidth = workArea.width - delta * (count - 1);
+ break;
+ default:
+ break;
+ }
+
+ for (w = s->windows; w; w = w->next)
+ {
+ if (!isTileWindow(w))
+ continue;
+
+ TILE_WINDOW(w);
+
+ if (!tw->savedValid)
+ saveCoords(w);
+
+ switch (ts->tileType)
+ {
+ case TileToggleTypeTile:
+ placeWin(w, currentX + border.left,
+ currentY + border.top,
+ winWidth - (border.left + border.right),
+ winHeight - (border.top + border.bottom));
+ tw->isTiled = TRUE;
+ break;
+ case TileToggleTypeLeft:
+ if (first)
+ {
+ x = workArea.x;
+ y = workArea.y;
+ winWidth = workArea.width * occupancy / 100;
+ winHeight = workArea.height;
+ first = FALSE;
+ }
+ else
+ {
+ x = workArea.x + (workArea.width * occupancy / 100);
+ y = workArea.y + (i * height);
+ winWidth = (workArea.width * (100 - occupancy) / 100);
+ winHeight = height;
+ }
+
+ placeWin(w, x + border.left, y + border.top,
+ winWidth - (border.left + border.right),
+ winHeight - (border.top + border.bottom));
+ tw->isTiled = TRUE;
+ break;
+ case TileToggleTypeTileVertically:
+ x = workArea.x + (winWidth * i);
+ placeWin(w, x + border.left, y + border.top,
+ winWidth - (border.left + border.right),
+ winHeight - (border.top + border.bottom));
+ tw->isTiled = TRUE;
+ break;
+ case TileToggleTypeTileHorizontally:
+ y = workArea.y + (winHeight * i);
+ placeWin(w, x + border.left, y + border.top,
+ winWidth - (border.left + border.right),
+ winHeight - (border.top + border.bottom));
+ tw->isTiled = TRUE;
+ break;
+ case TileToggleTypeCascade:
+ placeWin(w, currentX + border.left,
+ currentY + border.top,
+ winWidth - (border.left + border.right),
+ winHeight - (border.top + border.bottom));
+ tw->isTiled = TRUE;
+ break;
+ case -1:
+ if (tw->isTiled)
+ {
+ placeWin(w, tw->savedCoords.x, tw->savedCoords.y,
+ tw->savedCoords.width, tw->savedCoords.height);
+ tw->savedValid = FALSE;
+ tw->isTiled = FALSE;
+ }
+ break;
+ default:
+ break;
+ }
+
+ i++;
+ tw->animationNum = i;
+
+ switch (ts->tileType)
+ {
+ case TileToggleTypeTile:
+ if (!(i % countX))
+ {
+ currentX = workArea.x;
+ currentY += winHeight;
+ }
+ else
+ currentX += winWidth;
+ break;
+ case TileToggleTypeCascade:
+ currentX += delta;
+ currentY += delta;
+ default:
+ break;
+ }
+ }
+
+ if (!ts->grabIndex)
+ ts->grabIndex = pushScreenGrab(s, s->invisibleCursor, "tile");
+
+ ts->msResizing = 0;
+ }
+
+ 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);
+ ts->tileType = 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);
+ ts->tileType = TileToggleTypeTileVertically;
+ 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);
+ ts->tileType = TileToggleTypeTileHorizontally;
+ 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);
+ ts->tileType = 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);
+ ts->tileType = -1;
+ 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) {
+ CompWindow *w;
+ TILE_SCREEN(s);
+
+ for (w = s->windows; w; w = w->next)
+ {
+ TILE_WINDOW(w);
+ if (tw->isTiled)
+ break;
+ }
+
+ if (w)
+ {
+ ts->tileType = -1;
+ applyTiling(s);
+ }
+ else
+ {
+ ts->tileType = 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;
+
+ memset(&tw->newCoords, 0, sizeof(XRectangle));
+ memset(&tw->prevCoords, 0, sizeof(XRectangle));
+ memset(&tw->savedCoords, 0, sizeof(XRectangle));
+
+ tw->savedValid = FALSE;
+ tw->animationType = NoAnimation;
+ tw->savedMaxState = 0;
+ tw->isTiled = FALSE;
+
+ // 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);
+
+ 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",
+ tileGetVersion,
+ 0,
+ tileInit,
+ tileFini,
+ tileInitDisplay,
+ tileFiniDisplay,
+ tileInitScreen,
+ tileFiniScreen,
+ tileInitWindow,
+ tileFiniWindow,
+ 0,
+ 0,
+ 0, /*tileGetScreenOptions */
+ 0, /*tileSetScreenOption */
+ 0,
+ 0,
+ 0,
+ 0
+};
+
+CompPluginVTable *getCompPluginInfo(void)
+{
+ return &tileVTable;
+}
diff --git a/tile.xml b/tile.xml
new file mode 100644
index 0000000..bbea880
--- /dev/null
+++ b/tile.xml
@@ -0,0 +1,149 @@
+<?xml version="1.0"?>
+<compiz>
+ <plugin name="tile" useBcop="true">
+ <short>Tile</short>
+ <long>Tile windows</long>
+ <category>Window Management</category>
+ <display>
+ <group>
+ <short>Options</short>
+ <option name="animate_type" type="int">
+ <short>Tiling Animation Type</short>
+ <long>Selects the animation used while tiling</long>
+ <default>0</default>
+ <min>0</min>
+ <max>5</max>
+ <desc>
+ <value>0</value>
+ <name>None</name>
+ </desc>
+ <desc>
+ <value>1</value>
+ <name>Filled Outline</name>
+ </desc>
+ <desc>
+ <value>2</value>
+ <name>Slide</name>
+ </desc>
+ <desc>
+ <value>3</value>
+ <name>Zoom</name>
+ </desc>
+ <desc>
+ <value>4</value>
+ <name>Drop From Top</name>
+ </desc>
+ <desc>
+ <value>5</value>
+ <name>Fade</name>
+ </desc>
+ </option>
+ <option name="animation_duration" type="int">
+ <short>Animation Duration</short>
+ <long>Duration (in ms) of the tiling animation</long>
+ <default>1000</default>
+ <min>100</min>
+ <max>2000</max>
+ </option>
+ <option name="tile_toggle_type" type="int">
+ <short>Tiling Method For Toggle</short>
+ <long>Choose the tiling type you want when using toggle.</long>
+ <default>0</default>
+ <min>0</min>
+ <max>4</max>
+ <desc>
+ <value>0</value>
+ <name>Tile</name>
+ </desc>
+ <desc>
+ <value>1</value>
+ <name>Left</name>
+ </desc>
+ <desc>
+ <value>2</value>
+ <name>Tile Vertically</name>
+ </desc>
+ <desc>
+ <value>3</value>
+ <name>Tile Horizontally</name>
+ </desc>
+ <desc>
+ <value>4</value>
+ <name>Cascade</name>
+ </desc>
+ </option>
+ <option name="tile_join" type="bool">
+ <short>Join Windows (EXPERIMENTAL)</short>
+ <long>Tries to join the windows together when horizontal, vertical or left tiling is enabled so that when you resize a window surrounding windows resize accordingly. This may cause problems if you dont leave them enough space.</long>
+ <default>false</default>
+ </option>
+ <option name="tile_delta" type="int">
+ <short>Cascade Delta</short>
+ <long>Distance between windows when using cascade</long>
+ <default>35</default>
+ <min>0</min>
+ <max>250</max>
+ </option>
+ <option name="tile_left_occupancy" type="int">
+ <short>Left Occupancy</short>
+ <long>Occupancy percentage for window placed left. This number is percentage of screen width, which the active window will have it as width when tiled. Applies to Left tiling type.</long>
+ <default>60</default>
+ <min>20</min>
+ <max>80</max>
+ </option>
+ <option name="exclude_match" type="match">
+ <short>Excluded Windows</short>
+ <long>Windows which should be excluded from tiling</long>
+ <default/>
+ </option>
+ </group>
+ <group>
+ <short>Key Bindings</short>
+ <option name="tile_vertically" type="action">
+ <short>Tile Windows Vertically</short>
+ <long>Move and resize all visible windows so that they have full height, same width and occupy whole screen.</long>
+ <allowed/>
+ <default/>
+ </option>
+ <option name="tile_horizontally" type="action">
+ <short>Tile Windows Horizontally</short>
+ <long>Move and resize all visible windows so that they have full width, same height and occupy whole screen.</long>
+ <allowed/>
+ <default/>
+ </option>
+ <option name="tile_tile" type="action">
+ <short>Tile Windows</short>
+ <long>Move and resize all visible windows both vertically and horizontally, so that the occupy whole screen and are in a grid.</long>
+ <allowed key="true"/>
+ <default>
+ <key>&lt;Super&gt;&lt;Shift&gt;a</key>
+ </default>
+ </option>
+ <option name="tile_cascade" type="action">
+ <short>Cascade Windows</short>
+ <long>Move and resize all visible windows with the delta value set for cascading.</long>
+ <allowed key="true"/>
+ <default>
+ <key>&lt;Super&gt;&lt;Shift&gt;s</key>
+ </default>
+ </option>
+ <option name="tile_restore" type="action">
+ <short>Restore Windows</short>
+ <long>Restore windows to their original position they had before tiling.</long>
+ <allowed key="true"/>
+ <default>
+ <key>&lt;Super&gt;&lt;Shift&gt;z</key>
+ </default>
+ </option>
+ <option name="tile_toggle" type="action">
+ <short>Toggle Tiling</short>
+ <long>Toggle between tile and restore</long>
+ <allowed key="true"/>
+ <default>
+ <key>&lt;Super&gt;&lt;Shift&gt;x</key>
+ </default>
+ </option>
+ </group>
+ </display>
+ </plugin>
+</compiz>