[prev in list] [next in list] [prev in thread] [next in thread]
List: freedesktop-compiz
Subject: Re: [compiz] window walking interface
From: Danny Baumann <dannybaumann () web ! de>
Date: 2007-06-12 12:52:53
Message-ID: 1181652773.30401.4.camel () rechenknecht ! peppercon ! de
[Download RAW message or body]
Hi,
> > Thanks for putting these changes in git, it indeed makes more sense
> > than the way I did it first.
> > I re-made the transparent cube patches to work now with this new interface.
> > Tell me what you think about it.
I re-did Roi's patches for latest cube plugin changes and coding style.
Attached are the following:
> 0001-cube-painting-order.txt:
>
> This is good to go in if you're able to help fix any regressions that
> may appear. There's some coding style changes that need to be made
> before I can let this go in, though.
>
> Please replace all C++ comments with C comments, keep declarations at
> the beginning of each scope and make sure lines are no longer than 80
> columns.
This one is replaced by
0001-Improve-cube-paint-order-calculations.patch.
> 0002-plugin-events.txt:
>
> This is not OK. Plugin events should not be used at all now that 3d and
> rotate plugins can hook into the cube plugin directly. Just add whatever
> hooks that are appropriate to the cube plugin.
Replaced by 0002-Added-rotation-state.patch. With that patch, there is a
rotation state variable in CubeScreen that is written by the plugin
rotating it, rotate in the current case.
> 0003-transparent-cube.txt:
>
> Looks OK. The same coding style changes need to be made here too,
> though.
Replaced by 0003-Add-cube-transparency.patch (which adds the cube
transparency) and
0004-Added-option-to-enable-cube-transparency-only-on-but.patch (option
to enable transparency only on 'manual' rotation).
Are these patches ok to go in?
Regards,
Danny
["0001-Improve-cube-paint-order-calculations.patch" (0001-Improve-cube-paint-order-calculations.patch)]
From 6375fa5c10b4373bbdcc84492defec56a4f58121 Mon Sep 17 00:00:00 2001
From: Roi Cohen <roico.beryl@gmail.com>
Date: Tue, 12 Jun 2007 14:45:30 +0200
Subject: [PATCH] Improve cube paint order calculations.
---
include/cube.h | 15 ++-
plugins/cube.c | 453 +++++++++++++++++++++++++++++++++++++-------------------
2 files changed, 316 insertions(+), 152 deletions(-)
diff --git a/include/cube.h b/include/cube.h
index 29a2fa3..00e5b47 100644
--- a/include/cube.h
+++ b/include/cube.h
@@ -25,7 +25,7 @@
#include <compiz.h>
-#define CUBE_ABIVERSION 20070611
+#define CUBE_ABIVERSION 20070612
#define CUBE_DISPLAY_OPTION_ABI 0
#define CUBE_DISPLAY_OPTION_INDEX 1
@@ -71,6 +71,12 @@ typedef void (*CubePaintTopBottomProc) (CompScreen *s,
CompOutput *output,
int size);
+typedef enum _PaintOrder
+{
+ BTF = 0,
+ FTB
+} PaintOrder;
+
typedef struct _CubeScreen {
PreparePaintScreenProc preparePaintScreen;
DonePaintScreenProc donePaintScreen;
@@ -80,6 +86,7 @@ typedef struct _CubeScreen {
ApplyScreenTransformProc applyScreenTransform;
SetScreenOptionProc setScreenOption;
OutputChangeNotifyProc outputChangeNotify;
+ InitWindowWalkerProc initWindowWalker;
CubeGetRotationProc getRotation;
CubeClearTargetOutputProc clearTargetOutput;
@@ -87,8 +94,10 @@ typedef struct _CubeScreen {
CompOption opt[CUBE_SCREEN_OPTION_NUM];
- int invert;
- int xRotations;
+ int invert;
+ int xRotations;
+ PaintOrder paintOrder;
+
GLfloat distance;
GLushort color[3];
GLfloat tc[12];
diff --git a/plugins/cube.c b/plugins/cube.c
index 2252a24..7c4df03 100644
--- a/plugins/cube.c
+++ b/plugins/cube.c
@@ -32,6 +32,37 @@
#include <cube.h>
+#define MULTM(x, y, z) \
+z[0] = x[0] * y[0] + x[4] * y[1] + x[8] * y[2] + x[12] * y[3]; \
+z[1] = x[1] * y[0] + x[5] * y[1] + x[9] * y[2] + x[13] * y[3]; \
+z[2] = x[2] * y[0] + x[6] * y[1] + x[10] * y[2] + x[14] * y[3]; \
+z[3] = x[3] * y[0] + x[7] * y[1] + x[11] * y[2] + x[15] * y[3]; \
+z[4] = x[0] * y[4] + x[4] * y[5] + x[8] * y[6] + x[12] * y[7]; \
+z[5] = x[1] * y[4] + x[5] * y[5] + x[9] * y[6] + x[13] * y[7]; \
+z[6] = x[2] * y[4] + x[6] * y[5] + x[10] * y[6] + x[14] * y[7]; \
+z[7] = x[3] * y[4] + x[7] * y[5] + x[11] * y[6] + x[15] * y[7]; \
+z[8] = x[0] * y[8] + x[4] * y[9] + x[8] * y[10] + x[12] * y[11]; \
+z[9] = x[1] * y[8] + x[5] * y[9] + x[9] * y[10] + x[13] * y[11]; \
+z[10] = x[2] * y[8] + x[6] * y[9] + x[10] * y[10] + x[14] * y[11]; \
+z[11] = x[3] * y[8] + x[7] * y[9] + x[11] * y[10] + x[15] * y[11]; \
+z[12] = x[0] * y[12] + x[4] * y[13] + x[8] * y[14] + x[12] * y[15]; \
+z[13] = x[1] * y[12] + x[5] * y[13] + x[9] * y[14] + x[13] * y[15]; \
+z[14] = x[2] * y[12] + x[6] * y[13] + x[10] * y[14] + x[14] * y[15]; \
+z[15] = x[3] * y[12] + x[7] * y[13] + x[11] * y[14] + x[15] * y[15];
+
+#define MULTMV(m, v) { \
+float v0 = m[0]*v[0] + m[4]*v[1] + m[8]*v[2] + m[12]*v[3]; \
+float v1 = m[1]*v[0] + m[5]*v[1] + m[9]*v[2] + m[13]*v[3]; \
+float v2 = m[2]*v[0] + m[6]*v[1] + m[10]*v[2] + m[14]*v[3]; \
+float v3 = m[3]*v[0] + m[7]*v[1] + m[11]*v[2] + m[15]*v[3]; \
+v[0] = v0; v[1] = v1; v[2] = v2; v[3] = v3; }
+
+#define DIVV(v) \
+v[0] /= v[3]; \
+v[1] /= v[3]; \
+v[2] /= v[3]; \
+v[3] /= v[3];
+
static CompMetadata cubeMetadata;
static int cubeDisplayPrivateIndex;
@@ -808,7 +839,6 @@ cubePreparePaintScreen (CompScreen *s,
UNWRAP (cs, s, preparePaintScreen);
(*s->preparePaintScreen) (s, msSinceLastPaint);
WRAP (cs, s, preparePaintScreen, cubePreparePaintScreen);
-
}
static Bool
@@ -830,6 +860,8 @@ cubePaintOutput (CompScreen *s,
}
cs->srcOutput = (output->id != ~0) ? output->id : 0;
+ /* Always use BTF painting on non-transformed screen */
+ cs->paintOrder = BTF;
UNWRAP (cs, s, paintOutput);
status = (*s->paintOutput) (s, sAttrib, transform, region, output, mask);
@@ -851,18 +883,92 @@ cubeDonePaintScreen (CompScreen *s)
WRAP (cs, s, donePaintScreen, cubeDonePaintScreen);
}
+static Bool
+cubeCheckFTB (CompScreen *s,
+ const ScreenPaintAttrib *sAttrib,
+ const CompTransform *transform,
+ CompOutput *outputPtr)
+{
+ CompTransform sTransform = *transform;
+ float mvp[16];
+ float pntA[4], pntB[4], pntC[4];
+ float vecA[3], vecB[3];
+ float ortho[3];
+
+ (*s->applyScreenTransform) (s, sAttrib, outputPtr, &sTransform);
+ transformToScreenSpace (s, outputPtr, -sAttrib->zTranslate, &sTransform);
+
+ MULTM (s->projection, sTransform.m, mvp);
+
+ pntA[0] = outputPtr->region.extents.x1;
+ pntA[1] = outputPtr->region.extents.y1,
+ pntA[2] = 0.0f;
+ pntA[3] = 1.0f;
+
+ pntB[0] = outputPtr->region.extents.x2;
+ pntB[1] = outputPtr->region.extents.y1;
+ pntB[2] = 0.0f;
+ pntB[3] = 1.0f;
+
+ pntC[0] = outputPtr->region.extents.x1 + outputPtr->width / 2.0f;
+ pntC[1] = outputPtr->region.extents.y1 + outputPtr->height / 2.0f;
+ pntC[2] = 0.0f;
+ pntC[3] = 1.0f;
+
+ MULTMV (mvp, pntA);
+ DIVV (pntA);
+
+ MULTMV (mvp, pntB);
+ DIVV (pntB);
+
+ MULTMV (mvp, pntC);
+ DIVV (pntC);
+
+ vecA[0] = pntC[0] - pntA[0];
+ vecA[1] = pntC[1] - pntA[1];
+ vecA[2] = pntC[2] - pntA[2];
+
+ vecB[0] = pntC[0] - pntB[0];
+ vecB[1] = pntC[1] - pntB[1];
+ vecB[2] = pntC[2] - pntB[2];
+
+ ortho[0] = vecA[1] * vecB[2] - vecA[2] * vecB[1];
+ ortho[1] = vecA[2] * vecB[0] - vecA[0] * vecB[2];
+ ortho[2] = vecA[0] * vecB[1] - vecA[1] * vecB[0];
+
+ if (ortho[2] > 0.0f)
+ {
+ /* The viewport is reversed, should be painted front to back. */
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
static void
cubeMoveViewportAndPaint (CompScreen *s,
const ScreenPaintAttrib *sAttrib,
const CompTransform *transform,
CompOutput *outputPtr,
unsigned int mask,
+ PaintOrder paintOrder,
int dx)
{
- int output = (outputPtr->id != ~0) ? outputPtr->id : 0;
+ Bool ftb;
+ int output;
CUBE_SCREEN (s);
+ ftb = cubeCheckFTB (s, sAttrib, transform, outputPtr);
+
+ if ((paintOrder == FTB && !ftb) ||
+ (paintOrder == BTF && ftb))
+ return;
+
+ output = (outputPtr->id != ~0) ? outputPtr->id : 0;
+
+ cs->paintOrder = paintOrder;
+
if (cs->nOutput > 1)
{
int cubeOutput, dView;
@@ -903,6 +1009,108 @@ cubeMoveViewportAndPaint (CompScreen *s,
}
static void
+cubePaintAllViewports (CompScreen *s,
+ ScreenPaintAttrib *sAttrib,
+ const CompTransform *transform,
+ Region region,
+ CompOutput *outputPtr,
+ unsigned int mask,
+ int xMove,
+ float size,
+ int hsize,
+ PaintOrder paintOrder)
+{
+ CUBE_SCREEN(s);
+
+ ScreenPaintAttrib sa = *sAttrib;
+
+ int i;
+ int xMoveAdd;
+ int origXMoveAdd = 0; /* dx for the viewport we start
+ painting with (back-most). */
+ int iFirstSign; /* 1 if we do xMove += i first and
+ -1 if we do xMove -= i first. */
+
+ if (cs->invert == 1)
+ {
+ /* xMove ==> dx for the viewport which is the
+ nearest to the viewer in z axis.
+ xMove +/- hsize / 2 ==> dx for the viewport
+ which is the farthest to the viewer in z axis. */
+
+ if ((sa.xRotate < 0.0f && hsize % 2 == 1) ||
+ (sa.xRotate > 0.0f && hsize % 2 == 0))
+ {
+ origXMoveAdd = hsize / 2;
+ iFirstSign = 1;
+ }
+ else
+ {
+ origXMoveAdd = -hsize / 2;
+ iFirstSign = -1;
+ }
+ }
+ else
+ {
+ /* xMove is already the dx for farthest viewport. */
+ if (sa.xRotate > 0.0f)
+ iFirstSign = -1;
+ else
+ iFirstSign = 1;
+ }
+
+ for (i = 0; i <= hsize / 2; i++)
+ {
+ /* move to the correct viewport (back to front). */
+ xMoveAdd = origXMoveAdd; /* move to farthest viewport. */
+ xMoveAdd += iFirstSign * i; /* move i more viewports to
+ the right / left. */
+
+ /* Needed especially for unfold.
+ We paint the viewports around xMove viewport.
+ Adding or subtracting hsize from xMove has no effect on
+ what viewport we paint, but can make shorter paths. */
+ if (xMoveAdd < -hsize / 2)
+ xMoveAdd += hsize;
+ else if (xMoveAdd > hsize / 2)
+ xMoveAdd -= hsize;
+
+ /* Paint the viewport. */
+ xMove += xMoveAdd;
+
+ sa.yRotate -= cs->invert * xMoveAdd * 360.0f / size;
+ cubeMoveViewportAndPaint (s, &sa, transform, outputPtr, mask,
+ paintOrder, xMove);
+ sa.yRotate += cs->invert * xMoveAdd * 360.0f / size;
+
+ xMove -= xMoveAdd;
+
+ /* do the same for an equally far viewport. */
+ if (i == 0 || i * 2 == hsize)
+ continue;
+
+ xMoveAdd = origXMoveAdd; /* move to farthest viewport. */
+ xMoveAdd -= iFirstSign * i; /* move i more viewports to the
+ left / right (opposite side
+ from the one chosen first) */
+
+ if (xMoveAdd < -hsize / 2)
+ xMoveAdd += hsize;
+ else if (xMoveAdd > hsize / 2)
+ xMoveAdd -= hsize;
+
+ xMove += xMoveAdd;
+
+ sa.yRotate -= cs->invert * xMoveAdd * 360.0f / size;
+ cubeMoveViewportAndPaint (s, &sa, transform, outputPtr, mask,
+ paintOrder, xMove);
+ sa.yRotate += cs->invert * xMoveAdd * 360.0f / size;
+
+ xMove -= xMoveAdd;
+ }
+}
+
+static void
cubeGetRotation (CompScreen *s,
float *x,
float *v)
@@ -953,6 +1161,7 @@ cubePaintTopBottom (CompScreen *s,
{
ScreenPaintAttrib sa = *sAttrib;
CompTransform sTransform = *transform;
+ int i;
CUBE_SCREEN (s);
@@ -974,26 +1183,34 @@ cubePaintTopBottom (CompScreen *s,
glVertexPointer (3, GL_FLOAT, 0, cs->vertices);
- glNormal3f (0.0f, -1.0f, 0.0f);
-
- if (cs->invert == 1 && size == 4 && cs->texture.name)
- {
- enableTexture (s, &cs->texture, COMP_TEXTURE_FILTER_GOOD);
- glTexCoordPointer (2, GL_FLOAT, 0, cs->tc);
- glDrawArrays (GL_TRIANGLE_FAN, 0, cs->nVertices >> 1);
- disableTexture (s, &cs->texture);
- glDisableClientState (GL_TEXTURE_COORD_ARRAY);
- }
- else
+ for (i = 0; i < 2; i++)
{
- glDisableClientState (GL_TEXTURE_COORD_ARRAY);
- glDrawArrays (GL_TRIANGLE_FAN, 0, cs->nVertices >> 1);
+ if ((i == 0 && sAttrib->vRotate <= 0.0f) ||
+ (i == 1 && sAttrib->vRotate > 0.0f))
+ {
+ glNormal3f (0.0f, -1.0f, 0.0f);
+ if (cs->invert == 1 && size == 4 && cs->texture.name)
+ {
+ enableTexture (s, &cs->texture, COMP_TEXTURE_FILTER_GOOD);
+ glTexCoordPointer (2, GL_FLOAT, 0, cs->tc);
+ glDrawArrays (GL_TRIANGLE_FAN, 0, cs->nVertices >> 1);
+ disableTexture (s, &cs->texture);
+ glDisableClientState (GL_TEXTURE_COORD_ARRAY);
+ }
+ else
+ {
+ glDisableClientState (GL_TEXTURE_COORD_ARRAY);
+ glDrawArrays (GL_TRIANGLE_FAN, 0, cs->nVertices >> 1);
+ }
+ }
+ else
+ {
+ glNormal3f (0.0f, 1.0f, 0.0f);
+ glDrawArrays (GL_TRIANGLE_FAN, cs->nVertices >> 1,
+ cs->nVertices >> 1);
+ }
}
- glNormal3f (0.0f, 1.0f, 0.0f);
-
- glDrawArrays (GL_TRIANGLE_FAN, cs->nVertices >> 1, cs->nVertices >> 1);
-
glNormal3f (0.0f, 0.0f, -1.0f);
glPopMatrix ();
@@ -1014,6 +1231,8 @@ cubePaintTransformedOutput (CompScreen *s,
float xRotate, vRotate;
int hsize, xMove = 0;
float size;
+ GLenum filter = s->display->textureFilter;
+ PaintOrder paintOrder;
Bool clear;
int output = 0;
@@ -1065,9 +1284,6 @@ cubePaintTransformedOutput (CompScreen *s,
UNWRAP (cs, s, paintTransformedOutput);
- sa.xTranslate = sAttrib->xTranslate;
- sa.yTranslate = sAttrib->yTranslate;
-
if (cs->grabIndex)
{
sa.vRotate = 0.0f;
@@ -1081,22 +1297,6 @@ cubePaintTransformedOutput (CompScreen *s,
/* distance we move the camera back when unfolding the cube.
currently hardcoded to 1.5 but it should probably be optional. */
sa.zCamera -= cs->unfold * 1.5f;
-
- sa.xRotate = xRotate * cs->invert;
- if (sa.xRotate > 0.0f)
- {
- cs->xRotations = (int) (hsize * sa.xRotate) / 360;
- sa.xRotate = sa.xRotate - (360.0f * cs->xRotations) / hsize;
- }
- else
- {
- cs->xRotations = (int) (hsize * sa.xRotate) / 360;
- sa.xRotate = sa.xRotate -
- (360.0f * cs->xRotations) / hsize + 360.0f / hsize;
- cs->xRotations--;
- }
-
- sa.xRotate = sa.xRotate / size * hsize;
}
else
{
@@ -1108,132 +1308,63 @@ cubePaintTransformedOutput (CompScreen *s,
sa.vRotate = vRotate;
sa.zTranslate = -cs->invert * cs->distance;
- sa.xRotate = xRotate * cs->invert;
- if (sa.xRotate > 0.0f)
- {
- cs->xRotations = (int) (size * sa.xRotate) / 360;
- sa.xRotate = sa.xRotate - (360.0f * cs->xRotations) / size;
- }
- else
- {
- cs->xRotations = (int) (size * sa.xRotate) / 360;
- sa.xRotate = sa.xRotate -
- (360.0f * cs->xRotations) / size + 360.0f / size;
- cs->xRotations--;
- }
- }
-
- if (cs->grabIndex == 0 && hsize > 2 &&
- (cs->invert != 1 || sa.vRotate != 0.0f || sa.yTranslate != 0.0f))
- {
- (*cs->paintTopBottom) (s, &sa, transform, outputPtr, hsize);
}
- /* outside cube */
- if (cs->invert == 1)
- {
- if (cs->grabIndex || hsize > 4)
- {
- GLenum filter;
- int i;
-
- xMove = cs->xRotations - ((hsize >> 1) - 1);
- sa.yRotate += (360.0f / size) * ((hsize >> 1) - 1);
-
- filter = s->display->textureFilter;
- if (cs->grabIndex && cs->opt[CUBE_SCREEN_OPTION_MIPMAP].value.b)
- s->display->textureFilter = GL_LINEAR_MIPMAP_LINEAR;
-
- for (i = 0; i < hsize; i++)
- {
- cubeMoveViewportAndPaint (s, &sa, transform, outputPtr, mask,
- xMove);
-
- sa.yRotate -= 360.0f / size;
- xMove++;
- }
+ if (sa.xRotate > 0.0f)
+ cs->xRotations = (int) (hsize * sa.xRotate + 180.0f) / 360.0f;
+ else
+ cs->xRotations = (int) (hsize * sa.xRotate - 180.0f) / 360.0f;
- s->display->textureFilter = filter;
- }
- else
- {
- if (xRotate != 0.0f)
- {
- xMove = cs->xRotations;
+ sa.xRotate -= (360.0f * cs->xRotations) / hsize;
+ sa.xRotate *= cs->invert;
- cubeMoveViewportAndPaint (s, &sa, transform, outputPtr, mask,
- xMove);
+ sa.xRotate = sa.xRotate / size * hsize;
- xMove++;
- }
+ xMove = cs->xRotations;
- sa.yRotate -= 360.0f / size;
+ if (cs->grabIndex && cs->opt[CUBE_SCREEN_OPTION_MIPMAP].value.b)
+ s->display->textureFilter = GL_LINEAR_MIPMAP_LINEAR;
- cubeMoveViewportAndPaint (s, &sa, transform, outputPtr, mask,
- xMove);
- }
+ if (cs->invert == 1)
+ {
+ /* Outside cube - start with FTB faces */
+ paintOrder = FTB;
}
else
{
- if (sa.xRotate > 180.0f / size)
- {
- sa.yRotate -= 360.0f / size;
- cs->xRotations++;
- }
-
- sa.yRotate -= 360.0f / size;
- xMove = -1 - cs->xRotations;
-
- if (cs->grabIndex)
- {
- GLenum filter;
- int i;
-
- filter = s->display->textureFilter;
- if (cs->opt[CUBE_SCREEN_OPTION_MIPMAP].value.b)
- s->display->textureFilter = GL_LINEAR_MIPMAP_LINEAR;
-
- if (sa.xRotate > 180.0f / size)
- {
- xMove -= ((hsize >> 1) - 2);
- sa.yRotate -= (360.0f / size) * ((hsize >> 1) - 2);
- }
- else
- {
- xMove -= ((hsize >> 1) - 1);
- sa.yRotate -= (360.0f / size) * ((hsize >> 1) - 1);
- }
-
- for (i = 0; i < hsize; i++)
- {
- cubeMoveViewportAndPaint (s, &sa, transform, outputPtr, mask,
- xMove);
-
- sa.yRotate += 360.0f / size;
- xMove++;
- }
-
- s->display->textureFilter = filter;
- }
- else
- {
- cubeMoveViewportAndPaint (s, &sa, transform, outputPtr, mask,
- xMove);
-
- sa.yRotate += 360.0f / size;
- xMove = -cs->xRotations;
+ /* Inside cube - start with BTF faces */
+ paintOrder = BTF;
+ }
- cubeMoveViewportAndPaint (s, &sa, transform, outputPtr, mask,
- xMove);
+ if (cs->invert == -1)
+ cubePaintAllViewports (s, &sa,transform, region,
+ outputPtr, mask, xMove,
+ size, hsize, paintOrder);
- sa.yRotate += 360.0f / size;
- xMove = 1 - cs->xRotations;
+ if (cs->grabIndex == 0 && hsize > 2 &&
+ (cs->invert != 1 || sa.vRotate != 0.0f || sa.yTranslate != 0.0f))
+ {
+ (*cs->paintTopBottom) (s, &sa, transform, outputPtr, hsize);
+ }
- cubeMoveViewportAndPaint (s, &sa, transform, outputPtr, mask,
- xMove);
- }
+ if (cs->invert == 1)
+ {
+ /* Outside cube - continue with BTF faces */
+ paintOrder = BTF;
+ }
+ else
+ {
+ /* Inside cube - continue with FTB faces */
+ paintOrder = FTB;
}
+ if (cs->invert == 1)
+ cubePaintAllViewports (s, &sa, transform, region,
+ outputPtr, mask, xMove,
+ size, hsize, paintOrder);
+
+ s->display->textureFilter = filter;
+
WRAP (cs, s, paintTransformedOutput, cubePaintTransformedOutput);
}
@@ -1338,6 +1469,28 @@ cubePaintBackground (CompScreen *s,
}
static void
+cubeInitWindowWalker (CompScreen *s, CompWalker* walker)
+{
+ CUBE_SCREEN (s);
+
+ UNWRAP (cs, s, initWindowWalker);
+ (*s->initWindowWalker) (s, walker);
+ WRAP (cs, s, initWindowWalker, cubeInitWindowWalker);
+
+ if (cs->paintOrder == FTB)
+ {
+ WalkInitProc tmpInit = walker->first;
+ WalkStepProc tmpStep = walker->next;
+
+ walker->first = walker->last;
+ walker->last = tmpInit;
+
+ walker->next = walker->prev;
+ walker->prev = tmpStep;
+ }
+}
+
+static void
cubeApplyScreenTransform (CompScreen *s,
const ScreenPaintAttrib *sAttrib,
CompOutput *output,
@@ -1731,6 +1884,7 @@ cubeInitScreen (CompPlugin *p,
WRAP (cs, s, applyScreenTransform, cubeApplyScreenTransform);
WRAP (cs, s, setScreenOption, cubeSetGlobalScreenOption);
WRAP (cs, s, outputChangeNotify, cubeOutputChangeNotify);
+ WRAP (cs, s, initWindowWalker, cubeInitWindowWalker);
return TRUE;
}
@@ -1752,6 +1906,7 @@ cubeFiniScreen (CompPlugin *p,
UNWRAP (cs, s, applyScreenTransform);
UNWRAP (cs, s, setScreenOption);
UNWRAP (cs, s, outputChangeNotify);
+ UNWRAP (cs, s, initWindowWalker);
finiTexture (s, &cs->texture);
finiTexture (s, &cs->sky);
--
1.5.0.6
["0002-Added-rotation-state.patch" (0002-Added-rotation-state.patch)]
From dd331aff9965f60de6427d62f71033a78d51a814 Mon Sep 17 00:00:00 2001
From: Danny Baumann <dannybaumann@web.de>
Date: Tue, 12 Jun 2007 14:12:07 +0200
Subject: [PATCH] Added rotation state.
This variable should be set appropriately by plugins rotating the cube.
---
include/cube.h | 9 +++++++++
plugins/cube.c | 2 ++
plugins/rotate.c | 12 ++++++++++++
3 files changed, 23 insertions(+), 0 deletions(-)
diff --git a/include/cube.h b/include/cube.h
index 00e5b47..4010b6b 100644
--- a/include/cube.h
+++ b/include/cube.h
@@ -77,6 +77,13 @@ typedef enum _PaintOrder
FTB
} PaintOrder;
+typedef enum _RotationState
+{
+ RotationNone = 0,
+ RotationChange,
+ RotationManual
+} RotationState;
+
typedef struct _CubeScreen {
PreparePaintScreenProc preparePaintScreen;
DonePaintScreenProc donePaintScreen;
@@ -98,6 +105,8 @@ typedef struct _CubeScreen {
int xRotations;
PaintOrder paintOrder;
+ RotationState rotationState;
+
GLfloat distance;
GLushort color[3];
GLfloat tc[12];
diff --git a/plugins/cube.c b/plugins/cube.c
index 7c4df03..0162e0d 100644
--- a/plugins/cube.c
+++ b/plugins/cube.c
@@ -1859,6 +1859,8 @@ cubeInitScreen (CompPlugin *p,
cs->bg = NULL;
cs->nBg = 0;
+ cs->rotationState = RotationNone;
+
memset (cs->cleared, 0, sizeof (cs->cleared));
cubeUpdateOutputs (s);
diff --git a/plugins/rotate.c b/plugins/rotate.c
index 7aecd56..fdd9442 100644
--- a/plugins/rotate.c
+++ b/plugins/rotate.c
@@ -346,6 +346,9 @@ rotatePreparePaintScreen (CompScreen *s,
else
tx = (s->hsize * xrot / 360.0f) + 0.5f;
+ /* flag end of rotation */
+ cs->rotationState = RotationNone;
+
moveScreenViewport (s, tx, 0, TRUE);
rs->xrot = 0.0f;
@@ -559,6 +562,7 @@ rotateInitiate (CompDisplay *d,
if (s)
{
ROTATE_SCREEN (s);
+ CUBE_SCREEN (s);
if (s->hsize < 2)
return FALSE;
@@ -577,6 +581,14 @@ rotateInitiate (CompDisplay *d,
rs->moving = FALSE;
rs->slow = FALSE;
+ /* Set the rotation state for cube - if action is non-NULL,
+ we set it to manual (as we were called from the 'Initiate
+ Rotation' binding. Otherwise, we set to to Change. */
+ if (action)
+ cs->rotationState = RotationManual;
+ else
+ cs->rotationState = RotationChange;
+
if (!rs->grabIndex)
{
rs->grabIndex = pushScreenGrab (s, s->invisibleCursor, "rotate");
--
1.5.0.6
["0003-Add-cube-transparency.patch" (0003-Add-cube-transparency.patch)]
From db3ec1141b355af5fdca2748c235cd720e33589a Mon Sep 17 00:00:00 2001
From: Roi Cohen <roico.beryl@gmail.com>
Date: Tue, 12 Jun 2007 14:46:10 +0200
Subject: [PATCH] Add cube transparency.
---
include/cube.h | 39 ++++++++-----
metadata/cube.xml.in | 24 ++++++++
plugins/cube.c | 153 +++++++++++++++++++++++++++++++++++++++++++++++--
3 files changed, 193 insertions(+), 23 deletions(-)
diff --git a/include/cube.h b/include/cube.h
index 4010b6b..24ad28d 100644
--- a/include/cube.h
+++ b/include/cube.h
@@ -40,22 +40,25 @@ typedef struct _CubeDisplay {
CompOption opt[CUBE_DISPLAY_OPTION_NUM];
} CubeDisplay;
-#define CUBE_SCREEN_OPTION_COLOR 0
-#define CUBE_SCREEN_OPTION_IN 1
-#define CUBE_SCREEN_OPTION_SCALE_IMAGE 2
-#define CUBE_SCREEN_OPTION_IMAGES 3
-#define CUBE_SCREEN_OPTION_SKYDOME 4
-#define CUBE_SCREEN_OPTION_SKYDOME_IMG 5
-#define CUBE_SCREEN_OPTION_SKYDOME_ANIM 6
-#define CUBE_SCREEN_OPTION_SKYDOME_GRAD_START 7
-#define CUBE_SCREEN_OPTION_SKYDOME_GRAD_END 8
-#define CUBE_SCREEN_OPTION_ACCELERATION 9
-#define CUBE_SCREEN_OPTION_SPEED 10
-#define CUBE_SCREEN_OPTION_TIMESTEP 11
-#define CUBE_SCREEN_OPTION_MIPMAP 12
-#define CUBE_SCREEN_OPTION_BACKGROUNDS 13
-#define CUBE_SCREEN_OPTION_ADJUST_IMAGE 14
-#define CUBE_SCREEN_OPTION_NUM 15
+#define CUBE_SCREEN_OPTION_COLOR 0
+#define CUBE_SCREEN_OPTION_IN 1
+#define CUBE_SCREEN_OPTION_SCALE_IMAGE 2
+#define CUBE_SCREEN_OPTION_IMAGES 3
+#define CUBE_SCREEN_OPTION_SKYDOME 4
+#define CUBE_SCREEN_OPTION_SKYDOME_IMG 5
+#define CUBE_SCREEN_OPTION_SKYDOME_ANIM 6
+#define CUBE_SCREEN_OPTION_SKYDOME_GRAD_START 7
+#define CUBE_SCREEN_OPTION_SKYDOME_GRAD_END 8
+#define CUBE_SCREEN_OPTION_ACCELERATION 9
+#define CUBE_SCREEN_OPTION_SPEED 10
+#define CUBE_SCREEN_OPTION_TIMESTEP 11
+#define CUBE_SCREEN_OPTION_MIPMAP 12
+#define CUBE_SCREEN_OPTION_BACKGROUNDS 13
+#define CUBE_SCREEN_OPTION_ADJUST_IMAGE 14
+#define CUBE_SCREEN_OPTION_ACTIVE_OPACITY 15
+#define CUBE_SCREEN_OPTION_INACTIVE_OPACITY 16
+#define CUBE_SCREEN_OPTION_FADE_TIME 17
+#define CUBE_SCREEN_OPTION_NUM 18
typedef void (*CubeGetRotationProc) (CompScreen *s,
float *x,
@@ -90,6 +93,7 @@ typedef struct _CubeScreen {
PaintOutputProc paintOutput;
PaintTransformedOutputProc paintTransformedOutput;
PaintBackgroundProc paintBackground;
+ PaintWindowProc paintWindow;
ApplyScreenTransformProc applyScreenTransform;
SetScreenOptionProc setScreenOption;
OutputChangeNotifyProc outputChangeNotify;
@@ -142,6 +146,9 @@ typedef struct _CubeScreen {
float outputXOffset;
float outputYOffset;
+ float desktopOpacity;
+ float toOpacity;
+
CompTexture *bg;
int nBg;
} CubeScreen;
diff --git a/metadata/cube.xml.in b/metadata/cube.xml.in
index 0f674e7..23d93e0 100644
--- a/metadata/cube.xml.in
+++ b/metadata/cube.xml.in
@@ -134,6 +134,30 @@
<_long>Adjust top face image to rotation</_long>
<default>false</default>
</option>
+ <option name="active_opacity" type="float">
+ <_short>Opacity During Rotation</_short>
+ <_long>Opacity of desktop window during rotation.</_long>
+ <default>30.0</default>
+ <min>0.0</min>
+ <max>100.0</max>
+ <precision>1.0</precision>
+ </option>
+ <option name="inactive_opacity" type="float">
+ <_short>Opacity When Not Rotating</_short>
+ <_long>Opacity of desktop window when not rotating.</_long>
+ <default>100.0</default>
+ <min>0.0</min>
+ <max>100.0</max>
+ <precision>1.0</precision>
+ </option>
+ <option name="fade_time" type="float">
+ <_short>Fade Time</_short>
+ <_long>Desktop Window Opacity Fade Time.</_long>
+ <default>1.0</default>
+ <min>0.0</min>
+ <max>10.0</max>
+ <precision>0.1</precision>
+ </option>
</screen>
</plugin>
</compiz>
diff --git a/plugins/cube.c b/plugins/cube.c
index 0162e0d..bdabedc 100644
--- a/plugins/cube.c
+++ b/plugins/cube.c
@@ -798,6 +798,8 @@ static void
cubePreparePaintScreen (CompScreen *s,
int msSinceLastPaint)
{
+ int opt;
+
CUBE_SCREEN (s);
if (cs->grabIndex)
@@ -836,6 +838,35 @@ cubePreparePaintScreen (CompScreen *s,
memset (cs->cleared, 0, sizeof (Bool) * s->nOutputDev);
+ /* Transparency handling */
+ if (cs->rotationState != RotationNone)
+ opt = CUBE_SCREEN_OPTION_ACTIVE_OPACITY;
+ else
+ opt = CUBE_SCREEN_OPTION_INACTIVE_OPACITY;
+
+ cs->toOpacity = (cs->opt[opt].value.f / 100.0f) * OPAQUE;
+
+ if (cs->opt[CUBE_SCREEN_OPTION_FADE_TIME].value.f == 0.0f)
+ cs->desktopOpacity = cs->toOpacity;
+ else if (cs->desktopOpacity != cs->toOpacity)
+ {
+ float steps = (msSinceLastPaint * OPAQUE / 1000.0) /
+ cs->opt[CUBE_SCREEN_OPTION_FADE_TIME].value.f;
+ if (steps < 12)
+ steps = 12;
+
+ if (cs->toOpacity > cs->desktopOpacity)
+ {
+ cs->desktopOpacity += steps;
+ cs->desktopOpacity = MIN (cs->toOpacity, cs->desktopOpacity);
+ }
+ if (cs->toOpacity < cs->desktopOpacity)
+ {
+ cs->desktopOpacity -= steps;
+ cs->desktopOpacity = MAX (cs->toOpacity, cs->desktopOpacity);
+ }
+ }
+
UNWRAP (cs, s, preparePaintScreen);
(*s->preparePaintScreen) (s, msSinceLastPaint);
WRAP (cs, s, preparePaintScreen, cubePreparePaintScreen);
@@ -853,7 +884,7 @@ cubePaintOutput (CompScreen *s,
CUBE_SCREEN (s);
- if (cs->grabIndex)
+ if (cs->grabIndex || cs->desktopOpacity != OPAQUE)
{
mask &= ~PAINT_SCREEN_REGION_MASK;
mask |= PAINT_SCREEN_TRANSFORMED_MASK;
@@ -875,7 +906,7 @@ cubeDonePaintScreen (CompScreen *s)
{
CUBE_SCREEN (s);
- if (cs->grabIndex)
+ if (cs->grabIndex || cs->desktopOpacity != cs->toOpacity)
damageScreen (s);
UNWRAP (cs, s, donePaintScreen);
@@ -1167,7 +1198,7 @@ cubePaintTopBottom (CompScreen *s,
screenLighting (s, TRUE);
- glColor3usv (cs->color);
+ glColor4us (cs->color[0], cs->color[1], cs->color[2], cs->desktopOpacity);
glPushMatrix ();
@@ -1181,6 +1212,13 @@ cubePaintTopBottom (CompScreen *s,
glTranslatef (cs->outputXOffset, -cs->outputYOffset, 0.0f);
glScalef (cs->outputXScale, cs->outputYScale, 1.0f);
+ if (cs->desktopOpacity != OPAQUE)
+ {
+ screenTexEnvMode (s, GL_MODULATE);
+ glEnable (GL_BLEND);
+ glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+
glVertexPointer (3, GL_FLOAT, 0, cs->vertices);
for (i = 0; i < 2; i++)
@@ -1217,6 +1255,10 @@ cubePaintTopBottom (CompScreen *s,
glColor4usv (defaultColor);
glEnableClientState (GL_TEXTURE_COORD_ARRAY);
+
+ screenTexEnvMode (s, GL_REPLACE);
+ glDisable (GL_BLEND);
+ glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
}
static void
@@ -1234,6 +1276,7 @@ cubePaintTransformedOutput (CompScreen *s,
GLenum filter = s->display->textureFilter;
PaintOrder paintOrder;
Bool clear;
+ Bool wasCulled = FALSE;
int output = 0;
CUBE_SCREEN (s);
@@ -1243,6 +1286,13 @@ cubePaintTransformedOutput (CompScreen *s,
hsize = s->hsize * cs->nOutput;
size = hsize;
+ if (cs->desktopOpacity != OPAQUE)
+ {
+ wasCulled = glIsEnabled (GL_CULL_FACE);
+ if (wasCulled)
+ glDisable (GL_CULL_FACE);
+ }
+
if (!cs->fullscreenOutput)
{
cs->outputXScale = (float) s->width / s->outputDev[output].width;
@@ -1336,13 +1386,14 @@ cubePaintTransformedOutput (CompScreen *s,
paintOrder = BTF;
}
- if (cs->invert == -1)
+ if (cs->invert == -1 || cs->desktopOpacity != OPAQUE)
cubePaintAllViewports (s, &sa,transform, region,
outputPtr, mask, xMove,
size, hsize, paintOrder);
if (cs->grabIndex == 0 && hsize > 2 &&
- (cs->invert != 1 || sa.vRotate != 0.0f || sa.yTranslate != 0.0f))
+ (cs->invert != 1 || cs->desktopOpacity != OPAQUE ||
+ sa.vRotate != 0.0f || sa.yTranslate != 0.0f))
{
(*cs->paintTopBottom) (s, &sa, transform, outputPtr, hsize);
}
@@ -1358,17 +1409,64 @@ cubePaintTransformedOutput (CompScreen *s,
paintOrder = FTB;
}
- if (cs->invert == 1)
+ if (cs->invert == 1 || cs->desktopOpacity != OPAQUE)
cubePaintAllViewports (s, &sa, transform, region,
outputPtr, mask, xMove,
size, hsize, paintOrder);
s->display->textureFilter = filter;
+ if (wasCulled)
+ glEnable (GL_CULL_FACE);
+
WRAP (cs, s, paintTransformedOutput, cubePaintTransformedOutput);
}
static void
+cubeSetBackgroundOpacity (CompScreen* s)
+{
+ CUBE_SCREEN (s);
+
+ if (cs->desktopOpacity != OPAQUE)
+ {
+ if (s->desktopWindowCount)
+ {
+ glColor4us (0, 0, 0, 0);
+ glEnable (GL_BLEND);
+ }
+ else
+ {
+ glColor4us (0xffff, 0xffff, 0xffff, cs->desktopOpacity);
+ glEnable (GL_BLEND);
+ glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ }
+ }
+}
+
+static void
+cubeUnSetBackgroundOpacity (CompScreen* s)
+{
+ CUBE_SCREEN (s);
+
+ if (cs->desktopOpacity != OPAQUE)
+ {
+ if (s->desktopWindowCount)
+ {
+ glColor3usv (defaultColor);
+ glDisable (GL_BLEND);
+ }
+ else
+ {
+ glColor3usv (defaultColor);
+ glDisable (GL_BLEND);
+ glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ screenTexEnvMode(s, GL_REPLACE);
+ }
+ }
+}
+
+static void
cubePaintBackground (CompScreen *s,
Region region,
unsigned int mask)
@@ -1377,6 +1475,8 @@ cubePaintBackground (CompScreen *s,
CUBE_SCREEN (s);
+ cubeSetBackgroundOpacity(s);
+
n = cs->opt[CUBE_SCREEN_OPTION_BACKGROUNDS].value.list.nValue;
if (n)
{
@@ -1387,13 +1487,17 @@ cubePaintBackground (CompScreen *s,
GLfloat *d, *data;
if (!nBox)
+ {
+ cubeUnSetBackgroundOpacity(s);
return;
+ }
n = (s->x * cs->nOutput + cs->srcOutput) % n;
if (s->desktopWindowCount)
{
cubeUnloadBackgrounds (s);
+ cubeUnSetBackgroundOpacity(s);
return;
}
else
@@ -1409,7 +1513,10 @@ cubePaintBackground (CompScreen *s,
data = malloc (sizeof (GLfloat) * nBox * 16);
if (!data)
+ {
+ cubeUnSetBackgroundOpacity(s);
return;
+ }
d = data;
n = nBox;
@@ -1466,6 +1573,31 @@ cubePaintBackground (CompScreen *s,
(*s->paintBackground) (s, region, mask);
WRAP (cs, s, paintBackground, cubePaintBackground);
}
+
+ cubeUnSetBackgroundOpacity(s);
+}
+
+static Bool
+cubePaintWindow (CompWindow *w,
+ const WindowPaintAttrib *attrib,
+ const CompTransform *transform,
+ Region region,
+ unsigned int mask)
+{
+ Bool status;
+ CompScreen* s = w->screen;
+ CUBE_SCREEN(s);
+
+ WindowPaintAttrib wa = *attrib;
+
+ if (w->type & CompWindowTypeDesktopMask)
+ wa.opacity = cs->desktopOpacity;
+
+ UNWRAP (cs, s, paintWindow);
+ status = (*s->paintWindow) (w, &wa, transform, region, mask);
+ WRAP (cs, s, paintWindow, cubePaintWindow);
+
+ return status;
}
static void
@@ -1793,7 +1925,10 @@ static const CompMetadataOptionInfo cubeScreenOptionInfo[] = {
{ "timestep", "float", "<min>0.1</min>", 0, 0 },
{ "mipmap", "bool", 0, 0, 0 },
{ "backgrounds", "list", "<type>string</type>", 0, 0 },
- { "adjust_image", "bool", 0, 0, 0 }
+ { "adjust_image", "bool", 0, 0, 0 },
+ { "active_opacity", "float", "<min>0.0</min><max>100.0</max>", 0, 0 },
+ { "inactive_opacity", "float", "<min>0.0</min><max>100.0</max>", 0, 0 },
+ { "fade_time", "float", "<min>0.0</min>", 0, 0 }
};
static Bool
@@ -1861,6 +1996,8 @@ cubeInitScreen (CompPlugin *p,
cs->rotationState = RotationNone;
+ cs->desktopOpacity = OPAQUE;
+
memset (cs->cleared, 0, sizeof (cs->cleared));
cubeUpdateOutputs (s);
@@ -1883,6 +2020,7 @@ cubeInitScreen (CompPlugin *p,
WRAP (cs, s, paintOutput, cubePaintOutput);
WRAP (cs, s, paintTransformedOutput, cubePaintTransformedOutput);
WRAP (cs, s, paintBackground, cubePaintBackground);
+ WRAP (cs, s, paintWindow, cubePaintWindow);
WRAP (cs, s, applyScreenTransform, cubeApplyScreenTransform);
WRAP (cs, s, setScreenOption, cubeSetGlobalScreenOption);
WRAP (cs, s, outputChangeNotify, cubeOutputChangeNotify);
@@ -1905,6 +2043,7 @@ cubeFiniScreen (CompPlugin *p,
UNWRAP (cs, s, paintOutput);
UNWRAP (cs, s, paintTransformedOutput);
UNWRAP (cs, s, paintBackground);
+ UNWRAP (cs, s, paintWindow);
UNWRAP (cs, s, applyScreenTransform);
UNWRAP (cs, s, setScreenOption);
UNWRAP (cs, s, outputChangeNotify);
--
1.5.0.6
["0004-Added-option-to-enable-cube-transparency-only-on-but.patch" (0004-Added-option-to-enable-cube-transparency-only-on-but.patch)]
From d80190a673316ccced1c62179715d56bbe1e84c9 Mon Sep 17 00:00:00 2001
From: Roi Cohen <roico.beryl@gmail.com>
Date: Tue, 12 Jun 2007 14:25:06 +0200
Subject: [PATCH] Added option to enable cube transparency only on button binding initiated rotatation.
---
include/cube.h | 3 ++-
metadata/cube.xml.in | 5 +++++
plugins/cube.c | 11 +++++++++--
3 files changed, 16 insertions(+), 3 deletions(-)
diff --git a/include/cube.h b/include/cube.h
index 24ad28d..a8baf1f 100644
--- a/include/cube.h
+++ b/include/cube.h
@@ -58,7 +58,8 @@ typedef struct _CubeDisplay {
#define CUBE_SCREEN_OPTION_ACTIVE_OPACITY 15
#define CUBE_SCREEN_OPTION_INACTIVE_OPACITY 16
#define CUBE_SCREEN_OPTION_FADE_TIME 17
-#define CUBE_SCREEN_OPTION_NUM 18
+#define CUBE_SCREEN_OPTION_TRANSPARENT_MANUAL_ONLY 18
+#define CUBE_SCREEN_OPTION_NUM 19
typedef void (*CubeGetRotationProc) (CompScreen *s,
float *x,
diff --git a/metadata/cube.xml.in b/metadata/cube.xml.in
index 23d93e0..7405531 100644
--- a/metadata/cube.xml.in
+++ b/metadata/cube.xml.in
@@ -158,6 +158,11 @@
<max>10.0</max>
<precision>0.1</precision>
</option>
+ <option name="transparent_manual_only" type="bool">
+ <_short>Transparency Only on Mouse Rotate</_short>
+ <_long>Initiates Cube transparency only if rotation is mouse driven.</_long>
+ <default>true</default>
+ </option>
</screen>
</plugin>
</compiz>
diff --git a/plugins/cube.c b/plugins/cube.c
index bdabedc..f555e0d 100644
--- a/plugins/cube.c
+++ b/plugins/cube.c
@@ -839,10 +839,16 @@ cubePreparePaintScreen (CompScreen *s,
memset (cs->cleared, 0, sizeof (Bool) * s->nOutputDev);
/* Transparency handling */
- if (cs->rotationState != RotationNone)
+ if (cs->rotationState == RotationManual ||
+ (cs->rotationState == RotationChange &&
+ cs->opt[CUBE_SCREEN_OPTION_TRANSPARENT_MANUAL_ONLY].value.b))
+ {
opt = CUBE_SCREEN_OPTION_ACTIVE_OPACITY;
+ }
else
+ {
opt = CUBE_SCREEN_OPTION_INACTIVE_OPACITY;
+ }
cs->toOpacity = (cs->opt[opt].value.f / 100.0f) * OPAQUE;
@@ -1928,7 +1934,8 @@ static const CompMetadataOptionInfo cubeScreenOptionInfo[] = {
{ "adjust_image", "bool", 0, 0, 0 },
{ "active_opacity", "float", "<min>0.0</min><max>100.0</max>", 0, 0 },
{ "inactive_opacity", "float", "<min>0.0</min><max>100.0</max>", 0, 0 },
- { "fade_time", "float", "<min>0.0</min>", 0, 0 }
+ { "fade_time", "float", "<min>0.0</min>", 0, 0 },
+ { "transparent_manual_only", "bool", 0, 0, 0 }
};
static Bool
--
1.5.0.6
_______________________________________________
compiz mailing list
compiz@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/compiz
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic