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

scale.c

/*
 * Copyright © 2005 Novell, Inc.
 *
 * 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 * Author: David Reveman <davidr@novell.com>
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <sys/time.h>

#include <X11/Xatom.h>
#include <X11/cursorfont.h>

#include <beryl.h>

#define WIN_X(w) ((w)->attrib.x - (w)->input.left)
#define WIN_Y(w) ((w)->attrib.y - (w)->input.top)
#define WIN_W(w) ((w)->width + (w)->input.left + (w)->input.right)
#define WIN_H(w) ((w)->height + (w)->input.top + (w)->input.bottom)

#define SCALE_SPACING_DEFAULT 25
#define SCALE_SPACING_MIN     0
#define SCALE_SPACING_MAX     250

#define SCALE_SLOPPY_FOCUS_DEFAULT TRUE

#define SCALE_INITIATE_CURRENT_HEAD_KEY_DEFAULT "F9"
#define SCALE_INITIATE_CURRENT_HEAD_MODIFIERS_DEFAULT 0

#define SCALE_INITIATE_KEY_DEFAULT       "Pause"
#define SCALE_INITIATE_MODIFIERS_DEFAULT CompSuperMask

#define SCALE_INITIATE_APP_KEY_DEFAULT       "F7"
#define SCALE_INITIATE_APP_MODIFIERS_DEFAULT CompSuperMask

#define SCALE_INITIATE_ALL_KEY_DEFAULT       "F8"
#define SCALE_INITIATE_ALL_MODIFIERS_DEFAULT 0

#define SCALE_SPEED_DEFAULT   2.5f
#define SCALE_SPEED_MIN       0.1f
#define SCALE_SPEED_MAX       50.0f
#define SCALE_SPEED_PRECISION 0.1f

#define SCALE_HOVER_TIME_DEFAULT 1000
#define SCALE_HOVER_TIME_MIN   50
#define SCALE_HOVER_TIME_MAX     10000

#define SCALE_DARKEN_BACK_FACTOR_DEFAULT   0.67f
#define SCALE_DARKEN_BACK_FACTOR_MIN       0.00f
#define SCALE_DARKEN_BACK_FACTOR_MAX       1.00f
#define SCALE_DARKEN_BACK_FACTOR_PRECISION 0.01f

#define SCALE_TIMESTEP_DEFAULT   1.1f
#define SCALE_TIMESTEP_MIN       0.1f
#define SCALE_TIMESTEP_MAX       50.0f
#define SCALE_TIMESTEP_PRECISION 0.1f

#define SCALE_STATE_NONE 0
#define SCALE_STATE_OUT  1
#define SCALE_STATE_WAIT 2
#define SCALE_STATE_IN   3

#define SCALE_DARKEN_BACK_DEFAULT TRUE

#define SCALE_OPACITY_DEFAULT 75
#define SCALE_OPACITY_MIN     0
#define SCALE_OPACITY_MAX     100

#define SCALE_USE_CLASS_DEFAULT TRUE

#define SCALE_HEAD_DEFAULT 0
#define SCALE_HEAD_MIN     0
#define SCALE_HEAD_MAX     10

typedef enum _MultiMonitorMode
{
      AllHeads,
      Selected,
      Current
} MultiMonitorMode;

char *multiMonitorModes[] = {
      N_("On all monitors"),
      N_("On selected monitor"),
      N_("On current monitor")
};

#define MULTIMONITOR_MODE_DEFAULT AllHeads
#define NUM_MULTIMONITOR_MODES 3

#define SCALE_ORGANIC_DEFAULT FALSE

#define SCALE_SHOW_MINIMIZED_DEFAULT FALSE

static char *winType[] = {
      N_("Utility"),
      N_("Dialog"),
      N_("ModalDialog"),
      N_("Fullscreen"),
      N_("Normal")
};

#define N_WIN_TYPE (sizeof (winType) / sizeof (winType[0]))

typedef enum
{
      ScaleMethodStandard = 0,
      ScaleMethodEnhanced,
      ScaleMethodOrganic
} ScaleMethod;

static char *scaleMethodString[] = {
      N_("Standard"),
      N_("Enhanced"),
      N_("Organic")
};

static ScaleMethod scaleMethod[] = {
      ScaleMethodStandard,
      ScaleMethodEnhanced,
      ScaleMethodOrganic
};

#define N_SCALE_METHODS (sizeof (scaleMethodString) / sizeof (scaleMethodString[0]))
#define SCALE_METHOD_DEFAULT (scaleMethodString[1])
#define SCALE_METHOD_INT_DEFAULT ScaleMethodEnhanced

typedef enum
{
      ScaleIconNone = 0,
      ScaleIconEmblem,
      ScaleIconBig
} IconOverlay;

static char *iconOverlayString[] = {
      N_("None"),
      N_("Emblem"),
      N_("Big")
};

static IconOverlay iconOverlay[] = {
      ScaleIconNone,
      ScaleIconEmblem,
      ScaleIconBig
};

#define N_ICON_TYPE (sizeof (iconOverlayString) / sizeof (iconOverlayString[0]))
#define SCALE_ICON_DEFAULT (iconOverlayString[1])

#define SCALE_ALLOW_KEYBOARD_INPUT_DEFAULT FALSE

static int displayPrivateIndex;

typedef struct _ScaleSlot
{
      int x1, y1, x2, y2;
      int line;
      int filled;
      float scale;
} ScaleSlot;

typedef struct _SlotArea
{
      int nWindows;
      XRectangle workArea;
} SlotArea;

#define SCALE_DISPLAY_OPTION_INITIATE_NORMAL        0
#define SCALE_DISPLAY_OPTION_INITIATE_APP           1
#define SCALE_DISPLAY_OPTION_INITIATE_ALL           2
#define SCALE_DISPLAY_OPTION_INITIATE_CURRENT_HEAD  3
#define SCALE_DISPLAY_OPTION_NUM                    4

typedef struct _ScaleDisplay
{
      int screenPrivateIndex;
      HandleEventProc handleEvent;

      CompOption opt[SCALE_DISPLAY_OPTION_NUM];

      unsigned int lastActiveNum;
      KeyCode leftKeyCode, rightKeyCode, upKeyCode, downKeyCode;
} ScaleDisplay;

#define SCALE_SCREEN_OPTION_SPACING                  0
#define SCALE_SCREEN_OPTION_SLOPPY_FOCUS             1
#define SCALE_SCREEN_OPTION_ICON                     2
#define SCALE_SCREEN_OPTION_SPEED                    3
#define SCALE_SCREEN_OPTION_TIMESTEP                 4
#define SCALE_SCREEN_OPTION_WINDOW_TYPE              5
#define SCALE_SCREEN_OPTION_DARKEN_BACK              6
#define SCALE_SCREEN_OPTION_OPACITY                  7
#define SCALE_SCREEN_OPTION_USE_CLASS                8
#define SCALE_SCREEN_OPTION_DARKEN_BACK_FACTOR       9
#define SCALE_SCREEN_OPTION_MULTIMONITOR             10
#define SCALE_SCREEN_OPTION_HEAD                     11
#define SCALE_SCREEN_OPTION_SCALE_METHOD             12
#define SCALE_SCREEN_OPTION_ALLOW_KEYBOARD_INPUT     13
#define SCALE_SCREEN_OPTION_SHOW_MINIMIZED           14
#define SCALE_SCREEN_OPTION_HOVER_TIME               15
#define SCALE_SCREEN_OPTION_NUM                      16


typedef struct _ScaleScreen
{
      int windowPrivateIndex;

      PreparePaintScreenProc preparePaintScreen;
      DonePaintScreenProc donePaintScreen;
      PaintScreenProc paintScreen;
      PaintWindowProc paintWindow;
      DamageWindowRectProc damageWindowRect;

      CompOption opt[SCALE_SCREEN_OPTION_NUM];

      int spacing;

      float speed;
      float timestep;
      float darkenBackFactor;

      unsigned int wMask;

      Bool grab;
      int grabIndex;

      Window dndTarget;
      CompTimeoutHandle hoverHandle;

      int state;
      int moreAdjust;

      Cursor cursor;

      ScaleSlot *slots;
      int slotsSize;
      int nSlots;

      int *line;
      int lineSize;
      int nLine;

      /* only used for sorting */
      CompWindow **windows;
      int windowsSize;
      int nWindows;

      GLfloat scale;

      Bool darkenBack;
      GLushort opacity;

      Bool currentHead;
      Bool allScreensMode;
      Bool onlyCurrent;
      Bool useClass;
      CompWindow *currentWindow;
      CompWindow *hoveredWindow;
      Window selectedWindow;
      Window cancelWindow;

      int head;
      int currentOutputDev;

      ScaleMethod scaleMethod;
      IconOverlay iconOverlay;
      Bool clicked;

      MultiMonitorMode mmMode;

      //helper for interaction with rotate
      int scaleStateAtom;
} ScaleScreen;

typedef struct _ScaleWindow
{
      ScaleSlot *slot;

      int sid;
      int distance;

      Bool rescaled;
      Bool wasMinimized;
      GLfloat oldScale;
      int workspace;
      int origX, origY;

      GLfloat xVelocity, yVelocity, scaleVelocity;
      GLfloat scale;
      GLfloat tx, ty;
      float delta;
      Bool adjust;

      int animationAtom;
} ScaleWindow;


#define GET_SCALE_DISPLAY(d)                      \
    ((ScaleDisplay *) (d)->privates[displayPrivateIndex].ptr)

#define SCALE_DISPLAY(d)             \
    ScaleDisplay *sd = GET_SCALE_DISPLAY (d)

#define GET_SCALE_SCREEN(s, sd)                      \
    ((ScaleScreen *) (s)->privates[(sd)->screenPrivateIndex].ptr)

#define SCALE_SCREEN(s)                               \
    ScaleScreen *ss = GET_SCALE_SCREEN (s, GET_SCALE_DISPLAY (s->display))

#define GET_SCALE_WINDOW(w, ss)                      \
    ((ScaleWindow *) (w)->privates[(ss)->windowPrivateIndex].ptr)

#define SCALE_WINDOW(w)                           \
    ScaleWindow *sw = GET_SCALE_WINDOW  (w,               \
            GET_SCALE_SCREEN  (w->screen,           \
                GET_SCALE_DISPLAY (w->screen->display)))

#define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption))

static Bool scaleSetHead(ScaleScreen * ss, CompScreen * s, int head)
{
      if (s->nOutputDev == 1)
      {
            if (head > 0)
                  return FALSE;
            else
                  ss->head = 0;
      }
      else
      {
            if (head == -1)
                  ss->head = -1;
            else if (head > s->nOutputDev)
                  ss->head = -1;
            else
                  ss->head = head;
      }

      return TRUE;
}

static Bool
scaleSetScreenOption(CompScreen * screen, char *name, CompOptionValue * value)
{
      CompOption *o;
      int index;

      SCALE_SCREEN(screen);

      o = compFindOption(ss->opt, NUM_OPTIONS(ss), name, &index);

      if (!o)
            return FALSE;

      switch (index)
      {
      case SCALE_SCREEN_OPTION_SPACING:
            if (compSetIntOption(o, value))
            {
                  ss->spacing = o->value.i;
                  return TRUE;
            }
            break;
      case SCALE_SCREEN_OPTION_SLOPPY_FOCUS:
            if (compSetBoolOption(o, value))
                  return TRUE;
            break;
      case SCALE_SCREEN_OPTION_SCALE_METHOD:
            if (compSetStringOption(o, value))
            {
                  int i;

                  for (i = 0; i < N_SCALE_METHODS; i++)
                  {
                        if (strcmp(o->value.s, scaleMethodString[i]) == 0)
                        {
                              ss->scaleMethod = scaleMethod[i];
                              return TRUE;
                        }
                  }
            }
            break;
      case SCALE_SCREEN_OPTION_SPEED:
            if (compSetFloatOption(o, value))
            {
                  ss->speed = o->value.f;
                  return TRUE;
            }
            break;
      case SCALE_SCREEN_OPTION_DARKEN_BACK_FACTOR:
            if (compSetFloatOption(o, value))
            {
                  ss->darkenBackFactor = o->value.f;
                  return TRUE;
            }
            break;
      case SCALE_SCREEN_OPTION_TIMESTEP:
            if (compSetFloatOption(o, value))
            {
                  ss->timestep = o->value.f;
                  return TRUE;
            }
            break;
      case SCALE_SCREEN_OPTION_WINDOW_TYPE:
            if (compSetOptionList(o, value))
            {
                  ss->wMask = compWindowTypeMaskFromStringList(&o->value);
                  return TRUE;
            }
            break;
      case SCALE_SCREEN_OPTION_DARKEN_BACK:
            if (compSetBoolOption(o, value))
            {
                  ss->darkenBack = o->value.b;
                  return TRUE;
            }
            break;
      case SCALE_SCREEN_OPTION_USE_CLASS:
            if (compSetBoolOption(o, value))
            {
                  ss->useClass = o->value.b;
                  return TRUE;
            }
      case SCALE_SCREEN_OPTION_OPACITY:
            if (compSetIntOption(o, value))
            {
                  ss->opacity = (OPAQUE * o->value.i) / 100;
                  return TRUE;
            }
            break;
      case SCALE_SCREEN_OPTION_ICON:
            if (compSetStringOption(o, value))
            {
                  int i;

                  for (i = 0; i < N_ICON_TYPE; i++)
                  {
                        if (strcmp(o->value.s, iconOverlayString[i]) == 0)
                        {
                              ss->iconOverlay = iconOverlay[i];
                              return TRUE;
                        }
                  }
            }
            break;
      case SCALE_SCREEN_OPTION_HEAD:
            if (compSetIntOption(o, value))
            {
                  return scaleSetHead(ss, screen, o->value.i);
            }
            break;
      case SCALE_SCREEN_OPTION_HOVER_TIME:
            if (compSetIntOption(o, value))
                  return TRUE;
            break;
      case SCALE_SCREEN_OPTION_ALLOW_KEYBOARD_INPUT:
      case SCALE_SCREEN_OPTION_SHOW_MINIMIZED:
            if (compSetBoolOption(o, value))
                  return TRUE;
            break;

      case SCALE_SCREEN_OPTION_MULTIMONITOR:
            if (compSetStringOption(o, value))
            {
                  int i;
                  MultiMonitorMode mode = MULTIMONITOR_MODE_DEFAULT;

                  for (i = 0; i < o->rest.s.nString; i++)
                        if (strcmp(multiMonitorModes[i], o->value.s) == 0)
                              mode = (MultiMonitorMode) i;
                  ss->mmMode = mode;
                  return TRUE;
            }
            break;
      default:
            break;
      }

      return FALSE;
}

