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