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

resize.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>
 * Reworked by Nigel Cunningham <nigel@suspend2.net>
 * Fixes to outline painting and constraining 
 *   code by Danny Baumann <maniac@beryl-project.org>
 *
 * Parts of this code are taken from metacity and written by
 *   Eliah Newren, Rob Adams and others
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <X11/cursorfont.h>

#include <beryl.h>

#define ResizeUpMask    (1L << 0)
#define ResizeDownMask  (1L << 1)
#define ResizeLeftMask  (1L << 2)
#define ResizeRightMask (1L << 3)

#define RESIZE_INITIATE_BUTTON_DEFAULT    Button2
#define RESIZE_INITIATE_BUTTON_MODIFIERS_DEFAULT CompAltMask

#define RESIZE_OPACITY_DEFAULT 100
#define RESIZE_OPACITY_MIN     1
#define RESIZE_OPACITY_MAX     100

#define RESIZE_OPACIFY_NON_OPAQUE_DEFAULT FALSE

#define RESIZE_OPACITY_DEFAULT 100
#define RESIZE_OPACITY_MIN     1
#define RESIZE_OPACITY_MAX     100

#define RESIZE_OPACIFY_MIN_OPACITY_DEFAULT 80
#define RESIZE_OPACIFY_MIN_OPACITY_MIN     1
#define RESIZE_OPACIFY_MIN_OPACITY_MAX     100

#define RESIZE_SYNC_WINDOW_DEFAULT FALSE
#define RESIZE_WARP_POINTER_DEFAULT FALSE

#define RESIZE_INITIATE_KEY_DEFAULT "F8"
#define RESIZE_INITIATE_KEY_MODIFIERS_DEFAULT CompAltMask

struct _ResizeKeys
{
      char *name;
      int dx;
      int dy;
      unsigned int warpMask;
      unsigned int resizeMask;
} rKeys[] =
{
      {"Left", -1, 0, ResizeLeftMask | ResizeRightMask, ResizeLeftMask},
      {"Right", 1, 0, ResizeLeftMask | ResizeRightMask, ResizeRightMask},
      {"Up", 0, -1, ResizeUpMask | ResizeDownMask, ResizeUpMask},
      {"Down", 0, 1, ResizeUpMask | ResizeDownMask, ResizeDownMask}
};

#define NUM_KEYS (sizeof (rKeys) / sizeof (rKeys[0]))

#define MIN_KEY_WIDTH_INC  24
#define MIN_KEY_HEIGHT_INC 24

#define RESIZE_DISPLAY_OPTION_INITIATE                0
#define RESIZE_DISPLAY_OPTION_OPACITY                 1
#define RESIZE_DISPLAY_OPTION_OPACIFY_MIN_OPACITY     2
#define RESIZE_DISPLAY_OPTION_SYNC_WINDOW       3
#define RESIZE_DISPLAY_OPTION_WARP_POINTER            4
#define RESIZE_DISPLAY_OPTION_MODE              5
#define RESIZE_DISPLAY_OPTION_BORDER_COLOR            6
#define RESIZE_DISPLAY_OPTION_FILL_COLOR        7
#define RESIZE_DISPLAY_OPTION_NUM               8

static int displayPrivateIndex;

/* Order is important. In code, we check for > Stretch. */
typedef enum _ResizeMode
{
      ResizeModeNormal,
      ResizeModeStretch,
      ResizeModeOutline,
      ResizeModeFilled,
} ResizeMode;

char *resizeModes[] = {
      N_("Normal"),
      N_("Stretch"),
      N_("Outline"),
      N_("Filled Outline")
};

#define RESIZE_MODE_DEFAULT ResizeModeNormal
#define NUM_RESIZE_MODES 4

/* Ignore the NumLock and CapsLock keys. */
#define WARP_IGNORE_MASK (~(LockMask | Mod2Mask))

typedef struct _ResizeDisplay
{
      CompOption opt[RESIZE_DISPLAY_OPTION_NUM];

      int screenPrivateIndex;
      HandleEventProc handleEvent;

      CompWindow *w;
      XWindowAttributes savedAttrib;
      int releaseButton;
      unsigned int mask;
      int width;
      int height;
      KeyCode key[NUM_KEYS];

      int lastWidth;
      int lastHeight;
      int currentWidth;
      int currentHeight;
      int currentX;
      int currentY;
      int xdelta, ydelta;                 /* Offset when resize started */

      int dx_to_apply, dy_to_apply, width_to_apply, height_to_apply;

      int right_edge, bottom_edge;

      GLushort resizeOpacity;
      GLushort opacifyMinOpacity;

      int resizeMode;
      Bool ungrabPending;

      GLushort border[4];
      GLushort fill[4];
} ResizeDisplay;

typedef struct _ResizeScreen
{
      int grabIndex;
      Bool painted;
      int x, y;

      PaintWindowProc paintWindow;
      PreparePaintScreenProc preparePaintScreen;
      PaintScreenProc paintScreen;
      PaintTransformedScreenProc paintTransformedScreen;
      DonePaintScreenProc donePaintScreen;

      Cursor leftCursor;
      Cursor rightCursor;
      Cursor upCursor;
      Cursor upLeftCursor;
      Cursor upRightCursor;
      Cursor downCursor;
      Cursor downLeftCursor;
      Cursor downRightCursor;
      Cursor middleCursor;
      Cursor cursor[NUM_KEYS];
} ResizeScreen;

#define GET_RESIZE_DISPLAY(d)                            \
      ((ResizeDisplay *) (d)->privates[displayPrivateIndex].ptr)

#define RESIZE_DISPLAY(d)                    \
      ResizeDisplay *rd = GET_RESIZE_DISPLAY (d)

#define GET_RESIZE_SCREEN(s) \
      ((ResizeScreen *) (s)->privates[(GET_RESIZE_DISPLAY(s->display))->screenPrivateIndex].ptr)

#define RESIZE_SCREEN(s) ResizeScreen *rs = GET_RESIZE_SCREEN(s)

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

static void resizeUpdateWindowRealSize(CompDisplay * d, int move_only)
{
      RESIZE_DISPLAY(d);
      XWindowChanges xwc;
      unsigned int xwcm = 0;

      if (!rd->w || rd->w->syncWait)
            return;

      if (move_only)
      {
            moveWindow(rd->w, rd->currentX - rd->w->attrib.x,
                           rd->currentY - rd->w->attrib.y, TRUE, TRUE);
            syncWindowPosition(rd->w);
      }
      else
      {
            if (rd->currentX != rd->w->serverX)
                  xwcm |= CWX;
            if (rd->currentY != rd->w->serverY)
                  xwcm |= CWY;
            if (rd->currentWidth != rd->w->serverWidth)
                  xwcm |= CWWidth;
            if (rd->currentHeight != rd->w->serverHeight)
                  xwcm |= CWHeight;

            xwc.x = rd->currentX;
            xwc.y = rd->currentY;
            xwc.width = rd->currentWidth;
            xwc.height = rd->currentHeight;

            if (rd->resizeMode == ResizeModeNormal
                  && rd->opt[RESIZE_DISPLAY_OPTION_SYNC_WINDOW].value.b)
                  sendSyncRequest(rd->w);

            configureXWindow(rd->w, xwcm, &xwc);
      }
}