static void scaleScreenInitOptions(ScaleScreen * ss)
{
      CompOption *o;
      int i;

      o = &ss->opt[SCALE_SCREEN_OPTION_SPACING];
      o->advanced = False;
      o->name = "spacing";
      o->group = N_("Appearance");
      o->subGroup = N_("");
      o->displayHints = "";
      o->shortDesc = N_("Window Spacing");
      o->longDesc = N_("Space between Scaled Windows.");
      o->type = CompOptionTypeInt;
      o->value.i = SCALE_SPACING_DEFAULT;
      o->rest.i.min = SCALE_SPACING_MIN;
      o->rest.i.max = SCALE_SPACING_MAX;

      o = &ss->opt[SCALE_SCREEN_OPTION_SLOPPY_FOCUS];
      o->advanced = False;
      o->name = "sloppy_focus";
      o->group = N_("Behaviour");
      o->subGroup = N_("");
      o->displayHints = "";
      o->shortDesc = N_("Sloppy Focus");
      o->longDesc = N_("Focus follows mouse pointer in scaled mode.");
      o->type = CompOptionTypeBool;
      o->value.b = SCALE_SLOPPY_FOCUS_DEFAULT;

      o = &ss->opt[SCALE_SCREEN_OPTION_SPEED];
      o->advanced = False;
      o->name = "speed";
      o->group = N_("Misc. Options");
      o->subGroup = N_("");
      o->displayHints = "";
      o->shortDesc = N_("Scale Speed");
      o->longDesc = N_("Scale Speed.");
      o->type = CompOptionTypeFloat;
      o->value.f = SCALE_SPEED_DEFAULT;
      o->rest.f.min = SCALE_SPEED_MIN;
      o->rest.f.max = SCALE_SPEED_MAX;
      o->rest.f.precision = SCALE_SPEED_PRECISION;

      o = &ss->opt[SCALE_SCREEN_OPTION_DARKEN_BACK_FACTOR];
      o->advanced = False;
      o->name = "darken_back_factor";
      o->group = N_("Appearance");
      o->subGroup = N_("");
      o->displayHints = "";
      o->shortDesc = N_("Darken Background");
      o->longDesc = N_("Set how Dark the Background will become (0.0 to 1.0).");
      o->type = CompOptionTypeFloat;
      o->value.f = SCALE_DARKEN_BACK_FACTOR_DEFAULT;
      o->rest.f.min = SCALE_DARKEN_BACK_FACTOR_MIN;
      o->rest.f.max = SCALE_DARKEN_BACK_FACTOR_MAX;
      o->rest.f.precision = SCALE_DARKEN_BACK_FACTOR_PRECISION;

      o = &ss->opt[SCALE_SCREEN_OPTION_TIMESTEP];
      o->advanced = False;
      o->name = "timestep";
      o->group = N_("Misc. Options");
      o->subGroup = N_("");
      o->displayHints = "";
      o->shortDesc = N_("Scale Timestep");
      o->longDesc = N_("Scale Timestep.");
      o->type = CompOptionTypeFloat;
      o->value.f = SCALE_TIMESTEP_DEFAULT;
      o->rest.f.min = SCALE_TIMESTEP_MIN;
      o->rest.f.max = SCALE_TIMESTEP_MAX;
      o->rest.f.precision = SCALE_TIMESTEP_PRECISION;

      o = &ss->opt[SCALE_SCREEN_OPTION_WINDOW_TYPE];
      o->advanced = False;
      o->name = "window_types";
      o->group = N_("Behaviour");
      o->subGroup = N_("");
      o->displayHints = "";
      o->shortDesc = N_("Window Types");
      o->longDesc = N_("Window Types that should Scaled in Scale mode.");
      o->type = CompOptionTypeList;
      o->value.list.type = CompOptionTypeString;
      o->value.list.nValue = N_WIN_TYPE;
      o->value.list.value = malloc(sizeof(CompOptionValue) * N_WIN_TYPE);
      for (i = 0; i < N_WIN_TYPE; i++)
            o->value.list.value[i].s = strdup(winType[i]);
      o->rest.s.string = (char **)windowTypeString;
      o->rest.s.nString = nWindowTypeString;

      ss->wMask = compWindowTypeMaskFromStringList(&o->value);

      o = &ss->opt[SCALE_SCREEN_OPTION_DARKEN_BACK];
      o->advanced = False;
      o->name = "darken_back";
      o->group = N_("Appearance");
      o->subGroup = N_("");
      o->displayHints = "";
      o->shortDesc = N_("Darken Background");
      o->longDesc =
                  N_
                  ("Darken Background when Scaling windows, to make windows stand out more.");
      o->type = CompOptionTypeBool;
      o->value.b = SCALE_DARKEN_BACK_DEFAULT;

      o = &ss->opt[SCALE_SCREEN_OPTION_OPACITY];
      o->advanced = False;
      o->name = "opacity";
      o->group = N_("Appearance");
      o->subGroup = N_("");
      o->displayHints = "";
      o->shortDesc = N_("Opacity");
      o->longDesc = N_("Amount of Opacity in percent.");
      o->type = CompOptionTypeInt;
      o->value.i = SCALE_OPACITY_DEFAULT;
      o->rest.i.min = SCALE_OPACITY_MIN;
      o->rest.i.max = SCALE_OPACITY_MAX;

      o = &ss->opt[SCALE_SCREEN_OPTION_USE_CLASS];
      o->advanced = False;
      o->name = "use_class";
      o->group = N_("Misc. Options");
      o->subGroup = N_("");
      o->displayHints = "";
      o->shortDesc = N_("Use WM_CLASS for Only-Current");
      o->longDesc =
                  N_
                  ("Use WM_CLASS to find out whether a window should be Scaled in Only-Current.");
      o->type = CompOptionTypeBool;
      o->value.b = SCALE_USE_CLASS_DEFAULT;

      o = &ss->opt[SCALE_SCREEN_OPTION_MULTIMONITOR];
      o->advanced = False;
      o->name = "multimonitor_mode";
      o->group = N_("Behaviour");
      o->subGroup = N_("");
      o->displayHints = "";
      o->shortDesc = N_("MultiMonitor Mode");
      o->longDesc = N_("Multi Monitor Mode behavior.");
      o->type = CompOptionTypeString;
      o->value.s = strdup(multiMonitorModes[MULTIMONITOR_MODE_DEFAULT]);
      o->rest.s.string = multiMonitorModes;
      o->rest.s.nString = NUM_MULTIMONITOR_MODES;

      o = &ss->opt[SCALE_SCREEN_OPTION_HEAD];
      o->advanced = False;
      o->name = "head";
      o->group = N_("Behaviour");
      o->subGroup = N_("");
      o->displayHints = "";
      o->shortDesc = N_("Head");
      o->longDesc = N_("Head on Which windows are Scaled.");
      o->type = CompOptionTypeInt;
      o->value.i = SCALE_HEAD_DEFAULT;
      o->rest.i.min = SCALE_HEAD_MIN;
      o->rest.i.max = SCALE_HEAD_MAX;

      o = &ss->opt[SCALE_SCREEN_OPTION_ICON];
      o->advanced = False;
      o->name = "overlay_icon";
      o->group = N_("Appearance");
      o->subGroup = N_("");
      o->displayHints = "";
      o->shortDesc = N_("Overlay Icon");
      o->longDesc = N_("Overlay an Icon on windows once they are Scaled.");
      o->type = CompOptionTypeString;
      o->value.s = strdup(SCALE_ICON_DEFAULT);
      o->rest.s.string = iconOverlayString;
      o->rest.s.nString = N_ICON_TYPE;

      o = &ss->opt[SCALE_SCREEN_OPTION_SCALE_METHOD];
      o->advanced = False;
      o->name = "scale_method";
      o->group = N_("Behaviour");
      o->subGroup = N_("");
      o->displayHints = "";
      o->shortDesc = N_("Scaling Method");
      o->longDesc = N_("Algorithms for window Placement.");
      o->type = CompOptionTypeString;
      o->value.s = strdup(SCALE_METHOD_DEFAULT);
      o->rest.s.string = scaleMethodString;
      o->rest.s.nString = N_SCALE_METHODS;

      o = &ss->opt[SCALE_SCREEN_OPTION_ALLOW_KEYBOARD_INPUT];
      o->advanced = False;
      o->name = "allow_keyboard_input";
      o->group = N_("Behaviour");
      o->subGroup = N_("");
      o->displayHints = "";
      o->shortDesc = N_("Allow Keyboard Input");
      o->longDesc = N_("Allow Keyboard Input when Scaled.");
      o->type = CompOptionTypeBool;
      o->value.b = SCALE_ALLOW_KEYBOARD_INPUT_DEFAULT;

      o = &ss->opt[SCALE_SCREEN_OPTION_SHOW_MINIMIZED];
      o->advanced = False;
      o->name = "show_minimized";
      o->group = N_("Behaviour");
      o->subGroup = N_("");
      o->displayHints = "";
      o->shortDesc = N_("Show Minimized Windows");
      o->longDesc = N_("Show Minimized Windows.");
      o->type = CompOptionTypeBool;
      o->value.b = SCALE_SHOW_MINIMIZED_DEFAULT;

      o = &ss->opt[SCALE_SCREEN_OPTION_HOVER_TIME];
      o->advanced = False;
      o->name = "hover_time";
      o->group = N_("Misc. Options");
      o->subGroup = N_("");
      o->displayHints = "";
      o->shortDesc = N_("Hover Time");
      o->longDesc =
                  N_("Time (in ms) before Scale mode is terminated when"
                     "Hovering over a window.");
      o->type = CompOptionTypeInt;
      o->value.i = SCALE_HOVER_TIME_DEFAULT;
      o->rest.i.min = SCALE_HOVER_TIME_MIN;
      o->rest.i.max = SCALE_HOVER_TIME_MAX;
}

static CompOption *scaleGetScreenOptions(CompScreen * screen, int *count)
{
      if (screen)
      {
            SCALE_SCREEN(screen);

            *count = NUM_OPTIONS(ss);
            return ss->opt;
      }
      else
      {
            ScaleScreen *ss = malloc(sizeof(ScaleScreen));

            scaleScreenInitOptions(ss);
            *count = NUM_OPTIONS(ss);
            return ss->opt;
      }
}


static void gotoViewport(CompScreen * s, int x)
{
      SCALE_SCREEN(s);
      if (!ss->selectedWindow)
            return;
      ss->selectedWindow = 0;
      XEvent xev;

      xev.xclient.type = ClientMessage;
      xev.xclient.display = s->display->display;
      xev.xclient.format = 32;

      xev.xclient.message_type = s->display->desktopViewportAtom;
      xev.xclient.window = s->root;

      xev.xclient.data.l[0] = x * s->width;
      xev.xclient.data.l[1] = 0;
      xev.xclient.data.l[2] = 0;
      xev.xclient.data.l[3] = 0;
      xev.xclient.data.l[4] = 0;

      XSendEvent(s->display->display,
                     s->root,
                     FALSE,
                     SubstructureRedirectMask | SubstructureNotifyMask, &xev);

}
static void setWinPort(CompWindow * w)
{
      int x, y;

      SCALE_WINDOW(w);
      //FIXME - only works with x viewports
      defaultViewportForWindow(w, &x, &y);
      sw->workspace = x;
}

static Bool isNeverScaleWin(CompWindow * w)
{
      if (w->attrib.override_redirect)
            return TRUE;

      if (w->state & CompWindowStateOffscreenMask)
            return TRUE;

      if (w->wmType & (CompWindowTypeDockMask | CompWindowTypeDesktopMask))
            return TRUE;

      return FALSE;
}

static Bool isScaleWin(CompWindow * w)
{
      SCALE_SCREEN(w->screen);

      if (isNeverScaleWin(w))
            return FALSE;

      if (!ss->allScreensMode && ss->currentHead
            && !ss->onlyCurrent
            && screenGetOutputDevForWindow(w) != ss->currentOutputDev)
      {
            return FALSE;
      }

      if (ss->allScreensMode)
      {
            if ((!w->mapNum || w->attrib.map_state != IsViewable))
                  // && ((!w->minimized && !(w->state & CompWindowStateHiddenMask))))
                  if (!w->minimized
                        || !ss->opt[SCALE_SCREEN_OPTION_SHOW_MINIMIZED].value.b)
                        return FALSE;
      }
      else
      {
            int x, y;

            defaultViewportForWindow(w, &x, &y);
            if (x != w->screen->x)
                  return FALSE;
            if ((!(*w->screen->focusWindow) (w))
                  && (!w->minimized
                        || !ss->opt[SCALE_SCREEN_OPTION_SHOW_MINIMIZED].value.b))
                  return FALSE;
      }

      if (w->state & CompWindowStateShadedMask)
            return FALSE;

      if (!w->mapNum || w->attrib.map_state != IsViewable)
            if (!w->minimized
                  || !ss->opt[SCALE_SCREEN_OPTION_SHOW_MINIMIZED].value.b)
                  return FALSE;

      if (!(ss->wMask & w->type))
            return FALSE;

      if (w->state & CompWindowStateSkipPagerMask)
            return FALSE;

      if (ss->onlyCurrent && ss->currentWindow)
      {
            if (!ss->useClass
                  && (w->clientLeader != ss->currentWindow->clientLeader))
                  return FALSE;
            if (ss->useClass
                  && (strcmp(w->resClass, ss->currentWindow->resClass) != 0))
                  return FALSE;
      }

      if (!(ss->currentWindow))
            return TRUE;

      if (ss->onlyCurrent && ss->currentWindow != w)
            restackWindowBelow(w, ss->currentWindow);

      return TRUE;
}

