/**
*
* 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 <beryl.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)->x])
#define TILE_DISPLAY_OPTION_VERTICALLY 0
#define TILE_DISPLAY_OPTION_HORIZONTALLY 1
#define TILE_DISPLAY_OPTION_TILE 2
#define TILE_DISPLAY_OPTION_CASCADE 3
#define TILE_DISPLAY_OPTION_RESTORE 4
#define TILE_DISPLAY_OPTION_TOGGLE 5
#define TILE_DISPLAY_OPTION_EXCLUDE_LIST 6
#define TILE_DISPLAY_OPTION_JOIN 7
#define TILE_DISPLAY_OPTION_DELTA 8
#define TILE_DISPLAY_OPTION_LEFT_OCCUPANCY 9
#define TILE_DISPLAY_OPTION_ANIMATE 10
#define TILE_DISPLAY_OPTION_ANIMATION_DURATION 11
#define TILE_DISPLAY_OPTION_ANIMATION_TYPE 12
#define TILE_DISPLAY_OPTION_TOGGLE_TYPE 13
#define TILE_DISPLAY_OPTION_NUM 14
#define TILE_HORIZONTALLY_DISPLAY_OPTION_INITIATE_KEY "q"
#define TILE_HORIZONTALLY_DISPLAY_OPTION_INITIATE_MOD CompSuperMask|ShiftMask
#define TILE_VERTICALLY_DISPLAY_OPTION_INITIATE_KEY "w"
#define TILE_VERTICALLY_DISPLAY_OPTION_INITIATE_MOD CompSuperMask|ShiftMask
#define TILE_TILE_DISPLAY_OPTION_INITIATE_KEY "a"
#define TILE_TILE_DISPLAY_OPTION_INITIATE_MOD CompSuperMask|ShiftMask
#define TILE_CASCADE_DISPLAY_OPTION_INITIATE_KEY "s"
#define TILE_CASCADE_DISPLAY_OPTION_INITIATE_MOD CompSuperMask|ShiftMask
#define TILE_RESTORE_DISPLAY_OPTION_INITIATE_KEY "z"
#define TILE_RESTORE_DISPLAY_OPTION_INITIATE_MOD CompSuperMask|ShiftMask
#define TILE_TOGGLE_DISPLAY_OPTION_INITIATE_KEY "x"
#define TILE_TOGGLE_DISPLAY_OPTION_INITIATE_MOD CompSuperMask|ShiftMask
#define TILE_DEFAULT_JOIN FALSE
#define TILE_DEFAULT_ANIMATE FALSE
#define TILE_DEFAULT_ANIMATION fade
#define TILE_DEFAULT_TOGGLE_TYPE tile
#define TILE_DELTA_DEFAULT 35
#define TILE_DELTA_MIN 0
#define TILE_DELTA_MAX 250
#define TILE_LEFT_OCCUPANCY_DEFAULT 60
#define TILE_LEFT_OCCUPANCY_MIN 20
#define TILE_LEFT_OCCUPANCY_MAX 80
#define TILE_ANIMATION_DURATION_DEFAULT 1000
#define TILE_ANIMATION_DURATION_MIN 200
#define TILE_ANIMATION_DURATION_MAX 5000
// Minimal width and height under which the window cant be resized
#define MINIMUM_WIDTH 25
#define MINIMUM_HEIGHT 10
#define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption))
static int displayPrivateIndex = 0;
#define NUM_ANIMATIONS 5
static char *animationTypeString[] = {
N_("Filled outline"),
N_("Slide"),
N_("Zoom"),
N_("Drop from top"),
N_("Fade")
};
typedef enum {
outline = 0,
slide,
zoom,
drop,
fade
} animationType;
// number of tile types for dropdown, without 'none'
#define TILE_TYPE_NUM 5
static char *tileTypeString[] = {
N_("Tile"),
N_("Left"),
N_("Tile Vertically"),
N_("Tile Horizontally"),
N_("Cascade")
};
typedef enum {
tile = 0,
left,
vert,
horz,
cascade,
none
} tileType;
typedef struct _TileDisplay {
tileType currentToggleType; // toggle tiling type
animationType currentAnimationType;
int animationDuration; // duration of animation
int screenPrivateIndex;
CompOption opt[TILE_DISPLAY_OPTION_NUM];
} TileDisplay;
typedef struct _TileViewport {
tileType 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;
int decoWidth, decoHeight; // decoration width and height
int windowPrivateIndex;
} TileScreen;
typedef struct _TileWindow {
int isOtherAnimationAtom; // atom to check whether animation plugin is animating the window
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, Region region, unsigned int mask)
{
CompScreen *s = w->screen;
Bool status;
TILE_SCREEN(s);
TILE_WINDOW(w);
TILE_DISPLAY(s->display);
if(tw->isResizing)
mask |= PAINT_WINDOW_NO_CORE_INSTANCE_MASK;
if(tw->isResizing && td->currentAnimationType != outline) // on window texture animation
{
WindowPaintAttrib sAttrib = *attrib;
glPushMatrix();
glLoadIdentity();
prepareXCoords(s, s->currentOutputDev, -DEFAULT_Z_CAMERA);
switch(td->currentAnimationType)
{
/*
Drop animation
*/
case drop:
glRotatef(100.0f/td->animationDuration*ts->msResizing - 100, 0,0,1);
(*s->drawWindow) (w, &sAttrib, region, mask | PAINT_WINDOW_TRANSFORMED_MASK);
break;
/*
Zoom animation
*/
case zoom:
glTranslatef(0,0, -1 + ts->msResizing/(float)td->animationDuration);
(*s->drawWindow) (w, &sAttrib, region, mask | PAINT_WINDOW_TRANSFORMED_MASK);
break;
/*
Slide animation
*/
case slide:
if(ts->msResizing < 0.75*td->animationDuration)
sAttrib.opacity = OPAQUE / 2;
else
sAttrib.opacity = OPAQUE/2 + OPAQUE/2*(ts->msResizing - 0.75*td->animationDuration)/(0.25*td->animationDuration);
if(ts->msResizing > current*ts->oneDuration) // windows that have animation finished already
{
(*s->drawWindow) (w, &sAttrib, region, 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)
glTranslatef(-s->width + s->width * (float)thisDur/ts->oneDuration, 0, 0);
else
glTranslatef(s->width - s->width * (float)thisDur/ts->oneDuration, 0, 0);
(*s->drawWindow) (w, &sAttrib, region, mask | PAINT_WINDOW_TRANSFORMED_MASK);
}
break;
/*
Outline animation
*/
case outline:
break;
/*
Fade animation
*/
case fade:
// first half of the animation, fade out
if(ts->msResizing < 0.40f*td->animationDuration)
{
sAttrib.opacity = OPAQUE - OPAQUE*ts->msResizing/(0.40f*td->animationDuration);
(*s->drawWindow) (w, &sAttrib, region, mask | PAINT_WINDOW_TRANSFORMED_MASK);
}
else if(ts->msResizing > 0.40f*td->animationDuration && !tw->resizedAlready) // resize window right after first half
setWindowFutureSize(w);
else if(ts->msResizing > 0.60f*td->animationDuration) // second half of animation, fade in
{
sAttrib.opacity = OPAQUE*(ts->msResizing - 0.60f*td->animationDuration)/(0.40f*td->animationDuration);
(*s->drawWindow) (w, &sAttrib, region, mask | PAINT_WINDOW_TRANSFORMED_MASK);
}
break;
}
current -= 1;
glPopMatrix();
}
else // paint window as always
{
UNWRAP(ts, s, paintWindow);
status = (*s->paintWindow) (w, attrib, region, mask);
WRAP(ts, s, paintWindow, tilePaintWindow);
}
return status;
}
static void tilePreparePaintScreen(CompScreen * s, int msSinceLastPaint)
{
TILE_SCREEN(s);
TILE_DISPLAY(s->display);
// this probably shouldnt be here...
td->animationDuration = td->opt[TILE_DISPLAY_OPTION_ANIMATION_DURATION].value.i;
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 > td->animationDuration)
{
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);
// hope this is ok
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, Region region, int output, unsigned int mask)
{
Bool status;
TILE_SCREEN(s);
TILE_DISPLAY(s->display);
if(ts->isResizing)
mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK;
UNWRAP(ts, s, paintScreen);
status = (*s->paintScreen) (s, sa, 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(!td->opt[TILE_DISPLAY_OPTION_ANIMATE].value.b || !ts->isResizing || td->currentAnimationType != outline)
return status;
glPushMatrix();
glLoadIdentity();
prepareXCoords(s, output, -DEFAULT_Z_CAMERA);
glLineWidth(4.0f);
CompWindow *w = THIS_VIEWPORT(s).firstTiled;
while(w && isTileWindow(w))
{
TILE_WINDOW(w);
if(tw->isResizing)
{
// Coordinate = start + speed * elapsedTime
// Coordinate = start + (target - start)/interval * elapsedTime
// Draw outline
int x = ((float)w->attrib.x - (float)tw->previousX)/td->animationDuration * ts->msResizing + tw->previousX;
x -= w->input.left; // decoration
int y = ((float)w->attrib.y - (float)tw->previousY)/td->animationDuration * ts->msResizing + tw->previousY;
y -= w->input.top; // decoration
int width = ((float)w->attrib.width - (float)tw->previousWidth)/td->animationDuration * ts->msResizing + tw->previousWidth;
width += w->input.left + w->input.right; // decoration
int height = ((float)w->attrib.height - (float)tw->previousHeight)/td->animationDuration * ts->msResizing + tw->previousHeight;
height += w->input.top + w->input.bottom; //decoration
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();
}
w = tw->next;
}
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, Bool preview)
{
TILE_SCREEN(w->screen);
TILE_WINDOW(w);
TILE_DISPLAY(w->screen->display);
UNWRAP(ts, w->screen, windowResizeNotify);
(*w->screen->windowResizeNotify) (w, dx, dy, dwidth, dheight, preview);
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(preview || !td->opt[TILE_DISPLAY_OPTION_JOIN].value.b || ts->isResizing)
return;
if(THIS_VIEWPORT(w->screen).currentTileType == vert)
{
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);
}
}
else if(THIS_VIEWPORT(w->screen).currentTileType == horz)
{
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);
}
}
else if(THIS_VIEWPORT(w->screen).currentTileType == left)
{
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;
screenGetOutputDevWorkArea(w->screen, screenGetCurrentOutputDev(w->screen), &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;
screenGetOutputDevWorkArea(w->screen, screenGetCurrentOutputDev(w->screen), &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 * otw = GET_TILE_WINDOW(w, ts); // tilewindow from the resized window, tw is from temp
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;
}
}
}
}
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->decoWidth = 0;
ts->decoHeight = 0;
ts->isResizing = FALSE;
ts->msResizing = 0;
ts->oneDuration = 0;
ts->viewports = calloc(s->hsize, sizeof(TileViewport));
int i;
for(i=0;i<s->hsize;i++)
ts->viewports[i].currentTileType = none;
// 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);
addScreenAction(s, &td->opt[TILE_DISPLAY_OPTION_VERTICALLY].value.action);
addScreenAction(s, &td->opt[TILE_DISPLAY_OPTION_HORIZONTALLY].value.action);
addScreenAction(s, &td->opt[TILE_DISPLAY_OPTION_TILE].value. action);
addScreenAction(s, &td->opt[TILE_DISPLAY_OPTION_CASCADE].value.action);
addScreenAction(s, &td->opt[TILE_DISPLAY_OPTION_RESTORE].value.action);
addScreenAction(s, &td->opt[TILE_DISPLAY_OPTION_TOGGLE].value.action);
return TRUE;
}
static void tileFiniScreen(CompPlugin * p, CompScreen * s)
{
TILE_SCREEN(s);
TILE_DISPLAY(s->display);
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);
removeScreenAction(s, &td->opt[TILE_DISPLAY_OPTION_HORIZONTALLY].value.action);
removeScreenAction(s, &td->opt[TILE_DISPLAY_OPTION_VERTICALLY].value.action);
removeScreenAction(s, &td->opt[TILE_DISPLAY_OPTION_TILE].value.action);
removeScreenAction(s, &td->opt[TILE_DISPLAY_OPTION_CASCADE].value.action);
removeScreenAction(s, &td->opt[TILE_DISPLAY_OPTION_RESTORE].value.action);
removeScreenAction(s, &td->opt[TILE_DISPLAY_OPTION_TOGGLE].value.action);
//Free the pointer
free(ts);
}
static Bool tileSetDisplayOption(CompDisplay * display, char *name, CompOptionValue * value)
{
CompOption *o;
int index;
TILE_DISPLAY(display);
o = compFindOption(td->opt, NUM_OPTIONS(td), name, &index);
if (!o)
return FALSE;
switch (index) {
case TILE_DISPLAY_OPTION_VERTICALLY:
case TILE_DISPLAY_OPTION_HORIZONTALLY:
case TILE_DISPLAY_OPTION_TILE:
case TILE_DISPLAY_OPTION_CASCADE:
case TILE_DISPLAY_OPTION_RESTORE:
case TILE_DISPLAY_OPTION_TOGGLE:
if (setDisplayAction(display, o, value))
return TRUE;
break;
case TILE_DISPLAY_OPTION_ANIMATE:
case TILE_DISPLAY_OPTION_JOIN:
if (compSetBoolOption(o, value))
return TRUE;
break;
case TILE_DISPLAY_OPTION_ANIMATION_DURATION:
case TILE_DISPLAY_OPTION_DELTA:
case TILE_DISPLAY_OPTION_LEFT_OCCUPANCY:
if (compSetIntOption(o, value))
return TRUE;
break;
case TILE_DISPLAY_OPTION_EXCLUDE_LIST:
if (compSetOptionList(o, value))
return TRUE;
break;
case TILE_DISPLAY_OPTION_ANIMATION_TYPE:
if (compSetStringOption(o, value))
{
int i;
for (i = 0; i < NUM_ANIMATIONS; i++)
{
if (strcmp(o->value.s, animationTypeString[i]) == 0)
{
td->currentAnimationType = i;
return TRUE;
}
}
}
break;
case TILE_DISPLAY_OPTION_TOGGLE_TYPE:
if (compSetStringOption(o, value))
{
int i;
for (i = 0; i < TILE_TYPE_NUM; i++)
{
if (strcmp(o->value.s, tileTypeString[i]) == 0)
{
td->currentToggleType = i;
return TRUE;
}
}
}
break;
default:
break;
}
return FALSE;
}
// 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);
TILE_DISPLAY(w->screen->display);
// 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
if(!td->opt[TILE_DISPLAY_OPTION_ANIMATE].value.b)
setWindowFutureSize(w);
else
{
if(td->currentAnimationType != fade) // for now all animations except fade resize window before animation
{
setWindowFutureSize(w);
}
// set animation
if(td->opt[TILE_DISPLAY_OPTION_ANIMATE].value.b)
{
ts->isResizing = TRUE;
tw->isResizing = TRUE;
ts->msResizing = 0;
ts->oneDuration = td->animationDuration/THIS_VIEWPORT(w->screen).tiledCount;
}
}
return TRUE;
}
static Bool setWindowFutureSize(CompWindow *w)
{
TILE_WINDOW(w);
TILE_SCREEN(w->screen);
int x = tw->futureX;
int y = tw->futureY;
int width = tw->futureWidth;
int height = tw->futureHeight;
XWindowChanges xwc;
xwc.x = x;
xwc.y = y;
xwc.width = width;
xwc.height = height;
if(THIS_VIEWPORT(w->screen).currentTileType == none)
{
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);
// if after restoring the window is maximized, then restore "restore data" :P
if((w->state & MAXIMIZE_STATE) && THIS_VIEWPORT(w->screen).currentTileType == none)
{
xwc.x = x;
xwc.y = y;
xwc.width = width;
xwc.height = height;
// restore "restore data"
saveVertRestoreData(w, &xwc);
saveHorzRestoreData(w, &xwc);
}
else
configureXWindow (w, CWHeight | CWWidth | CWY | CWX, &xwc);
return TRUE;
}
// Heavily inspired by windowIs3D and isSDWindow, returns TRUE if the window is usable for tiling
static Bool isTileWindow(CompWindow * w)
{
TILE_DISPLAY(w->screen->display);
// Exclude windows from exclusion list
CompOption *o = &td->opt[TILE_DISPLAY_OPTION_EXCLUDE_LIST];
int i;
for (i = 0; i < o->value.list.nValue; i++)
{
if (w->resClass && (strcmp(o->value.list.value[i].s, w->resClass) == 0))
return FALSE;
}
if (w->attrib.override_redirect)
return FALSE;
if (w->state & CompWindowStateOffscreenMask)
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;
int decoHeight = 0, decoWidth = 0;
CompWindow *first = 0, *previous = 0;
CompWindow *w;
if(THIS_VIEWPORT(s).currentTileType != none)
for (w = s->windows; w; w = w->next)
{
int x,y;
defaultViewportForWindow(w,&x,&y);
if(isTileWindow(w) && s->x == x)
{
if(previous)
{
TILE_WINDOW(previous);
tw->next = w;
}
TILE_WINDOW(w);
if(!first)
first = w;
tw->prev = previous;
previous = w;
tw->next = 0;
count++;
decoHeight = w->input.top + w->input.bottom;
decoWidth = w->input.left + w->input.right;
}
}
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) && s->x == x)
{
// This fixes problem with window closing animation running while restoring window, causing beryl crash
if(IPCS_GetBool(IPCS_OBJECT(w), tw->isOtherAnimationAtom))
continue;
if(previous)
{
TILE_WINDOW(previous);
tw->next = w;
}
if(!first)
first = w;
tw->prev = previous;
previous = w;
tw->next = 0;
count++;
decoHeight = w->input.top + w->input.bottom;
decoWidth = w->input.left + w->input.right;
}
}
THIS_VIEWPORT(s).firstTiled = first;
THIS_VIEWPORT(s).tiledCount = count;
ts->decoHeight = decoHeight;
ts->decoWidth = decoWidth;
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)
{
XWindowChanges xwc;
restoreVertRestoreData(w, &xwc);
restoreHorzRestoreData(w, &xwc);
tw->originalX = xwc.x;
tw->originalY = xwc.y;
tw->originalWidth = xwc.width;
tw->originalHeight = xwc.height;
}
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);
TILE_DISPLAY(s->display);
if(ts->isResizing)
return FALSE;
loadTiledWindows(s);
if(THIS_VIEWPORT(s).tiledCount>1)
{
XRectangle workArea;
screenGetOutputDevWorkArea(s, screenGetCurrentOutputDev(s), &workArea);
CompWindow *w = THIS_VIEWPORT(s).firstTiled;
int i = 0;
switch(THIS_VIEWPORT(s).currentTileType)
{
/*
Tile into grid
*/
case tile :
{
int numWidth = ceil(sqrt(THIS_VIEWPORT(s).tiledCount));
int numHeight = ceil((float)THIS_VIEWPORT(s).tiledCount/numWidth);
int height = (workArea.height - ts->decoHeight*numHeight)/numHeight;
int width = (workArea.width - ts->decoWidth*numWidth)/numWidth;
int currentX = w->input.left + workArea.x;
int currentY = w->input.top + workArea.y;
while(w)
{
TILE_WINDOW(w);
if(!tw->originalWidth || !tw->originalHeight)
saveCoords(w);
placeWindow(w, currentX, currentY, width, height);
i++;
if(i%numWidth==0)
{
currentX = w->input.left + workArea.x;
currentY += height + ts->decoHeight;
}
else
currentX += width + ts->decoWidth;
w = tw->next;
}
}
break;
/*
Tile left
*/
case left:
{
int x = 0, y = 0, wid = 0, hei = 0;
int height = (workArea.height - ts->decoHeight*(THIS_VIEWPORT(s).tiledCount-1))/(THIS_VIEWPORT(s).tiledCount-1);
int occupancy = td->opt[TILE_DISPLAY_OPTION_LEFT_OCCUPANCY].value.i;
while(w)
{
TILE_WINDOW(w);
if (!tw->next) // this is the last window in the list - the active/topmost window
{
x = workArea.x + w->input.left;
y = workArea.y + w->input.top;
wid = workArea.width * occupancy / 100;
hei = workArea.height - w->input.top - w->input.bottom;
}
else
{
x = workArea.x + (w->input.left * 2) + w->input.right +
(workArea.width * occupancy / 100);
y = workArea.y + w->input.top*(i+1) + w->input.bottom*(i) + height*(i);
wid = (workArea.width * (100 - occupancy) / 100) -
((w->input.left + w->input.right) * 2);
hei = height;
}
if(!tw->originalWidth || !tw->originalHeight)
saveCoords(w);
placeWindow(w, x, y, wid, hei);
i++;
w = tw->next;
}
}
break;
/*
Tile vertically
*/
case vert:
{
int width = (workArea.width - ts->decoWidth*THIS_VIEWPORT(s).tiledCount)/THIS_VIEWPORT(s).tiledCount;
int height = workArea.height - w->input.top - w->input.bottom;
int y = w->input.top + workArea.y;
while(w)
{
TILE_WINDOW(w);
int x = workArea.x + w->input.left*(i+1) + w->input.right*i + width*i;
if(!tw->originalWidth || !tw->originalHeight)
saveCoords(w);
placeWindow(w, x, y, width, height);
i++;
w = tw->next;
}
}
break;
/*
Tile horizontally
*/
case horz:
{
int width = workArea.width - w->input.left - w->input.right;
int height=(workArea.height - ts->decoHeight*THIS_VIEWPORT(s).tiledCount)/THIS_VIEWPORT(s).tiledCount;
int x = w->input.left + workArea.x;
while(w)
{
TILE_WINDOW(w);
int y = workArea.y + w->input.top*(i+1) + w->input.bottom*i + height*i;
if(!tw->originalWidth || !tw->originalHeight)
saveCoords(w);
placeWindow(w, x, y, width, height);
i++;
w = tw->next;
}
}
break;
/*
Cascade
*/
case cascade:
{
int delta = td->opt[TILE_DISPLAY_OPTION_DELTA].value.i;
int currentX = w->input.left + workArea.x;
int currentY = w->input.top + workArea.y;
int height = workArea.height - delta*(THIS_VIEWPORT(s).tiledCount - 1) - ts->decoHeight;
int width = workArea.width - delta*(THIS_VIEWPORT(s).tiledCount - 1) - ts->decoWidth;
while(w)
{
TILE_WINDOW(w);
if(!tw->originalWidth || !tw->originalHeight)
saveCoords(w);
placeWindow(w, currentX, currentY, width, height);
currentX += delta;
currentY += delta;
w = tw->next;
}
}
break;
/*
Restore
*/
case none:
{
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 = tile;
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 = vert;
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 = horz;
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 = cascade;
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 != none)
{
THIS_VIEWPORT(s).currentTileType = none;
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 != none)
{
THIS_VIEWPORT(s).currentTileType = none;
applyTiling(s);
}
else
{
TILE_DISPLAY(d);
THIS_VIEWPORT(s).currentTileType = td->currentToggleType;
applyTiling(s);
}
}
return FALSE;
}
static void tileDisplayInitOptions(TileDisplay * td)
{
CompOption *o;
o = &td->opt[TILE_DISPLAY_OPTION_VERTICALLY];
o->name = "tile_vertically";
o->group = N_("Key Bindings");
o->subGroup = N_("Tile Windows Vertically");
o->displayHints = "";
o->shortDesc = N_("Tile Windows Vertically");
o->longDesc = N_("Move and resize all visible windows so that they have full height, same width and occupy whole screen.");
o->type = CompOptionTypeAction;
o->value.action.initiate = tileVertically;
o->value.action.terminate = 0;
o->value.action.bell = FALSE;
o->value.action.edgeMask = 0;
o->value.action.type = 0;
o->value.action.state = CompActionStateInitKey;
// o->value.action.key.modifiers = TILE_VERTICALLY_DISPLAY_OPTION_INITIATE_MOD;
// o->value.action.key.keysym =
// XStringToKeysym(TILE_VERTICALLY_DISPLAY_OPTION_INITIATE_KEY);
o = &td->opt[TILE_DISPLAY_OPTION_HORIZONTALLY];
o->name = "tile_horizontally";
o->group = N_("Key Bindings");
o->subGroup = N_("Tile Windows Horizontally");
o->displayHints = "";
o->shortDesc = N_("Tile Windows Horizontally");
o->longDesc = N_("Move and resize all visible windows so that they have full width, same height and occupy whole screen.");
o->type = CompOptionTypeAction;
o->value.action.initiate = tileHorizontally;
o->value.action.terminate = 0;
o->value.action.bell = FALSE;
o->value.action.edgeMask = 0;
o->value.action.type = 0;
o->value.action.state = CompActionStateInitKey;
// o->value.action.key.modifiers = TILE_HORIZONTALLY_DISPLAY_OPTION_INITIATE_MOD;
// o->value.action.key.keysym =
// XStringToKeysym(TILE_HORIZONTALLY_DISPLAY_OPTION_INITIATE_KEY);
o = &td->opt[TILE_DISPLAY_OPTION_TILE];
o->name = "tile_tile";
o->group = N_("Key Bindings");
o->subGroup = N_("Tile Windows");
o->displayHints = "";
o->shortDesc = N_("Tile Windows");
o->longDesc = N_("Move and resize all visible windows both vertically and horizontally, so that the occupy whole screen and are in a grid.");
o->type = CompOptionTypeAction;
o->value.action.initiate = tileTile;
o->value.action.terminate = 0;
o->value.action.bell = FALSE;
o->value.action.edgeMask = 0;
o->value.action.type = CompBindingTypeKey;
o->value.action.state = CompActionStateInitKey;
o->value.action.key.modifiers = TILE_TILE_DISPLAY_OPTION_INITIATE_MOD;
o->value.action.key.keysym =
XStringToKeysym(TILE_TILE_DISPLAY_OPTION_INITIATE_KEY);
o = &td->opt[TILE_DISPLAY_OPTION_CASCADE];
o->name = "tile_cascade";
o->group = N_("Key Bindings");
o->subGroup = N_("Cascade Windows");
o->displayHints = "";
o->shortDesc = N_("Cascade Windows");
o->longDesc = N_("Move and resize all visible windows with the delta value set for cascading.");
o->type = CompOptionTypeAction;
o->value.action.initiate = tileCascade;
o->value.action.terminate = 0;
o->value.action.bell = FALSE;
o->value.action.edgeMask = 0;
o->value.action.type = CompBindingTypeKey;
o->value.action.state = CompActionStateInitKey;
o->value.action.key.modifiers = TILE_CASCADE_DISPLAY_OPTION_INITIATE_MOD;
o->value.action.key.keysym =
XStringToKeysym(TILE_CASCADE_DISPLAY_OPTION_INITIATE_KEY);
o = &td->opt[TILE_DISPLAY_OPTION_RESTORE];
o->name = "tile_restore";
o->group = N_("Key Bindings");
o->subGroup = N_("Restore Windows");
o->displayHints = "";
o->shortDesc = N_("Restore Windows");
o->longDesc = N_("Restore windows to original position before tiling.");
o->type = CompOptionTypeAction;
o->value.action.initiate = tileRestore;
o->value.action.terminate = 0;
o->value.action.bell = FALSE;
o->value.action.edgeMask = 0;
o->value.action.type = CompBindingTypeKey;
o->value.action.state = CompActionStateInitKey;
o->value.action.key.modifiers = TILE_RESTORE_DISPLAY_OPTION_INITIATE_MOD;
o->value.action.key.keysym =
XStringToKeysym(TILE_RESTORE_DISPLAY_OPTION_INITIATE_KEY);
o = &td->opt[TILE_DISPLAY_OPTION_TOGGLE];
o->name = "tile_toggle";
o->group = N_("Key Bindings");
o->subGroup = N_("Toggle tiling");
o->displayHints = "";
o->shortDesc = N_("Toggle windows tiling");
o->longDesc = N_("Toggle between tiling and restoring.");
o->type = CompOptionTypeAction;
o->value.action.initiate = tileToggle;
o->value.action.terminate = 0;
o->value.action.bell = FALSE;
o->value.action.edgeMask = 0;
o->value.action.type = CompBindingTypeKey;
o->value.action.state = CompActionStateInitEdge;
o->value.action.state |= CompActionStateInitKey;
o->value.action.key.modifiers = TILE_TOGGLE_DISPLAY_OPTION_INITIATE_MOD;
o->value.action.key.keysym =
XStringToKeysym(TILE_TOGGLE_DISPLAY_OPTION_INITIATE_KEY);
o = &td->opt[TILE_DISPLAY_OPTION_ANIMATE];
o->advanced = False;
o->name = "tile_animate";
o->group = N_("Options");
o->subGroup = N_("Animation");
o->displayHints = "";
o->shortDesc = N_("Animate windows when tiling");
o->longDesc = N_("Adds animation to tiling process.");
o->type = CompOptionTypeBool;
o->value.b = TILE_DEFAULT_ANIMATE;
o = &td->opt[TILE_DISPLAY_OPTION_TOGGLE_TYPE];
o->advanced = False;
o->name = "tile_toggle_type";
o->group = N_("Options");
o->subGroup = N_("");
o->displayHints = "";
o->shortDesc = N_("Tiling method used when toggling");
o->longDesc = N_("Choose tiling type you want when using toggle.");
o->type = CompOptionTypeString;
o->value.s = strdup(tileTypeString[TILE_DEFAULT_TOGGLE_TYPE]);
o->rest.s.string = tileTypeString;
o->rest.s.nString = TILE_TYPE_NUM;
o = &td->opt[TILE_DISPLAY_OPTION_ANIMATION_TYPE];
o->advanced = False;
o->name = "tile_animation_type";
o->group = N_("Options");
o->subGroup = N_("Animation");
o->displayHints = "";
o->shortDesc = N_("Animation type");
o->longDesc = N_("Select desired animation on tiling.");
o->type = CompOptionTypeString;
o->value.s = strdup(animationTypeString[TILE_DEFAULT_ANIMATION]);
o->rest.s.string = animationTypeString;
o->rest.s.nString = NUM_ANIMATIONS;
o = &td->opt[TILE_DISPLAY_OPTION_ANIMATION_DURATION];
o->advanced = False;
o->name = "tile_animation_duration";
o->group = N_("Options");
o->subGroup = N_("Animation");
o->displayHints = "";
o->shortDesc = N_("Animation Duration");
o->longDesc = N_("Duration in millisecond the window resizing animation should take.");
o->type = CompOptionTypeInt;
o->value.i = TILE_ANIMATION_DURATION_DEFAULT;
o->rest.i.min = TILE_ANIMATION_DURATION_MIN;
o->rest.i.max = TILE_ANIMATION_DURATION_MAX;
o = &td->opt[TILE_DISPLAY_OPTION_JOIN];
o->advanced = False;
o->name = "tile_join";
o->group = N_("Options");
o->subGroup = N_("");
o->displayHints = "";
o->shortDesc = N_("Join windows together (experimental !)");
o->longDesc = N_("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 pwn your windows if you dont leave them enough space (will be fixed later).");
o->type = CompOptionTypeBool;
o->value.b = TILE_DEFAULT_JOIN;
o = &td->opt[TILE_DISPLAY_OPTION_DELTA];
o->advanced = False;
o->name = "tile_delta";
o->group = N_("Options");
o->subGroup = N_("");
o->displayHints = "";
o->shortDesc = N_("Cascade Delta");
o->longDesc = N_("Distance between windows when using cascade.");
o->type = CompOptionTypeInt;
o->value.i = TILE_DELTA_DEFAULT;
o->rest.i.min = TILE_DELTA_MIN;
o->rest.i.max = TILE_DELTA_MAX;
o = &td->opt[TILE_DISPLAY_OPTION_LEFT_OCCUPANCY];
o->advanced = False;
o->name = "tile_left_occupancy";
o->group = N_("Options");
o->subGroup = N_("");
o->displayHints = "";
o->shortDesc = N_("Left Occupancy");
o->longDesc = N_("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.");
o->type = CompOptionTypeInt;
o->value.i = TILE_LEFT_OCCUPANCY_DEFAULT;
o->rest.i.min = TILE_LEFT_OCCUPANCY_MIN;
o->rest.i.max = TILE_LEFT_OCCUPANCY_MAX;
o = &td->opt[TILE_DISPLAY_OPTION_EXCLUDE_LIST];
o->advanced = False;
o->name = "exclude_list";
o->group = N_("Options");
o->subGroup = N_("Exclusions");
o->displayHints = "";
o->shortDesc = N_("WM_CLASS to exclude");
o->longDesc = N_("Window classes which should not be tiled.");
o->type = CompOptionTypeList;
o->value.list.type = CompOptionTypeString;
o->value.list.nValue = 0;
o->value.list.value = 0;
o->rest.s.string = 0;
o->rest.s.nString = 0;
}
static CompOption *tileGetDisplayOptions(CompDisplay * display, int *count)
{
if (display) {
TILE_DISPLAY(display);
*count = NUM_OPTIONS(td);
return td->opt;
} else {
TileDisplay *td = malloc(sizeof(TileDisplay));
tileDisplayInitOptions(td);
*count = NUM_OPTIONS(td);
return td->opt;
}
}
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;
}
tileDisplayInitOptions(td);
td->animationDuration = 0;
td->currentAnimationType = TILE_DEFAULT_ANIMATION;
td->currentToggleType = TILE_DEFAULT_TOGGLE_TYPE;
//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->isOtherAnimationAtom = IPCS_GetAtom(IPCS_OBJECT(w), IPCS_BOOL, "IS_ANIMATED", True); // animation plugin sets is_animated to true while it animates the window
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 > 0 && tw->originalHeight > 0)
{
// 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);
}
CompPluginVTable tileVTable = {
"tile",
N_("Tile"),
N_("Tile windows"),
tileInit,
tileFini,
tileInitDisplay,
tileFiniDisplay,
tileInitScreen,
tileFiniScreen,
tileInitWindow,
tileFiniWindow,
tileGetDisplayOptions,
tileSetDisplayOption,
0, /*tileGetScreenOptions */
0, /*tileSetScreenOption */
0,
0,
0,
0,
BERYL_ABI_INFO,
"beryl-plugins-unsupported"
#if BERYL_VERSION >= 35
,"wm"
#endif
#if BERYL_VERSION >= 36
,0,0
#endif
#if BERYL_VERSION >= 37
,False
#endif
};
CompPluginVTable *getCompPluginInfo(void)
{
return &tileVTable;
}
syntax highlighted by Code2HTML, v. 0.9.1