rendered paste body/*
* Copyright 2005 Novell, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software
* and its documentation for any purpose is hereby granted without
* fee, provided that the above copyright notice appear in all copies
* and that both that copyright notice and this permission notice
* appear in supporting documentation, and that the name of
* Novell, Inc. not be used in advertising or publicity pertaining to
* distribution of the software without specific, written prior permission.
* Novell, Inc. makes no representations about the suitability of this
* software for any purpose. It is provided "as is" without express or
* implied warranty.
*
* NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
* NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
* WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Author: David Reveman <davidr@novell.com>
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/cursorfont.h>
#include <compiz.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_INITIATE_KEY_DEFAULT "F8"
#define RESIZE_INITIATE_KEY_MODIFIERS_DEFAULT CompAltMask
#define RESIZE_BORDER_COLOR_RED 0x2fff
#define RESIZE_BORDER_COLOR_GREEN 0x2fff
#define RESIZE_BORDER_COLOR_BLUE 0x4fff
#define RESIZE_BORDER_COLOR_ALPHA 0x9fff
#define RESIZE_FILL_COLOR_RED 0x2fff
#define RESIZE_FILL_COLOR_GREEN 0x2fff
#define RESIZE_FILL_COLOR_BLUE 0x4fff
#define RESIZE_FILL_COLOR_ALPHA 0x4fff
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_MODE 1
#define RESIZE_DISPLAY_OPTION_BORDER_COLOR 2
#define RESIZE_DISPLAY_OPTION_FILL_COLOR 3
#define RESIZE_DISPLAY_OPTION_NUM 4
typedef enum _ResizeMode {
ResizeModeNormal = 0,
ResizeModeStretch,
ResizeModeOutline,
ResizeModeFilledOutline
} ResizeMode;
char * resizeModes[] = {
N_("Normal"),
N_("Stretch"),
N_("Outline"),
N_("Filled Outline")
};
#define RESIZE_MODE_DEFAULT ResizeModeNormal
#define NUM_RESIZE_MODES (sizeof (resizeModes) / sizeof (resizeModes[0]))
static int displayPrivateIndex;
typedef struct _ResizeDisplay {
CompOption opt[RESIZE_DISPLAY_OPTION_NUM];
int screenPrivateIndex;
HandleEventProc handleEvent;
CompWindow *w;
XWindowAttributes savedAttrib;
int releaseButton;
unsigned int mask;
int currentX;
int currentY;
int currentWidth;
int currentHeight;
/* offsets between pointer and upper left
window edge on resize start */
int xDelta;
int yDelta;
int rightEdge;
int bottomEdge;
ResizeMode resizeMode;
KeyCode key[NUM_KEYS];
} ResizeDisplay;
typedef struct _ResizeScreen {
int grabIndex;
PaintWindowProc paintWindow;
PaintScreenProc paintScreen;
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, rd) \
((ResizeScreen *) (s)->privates[(rd)->screenPrivateIndex].ptr)
#define RESIZE_SCREEN(s) \
ResizeScreen *rs = GET_RESIZE_SCREEN (s, GET_RESIZE_DISPLAY (s->display))
#define NUM_OPTIONS(d) (sizeof ((d)->opt) / sizeof (CompOption))
static void
resizeUpdateRealWindowSize (CompDisplay *d)
{
RESIZE_DISPLAY (d);
XWindowChanges xwc;
if (!rd->w)
return;
if (rd->w->state & CompWindowStateMaximizedVertMask)
rd->currentHeight = rd->w->serverHeight;
if (rd->w->state & CompWindowStateMaximizedHorzMask)
rd->currentWidth = rd->w->serverWidth;
if (rd->currentWidth == rd->w->serverWidth &&
rd->currentHeight == rd->w->serverHeight)
return;
if (rd->w->syncWait)
return;
xwc.x = rd->currentX;
xwc.y = rd->currentY;
xwc.width = rd->currentWidth;
xwc.height = rd->currentHeight;
sendSyncRequest (rd->w);
configureXWindow (rd->w, CWX | CWY | CWWidth | CWHeight, &xwc);
}
static Bool
resizeInitiate (CompDisplay *d,
CompAction *action,
CompActionState state,
CompOption *option,
int nOption)
{
CompWindow *w;
Window xid;
RESIZE_DISPLAY (d);
xid = getIntOptionNamed (option, nOption, "window", 0);
w = findWindowAtDisplay (d, xid);
if (w && (w->actions & CompWindowActionResizeMask))
{
unsigned int mods;
unsigned int mask;
int x, y;
int button;
RESIZE_SCREEN (w->screen);
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->savedAttrib = w->attrib;
rd->currentX = w->attrib.x;
rd->currentY = w->attrib.y;
rd->currentWidth = w->attrib.width;
rd->currentHeight = w->attrib.height;
rd->rightEdge = rd->currentX + rd->currentWidth;
rd->bottomEdge = rd->currentY + rd->currentHeight;
rd->xDelta = 0;
rd->yDelta = 0;
lastPointerX = x;
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)
{
rd->releaseButton = button;
(w->screen->windowGrabNotify) (w, x, y, state,
CompWindowGrabResizeMask |
CompWindowGrabButtonMask);
if (state & CompActionStateInitKey)
{
int xRoot, yRoot;
xRoot = w->attrib.x + (w->width / 2);
yRoot = w->attrib.y + (w->height / 2);
warpPointer (d, xRoot - pointerX, yRoot - pointerY);
}
rd->xDelta = (x - rd->currentX);
if (rd->mask & ResizeRightMask)
rd->xDelta = rd->w->serverWidth - rd->xDelta;
rd->yDelta = (y - rd->currentY);
if (rd->mask & ResizeDownMask)
rd->yDelta = rd->w->serverHeight - rd->yDelta;
if (rd->resizeMode != ResizeModeNormal)
damageScreen (w->screen);
}
}
return FALSE;
}
static Bool
resizeTerminate (CompDisplay *d,
CompAction *action,
CompActionState state,
CompOption *option,
int nOption)
{
RESIZE_DISPLAY (d);
if (rd->w)
{
RESIZE_SCREEN (rd->w->screen);
(rd->w->screen->windowUngrabNotify) (rd->w);
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;
configureXWindow (rd->w,
CWX | CWY | CWWidth | CWHeight,
&xwc);
}
else
{
if (rd->resizeMode != ResizeModeNormal)
resizeUpdateRealWindowSize (d);
else
syncWindowPosition (rd->w);
}
if (rd->resizeMode != ResizeModeNormal)
damageScreen (rd->w->screen);
if (rs->grabIndex)
{
removeScreenGrab (rd->w->screen, rs->grabIndex, NULL);
rs->grabIndex = 0;
}
rd->w = 0;
rd->releaseButton = 0;
}
action->state &= ~(CompActionStateTermKey | CompActionStateTermButton);
return FALSE;
}
#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 */
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 */
static Bool
resizeConstrainAspectRatio(CompWindow * w,
int width,
int height,
int *retWidth,
int *retHeight)
{
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;
RESIZE_DISPLAY(w->screen->display);
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
resizeUpdateWindowSize (CompDisplay *d, int dx, int dy)
{
int width, height;
int newHeight, newWidth;
RESIZE_DISPLAY (d);
if (!rd->w)
return;
/* don't apply resizing in the direction
windows are maximized to */
if (rd->w->state & CompWindowStateMaximizedVertMask)
dy = 0;
if (rd->w->state & CompWindowStateMaximizedHorzMask)
dx = 0;
width = rd->currentWidth;
height = rd->currentHeight;
/* Ensure we don't push the window if constraints stop
it from 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)
width -= dx;
else if (rd->mask & ResizeRightMask)
width += dx;
if (rd->mask & ResizeUpMask)
height -= dy;
else if (rd->mask & ResizeDownMask)
height += dy;
/* Apply constraints */
resizeConstrainResizeIncrement (rd->w, width, height, &width, &height);
newWidth = width;
newHeight = height;
resizeConstrainAspectRatio (rd->w, width, height, &newWidth, &newHeight);
resizeConstrainMinMax (rd->w, newWidth, newHeight, &newWidth, &newHeight);
if ((width != newWidth) || (height != newHeight))
{
/* 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 = newWidth - width;
else if (rd->mask & ResizeLeftMask)
pointerAdjustX = width - newWidth;
if (rd->mask & ResizeDownMask)
pointerAdjustY = newHeight - height;
else if (rd->mask & ResizeUpMask)
pointerAdjustY = height - newHeight;
warpPointer (d, pointerAdjustX, pointerAdjustY);
}
dx -= newWidth - width;
dy -= newHeight - height;
/* If no resizing, drop out now. */
if (!dx && !dy)
return;
if (rd->resizeMode != ResizeModeNormal)
damageScreen(rd->w->screen);
/* Move window by delta if resizing from left/top */
if (rd->mask & ResizeLeftMask && newWidth != rd->currentWidth)
rd->currentX += dx;
if (rd->mask & ResizeUpMask && newHeight != rd->currentHeight)
rd->currentY += dy;
rd->currentWidth = newWidth;
rd->currentHeight = newHeight;
if (rd->mask & ResizeLeftMask)
rd->currentX = rd->rightEdge - rd->currentWidth;
if (rd->mask & ResizeUpMask)
rd->currentY = rd->bottomEdge - rd->currentHeight;
if (rd->resizeMode == ResizeModeNormal)
{
resizeUpdateRealWindowSize(d);
}
else if (rd->resizeMode == ResizeModeStretch)
{
moveWindow(rd->w,
rd->currentX - rd->w->attrib.x,
rd->currentY - rd->w->attrib.y,
TRUE, TRUE);
syncWindowPosition(rd->w);
}
}
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 (widthInc < MIN_KEY_WIDTH_INC)
widthInc = MIN_KEY_WIDTH_INC;
if (heightInc < MIN_KEY_HEIGHT_INC)
heightInc = MIN_KEY_HEIGHT_INC;
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->attrib.width + w->input.right;
height = w->input.top + w->attrib.height + 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->w->attrib.width - rd->xDelta;
rd->yDelta = (y - rd->currentY);
if (rd->mask & ResizeDownMask)
rd->yDelta = rd->w->attrib.height - rd->yDelta;
warpPointer (s->display, x - pointerX, y - pointerY);
updateScreenGrab (s, rs->grabIndex, rs->cursor[i]);
}
break;
}
}
}
static void
resizeHandleMotionEvent (CompScreen *s,
int xRoot,
int yRoot)
{
RESIZE_SCREEN (s);
if (!rs->grabIndex)
return;
RESIZE_DISPLAY (s->display);
int pointerDx, pointerDy;
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;
if (!pointerDx && !pointerDy)
return;
resizeUpdateWindowSize (s->display,
pointerDx,
pointerDy);
}
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 ButtonRelease: {
CompAction *action =
&rd->opt[RESIZE_DISPLAY_OPTION_INITIATE].value.action;
if (action->state & CompActionStateTermButton)
{
if (event->type == ButtonRelease &&
(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, pointerX, pointerY);
break;
case EnterNotify:
case LeaveNotify:
s = findScreenAtDisplay (d, event->xcrossing.root);
if (s)
resizeHandleMotionEvent (s, pointerX, pointerY);
break;
case ClientMessage:
if (event->xclient.message_type == d->wmMoveResizeAtom)
{
CompWindow *w;
if (event->xclient.data.l[2] <= WmMoveResizeSizeLeft ||
event->xclient.data.l[2] == WmMoveResizeSizeKeyboard)
{
w = findWindowAtDisplay (d, event->xclient.window);
if (w)
{
CompOption o[6];
CompAction *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);
}
}
}
}
}
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);
}
default:
break;
}
UNWRAP (rd, d, handleEvent);
(*d->handleEvent) (d, event);
WRAP (rd, d, handleEvent, resizeHandleEvent);
if (event->type == d->syncEvent + XSyncAlarmNotify)
{
if (rd->w)
{
XSyncAlarmNotifyEvent *sa;
sa = (XSyncAlarmNotifyEvent *) event;
if (rd->w->syncAlarm == sa->alarm)
resizeUpdateRealWindowSize (d);
}
}
}
static Bool
resizePaintWindow(CompWindow *w,
const WindowPaintAttrib *attrib,
const CompTransform *transform,
Region region,
unsigned int mask)
{
CompScreen *s = w->screen;
Bool status;
RESIZE_SCREEN (s);
RESIZE_DISPLAY (s->display);
if (rs->grabIndex && rd->w && (rd->w->id == w->id))
{
CompTransform wTransform = *transform;
if (rd->resizeMode == ResizeModeStretch)
{
float xScale = (float)(rd->currentWidth) /
(float)(rd->w->attrib.width);
float yScale = (float)(rd->currentHeight) /
(float)(rd->w->attrib.height);
matrixTranslate (&wTransform, w->attrib.x, w->attrib.y, 0.0f);
matrixScale (&wTransform, xScale, yScale, 0.0f);
matrixTranslate (&wTransform, -w->attrib.x, -w->attrib.y, 0.0f);
mask |= PAINT_WINDOW_TRANSFORMED_MASK;
}
UNWRAP(rs, s, paintWindow);
status = (*s->paintWindow) (w, attrib, &wTransform, region, mask);
WRAP(rs, s, paintWindow, resizePaintWindow);
}
else
{
UNWRAP(rs, s, paintWindow);
status = (*s->paintWindow) (w, attrib, transform, region, mask);
WRAP(rs, s, paintWindow, resizePaintWindow);
}
return status;
}
static void
resizePaintOutline(CompScreen *s,
const ScreenPaintAttrib *sa,
const CompTransform *transform,
int output,
Bool transformed)
{
int x1, x2, y1, y2;
CompTransform sTransform;
RESIZE_SCREEN(s);
if (!rs->grabIndex)
return;
RESIZE_DISPLAY(s->display);
if ((rd->resizeMode != ResizeModeOutline) &&
(rd->resizeMode != ResizeModeFilledOutline))
return;
if (!rd->w || (rd->w->state & MAXIMIZE_STATE))
return;
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->input.bottom +
(rd->w->shaded ? rd->w->height : rd->currentHeight);
sTransform = *transform;
if (transformed)
{
(s->applyScreenTransform) (s, sa, output, &sTransform);
transformToScreenSpace (s, output, -sa->zTranslate, &sTransform);
}
else
transformToScreenSpace (s, output, -DEFAULT_Z_CAMERA, &sTransform);
glPushMatrix ();
glLoadMatrixf (sTransform.m);
glDisableClientState (GL_TEXTURE_COORD_ARRAY);
glEnable (GL_BLEND);
if (rd->resizeMode == ResizeModeFilledOutline)
{
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,
const CompTransform *transform,
Region region,
int output,
unsigned int mask)
{
Bool status = FALSE;
RESIZE_SCREEN(s);
RESIZE_DISPLAY(s->display);
if (rs->grabIndex && rd->w && rd->resizeMode == ResizeModeStretch)
mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK;
UNWRAP(rs, s, paintScreen);
status = (*s->paintScreen) (s, sAttrib, transform, region, output, mask);
WRAP(rs, s, paintScreen, resizePaintScreen);
if (status)
resizePaintOutline(s, sAttrib, transform, output, FALSE);
return status;
}
static CompOption *
resizeGetDisplayOptions (CompPlugin *plugin,
CompDisplay *display,
int *count)
{
RESIZE_DISPLAY (display);
*count = NUM_OPTIONS (rd);
return rd->opt;
}
static Bool
resizeSetDisplayOption (CompPlugin *plugin,
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_MODE:
if (compSetStringOption (o, value))
{
int i;
rd->resizeMode = RESIZE_MODE_DEFAULT;
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,
Display *display)
{
CompOption *o;
o = &rd->opt[RESIZE_DISPLAY_OPTION_INITIATE];
o->name = "initiate";
o->shortDesc = N_("Initiate Window Resize");
o->longDesc = N_("Start resizing 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.keycode =
XKeysymToKeycode (display,
XStringToKeysym (RESIZE_INITIATE_KEY_DEFAULT));
o = &rd->opt[RESIZE_DISPLAY_OPTION_MODE];
o->name = "resize_mode";
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->name = "border_color";
o->shortDesc = N_("Outline Color");
o->longDesc = N_("Outline Color for Outline and Filled Outline modes.");
o->type = CompOptionTypeColor;
o->value.c[0] = RESIZE_BORDER_COLOR_RED;
o->value.c[1] = RESIZE_BORDER_COLOR_GREEN;
o->value.c[2] = RESIZE_BORDER_COLOR_BLUE;
o->value.c[3] = RESIZE_BORDER_COLOR_ALPHA;
o = &rd->opt[RESIZE_DISPLAY_OPTION_FILL_COLOR];
o->name = "fill_color";
o->shortDesc = N_("Fill Color");
o->longDesc = N_("Fill Color for Filled Outline mode.");
o->type = CompOptionTypeColor;
o->value.c[0] = RESIZE_FILL_COLOR_RED;
o->value.c[1] = RESIZE_FILL_COLOR_GREEN;
o->value.c[2] = RESIZE_FILL_COLOR_BLUE;
o->value.c[3] = RESIZE_FILL_COLOR_ALPHA;
}
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;
}
resizeDisplayInitOptions (rd, d->display);
rd->w = 0;
rd->releaseButton = 0;
rd->resizeMode = RESIZE_MODE_DEFAULT;
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, paintScreen, resizePaintScreen);
s->privates[rd->screenPrivateIndex].ptr = rs;
return TRUE;
}
static void
resizeFiniScreen (CompPlugin *p,
CompScreen *s)
{
RESIZE_SCREEN (s);
RESIZE_DISPLAY (s->display);
if (rs->leftCursor)
XFreeCursor (s->display->display, rs->leftCursor);
if (rs->rightCursor)
XFreeCursor (s->display->display, rs->rightCursor);
if (rs->upCursor)
XFreeCursor (s->display->display, rs->upCursor);
if (rs->downCursor)
XFreeCursor (s->display->display, rs->downCursor);
if (rs->middleCursor)
XFreeCursor (s->display->display, rs->middleCursor);
if (rs->upLeftCursor)
XFreeCursor (s->display->display, rs->upLeftCursor);
if (rs->upRightCursor)
XFreeCursor (s->display->display, rs->upRightCursor);
if (rs->downLeftCursor)
XFreeCursor (s->display->display, rs->downLeftCursor);
if (rs->downRightCursor)
XFreeCursor (s->display->display, rs->downRightCursor);
removeScreenAction (s,
&rd->opt[RESIZE_DISPLAY_OPTION_INITIATE].value.action);
UNWRAP (rs, s, paintWindow);
UNWRAP (rs, s, paintScreen);
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);
}
static int
resizeGetVersion (CompPlugin *plugin,
int version)
{
return ABIVERSION;
}
CompPluginVTable resizeVTable = {
"resize",
N_("Resize Window"),
N_("Resize window"),
resizeGetVersion,
resizeInit,
resizeFini,
resizeInitDisplay,
resizeFiniDisplay,
resizeInitScreen,
resizeFiniScreen,
0, /* InitWindow */
0, /* FiniWindow */
resizeGetDisplayOptions,
resizeSetDisplayOption,
0, /* GetScreenOptions */
0, /* SetScreenOption */
0, /* Deps */
0, /* nDeps */
0, /* Features */
0 /* nFeatures */
};
CompPluginVTable *
getCompPluginInfo (void)
{
return &resizeVTable;
}