static Bool
isInWorkArea(CompWindow *w, int output)
{
      XRectangle workArea;
      screenGetOutputDevWorkArea( w->screen, output, &workArea);
      int wx1 = workArea.x;
      int wy1 = workArea.y;
      int wx2 = wx1 + workArea.width;
      int wy2 = wy1 + workArea.height;
      int x1 = WIN_X(w);
      int y1 = WIN_Y(w);
      int x2 = x1 + WIN_W(w);
      int y2 = y1 + WIN_H(w);

      if (x1 >= wx1 && x1 <= wx2)
      {
            if (y1 >= wy1 && y1 <= wy2)
                  return TRUE;
            if (y2 >= wy1 && y2 <= wy2)
                  return TRUE;
      }
      if (x2 >= wx1 && x2 <= wx2)
      {
            if (y1 >= wy1 && y1 <= wy2)
                  return TRUE;
            if (y2 >= wy1 && y2 <= wy2)
                  return TRUE;
      }

      if (x1 <= wx1 && x2 >= wx1)
      {
            if (y1 >= wy1 && y1 <= wy2)
                  return TRUE;
            if (y2 >= wy1 && y2 <= wy2)
                  return TRUE;
            if (y1 <= wy1 && y2 >= wy1)
                  return TRUE;
      }

      if (y1 <= wy1 && y2 >= wy1)
      {
            if (x1 >= wx1 && x1 <= wx2)
                  return TRUE;
            if (x2 >= wx1 && x2 <= wx2)
                  return TRUE;
      }

      return FALSE;
}

static Bool
scalePaintWindow(CompWindow * w,
                         const WindowPaintAttrib * attrib,
                         Region region, unsigned int mask)
{
      CompScreen *s = w->screen;
      Bool status;

      SCALE_SCREEN(s);

      if (ss->grab)
      {
            WindowPaintAttrib sAttrib = *attrib;
            Bool scaled = FALSE;

            SCALE_WINDOW(w);

            if (sw->adjust || sw->slot)
            {
                  if (w->id != ss->selectedWindow &&
                        ss->opacity != OPAQUE && ss->state != SCALE_STATE_IN)
                  {
                        // hovered windows will be opaque, even with sloppy focus off
                        if (w == ss->hoveredWindow)
                              sAttrib.opacity = OPAQUE;
                        else
                              sAttrib.opacity = (sAttrib.opacity * ss->opacity) >> 16;
                  }

                  scaled = TRUE;

                  mask |= PAINT_WINDOW_NO_CORE_INSTANCE_MASK;
            }
            else if (ss->state != SCALE_STATE_IN)
            {
                  /* modify brightness of the other windows */
                  if (ss->darkenBack)
                        sAttrib.brightness =
                                    sAttrib.brightness * ss->darkenBackFactor;

                  /* make non-scaled windows invisible */
                  if (!(w->type & CompWindowTypeDesktopMask))
                  {
                        switch (ss->mmMode)
                        {
                              case Current:
                                    if (isInWorkArea(w,ss->currentOutputDev))
                                          sAttrib.opacity = 0;
                                    break;
                              case Selected:
                                    if (isInWorkArea(w,ss->head))
                                          sAttrib.opacity = 0;
                                    break;
                              case AllHeads:
                                    {
                                          Bool inside = FALSE;
                                          int i;
                                          for (i = 0; i < s->nOutputDev && !inside; i++)
                                                inside |= isInWorkArea(w,i);

                                          if (inside)
                                                sAttrib.opacity = 0;
                                    }
                                    break;
                        }
                  }
            }

            UNWRAP(ss, s, paintWindow);
            status = (*s->paintWindow) (w, &sAttrib, region, mask);
            WRAP(ss, s, paintWindow, scalePaintWindow);

            if (scaled)
            {
                  sAttrib.xScale = sw->scale;
                  sAttrib.yScale = sw->scale;
                  sAttrib.xTranslate = sw->tx;
                  sAttrib.yTranslate = sw->ty;

                  (*s->drawWindow) (w, &sAttrib, region,
                                            mask | PAINT_WINDOW_TRANSFORMED_MASK);
            }

            if ((ss->iconOverlay != ScaleIconNone) && scaled)
            {
                  CompIcon *icon;

                  icon = getWindowIcon(w, 96, 96);
                  if (!icon)
                        icon = getWindowIcon(w, 128, 128);
                  if (!icon)
                        icon = getWindowIcon(w, 256, 256);
                  if (!icon)
                        icon = getWindowIcon(w, 512, 512);

                  if (!icon)
                        icon = w->screen->defaultIcon;

                  if (icon
                        && (icon->texture.name || iconToTexture(w->screen, icon)))
                  {
                        REGION iconReg;
                        CompMatrix matrix;
                        int wx, wy, width, height;
                        int scaledWinWidth, scaledWinHeight;
                        float ds;

                        scaledWinWidth = w->width * sw->scale;
                        scaledWinHeight = w->height * sw->scale;

                        switch (ss->iconOverlay)
                        {
                        case ScaleIconNone:
                        case ScaleIconEmblem:
                              if (icon->width > 96)
                              {
                                    sAttrib.xScale = sAttrib.yScale =
                                                MIN((48.0f / (float)icon->width),
                                                      (48.0f / (float)icon->height));
                              }
                              else
                                    sAttrib.xScale = sAttrib.yScale = 1.0f;
                              break;
                        case ScaleIconBig:
                              sAttrib.opacity /= 3;
                              sAttrib.xScale = sAttrib.yScale =
                                          MIN(((float)scaledWinWidth /
                                                 (float)icon->width),
                                                ((float)scaledWinHeight /
                                                 (float)icon->height));
                        default:
                              break;
                        }

                        width = icon->width;
                        height = icon->height;

                        switch (ss->iconOverlay)
                        {
                        case ScaleIconNone:
                        case ScaleIconEmblem:
                              if (sAttrib.xScale != 1.0f)
                              {
                                    wx = w->attrib.x +
                                                ((w->width * sw->scale) -
                                                 (icon->width * sAttrib.xScale)) /
                                                sAttrib.xScale;
                                    wy = w->attrib.y +
                                                ((w->height * sw->scale) -
                                                 (icon->height * sAttrib.yScale)) /
                                                sAttrib.yScale;
                              }
                              else
                              {
                                    wx = w->attrib.x +
                                                (w->width * sw->scale) - icon->width;
                                    wy = w->attrib.y +
                                                (w->height * sw->scale) - icon->height;
                              }
                              break;
                        case ScaleIconBig:
                              wx = w->attrib.x +
                                          ((scaledWinWidth -
                                            width * sAttrib.xScale) / 2) / sAttrib.xScale;
                              wy = w->attrib.y +
                                          ((scaledWinHeight -
                                            height * sAttrib.yScale) / 2) / sAttrib.yScale;
                              break;
                        default:
                              wx = wy = 0;
                              break;
                        }

                        if (IPCS_GetBool(IPCS_OBJECT(w), sw->animationAtom))
                        {
                              /*wx-=w->attrib.x;
                                 wx/=sw->scale;
                                 wx+=w->attrib.x;
                                 wy-=w->attrib.y;
                                 wy/=sw->scale;
                                 wy+=w->attrib.y; */
                              sAttrib.xScale /= sw->scale;
                              sAttrib.yScale /= sw->scale;
                        }

                        switch (ss->scaleMethod)
                        {
                        case ScaleMethodEnhanced:
                              if (sw->slot)
                              {
                                    sw->delta =
                                                fabs(sw->slot->x1 - w->serverX) +
                                                fabs(sw->slot->y1 - w->serverY) +
                                                fabs(1.0f - sw->slot->scale) * 500.0f;
                              }


                              if (sw->delta)
                              {
                                    ds = fabs(sw->tx) + fabs(sw->ty) +
                                                fabs(1.0f - sw->scale) * 500.0f;

                                    if (ds > sw->delta)
                                          ds = sw->delta;

                                    sAttrib.opacity = (ds * sAttrib.opacity) / sw->delta;
                              }
                              break;
                        default:
                              ds = 1.0f - ss->scale;
                              if (ds)
                              {
                                    sAttrib.opacity =
                                                (fabs(1.0f - sw->scale) *
                                                 sAttrib.opacity) / ds;
                              }
                              else if (!sw->slot)
                              {
                                    sAttrib.opacity = 0;
                              }
                        }

                        mask |= PAINT_WINDOW_TRANSLUCENT_MASK |
                                    PAINT_WINDOW_TRANSFORMED_MASK;

                        iconReg.rects = &iconReg.extents;
                        iconReg.numRects = 1;

                        iconReg.extents.x1 = wx;
                        iconReg.extents.y1 = wy;
                        iconReg.extents.x2 = iconReg.extents.x1 + width;
                        iconReg.extents.y2 = iconReg.extents.y1 + height;

                        matrix = icon->texture.matrix;
                        matrix.x0 -= wx * icon->texture.matrix.xx;
                        matrix.y0 -= wy * icon->texture.matrix.yy;

                        w->vCount = 0;
                        if (iconReg.extents.x1 < iconReg.extents.x2
                              && iconReg.extents.y1 < iconReg.extents.y2)
                              (*w->screen->addWindowGeometry) (w,
                                                                               &matrix, 1, &iconReg,
                                                                               &iconReg);

                        if (w->vCount)
                              (*w->screen->drawWindowTexture) (w,
                                                                               &icon->texture, &sAttrib,
                                                                               mask);
                  }
            }
      }
      else
      {
            UNWRAP(ss, s, paintWindow);
            status = (*s->paintWindow) (w, attrib, region, mask);
            WRAP(ss, s, paintWindow, scalePaintWindow);
      }

      return status;

}


static int compareWindows(const void *elem1, const void *elem2)
{
      CompWindow *w1 = *((CompWindow **) elem1);
      CompWindow *w2 = *((CompWindow **) elem2);

      if (!(WIN_X(w1) - WIN_X(w2)))
            return WIN_Y(w1) - WIN_Y(w2);
      return WIN_X(w1) - WIN_X(w2);
}

/**
 * experimental organic layout method
 * inspired by smallwindows (smallwindows.sf.net) by Jens Egeblad
 * */
#define ORGANIC_STEP 0.05

static int organicCompareWindows(const void *elem1, const void *elem2)
{
      CompWindow *w1 = *((CompWindow **) elem1);
      CompWindow *w2 = *((CompWindow **) elem2);

      return (WIN_X(w1) + WIN_Y(w1)) - (WIN_X(w2) + WIN_Y(w2));
}


static double
layoutOrganicCalculateOverlap(CompScreen * s, int win, int x, int y)
{
      SCALE_SCREEN(s);
      int i;
      int x1 = x;
      int y1 = y;
      int x2 = x1 + WIN_W(ss->windows[win]) * ss->scale;
      int y2 = y1 + WIN_H(ss->windows[win]) * ss->scale;
      int overlapx = 0, overlapy = 0;
      int xmin, xmax;
      int ymin, ymax;

      double result = -0.01;

      for (i = 0; i < ss->nWindows; i++)
      {
            if (i == win)
                  continue;
            overlapx = overlapy = 0;
            xmax = MAX(ss->slots[i].x1, x1);
            xmin = MIN(ss->slots[i].x1 + WIN_W(ss->windows[i]) * ss->scale, x2);
            if (xmax <= xmin)
                  overlapx = xmin - xmax;

            ymax = MAX(ss->slots[i].y1, y1);
            ymin = MIN(ss->slots[i].y1 + WIN_H(ss->windows[i]) * ss->scale, y2);

            if (ymax <= ymin)
                  overlapy = ymin - ymax;

            result += (double)overlapx *overlapy;
      }
      return result;
}


static double
layoutOrganicFindBestHorizontalPosition(CompScreen * s, int win,
                                                            int *bestx, int areaWidth)
{
      SCALE_SCREEN(s);
      int i;
      int y1 = ss->slots[win].y1;
      int y2 = ss->slots[win].y1 + WIN_H(ss->windows[win]) * ss->scale;

      double bestoverlap = 1e31, overlap;
      int w = WIN_W(ss->windows[win]) * ss->scale;

      *bestx = ss->slots[win].x1;

      for (i = 0; i < ss->nWindows; i++)
      {
            if (i == win)
                  continue;

            if (ss->slots[i].y1 < y2
                  && ss->slots[i].y1 + WIN_H(ss->windows[i]) * ss->scale > y1)
            {
                  if (ss->slots[i].x1 - w >= 0)
                  {
                        double overlap = layoutOrganicCalculateOverlap(s, win,
                                                                                             ss->slots[i].
                                                                                             x1 - w, y1);

                        if (overlap < bestoverlap)
                        {
                              *bestx = ss->slots[i].x1 - w;
                              bestoverlap = overlap;
                        }
                  }
                  if (WIN_W(ss->windows[i]) * ss->scale +
                        ss->slots[i].x1 + w < areaWidth)
                  {
                        double overlap = layoutOrganicCalculateOverlap(s, win,
                                                                                             ss->slots[i].
                                                                                             x1 +
                                                                                             WIN_W(ss->
                                                                                                       windows
                                                                                                       [i]) *
                                                                                             ss->scale, y1);

                        if (overlap < bestoverlap)
                        {
                              *bestx = ss->slots[i].x1 +
                                          WIN_W(ss->windows[i]) * ss->scale;
                              bestoverlap = overlap;
                        }
                  }
            }
      }

      overlap = layoutOrganicCalculateOverlap(s, win, 0, y1);
      if (overlap < bestoverlap)
      {
            *bestx = 0;
            bestoverlap = overlap;
      }

      overlap = layoutOrganicCalculateOverlap(s, win, areaWidth - w, y1);
      if (overlap < bestoverlap)
      {
            *bestx = areaWidth - w;
            bestoverlap = overlap;
      }

      return bestoverlap;
}

