/* * 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 #include static CompMetadata resizeMetadata; #define ResizeUpMask (1L << 0) #define ResizeDownMask (1L << 1) #define ResizeLeftMask (1L << 2) #define ResizeRightMask (1L << 3) #define RESIZE_MODE_NORMAL 0 #define RESIZE_MODE_OUTLINE 1 #define RESIZE_MODE_RECTANGLE 2 #define RESIZE_MODE_STRETCH 3 #define RESIZE_MODE_LAST RESIZE_MODE_STRETCH 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_NORMAL_KEY 0 #define RESIZE_DISPLAY_OPTION_INITIATE_OUTLINE_KEY 1 #define RESIZE_DISPLAY_OPTION_INITIATE_RECTANGLE_KEY 2 #define RESIZE_DISPLAY_OPTION_INITIATE_STRETCH_KEY 3 #define RESIZE_DISPLAY_OPTION_INITIATE_BUTTON 4 #define RESIZE_DISPLAY_OPTION_INITIATE_KEY 5 #define RESIZE_DISPLAY_OPTION_MODE 6 #define RESIZE_DISPLAY_OPTION_BORDER_COLOR 7 #define RESIZE_DISPLAY_OPTION_FILL_COLOR 8 #define RESIZE_DISPLAY_OPTION_NORMAL_MATCH 9 #define RESIZE_DISPLAY_OPTION_OUTLINE_MATCH 10 #define RESIZE_DISPLAY_OPTION_RECTANGLE_MATCH 11 #define RESIZE_DISPLAY_OPTION_STRETCH_MATCH 12 #define RESIZE_DISPLAY_OPTION_NUM 13 static int displayPrivateIndex; typedef struct _ResizeDisplay { CompOption opt[RESIZE_DISPLAY_OPTION_NUM]; int screenPrivateIndex; HandleEventProc handleEvent; Atom resizeNotifyAtom; Atom resizeInformationAtom; CompWindow *w; int mode; XRectangle savedGeometry; XRectangle geometry; int releaseButton; unsigned int mask; int pointerDx; int pointerDy; KeyCode key[NUM_KEYS]; Bool offWorkAreaConstrained; } ResizeDisplay; typedef struct _ResizeScreen { int grabIndex; WindowResizeNotifyProc windowResizeNotify; PaintOutputProc paintOutput; PaintWindowProc paintWindow; DamageWindowRectProc damageWindowRect; Cursor leftCursor; Cursor rightCursor; Cursor upCursor; Cursor upLeftCursor; Cursor upRightCursor; Cursor downCursor; Cursor downLeftCursor; Cursor downRightCursor; Cursor middleCursor; Cursor cursor[NUM_KEYS]; const XRectangle *grabWindowWorkArea; } ResizeScreen; #define GET_RESIZE_DISPLAY(d) \ ((ResizeDisplay *) (d)->base.privates[displayPrivateIndex].ptr) #define RESIZE_DISPLAY(d) \ ResizeDisplay *rd = GET_RESIZE_DISPLAY (d) #define GET_RESIZE_SCREEN(s, rd) \ ((ResizeScreen *) (s)->base.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 resizeGetPaintRectangle (CompDisplay *d, BoxPtr pBox) { RESIZE_DISPLAY (d); pBox->x1 = rd->geometry.x - rd->w->input.left; pBox->y1 = rd->geometry.y - rd->w->input.top; pBox->x2 = rd->geometry.x + rd->geometry.width + rd->w->serverBorderWidth * 2 + rd->w->input.right; if (rd->w->shaded) { pBox->y2 = rd->geometry.y + rd->w->height + rd->w->input.bottom; } else { pBox->y2 = rd->geometry.y + rd->geometry.height + rd->w->serverBorderWidth * 2 + rd->w->input.bottom; } } static void resizeGetStretchScale (CompWindow *w, BoxPtr pBox, float *xScale, float *yScale) { int width, height; width = w->width + w->input.left + w->input.right; height = w->height + w->input.top + w->input.bottom; *xScale = (width) ? (pBox->x2 - pBox->x1) / (float) width : 1.0f; *yScale = (height) ? (pBox->y2 - pBox->y1) / (float) height : 1.0f; } static void resizeGetStretchRectangle (CompDisplay *d, BoxPtr pBox) { BoxRec box; float xScale, yScale; RESIZE_DISPLAY (d); resizeGetPaintRectangle (d, &box); resizeGetStretchScale (rd->w, &box, &xScale, &yScale); pBox->x1 = box.x1 - (rd->w->output.left - rd->w->input.left) * xScale; pBox->y1 = box.y1 - (rd->w->output.top - rd->w->input.top) * yScale; pBox->x2 = box.x2 + rd->w->output.right * xScale; pBox->y2 = box.y2 + rd->w->output.bottom * yScale; } static void resizeDamageRectangle (CompScreen *s, BoxPtr pBox) { REGION reg; reg.rects = ®.extents; reg.numRects = 1; reg.extents = *pBox; reg.extents.x1 -= 1; reg.extents.y1 -= 1; reg.extents.x2 += 1; reg.extents.y2 += 1; damageScreenRegion (s, ®); } static Cursor resizeCursorFromResizeMask (CompScreen *s, unsigned int mask) { Cursor cursor; RESIZE_SCREEN (s); 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; } return cursor; } static void resizeSendResizeNotify (CompDisplay *d) { XEvent xev; RESIZE_DISPLAY (d); xev.xclient.type = ClientMessage; xev.xclient.display = d->display; xev.xclient.format = 32; xev.xclient.message_type = rd->resizeNotifyAtom; xev.xclient.window = rd->w->id; xev.xclient.data.l[0] = rd->geometry.x; xev.xclient.data.l[1] = rd->geometry.y; xev.xclient.data.l[2] = rd->geometry.width; xev.xclient.data.l[3] = rd->geometry.height; xev.xclient.data.l[4] = 0; XSendEvent (d->display, rd->w->screen->root, FALSE, SubstructureRedirectMask | SubstructureNotifyMask, &xev); } static void resizeUpdateWindowProperty (CompDisplay *d) { unsigned long data[4]; RESIZE_DISPLAY (d); data[0] = rd->geometry.x; data[1] = rd->geometry.y; data[2] = rd->geometry.width; data[3] = rd->geometry.height; XChangeProperty (d->display, rd->w->id, rd->resizeInformationAtom, XA_CARDINAL, 32, PropModeReplace, (unsigned char*) data, 4); } static void resizeFinishResizing (CompDisplay *d) { RESIZE_DISPLAY (d); (*rd->w->screen->windowUngrabNotify) (rd->w); XDeleteProperty (d->display, rd->w->id, rd->resizeInformationAtom); rd->w = NULL; } 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 mask; int x, y; int button; int i; RESIZE_SCREEN (w->screen); x = getIntOptionNamed (option, nOption, "x", pointerX); y = getIntOptionNamed (option, nOption, "y", pointerY); button = getIntOptionNamed (option, nOption, "button", -1); mask = getIntOptionNamed (option, nOption, "direction", 0); /* Initiate the resize in the direction suggested by the * sector 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) { int sectorSizeX = w->serverWidth / 3; int sectorSizeY = w->serverHeight / 3; int posX = x - w->serverX; int posY = y - w->serverY; if (posX < sectorSizeX) mask |= ResizeLeftMask; else if (posX > (2 * sectorSizeX)) mask |= ResizeRightMask; if (posY < sectorSizeY) mask |= ResizeUpMask; else if (posY > (2 * sectorSizeY)) mask |= ResizeDownMask; /* if the pointer was in the middle of the window, just prevent input to the window */ if (!mask) return TRUE; } if (otherScreenGrabExist (w->screen, "resize", NULL)) 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->savedGeometry.x = w->serverX; rd->savedGeometry.y = w->serverY; rd->savedGeometry.width = w->serverWidth; rd->savedGeometry.height = w->serverHeight; rd->geometry = rd->savedGeometry; rd->pointerDx = x - pointerX; rd->pointerDy = y - pointerY; if ((w->state & MAXIMIZE_STATE) == MAXIMIZE_STATE) { /* if the window is fully maximized, showing the outline or rectangle would be visually distracting as the window can't be resized anyway; so we better don't use them in this case */ rd->mode = RESIZE_MODE_NORMAL; } else { rd->mode = rd->opt[RESIZE_DISPLAY_OPTION_MODE].value.i; for (i = 0; i <= RESIZE_MODE_LAST; i++) { if (action == &rd->opt[i].value.action) { rd->mode = i; break; } } if (i > RESIZE_MODE_LAST) { int index; for (i = 0; i <= RESIZE_MODE_LAST; i++) { index = RESIZE_DISPLAY_OPTION_NORMAL_MATCH + i; if (matchEval (&rd->opt[index].value.match, w)) { rd->mode = i; break; } } } } if (!rs->grabIndex) { Cursor cursor; if (state & CompActionStateInitKey) { cursor = rs->middleCursor; } else { cursor = resizeCursorFromResizeMask (w->screen, mask); } rs->grabIndex = pushScreenGrab (w->screen, cursor, "resize"); } if (rs->grabIndex) { unsigned int grabMask = CompWindowGrabResizeMask | CompWindowGrabButtonMask; Bool sourceExternalApp = getBoolOptionNamed (option, nOption, "external", FALSE); if (sourceExternalApp) grabMask |= CompWindowGrabExternalAppMask; BoxRec box; rd->releaseButton = button; (w->screen->windowGrabNotify) (w, x, y, state, grabMask); if (d->opt[COMP_DISPLAY_OPTION_RAISE_ON_CLICK].value.b) updateWindowAttributes (w, CompStackingUpdateModeAboveFullscreen); /* using the paint rectangle is enough here as we don't have any stretch yet */ resizeGetPaintRectangle (d, &box); resizeDamageRectangle (w->screen, &box); if (state & CompActionStateInitKey) { int xRoot, yRoot; xRoot = w->serverX + (w->serverWidth / 2); yRoot = w->serverY + (w->serverHeight / 2); warpPointer (w->screen, xRoot - pointerX, yRoot - pointerY); } /* Update offWorkAreaConstrained and workArea at grab time */ rd->offWorkAreaConstrained = FALSE; if (sourceExternalApp) { int output = outputDeviceForWindow (w); rs->grabWindowWorkArea = &w->screen->outputDev[output].workArea; /* Prevent resizing beyond work area edges when resize is initiated externally (e.g. with window frame or menu) and not with a key (e.g. alt+button) */ rd->offWorkAreaConstrained = TRUE; } } } return FALSE; } static Bool resizeTerminate (CompDisplay *d, CompAction *action, CompActionState state, CompOption *option, int nOption) { RESIZE_DISPLAY (d); if (rd->w) { CompWindow *w = rd->w; XWindowChanges xwc; unsigned int mask = 0; RESIZE_SCREEN (w->screen); if (rd->mode == RESIZE_MODE_NORMAL) { if (state & CompActionStateCancel) { xwc.x = rd->savedGeometry.x; xwc.y = rd->savedGeometry.y; xwc.width = rd->savedGeometry.width; xwc.height = rd->savedGeometry.height; mask = CWX | CWY | CWWidth | CWHeight; } } else { XRectangle geometry; if (state & CompActionStateCancel) geometry = rd->savedGeometry; else geometry = rd->geometry; if (memcmp (&geometry, &rd->savedGeometry, sizeof (geometry)) == 0) { BoxRec box; if (rd->mode == RESIZE_MODE_STRETCH) resizeGetStretchRectangle (d, &box); else resizeGetPaintRectangle (d, &box); resizeDamageRectangle (w->screen, &box); } else { xwc.x = geometry.x; xwc.y = geometry.y; xwc.width = geometry.width; xwc.height = geometry.height; mask = CWX | CWY | CWWidth | CWHeight; } } if ((mask & CWWidth) && xwc.width == w->serverWidth) mask &= ~CWWidth; if ((mask & CWHeight) && xwc.height == w->serverHeight) mask &= ~CWHeight; if (mask) { if (mask & (CWWidth | CWHeight)) sendSyncRequest (w); configureXWindow (w, mask, &xwc); } if (!(mask & (CWWidth | CWHeight))) resizeFinishResizing (d); if (rs->grabIndex) { removeScreenGrab (w->screen, rs->grabIndex, NULL); rs->grabIndex = 0; } rd->releaseButton = 0; } action->state &= ~(CompActionStateTermKey | CompActionStateTermButton); return FALSE; } static void resizeUpdateWindowSize (CompDisplay *d) { RESIZE_DISPLAY (d); if (rd->w->syncWait) return; if (rd->w->serverWidth != rd->geometry.width || rd->w->serverHeight != rd->geometry.height) { XWindowChanges xwc; xwc.x = rd->geometry.x; xwc.y = rd->geometry.y; xwc.width = rd->geometry.width; xwc.height = rd->geometry.height; sendSyncRequest (rd->w); configureXWindow (rd->w, CWX | CWY | CWWidth | CWHeight, &xwc); } } static void resizeHandleKeyEvent (CompScreen *s, KeyCode keycode) { RESIZE_SCREEN (s); RESIZE_DISPLAY (s->display); if (rs->grabIndex && rd->w) { CompWindow *w = rd->w; int widthInc, heightInc, i; 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->serverX - w->input.left; top = w->serverY - 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; warpPointer (s, x - pointerX, y - pointerY); rd->mask = rKeys[i].resizeMask; updateScreenGrab (s, rs->grabIndex, rs->cursor[i]); } break; } } } static void resizeHandleMotionEvent (CompScreen *s, int xRoot, int yRoot) { RESIZE_SCREEN (s); if (rs->grabIndex) { BoxRec box; int w, h; RESIZE_DISPLAY (s->display); w = rd->savedGeometry.width; h = rd->savedGeometry.height; if (!rd->mask) { CompWindow *w = rd->w; int xDist, yDist; int minPointerOffsetX, minPointerOffsetY; xDist = xRoot - (w->serverX + (w->serverWidth / 2)); yDist = yRoot - (w->serverY + (w->serverHeight / 2)); /* decision threshold is 10% of window size */ minPointerOffsetX = MIN (20, w->serverWidth / 10); minPointerOffsetY = MIN (20, w->serverHeight / 10); /* if we reached the threshold in one direction, make the threshold in the other direction smaller so there is a chance that this threshold also can be reached (by diagonal movement) */ if (abs (xDist) > minPointerOffsetX) minPointerOffsetY /= 2; else if (abs (yDist) > minPointerOffsetY) minPointerOffsetX /= 2; if (abs (xDist) > minPointerOffsetX) { if (xDist > 0) rd->mask |= ResizeRightMask; else rd->mask |= ResizeLeftMask; } if (abs (yDist) > minPointerOffsetY) { if (yDist > 0) rd->mask |= ResizeDownMask; else rd->mask |= ResizeUpMask; } /* if the pointer movement was enough to determine a direction, warp the pointer to the appropriate edge and set the right cursor */ if (rd->mask) { Cursor cursor; CompScreen *s = rd->w->screen; CompAction *action; int pointerAdjustX = 0; int pointerAdjustY = 0; int option = RESIZE_DISPLAY_OPTION_INITIATE_KEY; RESIZE_SCREEN (s); action = &rd->opt[option].value.action; action->state |= CompActionStateTermButton; if (rd->mask & ResizeRightMask) pointerAdjustX = w->serverX + w->serverWidth + w->input.right - xRoot; else if (rd->mask & ResizeLeftMask) pointerAdjustX = w->serverX - w->input.left - xRoot; if (rd->mask & ResizeDownMask) pointerAdjustY = w->serverY + w->serverHeight + w->input.bottom - yRoot; else if (rd->mask & ResizeUpMask) pointerAdjustY = w->serverY - w->input.top - yRoot; warpPointer (s, pointerAdjustX, pointerAdjustY); cursor = resizeCursorFromResizeMask (s, rd->mask); updateScreenGrab (s, rs->grabIndex, cursor); } } else { /* only accumulate pointer movement if a mask is already set as we don't have a use for the difference information otherwise */ rd->pointerDx += xRoot - lastPointerX; rd->pointerDy += yRoot - lastPointerY; } if (rd->mask & ResizeLeftMask) w -= rd->pointerDx; else if (rd->mask & ResizeRightMask) w += rd->pointerDx; if (rd->mask & ResizeUpMask) h -= rd->pointerDy; else if (rd->mask & ResizeDownMask) h += rd->pointerDy; if (rd->w->state & CompWindowStateMaximizedVertMask) h = rd->w->serverHeight; if (rd->w->state & CompWindowStateMaximizedHorzMask) w = rd->w->serverWidth; constrainNewWindowSize (rd->w, w, h, &w, &h); /* constrain to work area */ if (rd->offWorkAreaConstrained) { if (rd->mask & ResizeUpMask) { int decorTop = rd->savedGeometry.y + rd->savedGeometry.height - (h + rd->w->input.top); if (rs->grabWindowWorkArea->y > decorTop) h -= rs->grabWindowWorkArea->y - decorTop; } if (rd->mask & ResizeDownMask) { int decorBottom = rd->savedGeometry.y + h + rd->w->input.bottom; if (decorBottom > rs->grabWindowWorkArea->y + rs->grabWindowWorkArea->height) h -= decorBottom - (rs->grabWindowWorkArea->y + rs->grabWindowWorkArea->height); } if (rd->mask & ResizeLeftMask) { int decorLeft = rd->savedGeometry.x + rd->savedGeometry.width - (w + rd->w->input.left); if (rs->grabWindowWorkArea->x > decorLeft) w -= rs->grabWindowWorkArea->x - decorLeft; } if (rd->mask & ResizeRightMask) { int decorRight = rd->savedGeometry.x + w + rd->w->input.right; if (decorRight > rs->grabWindowWorkArea->x + rs->grabWindowWorkArea->width) w -= decorRight - (rs->grabWindowWorkArea->x + rs->grabWindowWorkArea->width); } } if (rd->mode != RESIZE_MODE_NORMAL) { if (rd->mode == RESIZE_MODE_STRETCH) resizeGetStretchRectangle (s->display, &box); else resizeGetPaintRectangle (s->display, &box); resizeDamageRectangle (s, &box); } if (rd->mask & ResizeLeftMask) rd->geometry.x -= w - rd->geometry.width; if (rd->mask & ResizeUpMask) rd->geometry.y -= h - rd->geometry.height; rd->geometry.width = w; rd->geometry.height = h; if (rd->mode != RESIZE_MODE_NORMAL) { if (rd->mode == RESIZE_MODE_STRETCH) resizeGetStretchRectangle (s->display, &box); else resizeGetPaintRectangle (s->display, &box); resizeDamageRectangle (s, &box); } else { resizeUpdateWindowSize (s->display); } resizeUpdateWindowProperty (s->display); resizeSendResizeNotify (s->display); } } 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 ButtonRelease: s = findScreenAtDisplay (d, event->xbutton.root); if (s) { RESIZE_SCREEN (s); if (rs->grabIndex) { if (rd->releaseButton == -1 || event->xbutton.button == rd->releaseButton) { int opt = RESIZE_DISPLAY_OPTION_INITIATE_BUTTON; CompAction *action = &rd->opt[opt].value.action; 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[7]; int option; o[0].type = CompOptionTypeInt; o[0].name = "window"; o[0].value.i = event->xclient.window; o[1].type = CompOptionTypeBool; o[1].name = "external"; o[1].value.b = TRUE; if (event->xclient.data.l[2] == WmMoveResizeSizeKeyboard) { option = RESIZE_DISPLAY_OPTION_INITIATE_KEY; resizeInitiate (d, &rd->opt[option].value.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; option = RESIZE_DISPLAY_OPTION_INITIATE_BUTTON; XQueryPointer (d->display, w->screen->root, &root, &child, &xRoot, &yRoot, &i, &i, &mods); /* TODO: not only button 1 */ if (mods & Button1Mask) { o[2].type = CompOptionTypeInt; o[2].name = "modifiers"; o[2].value.i = mods; o[3].type = CompOptionTypeInt; o[3].name = "x"; o[3].value.i = event->xclient.data.l[0]; o[4].type = CompOptionTypeInt; o[4].name = "y"; o[4].value.i = event->xclient.data.l[1]; o[5].type = CompOptionTypeInt; o[5].name = "direction"; o[5].value.i = mask[event->xclient.data.l[2]]; o[6].type = CompOptionTypeInt; o[6].name = "button"; o[6].value.i = event->xclient.data.l[3] ? event->xclient.data.l[3] : -1; resizeInitiate (d, &rd->opt[option].value.action, CompActionStateInitButton, o, 7); resizeHandleMotionEvent (w->screen, xRoot, yRoot); } } } } else if (rd->w && event->xclient.data.l[2] == WmMoveResizeCancel) { if (rd->w->id == event->xclient.window) { int option; option = RESIZE_DISPLAY_OPTION_INITIATE_BUTTON; resizeTerminate (d, &rd->opt[option].value.action, CompActionStateCancel, NULL, 0); option = RESIZE_DISPLAY_OPTION_INITIATE_KEY; resizeTerminate (d, &rd->opt[option].value.action, CompActionStateCancel, NULL, 0); } } } break; case DestroyNotify: if (rd->w && rd->w->id == event->xdestroywindow.window) { int option; option = RESIZE_DISPLAY_OPTION_INITIATE_BUTTON; resizeTerminate (d, &rd->opt[option].value.action, 0, NULL, 0); option = RESIZE_DISPLAY_OPTION_INITIATE_KEY; resizeTerminate (d, &rd->opt[option].value.action, 0, NULL, 0); } break; case UnmapNotify: if (rd->w && rd->w->id == event->xunmap.window) { int option; option = RESIZE_DISPLAY_OPTION_INITIATE_BUTTON; resizeTerminate (d, &rd->opt[option].value.action, 0, NULL, 0); option = RESIZE_DISPLAY_OPTION_INITIATE_KEY; resizeTerminate (d, &rd->opt[option].value.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) resizeUpdateWindowSize (d); } } } static void resizeWindowResizeNotify (CompWindow *w, int dx, int dy, int dwidth, int dheight) { RESIZE_DISPLAY (w->screen->display); RESIZE_SCREEN (w->screen); UNWRAP (rs, w->screen, windowResizeNotify); (*w->screen->windowResizeNotify) (w, dx, dy, dwidth, dheight); WRAP (rs, w->screen, windowResizeNotify, resizeWindowResizeNotify); if (rd->w == w && !rs->grabIndex) resizeFinishResizing (w->screen->display); } static void resizePaintRectangle (CompScreen *s, const ScreenPaintAttrib *sa, const CompTransform *transform, CompOutput *output, unsigned short *borderColor, unsigned short *fillColor) { BoxRec box; CompTransform sTransform = *transform; resizeGetPaintRectangle (s->display, &box); glPushMatrix (); transformToScreenSpace (s, output, -DEFAULT_Z_CAMERA, &sTransform); glLoadMatrixf (sTransform.m); glDisableClientState (GL_TEXTURE_COORD_ARRAY); glEnable (GL_BLEND); /* fill rectangle */ if (fillColor) { glColor4usv (fillColor); glRecti (box.x1, box.y2, box.x2, box.y1); } /* draw outline */ glColor4usv (borderColor); glLineWidth (2.0); glBegin (GL_LINE_LOOP); glVertex2i (box.x1, box.y1); glVertex2i (box.x2, box.y1); glVertex2i (box.x2, box.y2); glVertex2i (box.x1, box.y2); glEnd (); /* clean up */ glColor4usv (defaultColor); glDisable (GL_BLEND); glEnableClientState (GL_TEXTURE_COORD_ARRAY); glPopMatrix (); } static Bool resizePaintOutput (CompScreen *s, const ScreenPaintAttrib *sAttrib, const CompTransform *transform, Region region, CompOutput *output, unsigned int mask) { Bool status; RESIZE_SCREEN (s); RESIZE_DISPLAY (s->display); if (rd->w && (s == rd->w->screen)) { if (rd->mode == RESIZE_MODE_STRETCH) mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK; } UNWRAP (rs, s, paintOutput); status = (*s->paintOutput) (s, sAttrib, transform, region, output, mask); WRAP (rs, s, paintOutput, resizePaintOutput); if (status && rd->w && (s == rd->w->screen)) { unsigned short *border, *fill; border = rd->opt[RESIZE_DISPLAY_OPTION_BORDER_COLOR].value.c; fill = rd->opt[RESIZE_DISPLAY_OPTION_FILL_COLOR].value.c; switch (rd->mode) { case RESIZE_MODE_OUTLINE: resizePaintRectangle (s, sAttrib, transform, output, border, NULL); break; case RESIZE_MODE_RECTANGLE: resizePaintRectangle (s, sAttrib, transform, output, border, fill); default: break; } } return status; } 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 (w == rd->w && rd->mode == RESIZE_MODE_STRETCH) { FragmentAttrib fragment; CompTransform wTransform = *transform; BoxRec box; float xOrigin, yOrigin; float xScale, yScale; if (mask & PAINT_WINDOW_OCCLUSION_DETECTION_MASK) return FALSE; UNWRAP (rs, s, paintWindow); status = (*s->paintWindow) (w, attrib, transform, region, mask | PAINT_WINDOW_NO_CORE_INSTANCE_MASK); WRAP (rs, s, paintWindow, resizePaintWindow); initFragmentAttrib (&fragment, &w->lastPaint); if (w->alpha || fragment.opacity != OPAQUE) mask |= PAINT_WINDOW_TRANSLUCENT_MASK; resizeGetPaintRectangle (s->display, &box); resizeGetStretchScale (w, &box, &xScale, &yScale); xOrigin = w->attrib.x - w->input.left; yOrigin = w->attrib.y - w->input.top; matrixTranslate (&wTransform, xOrigin, yOrigin, 0.0f); matrixScale (&wTransform, xScale, yScale, 1.0f); matrixTranslate (&wTransform, (rd->geometry.x - w->attrib.x) / xScale - xOrigin, (rd->geometry.y - w->attrib.y) / yScale - yOrigin, 0.0f); glPushMatrix (); glLoadMatrixf (wTransform.m); (*s->drawWindow) (w, &wTransform, &fragment, region, mask | PAINT_WINDOW_TRANSFORMED_MASK); glPopMatrix (); } else { UNWRAP (rs, s, paintWindow); status = (*s->paintWindow) (w, attrib, transform, region, mask); WRAP (rs, s, paintWindow, resizePaintWindow); } return status; } static Bool resizeDamageWindowRect (CompWindow *w, Bool initial, BoxPtr rect) { Bool status = FALSE; RESIZE_SCREEN (w->screen); RESIZE_DISPLAY (w->screen->display); if (w == rd->w && rd->mode == RESIZE_MODE_STRETCH) { BoxRec box; resizeGetStretchRectangle (w->screen->display, &box); resizeDamageRectangle (w->screen, &box); status = TRUE; } UNWRAP (rs, w->screen, damageWindowRect); status |= (*w->screen->damageWindowRect) (w, initial, rect); WRAP (rs, w->screen, damageWindowRect, resizeDamageWindowRect); 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, const char *name, CompOptionValue *value) { CompOption *o; RESIZE_DISPLAY (display); o = compFindOption (rd->opt, NUM_OPTIONS (rd), name, NULL); if (!o) return FALSE; return compSetDisplayOption (display, o, value); } static const CompMetadataOptionInfo resizeDisplayOptionInfo[] = { { "initiate_normal_key", "key", 0, resizeInitiate, resizeTerminate }, { "initiate_outline_key", "key", 0, resizeInitiate, resizeTerminate }, { "initiate_rectangle_key", "key", 0, resizeInitiate, resizeTerminate }, { "initiate_stretch_key", "key", 0, resizeInitiate, resizeTerminate }, { "initiate_button", "button", 0, resizeInitiate, resizeTerminate }, { "initiate_key", "key", 0, resizeInitiate, resizeTerminate }, { "mode", "int", RESTOSTRING (0, RESIZE_MODE_LAST), 0, 0 }, { "border_color", "color", 0, 0, 0 }, { "fill_color", "color", 0, 0, 0 }, { "normal_match", "match", 0, 0, 0 }, { "outline_match", "match", 0, 0, 0 }, { "rectangle_match", "match", 0, 0, 0 }, { "stretch_match", "match", 0, 0, 0 } }; static Bool resizeInitDisplay (CompPlugin *p, CompDisplay *d) { ResizeDisplay *rd; int i; if (!checkPluginABI ("core", CORE_ABIVERSION)) return FALSE; rd = malloc (sizeof (ResizeDisplay)); if (!rd) return FALSE; if (!compInitDisplayOptionsFromMetadata (d, &resizeMetadata, resizeDisplayOptionInfo, rd->opt, RESIZE_DISPLAY_OPTION_NUM)) { free (rd); return FALSE; } rd->screenPrivateIndex = allocateScreenPrivateIndex (d); if (rd->screenPrivateIndex < 0) { compFiniDisplayOptions (d, rd->opt, RESIZE_DISPLAY_OPTION_NUM); free (rd); return FALSE; } rd->w = 0; rd->releaseButton = 0; rd->resizeNotifyAtom = XInternAtom (d->display, "_COMPIZ_RESIZE_NOTIFY", 0); rd->resizeInformationAtom = XInternAtom (d->display, "_COMPIZ_RESIZE_INFORMATION", 0); for (i = 0; i < NUM_KEYS; i++) rd->key[i] = XKeysymToKeycode (d->display, XStringToKeysym (rKeys[i].name)); rd->offWorkAreaConstrained = TRUE; WRAP (rd, d, handleEvent, resizeHandleEvent); d->base.privates[displayPrivateIndex].ptr = rd; return TRUE; } static void resizeFiniDisplay (CompPlugin *p, CompDisplay *d) { RESIZE_DISPLAY (d); freeScreenPrivateIndex (d, rd->screenPrivateIndex); UNWRAP (rd, d, handleEvent); compFiniDisplayOptions (d, rd->opt, RESIZE_DISPLAY_OPTION_NUM); 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; rs->grabWindowWorkArea = NULL; WRAP (rs, s, windowResizeNotify, resizeWindowResizeNotify); WRAP (rs, s, paintOutput, resizePaintOutput); WRAP (rs, s, paintWindow, resizePaintWindow); WRAP (rs, s, damageWindowRect, resizeDamageWindowRect); s->base.privates[rd->screenPrivateIndex].ptr = rs; return TRUE; } static void resizeFiniScreen (CompPlugin *p, CompScreen *s) { RESIZE_SCREEN (s); 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); UNWRAP (rs, s, windowResizeNotify); UNWRAP (rs, s, paintOutput); UNWRAP (rs, s, paintWindow); UNWRAP (rs, s, damageWindowRect); free (rs); } static CompBool resizeInitObject (CompPlugin *p, CompObject *o) { static InitPluginObjectProc dispTab[] = { (InitPluginObjectProc) 0, /* InitCore */ (InitPluginObjectProc) resizeInitDisplay, (InitPluginObjectProc) resizeInitScreen }; RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o)); } static void resizeFiniObject (CompPlugin *p, CompObject *o) { static FiniPluginObjectProc dispTab[] = { (FiniPluginObjectProc) 0, /* FiniCore */ (FiniPluginObjectProc) resizeFiniDisplay, (FiniPluginObjectProc) resizeFiniScreen }; DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o)); } static CompOption * resizeGetObjectOptions (CompPlugin *plugin, CompObject *object, int *count) { static GetPluginObjectOptionsProc dispTab[] = { (GetPluginObjectOptionsProc) 0, /* GetCoreOptions */ (GetPluginObjectOptionsProc) resizeGetDisplayOptions }; *count = 0; RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab), (void *) count, (plugin, object, count)); } static CompBool resizeSetObjectOption (CompPlugin *plugin, CompObject *object, const char *name, CompOptionValue *value) { static SetPluginObjectOptionProc dispTab[] = { (SetPluginObjectOptionProc) 0, /* SetCoreOption */ (SetPluginObjectOptionProc) resizeSetDisplayOption }; RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab), FALSE, (plugin, object, name, value)); } static Bool resizeInit (CompPlugin *p) { if (!compInitPluginMetadataFromInfo (&resizeMetadata, p->vTable->name, resizeDisplayOptionInfo, RESIZE_DISPLAY_OPTION_NUM, 0, 0)) return FALSE; displayPrivateIndex = allocateDisplayPrivateIndex (); if (displayPrivateIndex < 0) { compFiniMetadata (&resizeMetadata); return FALSE; } compAddMetadataFromFile (&resizeMetadata, p->vTable->name); return TRUE; } static void resizeFini (CompPlugin *p) { freeDisplayPrivateIndex (displayPrivateIndex); compFiniMetadata (&resizeMetadata); } static CompMetadata * resizeGetMetadata (CompPlugin *plugin) { return &resizeMetadata; } CompPluginVTable resizeVTable = { "resize", resizeGetMetadata, resizeInit, resizeFini, resizeInitObject, resizeFiniObject, resizeGetObjectOptions, resizeSetObjectOption }; CompPluginVTable * getCompPluginInfo20070830 (void) { return &resizeVTable; }