/*
*
* Compiz ring switcher plugin
*
* 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.
*
*/
/* TODO list for this plugin
* - is there a way to get icons larger than 96x96?
*/
#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 "ring_options.h"
typedef enum {
RingStateNone = 0,
RingStateOut,
RingStateSwitching,
RingStateIn
} RingState;
typedef enum {
RingTypeNormal = 0,
RingTypeGroup,
RingTypeAll
} RingType;
static int displayPrivateIndex;
typedef struct _RingSlot {
int x, y; /* thumb center coordinates */
float scale; /* size scale (fit to maximal thumb size */
float depthScale; /* scale for depth impression */
float depthBrightness; /* brightness for depth impression */
} RingSlot;
typedef struct _RingDrawSlot {
CompWindow *w;
RingSlot **slot;
} RingDrawSlot;
typedef struct _RingDisplay {
int screenPrivateIndex;
HandleEventProc handleEvent;
} RingDisplay;
typedef struct _RingScreen {
int windowPrivateIndex;
PreparePaintScreenProc preparePaintScreen;
DonePaintScreenProc donePaintScreen;
PaintOutputProc paintOutput;
PaintWindowProc paintWindow;
DamageWindowRectProc damageWindowRect;
int grabIndex;
RingState state;
RingType type;
Bool moreAdjust;
Bool rotateAdjust;
Bool paintingSwitcher;
int rotTarget;
int rotAdjust;
GLfloat rVelocity;
Cursor cursor;
/* only used for sorting */
CompWindow **windows;
RingDrawSlot *drawSlots;
int windowsSize;
int nWindows;
Window clientLeader;
Window selectedWindow;
/* text display support */
CompTexture textTexture;
Pixmap textPixmap;
int textWidth;
int textHeight;
CompMatch match;
CompMatch *currentMatch;
} RingScreen;
typedef struct _RingWindow {
RingSlot *slot;
GLfloat xVelocity;
GLfloat yVelocity;
GLfloat scaleVelocity;
GLfloat tx;
GLfloat ty;
GLfloat scale;
Bool adjust;
} RingWindow;
#define PI 3.1415926
#define DIST_ROT (3600 / rs->nWindows)
#define GET_RING_DISPLAY(d) \
((RingDisplay *) (d)->privates[displayPrivateIndex].ptr)
#define RING_DISPLAY(d) \
RingDisplay *rd = GET_RING_DISPLAY (d)
#define GET_RING_SCREEN(s, rd) \
((RingScreen *) (s)->privates[(rd)->screenPrivateIndex].ptr)
#define RING_SCREEN(s) \
RingScreen *rs = GET_RING_SCREEN (s, GET_RING_DISPLAY (s->display))
#define GET_RING_WINDOW(w, rs) \
((RingWindow *) (w)->privates[(rs)->windowPrivateIndex].ptr)
#define RING_WINDOW(w) \
RingWindow *rw = GET_RING_WINDOW (w, \
GET_RING_SCREEN (w->screen, \
GET_RING_DISPLAY (w->screen->display)))
static Bool
isRingWin (CompWindow *w)
{
RING_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 (ringGetMinimized (w->screen))
{
if (!w->minimized && !w->inShowDesktopMode && !w->shaded)
return FALSE;
}
else
return FALSE;
}
if (rs->type == RingTypeNormal)
{
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 (rs->type == RingTypeGroup &&
rs->clientLeader != w->clientLeader &&
rs->clientLeader != w->id)
{
return FALSE;
}
if (w->state & CompWindowStateSkipTaskbarMask)
return FALSE;
if (w->state & CompWindowStateShadedMask)
return FALSE;
if (!matchEval (rs->currentMatch, w))
return FALSE;
return TRUE;
}
static void
ringFreeWindowTitle (CompScreen *s)
{
RING_SCREEN(s);
if (!rs->textPixmap)
return;
releasePixmapFromTexture (s, &rs->textTexture);
initTexture (s, &rs->textTexture);
XFreePixmap (s->display->display, rs->textPixmap);
rs->textPixmap = None;
}
static void
ringRenderWindowTitle (CompScreen *s)
{
CompTextAttrib tA;
int stride;
void *data;
RING_SCREEN (s);
ringFreeWindowTitle (s);
if (!ringGetWindowTitle (s))
return;
int ox1, ox2, oy1, oy2;
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 = ringGetTitleFontSize (s);
tA.color[0] = ringGetTitleFontColorRed (s);
tA.color[1] = ringGetTitleFontColorGreen (s);
tA.color[2] = ringGetTitleFontColorBlue (s);
tA.color[3] = ringGetTitleFontColorAlpha (s);
tA.style = (ringGetTitleFontBold (s)) ?
TEXT_STYLE_BOLD : TEXT_STYLE_NORMAL;
tA.family = "Sans";
tA.ellipsize = TRUE;
tA.renderMode = TextRenderWindowTitle;
tA.data = (void*)rs->selectedWindow;
initTexture (s, &rs->textTexture);
if ((*s->display->fileToImage) (s->display, TEXT_ID, (char *)&tA,
&rs->textWidth, &rs->textHeight,
&stride, &data))
{
rs->textPixmap = (Pixmap)data;
bindPixmapToTexture (s, &rs->textTexture, rs->textPixmap,
rs->textWidth, rs->textHeight, 32);
}
else
{
rs->textPixmap = None;
rs->textWidth = 0;
rs->textHeight = 0;
}
}
static void
ringDrawWindowTitle (CompScreen *s)
{
RING_SCREEN(s);
GLboolean wasBlend;
GLint oldBlendSrc, oldBlendDst;
float width = rs->textWidth;
float height = rs->textHeight;
float border = 10.0f;
int ox1, ox2, oy1, oy2;
getCurrentOutputExtents (s, &ox1, &oy1, &ox2, &oy2);
float x = ox1 + ((ox2 - ox1) / 2) - (rs->textWidth / 2);
float y;
/* assign y (for the lower corner!) according to the setting */
switch (ringGetTitleTextPlacement (s))
{
case TitleTextPlacementCenteredOnScreen:
y = oy1 + ((oy2 - oy1) / 2) + (height / 2);
break;
case TitleTextPlacementAboveRing:
case TitleTextPlacementBelowRing:
{
XRectangle workArea;
getWorkareaForOutput (s, s->currentOutputDev, &workArea);
if (ringGetTitleTextPlacement (s) ==
TitleTextPlacementAboveRing)
y = oy1 + workArea.y + (2 * border) + height;
else
y = oy1 + workArea.y + workArea.height - (2 * border);
}
break;
default:
return;
break;
}
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 (ringGetTitleBackColorRed (s),
ringGetTitleBackColorGreen (s),
ringGetTitleBackColorBlue (s),
ringGetTitleBackColorAlpha (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, &rs->textTexture,COMP_TEXTURE_FILTER_GOOD);
CompMatrix *m = &rs->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, &rs->textTexture);
glColor4usv (defaultColor);
if (!wasBlend)
glDisable (GL_BLEND);
glBlendFunc (oldBlendSrc, oldBlendDst);
}
static Bool
ringPaintWindow (CompWindow *w,
const WindowPaintAttrib *attrib,
const CompTransform *transform,
Region region,
unsigned int mask)
{
CompScreen *s = w->screen;
Bool status;
RING_SCREEN (s);
if (rs->state != RingStateNone)
{
WindowPaintAttrib sAttrib = *attrib;
Bool scaled = FALSE;
RING_WINDOW (w);
if (w->mapNum)
{
if (!w->texture->pixmap && !w->bindFailed)
bindWindow (w);
}
if (rw->adjust || rw->slot)
{
scaled = rw->adjust || (rw->slot && rs->paintingSwitcher);
mask |= PAINT_WINDOW_NO_CORE_INSTANCE_MASK;
}
else if (rs->state != RingStateIn)
{
if (ringGetDarkenBack (s))
{
/* modify brightness of the other windows */
sAttrib.brightness = sAttrib.brightness / 2;
}
}
UNWRAP (rs, s, paintWindow);
status = (*s->paintWindow) (w, &sAttrib, transform, region, mask);
WRAP (rs, s, paintWindow, ringPaintWindow);
if (scaled && w->texture->pixmap)
{
FragmentAttrib fragment;
CompTransform wTransform = *transform;
if (mask & PAINT_WINDOW_OCCLUSION_DETECTION_MASK)
return FALSE;
initFragmentAttrib (&fragment, &w->lastPaint);
if (rw->slot)
{
fragment.brightness = (float) fragment.brightness *
rw->slot->depthBrightness;
if (w->id != rs->selectedWindow)
fragment.opacity = (float)fragment.opacity *
ringGetInactiveOpacity (s) / 100;
}
if (w->alpha || fragment.opacity != OPAQUE)
mask |= PAINT_WINDOW_TRANSLUCENT_MASK;
matrixTranslate (&wTransform, w->attrib.x, w->attrib.y, 0.0f);
matrixScale (&wTransform, rw->scale, rw->scale, 1.0f);
matrixTranslate (&wTransform,
rw->tx / rw->scale - w->attrib.x,
rw->ty / rw->scale - w->attrib.y,
0.0f);
glPushMatrix ();
glLoadMatrixf (wTransform.m);
(*s->drawWindow) (w, &wTransform, &fragment, region,
mask | PAINT_WINDOW_TRANSFORMED_MASK);
glPopMatrix ();
}
if (scaled && (rs->state != RingStateIn) &&
((ringGetOverlayIcon (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;
CompMatrix matrix;
float scale;
float x, y;
int width, height;
int scaledWinWidth, scaledWinHeight;
RingOverlayIconEnum iconOverlay = ringGetOverlayIcon (s);
scaledWinWidth = w->width * rw->scale;
scaledWinHeight = w->height * rw->scale;
if (!w->texture->pixmap)
iconOverlay = OverlayIconBig;
switch (iconOverlay)
{
case OverlayIconNone:
case OverlayIconEmblem:
scale = (rw->slot) ? rw->slot->depthScale : 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 = w->attrib.x + scaledWinWidth - width;
y = w->attrib.y + scaledWinHeight - height;
break;
case OverlayIconBig:
default:
x = w->attrib.x + scaledWinWidth / 2 - width / 2;
y = w->attrib.y + scaledWinHeight / 2 - height / 2;
break;
}
x += rw->tx;
y += rw->ty;
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 = w->attrib.x;
iconReg.extents.y1 = w->attrib.y;
iconReg.extents.x2 = w->attrib.x + icon->width;
iconReg.extents.y2 = w->attrib.y + icon->height;
matrix = icon->texture.matrix;
matrix.x0 -= (w->attrib.x * icon->texture.matrix.xx);
matrix.y0 -= (w->attrib.y * icon->texture.matrix.yy);
w->vCount = w->indexCount = 0;
(*w->screen->addWindowGeometry) (w, &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, &sAttrib);
if (rw->slot)
fragment.brightness = (float) fragment.brightness *
rw->slot->depthBrightness;
matrixTranslate (&wTransform,
w->attrib.x, w->attrib.y, 0.0f);
matrixScale (&wTransform, scale, scale, 1.0f);
matrixTranslate (&wTransform,
(x - w->attrib.x) / scale - w->attrib.x,
(y - w->attrib.y) / scale - w->attrib.y,
0.0f);
glPushMatrix ();
glLoadMatrixf (wTransform.m);
(*w->screen->drawWindowTexture) (w,
&icon->texture, &fragment,
mask);
glPopMatrix ();
}
}
}
}
else
{
UNWRAP (rs, s, paintWindow);
status = (*s->paintWindow) (w, attrib, transform, region, mask);
WRAP (rs, s, paintWindow, ringPaintWindow);
}
return status;
}
static inline float
ringLinearInterpolation(float valX,
float minX, float maxX,
float minY, float maxY)
{
double factor = (maxY - minY) / (maxX - minX);
return (minY + (factor * (valX - minX)));
}
static int
compareWindows (const void *elem1,
const void *elem2)
{
CompWindow *w1 = *((CompWindow **) elem1);
CompWindow *w2 = *((CompWindow **) elem2);
if (w1->mapNum && !w2->mapNum)
return -1;
if (w2->mapNum && !w1->mapNum)
return 1;
return w2->activeNum - w1->activeNum;
}
static int
compareRingWindowDepth (const void *elem1,
const void *elem2)
{
RingSlot *a1 = *(((RingDrawSlot *) elem1)->slot);
RingSlot *a2 = *(((RingDrawSlot *) elem2)->slot);
if (a1->y < a2->y)
return -1;
else if (a1->y > a2->y)
return 1;
else
return 0;
}
static Bool
layoutThumbs (CompScreen *s)
{
RING_SCREEN (s);
CompWindow *w;
float baseAngle = (2 * PI * rs->rotTarget) / 3600;
float angle;
int index;
int ww, wh;
float xScale, yScale;
if ((rs->state == RingStateNone) || (rs->state == RingStateIn))
return FALSE;
int ox1, ox2, oy1, oy2;
getCurrentOutputExtents (s, &ox1, &oy1, &ox2, &oy2);
/* 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 ellipseA = ((ox2 - ox1) * ringGetRingWidth (s)) / 200;
int ellipseB = ((oy2 - oy1) * ringGetRingHeight (s)) / 200;
for (index = 0; index < rs->nWindows; index++)
{
w = rs->windows[index];
RING_WINDOW (w);
if (!rw->slot)
rw->slot = malloc (sizeof(RingSlot));
if (!rw->slot)
return FALSE;
/* we subtract the angle from the base angle
to order the windows clockwise */
angle = baseAngle - (index * (2 * PI / rs->nWindows));
rw->slot->x = centerX + (ringGetRingClockwise(s) ? -1 : 1) *
((float)ellipseA * sin(angle));
rw->slot->y = centerY + ((float)ellipseB * cos(angle));
ww = w->width + w->input.left + w->input.right;
wh = w->height + w->input.top + w->input.bottom;
if (ww > ringGetThumbWidth (s))
xScale = (float)(ringGetThumbWidth (s)) / (float)ww;
else
xScale = 1.0f;
if (wh > ringGetThumbHeight (s))
yScale = (float)(ringGetThumbHeight (s)) / (float)wh;
else
yScale = 1.0f;
rw->slot->scale = MIN (xScale, yScale);
/* scale and brightness are obtained by doing a linear inter-
polation - the y positions are the x values for the interpolation
(the larger Y is, the nearer is the window), and scale/brightness
are the y values for the interpolation */
rw->slot->depthScale =
ringLinearInterpolation (rw->slot->y,
centerY - ellipseB,
centerY + ellipseB,
ringGetMinScale (s),
1.0f);
rw->slot->depthBrightness =
ringLinearInterpolation (rw->slot->y,
centerY - ellipseB,
centerY + ellipseB,
ringGetMinBrightness (s),
1.0f);
rs->drawSlots[index].w = w;
rs->drawSlots[index].slot = &rw->slot;
}
/* sort the draw list so that the windows with the
lowest Y value (the windows being farest away)
are drawn first */
qsort (rs->drawSlots, rs->nWindows, sizeof (RingDrawSlot),
compareRingWindowDepth);
return TRUE;
}
static void
ringAddWindowToList (CompScreen *s,
CompWindow *w)
{
RING_SCREEN (s);
if (rs->windowsSize <= rs->nWindows)
{
rs->windows = realloc (rs->windows,
sizeof (CompWindow *) * (rs->nWindows + 32));
if (!rs->windows)
return;
rs->drawSlots = realloc (rs->drawSlots,
sizeof (RingDrawSlot) * (rs->nWindows + 32));
if (!rs->drawSlots)
return;
rs->windowsSize = rs->nWindows + 32;
}
rs->windows[rs->nWindows++] = w;
}
static Bool
ringUpdateWindowList (CompScreen *s)
{
int i;
RING_SCREEN (s);
qsort (rs->windows, rs->nWindows, sizeof (CompWindow *), compareWindows);
rs->rotTarget = 0;
for (i = 0; i < rs->nWindows; i++)
{
if (rs->windows[i]->id == rs->selectedWindow)
break;
rs->rotTarget += DIST_ROT;
}
return layoutThumbs (s);
}
static Bool
ringCreateWindowList (CompScreen *s)
{
CompWindow *w;
RING_SCREEN (s);
rs->nWindows = 0;
for (w = s->windows; w; w = w->next)
{
if (isRingWin (w))
{
RING_WINDOW (w);
ringAddWindowToList (s, w);
rw->adjust = TRUE;
}
}
return ringUpdateWindowList (s);
}
static void
switchToWindow (CompScreen *s,
Bool toNext)
{
CompWindow *w;
int cur;
RING_SCREEN (s);
if (!rs->grabIndex)
return;
for (cur = 0; cur < rs->nWindows; cur++)
{
if (rs->windows[cur]->id == rs->selectedWindow)
break;
}
if (cur == rs->nWindows)
return;
if (toNext)
w = rs->windows[(cur + 1) % rs->nWindows];
else
w = rs->windows[(cur + rs->nWindows - 1) % rs->nWindows];
if (w)
{
Window old = rs->selectedWindow;
rs->selectedWindow = w->id;
if (old != w->id)
{
if (toNext)
rs->rotAdjust += DIST_ROT;
else
rs->rotAdjust -= DIST_ROT;
rs->rotateAdjust = TRUE;
damageScreen (s);
ringRenderWindowTitle (s);
}
}
}
static int
ringCountWindows (CompScreen *s)
{
CompWindow *w;
int count = 0;
for (w = s->windows; w; w = w->next)
{
if (isRingWin (w))
count++;
}
return count;
}
static int adjustRingRotation (CompScreen *s, float chunk)
{
float dx, adjust, amount;
int change;
RING_SCREEN(s);
dx = rs->rotAdjust;
adjust = dx * 0.15f;
amount = fabs(dx) * 1.5f;
if (amount < 0.2f)
amount = 0.2f;
else if (amount > 2.0f)
amount = 2.0f;
rs->rVelocity = (amount * rs->rVelocity + adjust) / (amount + 1.0f);
if (fabs (dx) < 0.1f && fabs (rs->rVelocity) < 0.2f)
{
rs->rVelocity = 0.0f;
rs->rotTarget += rs->rotAdjust;
rs->rotAdjust = 0;
return 0;
}
change = rs->rVelocity * chunk;
if (!change)
{
if (rs->rVelocity)
change = (rs->rotAdjust > 0) ? 1 : -1;
}
rs->rotAdjust -= change;
rs->rotTarget += change;
if (!layoutThumbs (s))
return FALSE;
return TRUE;
}
static int
adjustRingVelocity (CompWindow *w)
{
float dx, dy, ds, adjust, amount;
float x1, y1, scale;
RING_WINDOW (w);
if (rw->slot)
{
scale = rw->slot->scale * rw->slot->depthScale;
x1 = rw->slot->x - (w->attrib.width * scale) / 2;
y1 = rw->slot->y - (w->attrib.height * scale) / 2;
}
else
{
x1 = w->attrib.x;
y1 = w->attrib.y;
scale = 1.0f;
}
dx = x1 - (w->attrib.x + rw->tx);
adjust = dx * 0.15f;
amount = fabs (dx) * 1.5f;
if (amount < 0.5f)
amount = 0.5f;
else if (amount > 5.0f)
amount = 5.0f;
rw->xVelocity = (amount * rw->xVelocity + adjust) / (amount + 1.0f);
dy = y1 - (w->attrib.y + rw->ty);
adjust = dy * 0.15f;
amount = fabs (dy) * 1.5f;
if (amount < 0.5f)
amount = 0.5f;
else if (amount > 5.0f)
amount = 5.0f;
rw->yVelocity = (amount * rw->yVelocity + adjust) / (amount + 1.0f);
ds = scale - rw->scale;
adjust = ds * 0.1f;
amount = fabs (ds) * 7.0f;
if (amount < 0.01f)
amount = 0.01f;
else if (amount > 0.15f)
amount = 0.15f;
rw->scaleVelocity = (amount * rw->scaleVelocity + adjust) /
(amount + 1.0f);
if (fabs (dx) < 0.1f && fabs (rw->xVelocity) < 0.2f &&
fabs (dy) < 0.1f && fabs (rw->yVelocity) < 0.2f &&
fabs (ds) < 0.001f && fabs (rw->scaleVelocity) < 0.002f)
{
rw->xVelocity = rw->yVelocity = rw->scaleVelocity = 0.0f;
rw->tx = x1 - w->attrib.x;
rw->ty = y1 - w->attrib.y;
rw->scale = scale;
return 0;
}
return 1;
}
static Bool
ringPaintOutput (CompScreen *s,
const ScreenPaintAttrib *sAttrib,
const CompTransform *transform,
Region region,
CompOutput *output,
unsigned int mask)
{
Bool status;
RING_SCREEN (s);
if (rs->state != RingStateNone)
mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK;
UNWRAP (rs, s, paintOutput);
status = (*s->paintOutput) (s, sAttrib, transform, region, output, mask);
WRAP (rs, s, paintOutput, ringPaintOutput);
if (rs->state != RingStateNone)
{
int i;
CompWindow *w;
CompTransform sTransform = *transform;
transformToScreenSpace (s, output, -DEFAULT_Z_CAMERA, &sTransform);
glPushMatrix ();
glLoadMatrixf (sTransform.m);
rs->paintingSwitcher = TRUE;
for (i = 0; i < rs->nWindows; i++)
{
w = rs->drawSlots[i].w;
if (rs->drawSlots[i].slot && *(rs->drawSlots[i].slot))
{
(*s->paintWindow) (w, &w->paint, &sTransform,
&infiniteRegion, 0);
}
}
rs->paintingSwitcher = FALSE;
if (rs->textPixmap && (rs->state != RingStateIn))
ringDrawWindowTitle (s);
glPopMatrix ();
}
return status;
}
static void
ringPreparePaintScreen (CompScreen *s,
int msSinceLastPaint)
{
RING_SCREEN (s);
if (rs->state != RingStateNone &&
(rs->moreAdjust || rs->rotateAdjust))
{
CompWindow *w;
int steps;
float amount, chunk;
amount = msSinceLastPaint * 0.05f * ringGetSpeed (s);
steps = amount / (0.5f * ringGetTimestep (s));
if (!steps)
steps = 1;
chunk = amount / (float) steps;
while (steps--)
{
rs->rotateAdjust = adjustRingRotation (s, chunk);
rs->moreAdjust = FALSE;
for (w = s->windows; w; w = w->next)
{
RING_WINDOW (w);
if (rw->adjust)
{
rw->adjust = adjustRingVelocity (w);
rs->moreAdjust |= rw->adjust;
rw->tx += rw->xVelocity * chunk;
rw->ty += rw->yVelocity * chunk;
rw->scale += rw->scaleVelocity * chunk;
}
else if (rw->slot)
{
rw->scale = rw->slot->scale * rw->slot->depthScale;
rw->tx = rw->slot->x - w->attrib.x -
(w->attrib.width * rw->scale) / 2;
rw->ty = rw->slot->y - w->attrib.y -
(w->attrib.height * rw->scale) / 2;
}
}
if (!rs->moreAdjust && !rs->rotateAdjust)
break;
}
}
UNWRAP (rs, s, preparePaintScreen);
(*s->preparePaintScreen) (s, msSinceLastPaint);
WRAP (rs, s, preparePaintScreen, ringPreparePaintScreen);
}
static void
ringDonePaintScreen (CompScreen *s)
{
RING_SCREEN (s);
if (rs->state != RingStateNone)
{
if (rs->moreAdjust)
{
damageScreen (s);
}
else
{
if (rs->rotateAdjust)
{
damageScreen (s);
}
if (rs->state == RingStateIn)
rs->state = RingStateNone;
else if (rs->state == RingStateOut)
rs->state = RingStateSwitching;
}
}
UNWRAP (rs, s, donePaintScreen);
(*s->donePaintScreen) (s);
WRAP (rs, s, donePaintScreen, ringDonePaintScreen);
}
static Bool
ringTerminate (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)
{
RING_SCREEN (s);
if (xid && s->root != xid)
continue;
if (rs->grabIndex)
{
removeScreenGrab (s, rs->grabIndex, 0);
rs->grabIndex = 0;
}
if (rs->state != RingStateNone)
{
CompWindow *w;
for (w = s->windows; w; w = w->next)
{
RING_WINDOW (w);
if (rw->slot)
{
free (rw->slot);
rw->slot = NULL;
rw->adjust = TRUE;
}
}
rs->moreAdjust = TRUE;
rs->state = RingStateIn;
damageScreen (s);
if (!(state & CompActionStateCancel) && rs->selectedWindow)
{
w = findWindowAtScreen (s, rs->selectedWindow);
if (w)
sendWindowActivationRequest (s, w->id);
}
}
}
return FALSE;
}
static Bool
ringInitiate (CompScreen *s,
CompAction *action,
CompActionState state,
CompOption *option,
int nOption)
{
CompMatch *match;
int count;
RING_SCREEN (s);
if (otherScreenGrabExist (s, "ring", 0))
return FALSE;
rs->currentMatch = ringGetWindowMatch (s);
match = getMatchOptionNamed (option, nOption, "match", NULL);
if (match)
{
matchFini (&rs->match);
matchInit (&rs->match);
if (matchCopy (&rs->match, match))
{
matchUpdate (s->display, &rs->match);
rs->currentMatch = &rs->match;
}
}
count = ringCountWindows (s);
if (count < 1)
return FALSE;
if (!rs->grabIndex)
{
if (ringGetSelectWithMouse (s))
rs->grabIndex = pushScreenGrab (s, rs->cursor, "ring");
else
rs->grabIndex = pushScreenGrab (s, s->invisibleCursor, "ring");
}
if (rs->grabIndex)
{
rs->state = RingStateOut;
if (!ringCreateWindowList (s))
return FALSE;
rs->selectedWindow = rs->windows[0]->id;
ringRenderWindowTitle (s);
rs->rotTarget = 0;
rs->moreAdjust = TRUE;
damageScreen (s);
}
if (state & CompActionStateInitButton)
action->state |= CompActionStateTermButton;
if (state & CompActionStateInitKey)
action->state |= CompActionStateTermKey;
return TRUE;
}
static Bool
ringDoSwitch (CompDisplay *d,
CompAction *action,
CompActionState state,
CompOption *option,
int nOption,
Bool nextWindow,
RingType type)
{
CompScreen *s;
Window xid;
Bool ret = TRUE;
xid = getIntOptionNamed (option, nOption, "root", 0);
s = findScreenAtDisplay (d, xid);
if (s)
{
RING_SCREEN (s);
if ((rs->state == RingStateNone) || (rs->state == RingStateIn))
{
if (type == RingTypeGroup)
{
CompWindow *w;
w = findWindowAtDisplay (d, getIntOptionNamed (option, nOption,
"window", 0));
if (w)
{
rs->type = RingTypeGroup;
rs->clientLeader =
(w->clientLeader) ? w->clientLeader : w->id;
ret = ringInitiate (s, action, state, option, nOption);
}
}
else
{
rs->type = type;
ret = ringInitiate (s, action, state, option, nOption);
}
if (state & CompActionStateInitKey)
action->state |= CompActionStateTermKey;
if (state & CompActionStateInitButton)
action->state |= CompActionStateTermButton;
if (state & CompActionStateInitEdge)
action->state |= CompActionStateTermEdge;
}
if (ret)
switchToWindow (s, nextWindow);
}
return ret;
}
static Bool
ringNext (CompDisplay *d,
CompAction *action,
CompActionState state,
CompOption *option,
int nOption)
{
return ringDoSwitch (d, action, state, option, nOption,
TRUE, RingTypeNormal);
}
static Bool
ringPrev (CompDisplay *d,
CompAction *action,
CompActionState state,
CompOption *option,
int nOption)
{
return ringDoSwitch (d, action, state, option, nOption,
FALSE, RingTypeNormal);
}
static Bool
ringNextAll (CompDisplay *d,
CompAction *action,
CompActionState state,
CompOption *option,
int nOption)
{
return ringDoSwitch (d, action, state, option, nOption,
TRUE, RingTypeAll);
}
static Bool
ringPrevAll (CompDisplay *d,
CompAction *action,
CompActionState state,
CompOption *option,
int nOption)
{
return ringDoSwitch (d, action, state, option, nOption,
FALSE, RingTypeAll);
}
static Bool
ringNextGroup (CompDisplay *d,
CompAction *action,
CompActionState state,
CompOption *option,
int nOption)
{
return ringDoSwitch (d, action, state, option, nOption,
TRUE, RingTypeGroup);
}
static Bool
ringPrevGroup (CompDisplay *d,
CompAction *action,
CompActionState state,
CompOption *option,
int nOption)
{
return ringDoSwitch (d, action, state, option, nOption,
FALSE, RingTypeGroup);
}
static void
ringWindowSelectAt (CompScreen *s, int x, int y)
{
CompOption o;
CompWindow *w;
int i;
RING_SCREEN (s);
if (!ringGetSelectWithMouse (s))
return;
/* first find the top-most window the mouse
pointer is over */
for (i = rs->nWindows - 1; i >= 0; i--)
{
w = rs->drawSlots[i].w;
if (rs->drawSlots[i].slot && *(rs->drawSlots[i].slot))
{
RING_WINDOW (w);
if ((x >= (rw->tx + w->attrib.x)) &&
(x <= (rw->tx + w->attrib.x + (w->width * rw->scale))) &&
(y >= (rw->ty + w->attrib.y)) &&
(y <= (rw->ty + w->attrib.y + (w->height * rw->scale))))
{
/* we have found one, select it */
rs->selectedWindow = w->id;
break;
}
}
}
if (i >= 0)
{
o.type = CompOptionTypeInt;
o.name = "root";
o.value.i = s->root;
ringTerminate(s->display, NULL, 0, &o, 1);
}
}
static void
ringWindowRemove (CompDisplay * d,
Window id)
{
CompWindow *w;
w = findWindowAtDisplay (d, id);
if (w)
{
Bool inList = FALSE;
int j, i = 0;
Window selected;
RING_SCREEN(w->screen);
if (rs->state == RingStateNone)
return;
if (isRingWin(w))
return;
selected = rs->selectedWindow;
while (i < rs->nWindows)
{
if (w->id == rs->windows[i]->id)
{
inList = TRUE;
if (w->id == selected)
{
if (i < (rs->nWindows - 1))
selected = rs->windows[i + 1]->id;
else
selected = rs->windows[0]->id;
rs->selectedWindow = selected;
}
rs->nWindows--;
for (j = i; j < rs->nWindows; j++)
rs->windows[j] = rs->windows[j + 1];
}
else
{
i++;
}
}
if (!inList)
return;
if (rs->nWindows == 0)
{
CompOption o;
o.type = CompOptionTypeInt;
o.name = "root";
o.value.i = w->screen->root;
ringTerminate (d, NULL, 0, &o, 1);
return;
}
if (!rs->grabIndex)
return;
if (ringUpdateWindowList (w->screen))
{
rs->moreAdjust = TRUE;
rs->state = RingStateOut;
damageScreen (w->screen);
}
}
}
static void
ringHandleEvent (CompDisplay *d,
XEvent *event)
{
RING_DISPLAY (d);
UNWRAP (rd, d, handleEvent);
(*d->handleEvent) (d, event);
WRAP (rd, d, handleEvent, ringHandleEvent);
switch (event->type) {
case PropertyNotify:
if (event->xproperty.atom == XA_WM_NAME)
{
CompWindow *w;
w = findWindowAtDisplay (d, event->xproperty.window);
if (w)
{
RING_SCREEN (w->screen);
if (rs->grabIndex && (w->id == rs->selectedWindow))
{
ringRenderWindowTitle (w->screen);
damageScreen (w->screen);
}
}
}
break;
case ButtonPress:
if (event->xbutton.button == Button1)
{
CompScreen *s;
s = findScreenAtDisplay (d, event->xbutton.root);
if (s)
{
RING_SCREEN (s);
if (rs->grabIndex)
ringWindowSelectAt (s,
event->xbutton.x_root,
event->xbutton.y_root);
}
}
break;
case UnmapNotify:
ringWindowRemove (d, event->xunmap.window);
break;
case DestroyNotify:
ringWindowRemove (d, event->xdestroywindow.window);
break;
}
}
static Bool
ringDamageWindowRect (CompWindow *w,
Bool initial,
BoxPtr rect)
{
Bool status = FALSE;
RING_SCREEN (w->screen);
if (initial)
{
if (rs->grabIndex && isRingWin (w))
{
ringAddWindowToList (w->screen, w);
if (ringUpdateWindowList (w->screen))
{
RING_WINDOW (w);
rw->adjust = TRUE;
rs->moreAdjust = TRUE;
rs->state = RingStateOut;
damageScreen (w->screen);
}
}
}
else if (rs->state == RingStateSwitching)
{
RING_WINDOW (w);
if (rw->slot)
{
damageTransformedWindowRect (w,
rw->scale, rw->scale,
rw->tx, rw->ty,
rect);
status = TRUE;
}
}
UNWRAP (rs, w->screen, damageWindowRect);
status |= (*w->screen->damageWindowRect) (w, initial, rect);
WRAP (rs, w->screen, damageWindowRect, ringDamageWindowRect);
return status;
}
static Bool
ringInitDisplay (CompPlugin *p,
CompDisplay *d)
{
RingDisplay *rd;
rd = malloc (sizeof (RingDisplay));
if (!rd)
return FALSE;
rd->screenPrivateIndex = allocateScreenPrivateIndex (d);
if (rd->screenPrivateIndex < 0)
{
free (rd);
return FALSE;
}
ringSetNextInitiate (d, ringNext);
ringSetNextTerminate (d, ringTerminate);
ringSetPrevInitiate (d, ringPrev);
ringSetPrevTerminate (d, ringTerminate);
ringSetNextAllInitiate (d, ringNextAll);
ringSetNextAllTerminate (d, ringTerminate);
ringSetPrevAllInitiate (d, ringPrevAll);
ringSetPrevAllTerminate (d, ringTerminate);
ringSetNextGroupInitiate (d, ringNextGroup);
ringSetNextGroupTerminate (d, ringTerminate);
ringSetPrevGroupInitiate (d, ringPrevGroup);
ringSetPrevGroupTerminate (d, ringTerminate);
WRAP (rd, d, handleEvent, ringHandleEvent);
d->privates[displayPrivateIndex].ptr = rd;
return TRUE;
}
static void
ringFiniDisplay (CompPlugin *p,
CompDisplay *d)
{
RING_DISPLAY (d);
freeScreenPrivateIndex (d, rd->screenPrivateIndex);
UNWRAP (rd, d, handleEvent);
free (rd);
}
static Bool
ringInitScreen (CompPlugin *p,
CompScreen *s)
{
RingScreen *rs;
RING_DISPLAY (s->display);
rs = malloc (sizeof (RingScreen));
if (!rs)
return FALSE;
rs->windowPrivateIndex = allocateWindowPrivateIndex (s);
if (rs->windowPrivateIndex < 0)
{
free (rs);
return FALSE;
}
rs->grabIndex = 0;
rs->state = RingStateNone;
rs->windows = NULL;
rs->drawSlots = NULL;
rs->windowsSize = 0;
rs->paintingSwitcher = FALSE;
rs->selectedWindow = None;
rs->moreAdjust = FALSE;
rs->rotateAdjust = FALSE;
rs->rotAdjust = 0;
rs->rVelocity = 0;
rs->textPixmap = None;
matchInit (&rs->match);
WRAP (rs, s, preparePaintScreen, ringPreparePaintScreen);
WRAP (rs, s, donePaintScreen, ringDonePaintScreen);
WRAP (rs, s, paintOutput, ringPaintOutput);
WRAP (rs, s, paintWindow, ringPaintWindow);
WRAP (rs, s, damageWindowRect, ringDamageWindowRect);
rs->cursor = XCreateFontCursor (s->display->display, XC_left_ptr);
s->privates[rd->screenPrivateIndex].ptr = rs;
return TRUE;
}
static void
ringFiniScreen (CompPlugin *p,
CompScreen *s)
{
RING_SCREEN (s);
freeWindowPrivateIndex (s, rs->windowPrivateIndex);
UNWRAP (rs, s, preparePaintScreen);
UNWRAP (rs, s, donePaintScreen);
UNWRAP (rs, s, paintOutput);
UNWRAP (rs, s, paintWindow);
UNWRAP (rs, s, damageWindowRect);
matchFini (&rs->match);
ringFreeWindowTitle (s);
XFreeCursor (s->display->display, rs->cursor);
if (rs->windows)
free (rs->windows);
if (rs->drawSlots)
free (rs->drawSlots);
free (rs);
}
static Bool
ringInitWindow (CompPlugin *p,
CompWindow *w)
{
RingWindow *rw;
RING_SCREEN (w->screen);
rw = malloc (sizeof (RingWindow));
if (!rw)
return FALSE;
rw->slot = 0;
rw->scale = 1.0f;
rw->tx = rw->ty = 0.0f;
rw->adjust = FALSE;
rw->xVelocity = rw->yVelocity = 0.0f;
rw->scaleVelocity = 0.0f;
w->privates[rs->windowPrivateIndex].ptr = rw;
return TRUE;
}
static void
ringFiniWindow (CompPlugin *p,
CompWindow *w)
{
RING_WINDOW (w);
free (rw);
}
static Bool
ringInit (CompPlugin *p)
{
displayPrivateIndex = allocateDisplayPrivateIndex ();
if (displayPrivateIndex < 0)
return FALSE;
return TRUE;
}
static void
ringFini (CompPlugin *p)
{
if (displayPrivateIndex >= 0)
freeDisplayPrivateIndex (displayPrivateIndex);
}
static int
ringGetVersion (CompPlugin *plugin,
int version)
{
return ABIVERSION;
}
CompPluginVTable ringVTable = {
"ring",
ringGetVersion,
0,
ringInit,
ringFini,
ringInitDisplay,
ringFiniDisplay,
ringInitScreen,
ringFiniScreen,
ringInitWindow,
ringFiniWindow,
0,
0,
0,
0
};
CompPluginVTable *
getCompPluginInfo (void)
{
return &ringVTable;
}
syntax highlighted by Code2HTML, v. 0.9.1