static double
layoutOrganicFindBestVerticalPosition(CompScreen * s, int win,
                                                        int *besty, int areaHeight)
{
      SCALE_SCREEN(s);
      int i;
      int x1 = ss->slots[win].x1;
      int x2 = ss->slots[win].x1 + WIN_W(ss->windows[win]) * ss->scale;

      double bestoverlap = 1e31, overlap;
      int h = WIN_H(ss->windows[win]) * ss->scale;

      *besty = ss->slots[win].y1;

      for (i = 0; i < ss->nWindows; i++)
      {
            if (i == win)
                  continue;

            if (ss->slots[i].x1 < x2
                  && ss->slots[i].x1 + WIN_W(ss->windows[i]) * ss->scale > x1)
            {
                  if (ss->slots[i].y1 - h >= 0 && ss->slots[i].y1 < areaHeight)
                  {
                        double overlap = layoutOrganicCalculateOverlap(s, win,
                                                                                             x1,
                                                                                             ss->slots[i].
                                                                                             y1 - h);
                        if (overlap < bestoverlap)
                        {
                              *besty = ss->slots[i].y1 - h;
                              bestoverlap = overlap;
                        }
                  }
                  if (WIN_H(ss->windows[i]) * ss->scale + ss->slots[i].y1 > 0
                        && WIN_H(ss->windows[i]) * ss->scale + h +
                        ss->slots[i].y1 < areaHeight)
                  {
                        double overlap = layoutOrganicCalculateOverlap(s, win,
                                                                                             x1,
                                                                                             WIN_H(ss->
                                                                                                       windows
                                                                                                       [i]) *
                                                                                             ss->scale +
                                                                                             ss->slots[i].
                                                                                             y1);

                        if (overlap < bestoverlap)
                        {
                              *besty = ss->slots[i].y1 +
                                          WIN_H(ss->windows[i]) * ss->scale;
                              bestoverlap = overlap;
                        }
                  }
            }
      }

      overlap = layoutOrganicCalculateOverlap(s, win, x1, 0);
      if (overlap < bestoverlap)
      {
            *besty = 0;
            bestoverlap = overlap;
      }

      overlap = layoutOrganicCalculateOverlap(s, win, x1, areaHeight - h);
      if (overlap < bestoverlap)
      {
            *besty = areaHeight - h;
            bestoverlap = overlap;
      }

      return bestoverlap;
}


static Bool
layoutOrganicLocalSearch(CompScreen * s, int areaWidth, int areaHeight)
{
      SCALE_SCREEN(s);
      Bool improvement = FALSE;
      int i;
      double totaloverlap;

      do
      {
            improvement = FALSE;
            for (i = 0; i < ss->nWindows; i++)
            {
                  Bool improved = FALSE;

                  do
                  {
                        int newx, newy;
                        double oldoverlap, overlaph, overlapv;

                        improved = FALSE;
                        oldoverlap = layoutOrganicCalculateOverlap(s, i,
                                                                                       ss->slots[i].x1,
                                                                                       ss->slots[i].y1);

                        overlaph =
                                    layoutOrganicFindBestHorizontalPosition(s, i, &newx,
                                                                                                areaWidth);
                        overlapv =
                                    layoutOrganicFindBestVerticalPosition(s, i, &newy,
                                                                                            areaHeight);

                        if (overlaph < oldoverlap - 0.1
                              || overlapv < oldoverlap - 0.1)
                        {
                              improved = TRUE;
                              improvement = TRUE;
                              if (overlapv > overlaph)
                                    ss->slots[i].x1 = newx;
                              else
                                    ss->slots[i].y1 = newy;
                        }

                  }
                  while (improved);
            }
      }
      while (improvement);

      totaloverlap = 0.0;
      for (i = 0; i < ss->nWindows; i++)
      {
            totaloverlap += layoutOrganicCalculateOverlap(s,
                                                                                i, ss->slots[i].x1,
                                                                                ss->slots[i].y1);
      }
      return (totaloverlap > 0.1);
}

static void
layoutOrganicRemoveOverlap(CompScreen * s, int areaWidth, int areaHeight)
{
      int i;

      SCALE_SCREEN(s);
      CompWindow *w;

      while (layoutOrganicLocalSearch(s, areaWidth, areaHeight))
      {
            for (i = 0; i < ss->nWindows; i++)
            {
                  int centerX, centerY;
                  int newX, newY, newWidth, newHeight;

                  w = ss->windows[i];

                  centerX = ss->slots[i].x1 + WIN_W(w) / 2;
                  centerY = ss->slots[i].y1 + WIN_H(w) / 2;

                  newWidth = (int)((1.0 - ORGANIC_STEP) *
                                           (double)WIN_W(w)) - ss->spacing / 2;
                  newHeight = (int)((1.0 - ORGANIC_STEP) *
                                            (double)WIN_H(w)) - ss->spacing / 2;
                  newX = centerX - (newWidth / 2);
                  newY = centerY - (newHeight / 2);

                  ss->slots[i].x1 = newX;
                  ss->slots[i].y1 = newY;
                  ss->slots[i].x2 = newX + WIN_W(w);
                  ss->slots[i].y2 = newY + WIN_H(w);
            }
            ss->scale -= ORGANIC_STEP;
      }
}

/* TODO: Place window thumbnails at smarter positions */
static Bool layoutStandardThumbs(CompScreen * s)
{
      CompWindow *w;
      int i, j, y2;
      int cx, cy;
      int head;
      int lineLength, itemsPerLine;
      float scaleW, scaleH;
      int totalWidth, totalHeight;
      XRectangle outputRect, workArea;

      SCALE_SCREEN(s);

      if (ss->mmMode == Current)
      {
            head = screenGetCurrentOutputDev(s);
            screenGetOutputDevRect(s, head, &outputRect);
            screenGetOutputDevWorkArea(s, head, &workArea);
      }
      else if (ss->mmMode == Selected)
      {
            head = ss->head;
            screenGetOutputDevRect(s, head, &outputRect);
            screenGetOutputDevWorkArea(s, head, &workArea);
      }
      else
      {
            outputRect.x = 0;
            outputRect.y = 0;
            outputRect.width = s->width;
            outputRect.height = s->height;
            workArea = s->workArea;
      }


      cx = cy = ss->nWindows = 0;

      for (w = s->windows; w; w = w->next)
      {
            SCALE_WINDOW(w);

            if (sw->slot)
                  sw->adjust = TRUE;

            sw->slot = 0;

            if (!isScaleWin(w))
                  continue;

            if (ss->opt[SCALE_SCREEN_OPTION_SHOW_MINIMIZED].value.b
                  && w->minimized)
            {
                  sw->wasMinimized = TRUE;
                  unminimizeWindow(w);
            }

            if (!ss->grab)
                  setWinPort(w);

            if (ss->windowsSize <= ss->nWindows)
            {
                  ss->windows = realloc(ss->windows,
                                                  sizeof(CompWindow *) * (ss->nWindows + 32));
                  if (!ss->windows)
                        return FALSE;

                  ss->windowsSize = ss->nWindows + 32;
            }

            ss->windows[ss->nWindows++] = w;
      }

      if (ss->nWindows == 0)
            return FALSE;

      qsort(ss->windows, ss->nWindows, sizeof(CompWindow *), compareWindows);

      itemsPerLine =
                  (sqrt(ss->nWindows) * outputRect.width) / outputRect.height;
      if (itemsPerLine < 1)
            itemsPerLine = 1;

      if (ss->lineSize <= ss->nWindows / itemsPerLine + 1)
      {
            ss->line = realloc(ss->line, sizeof(int) *
                                       (ss->nWindows / itemsPerLine + 2));
            if (!ss->line)
                  return FALSE;

            ss->lineSize = ss->nWindows / itemsPerLine + 2;
      }

      totalWidth = totalHeight = 0;

      ss->line[0] = 0;
      ss->nLine = 1;
      lineLength = itemsPerLine;

      if (ss->slotsSize <= ss->nWindows)
      {
            ss->slots = realloc(ss->slots,
                                          sizeof(ScaleSlot) * (ss->nWindows + 1));
            if (!ss->slots)
                  return FALSE;

            ss->slotsSize = ss->nWindows + 1;
      }
      ss->nSlots = 0;

      for (i = 0; i < ss->nWindows; i++)
      {
            SCALE_WINDOW(ss->windows[i]);

            w = ss->windows[i];

            if (ss->onlyCurrent)
                  raiseWindow(w);

            /* find a good place between other elements */
            for (j = 0; j < ss->nSlots; j++)
            {
                  y2 = ss->slots[j].y2 + ss->spacing + WIN_H(w);
                  if (w->width < ss->slots[j].x2 - ss->slots[j].x1 &&
                        y2 <= ss->line[ss->slots[j].line])
                        break;
            }

            /* otherwise append or start a new line */
            if (j == ss->nSlots)
            {
                  if (lineLength < itemsPerLine)
                  {
                        lineLength++;

                        ss->slots[ss->nSlots].x1 = cx;
                        ss->slots[ss->nSlots].y1 = cy;
                        ss->slots[ss->nSlots].x2 = cx + WIN_W(w);
                        ss->slots[ss->nSlots].y2 = cy + WIN_H(w);
                        ss->slots[ss->nSlots].line = ss->nLine - 1;

                        ss->line[ss->nLine - 1] =
                                    MAX(ss->line[ss->nLine - 1],
                                          ss->slots[ss->nSlots].y2);
                  }
                  else
                  {
                        lineLength = 1;

                        cx = ss->spacing;
                        cy = ss->line[ss->nLine - 1] + ss->spacing;

                        ss->slots[ss->nSlots].x1 = cx;
                        ss->slots[ss->nSlots].y1 = cy;
                        ss->slots[ss->nSlots].x2 = cx + WIN_W(w);
                        ss->slots[ss->nSlots].y2 = cy + WIN_H(w);
                        ss->slots[ss->nSlots].line = ss->nLine - 1;

                        ss->line[ss->nLine] = ss->slots[ss->nSlots].y2;

                        ss->nLine++;
                  }

                  if (ss->slots[ss->nSlots].y2 > totalHeight)
                        totalHeight = ss->slots[ss->nSlots].y2;
            }
            else
            {
                  ss->slots[ss->nSlots].x1 = ss->slots[j].x1;
                  ss->slots[ss->nSlots].y1 = ss->slots[j].y2 + ss->spacing;
                  ss->slots[ss->nSlots].x2 = ss->slots[ss->nSlots].x1 + WIN_W(w);
                  ss->slots[ss->nSlots].y2 = ss->slots[ss->nSlots].y1 + WIN_H(w);
                  ss->slots[ss->nSlots].line = ss->slots[j].line;

                  ss->slots[j].line = 0;
            }

            cx = ss->slots[ss->nSlots].x2;
            if (cx > totalWidth)
                  totalWidth = cx;

            cx += ss->spacing;

            sw->slot = &ss->slots[ss->nSlots];
            sw->adjust = TRUE;

            ss->nSlots++;
      }

      totalWidth += ss->spacing;
      totalHeight += ss->spacing;

      scaleW = (float)workArea.width / totalWidth;
      scaleH = (float)workArea.height / totalHeight;

      ss->scale = MIN(MIN(scaleH, scaleW), 1.0f);

      for (i = 0; i < ss->nWindows; i++)
      {
            SCALE_WINDOW(ss->windows[i]);

            if (sw->slot)
            {
                  ss->slots[i].y1 += ss->windows[i]->input.top;
                  ss->slots[i].x1 += ss->windows[i]->input.left;
                  ss->slots[i].y2 += ss->windows[i]->input.top;
                  ss->slots[i].x2 += ss->windows[i]->input.left;
                  ss->slots[i].y1 = (float)ss->slots[i].y1 * ss->scale;
                  ss->slots[i].x1 = (float)ss->slots[i].x1 * ss->scale;
                  ss->slots[i].y2 = (float)ss->slots[i].y2 * ss->scale;
                  ss->slots[i].x2 = (float)ss->slots[i].x2 * ss->scale;
                  ss->slots[i].x1 += workArea.x;
                  ss->slots[i].y1 += workArea.y;
                  ss->slots[i].x2 += workArea.x;
                  ss->slots[i].y2 += workArea.y;
            }
      }

      return TRUE;
}

static int compareWindowsDistance(const void *elem1, const void *elem2)
{
      CompWindow *w1 = *((CompWindow **) elem1);
      CompWindow *w2 = *((CompWindow **) elem2);

      SCALE_SCREEN(w1->screen);

      return GET_SCALE_WINDOW(w1, ss)->distance -
                  GET_SCALE_WINDOW(w2, ss)->distance;
}

