/* * 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 #include #include #include #include #include #include #include #include #define WIN_X(w) ((w)->attrib.x - (w)->input.left) #define WIN_Y(w) ((w)->attrib.y - (w)->input.top) #define WIN_W(w) ((w)->width + (w)->input.left + (w)->input.right) #define WIN_H(w) ((w)->height + (w)->input.top + (w)->input.bottom) #define SCALE_SPACING_DEFAULT 10 #define SCALE_SPACING_MIN 0 #define SCALE_SPACING_MAX 250 #define SCALE_INITIATE_KEY_DEFAULT "Up" #define SCALE_INITIATE_MODIFIERS_DEFAULT (ControlMask | CompAltMask) #define SCALE_SPEED_DEFAULT 1.5f #define SCALE_SPEED_MIN 0.1f #define SCALE_SPEED_MAX 50.0f #define SCALE_SPEED_PRECISION 0.1f #define SCALE_TIMESTEP_DEFAULT 1.2f #define SCALE_TIMESTEP_MIN 0.1f #define SCALE_TIMESTEP_MAX 50.0f #define SCALE_TIMESTEP_PRECISION 0.1f #define SCALE_STATE_NONE 0 #define SCALE_STATE_OUT 1 #define SCALE_STATE_WAIT 2 #define SCALE_STATE_IN 3 #define SCALE_DARKEN_BACK_DEFAULT TRUE #define SCALE_OPACITY_DEFAULT 75 #define SCALE_OPACITY_MIN 0 #define SCALE_OPACITY_MAX 100 #define SCALE_WINDOW_MATCH_DEFAULT \ "Toolbar | Utility | Dialog | Normal | Unknown" typedef enum { ScaleIconNone = 0, ScaleIconEmblem, ScaleIconBig } IconOverlay; static char *iconOverlayString[] = { N_("None"), N_("Emblem"), N_("Big") }; static IconOverlay iconOverlay[] = { ScaleIconNone, ScaleIconEmblem, ScaleIconBig }; #define N_ICON_TYPE (sizeof (iconOverlayString) / sizeof (iconOverlayString[0])) #define SCALE_ICON_DEFAULT (iconOverlayString[1]) #define SCALE_HOVER_TIME_DEFAULT 750 #define SCALE_HOVER_TIME_MIN 50 #define SCALE_HOVER_TIME_MAX 10000 static int displayPrivateIndex; typedef struct _ScaleSlot { int x1, y1, x2, y2; int filled; float scale; } ScaleSlot; #define SCALE_DISPLAY_OPTION_INITIATE 0 #define SCALE_DISPLAY_OPTION_INITIATE_ALL 1 #define SCALE_DISPLAY_OPTION_INITIATE_GROUP 2 #define SCALE_DISPLAY_OPTION_INITIATE_OUTPUT 3 #define SCALE_DISPLAY_OPTION_NUM 4 typedef struct _ScaleDisplay { int screenPrivateIndex; HandleEventProc handleEvent; CompOption opt[SCALE_DISPLAY_OPTION_NUM]; Bool xevie; unsigned int lastActiveNum; Window lastActiveWindow; Window selectedWindow; KeyCode leftKeyCode, rightKeyCode, upKeyCode, downKeyCode; } ScaleDisplay; #define SCALE_SCREEN_OPTION_SPACING 0 #define SCALE_SCREEN_OPTION_SPEED 1 #define SCALE_SCREEN_OPTION_TIMESTEP 2 #define SCALE_SCREEN_OPTION_WINDOW_MATCH 3 #define SCALE_SCREEN_OPTION_DARKEN_BACK 4 #define SCALE_SCREEN_OPTION_OPACITY 5 #define SCALE_SCREEN_OPTION_ICON 6 #define SCALE_SCREEN_OPTION_HOVER_TIME 7 #define SCALE_SCREEN_OPTION_NUM 8 typedef enum { ScaleTypeNormal = 0, ScaleTypeOutput, ScaleTypeGroup, ScaleTypeAll } ScaleType; typedef struct _ScaleScreen { int windowPrivateIndex; PreparePaintScreenProc preparePaintScreen; DonePaintScreenProc donePaintScreen; PaintScreenProc paintScreen; PaintWindowProc paintWindow; DamageWindowRectProc damageWindowRect; CompOption opt[SCALE_SCREEN_OPTION_NUM]; int spacing; float speed; float timestep; Bool grab; // int grabIndex; Window dndTarget; CompTimeoutHandle hoverHandle; int state; int moreAdjust; Cursor cursor; ScaleSlot *slots; int slotsSize; int nSlots; /* only used for sorting */ CompWindow **windows; int windowsSize; int nWindows; Bool darkenBack; GLushort opacity; IconOverlay iconOverlay; ScaleType type; Window clientLeader; CompMatch match; CompMatch *currentMatch; } ScaleScreen; typedef struct _ScaleWindow { ScaleSlot *slot; int sid; int distance; GLfloat xVelocity, yVelocity, scaleVelocity; GLfloat scale; GLfloat tx, ty; float delta; Bool adjust; float lastThumbOpacity; } ScaleWindow; #define GET_SCALE_DISPLAY(d) \ ((ScaleDisplay *) (d)->privates[displayPrivateIndex].ptr) #define SCALE_DISPLAY(d) \ ScaleDisplay *sd = GET_SCALE_DISPLAY (d) #define GET_SCALE_SCREEN(s, sd) \ ((ScaleScreen *) (s)->privates[(sd)->screenPrivateIndex].ptr) #define SCALE_SCREEN(s) \ ScaleScreen *ss = GET_SCALE_SCREEN (s, GET_SCALE_DISPLAY (s->display)) #define GET_SCALE_WINDOW(w, ss) \ ((ScaleWindow *) (w)->privates[(ss)->windowPrivateIndex].ptr) #define SCALE_WINDOW(w) \ ScaleWindow *sw = GET_SCALE_WINDOW (w, \ GET_SCALE_SCREEN (w->screen, \ GET_SCALE_DISPLAY (w->screen->display))) #define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption)) static CompOption * scaleGetScreenOptions (CompPlugin *plugin, CompScreen *screen, int *count) { SCALE_SCREEN (screen); *count = NUM_OPTIONS (ss); return ss->opt; } static Bool scaleSetScreenOption (CompPlugin *plugin, CompScreen *screen, char *name, CompOptionValue *value) { CompOption *o; int index; SCALE_SCREEN (screen); o = compFindOption (ss->opt, NUM_OPTIONS (ss), name, &index); if (!o) return FALSE; switch (index) { case SCALE_SCREEN_OPTION_SPACING: if (compSetIntOption (o, value)) { ss->spacing = o->value.i; return TRUE; } break; case SCALE_SCREEN_OPTION_SPEED: if (compSetFloatOption (o, value)) { ss->speed = o->value.f; return TRUE; } break; case SCALE_SCREEN_OPTION_TIMESTEP: if (compSetFloatOption (o, value)) { ss->timestep = o->value.f; return TRUE; } break; case SCALE_SCREEN_OPTION_DARKEN_BACK: if (compSetBoolOption (o, value)) { ss->darkenBack = o->value.b; return TRUE; } break; case SCALE_SCREEN_OPTION_OPACITY: if (compSetIntOption (o, value)) { ss->opacity = (OPAQUE * o->value.i) / 100; return TRUE; } break; case SCALE_SCREEN_OPTION_ICON: if (compSetStringOption (o, value)) { int i; for (i = 0; i < N_ICON_TYPE; i++) { if (strcmp (o->value.s, iconOverlayString[i]) == 0) { ss->iconOverlay = iconOverlay[i]; return TRUE; } } } break; default: if (compSetOption (o, value)) return TRUE; break; } return FALSE; } static void scaleScreenInitOptions (ScaleScreen *ss) { CompOption *o; o = &ss->opt[SCALE_SCREEN_OPTION_SPACING]; o->name = "spacing"; o->shortDesc = N_("Spacing"); o->longDesc = N_("Space between windows"); o->type = CompOptionTypeInt; o->value.i = SCALE_SPACING_DEFAULT; o->rest.i.min = SCALE_SPACING_MIN; o->rest.i.max = SCALE_SPACING_MAX; o = &ss->opt[SCALE_SCREEN_OPTION_SPEED]; o->name = "speed"; o->shortDesc = N_("Speed"); o->longDesc = N_("Scale speed"); o->type = CompOptionTypeFloat; o->value.f = SCALE_SPEED_DEFAULT; o->rest.f.min = SCALE_SPEED_MIN; o->rest.f.max = SCALE_SPEED_MAX; o->rest.f.precision = SCALE_SPEED_PRECISION; o = &ss->opt[SCALE_SCREEN_OPTION_TIMESTEP]; o->name = "timestep"; o->shortDesc = N_("Timestep"); o->longDesc = N_("Scale timestep"); o->type = CompOptionTypeFloat; o->value.f = SCALE_TIMESTEP_DEFAULT; o->rest.f.min = SCALE_TIMESTEP_MIN; o->rest.f.max = SCALE_TIMESTEP_MAX; o->rest.f.precision = SCALE_TIMESTEP_PRECISION; o = &ss->opt[SCALE_SCREEN_OPTION_WINDOW_MATCH]; o->name = "window_match"; o->shortDesc = N_("Scale Windows"); o->longDesc = N_("Windows that should be scaled in scale mode"); o->type = CompOptionTypeMatch; matchInit (&o->value.match); matchAddFromString (&o->value.match, SCALE_WINDOW_MATCH_DEFAULT); o = &ss->opt[SCALE_SCREEN_OPTION_DARKEN_BACK]; o->name = "darken_back"; o->shortDesc = N_("Darken Background"); o->longDesc = N_("Darken background when scaling windows"); o->type = CompOptionTypeBool; o->value.b = SCALE_DARKEN_BACK_DEFAULT; o = &ss->opt[SCALE_SCREEN_OPTION_OPACITY]; o->name = "opacity"; o->shortDesc = N_("Opacity"); o->longDesc = N_("Amount of opacity in percent"); o->type = CompOptionTypeInt; o->value.i = SCALE_OPACITY_DEFAULT; o->rest.i.min = SCALE_OPACITY_MIN; o->rest.i.max = SCALE_OPACITY_MAX; o = &ss->opt[SCALE_SCREEN_OPTION_ICON]; o->name = "overlay_icon"; o->shortDesc = N_("Overlay Icon"); o->longDesc = N_("Overlay an icon on windows once they are scaled"); o->type = CompOptionTypeString; o->value.s = strdup (SCALE_ICON_DEFAULT); o->rest.s.string = iconOverlayString; o->rest.s.nString = N_ICON_TYPE; o = &ss->opt[SCALE_SCREEN_OPTION_HOVER_TIME]; o->name = "hover_time"; o->shortDesc = N_("Hover Time"); o->longDesc = N_("Time (in ms) before scale mode is terminated when " "hovering over a window"); o->type = CompOptionTypeInt; o->value.i = SCALE_HOVER_TIME_DEFAULT; o->rest.i.min = SCALE_HOVER_TIME_MIN; o->rest.i.max = SCALE_HOVER_TIME_MAX; } static Bool isNeverScaleWin (CompWindow *w) { if (w->attrib.override_redirect) return TRUE; if (w->wmType & (CompWindowTypeDockMask | CompWindowTypeDesktopMask)) return TRUE; return FALSE; } static Bool isScaleWin (CompWindow *w) { SCALE_SCREEN (w->screen); if (isNeverScaleWin (w)) return FALSE; if (!ss->type || ss->type == ScaleTypeOutput) { if (!(*w->screen->focusWindow) (w)) return FALSE; } if (w->state & CompWindowStateSkipPagerMask) return FALSE; if (w->state & CompWindowStateShadedMask) return FALSE; if (!w->mapNum || w->attrib.map_state != IsViewable) return FALSE; switch (ss->type) { case ScaleTypeGroup: if (ss->clientLeader != w->clientLeader && ss->clientLeader != w->id) return FALSE; break; case ScaleTypeOutput: if (outputDeviceForWindow(w) != w->screen->currentOutputDev) return FALSE; default: break; } if (!matchEval (ss->currentMatch, w)) return FALSE; return TRUE; } static Bool scalePaintWindow (CompWindow *w, const WindowPaintAttrib *attrib, const CompTransform *transform, Region region, unsigned int mask) { CompScreen *s = w->screen; Bool status; SCALE_SCREEN (s); if (ss->state != SCALE_STATE_NONE) { WindowPaintAttrib sAttrib = *attrib; Bool scaled = FALSE; SCALE_WINDOW (w); if (sw->adjust || sw->slot) { SCALE_DISPLAY (s->display); if (w->id != sd->selectedWindow && ss->opacity != OPAQUE && ss->state != SCALE_STATE_IN) { /* modify opacity of windows that are not active */ sAttrib.opacity = (sAttrib.opacity * ss->opacity) >> 16; } scaled = TRUE; mask |= PAINT_WINDOW_NO_CORE_INSTANCE_MASK; } else if (ss->state != SCALE_STATE_IN) { if (ss->darkenBack) { /* modify brightness of the other windows */ sAttrib.brightness = sAttrib.brightness / 2; } /* hide windows on this output that are not in scale mode */ if (!isNeverScaleWin (w) && outputDeviceForWindow (w) == s->currentOutputDev) { sAttrib.opacity = 0; } } UNWRAP (ss, s, paintWindow); status = (*s->paintWindow) (w, &sAttrib, transform, region, mask); WRAP (ss, s, paintWindow, scalePaintWindow); if (scaled) { FragmentAttrib fragment; CompTransform wTransform = *transform; if (mask & PAINT_WINDOW_OCCLUSION_DETECTION_MASK) return FALSE; initFragmentAttrib (&fragment, &w->lastPaint); if (w->alpha || fragment.opacity != OPAQUE) mask |= PAINT_WINDOW_TRANSLUCENT_MASK; matrixTranslate (&wTransform, w->attrib.x, w->attrib.y, 0.0f); matrixScale (&wTransform, sw->scale, sw->scale, 0.0f); matrixTranslate (&wTransform, sw->tx / sw->scale - w->attrib.x, sw->ty / sw->scale - w->attrib.y, 0.0f); glPushMatrix (); glLoadMatrixf (wTransform.m); (*s->drawWindow) (w, &wTransform, &fragment, region, mask | PAINT_WINDOW_TRANSFORMED_MASK); glPopMatrix (); } if ((ss->iconOverlay != ScaleIconNone) && scaled) { CompIcon *icon; icon = getWindowIcon (w, 96, 96); if (!icon) icon = w->screen->defaultIcon; if (icon && (icon->texture.name || iconToTexture (w->screen, icon))) { REGION iconReg; float scale; float x, y; int width, height; int scaledWinWidth, scaledWinHeight; float ds; scaledWinWidth = w->width * sw->scale; scaledWinHeight = w->height * sw->scale; switch (ss->iconOverlay) { case ScaleIconNone: case ScaleIconEmblem: scale = 1.0f; break; case ScaleIconBig: default: sAttrib.opacity /= 3; scale = MIN (((float) scaledWinWidth / icon->width), ((float) scaledWinHeight / icon->height)); break; } width = icon->width * scale; height = icon->height * scale; switch (ss->iconOverlay) { case ScaleIconNone: case ScaleIconEmblem: x = w->attrib.x + scaledWinWidth - icon->width; y = w->attrib.y + scaledWinHeight - icon->height; break; case ScaleIconBig: default: x = w->attrib.x + scaledWinWidth / 2 - width / 2; y = w->attrib.y + scaledWinHeight / 2 - height / 2; break; } x += sw->tx; y += sw->ty; if (sw->slot) { sw->delta = fabs (sw->slot->x1 - w->attrib.x) + fabs (sw->slot->y1 - w->attrib.y) + fabs (1.0f - sw->slot->scale) * 500.0f; } if (sw->delta) { float o; ds = fabs (sw->tx) + fabs (sw->ty) + fabs (1.0f - sw->scale) * 500.0f; if (ds > sw->delta) ds = sw->delta; o = ds / sw->delta; if (sw->slot) { if (o < sw->lastThumbOpacity) o = sw->lastThumbOpacity; } else { if (o > sw->lastThumbOpacity) o = 0.0f; } sw->lastThumbOpacity = o; sAttrib.opacity = sAttrib.opacity * o; } mask |= PAINT_WINDOW_BLEND_MASK; iconReg.rects = &iconReg.extents; iconReg.numRects = 1; iconReg.extents.x1 = 0; iconReg.extents.y1 = 0; iconReg.extents.x2 = iconReg.extents.x1 + width; iconReg.extents.y2 = iconReg.extents.y1 + height; w->vCount = w->indexCount = 0; if (iconReg.extents.x1 < iconReg.extents.x2 && iconReg.extents.y1 < iconReg.extents.y2) (*w->screen->addWindowGeometry) (w, &icon->texture.matrix, 1, &iconReg, &iconReg); if (w->vCount) { FragmentAttrib fragment; CompTransform wTransform = *transform; initFragmentAttrib (&fragment, &sAttrib); matrixScale (&wTransform, scale, scale, 1.0f); matrixTranslate (&wTransform, x / scale, y / scale, 0.0f); glPushMatrix (); glLoadMatrixf (wTransform.m); (*w->screen->drawWindowTexture) (w, &icon->texture, &fragment, mask); glPopMatrix (); } } } } else { UNWRAP (ss, s, paintWindow); status = (*s->paintWindow) (w, attrib, transform, region, mask); WRAP (ss, s, paintWindow, scalePaintWindow); } return status; } static int compareWindowsDistance (const void *elem1, const void *elem2) { CompWindow *w1 = *((CompWindow **) elem1); CompWindow *w2 = *((CompWindow **) elem2); SCALE_SCREEN (w1->screen); return GET_SCALE_WINDOW (w1, ss)->distance - GET_SCALE_WINDOW (w2, ss)->distance; } static void layoutSlots (CompScreen *s) { int i, j, x, y, width, height, lines, n; int x1, y1, x2, y2; SCALE_SCREEN (s); ss->nSlots = 0; lines = sqrt (ss->nWindows + 1); getCurrentOutputExtents (s, &x1, &y1, &x2, &y2); if (s->workArea.x > x1) x1 = s->workArea.x; if (s->workArea.x + s->workArea.width < x2) x2 = s->workArea.x + s->workArea.width; if (s->workArea.y > y1) y1 = s->workArea.y; if (s->workArea.y + s->workArea.height < y2) y2 = s->workArea.y + s->workArea.height; y = y1 + ss->spacing; height = ((y2 - y1) - (lines + 1) * ss->spacing) / lines; for (i = 0; i < lines; i++) { n = MIN (ss->nWindows - ss->nSlots, ceilf ((float) ss->nWindows / lines)); x = x1 + ss->spacing; width = ((x2 - x1) - (n + 1) * ss->spacing) / n; for (j = 0; j < n; j++) { ss->slots[ss->nSlots].x1 = x; ss->slots[ss->nSlots].y1 = y; ss->slots[ss->nSlots].x2 = x + width; ss->slots[ss->nSlots].y2 = y + height; ss->slots[ss->nSlots].filled = FALSE; x += width + ss->spacing; ss->nSlots++; } y += height + ss->spacing; } } static void findBestSlots (CompScreen *s) { CompWindow *w; int i, j, d, d0 = 0; float sx, sy, cx, cy; SCALE_SCREEN (s); for (i = 0; i < ss->nWindows; i++) { w = ss->windows[i]; SCALE_WINDOW (w); if (sw->slot) continue; sw->sid = 0; sw->distance = MAXSHORT; for (j = 0; j < ss->nSlots; j++) { if (!ss->slots[j].filled) { sx = (ss->slots[j].x2 + ss->slots[j].x1) / 2; sy = (ss->slots[j].y2 + ss->slots[j].y1) / 2; cx = w->serverX + w->width / 2; cy = w->serverY + w->height / 2; cx -= sx; cy -= sy; d = sqrt (cx * cx + cy * cy); if (d0 + d < sw->distance) { sw->sid = j; sw->distance = d0 + d; } } } d0 += sw->distance; } } static Bool fillInWindows (CompScreen *s) { CompWindow *w; int i, width, height; float sx, sy, cx, cy; SCALE_SCREEN (s); for (i = 0; i < ss->nWindows; i++) { w = ss->windows[i]; SCALE_WINDOW (w); if (!sw->slot) { if (ss->slots[sw->sid].filled) return TRUE; sw->slot = &ss->slots[sw->sid]; width = w->width + w->input.left + w->input.right; height = w->height + w->input.top + w->input.bottom; sx = (float) (sw->slot->x2 - sw->slot->x1) / width; sy = (float) (sw->slot->y2 - sw->slot->y1) / height; sw->slot->scale = MIN (MIN (sx, sy), 1.0f); sx = width * sw->slot->scale; sy = height * sw->slot->scale; cx = (sw->slot->x1 + sw->slot->x2) / 2; cy = (sw->slot->y1 + sw->slot->y2) / 2; cx += w->input.left * sw->slot->scale; cy += w->input.top * sw->slot->scale; sw->slot->x1 = cx - sx / 2; sw->slot->y1 = cy - sy / 2; sw->slot->x2 = cx + sx / 2; sw->slot->y2 = cy + sy / 2; sw->slot->filled = TRUE; sw->lastThumbOpacity = 0.0f; sw->adjust = TRUE; } } return FALSE; } static Bool layoutThumbs (CompScreen *s) { CompWindow *w; SCALE_SCREEN (s); ss->nWindows = 0; /* add windows scale list, top most window first */ for (w = s->reverseWindows; w; w = w->prev) { SCALE_WINDOW (w); if (sw->slot) sw->adjust = TRUE; sw->slot = 0; if (!isScaleWin (w)) continue; if (ss->windowsSize <= ss->nWindows) { ss->windows = realloc (ss->windows, sizeof (CompWindow *) * (ss->nWindows + 32)); if (!ss->windows) return FALSE; ss->windowsSize = ss->nWindows + 32; } ss->windows[ss->nWindows++] = w; } if (ss->nWindows == 0) return FALSE; if (ss->slotsSize < ss->nWindows) { ss->slots = realloc (ss->slots, sizeof (ScaleSlot) * ss->nWindows); if (!ss->slots) return FALSE; ss->slotsSize = ss->nWindows; } /* create a grid of slots */ layoutSlots (s); do { /* find most appropriate slots for windows */ findBestSlots (s); /* sort windows, window with closest distance to a slot first */ qsort (ss->windows, ss->nWindows, sizeof (CompWindow *), compareWindowsDistance); } while (fillInWindows (s)); return TRUE; } static int adjustScaleVelocity (CompWindow *w) { float dx, dy, ds, adjust, amount; float x1, y1, scale; SCALE_WINDOW (w); if (sw->slot) { x1 = sw->slot->x1; y1 = sw->slot->y1; scale = sw->slot->scale; } else { x1 = w->attrib.x; y1 = w->attrib.y; scale = 1.0f; } dx = x1 - (w->attrib.x + sw->tx); adjust = dx * 0.15f; amount = fabs (dx) * 1.5f; if (amount < 0.5f) amount = 0.5f; else if (amount > 5.0f) amount = 5.0f; sw->xVelocity = (amount * sw->xVelocity + adjust) / (amount + 1.0f); dy = y1 - (w->attrib.y + sw->ty); adjust = dy * 0.15f; amount = fabs (dy) * 1.5f; if (amount < 0.5f) amount = 0.5f; else if (amount > 5.0f) amount = 5.0f; sw->yVelocity = (amount * sw->yVelocity + adjust) / (amount + 1.0f); ds = scale - sw->scale; adjust = ds * 0.1f; amount = fabs (ds) * 7.0f; if (amount < 0.01f) amount = 0.01f; else if (amount > 0.15f) amount = 0.15f; sw->scaleVelocity = (amount * sw->scaleVelocity + adjust) / (amount + 1.0f); if (fabs (dx) < 0.1f && fabs (sw->xVelocity) < 0.2f && fabs (dy) < 0.1f && fabs (sw->yVelocity) < 0.2f && fabs (ds) < 0.001f && fabs (sw->scaleVelocity) < 0.002f) { sw->xVelocity = sw->yVelocity = sw->scaleVelocity = 0.0f; sw->tx = x1 - w->attrib.x; sw->ty = y1 - w->attrib.y; sw->scale = scale; return 0; } return 1; } static Bool scalePaintScreen (CompScreen *s, const ScreenPaintAttrib *sAttrib, const CompTransform *transform, Region region, int output, unsigned int mask) { Bool status; SCALE_SCREEN (s); if (ss->state != SCALE_STATE_NONE) mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK; UNWRAP (ss, s, paintScreen); status = (*s->paintScreen) (s, sAttrib, transform, region, output, mask); WRAP (ss, s, paintScreen, scalePaintScreen); return status; } static void scalePreparePaintScreen (CompScreen *s, int msSinceLastPaint) { SCALE_SCREEN (s); if (ss->state != SCALE_STATE_NONE && ss->state != SCALE_STATE_WAIT) { CompWindow *w; int steps; float amount, chunk; amount = msSinceLastPaint * 0.05f * ss->speed; steps = amount / (0.5f * ss->timestep); if (!steps) steps = 1; chunk = amount / (float) steps; while (steps--) { ss->moreAdjust = 0; for (w = s->windows; w; w = w->next) { SCALE_WINDOW (w); if (sw->adjust) { sw->adjust = adjustScaleVelocity (w); ss->moreAdjust |= sw->adjust; sw->tx += sw->xVelocity * chunk; sw->ty += sw->yVelocity * chunk; sw->scale += sw->scaleVelocity * chunk; } } if (!ss->moreAdjust) break; } } UNWRAP (ss, s, preparePaintScreen); (*s->preparePaintScreen) (s, msSinceLastPaint); WRAP (ss, s, preparePaintScreen, scalePreparePaintScreen); } static void scaleDonePaintScreen (CompScreen *s) { SCALE_SCREEN (s); if (ss->state != SCALE_STATE_NONE) { if (ss->moreAdjust) { damageScreen (s); } else { if (ss->state == SCALE_STATE_IN) ss->state = SCALE_STATE_NONE; else if (ss->state == SCALE_STATE_OUT) ss->state = SCALE_STATE_WAIT; } } UNWRAP (ss, s, donePaintScreen); (*s->donePaintScreen) (s); WRAP (ss, s, donePaintScreen, scaleDonePaintScreen); } static CompWindow * scaleCheckForWindowAt (CompScreen *s, int x, int y) { int x1, y1, x2, y2; CompWindow *w; for (w = s->reverseWindows; w; w = w->prev) { SCALE_WINDOW (w); if (sw->slot) { x1 = w->attrib.x - w->input.left * sw->scale; y1 = w->attrib.y - w->input.top * sw->scale; x2 = w->attrib.x + (w->width + w->input.right) * sw->scale; y2 = w->attrib.y + (w->height + w->input.bottom) * sw->scale; x1 += sw->tx; y1 += sw->ty; x2 += sw->tx; y2 += sw->ty; if (x1 <= x && y1 <= y && x2 > x && y2 > y) return w; } } return 0; } static void scaleTransformXY(CompWindow* w, int x, int y, int *newX, int *newY) { int x1, y1; SCALE_WINDOW(w); x1 = w->attrib.x + sw->tx; y1 = w->attrib.y + sw->ty; *newX = (x - x1) / sw->scale; *newY = (y - y1) / sw->scale; } static void sendViewportMoveRequest (CompScreen *s, int x, int y) { XEvent xev; xev.xclient.type = ClientMessage; xev.xclient.display = s->display->display; xev.xclient.format = 32; xev.xclient.message_type = s->display->desktopViewportAtom; xev.xclient.window = s->root; xev.xclient.data.l[0] = x; xev.xclient.data.l[1] = y; xev.xclient.data.l[2] = 0; xev.xclient.data.l[3] = 0; xev.xclient.data.l[4] = 0; XSendEvent (s->display->display, s->root, FALSE, SubstructureRedirectMask | SubstructureNotifyMask, &xev); } static void sendDndStatusMessage (CompScreen *s, Window source) { XEvent xev; SCALE_SCREEN (s); xev.xclient.type = ClientMessage; xev.xclient.display = s->display->display; xev.xclient.format = 32; xev.xclient.message_type = s->display->xdndStatusAtom; xev.xclient.window = source; xev.xclient.data.l[0] = ss->dndTarget; xev.xclient.data.l[1] = 2; xev.xclient.data.l[2] = 0; xev.xclient.data.l[3] = 0; xev.xclient.data.l[4] = None; XSendEvent (s->display->display, source, FALSE, 0, &xev); } static Bool scaleTerminate (CompDisplay *d, CompAction *action, CompActionState state, CompOption *option, int nOption) { CompScreen *s; Window xid; SCALE_DISPLAY (d); xid = getIntOptionNamed (option, nOption, "root", 0); for (s = d->screens; s; s = s->next) { SCALE_SCREEN (s); if (xid && s->root != xid) continue; if (ss->grab) { /*if (ss->grabIndex) { removeScreenGrab (s, ss->grabIndex, 0); ss->grabIndex = 0; }*/ if (sd->xevie) { sd->xevie = FALSE; XevieEnd(d->display); } if (ss->dndTarget) XUnmapWindow (d->display, ss->dndTarget); ss->grab = FALSE; if (ss->state != SCALE_STATE_NONE) { CompWindow *w; for (w = s->windows; w; w = w->next) { SCALE_WINDOW (w); if (sw->slot) { sw->slot = 0; sw->adjust = TRUE; } } if (ss->state != SCALE_STATE_IN) { w = findWindowAtScreen (s, sd->lastActiveWindow); if (w) { int x, y; activateWindow (w); defaultViewportForWindow (w, &x, &y); if (x != s->x || y != s->y) sendViewportMoveRequest (s, x * s->width, y * s->height); } } ss->state = SCALE_STATE_IN; damageScreen (s); } sd->lastActiveNum = 0; } } return FALSE; } static Bool scaleEnsureDndRedirectWindow (CompScreen *s) { SCALE_SCREEN (s); if (!ss->dndTarget) { XSetWindowAttributes attr; long xdndVersion = 3; attr.override_redirect = TRUE; ss->dndTarget = XCreateWindow (s->display->display, s->root, 0, 0, 1, 1, 0, CopyFromParent, InputOnly, CopyFromParent, CWOverrideRedirect, &attr); XChangeProperty (s->display->display, ss->dndTarget, s->display->xdndAwareAtom, XA_ATOM, 32, PropModeReplace, (unsigned char *) &xdndVersion, 1); } XMoveResizeWindow (s->display->display, ss->dndTarget, 0, 0, s->width, s->height); XMapRaised (s->display->display, ss->dndTarget); return TRUE; } static Bool scaleInitiateCommon (CompScreen *s, CompAction *action, CompActionState state, CompOption *option, int nOption) { CompMatch *match; SCALE_DISPLAY (s->display); SCALE_SCREEN (s); if (otherScreenGrabExist (s, "scale", 0)) return FALSE; ss->currentMatch = &ss->opt[SCALE_SCREEN_OPTION_WINDOW_MATCH].value.match; match = getMatchOptionNamed (option, nOption, "match", NULL); if (match) { matchFini (&ss->match); matchInit (&ss->match); if (matchCopy (&ss->match, match)) { matchUpdate (s->display, &ss->match); ss->currentMatch = &ss->match; } } if (!layoutThumbs (s)) return FALSE; if (state & CompActionStateInitEdgeDnd) { if (scaleEnsureDndRedirectWindow (s)) ss->grab = TRUE; } else { /*ss->grabIndex = pushScreenGrab (s, ss->cursor, "scale"); if (ss->grabIndex) ss->grab = TRUE;*/ if(XevieStart(s->display->display)) { ss->grab = TRUE; sd->xevie = TRUE; } } if (ss->grab) { if (!sd->lastActiveNum) sd->lastActiveNum = s->activeNum - 1; sd->lastActiveWindow = s->display->activeWindow; sd->selectedWindow = s->display->activeWindow; ss->state = SCALE_STATE_OUT; damageScreen (s); } if (state & CompActionStateInitButton) action->state |= CompActionStateTermButton; if (state & CompActionStateInitKey) action->state |= CompActionStateTermKey; return FALSE; } static Bool scaleInitiate (CompDisplay *d, CompAction *action, CompActionState state, CompOption *option, int nOption) { CompScreen *s; Window xid; xid = getIntOptionNamed (option, nOption, "root", 0); s = findScreenAtDisplay (d, xid); if (s) { SCALE_SCREEN (s); if (ss->state != SCALE_STATE_WAIT && ss->state != SCALE_STATE_OUT) { ss->type = ScaleTypeNormal; return scaleInitiateCommon (s, action, state, option, nOption); } } return FALSE; } static Bool scaleInitiateAll (CompDisplay *d, CompAction *action, CompActionState state, CompOption *option, int nOption) { CompScreen *s; Window xid; xid = getIntOptionNamed (option, nOption, "root", 0); s = findScreenAtDisplay (d, xid); if (s) { SCALE_SCREEN (s); if (ss->state != SCALE_STATE_WAIT && ss->state != SCALE_STATE_OUT) { ss->type = ScaleTypeAll; return scaleInitiateCommon (s, action, state, option, nOption); } } return FALSE; } static Bool scaleInitiateGroup (CompDisplay *d, CompAction *action, CompActionState state, CompOption *option, int nOption) { CompScreen *s; Window xid; xid = getIntOptionNamed (option, nOption, "root", 0); s = findScreenAtDisplay (d, xid); if (s) { SCALE_SCREEN (s); if (ss->state != SCALE_STATE_WAIT && ss->state != SCALE_STATE_OUT) { CompWindow *w; w = findWindowAtDisplay (d, getIntOptionNamed (option, nOption, "window", 0)); if (w) { ss->type = ScaleTypeGroup; ss->clientLeader = (w->clientLeader) ? w->clientLeader : w->id; return scaleInitiateCommon (s, action, state, option, nOption); } } } return FALSE; } static Bool scaleInitiateOutput (CompDisplay *d, CompAction *action, CompActionState state, CompOption *option, int nOption) { CompScreen *s; Window xid; xid = getIntOptionNamed (option, nOption, "root", 0); s = findScreenAtDisplay (d, xid); if (s) { SCALE_SCREEN (s); if (ss->state != SCALE_STATE_WAIT && ss->state != SCALE_STATE_OUT) { ss->type = ScaleTypeOutput; return scaleInitiateCommon (s, action, state, option, nOption); } } return FALSE; } static void scaleSelectWindow (CompWindow *w) { SCALE_DISPLAY (w->screen->display); if (sd->selectedWindow != w->id) { CompWindow *old, *new; old = findWindowAtScreen (w->screen, sd->selectedWindow); new = findWindowAtScreen (w->screen, w->id); sd->selectedWindow = w->id; if (old) addWindowDamage (old); addWindowDamage (new); } } static Bool scaleSelectWindowAt (CompScreen *s, int x, int y, Bool moveInputFocus) { CompWindow *w; w = scaleCheckForWindowAt (s, x, y); if (w && isScaleWin (w)) { SCALE_DISPLAY (s->display); scaleSelectWindow (w); if (moveInputFocus) { sd->lastActiveNum = w->activeNum; sd->lastActiveWindow = w->id; moveInputFocusToWindow (w); } return TRUE; } return FALSE; } static void scaleMoveFocusWindow (CompScreen *s, int dx, int dy) { CompWindow *active; CompWindow *focus = NULL; active = findWindowAtScreen (s, s->display->activeWindow); if (active) { SCALE_WINDOW (active); if (sw->slot) { SCALE_SCREEN (s); CompWindow *w; ScaleSlot *slot; int x, y, cx, cy, d, min = MAXSHORT; cx = (sw->slot->x1 + sw->slot->x2) / 2; cy = (sw->slot->y1 + sw->slot->y2) / 2; for (w = s->windows; w; w = w->next) { slot = GET_SCALE_WINDOW (w, ss)->slot; if (!slot) continue; x = (slot->x1 + slot->x2) / 2; y = (slot->y1 + slot->y2) / 2; d = abs (x - cx) + abs (y - cy); if (d < min) { if ((dx > 0 && slot->x1 < sw->slot->x2) || (dx < 0 && slot->x2 > sw->slot->x1) || (dy > 0 && slot->y1 < sw->slot->y2) || (dy < 0 && slot->y2 > sw->slot->y1)) continue; min = d; focus = w; } } } } /* move focus to the last focused window if no slot window is currently focused */ if (!focus) { CompWindow *w; SCALE_SCREEN (s); for (w = s->windows; w; w = w->next) { if (!GET_SCALE_WINDOW (w, ss)->slot) continue; if (!focus || focus->activeNum < w->activeNum) focus = w; } } if (focus) { SCALE_DISPLAY (s->display); scaleSelectWindow (focus); sd->lastActiveNum = focus->activeNum; sd->lastActiveWindow = focus->id; moveInputFocusToWindow (focus); } } static void scaleWindowRemove (CompDisplay *d, Window id) { CompWindow *w; w = findWindowAtDisplay (d, id); if (w) { SCALE_SCREEN (w->screen); if (ss->state != SCALE_STATE_NONE && ss->state != SCALE_STATE_IN) { int i; for (i = 0; i < ss->nWindows; i++) { if (ss->windows[i] == w) { if (layoutThumbs (w->screen)) { ss->state = SCALE_STATE_OUT; damageScreen (w->screen); break; } } } } } } static Bool scaleHoverTimeout (void *closure) { CompScreen *s = closure; SCALE_SCREEN (s); SCALE_DISPLAY (s->display); if (ss->grab && ss->state != SCALE_STATE_IN) { CompWindow *w; CompOption o; CompAction *action = &sd->opt[SCALE_DISPLAY_OPTION_INITIATE].value.action; w = findWindowAtDisplay (s->display, sd->selectedWindow); if (w) { sd->lastActiveNum = w->activeNum; sd->lastActiveWindow = w->id; moveInputFocusToWindow (w); } o.type = CompOptionTypeInt; o.name = "root"; o.value.i = s->root; scaleTerminate (s->display, action, 0, &o, 1); } ss->hoverHandle = 0; return FALSE; } static void scaleHandleEvent (CompDisplay *d, XEvent *event) { CompScreen *s; SCALE_DISPLAY (d); switch (event->type) { /*case KeyPress: s = findScreenAtDisplay (d, event->xkey.root); if (s) { SCALE_SCREEN (s); if (ss->grabIndex) { if (event->xkey.keycode == sd->leftKeyCode) scaleMoveFocusWindow (s, -1, 0); else if (event->xkey.keycode == sd->rightKeyCode) scaleMoveFocusWindow (s, 1, 0); else if (event->xkey.keycode == sd->upKeyCode) scaleMoveFocusWindow (s, 0, -1); else if (event->xkey.keycode == sd->downKeyCode) scaleMoveFocusWindow (s, 0, 1); } } break; case ButtonPress: if (event->xbutton.button == Button1) { s = findScreenAtDisplay (d, event->xbutton.root); if (s) { CompAction *action = &sd->opt[SCALE_DISPLAY_OPTION_INITIATE].value.action; SCALE_SCREEN (s); if (ss->grabIndex && ss->state != SCALE_STATE_IN) { CompOption o; o.type = CompOptionTypeInt; o.name = "root"; o.value.i = s->root; if (scaleSelectWindowAt (s, event->xbutton.x_root, event->xbutton.y_root, TRUE)) { scaleTerminate (d, action, 0, &o, 1); } else if (event->xbutton.x_root > s->workArea.x && event->xbutton.x_root < (s->workArea.x + s->workArea.width) && event->xbutton.y_root > s->workArea.y && event->xbutton.y_root < (s->workArea.y + s->workArea.height)) { scaleTerminate (d, action, 0, &o, 1); (*s->enterShowDesktopMode) (s); } } } } break;*/ case MotionNotify: s = findScreenAtDisplay (d, event->xmotion.root); if (s) { SCALE_SCREEN (s); if (ss->grab && ss->state != SCALE_STATE_IN) { Bool focus; focus = !d->opt[COMP_DISPLAY_OPTION_CLICK_TO_FOCUS].value.b; scaleSelectWindowAt (s, event->xmotion.x_root, event->xmotion.y_root, focus); } } break; case ClientMessage: if (event->xclient.message_type == d->xdndPositionAtom) { CompWindow *w; w = findWindowAtDisplay (d, event->xclient.window); if (w) { Bool focus; SCALE_SCREEN (w->screen); s = w->screen; focus = !d->opt[COMP_DISPLAY_OPTION_CLICK_TO_FOCUS].value.b; if (w->id == ss->dndTarget) sendDndStatusMessage (w->screen, event->xclient.data.l[0]); if (ss->grab && ss->state != SCALE_STATE_IN && w->id == ss->dndTarget) { int x = event->xclient.data.l[2] >> 16; int y = event->xclient.data.l[2] & 0xffff; w = scaleCheckForWindowAt (s, x, y); if (w && isScaleWin (w)) { int time; time = ss->opt[SCALE_SCREEN_OPTION_HOVER_TIME].value.i; if (ss->hoverHandle) { if (w->id != sd->selectedWindow) { compRemoveTimeout (ss->hoverHandle); ss->hoverHandle = 0; } } if (!ss->hoverHandle) ss->hoverHandle = compAddTimeout (time, scaleHoverTimeout, s); scaleSelectWindowAt (s, x, y, focus); } else { if (ss->hoverHandle) compRemoveTimeout (ss->hoverHandle); ss->hoverHandle = 0; } } } } else if (event->xclient.message_type == d->xdndDropAtom || event->xclient.message_type == d->xdndLeaveAtom) { CompWindow *w; w = findWindowAtDisplay (d, event->xclient.window); if (w) { CompAction *action = &sd->opt[SCALE_DISPLAY_OPTION_INITIATE].value.action; SCALE_SCREEN (w->screen); if (ss->grab && ss->state != SCALE_STATE_IN && w->id == ss->dndTarget) { CompOption o; o.type = CompOptionTypeInt; o.name = "root"; o.value.i = w->screen->root; scaleTerminate (d, action, 0, &o, 1); } } } default: break; } UNWRAP (sd, d, handleEvent); (*d->handleEvent) (d, event); WRAP (sd, d, handleEvent, scaleHandleEvent); switch (event->type) { case UnmapNotify: scaleWindowRemove (d, event->xunmap.window); break; case DestroyNotify: scaleWindowRemove (d, event->xdestroywindow.window); break; } if (sd->xevie) { XEvent newEvent = *event; Bool eventModified = FALSE; Bool xevieEvent = FALSE; switch (newEvent.type) { case KeyPress: case KeyRelease: xevieEvent = TRUE; break; case ButtonPress: case ButtonRelease: xevieEvent = TRUE; s = findScreenAtDisplay (d, newEvent.xbutton.root); if (s) { CompWindow* w = scaleCheckForWindowAt(s, newEvent.xbutton.x_root, newEvent.xbutton.y_root); if (w) { int newX, newY; scaleTransformXY(w, newEvent.xbutton.x_root, newEvent.xbutton.y_root, &newX, &newY); newEvent.xbutton.window = w->id; newEvent.xbutton.x = newX; newEvent.xbutton.y = newY; printf("%d %d\n", newX, newY); eventModified = TRUE; newEvent.xbutton.x_root = newX + WIN_X(w); newEvent.xbutton.y_root = newY + WIN_Y(w); } } break; case MotionNotify: xevieEvent = TRUE; s = findScreenAtDisplay (d, newEvent.xmotion.root); if (s) { CompWindow* w = scaleCheckForWindowAt(s, newEvent.xmotion.x_root, newEvent.xmotion.y_root); if (w) { int newX, newY; scaleTransformXY(w, newEvent.xmotion.x_root, newEvent.xmotion.y_root, &newX, &newY); newEvent.xmotion.window = w->id; newEvent.xmotion.x = newX; newEvent.xmotion.y = newY; printf("%d %d\n", newX, newY); eventModified = TRUE; newEvent.xmotion.x_root = newX + WIN_X(w); newEvent.xmotion.y_root = newY + WIN_Y(w); } } break; default: break; } if (xevieEvent) XevieSendEvent(d->display, &newEvent, (eventModified)? XEVIE_MODIFIED: XEVIE_UNMODIFIED); } } static Bool scaleDamageWindowRect (CompWindow *w, Bool initial, BoxPtr rect) { Bool status = FALSE; SCALE_SCREEN (w->screen); if (initial) { if (ss->grab && isScaleWin (w)) { if (layoutThumbs (w->screen)) { ss->state = SCALE_STATE_OUT; damageScreen (w->screen); } } } else if (ss->state == SCALE_STATE_WAIT) { SCALE_WINDOW (w); if (sw->slot) { damageTransformedWindowRect (w, sw->scale, sw->scale, sw->tx, sw->ty, rect); status = TRUE; } } UNWRAP (ss, w->screen, damageWindowRect); status |= (*w->screen->damageWindowRect) (w, initial, rect); WRAP (ss, w->screen, damageWindowRect, scaleDamageWindowRect); return status; } static CompOption * scaleGetDisplayOptions (CompPlugin *plugin, CompDisplay *display, int *count) { SCALE_DISPLAY (display); *count = NUM_OPTIONS (sd); return sd->opt; } static Bool scaleSetDisplayOption (CompPlugin *plugin, CompDisplay *display, char *name, CompOptionValue *value) { CompOption *o; int index; SCALE_DISPLAY (display); o = compFindOption (sd->opt, NUM_OPTIONS (sd), name, &index); if (!o) return FALSE; switch (index) { case SCALE_DISPLAY_OPTION_INITIATE: case SCALE_DISPLAY_OPTION_INITIATE_ALL: case SCALE_DISPLAY_OPTION_INITIATE_GROUP: case SCALE_DISPLAY_OPTION_INITIATE_OUTPUT: if (setDisplayAction (display, o, value)) return TRUE; default: break; } return FALSE; } static void scaleDisplayInitOptions (ScaleDisplay *sd, Display *display) { CompOption *o; o = &sd->opt[SCALE_DISPLAY_OPTION_INITIATE]; o->name = "initiate"; o->shortDesc = N_("Initiate Window Picker"); o->longDesc = N_("Layout and start transforming windows"); o->type = CompOptionTypeAction; o->value.action.initiate = scaleInitiate; o->value.action.terminate = scaleTerminate; o->value.action.bell = FALSE; o->value.action.edgeMask = (1 << SCREEN_EDGE_TOPRIGHT); o->value.action.state = CompActionStateInitEdge; o->value.action.state |= CompActionStateInitEdgeDnd; o->value.action.type = CompBindingTypeKey; o->value.action.state |= CompActionStateInitKey; o->value.action.state |= CompActionStateInitButton; o->value.action.key.modifiers = SCALE_INITIATE_MODIFIERS_DEFAULT; o->value.action.key.keycode = XKeysymToKeycode (display, XStringToKeysym (SCALE_INITIATE_KEY_DEFAULT)); o = &sd->opt[SCALE_DISPLAY_OPTION_INITIATE_ALL]; o->name = "initiate_all"; o->shortDesc = N_("Initiate Window Picker For All Windows"); o->longDesc = N_("Layout and start transforming all windows"); o->type = CompOptionTypeAction; o->value.action.initiate = scaleInitiateAll; o->value.action.terminate = scaleTerminate; o->value.action.bell = FALSE; o->value.action.edgeMask = 0; o->value.action.state = CompActionStateInitEdge; o->value.action.state |= CompActionStateInitEdgeDnd; o->value.action.type = 0; o->value.action.state |= CompActionStateInitKey; o->value.action.state |= CompActionStateInitButton; o = &sd->opt[SCALE_DISPLAY_OPTION_INITIATE_GROUP]; o->name = "initiate_group"; o->shortDesc = N_("Initiate Window Picker For Window Group"); o->longDesc = N_("Layout and start transforming " "window group"); o->type = CompOptionTypeAction; o->value.action.initiate = scaleInitiateGroup; o->value.action.terminate = scaleTerminate; o->value.action.bell = FALSE; o->value.action.edgeMask = 0; o->value.action.state = CompActionStateInitEdge; o->value.action.state |= CompActionStateInitEdgeDnd; o->value.action.type = 0; o->value.action.state |= CompActionStateInitKey; o->value.action.state |= CompActionStateInitButton; o = &sd->opt[SCALE_DISPLAY_OPTION_INITIATE_OUTPUT]; o->name = "initiate_output"; o->shortDesc = N_("Initiate Window Picker For Windows on " "Current Output"); o->longDesc = N_("Layout and start transforming " "windows on current output"); o->type = CompOptionTypeAction; o->value.action.initiate = scaleInitiateOutput; o->value.action.terminate = scaleTerminate; o->value.action.bell = FALSE; o->value.action.edgeMask = 0; o->value.action.state = CompActionStateInitEdge; o->value.action.state |= CompActionStateInitEdgeDnd; o->value.action.type = 0; o->value.action.state |= CompActionStateInitKey; o->value.action.state |= CompActionStateInitButton; } static Bool scaleInitDisplay (CompPlugin *p, CompDisplay *d) { ScaleDisplay *sd; sd = malloc (sizeof (ScaleDisplay)); if (!sd) return FALSE; sd->screenPrivateIndex = allocateScreenPrivateIndex (d); if (sd->screenPrivateIndex < 0) { free (sd); return FALSE; } sd->lastActiveNum = None; sd->selectedWindow = None; scaleDisplayInitOptions (sd, d->display); sd->leftKeyCode = XKeysymToKeycode (d->display, XStringToKeysym ("Left")); sd->rightKeyCode = XKeysymToKeycode (d->display, XStringToKeysym ("Right")); sd->upKeyCode = XKeysymToKeycode (d->display, XStringToKeysym ("Up")); sd->downKeyCode = XKeysymToKeycode (d->display, XStringToKeysym ("Down")); WRAP (sd, d, handleEvent, scaleHandleEvent); d->privates[displayPrivateIndex].ptr = sd; return TRUE; } static void scaleFiniDisplay (CompPlugin *p, CompDisplay *d) { SCALE_DISPLAY (d); freeScreenPrivateIndex (d, sd->screenPrivateIndex); UNWRAP (sd, d, handleEvent); free (sd); } static Bool scaleInitScreen (CompPlugin *p, CompScreen *s) { ScaleScreen *ss; SCALE_DISPLAY (s->display); ss = malloc (sizeof (ScaleScreen)); if (!ss) return FALSE; ss->windowPrivateIndex = allocateWindowPrivateIndex (s); if (ss->windowPrivateIndex < 0) { free (ss); return FALSE; } ss->grab = FALSE; // ss->grabIndex = 0; ss->hoverHandle = 0; ss->dndTarget = None; ss->state = SCALE_STATE_NONE; ss->slots = 0; ss->slotsSize = 0; ss->windows = 0; ss->windowsSize = 0; ss->spacing = SCALE_SPACING_DEFAULT; ss->speed = SCALE_SPEED_DEFAULT; ss->timestep = SCALE_TIMESTEP_DEFAULT; ss->opacity = (OPAQUE * SCALE_OPACITY_DEFAULT) / 100; ss->darkenBack = SCALE_DARKEN_BACK_DEFAULT; ss->iconOverlay = ScaleIconEmblem; scaleScreenInitOptions (ss); matchInit (&ss->match); matchUpdate (s->display, &ss->opt[SCALE_SCREEN_OPTION_WINDOW_MATCH].value.match); addScreenAction (s, &sd->opt[SCALE_DISPLAY_OPTION_INITIATE].value.action); addScreenAction (s, &sd->opt[SCALE_DISPLAY_OPTION_INITIATE_ALL].value.action); addScreenAction (s, &sd->opt[SCALE_DISPLAY_OPTION_INITIATE_GROUP].value.action); addScreenAction (s, &sd->opt[SCALE_DISPLAY_OPTION_INITIATE_OUTPUT].value.action); WRAP (ss, s, preparePaintScreen, scalePreparePaintScreen); WRAP (ss, s, donePaintScreen, scaleDonePaintScreen); WRAP (ss, s, paintScreen, scalePaintScreen); WRAP (ss, s, paintWindow, scalePaintWindow); WRAP (ss, s, damageWindowRect, scaleDamageWindowRect); ss->cursor = XCreateFontCursor (s->display->display, XC_left_ptr); s->privates[sd->screenPrivateIndex].ptr = ss; return TRUE; } static void scaleFiniScreen (CompPlugin *p, CompScreen *s) { SCALE_SCREEN (s); SCALE_DISPLAY (s->display); matchFini (&ss->match); matchFini (&ss->opt[SCALE_SCREEN_OPTION_WINDOW_MATCH].value.match); removeScreenAction (s, &sd->opt[SCALE_DISPLAY_OPTION_INITIATE].value.action); removeScreenAction (s, &sd->opt[SCALE_DISPLAY_OPTION_INITIATE_ALL].value.action); removeScreenAction (s, &sd->opt[SCALE_DISPLAY_OPTION_INITIATE_GROUP].value.action); removeScreenAction (s, &sd->opt[SCALE_DISPLAY_OPTION_INITIATE_OUTPUT].value.action); UNWRAP (ss, s, preparePaintScreen); UNWRAP (ss, s, donePaintScreen); UNWRAP (ss, s, paintScreen); UNWRAP (ss, s, paintWindow); UNWRAP (ss, s, damageWindowRect); if (ss->cursor) XFreeCursor (s->display->display, ss->cursor); if (ss->slotsSize) free (ss->slots); if (ss->windowsSize) free (ss->windows); free (ss); } static Bool scaleInitWindow (CompPlugin *p, CompWindow *w) { ScaleWindow *sw; SCALE_SCREEN (w->screen); sw = malloc (sizeof (ScaleWindow)); if (!sw) return FALSE; sw->slot = 0; sw->scale = 1.0f; sw->tx = sw->ty = 0.0f; sw->adjust = FALSE; sw->xVelocity = sw->yVelocity = 0.0f; sw->scaleVelocity = 1.0f; sw->delta = 1.0f; sw->lastThumbOpacity = 0.0f; w->privates[ss->windowPrivateIndex].ptr = sw; return TRUE; } static void scaleFiniWindow (CompPlugin *p, CompWindow *w) { SCALE_WINDOW (w); free (sw); } static Bool scaleInit (CompPlugin *p) { displayPrivateIndex = allocateDisplayPrivateIndex (); if (displayPrivateIndex < 0) return FALSE; return TRUE; } static void scaleFini (CompPlugin *p) { if (displayPrivateIndex >= 0) freeDisplayPrivateIndex (displayPrivateIndex); } static int scaleGetVersion (CompPlugin *plugin, int version) { return ABIVERSION; } CompPluginVTable scaleVTable = { "scale", N_("Scale"), N_("Scale windows"), scaleGetVersion, 0, /* GetMetadata */ scaleInit, scaleFini, scaleInitDisplay, scaleFiniDisplay, scaleInitScreen, scaleFiniScreen, scaleInitWindow, scaleFiniWindow, scaleGetDisplayOptions, scaleSetDisplayOption, scaleGetScreenOptions, scaleSetScreenOption, 0, /* Deps */ 0, /* nDeps */ 0, /* Features */ 0 /* nFeatures */ }; CompPluginVTable * getCompPluginInfo (void) { return &scaleVTable; }