#define CLAMP(v, min, max) ((v) <= (min) ? (min) : (v) >= (max) ? (max) : (v))

static void
resizeConstrainMinMax(CompWindow * w,
                                int width, int height, int *newWidth, int *newHeight)
{
      const XSizeHints *hints = &w->sizeHints;
      int min_width = 0;
      int min_height = 0;
      int max_width = MAXSHORT;
      int max_height = MAXSHORT;

      if ((hints->flags & PBaseSize) && (hints->flags & PMinSize))
      {
            min_width = hints->min_width;
            min_height = hints->min_height;
      }
      else if (hints->flags & PBaseSize)
      {
            min_width = hints->base_width;
            min_height = hints->base_height;
      }
      else if (hints->flags & PMinSize)
      {
            min_width = hints->min_width;
            min_height = hints->min_height;
      }

      if (hints->flags & PMaxSize)
      {
            max_width = hints->max_width;
            max_height = hints->max_height;
      }

      /* clamp width and height to min and max values */
      width = CLAMP(width, min_width, max_width);
      height = CLAMP(height, min_height, max_height);

      *newWidth = width;
      *newHeight = height;
}

static void
resizeConstrainResizeIncrement(CompWindow * w, int width, int height, int *newWidth, int *newHeight)
{
      int baseWidth, baseHeight;
      const XSizeHints *hints = &w->sizeHints;

      if (hints->flags & PResizeInc) {
            if (hints->flags & PBaseSize) {
                  baseWidth = hints->base_width;
                  baseHeight = hints->base_height;
            } else {
                  baseWidth = 0;
                  baseHeight = 0;
            }

            if ((width - baseWidth) % hints->width_inc) 
                  width = baseWidth + (((width - baseWidth) / hints->width_inc) * hints->width_inc);

            if ((height - baseHeight) % hints->height_inc) 
                  height = baseHeight + (((height - baseHeight) / hints->height_inc) * hints->height_inc);
      }

      *newWidth = width;
      *newHeight = height;
}

/* this code has its origin in metacity's boxes.c - thanks! */
static void
resizeFindLinePointClosestToPoint (double x1, double y1, 
            double x2, double y2, double px, double py, double *valx, double *valy)
{
  /* I'll use the shorthand rx, ry for the return values, valx & valy.
   * Now, we need (rx,ry) to be on the line between (x1,y1) and (x2,y2).
   * For that to happen, we first need the slope of the line from (x1,y1)
   * to (rx,ry) must match the slope of (x1,y1) to (x2,y2), i.e.:
   *   (ry-y1)   (y2-y1)
   *   ------- = -------
   *   (rx-x1)   (x2-x1)
   * If x1==x2, though, this gives divide by zero errors, so we want to
   * rewrite the equation by multiplying both sides by (rx-x1)*(x2-x1):
   *   (ry-y1)(x2-x1) = (y2-y1)(rx-x1)
   * This is a valid requirement even when x1==x2 (when x1==x2, this latter
   * equation will basically just mean that rx must be equal to both x1 and
   * x2)
   *
   * The other requirement that we have is that the line from (rx,ry) to
   * (px,py) must be perpendicular to the line from (x1,y1) to (x2,y2).  So
   * we just need to get a vector in the direction of each line, take the
   * dot product of the two, and ensure that the result is 0:
   *   (rx-px)*(x2-x1) + (ry-py)*(y2-y1) = 0.
   *
   * This gives us two equations and two unknowns:
   *
   *   (ry-y1)(x2-x1) = (y2-y1)(rx-x1)
   *   (rx-px)*(x2-x1) + (ry-py)*(y2-y1) = 0.
   *
   * This particular pair of equations is always solvable so long as
   * (x1,y1) and (x2,y2) are not the same point (and note that anyone who
   * calls this function that way is braindead because it means that they
   * really didn't specify a line after all).  However, the caller should
   * be careful to avoid making (x1,y1) and (x2,y2) too close (e.g. like
   * 10^{-8} apart in each coordinate), otherwise roundoff error could
   * cause issues.  Solving these equations by hand (or using Maple(TM) or
   * Mathematica(TM) or whatever) results in slightly messy expressions,
   * but that's all the below few lines do.
   */

      double diffx, diffy, den;
      diffx = x2 - x1;
      diffy = y2 - y1;
      den = diffx * diffx + diffy * diffy;

      *valx = (py * diffx * diffy + px * diffx * diffx +
             y2 * x1 * diffy - y1 * x2 * diffy) / den;
      *valy = (px * diffx * diffy + py * diffy * diffy +
                   x2 * y1 * diffx - x1 * y2 * diffx) / den;
}

/* this function is heavily inspired by metacity's 
   constrain_aspect_ratio - thanks another time! */
static Bool
resizeConstrainAspectRatio(CompWindow * w, int width, int height, int *retWidth, int *retHeight)
{
      RESIZE_DISPLAY(w->screen->display);
      const XSizeHints *hints = &w->sizeHints;
      double minRatio, maxRatio;
      int newWidth, newHeight;
      double altWidth, altHeight;
      double bestWidth, bestHeight;
      int tolerance = 1;
      Bool resizeLeftRight = FALSE;
      Bool resizeUpDown = FALSE;

      newWidth = width;
      newHeight = height;

      if (hints->flags & PAspect) {
            minRatio = hints->min_aspect.x / (double) hints->min_aspect.y;
            maxRatio = hints->max_aspect.x / (double) hints->max_aspect.y;

            if (minRatio > maxRatio)
                  return FALSE;

            resizeLeftRight = (rd->mask == (rd->mask & ResizeLeftMask)) ||
                                      (rd->mask == (rd->mask & ResizeRightMask));
            resizeUpDown = (rd->mask == (rd->mask & ResizeUpMask)) ||
                                 (rd->mask == (rd->mask & ResizeDownMask));

            if (resizeLeftRight || resizeUpDown)
                  tolerance = 2;

            /* return if constraint already is satisfied */
            if (((width - (height * minRatio)) > (-minRatio * tolerance)) &&
                  ((width - (height * maxRatio)) < (maxRatio * tolerance)))
            {
                  return FALSE;
            }

            if (resizeLeftRight)
                  newHeight = CLAMP (newHeight, newWidth / maxRatio, newWidth / minRatio);
            else if (resizeUpDown)
                  newWidth = CLAMP (newWidth, newHeight * minRatio, newHeight * maxRatio);
            else {
                  altWidth = CLAMP (newWidth, newHeight * minRatio, newHeight * maxRatio);
                  altHeight = CLAMP (newHeight, newWidth / maxRatio, newWidth / minRatio);

                  resizeFindLinePointClosestToPoint(altWidth, newHeight, newWidth, altHeight,
                              newWidth, newHeight, &bestWidth, &bestHeight);

                  newWidth = bestWidth;
                  newHeight = bestHeight;
            }
      }

      *retWidth = newWidth;
      *retHeight = newHeight;

      return TRUE;
}

#undef CLAMP

static void resizeApply(CompDisplay * d)
{
      int move_only = 0, cw, ch;

      RESIZE_DISPLAY(d);

      if ((!rd->dx_to_apply && !rd->dy_to_apply) || rd->w->syncWait)
            return;

      /* Move window by delta if resizing from left/top */
      if (rd->mask & ResizeLeftMask && rd->width_to_apply != rd->currentWidth)
            rd->currentX += rd->dx_to_apply;

      if (rd->mask & ResizeUpMask && rd->height_to_apply != rd->currentHeight)
            rd->currentY += rd->dy_to_apply;

      cw = rd->currentWidth = rd->width_to_apply;
      ch = rd->currentHeight = rd->height_to_apply;

      rd->dx_to_apply = rd->dy_to_apply = 0;

      constrainNewWindowSize(rd->w, rd->currentWidth, rd->currentHeight,
                                       &cw, &ch);

      if (rd->mask & ResizeLeftMask)
            rd->currentX = rd->right_edge - cw;

      if (rd->mask & ResizeUpMask)
            rd->currentY = rd->bottom_edge - ch;

      rd->currentWidth = cw;
      rd->currentHeight = ch;

      switch (rd->resizeMode)
      {
            case ResizeModeStretch:
                  move_only = 1;
            case ResizeModeNormal:
                  resizeUpdateWindowRealSize(d, move_only);
      }

      if (rd->resizeMode != ResizeModeNormal)
            resizeWindowPreview (rd->w, rd->currentX, rd->currentY,
                                           rd->currentWidth, rd->currentHeight);
}

static void resizeUpdateWindowSize(CompDisplay * d, int dx, int dy)
{
      int w, h, new_height, new_width;

      RESIZE_DISPLAY(d);

      if (!rd->w)
            return;

      if (rd->resizeMode != ResizeModeNormal)
            damageScreen(rd->w->screen);

      if (rd->w->state & CompWindowStateMaximizedVertMask)
            rd->currentHeight = rd->w->serverHeight;

      if (rd->w->state & CompWindowStateMaximizedHorzMask)
            rd->currentWidth = rd->w->serverWidth;

      if (rd->w->state & CompWindowStateMaximizedVertMask &&
            rd->w->state & CompWindowStateMaximizedHorzMask)
            return;

      w = rd->currentWidth;
      h = rd->currentHeight;

      /* Ensure we don't push the window if constraints stop it being
       * resized. */
      if (!(rd->mask & (ResizeLeftMask | ResizeRightMask)))
            dx = 0;

      if (!(rd->mask & (ResizeUpMask | ResizeDownMask)))
            dy = 0;

      /* Apply the width/height modifications */
      if (rd->mask & ResizeLeftMask)
            w -= dx;
      else if (rd->mask & ResizeRightMask)
            w += dx;

      if (rd->mask & ResizeUpMask)
            h -= dy;
      else if (rd->mask & ResizeDownMask)
            h += dy;

      /* Apply constraints */
      resizeConstrainResizeIncrement(rd->w, w, h, &w, &h);

      new_width = w;
      new_height = h;
      resizeConstrainAspectRatio(rd->w, w, h, &new_width, &new_height);
      resizeConstrainMinMax(rd->w, new_width, new_height, &new_width, &new_height);

      if ((w != new_width) || (h != new_height))
      {
            /* if the resizing hit constraints, move the mouse
               pointer to the new border to avoid desynchronization */
            int pointerAdjustX = 0, pointerAdjustY = 0;

            if (rd->mask & ResizeRightMask)
                  pointerAdjustX = new_width - w;
            else if (rd->mask & ResizeLeftMask)
                  pointerAdjustX = w - new_width;

            if (rd->mask & ResizeDownMask)
                  pointerAdjustY = new_height - h;
            else if (rd->mask & ResizeUpMask)
                  pointerAdjustY = h - new_height;

            warpPointer(d, pointerAdjustX, pointerAdjustY);
      }

      dx -= new_width - w;
      dy -= new_height - h;

      /* Avoid annoying jiggling if we try to move 1 pixel */

      if (abs(dx) < 2)
            dx = 0;

      if (abs(dy) < 2)
            dy = 0;

      /* If no resizing, drop out now. */
      if (!dx && !dy)
            return;

      rd->dx_to_apply = dx;
      rd->dy_to_apply = dy;
      rd->width_to_apply = new_width;
      rd->height_to_apply = new_height;

      resizeApply(d);
}

static Bool
resizeInitiate(CompDisplay * d,
                     CompAction * action,
                     CompActionState state, CompOption * option, int nOption)
{
      CompWindow *w;
      ResizeScreen *rs;
      Window xid;

      unsigned int mods;
      unsigned int mask;
      int x, y;
      int button;

      RESIZE_DISPLAY(d);

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

      w = findWindowAtDisplay(d, xid);
      if (!w)
            return FALSE;

      rs = GET_RESIZE_SCREEN(w->screen);

      rd->dx_to_apply = rd->dy_to_apply = 0;

      mods = getIntOptionNamed(option, nOption, "modifiers", 0);

      x = getIntOptionNamed(option, nOption, "x", w->attrib.x + (w->width / 2));
      y = getIntOptionNamed(option, nOption, "y",
                                      w->attrib.y + (w->height / 2));

      button = getIntOptionNamed(option, nOption, "button", -1);

      mask = getIntOptionNamed(option, nOption, "direction", 0);

      /* Initiate the resize in the direction suggested by the
       * quarter of the window the mouse is in, eg drag in top left
       * will resize up and to the left.  Keyboard resize starts out
       * with the cursor in the middle of the window and then starts
       * resizing the edge corresponding to the next key press. */
      if (state & CompActionStateInitKey)
            mask = 0;
      else if (!mask)
      {
            mask |= ((x - w->attrib.x) < (w->width / 2)) ?
                        ResizeLeftMask : ResizeRightMask;

            mask |= ((y - w->attrib.y) < (w->height / 2)) ?
                        ResizeUpMask : ResizeDownMask;
      }

      if (otherScreenGrabExist(w->screen, "resize", 0))
            return FALSE;

      if (rd->w)
            return FALSE;

      if (w->type & (CompWindowTypeDesktopMask |
                           CompWindowTypeDockMask | CompWindowTypeFullscreenMask))
            return FALSE;

      if (w->attrib.override_redirect)
            return FALSE;

      if (state & CompActionStateInitButton)
            action->state |= CompActionStateTermButton;

      if (w->shaded)
            mask &= ~(ResizeUpMask | ResizeDownMask);

      rd->w = w;
      rd->mask = mask;
      rd->currentWidth = rd->width = w->serverWidth;
      rd->currentHeight = rd->height = w->serverHeight;
      rd->currentX = w->attrib.x;
      rd->currentY = w->attrib.y;
      rd->savedAttrib = w->attrib;
      rd->right_edge = rd->currentX + rd->currentWidth;
      rd->bottom_edge = rd->currentY + rd->currentHeight;
      rd->xdelta = rd->ydelta = 0;

      if (rd->resizeMode != ResizeModeNormal)
            rd->lastWidth = rd->lastHeight = 0.0f;
      else
            addWindowDamage(rd->w);

      d->lastPointerX = x;
      d->lastPointerY = y;

      if (!rs->grabIndex)
      {
            Cursor cursor;

            if (state & CompActionStateInitKey)
                  cursor = rs->middleCursor;
            else if (mask & ResizeLeftMask)
            {
                  if (mask & ResizeDownMask)
                        cursor = rs->downLeftCursor;
                  else if (mask & ResizeUpMask)
                        cursor = rs->upLeftCursor;
                  else
                        cursor = rs->leftCursor;
            }
            else if (mask & ResizeRightMask)
            {
                  if (mask & ResizeDownMask)
                        cursor = rs->downRightCursor;
                  else if (mask & ResizeUpMask)
                        cursor = rs->upRightCursor;
                  else
                        cursor = rs->rightCursor;
            }
            else if (mask & ResizeUpMask)
                  cursor = rs->upCursor;
            else
                  cursor = rs->downCursor;

            rs->grabIndex = pushScreenGrab(w->screen, cursor, "resize");
      }

      if (rs->grabIndex)
      {
            int mods_sought = virtualToRealModMask(d,
                                                                     rd->
                                                                     opt
                                                                     [RESIZE_DISPLAY_OPTION_INITIATE].
                                                                     value.action.button.modifiers);
            int button_sought =
                        rd->opt[RESIZE_DISPLAY_OPTION_INITIATE].value.action.
                        button.button;

            rd->releaseButton = button;

            /* FIXME: we use CompWindowGrabButtonMask only for the normal mode
               to prevent wobbling for the other modes
               this is fine for now, but may break the API (documentation would be nice...) */

            Bool wobbleDesired = (rd->resizeMode == ResizeModeNormal);

            wobbleDesired |= (!(mask & ~(ResizeUpMask | ResizeDownMask))) &&
                        (w->state & CompWindowStateMaximizedVertMask);
            wobbleDesired |= (!(mask & ~(ResizeLeftMask | ResizeRightMask))) &&
                        (w->state & CompWindowStateMaximizedHorzMask);

            (w->screen->windowGrabNotify) (w, x, y, state,
                                                         CompWindowGrabResizeMask |
                                                         (wobbleDesired ?
                                                            CompWindowGrabButtonMask : 0));

            if (state & CompActionStateInitKey)
            {
                  x = w->attrib.x + (w->width / 2);
                  y = w->attrib.y + (w->height / 2);
                  warpPointer(d, x - d->pointerX, y - d->pointerY);
            }
            else if ((mods & WARP_IGNORE_MASK) == mods_sought &&
                         button == button_sought &&
                         rd->opt[RESIZE_DISPLAY_OPTION_WARP_POINTER].value.b)
            {
                  /* May warp if initiated with mouse combo */
                  x = w->attrib.x - w->input.left +
                              ((mask & ResizeLeftMask) ? 0 : rd->width +
                               w->input.left + w->input.right);
                  y = w->attrib.y - w->input.top +
                              ((mask & ResizeUpMask) ? 0 : rd->height +
                               w->input.top + w->input.bottom);
                  warpPointer(d, x - d->pointerX, y - d->pointerY);
            }

            rd->xdelta = (x - rd->currentX);
            if (rd->mask & ResizeRightMask)
                  rd->xdelta = rd->width - rd->xdelta;

            rd->ydelta = (y - rd->currentY);
            if (rd->mask & ResizeDownMask)
                  rd->ydelta = rd->height - rd->ydelta;

      }

      return FALSE;
}

static Bool
resizeTerminate(CompDisplay * d,
                        CompAction * action,
                        CompActionState state, CompOption * option, int nOption)
{
      RESIZE_DISPLAY(d);
      ResizeScreen *rs;

      action->state &= ~(CompActionStateTermKey | CompActionStateTermButton);

      if (!rd->w)
            return FALSE;

      rs = GET_RESIZE_SCREEN(rd->w->screen);

      if (state & CompActionStateCancel)
      {
            XWindowChanges xwc;

            sendSyncRequest(rd->w);

            xwc.x = rd->savedAttrib.x;
            xwc.y = rd->savedAttrib.y;
            xwc.width = rd->savedAttrib.width;
            xwc.height = rd->savedAttrib.height;

            if ((xwc.x != rd->w->serverX) || (xwc.y != rd->w->serverY) ||
                  (xwc.width != rd->w->serverWidth) || (xwc.height != rd->w->serverHeight))
            {
                  rd->ungrabPending = TRUE;
            } else {
                  (rd->w->screen->windowUngrabNotify) (rd->w);
            }
            configureXWindow(rd->w, CWX | CWY | CWWidth | CWHeight, &xwc);
      } else if (rd->resizeMode != ResizeModeNormal
                   && (rd->currentX != rd->w->serverX
                         || rd->currentY != rd->w->serverY
                         || rd->currentWidth != rd->w->serverWidth
                         || rd->currentHeight != rd->w->serverHeight)) {
            rd->ungrabPending = TRUE;
            resizeUpdateWindowRealSize(d, 0);
      } else {
            syncWindowPosition(rd->w);
            (rd->w->screen->windowUngrabNotify) (rd->w);
      }

      if (rs->grabIndex)
      {
            removeScreenGrab(rd->w->screen, rs->grabIndex, NULL);
            rs->grabIndex = 0;
      }

      addWindowDamage(rd->w);

      if (rd->resizeMode != ResizeModeNormal)
            damageScreen(rd->w->screen);

      if (!rd->ungrabPending)
            rd->w = NULL;

      rd->releaseButton = 0;

      return FALSE;
}

static void resizeHandleKeyEvent(CompScreen * s, KeyCode keycode)
{
      RESIZE_SCREEN(s);
      RESIZE_DISPLAY(s->display);

      if (rs->grabIndex && rd->w)
      {
            CompWindow *w;
            int i, widthInc, heightInc;

            w = rd->w;

            widthInc = w->sizeHints.width_inc;
            heightInc = w->sizeHints.height_inc;

            /* if the resize increment hint is smaller than our
               minimum amount, then resize by the next multiple
               of the hint value large than our minimum */
            if (widthInc < MIN_KEY_WIDTH_INC)
                  widthInc = ((MIN_KEY_WIDTH_INC / widthInc) + 1) * widthInc;

            if (heightInc < MIN_KEY_HEIGHT_INC)
                  heightInc = ((MIN_KEY_HEIGHT_INC / heightInc) + 1) * heightInc;

            for (i = 0; i < NUM_KEYS; i++)
            {
                  if (keycode != rd->key[i])
                        continue;

                  if (rd->mask & rKeys[i].warpMask)
                        XWarpPointer(s->display->display, None,
                                           None, 0, 0, 0, 0,
                                           rKeys[i].dx * widthInc, rKeys[i].dy * heightInc);
                  else
                  {
                        int x, y, left, top, width, height;

                        left = w->attrib.x - w->input.left;
                        top = w->attrib.y - w->input.top;
                        width = w->input.left + w->serverWidth + w->input.right;
                        height = w->input.top + w->serverHeight + w->input.bottom;

                        x = left + width * (rKeys[i].dx + 1) / 2;
                        y = top + height * (rKeys[i].dy + 1) / 2;

                        rd->mask = rKeys[i].resizeMask;

                        /* recalculate delta */
                        rd->xdelta = (x - rd->currentX);
                        if (rd->mask & ResizeRightMask)
                              rd->xdelta = rd->width - rd->xdelta;

                        rd->ydelta = (y - rd->currentY);
                        if (rd->mask & ResizeDownMask)
                              rd->ydelta = rd->height - rd->ydelta;

                        warpPointer(s->display,
                                          x - s->display->pointerX,
                                          y - s->display->pointerY);
                        updateScreenGrab(s, rs->grabIndex, rs->cursor[i]);
                  }

                  break;
            }
      }
}

static void resizeHandleMotionEvent(CompScreen * s, int xRoot, int yRoot)
{
      int pointerDx, pointerDy;

      RESIZE_SCREEN(s);
      RESIZE_DISPLAY(s->display);

      if (!rs->grabIndex)
            return;

      if (rd->mask == 0 && rd->w)
      {
            rd->mask =
                        ((xRoot - rd->w->attrib.x) <
                         (rd->w->width / 2)) ? ResizeLeftMask : ResizeRightMask;
            rd->mask |=
                        ((yRoot - rd->w->attrib.y) <
                         (rd->w->height / 2)) ? ResizeUpMask : ResizeDownMask;
      }

      pointerDx = xRoot - rd->currentX;
      pointerDy = yRoot - rd->currentY;

      if (rd->mask & ResizeRightMask)
            pointerDx -= (rd->currentWidth - rd->xdelta);

      if (rd->mask & ResizeLeftMask)
            pointerDx -= rd->xdelta;

      if (rd->mask & ResizeDownMask)
            pointerDy -= (rd->currentHeight - rd->ydelta);

      if (rd->mask & ResizeUpMask)
            pointerDy -= rd->ydelta;

      /* Any motion? */
      if (!pointerDx && !pointerDy)
            return;

      resizeUpdateWindowSize(s->display, pointerDx, pointerDy);
}

static void resizeHandleMoveResizeMessage(CompDisplay * d, XEvent * event)
{
      CompWindow *w;
      CompOption o[6];
      CompAction *action;

      RESIZE_DISPLAY(d);

      if (event->xclient.data.l[2] > WmMoveResizeSizeLeft &&
            event->xclient.data.l[2] != WmMoveResizeSizeKeyboard)
            return;

      w = findWindowAtDisplay(d, event->xclient.window);

      if (!w)
            return;

      action = &rd->opt[RESIZE_DISPLAY_OPTION_INITIATE].value.action;

      o[0].type = CompOptionTypeInt;
      o[0].name = "window";
      o[0].value.i = event->xclient.window;

      if (event->xclient.data.l[2] == WmMoveResizeSizeKeyboard)
      {
            o[1].type = CompOptionTypeInt;
            o[1].name = "button";
            o[1].value.i = 0;

            resizeInitiate(d, action, CompActionStateInitKey, o, 2);
      }
      else
      {
            static unsigned int mask[] = {
                  ResizeUpMask | ResizeLeftMask,
                  ResizeUpMask,
                  ResizeUpMask | ResizeRightMask,
                  ResizeRightMask,
                  ResizeDownMask | ResizeRightMask,
                  ResizeDownMask,
                  ResizeDownMask | ResizeLeftMask,
                  ResizeLeftMask,
            };
            unsigned int mods;
            Window root, child;
            int xRoot, yRoot, i;

            XQueryPointer(d->display, w->screen->root,
                                &root, &child, &xRoot, &yRoot, &i, &i, &mods);

            /* TODO: not only button 1 */
            if (mods & Button1Mask)
            {
                  o[1].type = CompOptionTypeInt;
                  o[1].name = "modifiers";
                  o[1].value.i = mods;

                  o[2].type = CompOptionTypeInt;
                  o[2].name = "x";
                  o[2].value.i = event->xclient.data.l[0];

                  o[3].type = CompOptionTypeInt;
                  o[3].name = "y";
                  o[3].value.i = event->xclient.data.l[1];

                  o[4].type = CompOptionTypeInt;
                  o[4].name = "direction";
                  o[4].value.i = mask[event->xclient.data.l[2]];

                  o[5].type = CompOptionTypeInt;
                  o[5].name = "button";
                  o[5].value.i = event->xclient.data.l[3] ?
                              event->xclient.data.l[3] : -1;

                  resizeInitiate(d, action, CompActionStateInitButton, o, 6);

                  resizeHandleMotionEvent(w->screen, xRoot, yRoot);
            }
      }
}

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

      RESIZE_DISPLAY(d);

      switch (event->type)
      {

      case KeyPress:
            s = findScreenAtDisplay(d, event->xkey.root);
            if (s)
                  resizeHandleKeyEvent(s, event->xkey.keycode);
            break;

      case KeyRelease:
            break;

      case ButtonPress:
            s = findScreenAtDisplay(d, event->xbutton.root);
            if (s)
            {
                  RESIZE_SCREEN(s);

                  if (rs->grabIndex)
                  {
                        resizeTerminate(d,
                                                &rd->
                                                opt
                                                [RESIZE_DISPLAY_OPTION_INITIATE].
                                                value.action, 0, NULL, 0);
                  }
            }
            break;

      case ButtonRelease:
      {
            CompAction *action =
                        &rd->opt[RESIZE_DISPLAY_OPTION_INITIATE].value.action;

            if (action->state & CompActionStateTermButton &&
                  (rd->releaseButton == -1 ||
                   event->xbutton.button == rd->releaseButton))
                  resizeTerminate(d, action, CompActionStateTermButton, NULL, 0);
      }
            break;
      case MotionNotify:
            s = findScreenAtDisplay(d, event->xmotion.root);
            if (s)
                  resizeHandleMotionEvent(s, d->pointerX, d->pointerY);
            break;
      case EnterNotify:
      case LeaveNotify:
            s = findScreenAtDisplay(d, event->xcrossing.root);
            if (s)
                  resizeHandleMotionEvent(s, d->pointerX, d->pointerY);
            break;
      case ClientMessage:
            if (event->xclient.message_type == d->wmMoveResizeAtom)
                  resizeHandleMoveResizeMessage(d, event);
            break;
      case DestroyNotify:
            if (rd->w && rd->w->id == event->xdestroywindow.window)
            {
                  CompAction *action =
                              &rd->opt[RESIZE_DISPLAY_OPTION_INITIATE].value.action;
                  resizeTerminate(d, action, 0, NULL, 0);
            }
            break;
      case UnmapNotify:
            if (rd->w && rd->w->id == event->xunmap.window)
            {
                  CompAction *action =
                              &rd->opt[RESIZE_DISPLAY_OPTION_INITIATE].value.action;
                  resizeTerminate(d, action, 0, NULL, 0);
            }
            break;
      default:
            if (event->type == d->syncEvent + XSyncAlarmNotify && rd->w)
            {
                  XSyncAlarmNotifyEvent *sa;

                  sa = (XSyncAlarmNotifyEvent *) event;

                  if (rd->w->syncAlarm == sa->alarm)
                        resizeApply(d);
            }
            break;
      }

      UNWRAP(rd, d, handleEvent);
      (*d->handleEvent) (d, event);
      WRAP(rd, d, handleEvent, resizeHandleEvent);

      switch (event->type) {
      case ConfigureNotify:
            if (rd->ungrabPending) {
                  if (rd->w && (rd->w->id == event->xconfigure.window)) {
                        (rd->w->screen->windowUngrabNotify) (rd->w);
                        syncWindowPosition(rd->w);
                        rd->w = NULL;
                  }
                  rd->ungrabPending = FALSE;
            }
            break;

      default:
            break;
      }
}

static void resizePreparePaintScreen(CompScreen * s, int msSinceLastPaint)
{

      RESIZE_SCREEN(s);
      RESIZE_DISPLAY(s->display);

      if (rd->w && rd->resizeMode > ResizeModeNormal &&
            rs->grabIndex &&
            (rd->lastWidth != rd->currentWidth ||
             rd->lastHeight != rd->currentHeight))
      {
            damageScreen(s);
      }

      UNWRAP(rs, s, preparePaintScreen);
      (*s->preparePaintScreen) (s, msSinceLastPaint);
      WRAP(rs, s, preparePaintScreen, resizePreparePaintScreen);
}

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

      RESIZE_SCREEN(s);
      RESIZE_DISPLAY(s->display);

      if ((rs->grabIndex || rd->ungrabPending) && (rd->w == w))
      {
            sAttrib = *attrib;

            if (rd->resizeMode != ResizeModeNormal)
                  mask |= PAINT_WINDOW_TRANSFORMED_MASK;

            if (rd->resizeMode == ResizeModeStretch)
            {
                  sAttrib.xScale =
                              (float)(rd->currentWidth) /
                              (float)(rd->w->attrib.width);
                  sAttrib.yScale =
                              (float)(rd->currentHeight) /
                              (float)(rd->w->attrib.height);
            }

            if (rd->resizeOpacity != OPAQUE
                  && sAttrib.opacity >= rd->opacifyMinOpacity)
            {
                  sAttrib.opacity = (sAttrib.opacity * rd->resizeOpacity) >> 16;
            }
            attrib = &sAttrib;
      }

      UNWRAP(rs, s, paintWindow);
      status = (*s->paintWindow) (w, attrib, region, mask);
      WRAP(rs, s, paintWindow, resizePaintWindow);

      return status;
}

static void
resizePaintOutline(CompScreen * s, const ScreenPaintAttrib * sa, int output,
                           Bool transformed)
{
      int x1 = 0, x2 = 0, y1 = 0, y2 = 0;

      RESIZE_SCREEN(s);
      RESIZE_DISPLAY(s->display);

      if (rd->resizeMode > ResizeModeStretch && rd->w &&
            !(rd->w->state & MAXIMIZE_STATE) && rs->grabIndex)
      {
            x1 = rd->currentX - rd->w->input.left;
            x2 = rd->currentX + rd->currentWidth + rd->w->input.right;
            y1 = rd->currentY - rd->w->input.top;
            y2 = rd->currentY +
                        (rd->w->shaded ? rd->w->height : rd->currentHeight) +
                        rd->w->input.bottom;

            glPushMatrix();

            if (transformed)
            {
                  glLoadIdentity();
                  (s->applyScreenTransform) (s, sa, output);
            }

            prepareXCoords(s, output,
                                 transformed ? -sa->zTranslate : -DEFAULT_Z_CAMERA);
            glDisableClientState(GL_TEXTURE_COORD_ARRAY);
            glEnable(GL_BLEND);

            if (rd->resizeMode == ResizeModeFilled)
            {
                  glColor4usv(rd->opt[RESIZE_DISPLAY_OPTION_FILL_COLOR].value.c);
                  glRecti(x1, y2, x2, y1);
            }

            /* This section draws the outline */
            glColor4usv(rd->opt[RESIZE_DISPLAY_OPTION_BORDER_COLOR].value.c);
            glLineWidth(2.0);
            glBegin(GL_LINE_LOOP);
            glVertex2i(x1, y1);
            glVertex2i(x2, y1);
            glVertex2i(x2, y2);
            glVertex2i(x1, y2);
            glEnd();

            /* Clean up */
            glColor4usv(defaultColor);
            glDisable(GL_BLEND);
            glEnableClientState(GL_TEXTURE_COORD_ARRAY);
            glPopMatrix();
      }
}

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

      RESIZE_SCREEN(s);
      RESIZE_DISPLAY(s->display);

      rs->painted = FALSE;
      rs->x = s->x;
      rs->y = s->y;

      if (rd->w && rs->grabIndex && rd->resizeMode == ResizeModeStretch)
            mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK;

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

      if (status && !rs->painted)
            resizePaintOutline(s, sAttrib, output, FALSE);

      return status;
}

static void
resizePaintTransformedScreen(CompScreen * s, const ScreenPaintAttrib * sa,
                                           Region region, int output, unsigned int mask)
{
      RESIZE_SCREEN(s);

      UNWRAP(rs, s, paintTransformedScreen);
      (*s->paintTransformedScreen) (s, sa, region, output, mask);
      WRAP(rs, s, paintTransformedScreen, resizePaintTransformedScreen);

      if ((rs->x == s->x) && (rs->y == s->y)) {
            rs->painted = TRUE;
            resizePaintOutline(s, sa, output, TRUE);
      }
}