static void layoutSlotsForArea(CompScreen * s, XRectangle workArea, int numWin)
{
      int i, j, x, y, width, height, lines, n, nS;
      lines = sqrt(numWin + 1);

      SCALE_SCREEN(s);

      if (!numWin)
            return;

      nS = 0;
      y = workArea.y + ss->spacing;
      height = (workArea.height - (lines + 1) * ss->spacing) / lines;

      for (i = 0; i < lines; i++)
      {
            n = MIN(numWin - nS,
                        ceilf((float)numWin / lines));

            x = workArea.x + ss->spacing;
            width = (workArea.width - (n + 1) * ss->spacing) / n;

            for (j = 0; j < n; j++)
            {
                  ss->slots[ss->nSlots].x1 = x;
                  ss->slots[ss->nSlots].y1 = y;
                  ss->slots[ss->nSlots].x2 = x + width;
                  ss->slots[ss->nSlots].y2 = y + height;

                  ss->slots[ss->nSlots].filled = FALSE;

                  x += width + ss->spacing;

                  ss->nSlots++;
                  nS++;
            }

            y += height + ss->spacing;
      }
}

static SlotArea* getSlotAreas(CompScreen *s)
{
      SCALE_SCREEN(s);

      int i;
      XRectangle workArea;
      float *size = malloc(s->nOutputDev * sizeof(int));
      SlotArea* sA = malloc(s->nOutputDev * sizeof(SlotArea));

      float sum = 0;
      int left = ss->nWindows;

      for (i = 0; i < s->nOutputDev; i++)
      {
            // determinate the size of the workarea for each output device
            screenGetOutputDevWorkArea(s, i, &workArea);
            size[i] = workArea.width * workArea.height;
            sum += size[i];
            sA[i].nWindows = 0;
            sA[i].workArea = workArea;
      }

      // calculate size available for each window
      float spw = sum / ss->nWindows;

      for (i = 0; i < s->nOutputDev && left; i++)
      {
            // fill the areas with windows
            int nw = floor(size[i] / spw);
            nw = MIN(nw,left);
            size[i] -= nw * spw;
            sA[i].nWindows = nw;
            left -= nw;
      }

      // add left windows to output devices with the biggest free space
      while (left > 0)
      {
            int num = 0;
            float big = 0;
            for (i = 0; i < s->nOutputDev; i++)
                  if (size[i] > big)
                  {
                        num = i;
                        big = size[i];
                  }
            size[num] -= spw;
            sA[num].nWindows++;
            left--;
      }

      free(size);
      return sA;
}

static void layoutSlots(CompScreen * s)
{
      int head = 0, i;
      XRectangle workArea;

      SCALE_SCREEN(s);

      ss->nSlots = 0;

      if (ss->mmMode == Current || s->nOutputDev == 1)
            head = screenGetCurrentOutputDev(s);
      else if (ss->mmMode == Selected)
            head = ss->head;

      if (ss->mmMode == Current || ss->mmMode == Selected || s->nOutputDev == 1)
      {
            screenGetOutputDevWorkArea(s, head, &workArea);
            layoutSlotsForArea(s, workArea, ss->nWindows);
      } else if (ss->mmMode == AllHeads)
      {
            SlotArea* sa = getSlotAreas(s);
            if (sa)
            {
                  for (i = 0; i < s->nOutputDev; i++)
                        layoutSlotsForArea(s, sa[i].workArea, sa[i].nWindows);
                  free(sa);
            }
      }



}

static void findBestSlots(CompScreen * s)
{
      CompWindow *w;
      int i, j, d, d0 = 0;
      float sx, sy, cx, cy;

      SCALE_SCREEN(s);

      for (i = 0; i < ss->nWindows; i++)
      {
            w = ss->windows[i];

            SCALE_WINDOW(w);

            if (sw->slot)
                  continue;

            sw->sid = 0;
            sw->distance = MAXSHORT;

            for (j = 0; j < ss->nSlots; j++)
            {
                  if (!ss->slots[j].filled)
                  {
                        sx = (ss->slots[j].x2 + ss->slots[j].x1) / 2;
                        sy = (ss->slots[j].y2 + ss->slots[j].y1) / 2;

                        cx = w->serverX + w->width / 2;
                        cy = w->serverY + w->height / 2;

                        cx -= sx;
                        cy -= sy;

                        d = sqrt(cx * cx + cy * cy);
                        if (d0 + d < sw->distance)
                        {
                              sw->sid = j;
                              sw->distance = d0 + d;
                        }
                  }
            }

            d0 += sw->distance;
      }
}

static Bool fillInWindows(CompScreen * s)
{
      CompWindow *w;
      int i, width, height;
      float sx, sy, cx, cy;

      SCALE_SCREEN(s);

      for (i = 0; i < ss->nWindows; i++)
      {
            w = ss->windows[i];

            SCALE_WINDOW(w);

            if (!sw->slot)
            {
                  if (ss->slots[sw->sid].filled)
                        return TRUE;

                  sw->slot = &ss->slots[sw->sid];

                  width = w->width + w->input.left + w->input.right;
                  height = w->height + w->input.top + w->input.bottom;

                  sx = (float)(sw->slot->x2 - sw->slot->x1) / width;
                  sy = (float)(sw->slot->y2 - sw->slot->y1) / height;

                  sw->slot->scale = MIN(MIN(sx, sy), 1.0f);

                  sx = w->width * sw->slot->scale;
                  sy = w->height * sw->slot->scale;
                  cx = (sw->slot->x1 + sw->slot->x2) / 2;
                  cy = (sw->slot->y1 + sw->slot->y2) / 2;

                  cx += (w->input.left - w->input.right) * sw->slot->scale;
                  cy += (w->input.top - w->input.bottom) * sw->slot->scale;

                  sw->slot->x1 = cx - sx / 2;
                  sw->slot->y1 = cy - sy / 2;
                  sw->slot->x2 = cx + sx / 2;
                  sw->slot->y2 = cy + sy / 2;

                  sw->slot->filled = TRUE;

                  sw->adjust = TRUE;
            }
      }

      return FALSE;
}


static Bool layoutEnhancedThumbs(CompScreen * s)
{
      CompWindow *w;
      int i;

      SCALE_SCREEN(s);

      ss->nWindows = 0;

      /* add windows scale list, top most window first */
      for (w = s->reverseWindows; w; w = w->prev)
      {
            SCALE_WINDOW(w);

            if (sw->slot)
                  sw->adjust = TRUE;

            sw->slot = 0;

            if (!isScaleWin(w))
                  continue;

            if (ss->opt[SCALE_SCREEN_OPTION_SHOW_MINIMIZED].value.b
                  && w->minimized)
            {
                  sw->wasMinimized = TRUE;
                  unminimizeWindow(w);
            }

            if (!ss->grab)
                  setWinPort(w);

            if (ss->windowsSize <= ss->nWindows)
            {
                  ss->windows = realloc(ss->windows,
                                                  sizeof(CompWindow *) * (ss->nWindows + 32));
                  if (!ss->windows)
                        return FALSE;

                  ss->windowsSize = ss->nWindows + 32;
            }

            ss->windows[ss->nWindows++] = w;
      }

      if (ss->nWindows == 0)
            return FALSE;

      if (ss->slotsSize < ss->nWindows)
      {
            ss->slots = realloc(ss->slots, sizeof(ScaleSlot) * ss->nWindows);
            if (!ss->slots)
                  return FALSE;

            ss->slotsSize = ss->nWindows;
      }

      /* create a grid of slots */
      layoutSlots(s);

      do
      {
            /* find most appropriate slots for windows */
            findBestSlots(s);

            /* sort windows, window with closest distance to a slot first */
            qsort(ss->windows, ss->nWindows, sizeof(CompWindow *),
                    compareWindowsDistance);

      }
      while (fillInWindows(s));

      for (i = 0; i < ss->nWindows; i++)
      {
            SCALE_WINDOW(ss->windows[i]);

            if (ss->onlyCurrent)
                  raiseWindow(ss->windows[i]);
            if (sw->slot)
                  sw->adjust = TRUE;
      }

      return TRUE;
}

static Bool layoutOrganicThumbs(CompScreen * s)
{
      CompWindow *w;

      int i;
      int cx, cy;
      int head;
      XRectangle workArea;

      SCALE_SCREEN(s);

      if (ss->mmMode == Current)
      {
            head = screenGetCurrentOutputDev(s);
            screenGetOutputDevWorkArea(s, head, &workArea);
      }
      else if (ss->mmMode == Selected)
      {
            head = ss->head;
            screenGetOutputDevWorkArea(s, head, &workArea);
      }
      else
      {
            workArea = s->workArea;
      }

      ss->scale = 1.0f;

      cx = cy = ss->nWindows = 0;
      for (w = s->windows; w; w = w->next)
      {
            SCALE_WINDOW(w);

            if (sw->slot)
                  sw->adjust = TRUE;

            sw->slot = 0;

            if (!isScaleWin(w))
                  continue;

            if (ss->opt[SCALE_SCREEN_OPTION_SHOW_MINIMIZED].value.b
                  && w->minimized)
            {
                  sw->wasMinimized = TRUE;
                  unminimizeWindow(w);
            }

            if (!ss->grab)
                  setWinPort(w);

            if (ss->windowsSize <= ss->nWindows)
            {
                  ss->windows = realloc(ss->windows,
                                                  sizeof(CompWindow *) * (ss->nWindows + 32));
                  if (!ss->windows)
                        return FALSE;

                  ss->windowsSize = ss->nWindows + 32;
            }

            ss->windows[ss->nWindows++] = w;
      }
      if (ss->nWindows == 0)
            return FALSE;

      qsort(ss->windows, ss->nWindows, sizeof(CompWindow *),
              organicCompareWindows);

      if (ss->slotsSize <= ss->nWindows)
      {
            ss->slots = realloc(ss->slots, sizeof(ScaleSlot) *
                                          (ss->nWindows + 1));

            if (!ss->slots)
                  return FALSE;

            ss->slotsSize = ss->nWindows + 1;
      }

      for (i = 0; i < ss->nWindows; i++)
      {
            SCALE_WINDOW(ss->windows[i]);
            w = ss->windows[i];

            sw->slot = &ss->slots[i];
            ss->slots[i].x1 = WIN_X(w) - workArea.x;
            ss->slots[i].y1 = WIN_Y(w) - workArea.y;
            ss->slots[i].x2 = WIN_X(w) + WIN_W(w) - workArea.x;
            ss->slots[i].y2 = WIN_Y(w) + WIN_H(w) - workArea.y;

            if (ss->slots[i].x1 < 0)
            {
                  ss->slots[i].x2 += abs(ss->slots[i].x1);
                  ss->slots[i].x1 = 0;
            }
            if (ss->slots[i].x2 > workArea.width - workArea.x)
            {
                  ss->slots[i].x1 -= abs(ss->slots[i].x2 - workArea.width);
                  ss->slots[i].x2 = workArea.width - workArea.x;
            }

            if (ss->slots[i].y1 < 0)
            {
                  ss->slots[i].y2 += abs(ss->slots[i].y1);
                  ss->slots[i].y1 = 0;
            }
            if (ss->slots[i].y2 > workArea.height - workArea.y)
            {
                  ss->slots[i].y1 -= abs(ss->slots[i].y2 -
                                                   workArea.height - workArea.y);
                  ss->slots[i].y2 = workArea.height - workArea.y;
            }
      }

      ss->nSlots = ss->nWindows;

      layoutOrganicRemoveOverlap(s, workArea.width - workArea.x,
                                             workArea.height - workArea.y);
      for (i = 0; i < ss->nWindows; i++)
      {
            SCALE_WINDOW(ss->windows[i]);

            if (ss->onlyCurrent)
                  raiseWindow(ss->windows[i]);

            ss->slots[i].x1 += ss->windows[i]->input.left + workArea.x;
            ss->slots[i].x2 += ss->windows[i]->input.left + workArea.x;
            ss->slots[i].y1 += ss->windows[i]->input.top + workArea.y;
            ss->slots[i].y2 += ss->windows[i]->input.top + workArea.y;
            sw->adjust = TRUE;
      }

      return TRUE;
}

static void zoomWindow(CompScreen * s, CompWindow * w)
{
      SCALE_WINDOW(w);

      XRectangle workArea, outputRect;
      int head;

      head = screenGetCurrentOutputDev(s);
      screenGetOutputDevRect(s, head, &outputRect);

      screenGetOutputDevWorkArea(s, screenGetCurrentOutputDev(s), &workArea);


      if (sw->rescaled == FALSE)
      {
            raiseWindow(w);
            //backup old values
            sw->oldScale = sw->scale;
            sw->origX = sw->slot->x1;
            sw->origY = sw->slot->y1;

            sw->scale = 1.0f;
            sw->adjust = TRUE;

            sw->rescaled = TRUE;

            sw->slot->x1 = (outputRect.width / 2) - (WIN_W(w) / 2) + workArea.x;
            sw->slot->y1 = (outputRect.height / 2) - (WIN_H(w) / 2) + workArea.y;
      }
      else
      {
            restackWindowAbove(w, s->windows);  // TODO make sure this is right
            sw->rescaled = FALSE;
            sw->scale = sw->oldScale;
            sw->slot->x1 = sw->origX;
            sw->slot->y1 = sw->origY;
            sw->adjust = TRUE;
      }
}

static void resetZoomValues(CompScreen * s)
{
      CompWindow *w;

      for (w = s->windows; w; w = w->next)
      {
            SCALE_WINDOW(w);
            if (sw->rescaled && isScaleWin(w))
                  sw->rescaled = FALSE;
      }
}


