Logo Search packages:      
Sourcecode: beryl-plugins-unsupported version File versions

tile.c

/**
 *
 * 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;
}

Generated by  Doxygen 1.6.0   Back to index