static void resizeDonePaintScreen(CompScreen * s)
{
      RESIZE_SCREEN(s);
      RESIZE_DISPLAY(s->display);

      if (rd->resizeMode != ResizeModeNormal && rd->w && rs->grabIndex)
      {
            rd->lastWidth = rd->currentWidth;
            rd->lastHeight = rd->currentHeight;
      }

      UNWRAP(rs, s, donePaintScreen);
      (*s->donePaintScreen) (s);
      WRAP(rs, s, donePaintScreen, resizeDonePaintScreen);
}

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

      RESIZE_DISPLAY(display);

      o = compFindOption(rd->opt, NUM_OPTIONS(rd), name, &index);
      if (!o)
            return FALSE;

      switch (index)
      {

      case RESIZE_DISPLAY_OPTION_INITIATE:
            if (setDisplayAction(display, o, value))
                  return TRUE;
            break;

      case RESIZE_DISPLAY_OPTION_OPACITY:
            if (compSetIntOption(o, value))
            {
                  rd->resizeOpacity = (o->value.i * OPAQUE) / 100;
                  return TRUE;
            }
            break;

      case RESIZE_DISPLAY_OPTION_OPACIFY_MIN_OPACITY:
            if (compSetIntOption(o, value))
            {
                  rd->opacifyMinOpacity = (o->value.i * OPAQUE) / 100;
                  return TRUE;
            }
            break;

      case RESIZE_DISPLAY_OPTION_SYNC_WINDOW:
      case RESIZE_DISPLAY_OPTION_WARP_POINTER:
            if (compSetBoolOption(o, value))
                  return TRUE;
            break;

      case RESIZE_DISPLAY_OPTION_MODE:
            if (compSetStringOption(o, value))
            {
                  int i;

                  for (i = 0; i < o->rest.s.nString; i++)
                        if (strcmp(resizeModes[i], o->value.s) == 0)
                              rd->resizeMode = (ResizeMode) i;

                  return TRUE;
            }
            break;
      case RESIZE_DISPLAY_OPTION_BORDER_COLOR:
      case RESIZE_DISPLAY_OPTION_FILL_COLOR:
            if (compSetColorOption(o, value))
                  return TRUE;
            break;
      default:
            break;
      }

      return FALSE;
}

static void resizeDisplayInitOptions(ResizeDisplay * rd)
{
      CompOption *o;

      o = &rd->opt[RESIZE_DISPLAY_OPTION_INITIATE];
      o->advanced = False;
      o->name = "initiate";
      o->group = N_("Binding");
      o->subGroup = N_("");
      o->displayHints = "";
      o->shortDesc = N_("Initiate Window Resize");
      o->longDesc = N_("Start Resizing a Window.");
      o->type = CompOptionTypeAction;
      o->value.action.initiate = resizeInitiate;
      o->value.action.terminate = resizeTerminate;
      o->value.action.bell = FALSE;
      o->value.action.edgeMask = 0;
      o->value.action.type = CompBindingTypeButton;
      o->value.action.state = CompActionStateInitButton;
      o->value.action.button.modifiers =
                  RESIZE_INITIATE_BUTTON_MODIFIERS_DEFAULT;
      o->value.action.button.button = RESIZE_INITIATE_BUTTON_DEFAULT;
      o->value.action.type |= CompBindingTypeKey;
      o->value.action.state |= CompActionStateInitKey;
      o->value.action.key.modifiers = RESIZE_INITIATE_KEY_MODIFIERS_DEFAULT;
      o->value.action.key.keysym = XStringToKeysym(RESIZE_INITIATE_KEY_DEFAULT);

      o = &rd->opt[RESIZE_DISPLAY_OPTION_WARP_POINTER];
      o->advanced = False;
      o->name = "warp_pointer";
      o->group = N_("Behaviour");
      o->subGroup = N_("");
      o->displayHints = "";
      o->shortDesc =
                  N_
                  ("Warp Pointer When Starting a Resize With Mouse Initiate Combo.");
      o->longDesc =
                  N_("If this is set, the Pointer will be Warped to the "
                     "closest corner when you start a Resize.");
      o->type = CompOptionTypeBool;
      o->value.b = RESIZE_WARP_POINTER_DEFAULT;

      o = &rd->opt[RESIZE_DISPLAY_OPTION_OPACITY];
      o->advanced = False;
      o->name = "opacity";
      o->group = N_("Appearance");
      o->subGroup = N_("Opacity");
      o->displayHints = "";
      o->shortDesc = N_("Opacity");
      o->longDesc = N_("Opacity level of resizing windows.");
      o->type = CompOptionTypeInt;
      o->value.i = RESIZE_OPACITY_DEFAULT;
      o->rest.i.min = RESIZE_OPACITY_MIN;
      o->rest.i.max = RESIZE_OPACITY_MAX;

      o = &rd->opt[RESIZE_DISPLAY_OPTION_OPACIFY_MIN_OPACITY];
      o->advanced = False;
      o->name = "opacify_min_opacity";
      o->group = N_("Appearance");
      o->subGroup = N_("Opacity");
      o->displayHints = "";
      o->shortDesc = N_("Minimum Opacity for Opacify");
      o->longDesc =
                  N_
                  ("Opacify only Windows whose Opacity is higher than this value.");
      o->type = CompOptionTypeInt;
      o->value.i = RESIZE_OPACIFY_MIN_OPACITY_DEFAULT;
      o->rest.i.min = RESIZE_OPACIFY_MIN_OPACITY_MIN;
      o->rest.i.max = RESIZE_OPACIFY_MIN_OPACITY_MAX;

      o = &rd->opt[RESIZE_DISPLAY_OPTION_SYNC_WINDOW];
      o->advanced = False;
      o->name = "sync_window";
      o->group = N_("Behaviour");
      o->subGroup = N_("");
      o->displayHints = "";
      o->shortDesc = N_("Repaints the Window on Each Resize Step");
      o->longDesc =
                  N_("If this is set to true the Window will Repaint itself "
                     "during Resize, which may cause some lag.");
      o->type = CompOptionTypeBool;
      o->value.b = RESIZE_SYNC_WINDOW_DEFAULT;

      o = &rd->opt[RESIZE_DISPLAY_OPTION_MODE];
      o->advanced = False;
      o->name = "resize_mode";
      o->group = N_("Behaviour");
      o->subGroup = N_("");
      o->displayHints = "";
      o->shortDesc = N_("Resize Display Mode");
      o->longDesc =
                  N_
                  ("Select between 'Normal', 'Stretched Texture', "
                   "'Outline and Filled' outline modes.");
      o->type = CompOptionTypeString;
      o->value.s = strdup(resizeModes[RESIZE_MODE_DEFAULT]);
      o->rest.s.string = resizeModes;
      o->rest.s.nString = NUM_RESIZE_MODES;

      o = &rd->opt[RESIZE_DISPLAY_OPTION_BORDER_COLOR];
      o->advanced = False;
      o->name = "border_color";
      o->group = N_("Appearance");
      o->subGroup = N_("Outline Mode");
      o->displayHints = "";
      o->shortDesc = N_("Outline Color");
      o->longDesc = N_("Outline Color for Outline and Filled Outline modes.");
      o->type = CompOptionTypeColor;
      o->value.c[0] = 0x2fff;
      o->value.c[1] = 0x2fff;
      o->value.c[2] = 0x4fff;
      o->value.c[3] = 0x9fff;

      o = &rd->opt[RESIZE_DISPLAY_OPTION_FILL_COLOR];
      o->advanced = False;
      o->name = "fill_color";
      o->group = N_("Appearance");
      o->subGroup = N_("Outline Mode");
      o->displayHints = "";
      o->shortDesc = N_("Fill Color");
      o->longDesc = N_("Fill Color for Filled Outline mode.");
      o->type = CompOptionTypeColor;
      o->value.c[0] = 0x2fff;
      o->value.c[1] = 0x2fff;
      o->value.c[2] = 0x4fff;
      o->value.c[3] = 0x4fff;
}