/* TODO: Place window thumbnails at smarter positions */
static Bool layoutThumbs(CompScreen * s)
{
      SCALE_SCREEN(s);

      switch (ss->scaleMethod)
      {
      case ScaleMethodStandard:
            return layoutStandardThumbs(s);
      case ScaleMethodEnhanced:
            return layoutEnhancedThumbs(s);
      case ScaleMethodOrganic:
            return layoutOrganicThumbs(s);

      }
      return FALSE;
}

static int adjustScaleVelocity(CompWindow * w)
{
      float dx, dy, ds, adjust, amount;
      float x1, y1, scale;

      SCALE_SCREEN(w->screen);
      SCALE_WINDOW(w);

      if (sw->slot)
      {
            x1 = sw->slot->x1;
            y1 = sw->slot->y1;
            switch (ss->scaleMethod)
            {
            case ScaleMethodEnhanced:
                  scale = sw->slot->scale;
                  break;
            default:
                  scale = ss->scale;
            }
      }
      else
      {
            x1 = w->serverX;
            y1 = w->serverY;
            scale = 1.0f;
      }

      dx = x1 - (w->serverX + sw->tx);

      adjust = dx * 0.15f;
      amount = fabs(dx) * 1.5f;
      if (amount < 0.5f)
            amount = 0.5f;
      else if (amount > 5.0f)
            amount = 5.0f;

      sw->xVelocity = (amount * sw->xVelocity + adjust) / (amount + 1.0f);

      dy = y1 - (w->serverY + sw->ty);

      adjust = dy * 0.15f;
      amount = fabs(dy) * 1.5f;
      if (amount < 0.5f)
            amount = 0.5f;
      else if (amount > 5.0f)
            amount = 5.0f;

      sw->yVelocity = (amount * sw->yVelocity + adjust) / (amount + 1.0f);

      ds = scale - sw->scale;

      adjust = ds * 0.1f;
      amount = fabs(ds) * 7.0f;
      if (amount < 0.01f)
            amount = 0.01f;
      else if (amount > 0.15f)
            amount = 0.15f;

      sw->scaleVelocity = (amount * sw->scaleVelocity + adjust) /
                  (amount + 1.0f);

      if (fabs(dx) < 0.1f && fabs(sw->xVelocity) < 0.2f &&
            fabs(dy) < 0.1f && fabs(sw->yVelocity) < 0.2f &&
            fabs(ds) < 0.001f && fabs(sw->scaleVelocity) < 0.002f)
      {
            sw->xVelocity = sw->yVelocity = sw->scaleVelocity = 0.0f;
            sw->tx = x1 - w->serverX;
            sw->ty = y1 - w->serverY;

            if (sw->rescaled == FALSE)
            {
                  sw->scale = scale;
            }

            return 0;
      }

      return 1;
}

static Bool
scalePaintScreen(CompScreen * s,
                         const ScreenPaintAttrib * sAttrib,
                         Region region, int output, unsigned int mask)
{
      Bool status;

      SCALE_SCREEN(s);

      if (ss->grab)
            mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK;

      UNWRAP(ss, s, paintScreen);
      status = (*s->paintScreen) (s, sAttrib, region, output, mask);
      WRAP(ss, s, paintScreen, scalePaintScreen);

      return status;
}

static void scalePreparePaintScreen(CompScreen * s, int msSinceLastPaint)
{
      SCALE_SCREEN(s);

      ss->clicked = FALSE;

      if (ss->grab
            && !IPCS_GetBoolND(IPCS_OBJECT(s), "SHOWDESKTOP_ACTIVE", FALSE))
      {
            CompWindow *w;
            int steps;
            float amount, chunk;

            amount = msSinceLastPaint * 0.05f * ss->speed;
            steps = amount / (0.5f * ss->timestep);
            if (!steps)
                  steps = 1;
            chunk = amount / (float)steps;

            while (steps--)
            {
                  ss->moreAdjust = 0;

                  for (w = s->windows; w; w = w->next)
                  {
                        SCALE_WINDOW(w);

                        if (sw->adjust)
                        {
                              sw->adjust = adjustScaleVelocity(w);

                              ss->moreAdjust |= sw->adjust;

                              sw->tx += sw->xVelocity * chunk;
                              sw->ty += sw->yVelocity * chunk;

                              if (sw->rescaled == FALSE)
                              {
                                    sw->scale += sw->scaleVelocity * chunk;
                              }
                        }

                  }

                  if (!ss->moreAdjust)
                        break;
            }
      }

      UNWRAP(ss, s, preparePaintScreen);
      (*s->preparePaintScreen) (s, msSinceLastPaint);
      WRAP(ss, s, preparePaintScreen, scalePreparePaintScreen);
}

static void scaleDonePaintScreen(CompScreen * s)
{
      SCALE_SCREEN(s);

      if (ss->grab)
      {
            if (ss->moreAdjust)
            {
                  damageScreen(s);
            }
            else
            {
                  if (ss->state == SCALE_STATE_IN)
                  {
                        ss->grab = FALSE;
                        removeScreenGrabKeyboardOptional(s,
                                                                         ss->grabIndex, 0,
                                                                         !ss->
                                                                         opt
                                                                         [SCALE_SCREEN_OPTION_ALLOW_KEYBOARD_INPUT].
                                                                         value.b);
                        ss->grabIndex = 0;
                        if (ss->selectedWindow)
                        {

                              CompWindow *w = findWindowAtScreen(s,
                                                                                 ss->selectedWindow);

                              if (w && isScaleWin(w))
                              {
                                    SCALE_WINDOW(w);
                                    raiseWindow(w);
                                    gotoViewport(s, sw->workspace);
                              }
                        }
                  }
                  else if (ss->state == SCALE_STATE_OUT)
                        ss->state = SCALE_STATE_WAIT;
            }
      }

      UNWRAP(ss, s, donePaintScreen);
      (*s->donePaintScreen) (s);
      WRAP(ss, s, donePaintScreen, scaleDonePaintScreen);
}

static CompWindow *scaleCheckForWindowAt(CompScreen * s, int x, int y)
{
      int x1, y1, x2, y2;
      CompWindow *w;

      for (w = s->reverseWindows; w; w = w->prev)
      {
            SCALE_WINDOW(w);

            if (sw->slot)
            {
                  x1 = w->attrib.x - w->input.left * sw->scale;
                  y1 = w->attrib.y - w->input.top * sw->scale;
                  x2 = w->attrib.x + (w->width + w->input.right) * sw->scale;
                  y2 = w->attrib.y + (w->height + w->input.bottom) * sw->scale;

                  x1 += sw->tx;
                  y1 += sw->ty;
                  x2 += sw->tx;
                  y2 += sw->ty;

                  if (x1 <= x && y1 <= y && x2 > x && y2 > y)
                        return w;
            }
      }

      return 0;
}

static void sendDndStatusMessage(CompScreen * s, Window source)
{
      XEvent xev;

      SCALE_SCREEN(s);

      xev.xclient.type = ClientMessage;
      xev.xclient.display = s->display->display;
      xev.xclient.format = 32;

      xev.xclient.message_type = s->display->xdndStatusAtom;
      xev.xclient.window = source;

      xev.xclient.data.l[0] = ss->dndTarget;
      xev.xclient.data.l[1] = 2;
      xev.xclient.data.l[2] = 0;
      xev.xclient.data.l[3] = 0;
      xev.xclient.data.l[4] = None;

      XSendEvent(s->display->display, source, FALSE, 0, &xev);
}

static Bool
scaleTerminate(CompDisplay * d,
                     CompAction * action,
                     CompActionState state, CompOption * option, int nOption)
{
      CompScreen *s;
      Window xid;

      SCALE_DISPLAY(d);

      xid = getIntOptionNamed(option, nOption, "root", 0);

      for (s = d->screens; s; s = s->next)
      {
            SCALE_SCREEN(s);
            if (xid && s->root != xid)
                  continue;

            ss->currentWindow = 0;
            ss->allScreensMode = FALSE;
            ss->onlyCurrent = FALSE;

            //clean up any sw->rescaled values that may still be true
            //causes odd behavior on next scale/zoom otherwise
            resetZoomValues(s);

            if (ss->grab)
            {
                  if (ss->dndTarget)
                  {
                        XUnmapWindow(d->display, ss->dndTarget);
                        ss->dndTarget = 0;
                  }

                  if (ss->state == SCALE_STATE_NONE)
                  {
                        if (ss->grabIndex)
                        {
                              removeScreenGrabKeyboardOptional(s,
                                                                               ss->grabIndex, 0,
                                                                               !ss->
                                                                               opt
                                                                               [SCALE_SCREEN_OPTION_ALLOW_KEYBOARD_INPUT].
                                                                               value.b);
                              ss->grabIndex = 0;
                        }

                        ss->grab = FALSE;

                        if (ss->selectedWindow && !(state & CompActionStateCancel))
                        {
                              CompWindow *w = findWindowAtScreen(s,
                                                                                 ss->selectedWindow);

                              if (w)
                              {
                                    SCALE_WINDOW(w);
                                    raiseWindow(w);
                                    gotoViewport(s, sw->workspace);
                              }
                        }
                  }
                  else
                  {
                        CompWindow *w;

                        for (w = s->windows; w; w = w->next)
                        {
                              SCALE_WINDOW(w);

                              if (sw->slot)
                              {
                                    sw->slot = 0;
                                    sw->adjust = TRUE;
                              }
                              if (sw->wasMinimized && (w->id != ss->selectedWindow))
                              {
                                    minimizeWindow(w);
                                    sw->wasMinimized = FALSE;
                              }
                              if (w->id == ss->selectedWindow)
                              {
                                    raiseWindow(w);
                                    //goto viewport immediately
                                    gotoViewport(s, sw->workspace);
                              }
                        }

                        ss->state = SCALE_STATE_IN;

                        damageScreen(s);
                  }

                  if (state & CompActionStateCancel)
                  {
                        CompWindow *aw = findTopLevelWindowAtScreen(s,
                                                                                          ss->cancelWindow);

                        if (aw)
                              sendWindowActivationRequest(s, aw->id);

                        ss->cancelWindow = 0;
                        ss->selectedWindow = 0;
                  }
                  sd->lastActiveNum = None;
            }
      }

      return FALSE;
}

static Bool scaleEnsureDndRedirectWindow(CompScreen * s)
{
      SCALE_SCREEN(s);

      if (!ss->dndTarget)
      {
            XSetWindowAttributes attr;
            long xdndVersion = 3;

            attr.override_redirect = TRUE;

            ss->dndTarget = XCreateWindow(s->display->display,
                                                        s->root, 0, 0, 1, 1, 0, CopyFromParent,
                                                        InputOnly, CopyFromParent,
                                                        CWOverrideRedirect, &attr);

            XChangeProperty(s->display->display, ss->dndTarget,
                                    s->display->xdndAwareAtom,
                                    XA_ATOM, 32, PropModeReplace,
                                    (unsigned char *)&xdndVersion, 1);
      }

      XMoveResizeWindow(s->display->display, ss->dndTarget,
                                0, 0, s->width, s->height);
      XMapRaised(s->display->display, ss->dndTarget);

      return TRUE;
}

#define SCALE_KIND_NORMAL 0
#define SCALE_KIND_APP 1
#define SCALE_KIND_ALL 2
#define SCALE_KIND_CURRENT_HEAD 3

static Bool
scaleInitiateReal(CompDisplay * d,
                          CompAction * action,
                          CompActionState state,
                          CompOption * option, int nOption, int scaleKind)
{
      CompScreen *s;
      CompWindow *w;
      Window xid;
      Window xid2;

      xid = getIntOptionNamed(option, nOption, "root", 0);
      s = findScreenAtDisplay(d, xid);
      if (s)
      {
            SCALE_SCREEN(s);
            if (ss->clicked)
            {
                  ss->clicked = FALSE;
                  return FALSE;
            }
            w = findTopLevelWindowAtScreen(s, s->display->activeWindow);
            xid2 = w ? w->id : 0;

            SCALE_DISPLAY(s->display);

            if (ss->state != SCALE_STATE_WAIT && ss->state != SCALE_STATE_OUT)
            {
                  //ensure we are facing the proper viewport
                  gotoViewport(s, s->x);

                  ss->cancelWindow = ss->selectedWindow = 0;
                  if (s->display->activeWindow)
                  {
                        CompWindow *aw = findTopLevelWindowAtScreen(s,
                                                                                          s->display->
                                                                                          activeWindow);

                        //make sure that the selected window is a valid one
                        if (aw && isScaleWin(aw))
                        {
                              ss->cancelWindow = aw->id;
                              ss->selectedWindow = aw->id;
                        }
                  }

                  ss->onlyCurrent = (scaleKind == SCALE_KIND_APP);
                  if (ss->onlyCurrent)
                  {
                        ss->currentWindow = findWindowAtDisplay(d, xid2);
                  }
                  ss->allScreensMode = (scaleKind == SCALE_KIND_ALL);
                  ss->currentHead = (scaleKind == SCALE_KIND_CURRENT_HEAD);


                  ss->currentOutputDev = screenGetCurrentOutputDev(s);

                  if (!layoutThumbs(s))
                        return FALSE;

                  if (otherScreenGrabExist(s, "scale", 0))
                        return FALSE;

                  if (state & CompActionStateInitEdgeDnd)
                  {
                        if (scaleEnsureDndRedirectWindow(s))
                              ss->grab = TRUE;
                  }
                  else if (!ss->grabIndex)
                  {
                        ss->grabIndex = pushScreenGrabKeyboardOptional(s,
                                                                                             ss->cursor,
                                                                                             "scale",
                                                                                             !ss->
                                                                                             opt
                                                                                             [SCALE_SCREEN_OPTION_ALLOW_KEYBOARD_INPUT].
                                                                                             value.b);
                        if (ss->grabIndex)
                              ss->grab = TRUE;
                  }

                  if (ss->grab)
                  {
                        if (!sd->lastActiveNum)
                              sd->lastActiveNum = s->activeNum - 1;

                        ss->state = SCALE_STATE_OUT;

                        damageScreen(s);
                  }
            }
            else
            {
                  state |= CompActionStateCancel;
                  scaleTerminate(d, action, state, option, nOption);
            }
      }

      return FALSE;
}

