/* * * Compiz shift switcher plugin * * shift.c * * Copyright : (C) 2007 by Dennis Kasprzyk * E-mail : onestone@opencompositing.org * * * Based on ring.c: * Copyright : (C) 2007 by Danny Baumann * E-mail : maniac@opencompositing.org * * Based on scale.c and switcher.c: * Copyright : (C) 2007 David Reveman * E-mail : davidr@novell.com * * Rounded corner drawing taken from wall.c: * Copyright : (C) 2007 Robert Carr * E-mail : racarr@beryl-project.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include #include #include #include #include #include #include #include #include #include "shift_options.h" typedef enum { ShiftStateNone = 0, ShiftStateOut, ShiftStateSwitching, ShiftStateIn } ShiftState; typedef enum { ShiftTypeNormal = 0, ShiftTypeGroup, ShiftTypeAll } ShiftType; static int displayPrivateIndex; typedef struct _ShiftSlot { int x, y; /* thumb center coordinates */ float z; float scale; /* size scale (fit to maximal thumb size */ float opacity; float rotation; GLfloat tx; GLfloat ty; Bool primary; } ShiftSlot; typedef struct _ShiftDrawSlot { CompWindow *w; ShiftSlot *slot; float distance; } ShiftDrawSlot; typedef struct _ShiftDisplay { int screenPrivateIndex; HandleEventProc handleEvent; KeyCode leftKey; KeyCode rightKey; KeyCode upKey; KeyCode downKey; } ShiftDisplay; typedef struct _ShiftScreen { int windowPrivateIndex; PreparePaintScreenProc preparePaintScreen; DonePaintScreenProc donePaintScreen; PaintScreenProc paintScreen; PaintOutputProc paintOutput; PaintWindowProc paintWindow; DamageWindowRectProc damageWindowRect; int grabIndex; ShiftState state; ShiftType type; Bool moreAdjust; Bool moveAdjust; float mvTarget; float mvAdjust; GLfloat mvVelocity; Bool invert; Cursor cursor; /* only used for sorting */ CompWindow **windows; int windowsSize; int nWindows; ShiftDrawSlot *drawSlots; int slotsSize; int nSlots; ShiftDrawSlot *activeSlot; Window clientLeader; Window selectedWindow; /* text display support */ CompTexture textTexture; Pixmap textPixmap; int textWidth; int textHeight; CompMatch match; CompMatch *currentMatch; CompOutput *output; int usedOutput; float anim; float animVelocity; float reflectBrightness; Bool reflectActive; int buttonPressTime; Bool buttonPressed; int startX; int startY; float startTarget; float lastTitle; Bool paintingAbove; } ShiftScreen; typedef struct _ShiftWindow { ShiftSlot slots[2]; float opacity; float brightness; float opacityVelocity; float brightnessVelocity; Bool active; } ShiftWindow; #define PI 3.1415926 #define GET_SHIFT_DISPLAY(d) \ ((ShiftDisplay *) (d)->privates[displayPrivateIndex].ptr) #define SHIFT_DISPLAY(d) \ ShiftDisplay *sd = GET_SHIFT_DISPLAY (d) #define GET_SHIFT_SCREEN(s, sd) \ ((ShiftScreen *) (s)->privates[(sd)->screenPrivateIndex].ptr) #define SHIFT_SCREEN(s) \ ShiftScreen *ss = GET_SHIFT_SCREEN (s, GET_SHIFT_DISPLAY (s->display)) #define GET_SHIFT_WINDOW(w, ss) \ ((ShiftWindow *) (w)->privates[(ss)->windowPrivateIndex].ptr) #define SHIFT_WINDOW(w) \ ShiftWindow *sw = GET_SHIFT_WINDOW (w, \ GET_SHIFT_SCREEN (w->screen, \ GET_SHIFT_DISPLAY (w->screen->display))) static void shiftActivateEvent (CompScreen *s, Bool activating) { CompOption o[2]; o[0].type = CompOptionTypeInt; o[0].name = "root"; o[0].value.i = s->root; o[1].type = CompOptionTypeBool; o[1].name = "active"; o[1].value.b = activating; (*s->display->handleCompizEvent) (s->display, "shift", "activate", o, 2); } static Bool isShiftWin (CompWindow *w) { SHIFT_SCREEN (w->screen); if (w->attrib.override_redirect) return FALSE; if (w->wmType & (CompWindowTypeDockMask | CompWindowTypeDesktopMask)) return FALSE; if (!w->mapNum || w->attrib.map_state != IsViewable) { if (shiftGetMinimized (w->screen)) { if (!w->minimized && !w->inShowDesktopMode && !w->shaded) return FALSE; } else return FALSE; } if (ss->type == ShiftTypeNormal) { if (!w->mapNum || w->attrib.map_state != IsViewable) { if (w->serverX + w->width <= 0 || w->serverY + w->height <= 0 || w->serverX >= w->screen->width || w->serverY >= w->screen->height) return FALSE; } else { if (!(*w->screen->focusWindow) (w)) return FALSE; } } else if (ss->type == ShiftTypeGroup && ss->clientLeader != w->clientLeader && ss->clientLeader != w->id) { return FALSE; } if (w->state & CompWindowStateSkipTaskbarMask) return FALSE; if (w->state & CompWindowStateShadedMask) return FALSE; if (!matchEval (ss->currentMatch, w)) return FALSE; return TRUE; } static void shiftFreeWindowTitle (CompScreen *s) { SHIFT_SCREEN(s); if (!ss->textPixmap) return; releasePixmapFromTexture (s, &ss->textTexture); initTexture (s, &ss->textTexture); XFreePixmap (s->display->display, ss->textPixmap); ss->textPixmap = None; } static void shiftRenderWindowTitle (CompScreen *s) { CompTextAttrib tA; int stride; void *data; SHIFT_SCREEN (s); shiftFreeWindowTitle (s); if (!shiftGetWindowTitle (s)) return; int ox1, ox2, oy1, oy2; if (shiftGetMultioutputMode (s) == MultioutputModeOneBigSwitcher) { ox1 = oy1 = 0; ox2 = s->width; oy2 = s->height; } else getCurrentOutputExtents (s, &ox1, &oy1, &ox2, &oy2); /* 75% of the output device as maximum width */ tA.maxwidth = (ox2 - ox1) * 3 / 4; tA.maxheight = 100; tA.screen = s; tA.size = shiftGetTitleFontSize (s); tA.color[0] = shiftGetTitleFontColorRed (s); tA.color[1] = shiftGetTitleFontColorGreen (s); tA.color[2] = shiftGetTitleFontColorBlue (s); tA.color[3] = shiftGetTitleFontColorAlpha (s); tA.style = (shiftGetTitleFontBold (s)) ? TEXT_STYLE_BOLD : TEXT_STYLE_NORMAL; tA.family = "Sans"; tA.ellipsize = TRUE; tA.renderMode = TextRenderWindowTitle; tA.data = (void*)ss->selectedWindow; initTexture (s, &ss->textTexture); if ((*s->display->fileToImage) (s->display, TEXT_ID, (char *)&tA, &ss->textWidth, &ss->textHeight, &stride, &data)) { ss->textPixmap = (Pixmap)data; bindPixmapToTexture (s, &ss->textTexture, ss->textPixmap, ss->textWidth, ss->textHeight, 32); } else { ss->textPixmap = None; ss->textWidth = 0; ss->textHeight = 0; } } static void shiftDrawWindowTitle (CompScreen *s) { SHIFT_SCREEN(s); GLboolean wasBlend; GLint oldBlendSrc, oldBlendDst; float width = ss->textWidth; float height = ss->textHeight; float border = 10.0f; int ox1, ox2, oy1, oy2; if (shiftGetMultioutputMode (s) == MultioutputModeOneBigSwitcher) { ox1 = oy1 = 0; ox2 = s->width; oy2 = s->height; } else { ox1 = s->outputDev[ss->usedOutput].region.extents.x1; ox2 = s->outputDev[ss->usedOutput].region.extents.x2; oy1 = s->outputDev[ss->usedOutput].region.extents.y1; oy2 = s->outputDev[ss->usedOutput].region.extents.y2; } float x = ox1 + ((ox2 - ox1) / 2) - (ss->textWidth / 2); float y; /* assign y (for the lower corner!) according to the setting */ switch (shiftGetTitleTextPlacement (s)) { case TitleTextPlacementCenteredOnScreen: y = oy1 + ((oy2 - oy1) / 2) + (height / 2); break; case TitleTextPlacementAbove: case TitleTextPlacementBelow: { XRectangle workArea; getWorkareaForOutput (s, s->currentOutputDev, &workArea); if (shiftGetTitleTextPlacement (s) == TitleTextPlacementAbove) y = oy1 + workArea.y + (2 * border) + height; else y = oy1 + workArea.y + workArea.height - (2 * border); } break; default: return; } x = floor (x); y = floor (y); glGetIntegerv (GL_BLEND_SRC, &oldBlendSrc); glGetIntegerv (GL_BLEND_DST, &oldBlendDst); wasBlend = glIsEnabled (GL_BLEND); if (!wasBlend) glEnable (GL_BLEND); glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA); glColor4us (shiftGetTitleBackColorRed (s), shiftGetTitleBackColorGreen (s), shiftGetTitleBackColorBlue (s), shiftGetTitleBackColorAlpha (s)); glPushMatrix (); glTranslatef (x, y - height, 0.0f); glRectf (0.0f, height, width, 0.0f); glRectf (0.0f, 0.0f, width, -border); glRectf (0.0f, height + border, width, height); glRectf (-border, height, 0.0f, 0.0f); glRectf (width, height, width + border, 0.0f); glTranslatef (-border, -border, 0.0f); #define CORNER(a,b) \ for (k = a; k < b; k++) \ {\ float rad = k * (PI / 180.0f);\ glVertex2f (0.0f, 0.0f);\ glVertex2f (cos(rad) * border, sin(rad) * border);\ glVertex2f (cos((k-1) * (PI / 180.0f)) * border, \ sin((k-1) * (PI / 180.0f)) * border);\ } /* Rounded corners */ int k; glTranslatef (border, border, 0.0f); glBegin (GL_TRIANGLES); CORNER (180, 270) glEnd (); glTranslatef (-border, -border, 0.0f); glTranslatef (width + border, border, 0.0f); glBegin (GL_TRIANGLES); CORNER (270, 360) glEnd (); glTranslatef (-(width + border), -border, 0.0f); glTranslatef (border, height + border, 0.0f); glBegin (GL_TRIANGLES); CORNER (90, 180) glEnd (); glTranslatef (-border, -(height + border), 0.0f); glTranslatef (width + border, height + border, 0.0f); glBegin (GL_TRIANGLES); CORNER (0, 90) glEnd (); glTranslatef (-(width + border), -(height + border), 0.0f); glPopMatrix (); #undef CORNER glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glColor4f (1.0, 1.0, 1.0, 1.0); enableTexture (s, &ss->textTexture,COMP_TEXTURE_FILTER_GOOD); CompMatrix *m = &ss->textTexture.matrix; glBegin (GL_QUADS); glTexCoord2f (COMP_TEX_COORD_X (m, 0), COMP_TEX_COORD_Y (m ,0)); glVertex2f (x, y - height); glTexCoord2f (COMP_TEX_COORD_X (m, 0), COMP_TEX_COORD_Y (m, height)); glVertex2f (x, y); glTexCoord2f (COMP_TEX_COORD_X (m, width), COMP_TEX_COORD_Y (m, height)); glVertex2f (x + width, y); glTexCoord2f (COMP_TEX_COORD_X (m, width), COMP_TEX_COORD_Y (m, 0)); glVertex2f (x + width, y - height); glEnd (); disableTexture (s, &ss->textTexture); glColor4usv (defaultColor); if (!wasBlend) glDisable (GL_BLEND); glBlendFunc (oldBlendSrc, oldBlendDst); } static Bool shiftPaintWindow (CompWindow *w, const WindowPaintAttrib *attrib, const CompTransform *transform, Region region, unsigned int mask) { CompScreen *s = w->screen; Bool status; SHIFT_SCREEN (s); SHIFT_WINDOW (w); if (ss->state != ShiftStateNone && !ss->paintingAbove) { WindowPaintAttrib sAttrib = *attrib; Bool scaled = FALSE; if (w->mapNum) { if (!w->texture->pixmap && !w->bindFailed) bindWindow (w); } if (sw->active) scaled = (ss->activeSlot != NULL); if (sw->opacity > 0.0 && (ss->activeSlot == NULL)) { sAttrib.brightness = sAttrib.brightness * sw->brightness; sAttrib.opacity = sAttrib.opacity * sw->opacity; } else mask |= PAINT_WINDOW_NO_CORE_INSTANCE_MASK; if (sw->active && (ss->output->id == ss->usedOutput || ss->output->id == ~0)) mask |= PAINT_WINDOW_NO_CORE_INSTANCE_MASK; UNWRAP (ss, s, paintWindow); status = (*s->paintWindow) (w, &sAttrib, transform, region, mask); WRAP (ss, s, paintWindow, shiftPaintWindow); if (scaled && w->texture->pixmap) { FragmentAttrib fragment; CompTransform wTransform = *transform; ShiftSlot *slot = ss->activeSlot->slot; float sx = ss->anim * slot->tx; float sy = ss->anim * slot->ty; float sz = ss->anim * slot->z; float sscale = (ss->anim * slot->scale) + (1 - ss->anim); float srot = (ss->anim * slot->rotation); float sopacity; if (slot->primary && !ss->reflectActive) sopacity = (ss->anim * slot->opacity) + (1 - ss->anim); else sopacity = ss->anim * slot->opacity; if (sopacity <= 0.0) return status; if (mask & PAINT_WINDOW_OCCLUSION_DETECTION_MASK) return FALSE; initFragmentAttrib (&fragment, attrib); fragment.opacity = (float)fragment.opacity * sopacity; fragment.brightness = (float)fragment.brightness * ss->reflectBrightness; if (w->alpha || fragment.opacity != OPAQUE) mask |= PAINT_WINDOW_TRANSLUCENT_MASK; matrixTranslate (&wTransform, sx, sy, sz); matrixTranslate (&wTransform, w->attrib.x + (w->width * sscale / 2), w->attrib.y + (w->height * sscale / 2.0), 0.0f); matrixScale (&wTransform, ss->output->width, -ss->output->height, 1.0f); matrixRotate (&wTransform, srot, 0.0, 1.0, 0.0); matrixScale (&wTransform, 1.0f / ss->output->width, -1.0f / ss->output->height, 1.0f); matrixScale (&wTransform, sscale, sscale, 1.0f); matrixTranslate (&wTransform, -w->attrib.x - (w->width / 2), -w->attrib.y - (w->height / 2), 0.0f); glPushMatrix (); glLoadMatrixf (wTransform.m); (*s->drawWindow) (w, &wTransform, &fragment, region, mask | PAINT_WINDOW_TRANSFORMED_MASK); glPopMatrix (); } if (scaled && (ss->state != ShiftStateIn) && ((shiftGetOverlayIcon (s) != OverlayIconNone) || !w->texture->pixmap)) { 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; ShiftOverlayIconEnum iconOverlay = shiftGetOverlayIcon (s); ShiftSlot *slot = ss->activeSlot->slot; float sx = ss->anim * slot->tx; float sy = ss->anim * slot->ty; float sz = ss->anim * slot->z; float sscale = (ss->anim * slot->scale) + (1 - ss->anim); float srot = (ss->anim * slot->rotation); float sopacity = ss->anim * slot->opacity; scaledWinWidth = w->width * sscale; scaledWinHeight = w->height * sscale; if (!w->texture->pixmap) iconOverlay = OverlayIconBig; switch (iconOverlay) { case OverlayIconNone: case OverlayIconEmblem: scale = 1.0f; break; case OverlayIconBig: default: /* only change opacity if not painting an icon for a minimized window */ if (w->texture->pixmap) sAttrib.opacity /= 3; scale = MIN (((float) scaledWinWidth / icon->width), ((float) scaledWinHeight / icon->height)); break; } width = icon->width * scale; height = icon->height * scale; switch (iconOverlay) { case OverlayIconNone: case OverlayIconEmblem: x = scaledWinWidth - width; y = scaledWinHeight - height; break; case OverlayIconBig: default: x = scaledWinWidth / 2 - width / 2; y = scaledWinHeight / 2 - height / 2; break; } mask |= PAINT_WINDOW_BLEND_MASK; /* if we paint the icon for a minimized window, we need to force the usage of a good texture filter */ if (!w->texture->pixmap) mask |= PAINT_WINDOW_TRANSFORMED_MASK; iconReg.rects = &iconReg.extents; iconReg.numRects = 1; iconReg.extents.x1 = 0; iconReg.extents.y1 = 0; iconReg.extents.x2 = icon->width; iconReg.extents.y2 = icon->height; w->vCount = w->indexCount = 0; (*w->screen->addWindowGeometry) (w, &icon->texture.matrix, 1, &iconReg, &infiniteRegion); if (w->vCount) { FragmentAttrib fragment; CompTransform wTransform = *transform; if (!w->texture->pixmap) { /* the fade plugin does weird things to w->paint.opacity, so better use the atom value */ sAttrib.opacity = w->opacity; } initFragmentAttrib (&fragment, attrib); fragment.opacity = (float)fragment.opacity * sopacity; fragment.brightness = (float)fragment.brightness * ss->reflectBrightness; matrixTranslate (&wTransform, sx, sy, sz); matrixTranslate (&wTransform, w->attrib.x + (w->width * sscale / 2), w->attrib.y + (w->height * sscale / 2.0), 0.0f); matrixScale (&wTransform, ss->output->width, -ss->output->height, 1.0f); matrixRotate (&wTransform, srot, 0.0, 1.0, 0.0); matrixScale (&wTransform, 1.0f / ss->output->width, -1.0f / ss->output->height, 1.0f); matrixTranslate (&wTransform, x - (w->width * sscale / 2), y - (w->height * sscale / 2.0), 0.0f); matrixScale (&wTransform, scale, scale, 1.0f); glPushMatrix (); glLoadMatrixf (wTransform.m); (*w->screen->drawWindowTexture) (w, &icon->texture, &fragment, mask); glPopMatrix (); } } } } else { WindowPaintAttrib sAttrib = *attrib; if (ss->paintingAbove) { sAttrib.opacity = sAttrib.opacity * (1.0 - ss->anim); } UNWRAP (ss, s, paintWindow); status = (*s->paintWindow) (w, &sAttrib, transform, region, mask); WRAP (ss, s, paintWindow, shiftPaintWindow); } return status; } static int compareWindows (const void *elem1, const void *elem2) { CompWindow *w1 = *((CompWindow **) elem1); CompWindow *w2 = *((CompWindow **) elem2); CompWindow *w = w1; if (w1 == w2) return 0; if (!w1->shaded && w1->attrib.map_state != IsViewable && (w2->shaded || w2->attrib.map_state == IsViewable)) { return 1; } if (!w2->shaded && w2->attrib.map_state != IsViewable && (w1->shaded || w1->attrib.map_state == IsViewable)) { return -1; } while (w) { if (w == w2) return 1; w = w->next; } return -1; /* if (w1->mapNum && !w2->mapNum) return -1; if (w2->mapNum && !w1->mapNum) return 1; return w2->activeNum - w1->activeNum; */ } static int compareShiftWindowDistance (const void *elem1, const void *elem2) { float a1 = ((ShiftDrawSlot *) elem1)->distance; float a2 = ((ShiftDrawSlot *) elem2)->distance; if (a1 > a2) return -1; else if (a1 < a2) return 1; else return 0; } static Bool layoutThumbsCover (CompScreen *s) { SHIFT_SCREEN (s); CompWindow *w; int index; int ww, wh; float xScale, yScale; float distance; int i; int ox1, ox2, oy1, oy2; if (shiftGetMultioutputMode (s) == MultioutputModeOneBigSwitcher) { ox1 = oy1 = 0; ox2 = s->width; oy2 = s->height; } else { ox1 = s->outputDev[ss->usedOutput].region.extents.x1; ox2 = s->outputDev[ss->usedOutput].region.extents.x2; oy1 = s->outputDev[ss->usedOutput].region.extents.y1; oy2 = s->outputDev[ss->usedOutput].region.extents.y2; } /* the center of the ellipse is in the middle of the used output device */ int centerX = ox1 + (ox2 - ox1) / 2; int centerY = oy1 + (oy2 - oy1) / 2; int maxThumbWidth = (ox2 - ox1) * shiftGetSize(s) / 100; int maxThumbHeight = (oy2 - oy1) * shiftGetSize(s) / 100; for (index = 0; index < ss->nWindows; index++) { w = ss->windows[index]; SHIFT_WINDOW (w); ww = w->width + w->input.left + w->input.right; wh = w->height + w->input.top + w->input.bottom; if (ww > maxThumbWidth) xScale = (float)(maxThumbWidth) / (float)ww; else xScale = 1.0f; if (wh > maxThumbHeight) yScale = (float)(maxThumbHeight) / (float)wh; else yScale = 1.0f; float val1 = floor((float)ss->nWindows / 2.0); float pos; float space = maxThumbWidth / 2; space *= cos (sin (PI / 4) * PI / 3); space *= 2; //space += (space / sin (PI / 4)) - space; for (i = 0; i < 2; i++) { if (ss->invert ^ (i == 0)) { distance = ss->mvTarget - index; distance += shiftGetCoverOffset (s); } else { distance = ss->mvTarget - index + ss->nWindows; distance += shiftGetCoverOffset (s); if (distance > ss->nWindows) distance -= ss->nWindows * 2; } pos = MIN (1.0, MAX (-1.0, distance)); sw->slots[i].opacity = 1.0 - MIN (1.0, MAX (0.0, fabs(distance) - val1)); sw->slots[i].scale = MIN (xScale, yScale); sw->slots[i].y = centerY + (maxThumbHeight / 2.0) - (((w->height / 2.0) + w->input.bottom) * sw->slots[i].scale); if (fabs(distance) < 1.0) { sw->slots[i].x = centerX + (sin(pos * PI * 0.5) * space); sw->slots[i].z = fabs (distance); sw->slots[i].z *= -(maxThumbWidth / (2.0 * (ox2 - ox1))); sw->slots[i].rotation = sin(pos * PI * 0.5) * -60; } else { float rad = (space / (ox2 - ox1)) / sin(PI / 6.0); float ang = (PI / MAX(72.0, ss->nWindows * 2)) * (distance - pos) + (pos * (PI / 6.0)); sw->slots[i].x = centerX; sw->slots[i].x += sin(ang) * rad * (ox2 - ox1); sw->slots[i].rotation = 90; sw->slots[i].rotation -= fabs(ang) * 180.0 / PI; sw->slots[i].rotation *= -pos; sw->slots[i].z = -(maxThumbWidth / (2.0 * (ox2 - ox1))); sw->slots[i].z += -(cos(PI / 6.0) * rad); sw->slots[i].z += (cos(ang) * rad); } ss->drawSlots[index * 2 + i].w = w; ss->drawSlots[index * 2 + i].slot = &sw->slots[i]; ss->drawSlots[index * 2 + i].distance = fabs(distance); } if (ss->drawSlots[index * 2].distance > ss->drawSlots[index * 2 + 1].distance) { ss->drawSlots[index * 2].slot->primary = FALSE; ss->drawSlots[index * 2 + 1].slot->primary = TRUE; } else { ss->drawSlots[index * 2].slot->primary = TRUE; ss->drawSlots[index * 2 + 1].slot->primary = FALSE; } } ss->nSlots = ss->nWindows * 2; qsort (ss->drawSlots, ss->nSlots, sizeof (ShiftDrawSlot), compareShiftWindowDistance); return TRUE; } static Bool layoutThumbsFlip (CompScreen *s) { SHIFT_SCREEN (s); CompWindow *w; int index; int ww, wh; float xScale, yScale; float distance; int i; float angle; int slotNum; int ox1, ox2, oy1, oy2; if (shiftGetMultioutputMode (s) == MultioutputModeOneBigSwitcher) { ox1 = oy1 = 0; ox2 = s->width; oy2 = s->height; } else { ox1 = s->outputDev[ss->usedOutput].region.extents.x1; ox2 = s->outputDev[ss->usedOutput].region.extents.x2; oy1 = s->outputDev[ss->usedOutput].region.extents.y1; oy2 = s->outputDev[ss->usedOutput].region.extents.y2; } /* the center of the ellipse is in the middle of the used output device */ int centerX = ox1 + (ox2 - ox1) / 2; int centerY = oy1 + (oy2 - oy1) / 2; int maxThumbWidth = (ox2 - ox1) * shiftGetSize(s) / 100; int maxThumbHeight = (oy2 - oy1) * shiftGetSize(s) / 100; slotNum = 0; for (index = 0; index < ss->nWindows; index++) { w = ss->windows[index]; SHIFT_WINDOW (w); ww = w->width + w->input.left + w->input.right; wh = w->height + w->input.top + w->input.bottom; if (ww > maxThumbWidth) xScale = (float)(maxThumbWidth) / (float)ww; else xScale = 1.0f; if (wh > maxThumbHeight) yScale = (float)(maxThumbHeight) / (float)wh; else yScale = 1.0f; angle = shiftGetFlipRotation (s) * PI / 180.0; for (i = 0; i < 2; i++) { if (ss->invert ^ (i == 0)) distance = ss->mvTarget - index; else { distance = ss->mvTarget - index + ss->nWindows; if (distance > 1.0) distance -= ss->nWindows * 2; } if (distance > 0.0) sw->slots[i].opacity = MAX (0.0, 1.0 - (distance * 1.0)); else { if (distance < -(ss->nWindows - 1)) sw->slots[i].opacity = MAX (0.0, ss->nWindows + distance); else sw->slots[i].opacity = 1.0; } if (distance > 0.0 && w->id != ss->selectedWindow) sw->slots[i].primary = FALSE; else sw->slots[i].primary = TRUE; sw->slots[i].scale = MIN (xScale, yScale); sw->slots[i].y = centerY + (maxThumbHeight / 2.0) - (((w->height / 2.0) + w->input.bottom) * sw->slots[i].scale); sw->slots[i].x = sin(angle) * distance * (maxThumbWidth / 2); if (distance > 0 && FALSE) sw->slots[i].x *= 1.5; sw->slots[i].x += centerX; sw->slots[i].z = cos(angle) * distance; if (distance > 0) sw->slots[i].z *= 1.5; sw->slots[i].z *= (maxThumbWidth / (2.0 * (ox2 - ox1))); sw->slots[i].rotation = shiftGetFlipRotation (s); if (sw->slots[i].opacity > 0.0) { ss->drawSlots[slotNum].w = w; ss->drawSlots[slotNum].slot = &sw->slots[i]; ss->drawSlots[slotNum].distance = -distance; slotNum++; } } } ss->nSlots = slotNum; qsort (ss->drawSlots, ss->nSlots, sizeof (ShiftDrawSlot), compareShiftWindowDistance); return TRUE; } static Bool layoutThumbs (CompScreen *s) { SHIFT_SCREEN (s); if ((ss->state == ShiftStateNone) || (ss->state == ShiftStateIn)) return FALSE; switch (shiftGetMode (s)) { case ModeCover: return layoutThumbsCover (s); case ModeFlip: return layoutThumbsFlip (s); } return FALSE; } static void shiftAddWindowToList (CompScreen *s, CompWindow *w) { SHIFT_SCREEN (s); if (ss->windowsSize <= ss->nWindows) { ss->windows = realloc (ss->windows, sizeof (CompWindow *) * (ss->nWindows + 32)); if (!ss->windows) return; ss->windowsSize = ss->nWindows + 32; } if (ss->slotsSize <= ss->nWindows * 2) { ss->drawSlots = realloc (ss->drawSlots, sizeof (ShiftDrawSlot) * ((ss->nWindows * 2) + 64)); if (!ss->drawSlots) return; ss->slotsSize = (ss->nWindows * 2) + 64; } ss->windows[ss->nWindows++] = w; } static Bool shiftUpdateWindowList (CompScreen *s) { int i; SHIFT_SCREEN (s); qsort (ss->windows, ss->nWindows, sizeof (CompWindow *), compareWindows); ss->mvTarget = 0; ss->mvAdjust = 0; ss->mvVelocity = 0; for (i = 0; i < ss->nWindows; i++) { if (ss->windows[i]->id == ss->selectedWindow) break; ss->mvTarget++; } return layoutThumbs (s); } static Bool shiftCreateWindowList (CompScreen *s) { CompWindow *w; SHIFT_SCREEN (s); ss->nWindows = 0; for (w = s->windows; w; w = w->next) { if (isShiftWin (w)) { SHIFT_WINDOW (w); shiftAddWindowToList (s, w); sw->active = TRUE; } } return shiftUpdateWindowList (s); } static void switchToWindow (CompScreen *s, Bool toNext) { CompWindow *w; int cur; SHIFT_SCREEN (s); if (!ss->grabIndex) return; for (cur = 0; cur < ss->nWindows; cur++) { if (ss->windows[cur]->id == ss->selectedWindow) break; } if (cur == ss->nWindows) return; if (toNext) w = ss->windows[(cur + 1) % ss->nWindows]; else w = ss->windows[(cur + ss->nWindows - 1) % ss->nWindows]; if (w) { Window old = ss->selectedWindow; ss->selectedWindow = w->id; if (old != w->id) { if (toNext) ss->mvAdjust += 1; else ss->mvAdjust -= 1; ss->moveAdjust = TRUE; damageScreen (s); shiftRenderWindowTitle (s); } } } static int shiftCountWindows (CompScreen *s) { CompWindow *w; int count = 0; for (w = s->windows; w; w = w->next) { if (isShiftWin (w)) count++; } return count; } static int adjustShiftMovement (CompScreen *s, float chunk) { float dx, adjust, amount; float change; SHIFT_SCREEN(s); dx = ss->mvAdjust; adjust = dx * 0.15f; amount = fabs(dx) * 1.5f; if (amount < 0.2f) amount = 0.2f; else if (amount > 2.0f) amount = 2.0f; ss->mvVelocity = (amount * ss->mvVelocity + adjust) / (amount + 1.0f); if (fabs (dx) < 0.002f && fabs (ss->mvVelocity) < 0.004f) { ss->mvVelocity = 0.0f; ss->mvTarget = ss->mvTarget + ss->mvAdjust; ss->mvAdjust = 0; layoutThumbs (s); return FALSE; } change = ss->mvVelocity * chunk; if (!change) { if (ss->mvVelocity) change = (ss->mvAdjust > 0) ? 0.01 : -0.01; } ss->mvAdjust -= change; ss->mvTarget += change; while (ss->mvTarget >= ss->nWindows) { ss->mvTarget -= ss->nWindows; ss->invert = !ss->invert; } while (ss->mvTarget < 0) { ss->mvTarget += ss->nWindows; ss->invert = !ss->invert; } if (!layoutThumbs (s)) return FALSE; return TRUE; } static Bool adjustShiftWindowAttribs (CompWindow *w, float chunk) { float dp, db, adjust, amount; float opacity, brightness; SHIFT_WINDOW (w); SHIFT_SCREEN (w->screen); if ((sw->active && ss->state != ShiftStateIn && ss->state != ShiftStateNone) || (shiftGetHideAll(w->screen) && !(w->type & CompWindowTypeDesktopMask) && (ss->state == ShiftStateOut || ss->state == ShiftStateSwitching))) opacity = 0.0; else opacity = 1.0; if (ss->state == ShiftStateIn || ss->state == ShiftStateNone) brightness = 1.0; else brightness = shiftGetBackgroundIntensity (w->screen); dp = opacity - sw->opacity; adjust = dp * 0.1f; amount = fabs (dp) * 7.0f; if (amount < 0.01f) amount = 0.01f; else if (amount > 0.15f) amount = 0.15f; sw->opacityVelocity = (amount * sw->opacityVelocity + adjust) / (amount + 1.0f); db = brightness - sw->brightness; adjust = db * 0.1f; amount = fabs (db) * 7.0f; if (amount < 0.01f) amount = 0.01f; else if (amount > 0.15f) amount = 0.15f; sw->brightnessVelocity = (amount * sw->brightnessVelocity + adjust) / (amount + 1.0f); if (fabs (dp) < 0.01f && fabs (sw->opacityVelocity) < 0.02f && fabs (db) < 0.01f && fabs (sw->brightnessVelocity) < 0.02f) { sw->brightness = brightness; sw->opacity = opacity; return FALSE; } sw->brightness += sw->brightnessVelocity * chunk; sw->opacity += sw->opacityVelocity * chunk; return TRUE; } static Bool adjustShiftAnimationAttribs (CompScreen *s, float chunk) { float dr, adjust, amount; float anim; SHIFT_SCREEN (s); if (ss->state != ShiftStateIn && ss->state != ShiftStateNone) anim = 1.0; else anim = 0.0; dr = anim - ss->anim; adjust = dr * 0.1f; amount = fabs (dr) * 7.0f; if (amount < 0.002f) amount = 0.002f; else if (amount > 0.15f) amount = 0.15f; ss->animVelocity = (amount * ss->animVelocity + adjust) / (amount + 1.0f); if (fabs (dr) < 0.002f && fabs (ss->animVelocity) < 0.004f) { ss->anim = anim; return FALSE; } ss->anim += ss->animVelocity * chunk; return TRUE; } static Bool shiftPaintOutput (CompScreen *s, const ScreenPaintAttrib *sAttrib, const CompTransform *transform, Region region, CompOutput *output, unsigned int mask) { Bool status; SHIFT_SCREEN (s); if (ss->state != ShiftStateNone) mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK; ss->paintingAbove = FALSE; ss->output = output; UNWRAP (ss, s, paintOutput); status = (*s->paintOutput) (s, sAttrib, transform, region, output, mask); WRAP (ss, s, paintOutput, shiftPaintOutput); if (ss->state != ShiftStateNone && (output->id == ss->usedOutput || output->id == ~0)) { int i; CompWindow *w; CompTransform sTransform = *transform; int oy1 = s->outputDev[ss->usedOutput].region.extents.y1; int oy2 = s->outputDev[ss->usedOutput].region.extents.y2; if (shiftGetMultioutputMode (s) == MultioutputModeOneBigSwitcher) { oy1 = 0; oy2 = s->height; } int maxThumbHeight = (oy2 - oy1) * shiftGetSize(s) / 100; int oldFilter = s->display->textureFilter; transformToScreenSpace (s, output, -DEFAULT_Z_CAMERA, &sTransform); GLdouble clip[4] = { 0.0, -1.0, 0.0, 0.0}; clip[3] = ((oy1 + (oy2 - oy1)) / 2) + (maxThumbHeight / 2.0); if (shiftGetReflection (s)) { CompTransform rTransform = sTransform; unsigned short color[4]; int maxThumbHeight = (oy2 - oy1) * shiftGetSize(s) / 100; matrixTranslate (&rTransform, 0.0, (oy2 - oy1) + maxThumbHeight, 0.0); matrixScale (&rTransform, 1.0, -1.0, 1.0); glPushMatrix (); glLoadMatrixf (rTransform.m); glDisable (GL_CULL_FACE); if (shiftGetMipmaps (s)) s->display->textureFilter = GL_LINEAR_MIPMAP_LINEAR; if (ss->anim == 1.0) { glClipPlane (GL_CLIP_PLANE0, clip); glEnable (GL_CLIP_PLANE0); } ss->reflectActive = TRUE; ss->reflectBrightness = shiftGetIntensity(s); for (i = 0; i < ss->nSlots; i++) { w = ss->drawSlots[i].w; ss->activeSlot = &ss->drawSlots[i]; { (*s->paintWindow) (w, &w->paint, &rTransform, &infiniteRegion, 0); } } glDisable (GL_CLIP_PLANE0); glEnable( GL_CULL_FACE); glLoadIdentity(); glTranslatef (0.0, 0.0, -DEFAULT_Z_CAMERA); glEnable(GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBegin (GL_QUADS); glColor4f (0.0, 0.0, 0.0, 0.0); glVertex2f (0.5, 0.0); glVertex2f (-0.5, 0.0); glColor4f (0.0, 0.0, 0.0, MIN (1.0, 1.0 - shiftGetIntensity (s)) * 2.0 * ss->anim); glVertex2f (-0.5, -0.5); glVertex2f (0.5, -0.5); glEnd(); if (shiftGetGroundSize (s) > 0.0) { glBegin (GL_QUADS); color[0] = shiftGetGroundColor1 (s)[0]; color[1] = shiftGetGroundColor1 (s)[1]; color[2] = shiftGetGroundColor1 (s)[2]; color[3] = (float)shiftGetGroundColor1 (s)[3] * ss->anim; glColor4usv (color); glVertex2f (-0.5, -0.5); glVertex2f (0.5, -0.5); color[0] = shiftGetGroundColor2 (s)[0]; color[1] = shiftGetGroundColor2 (s)[1]; color[2] = shiftGetGroundColor2 (s)[2]; color[3] = (float)shiftGetGroundColor2 (s)[3] * ss->anim; glColor4usv (color); glVertex2f (0.5, -0.5 + shiftGetGroundSize (s)); glVertex2f (-0.5, -0.5 + shiftGetGroundSize (s)); glEnd(); } glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_BLEND); glColor4f (1.0, 1.0, 1.0, 1.0); glPopMatrix (); } glPushMatrix (); glLoadMatrixf (sTransform.m); if (shiftGetReflection (s) && ss->anim == 1.0) { glClipPlane (GL_CLIP_PLANE0, clip); glEnable (GL_CLIP_PLANE0); } ss->reflectBrightness = 1.0; ss->reflectActive = FALSE; for (i = 0; i < ss->nSlots; i++) { w = ss->drawSlots[i].w; ss->activeSlot = &ss->drawSlots[i]; { (*s->paintWindow) (w, &w->paint, &sTransform, &infiniteRegion, 0); } } glDisable (GL_CLIP_PLANE0); ss->activeSlot = NULL; s->display->textureFilter = oldFilter; if (ss->textPixmap && (ss->state != ShiftStateIn)) shiftDrawWindowTitle (s); if (ss->state == ShiftStateIn || ss->state == ShiftStateOut) { Bool found; ss->paintingAbove = TRUE; w = findWindowAtScreen (s, ss->selectedWindow); for (; w; w = w->next) { if (w->destroyed) continue; if (!w->shaded) { if (w->attrib.map_state != IsViewable || !w->damaged) continue; } found = FALSE; for (i = 0; i < ss->nWindows; i++) if (ss->windows[i] == w) found = TRUE; if (found) continue; (*s->paintWindow) (w, &w->paint, &sTransform, &infiniteRegion, 0); } ss->paintingAbove = FALSE; } glPopMatrix (); } return status; } static void shiftPaintScreen (CompScreen *s, CompOutput *outputs, int numOutputs, unsigned int mask) { SHIFT_SCREEN (s); if (ss->state != ShiftStateNone && numOutputs > 0 && shiftGetMultioutputMode (s) != MultioutputModeDisabled) { outputs = &s->fullscreenOutput; numOutputs = 1; } UNWRAP (ss, s, paintScreen); (*s->paintScreen) (s, outputs, numOutputs, mask); WRAP (ss, s, paintScreen, shiftPaintScreen); } static void shiftPreparePaintScreen (CompScreen *s, int msSinceLastPaint) { SHIFT_SCREEN (s); if (ss->state != ShiftStateNone && (ss->moreAdjust || ss->moveAdjust)) { CompWindow *w; int steps; float amount, chunk; int i; amount = msSinceLastPaint * 0.05f * shiftGetShiftSpeed (s); steps = amount / (0.5f * shiftGetTimestep (s)); if (!steps) steps = 1; chunk = amount / (float) steps; while (steps--) { ss->moveAdjust = adjustShiftMovement (s, chunk); if (!ss->moveAdjust) break; } amount = msSinceLastPaint * 0.05f * shiftGetSpeed (s); steps = amount / (0.5f * shiftGetTimestep (s)); if (!steps) steps = 1; chunk = amount / (float) steps; while (steps--) { ss->moreAdjust = adjustShiftAnimationAttribs (s, chunk); for (w = s->windows; w; w = w->next) { SHIFT_WINDOW (w); ss->moreAdjust |= adjustShiftWindowAttribs (w, chunk); for (i = 0; i < 2; i++) { ShiftSlot *slot = &sw->slots[i]; slot->tx = slot->x - w->attrib.x - (w->attrib.width * slot->scale) / 2; slot->ty = slot->y - w->attrib.y - (w->attrib.height * slot->scale) / 2; } } if (!ss->moreAdjust) break; } } UNWRAP (ss, s, preparePaintScreen); (*s->preparePaintScreen) (s, msSinceLastPaint); WRAP (ss, s, preparePaintScreen, shiftPreparePaintScreen); } static void shiftDonePaintScreen (CompScreen *s) { SHIFT_SCREEN (s); CompWindow *w; if (ss->state != ShiftStateNone) { if (ss->moreAdjust) { damageScreen (s); } else { if (ss->moveAdjust) { damageScreen (s); } if (ss->state == ShiftStateIn) { ss->state = ShiftStateNone; shiftActivateEvent(s, FALSE); for (w = s->windows; w; w = w->next) { SHIFT_WINDOW (w); sw->active = FALSE; } damageScreen (s); } else if (ss->state == ShiftStateOut) ss->state = ShiftStateSwitching; } } UNWRAP (ss, s, donePaintScreen); (*s->donePaintScreen) (s); WRAP (ss, s, donePaintScreen, shiftDonePaintScreen); } static Bool canStackRelativeTo (CompWindow *w) { if (w->attrib.override_redirect) return FALSE; if (!w->shaded && !w->pendingMaps) { if (w->attrib.map_state != IsViewable || w->mapNum == 0) return FALSE; } return TRUE; } static void shiftTerm (CompScreen *s, Bool cancel) { SHIFT_SCREEN (s); if (ss->grabIndex) { removeScreenGrab (s, ss->grabIndex, 0); ss->grabIndex = 0; } if (ss->state != ShiftStateNone) { CompWindow *w; CompWindow *pw = NULL; int i; for (i = 0; i < ss->nSlots; i++) { w = ss->drawSlots[i].w; if (ss->drawSlots[i].slot->primary && canStackRelativeTo (w)) { if (pw) restackWindowAbove (w,pw); pw = w; } } ss->moreAdjust = TRUE; ss->state = ShiftStateIn; damageScreen (s); if (!cancel && ss->selectedWindow) { w = findWindowAtScreen (s, ss->selectedWindow); if (w) sendWindowActivationRequest (s, w->id); } } } static Bool shiftTerminate (CompDisplay *d, CompAction *action, CompActionState state, CompOption *option, int nOption) { CompScreen *s; Window xid; xid = getIntOptionNamed (option, nOption, "root", 0); for (s = d->screens; s; s = s->next) { if (xid && s->root != xid) continue; shiftTerm (s, (state & CompActionStateCancel)); if (state & CompActionStateTermButton) action->state &= ~CompActionStateTermButton; if (state & CompActionStateTermKey) action->state &= ~CompActionStateTermKey; } return FALSE; } static Bool shiftInitiateScreen (CompScreen *s, CompAction *action, CompActionState state, CompOption *option, int nOption) { CompMatch *match; int count; SHIFT_SCREEN (s); if (otherScreenGrabExist (s, "shift", 0)) return FALSE; ss->currentMatch = shiftGetWindowMatch (s); 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; } } count = shiftCountWindows (s); if (count < 1) return FALSE; if (!ss->grabIndex) ss->grabIndex = pushScreenGrab (s, s->invisibleCursor, "shift"); if (ss->grabIndex) { ss->state = ShiftStateOut; shiftActivateEvent(s, TRUE); if (!shiftCreateWindowList (s)) return FALSE; ss->selectedWindow = ss->windows[0]->id; shiftRenderWindowTitle (s); ss->mvTarget = 0; ss->mvAdjust = 0; ss->mvVelocity = 0; ss->moreAdjust = TRUE; damageScreen (s); } ss->usedOutput = s->currentOutputDev; return TRUE; } static Bool shiftDoSwitch (CompDisplay *d, CompAction *action, CompActionState state, CompOption *option, int nOption, Bool nextWindow, ShiftType type) { CompScreen *s; Window xid; Bool ret = TRUE; Bool initial = FALSE; xid = getIntOptionNamed (option, nOption, "root", 0); s = findScreenAtDisplay (d, xid); if (s) { SHIFT_SCREEN (s); if ((ss->state == ShiftStateNone) || (ss->state == ShiftStateIn)) { if (type == ShiftTypeGroup) { CompWindow *w; w = findWindowAtDisplay (d, getIntOptionNamed (option, nOption, "window", 0)); if (w) { ss->type = ShiftTypeGroup; ss->clientLeader = (w->clientLeader) ? w->clientLeader : w->id; ret = shiftInitiateScreen (s, action, state, option, nOption); } } else { ss->type = type; ret = shiftInitiateScreen (s, action, state, option, nOption); } if (state & CompActionStateInitKey) action->state |= CompActionStateTermKey; if (state & CompActionStateInitButton) action->state |= CompActionStateTermButton; if (state & CompActionStateInitEdge) action->state |= CompActionStateTermEdge; initial = TRUE; } if (ret) { switchToWindow (s, nextWindow); if (initial && FALSE) { ss->mvTarget += ss->mvAdjust; ss->mvAdjust = 0.0; } } } return ret; } static Bool shiftInitiate (CompDisplay *d, CompAction *action, CompActionState state, CompOption *option, int nOption) { CompScreen *s; Window xid; Bool ret = TRUE; xid = getIntOptionNamed (option, nOption, "root", 0); s = findScreenAtDisplay (d, xid); if (s) { SHIFT_SCREEN (s); ss->type = ShiftTypeNormal; if ((ss->state == ShiftStateNone) || (ss->state == ShiftStateIn)) ret = shiftInitiateScreen (s, action, state, option, nOption); else ret = shiftTerminate (d, action, state, option, nOption); if (state & CompActionStateTermButton) action->state &= ~CompActionStateTermButton; if (state & CompActionStateTermKey) action->state &= ~CompActionStateTermKey; } return ret; } static Bool shiftInitiateAll (CompDisplay *d, CompAction *action, CompActionState state, CompOption *option, int nOption) { CompScreen *s; Window xid; Bool ret = TRUE; xid = getIntOptionNamed (option, nOption, "root", 0); s = findScreenAtDisplay (d, xid); if (s) { SHIFT_SCREEN (s); ss->type = ShiftTypeAll; if ((ss->state == ShiftStateNone) || (ss->state == ShiftStateIn)) ret = shiftInitiateScreen (s, action, state, option, nOption); else ret = shiftTerminate (d, action, state, option, nOption); if (state & CompActionStateTermButton) action->state &= ~CompActionStateTermButton; if (state & CompActionStateTermKey) action->state &= ~CompActionStateTermKey; } return ret; } static Bool shiftNext (CompDisplay *d, CompAction *action, CompActionState state, CompOption *option, int nOption) { return shiftDoSwitch (d, action, state, option, nOption, TRUE, ShiftTypeNormal); } static Bool shiftPrev (CompDisplay *d, CompAction *action, CompActionState state, CompOption *option, int nOption) { return shiftDoSwitch (d, action, state, option, nOption, FALSE, ShiftTypeNormal); } static Bool shiftNextAll (CompDisplay *d, CompAction *action, CompActionState state, CompOption *option, int nOption) { return shiftDoSwitch (d, action, state, option, nOption, TRUE, ShiftTypeAll); } static Bool shiftPrevAll (CompDisplay *d, CompAction *action, CompActionState state, CompOption *option, int nOption) { return shiftDoSwitch (d, action, state, option, nOption, FALSE, ShiftTypeAll); } static Bool shiftNextGroup (CompDisplay *d, CompAction *action, CompActionState state, CompOption *option, int nOption) { return shiftDoSwitch (d, action, state, option, nOption, TRUE, ShiftTypeGroup); } static Bool shiftPrevGroup (CompDisplay *d, CompAction *action, CompActionState state, CompOption *option, int nOption) { return shiftDoSwitch (d, action, state, option, nOption, FALSE, ShiftTypeGroup); } static void shiftWindowRemove (CompDisplay * d, Window id) { CompWindow *w; w = findWindowAtDisplay (d, id); if (w) { Bool inList = FALSE; int j, i = 0; Window selected; SHIFT_SCREEN(w->screen); if (ss->state == ShiftStateNone) return; if (isShiftWin(w)) return; selected = ss->selectedWindow; while (i < ss->nWindows) { if (w->id == ss->windows[i]->id) { inList = TRUE; if (w->id == selected) { if (i < (ss->nWindows - 1)) selected = ss->windows[i + 1]->id; else selected = ss->windows[0]->id; ss->selectedWindow = selected; } ss->nWindows--; for (j = i; j < ss->nWindows; j++) ss->windows[j] = ss->windows[j + 1]; } else { i++; } } if (!inList) return; if (ss->nWindows == 0) { CompOption o; o.type = CompOptionTypeInt; o.name = "root"; o.value.i = w->screen->root; shiftTerminate (d, NULL, 0, &o, 1); return; } if (!ss->grabIndex) return; if (shiftUpdateWindowList (w->screen)) { ss->moreAdjust = TRUE; ss->state = ShiftStateOut; damageScreen (w->screen); } } } static void shiftHandleEvent (CompDisplay *d, XEvent *event) { SHIFT_DISPLAY (d); CompScreen *s; UNWRAP (sd, d, handleEvent); (*d->handleEvent) (d, event); WRAP (sd, d, handleEvent, shiftHandleEvent); switch (event->type) { case PropertyNotify: if (event->xproperty.atom == XA_WM_NAME) { CompWindow *w; w = findWindowAtDisplay (d, event->xproperty.window); if (w) { SHIFT_SCREEN (w->screen); if (ss->grabIndex && (w->id == ss->selectedWindow)) { shiftRenderWindowTitle (w->screen); damageScreen (w->screen); } } } break; case UnmapNotify: shiftWindowRemove (d, event->xunmap.window); break; case DestroyNotify: shiftWindowRemove (d, event->xdestroywindow.window); break; case KeyPress: s = findScreenAtDisplay (d, event->xkey.root); if (s) { SHIFT_SCREEN (s); if (ss->state == ShiftStateSwitching) { if (event->xkey.keycode == sd->leftKey) switchToWindow (s, FALSE); else if (event->xkey.keycode == sd->rightKey) switchToWindow (s, TRUE); else if (event->xkey.keycode == sd->upKey) switchToWindow (s, FALSE); else if (event->xkey.keycode == sd->downKey) switchToWindow (s, TRUE); } } break; case ButtonPress: s = findScreenAtDisplay (d, event->xbutton.root); if (s) { SHIFT_SCREEN (s); if (ss->state == ShiftStateSwitching || ss->state == ShiftStateOut) { if (event->xbutton.button == Button5) switchToWindow (s, FALSE); else if (event->xbutton.button == Button4) switchToWindow (s, TRUE); if (event->xbutton.button == Button1) { ss->buttonPressTime = event->xbutton.time; ss->buttonPressed = TRUE; ss->startX = event->xbutton.x_root; ss->startY = event->xbutton.y_root; ss->startTarget = ss->mvTarget + ss->mvAdjust; } else if (event->xbutton.button == Button3) shiftTerm (s, TRUE); } } break; case ButtonRelease: s = findScreenAtDisplay (d, event->xbutton.root); if (s) { SHIFT_SCREEN (s); if (ss->state == ShiftStateSwitching || ss->state == ShiftStateOut) { if (event->xbutton.button == Button1 && ss->buttonPressed) { int new; if (event->xbutton.time - ss->buttonPressTime < shiftGetClickDuration (s)) shiftTerm (s, FALSE); ss->buttonPressTime = 0; ss->buttonPressed = FALSE; if (ss->mvTarget - floor (ss->mvTarget) >= 0.5) { ss->mvAdjust = ceil(ss->mvTarget) - ss->mvTarget; new = ceil(ss->mvTarget); } else { ss->mvAdjust = floor(ss->mvTarget) - ss->mvTarget; new = floor(ss->mvTarget); } while (new < 0) new += ss->nWindows; new = new % ss->nWindows; ss->selectedWindow = ss->windows[new]->id; shiftRenderWindowTitle (s); ss->moveAdjust = TRUE; damageScreen(s); } } } break; case MotionNotify: s = findScreenAtDisplay (d, event->xbutton.root); if (s) { SHIFT_SCREEN (s); if (ss->state == ShiftStateSwitching || ss->state == ShiftStateOut) { if (ss->buttonPressed) { int ox1 = s->outputDev[ss->usedOutput].region.extents.x1; int ox2 = s->outputDev[ss->usedOutput].region.extents.x2; int oy1 = s->outputDev[ss->usedOutput].region.extents.y1; int oy2 = s->outputDev[ss->usedOutput].region.extents.y2; float div = 0; int wx = 0; int wy = 0; int new; switch (shiftGetMode (s)) { case ModeCover: div = event->xmotion.x_root - ss->startX; div /= (ox2 - ox1) / shiftGetMouseSpeed (s); break; case ModeFlip: div = event->xmotion.y_root - ss->startY; div /= (oy2 - oy1) / shiftGetMouseSpeed (s); break; } ss->mvTarget = ss->startTarget + div - ss->mvAdjust; ss->moveAdjust = TRUE; while (ss->mvTarget >= ss->nWindows) { ss->mvTarget -= ss->nWindows; ss->invert = !ss->invert; } while (ss->mvTarget < 0) { ss->mvTarget += ss->nWindows; ss->invert = !ss->invert; } if (ss->mvTarget - floor (ss->mvTarget) >= 0.5) new = ceil(ss->mvTarget); else new = floor(ss->mvTarget); while (new < 0) new += ss->nWindows; new = new % ss->nWindows; if (ss->selectedWindow != ss->windows[new]->id) { ss->selectedWindow = ss->windows[new]->id; shiftRenderWindowTitle (s); } if (event->xmotion.x_root < 50) wx = 50; if (s->width - event->xmotion.x_root < 50) wx = -50; if (event->xmotion.y_root < 50) wy = 50; if (s->height - event->xmotion.y_root < 50) wy = -50; if (wx != 0 || wy != 0) { warpPointer (s, wx, wy); ss->startX += wx; ss->startY += wy; } damageScreen(s); } } } } } static Bool shiftDamageWindowRect (CompWindow *w, Bool initial, BoxPtr rect) { Bool status = FALSE; SHIFT_SCREEN (w->screen); if (initial) { if (ss->grabIndex && isShiftWin (w)) { shiftAddWindowToList (w->screen, w); if (shiftUpdateWindowList (w->screen)) { SHIFT_WINDOW (w); sw->active = TRUE; ss->moreAdjust = TRUE; ss->state = ShiftStateOut; damageScreen (w->screen); } } } else if (ss->state == ShiftStateSwitching) { SHIFT_WINDOW (w); if (sw->active) { damageScreen (w->screen); status = TRUE; } } UNWRAP (ss, w->screen, damageWindowRect); status |= (*w->screen->damageWindowRect) (w, initial, rect); WRAP (ss, w->screen, damageWindowRect, shiftDamageWindowRect); return status; } static Bool shiftInitDisplay (CompPlugin *p, CompDisplay *d) { ShiftDisplay *sd; sd = malloc (sizeof (ShiftDisplay)); if (!sd) return FALSE; sd->screenPrivateIndex = allocateScreenPrivateIndex (d); if (sd->screenPrivateIndex < 0) { free (sd); return FALSE; } sd->leftKey = XKeysymToKeycode (d->display, XStringToKeysym ("Left")); sd->rightKey = XKeysymToKeycode (d->display, XStringToKeysym ("Right")); sd->upKey = XKeysymToKeycode (d->display, XStringToKeysym ("Up")); sd->downKey = XKeysymToKeycode (d->display, XStringToKeysym ("Down")); shiftSetInitiateInitiate (d, shiftInitiate); shiftSetInitiateTerminate (d, shiftTerminate); shiftSetInitiateAllInitiate (d, shiftInitiateAll); shiftSetInitiateAllTerminate (d, shiftTerminate); shiftSetNextInitiate (d, shiftNext); shiftSetNextTerminate (d, shiftTerminate); shiftSetPrevInitiate (d, shiftPrev); shiftSetPrevTerminate (d, shiftTerminate); shiftSetNextAllInitiate (d, shiftNextAll); shiftSetNextAllTerminate (d, shiftTerminate); shiftSetPrevAllInitiate (d, shiftPrevAll); shiftSetPrevAllTerminate (d, shiftTerminate); shiftSetNextGroupInitiate (d, shiftNextGroup); shiftSetNextGroupTerminate (d, shiftTerminate); shiftSetPrevGroupInitiate (d, shiftPrevGroup); shiftSetPrevGroupTerminate (d, shiftTerminate); WRAP (sd, d, handleEvent, shiftHandleEvent); d->privates[displayPrivateIndex].ptr = sd; return TRUE; } static void shiftFiniDisplay (CompPlugin *p, CompDisplay *d) { SHIFT_DISPLAY (d); freeScreenPrivateIndex (d, sd->screenPrivateIndex); UNWRAP (sd, d, handleEvent); free (sd); } static Bool shiftInitScreen (CompPlugin *p, CompScreen *s) { ShiftScreen *ss; SHIFT_DISPLAY (s->display); ss = malloc (sizeof (ShiftScreen)); if (!ss) return FALSE; ss->windowPrivateIndex = allocateWindowPrivateIndex (s); if (ss->windowPrivateIndex < 0) { free (ss); return FALSE; } ss->grabIndex = 0; ss->state = ShiftStateNone; ss->windows = NULL; ss->windowsSize = 0; ss->drawSlots = NULL; ss->slotsSize = 0; ss->activeSlot = NULL; ss->selectedWindow = None; ss->moreAdjust = FALSE; ss->usedOutput = 0; ss->mvAdjust = 0; ss->mvVelocity = 0; ss->mvTarget = 0; ss->invert = FALSE; ss->textPixmap = None; ss->anim = 0.0; ss->animVelocity = 0.0; ss->buttonPressed = FALSE; matchInit (&ss->match); WRAP (ss, s, preparePaintScreen, shiftPreparePaintScreen); WRAP (ss, s, donePaintScreen, shiftDonePaintScreen); WRAP (ss, s, paintScreen, shiftPaintScreen); WRAP (ss, s, paintOutput, shiftPaintOutput); WRAP (ss, s, paintWindow, shiftPaintWindow); WRAP (ss, s, damageWindowRect, shiftDamageWindowRect); ss->cursor = XCreateFontCursor (s->display->display, XC_left_ptr); s->privates[sd->screenPrivateIndex].ptr = ss; return TRUE; } static void shiftFiniScreen (CompPlugin *p, CompScreen *s) { SHIFT_SCREEN (s); freeWindowPrivateIndex (s, ss->windowPrivateIndex); UNWRAP (ss, s, preparePaintScreen); UNWRAP (ss, s, donePaintScreen); UNWRAP (ss, s, paintScreen); UNWRAP (ss, s, paintOutput); UNWRAP (ss, s, paintWindow); UNWRAP (ss, s, damageWindowRect); matchFini (&ss->match); shiftFreeWindowTitle (s); XFreeCursor (s->display->display, ss->cursor); if (ss->windows) free (ss->windows); if (ss->drawSlots) free (ss->drawSlots); free (ss); } static Bool shiftInitWindow (CompPlugin *p, CompWindow *w) { ShiftWindow *sw; SHIFT_SCREEN (w->screen); sw = calloc (1, sizeof (ShiftWindow)); if (!sw) return FALSE; sw->slots[0].scale = 1.0; sw->slots[1].scale = 1.0; w->privates[ss->windowPrivateIndex].ptr = sw; return TRUE; } static void shiftFiniWindow (CompPlugin *p, CompWindow *w) { SHIFT_WINDOW (w); free (sw); } static Bool shiftInit (CompPlugin *p) { displayPrivateIndex = allocateDisplayPrivateIndex (); if (displayPrivateIndex < 0) return FALSE; return TRUE; } static void shiftFini (CompPlugin *p) { if (displayPrivateIndex >= 0) freeDisplayPrivateIndex (displayPrivateIndex); } static int shiftGetVersion (CompPlugin *plugin, int version) { return ABIVERSION; } CompPluginVTable shiftVTable = { "shift", shiftGetVersion, 0, shiftInit, shiftFini, shiftInitDisplay, shiftFiniDisplay, shiftInitScreen, shiftFiniScreen, shiftInitWindow, shiftFiniWindow, 0, 0, 0, 0 }; CompPluginVTable * getCompPluginInfo (void) { return &shiftVTable; }