static CompOption *resizeGetDisplayOptions(CompDisplay * display, int *count)
{
      if (display)
      {
            RESIZE_DISPLAY(display);

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

            resizeDisplayInitOptions(rd);
            *count = NUM_OPTIONS(rd);
            return rd->opt;
      }
}

static Bool resizeInitDisplay(CompPlugin * p, CompDisplay * d)
{
      ResizeDisplay *rd;
      int i;

      rd = malloc(sizeof(ResizeDisplay));
      if (!rd)
            return FALSE;

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

      rd->resizeMode = ResizeModeNormal;
      rd->resizeOpacity = (RESIZE_OPACITY_DEFAULT * OPAQUE) / 100;
      rd->opacifyMinOpacity =
                  (RESIZE_OPACIFY_MIN_OPACITY_DEFAULT * OPAQUE) / 100;

      resizeDisplayInitOptions(rd);

      rd->w = 0;
      rd->ungrabPending = FALSE;

      rd->releaseButton = 0;

      for (i = 0; i < NUM_KEYS; i++)
            rd->key[i] = XKeysymToKeycode(d->display,
                                                        XStringToKeysym(rKeys[i].name));

      WRAP(rd, d, handleEvent, resizeHandleEvent);

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

      return TRUE;
}

static void resizeFiniDisplay(CompPlugin * p, CompDisplay * d)
{
      RESIZE_DISPLAY(d);

      freeScreenPrivateIndex(d, rd->screenPrivateIndex);

      UNWRAP(rd, d, handleEvent);

      free(rd);
}

static Bool resizeInitScreen(CompPlugin * p, CompScreen * s)
{
      ResizeScreen *rs;

      RESIZE_DISPLAY(s->display);

      rs = malloc(sizeof(ResizeScreen));
      if (!rs)
            return FALSE;

      rs->grabIndex = 0;

      rs->leftCursor = XCreateFontCursor(s->display->display, XC_left_side);
      rs->rightCursor = XCreateFontCursor(s->display->display, XC_right_side);
      rs->upCursor = XCreateFontCursor(s->display->display, XC_top_side);
      rs->upLeftCursor = XCreateFontCursor(s->display->display,
                                                             XC_top_left_corner);
      rs->upRightCursor = XCreateFontCursor(s->display->display,
                                                              XC_top_right_corner);
      rs->downCursor = XCreateFontCursor(s->display->display, XC_bottom_side);
      rs->downLeftCursor = XCreateFontCursor(s->display->display,
                                                               XC_bottom_left_corner);
      rs->downRightCursor = XCreateFontCursor(s->display->display,
                                                                  XC_bottom_right_corner);
      rs->middleCursor = XCreateFontCursor(s->display->display, XC_fleur);

      rs->cursor[0] = rs->leftCursor;
      rs->cursor[1] = rs->rightCursor;
      rs->cursor[2] = rs->upCursor;
      rs->cursor[3] = rs->downCursor;

      addScreenAction(s, &rd->opt[RESIZE_DISPLAY_OPTION_INITIATE].value.action);

      WRAP(rs, s, paintWindow, resizePaintWindow);
      WRAP(rs, s, preparePaintScreen, resizePreparePaintScreen);
      WRAP(rs, s, paintScreen, resizePaintScreen);
      WRAP(rs, s, paintTransformedScreen, resizePaintTransformedScreen);
      WRAP(rs, s, donePaintScreen, resizeDonePaintScreen);

      s->privates[rd->screenPrivateIndex].ptr = rs;

      return TRUE;
}

static void resizeFiniScreen(CompPlugin * p, CompScreen * s)
{
      RESIZE_SCREEN(s);
      RESIZE_DISPLAY(s->display);

      UNWRAP(rs, s, paintWindow);
      UNWRAP(rs, s, preparePaintScreen);
      UNWRAP(rs, s, paintScreen);
      UNWRAP(rs, s, paintTransformedScreen);
      UNWRAP(rs, s, donePaintScreen);

      removeScreenAction(s,
                                 &rd->opt[RESIZE_DISPLAY_OPTION_INITIATE].value.action);

      XFreeCursor(s->display->display, rs->leftCursor);
      XFreeCursor(s->display->display, rs->rightCursor);
      XFreeCursor(s->display->display, rs->upCursor);
      XFreeCursor(s->display->display, rs->upLeftCursor);
      XFreeCursor(s->display->display, rs->upRightCursor);
      XFreeCursor(s->display->display, rs->downCursor);
      XFreeCursor(s->display->display, rs->downLeftCursor);
      XFreeCursor(s->display->display, rs->downRightCursor);
      XFreeCursor(s->display->display, rs->middleCursor);

      free(rs);
}

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

      return TRUE;
}

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

CompPluginFeature resizeFeatures[] = {
      {"resize"}
};

CompPluginVTable resizeVTable = {
      "resize",
      N_("Resize Window"),
      N_("Resize window"),
      resizeInit,
      resizeFini,
      resizeInitDisplay,
      resizeFiniDisplay,
      resizeInitScreen,
      resizeFiniScreen,
      0,                                        /* InitWindow */
      0,                                        /* FiniWindow */
      resizeGetDisplayOptions,
      resizeSetDisplayOption,
      0,                                        /* GetScreenOptions */
      0,                                        /* SetScreenOption */
      NULL,
      0,
      resizeFeatures,
      sizeof(resizeFeatures) / sizeof(resizeFeatures[0]),
      BERYL_ABI_INFO,
      "beryl-plugins",
      "wm",
      0,
      0,
      True,
};

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

Generated by  Doxygen 1.6.0   Back to index