static Bool
scaleInitiate(CompDisplay * d,
                    CompAction * action,
                    CompActionState state, CompOption * option, int nOption)
{
      return scaleInitiateReal(d, action, state, option, nOption,
                                           SCALE_KIND_NORMAL);
}

static Bool
scaleInitiateApp(CompDisplay * d,
                         CompAction * action,
                         CompActionState state, CompOption * option, int nOption)
{
      return scaleInitiateReal(d, action, state, option, nOption,
                                           SCALE_KIND_APP);
}

static Bool
scaleInitiateAll(CompDisplay * d,
                         CompAction * action,
                         CompActionState state, CompOption * option, int nOption)
{
      return scaleInitiateReal(d, action, state, option, nOption,
                                           SCALE_KIND_ALL);
}

static Bool
scaleInitiateCurrentHead(CompDisplay * d,
                                     CompAction * action,
                                     CompActionState state,
                                     CompOption * option, int nOption)
{
      return scaleInitiateReal(d, action, state, option, nOption,
                                           SCALE_KIND_CURRENT_HEAD);
}


static void scaleSelectWindow(CompWindow * w)
{
      SCALE_SCREEN(w->screen);
      //raiseWindow (w);
      moveInputFocusToWindow(w);
      ss->selectedWindow = w->id;
      damageScreen(w->screen);
}

static Bool scaleHoverWindowAt(CompScreen * s, int x, int y, Bool sloppyFocus)
{
      CompWindow *w;

      SCALE_SCREEN(s);

      w = scaleCheckForWindowAt(s, x, y);
      if (w && isScaleWin(w))
      {
            if (sloppyFocus)
                  scaleSelectWindow(w);
            else
            {
                  ss->hoveredWindow = w;
                  damageScreen(w->screen);
            }

            return TRUE;
      }
      else
            ss->hoveredWindow = NULL;

      return FALSE;
}

static Bool scaleSelectWindowAt(CompScreen * s, int x, int y)
{
      CompWindow *w;

      w = scaleCheckForWindowAt(s, x, y);
      if (w && isScaleWin(w))
      {
            scaleSelectWindow(w);
            return TRUE;
      }

      return FALSE;
}

static void scaleMoveFocusWindow(CompScreen * s, int dx, int dy)
{
      CompWindow *active;

      active = findWindowAtScreen(s, s->display->activeWindow);
      if (active)
      {
            CompWindow *w, *focus = NULL;
            ScaleSlot *slot;
            int x, y, cx, cy, d, min = MAXSHORT;

            SCALE_SCREEN(s);
            SCALE_WINDOW(active);

            if (!sw->slot)
                  return;

            cx = (sw->slot->x1 + sw->slot->x2) / 2;
            cy = (sw->slot->y1 + sw->slot->y2) / 2;

            for (w = s->windows; w; w = w->next)
            {
                  if (w->id == active->id)
                        continue;
                  slot = GET_SCALE_WINDOW(w, ss)->slot;
                  if (!slot)
                        continue;

                  x = (slot->x1 + slot->x2) / 2;
                  y = (slot->y1 + slot->y2) / 2;

                  d = sqrt((x - cx) * (x - cx) + (y - cy) * (y - cy));
                  if (d < min)
                  {
                        if ((dx > 0 && slot->x1 < sw->slot->x2) ||
                              (dx < 0 && slot->x2 > sw->slot->x1) ||
                              (dy > 0 && slot->y1 < sw->slot->y2) ||
                              (dy < 0 && slot->y2 > sw->slot->y1))
                              /*
                                 if ((dx > 0 && x < cx) ||
                                 (dx < 0 && x > cx) ||
                                 (dy > 0 && y < cy) ||
                                 (dy < 0 && y > cy)) */
                              continue;

                        min = d;
                        focus = w;
                  }
            }

            if (focus)
            {
                  SCALE_DISPLAY(s->display);

                  sd->lastActiveNum = focus->activeNum;

                  scaleSelectWindow(focus);
            }
      }
}

static void scaleWindowRemove(CompDisplay * d, Window id)
{
      CompWindow *w;

      w = findWindowAtDisplay(d, id);
      if (w)
      {
            SCALE_SCREEN(w->screen);

            if (ss->grab && ss->state != SCALE_STATE_IN)
            {
                  int i;

                  for (i = 0; i < ss->nWindows; i++)
                  {
                        if (ss->windows[i] == w)
                        {
                              if (layoutThumbs(w->screen))
                              {
                                    ss->state = SCALE_STATE_OUT;
                                    damageScreen(w->screen);
                                    break;
                              }
                        }
                  }
            }
      }
}

static Bool scaleHoverTimeout(void *closure)
{
      CompScreen *s = closure;

      SCALE_SCREEN(s);
      SCALE_DISPLAY(s->display);

      if (ss->grab && ss->state != SCALE_STATE_IN)
      {
            CompOption o;
            CompAction *action =
                        &sd->opt[SCALE_DISPLAY_OPTION_INITIATE_NORMAL].value.action;

            o.type = CompOptionTypeInt;
            o.name = "root";
            o.value.i = s->root;

            scaleTerminate(s->display, action, 0, &o, 1);
      }

      ss->hoverHandle = 0;

      return FALSE;
}

static void scaleHandleEvent(CompDisplay * d, XEvent * event)
{
      CompScreen *s;

      SCALE_DISPLAY(d);

      switch (event->type)
      {
      case KeyPress:
            s = findScreenAtDisplay(d, event->xkey.root);
            if (s)
            {
                  SCALE_SCREEN(s);

                  if (ss->grab)
                  {
                        if (event->xkey.keycode == sd->leftKeyCode)
                              scaleMoveFocusWindow(s, -1, 0);
                        else if (event->xkey.keycode == sd->rightKeyCode)
                              scaleMoveFocusWindow(s, 1, 0);
                        else if (event->xkey.keycode == sd->upKeyCode)
                              scaleMoveFocusWindow(s, 0, -1);
                        else if (event->xkey.keycode == sd->downKeyCode)
                              scaleMoveFocusWindow(s, 0, 1);
                  }
            }
            break;
      case ButtonPress:
            s = findScreenAtDisplay(d, event->xbutton.root);
            if (s)
            {
                  CompAction *action =
                              &sd->opt[SCALE_DISPLAY_OPTION_INITIATE_NORMAL].value.
                              action;

                  SCALE_SCREEN(s);


                  if (ss->grab && ss->state != SCALE_STATE_IN)
                  {
                        ss->clicked = TRUE;
                        CompOption o;

                        o.type = CompOptionTypeInt;
                        o.name = "root";
                        o.value.i = s->root;

                        if (event->xbutton.button == Button3)
                        {
                              CompWindow *w;

                              w = scaleCheckForWindowAt(s,
                                                                    event->xbutton.x_root,
                                                                    event->xbutton.y_root);

                              if (w)
                                    zoomWindow(s, w);
                        }
                        else if (event->xbutton.button == Button2)
                        {
                              CompWindow *w;

                              w = scaleCheckForWindowAt(s,
                                                                    event->xbutton.x_root,
                                                                    event->xbutton.y_root);

                              if (w)
                                    closeWindow(w, event->xkey.time);

                        }
                        else if (scaleSelectWindowAt(s,
                                                                   event->xbutton.x_root,
                                                                   event->xbutton.y_root) &&
                                     event->xbutton.button == Button1)
                        {
                              scaleTerminate(d, action, 0, &o, 1);
                        }
                        else if (event->xbutton.button == Button1)
                        {
                              scaleTerminate(d, action, 0, &o, 1);
                        }
                        else
                              ss->clicked = FALSE;
                        /*else if (event->xbutton.x_root > s->workArea.x &&
                           event->xbutton.x_root < (s->workArea.x +
                           s->workArea.width) &&
                           event->xbutton.y_root > s->workArea.y &&
                           event->xbutton.y_root < (s->workArea.y +
                           s->workArea.height))
                           {
                           scaleTerminate (d, action, 0, &o, 1);
                           } */
                        //disabled exit-scale-by-invalid-click-into-show-desktop
                  }
            }
            break;
      case MotionNotify:
            s = findScreenAtDisplay(d, event->xmotion.root);
            if (s)
            {
                  SCALE_SCREEN(s);

                  if (ss->grab && ss->state != SCALE_STATE_IN)
                        scaleHoverWindowAt(s,
                                                   event->xmotion.x_root,
                                                   event->xmotion.y_root,
                                                   ss->opt[SCALE_SCREEN_OPTION_SLOPPY_FOCUS].
                                                   value.b);
            }
            break;
      case ClientMessage:
            if (event->xclient.message_type == d->xdndPositionAtom)
            {
                  CompWindow *w;

                  w = findWindowAtDisplay(d, event->xclient.window);
                  if (w)
                  {
                        SCALE_SCREEN(w->screen);

                        s = w->screen;

                        if (w->id == ss->dndTarget)
                              sendDndStatusMessage(w->screen, event->xclient.data.l[0]);

                        if (ss->grab && ss->state != SCALE_STATE_IN &&
                              w->id == ss->dndTarget)
                        {
                              int x = event->xclient.data.l[2] >> 16;
                              int y = event->xclient.data.l[2] & 0xffff;

                              w = scaleCheckForWindowAt(s, x, y);
                              if (w && isScaleWin(w))
                              {
                                    if (ss->hoverHandle)
                                    {
                                          if (w->activeNum != sd->lastActiveNum)
                                          {
                                                compRemoveTimeout(ss->hoverHandle);
                                                ss->hoverHandle = 0;
                                          }
                                    }

                                    if (!ss->hoverHandle)
                                          ss->hoverHandle =
                                                      compAddTimeout(ss->
                                                                           opt
                                                                           [SCALE_SCREEN_OPTION_HOVER_TIME].
                                                                           value.i, scaleHoverTimeout,
                                                                           s);

                                    scaleSelectWindowAt(s, x, y);
                              }
                              else
                              {
                                    if (ss->hoverHandle)
                                          compRemoveTimeout(ss->hoverHandle);

                                    ss->hoverHandle = 0;
                              }
                        }
                  }
            }
            else if (event->xclient.message_type == d->xdndDropAtom
                         || event->xclient.message_type == d->xdndLeaveAtom)
            {
                  CompWindow *w;

                  w = findWindowAtDisplay(d, event->xclient.window);
                  if (w)
                  {
                        CompAction *action =
                                    &sd->
                                    opt[SCALE_DISPLAY_OPTION_INITIATE_NORMAL].value.
                                    action;

                        SCALE_SCREEN(w->screen);

                        if (ss->grab &&
                              ss->state != SCALE_STATE_IN && w->id == ss->dndTarget)
                        {
                              CompOption o;

                              o.type = CompOptionTypeInt;
                              o.name = "root";
                              o.value.i = w->screen->root;

                              scaleTerminate(d, action, 0, &o, 1);
                        }
                  }
            }
            break;
      default:
            break;
      }

      UNWRAP(sd, d, handleEvent);
      (*d->handleEvent) (d, event);
      WRAP(sd, d, handleEvent, scaleHandleEvent);

      switch (event->type)
      {
      case UnmapNotify:
            scaleWindowRemove(d, event->xunmap.window);
            break;
      case DestroyNotify:
            scaleWindowRemove(d, event->xdestroywindow.window);
            break;
      }

}

static Bool scaleDamageWindowRect(CompWindow * w, Bool initial, BoxPtr rect)
{
      Bool status = FALSE;

      SCALE_SCREEN(w->screen);

      if (initial)
      {
            if (ss->grab && isScaleWin(w))
            {
                  if (layoutThumbs(w->screen))
                  {
                        ss->state = SCALE_STATE_OUT;
                        damageScreen(w->screen);
                  }
            }
      }
      else if (ss->state == SCALE_STATE_WAIT)
      {
            SCALE_WINDOW(w);

            if (sw->slot)
            {
                  damageTransformedWindowRect(w, sw->scale,
                                                            sw->scale, sw->tx, sw->ty, rect);

                  status = TRUE;
            }
      }

      UNWRAP(ss, w->screen, damageWindowRect);
      status |= (*w->screen->damageWindowRect) (w, initial, rect);
      WRAP(ss, w->screen, damageWindowRect, scaleDamageWindowRect);

      return status;
}

static Bool
scaleSetDisplayOption(CompDisplay * display,
                                char *name, CompOptionValue * value)
{
      CompOption *o;
      int index;

      SCALE_DISPLAY(display);

      o = compFindOption(sd->opt, NUM_OPTIONS(sd), name, &index);

      if (!o)
            return FALSE;

      switch (index)
      {
      case SCALE_DISPLAY_OPTION_INITIATE_NORMAL:
      case SCALE_DISPLAY_OPTION_INITIATE_APP:
      case SCALE_DISPLAY_OPTION_INITIATE_CURRENT_HEAD:
      case SCALE_DISPLAY_OPTION_INITIATE_ALL:
            if (setDisplayAction(display, o, value))
                  return TRUE;
      default:
            break;
      }

      return FALSE;
}

