/*
*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <sys/time.h>
#include <X11/Xatom.h>
#include <X11/cursorfont.h>
#include <compiz.h>
#include <text.h>
#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;
}
syntax highlighted by Code2HTML, v. 0.9.1