static void scaleDisplayInitOptions(ScaleDisplay * sd)
{
      CompOption *o;

      o = &sd->opt[SCALE_DISPLAY_OPTION_INITIATE_NORMAL];
      o->advanced = False;
      o->name = "initiate";
      o->group = N_("Bindings");
      o->subGroup = N_("Initiate Window Picker");
      o->displayHints = "";
      o->shortDesc = N_("Initiate Window Picker");
      o->longDesc = N_("Layout and start transforming Windows.");
      o->type = CompOptionTypeAction;
      o->value.action.initiate = scaleInitiate;
      o->value.action.terminate = scaleTerminate;
      o->value.action.bell = FALSE;
      o->value.action.edgeMask = 0;
      o->value.action.state = CompActionStateInitEdge;;
      o->value.action.state |= CompActionStateInitEdgeDnd;
      o->value.action.type = CompBindingTypeKey;
      o->value.action.state |= CompActionStateInitKey;
      o->value.action.state |= CompActionStateInitButton;
      o->value.action.key.modifiers = SCALE_INITIATE_MODIFIERS_DEFAULT;
      o->value.action.key.keysym = XStringToKeysym(SCALE_INITIATE_KEY_DEFAULT);

      o = &sd->opt[SCALE_DISPLAY_OPTION_INITIATE_APP];
      o->advanced = False;
      o->name = "initiate_app";
      o->group = N_("Bindings");
      o->subGroup = N_("Initiate Window Picker for Current App");
      o->displayHints = "";
      o->shortDesc = N_("Initiate Window Picker for Current App");
      o->longDesc =
                  N_
                  ("Layout and start transforming Windows of Current Application.");
      o->type = CompOptionTypeAction;
      o->value.action.initiate = scaleInitiateApp;
      o->value.action.terminate = scaleTerminate;
      o->value.action.bell = FALSE;
      o->value.action.edgeMask = 0;
      o->value.action.state = CompActionStateInitEdge;
      o->value.action.state |= CompActionStateInitEdgeDnd;
      o->value.action.type = CompBindingTypeKey;
      o->value.action.state |= CompActionStateInitKey;
      o->value.action.state |= CompActionStateInitButton;
      o->value.action.key.modifiers = SCALE_INITIATE_APP_MODIFIERS_DEFAULT;
      o->value.action.key.keysym =
                  XStringToKeysym(SCALE_INITIATE_APP_KEY_DEFAULT);

      o = &sd->opt[SCALE_DISPLAY_OPTION_INITIATE_ALL];
      o->advanced = False;
      o->name = "initiate_all";
      o->group = N_("Bindings");
      o->subGroup = N_("Initiate Window Picker for All Workspaces");
      o->displayHints = "";
      o->shortDesc = N_("Initiate Window Picker for All Workspaces");
      o->longDesc =
                  N_("Layout and start transforming Windows from All Workspaces.");
      o->type = CompOptionTypeAction;
      o->value.action.initiate = scaleInitiateAll;
      o->value.action.terminate = scaleTerminate;
      o->value.action.bell = FALSE;
      o->value.action.edgeMask = (1 << SCREEN_EDGE_TOPRIGHT);
      o->value.action.state = CompActionStateInitEdge;
      o->value.action.state |= CompActionStateInitEdgeDnd;
      o->value.action.type = CompBindingTypeKey;
      o->value.action.state |= CompActionStateInitKey;
      o->value.action.state |= CompActionStateInitButton;
      o->value.action.key.modifiers = SCALE_INITIATE_ALL_MODIFIERS_DEFAULT;
      o->value.action.key.keysym =
                  XStringToKeysym(SCALE_INITIATE_ALL_KEY_DEFAULT);

      o = &sd->opt[SCALE_DISPLAY_OPTION_INITIATE_CURRENT_HEAD];
      o->advanced = False;
      o->name = "initiate_current_head";
      o->group = N_("Bindings");
      o->subGroup = N_("Initiate current head scale only");
      o->displayHints = "";
      o->shortDesc = N_("Initiate Current Head Scale Only");
      o->longDesc = N_("Only Scale windows on the Current Monitor.");
      o->type = CompOptionTypeAction;
      o->value.action.initiate = scaleInitiateCurrentHead;
      o->value.action.terminate = scaleTerminate;
      o->value.action.bell = FALSE;
      o->value.action.edgeMask = 0;
      o->value.action.state = CompActionStateInitEdge;
      o->value.action.state |= CompActionStateInitEdgeDnd;
      o->value.action.type = CompBindingTypeKey;
      o->value.action.state |= CompActionStateInitKey;
      o->value.action.state |= CompActionStateInitButton;
      o->value.action.key.modifiers =
                  SCALE_INITIATE_CURRENT_HEAD_MODIFIERS_DEFAULT;
      o->value.action.key.keysym =
                  XStringToKeysym(SCALE_INITIATE_CURRENT_HEAD_KEY_DEFAULT);

}

static CompOption *scaleGetDisplayOptions(CompDisplay * display, int *count)
{
      if (display)
      {
            SCALE_DISPLAY(display);

            *count = NUM_OPTIONS(sd);
            return sd->opt;
      }
      else
      {
            ScaleDisplay *sd = malloc(sizeof(ScaleDisplay));

            scaleDisplayInitOptions(sd);
            *count = NUM_OPTIONS(sd);
            return sd->opt;
      }
}


static Bool scaleInitDisplay(CompPlugin * p, CompDisplay * d)
{
      ScaleDisplay *sd;

      sd = malloc(sizeof(ScaleDisplay));
      if (!sd)
            return FALSE;

      sd->screenPrivateIndex = allocateScreenPrivateIndex(d);
      if (sd->screenPrivateIndex < 0)
      {
            free(sd);
            return FALSE;
      }

      sd->lastActiveNum = None;

      scaleDisplayInitOptions(sd);

      sd->leftKeyCode = XKeysymToKeycode(d->display, XStringToKeysym("Left"));
      sd->rightKeyCode = XKeysymToKeycode(d->display, XStringToKeysym("Right"));
      sd->upKeyCode = XKeysymToKeycode(d->display, XStringToKeysym("Up"));
      sd->downKeyCode = XKeysymToKeycode(d->display, XStringToKeysym("Down"));

      WRAP(sd, d, handleEvent, scaleHandleEvent);

      d->privates[displayPrivateIndex].ptr = sd;

      return TRUE;
}

static void scaleFiniDisplay(CompPlugin * p, CompDisplay * d)
{
      SCALE_DISPLAY(d);

      freeScreenPrivateIndex(d, sd->screenPrivateIndex);

      UNWRAP(sd, d, handleEvent);

      free(sd);
}

static Bool scaleInitScreen(CompPlugin * p, CompScreen * s)
{
      ScaleScreen *ss;

      SCALE_DISPLAY(s->display);

      ss = malloc(sizeof(ScaleScreen));
      if (!ss)
            return FALSE;
      ss->clicked = FALSE;

      ss->windowPrivateIndex = allocateWindowPrivateIndex(s);
      if (ss->windowPrivateIndex < 0)
      {
            free(ss);
            return FALSE;
      }

      ss->grab = FALSE;
      ss->grabIndex = 0;

      ss->hoverHandle = 0;
      ss->dndTarget = None;

      ss->state = SCALE_STATE_NONE;
      ss->useClass = SCALE_USE_CLASS_DEFAULT;

      ss->slots = 0;
      ss->slotsSize = 0;

      ss->windows = 0;
      ss->windowsSize = 0;

      ss->line = 0;
      ss->lineSize = 0;

      ss->scale = 1.0f;

      ss->spacing = SCALE_SPACING_DEFAULT;
      ss->speed = SCALE_SPEED_DEFAULT;
      ss->timestep = SCALE_TIMESTEP_DEFAULT;
      ss->opacity = (OPAQUE * SCALE_OPACITY_DEFAULT) / 100;
      ss->darkenBackFactor = SCALE_DARKEN_BACK_FACTOR_DEFAULT;

      ss->onlyCurrent = FALSE;
      ss->currentWindow = 0;
      ss->selectedWindow = 0;
      ss->allScreensMode = FALSE;

      ss->darkenBack = SCALE_DARKEN_BACK_DEFAULT;
      ss->currentHead = FALSE;

      ss->head = 0;

      scaleSetHead(ss, s, SCALE_HEAD_DEFAULT);

      ss->iconOverlay = ScaleIconEmblem;
      ss->scaleMethod = SCALE_METHOD_INT_DEFAULT;

      ss->mmMode = MULTIMONITOR_MODE_DEFAULT;

      ss->scaleStateAtom =
                  IPCS_GetAtom(IPCS_OBJECT(s), IPCS_VPTR, "SCALE_STATE_INT_PTR",
                                     TRUE);
      IPCS_SetVPtr(IPCS_OBJECT(s), ss->scaleStateAtom, (void *)&ss->state);

      scaleScreenInitOptions(ss);

      addScreenAction(s,
                              &sd->opt[SCALE_DISPLAY_OPTION_INITIATE_NORMAL].
                              value.action);
      addScreenAction(s,
                              &sd->opt[SCALE_DISPLAY_OPTION_INITIATE_APP].value.action);
      addScreenAction(s,
                              &sd->opt[SCALE_DISPLAY_OPTION_INITIATE_ALL].value.action);
      addScreenAction(s,
                              &sd->
                              opt[SCALE_DISPLAY_OPTION_INITIATE_CURRENT_HEAD].
                              value.action);

      WRAP(ss, s, preparePaintScreen, scalePreparePaintScreen);
      WRAP(ss, s, donePaintScreen, scaleDonePaintScreen);
      WRAP(ss, s, paintScreen, scalePaintScreen);
      WRAP(ss, s, paintWindow, scalePaintWindow);
      WRAP(ss, s, damageWindowRect, scaleDamageWindowRect);

      ss->cursor = XCreateFontCursor(s->display->display, XC_left_ptr);

      s->privates[sd->screenPrivateIndex].ptr = ss;

      return TRUE;
}

static void scaleFiniScreen(CompPlugin * p, CompScreen * s)
{
      SCALE_SCREEN(s);
      SCALE_DISPLAY(s->display);

      UNWRAP(ss, s, preparePaintScreen);
      UNWRAP(ss, s, donePaintScreen);
      UNWRAP(ss, s, paintScreen);
      UNWRAP(ss, s, paintWindow);
      UNWRAP(ss, s, damageWindowRect);

      removeScreenAction(s,
                                 &sd->opt[SCALE_DISPLAY_OPTION_INITIATE_NORMAL].
                                 value.action);
      removeScreenAction(s,
                                 &sd->opt[SCALE_DISPLAY_OPTION_INITIATE_APP].
                                 value.action);
      removeScreenAction(s,
                                 &sd->opt[SCALE_DISPLAY_OPTION_INITIATE_ALL].
                                 value.action);
      removeScreenAction(s,
                                 &sd->
                                 opt[SCALE_DISPLAY_OPTION_INITIATE_CURRENT_HEAD].
                                 value.action);

      IPCS_Unset(IPCS_OBJECT(s), ss->scaleStateAtom);

      if (ss->slotsSize)
            free(ss->slots);

      if (ss->lineSize)
            free(ss->line);

      if (ss->windowsSize)
            free(ss->windows);

      XFreeCursor(s->display->display, ss->cursor);

      freeWindowPrivateIndex(s, ss->windowPrivateIndex);

      free(ss);
}

static Bool scaleInitWindow(CompPlugin * p, CompWindow * w)
{
      ScaleWindow *sw;

      SCALE_SCREEN(w->screen);

      sw = malloc(sizeof(ScaleWindow));
      if (!sw)
            return FALSE;

      sw->animationAtom =
                  IPCS_GetAtom(IPCS_OBJECT(w), IPCS_BOOL, "IS_ANIMATED", True);
      sw->slot = 0;
      sw->scale = 1.0f;
      sw->tx = sw->ty = 0.0f;
      sw->adjust = FALSE;
      sw->xVelocity = sw->yVelocity = 0.0f;
      sw->scaleVelocity = 1.0f;
      sw->delta = 1.0f;
      sw->rescaled = FALSE;
      sw->wasMinimized = FALSE;
      sw->oldScale = -1.0f;

      w->privates[ss->windowPrivateIndex].ptr = sw;

      setWinPort(w);

      return TRUE;
}

static void scaleFiniWindow(CompPlugin * p, CompWindow * w)
{
      SCALE_WINDOW(w);

      free(sw);
}

static Bool scaleInit(CompPlugin * p)
{
      displayPrivateIndex = allocateDisplayPrivateIndex();
      if (displayPrivateIndex < 0)
            return FALSE;

      return TRUE;
}

static void scaleFini(CompPlugin * p)
{
      if (displayPrivateIndex >= 0)
            freeDisplayPrivateIndex(displayPrivateIndex);
}

CompPluginDep setDeps[] = {
      {CompPluginRuleAfter, "decoration"}
      ,
      {CompPluginRuleAfter, "animation"}
      ,
      {CompPluginRuleAfter, "cube"}
      ,
      {CompPluginRuleAfter, "wobbly"}
      ,
      {CompPluginRuleAfter, "fade"}
      ,
};

CompPluginVTable scaleVTable = {
      "scale",
      N_("Scale"),
      N_("Scale windows"),
      scaleInit,
      scaleFini,
      scaleInitDisplay,
      scaleFiniDisplay,
      scaleInitScreen,
      scaleFiniScreen,
      scaleInitWindow,
      scaleFiniWindow,
      scaleGetDisplayOptions,
      scaleSetDisplayOption,
      scaleGetScreenOptions,
      scaleSetScreenOption,
      setDeps,
      sizeof(setDeps) / sizeof(setDeps[0]),
      0,
      0,
      BERYL_ABI_INFO,
      "beryl-plugins",
      "wm",
      0,
      0,
      True,
};

CompPluginVTable *getCompPluginInfo(void)
{
      return &scaleVTable;
}

Generated by  Doxygen 1.6.0   Back to index