/**
*
* Compiz wall plugin
*
* wall.c
*
* Copyright (c) 2006 Robert Carr <racarr@beryl-project.org>
*
* Authors:
* Robert Carr <racarr@beryl-project.org>
* Dennis Kasprzyk <onestone@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 <compiz.h>
#include "wall_options.h"
#include <GL/glu.h>
#include <cairo-xlib-xrender.h>
#include <cairo.h>
#define PI 3.14159265359f
#define VIEWPORT_SWITCHER_SIZE 70
#define ARROW_SIZE 33
#define WIN_X(w) ((w)->attrib.x - (w)->input.left)
#define WIN_Y(w) ((w)->attrib.y - (w)->input.top)
#define WIN_W(w) ((w)->width + (w)->input.left + (w)->input.right)
#define WIN_H(w) ((w)->height + (w)->input.top + (w)->input.bottom)
#define getColorRGBA(name, _display) \
r = wallGet##name##Red(_display) / 65535.0f;\
g = wallGet##name##Green(_display) / 65535.0f; \
b = wallGet##name##Blue(_display) / 65535.0f; \
a = wallGet##name##Alpha(_display) / 65535.0f
static int displayPrivateIndex;
/* Enums */
typedef enum
{
Up = 0,
Left,
Down,
Right
} Direction;
typedef struct _WallCairoContext
{
Pixmap pixmap;
CompTexture texture;
cairo_surface_t *surface;
cairo_t *cr;
int width;
int height;
} WallCairoContext;
typedef struct _WallDisplay
{
int screenPrivateIndex;
HandleEventProc handleEvent;
} WallDisplay;
typedef struct _WallScreen
{
DonePaintScreenProc donePaintScreen;
PaintOutputProc paintOutput;
PaintScreenProc paintScreen;
PreparePaintScreenProc preparePaintScreen;
PaintTransformedOutputProc paintTransformedOutput;
PaintWindowProc paintWindow;
SetScreenOptionProc setScreenOption;
Bool moving; /* Used to track miniview movement */
float curPosX;
float curPosY;
int gotoX;
int gotoY;
int boxTimeout;
int boxOutputDevice;
int timer;
Window moveWindow;
Bool miniScreen;
WindowPaintAttrib mSAttribs;
float mSzCamera;
float firstViewportX;
float firstViewportY;
float viewportWidth;
float viewportHeight;
float viewportBorder;
int moveWindowX;
int moveWindowY;
WallCairoContext switcherContext;
WallCairoContext thumbContext;
WallCairoContext highlightContext;
WallCairoContext arrowContext;
} WallScreen;
/* Helpers */
#define GET_WALL_DISPLAY(d) \
((WallDisplay *) (d)->privates[displayPrivateIndex].ptr)
#define WALL_DISPLAY(d) \
WallDisplay *wd = GET_WALL_DISPLAY(d);
#define GET_WALL_SCREEN(s, wd) \
((WallScreen *) (s)->privates[(wd)->screenPrivateIndex].ptr)
#define WALL_SCREEN(s) \
WallScreen *ws = GET_WALL_SCREEN(s, GET_WALL_DISPLAY(s->display))
#define GET_SCREEN \
CompScreen *s; \
Window xid; \
xid = getIntOptionNamed(option, nOption, "root", 0);\
s = findScreenAtDisplay(d, xid); \
if (!s) \
return FALSE;
#define sigmoid(x) (1.0f / (1.0f + exp (-5.5f * 2 * ((x) - 0.5))))
#define sigmoidProgress(x) ((sigmoid (x) - sigmoid (0)) / \
(sigmoid (1) - sigmoid (0)))
static void
wallClearCairoLayer (cairo_t *cr)
{
cairo_save (cr);
cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
cairo_paint (cr);
cairo_restore (cr);
cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
cairo_save (cr);
}
static void
wallDrawSwitcherBackground (CompScreen *s)
{
cairo_t *cr;
cairo_pattern_t *pattern;
float border = 10.0f, outline = 2.0f;
float width, height, radius;
float r, g, b, a;
int viewportWidth, viewportHeight;
int i, j;
WALL_SCREEN (s);
cr = ws->switcherContext.cr;
wallClearCairoLayer (cr);
width = (float) ws->switcherContext.width - outline;
height = (float) ws->switcherContext.height - outline;
cairo_translate (cr, outline / 2.0f, outline / 2.0f);
/* set the pattern for the switcher's background */
pattern = cairo_pattern_create_linear (0, 0, width, height);
getColorRGBA (BackgroundGradientBaseColor, s->display);
cairo_pattern_add_color_stop_rgba (pattern, 0.00f, r, g, b, a);
getColorRGBA (BackgroundGradientHighlightColor, s->display);
cairo_pattern_add_color_stop_rgba (pattern, 0.65f, r, g, b, a);
getColorRGBA (BackgroundGradientShadowColor, s->display);
cairo_pattern_add_color_stop_rgba (pattern, 0.85f, r, g, b, a);
cairo_set_source (cr, pattern);
/* draw the border's shape */
radius = (float) wallGetEdgeRadius (s->display);
if (radius)
{
cairo_arc (cr, radius, radius, radius, PI, 1.5f * PI);
cairo_arc (cr, radius + width - 2 * radius,
radius, radius, 1.5f * PI, 2.0 * PI);
cairo_arc (cr, width - radius, height - radius, radius, 0, PI / 2.0f);
cairo_arc (cr, radius, height - radius, radius, PI / 2.0f, PI);
}
else
cairo_rectangle (cr, 0, 0, width, height);
cairo_close_path (cr);
/* apply pattern to background... */
cairo_fill_preserve (cr);
/* ... and draw an outline */
cairo_set_line_width (cr, outline);
getColorRGBA (OutlineColor, s->display);
cairo_set_source_rgba (cr, r, g, b, a);
cairo_stroke (cr);
cairo_pattern_destroy (pattern);
viewportWidth = floor ((width - border * (s->hsize + 1)) / s->hsize -
outline / 2.0f);
viewportHeight = floor ((height - border * (s->vsize + 1)) / s->vsize -
outline / 2.0f);
ws->viewportWidth = viewportWidth;
ws->viewportHeight = viewportHeight;
ws->viewportBorder = border;
cairo_translate (cr, border, border);
for (i = 0; i < s->hsize; i++)
{
for (j = 0; j < s->vsize; j++)
{
float vpX, vpY;
vpX = i * (viewportWidth + border + outline / 2.0f);
vpY = j * (viewportHeight + border + outline / 2.0f);
/* this cuts a hole into our background */
cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 1.0);
cairo_rectangle (cr, vpX, vpY, viewportWidth, viewportHeight);
cairo_fill_preserve (cr);
cairo_set_operator (cr, CAIRO_OPERATOR_XOR);
cairo_fill (cr);
}
}
cairo_restore (cr);
}
static void
wallDrawThumb (CompScreen *s)
{
cairo_t *cr;
cairo_pattern_t *pattern;
float r, g, b, a;
float border = 10.0f, outline = 2.0f;
float width, height;
WALL_SCREEN(s);
cr = ws->thumbContext.cr;
wallClearCairoLayer (cr);
width = (float) ws->thumbContext.width;
height = (float) ws->thumbContext.height;
ws->viewportWidth = width;
ws->viewportHeight = height;
ws->viewportBorder = border;
width -= outline;
height -= outline;
cairo_translate (cr, outline / 2.0f, outline / 2.0f);
pattern = cairo_pattern_create_linear (0, 0, width, height);
getColorRGBA (ThumbGradientBaseColor, s->display);
cairo_pattern_add_color_stop_rgba (pattern, 0.0f, r, g, b, a);
getColorRGBA (ThumbGradientHighlightColor, s->display);
cairo_pattern_add_color_stop_rgba (pattern, 1.0f, r, g, b, a);
/* apply the pattern for thumb background */
cairo_set_source (cr, pattern);
cairo_rectangle (cr, 0, 0, width, height);
cairo_fill_preserve (cr);
cairo_set_line_width (cr, outline);
getColorRGBA (OutlineColor, s->display);
cairo_set_source_rgba (cr, r, g, b, a);
cairo_stroke (cr);
cairo_pattern_destroy (pattern);
cairo_restore (cr);
}
static void
wallDrawHighlight(CompScreen *s)
{
cairo_t *cr;
cairo_pattern_t *pattern;
float width, height;
float r, g, b, a;
float outline = 2.0f;
WALL_SCREEN(s);
cr = ws->highlightContext.cr;
wallClearCairoLayer (cr);
width = (float) ws->highlightContext.width - outline;
height = (float) ws->highlightContext.height - outline;
cairo_translate (cr, outline / 2.0f, outline / 2.0f);
pattern = cairo_pattern_create_linear (0, 0, width, height);
getColorRGBA (ThumbHighlightGradientBaseColor, s->display);
cairo_pattern_add_color_stop_rgba (pattern, 0.0f, r, g, b, a);
getColorRGBA (ThumbHighlightGradientShadowColor, s->display);
cairo_pattern_add_color_stop_rgba (pattern, 1.0f, r, g, b, a);
/* apply the pattern for thumb background */
cairo_set_source (cr, pattern);
cairo_rectangle (cr, 0, 0, width, height);
cairo_fill_preserve (cr);
cairo_set_line_width (cr, outline);
getColorRGBA (OutlineColor, s->display);
cairo_set_source_rgba (cr, r, g, b, a);
cairo_stroke (cr);
cairo_pattern_destroy (pattern);
cairo_restore (cr);
}
static void
wallDrawArrow (CompScreen *s)
{
cairo_t *cr;
float outline = 2.0f;
float r, g, b, a;
WALL_SCREEN(s);
cr = ws->arrowContext.cr;
wallClearCairoLayer (cr);
cairo_translate (cr, outline / 2.0f, outline / 2.0f);
/* apply the pattern for thumb background */
cairo_set_line_width (cr, outline);
/* draw top part of the arrow */
getColorRGBA (ArrowBaseColor, s->display);
cairo_set_source_rgba (cr, r, g, b, a);
cairo_move_to (cr, 15, 0);
cairo_line_to (cr, 30, 30);
cairo_line_to (cr, 15, 24.5);
cairo_line_to (cr, 15, 0);
cairo_fill (cr);
/* draw bottom part of the arrow */
getColorRGBA (ArrowShadowColor, s->display);
cairo_set_source_rgba (cr, r, g, b, a);
cairo_move_to (cr, 15, 0);
cairo_line_to (cr, 0, 30);
cairo_line_to (cr, 15, 24.5);
cairo_line_to (cr, 15, 0);
cairo_fill (cr);
/* draw the arrow outline */
getColorRGBA (OutlineColor, s->display);
cairo_set_source_rgba (cr, r, g, b, a);
cairo_move_to (cr, 15, 0);
cairo_line_to (cr, 30, 30);
cairo_line_to (cr, 15, 24.5);
cairo_line_to (cr, 0, 30);
cairo_line_to (cr, 15, 0);
cairo_stroke (cr);
cairo_restore (cr);
}
static void
wallSetupCairoContext (CompScreen *s,
WallCairoContext *context)
{
XRenderPictFormat *format;
Screen *screen;
int width, height;
screen = ScreenOfDisplay (s->display->display, s->screenNum);
width = context->width;
height = context->height;
initTexture (s, &context->texture);
format = XRenderFindStandardFormat (s->display->display,
PictStandardARGB32);
context->pixmap = XCreatePixmap (s->display->display, s->root,
width, height, 32);
if (!bindPixmapToTexture(s, &context->texture, context->pixmap,
width, height, 32))
{
compLogMessage (s->display, "wall", CompLogLevelError,
"Couldn't create cairo context for switcher");
}
context->surface =
cairo_xlib_surface_create_with_xrender_format (s->display->display,
context->pixmap,
screen, format,
width, height);
context->cr = cairo_create (context->surface);
wallClearCairoLayer (context->cr);
}
static void
wallDestroyCairoContext (CompScreen *s,
WallCairoContext *context)
{
if (context->cr)
cairo_destroy (context->cr);
if (context->surface)
cairo_surface_destroy (context->surface);
finiTexture (s, &context->texture);
if (context->pixmap)
XFreePixmap (s->display->display, context->pixmap);
}
static Bool
wallCheckDestination (CompScreen *s,
int destX,
int destY)
{
if (s->x - destX < 0)
return FALSE;
if (s->x - destX >= s->hsize)
return FALSE;
if (s->y - destY >= s->vsize)
return FALSE;
if (s->y - destY < 0)
return FALSE;
return TRUE;
}
static void
wallReleaseMoveWindow (CompScreen *s)
{
CompWindow *w;
WALL_SCREEN (s);
w = findWindowAtScreen (s, ws->moveWindow);
if (w)
syncWindowPosition (w);
ws->moveWindow = 0;
}
static void
wallComputeTranslation (CompScreen *s,
float *x,
float *y)
{
float dx, dy, elapsed;
WALL_SCREEN (s);
elapsed = 1 - (ws->timer / (wallGetSlideDuration (s->display) * 1000));
if (elapsed < 0.0)
elapsed = 0.0;
if (elapsed > 1.0)
elapsed = 1.0;
/* Use temporary variables to you can pass in &ps->cur_x */
dx = (ws->gotoX - ws->curPosX) * elapsed + ws->curPosX;
dy = (ws->gotoY - ws->curPosY) * elapsed + ws->curPosY;
*x = dx;
*y = dy;
}
static Bool
wallMoveViewport (CompScreen *s,
int x,
int y,
Window moveWindow)
{
WALL_SCREEN (s);
if (!x && !y)
return FALSE;
if (wallCheckDestination (s, x, y))
{
if (ws->moveWindow != moveWindow)
{
CompWindow *w;
wallReleaseMoveWindow (s);
w = findWindowAtScreen (s, moveWindow);
if (w)
{
if (!(w->type & (CompWindowTypeDesktopMask |
CompWindowTypeDockMask)))
{
if (!(w->state & CompWindowStateStickyMask))
{
ws->moveWindow = w->id;
ws->moveWindowX = w->attrib.x;
ws->moveWindowY = w->attrib.y;
raiseWindow (w);
}
}
}
}
if (!ws->moving)
{
ws->curPosX = s->x;
ws->curPosY = s->y;
}
ws->gotoX = s->x - x;
ws->gotoY = s->y - y;
moveScreenViewport (s, x, y, TRUE);
ws->moving = TRUE;
ws->boxOutputDevice = s->currentOutputDev;
}
if (ws->moving)
{
if (wallGetShowSwitcher (s->display))
ws->boxTimeout = wallGetPreviewTimeout (s->display) * 1000;
else
ws->boxTimeout = 0;
if (otherScreenGrabExist (s, "move", "scale", "group-drag", "wall", 0))
{
ws->boxTimeout = 0;
ws->moving = FALSE;
}
ws->timer = wallGetSlideDuration (s->display) * 1000;
}
damageScreen (s);
return ws->moving;
}
static void
wallHandleEvent (CompDisplay *d,
XEvent *event)
{
WALL_DISPLAY (d);
switch (event->type)
{
case ClientMessage:
if (event->xclient.message_type == d->winActiveAtom)
{
CompWindow *w;
w = findWindowAtDisplay (d, event->xclient.window);
if (w)
{
int dx, dy;
/* window must be placed */
if (!w->placed)
break;
if (otherScreenGrabExist (w->screen, "switcher", "scale", 0))
break;
defaultViewportForWindow (w, &dx, &dy);
dx -= w->screen->x;
dy -= w->screen->y;
if (dx || dy)
wallMoveViewport (w->screen, -dx, -dy, None);
}
}
else if (event->xclient.message_type == d->desktopViewportAtom)
{
int dx, dy;
CompScreen *s;
s = findScreenAtDisplay (d, event->xclient.window);
if (!s)
break;
if (otherScreenGrabExist (s, "switcher", "scale", 0))
break;
dx = event->xclient.data.l[0] / s->width - s->x;
dy = event->xclient.data.l[1] / s->height - s->y;
if (!dx && !dy)
break;
wallMoveViewport (s, -dx, -dy, None);
}
break;
}
UNWRAP (wd, d, handleEvent);
(*d->handleEvent) (d, event);
WRAP (wd, d, handleEvent, wallHandleEvent);
}
static Bool
wallNext (CompDisplay *d,
CompAction *action,
CompActionState state,
CompOption *option,
int nOption)
{
GET_SCREEN;
if ((s->x == s->hsize - 1) && (s->y == s->vsize - 1))
wallMoveViewport (s, s->hsize - 1, s->vsize - 1, None);
else if (s->x == s->hsize - 1)
wallMoveViewport (s, s->hsize - 1, -1, None);
else
wallMoveViewport (s, -1, 0, None);
return TRUE;
}
static Bool
wallPrev (CompDisplay *d,
CompAction *action,
CompActionState state,
CompOption *option,
int nOption)
{
GET_SCREEN;
if ((s->x == 0) && (s->y == 0))
wallMoveViewport (s, -(s->hsize - 1), -(s->vsize - 1), None);
else if (s->x == 0)
wallMoveViewport (s, -(s->hsize - 1), 1, None);
else
wallMoveViewport (s, 1, 0, None);
return TRUE;
}
static void
wallCheckAmount (CompScreen *s,
int dx,
int dy,
int *amountX,
int *amountY)
{
*amountX = -dx;
*amountY = -dy;
if (wallGetAllowWraparound (s->display))
{
if ((s->x + dx) < 0)
*amountX = -(s->hsize + dx);
else if ((s->x + dx) >= s->hsize)
*amountX = s->hsize - dx;
if ((s->y + dy) < 0)
*amountY = -(s->vsize + dy);
else if ((s->y + dy) >= s->vsize)
*amountY = s->vsize - dy;
}
}
static Bool
wallInitiate (CompScreen *s,
int dx,
int dy,
Window win)
{
int amountX, amountY;
wallCheckAmount (s, dx, dy, &amountX, &amountY);
wallMoveViewport (s, amountX, amountY, win);
return TRUE;
}
static Bool
wallInitiateFlip (CompScreen *s,
Direction direction,
Bool dnd)
{
int dx, dy;
int amountX, amountY;
if (otherScreenGrabExist (s, "wall", "move", "group-drag", 0))
return FALSE;
if (dnd)
{
if (!wallGetEdgeflipDnd (s))
return FALSE;
if (otherScreenGrabExist (s, "wall", 0))
return FALSE;
}
else if (otherScreenGrabExist (s, "wall", "group-drag", 0))
{
/* not wall or group means move */
if (!wallGetEdgeflipMove (s))
return FALSE;
}
else if (otherScreenGrabExist (s, "wall", 0))
{
/* move was ruled out before, so we have group */
if (!wallGetEdgeflipDnd (s))
return FALSE;
}
else if (!wallGetEdgeflipPointer (s))
return FALSE;
switch (direction)
{
case Left:
dx = -1; dy = 0;
break;
case Right:
dx = 1; dy = 0;
break;
case Up:
dx = 0; dy = -1;
break;
case Down:
dx = 0; dy = 1;
break;
default:
dx = 0; dy = 0;
break;
}
wallCheckAmount (s, dx, dy, &amountX, &amountY);
if (wallMoveViewport (s, amountX, amountY, None))
{
int offsetX, offsetY;
int warpX, warpY;
if (dx < 0)
{
offsetX = s->width - 10;
warpX = pointerX + s->width;
}
else if (dx > 0)
{
offsetX = 1- s->width;
warpX = pointerX - s->width;
}
else
{
offsetX = 0;
warpX = lastPointerX;
}
if (dy < 0)
{
offsetY = s->height - 10;
warpY = pointerY + s->height;
}
else if (dy > 0)
{
offsetY = 1- s->height;
warpY = pointerY - s->height;
}
else
{
offsetY = 0;
warpY = lastPointerY;
}
warpPointer (s, offsetX, offsetY);
lastPointerX = warpX;
lastPointerY = warpY;
}
return TRUE;
}
static Bool
wallLeft (CompDisplay *d,
CompAction *action,
CompActionState state,
CompOption *option,
int nOption)
{
GET_SCREEN;
return wallInitiate (s, -1, 0, None);
}
static Bool
wallRight (CompDisplay *d,
CompAction *action,
CompActionState state,
CompOption *option,
int nOption)
{
GET_SCREEN;
return wallInitiate (s, 1, 0, None);
}
static Bool
wallUp (CompDisplay *d,
CompAction *action,
CompActionState state,
CompOption *option,
int nOption)
{
GET_SCREEN;
return wallInitiate (s, 0, -1, None);
}
static Bool
wallDown (CompDisplay *d,
CompAction *action,
CompActionState state,
CompOption *option,
int nOption)
{
GET_SCREEN;
return wallInitiate (s, 0, 1, None);
}
static Bool
wallFlipLeft (CompDisplay *d,
CompAction *action,
CompActionState state,
CompOption *option,
int nOption)
{
GET_SCREEN;
return wallInitiateFlip (s, Left, (state & CompActionStateInitEdgeDnd));
}
static Bool
wallFlipRight (CompDisplay *d,
CompAction *action,
CompActionState state,
CompOption *option,
int nOption)
{
GET_SCREEN;
return wallInitiateFlip (s, Right, (state & CompActionStateInitEdgeDnd));
}
static Bool
wallFlipUp (CompDisplay *d,
CompAction *action,
CompActionState state,
CompOption *option,
int nOption)
{
GET_SCREEN;
return wallInitiateFlip (s, Up, (state & CompActionStateInitEdgeDnd));
}
static Bool
wallFlipDown (CompDisplay *d,
CompAction *action,
CompActionState state,
CompOption *option,
int nOption)
{
GET_SCREEN;
return wallInitiateFlip (s, Down, (state & CompActionStateInitEdgeDnd));
}
static Bool
wallLeftWithWindow (CompDisplay *d,
CompAction *action,
CompActionState state,
CompOption *option,
int nOption)
{
GET_SCREEN;
Window win = getIntOptionNamed (option, nOption, "window", 0);
return wallInitiate (s, -1, 0, win);
}
static Bool
wallRightWithWindow (CompDisplay *d,
CompAction *action,
CompActionState state,
CompOption *option,
int nOption)
{
GET_SCREEN;
Window win = getIntOptionNamed (option, nOption, "window", 0);
return wallInitiate (s, 1, 0, win);
}
static Bool
wallUpWithWindow (CompDisplay *d,
CompAction *action,
CompActionState state,
CompOption *option,
int nOption)
{
GET_SCREEN;
Window win = getIntOptionNamed (option, nOption, "window", 0);
return wallInitiate (s, 0, -1, win);
}
static Bool
wallDownWithWindow (CompDisplay *d,
CompAction *action,
CompActionState state,
CompOption *option,
int nOption)
{
GET_SCREEN;
Window win = getIntOptionNamed (option, nOption, "window", 0);
return wallInitiate (s, 0, 1, win);
}
static inline void
wallDrawQuad (CompMatrix *matrix, BOX *box)
{
glTexCoord2f (COMP_TEX_COORD_X (matrix, box->x1),
COMP_TEX_COORD_Y (matrix, box->y2));
glVertex2i (box->x1, box->y2);
glTexCoord2f (COMP_TEX_COORD_X (matrix, box->x2),
COMP_TEX_COORD_Y (matrix, box->y2));
glVertex2i (box->x2, box->y2);
glTexCoord2f (COMP_TEX_COORD_X (matrix, box->x2),
COMP_TEX_COORD_Y (matrix, box->y1));
glVertex2i (box->x2, box->y1);
glTexCoord2f (COMP_TEX_COORD_X (matrix, box->x1),
COMP_TEX_COORD_Y (matrix, box->y1));
glVertex2i (box->x1, box->y1);
}
static void
wallDrawCairoTextureOnScreen (CompScreen *s)
{
float centerX, centerY;
float width, height;
float topLeftX, topLeftY;
float border = 10.0f;
int i, j;
CompMatrix matrix;
BOX box;
WALL_SCREEN(s);
glDisableClientState (GL_TEXTURE_COORD_ARRAY);
glEnable (GL_BLEND);
centerX = s->outputDev[ws->boxOutputDevice].region.extents.x1 +
(s->outputDev[ws->boxOutputDevice].width / 2.0f);
centerY = s->outputDev[ws->boxOutputDevice].region.extents.y1 +
(s->outputDev[ws->boxOutputDevice].height / 2.0f);
width = (float) ws->switcherContext.width;
height = (float) ws->switcherContext.height;
topLeftX = centerX - floor (width / 2.0f);
topLeftY = centerY - floor (height / 2.0f);
ws->firstViewportX = topLeftX + border;
ws->firstViewportY = topLeftY + border;
if (!ws->moving)
{
double left;
left = (float)ws->boxTimeout /
(wallGetPreviewTimeout (s->display) * 1000.0f);
if (left < 0)
left = 0.0f;
else if (left > 0.5)
left = 1.0f;
else
left = 2 * left;
screenTexEnvMode (s, GL_MODULATE);
glColor4f (left, left, left, left);
glTranslatef (0.0f,0.0f, -(1 - left));
ws->mSzCamera = -(1 - left);
}
else
ws->mSzCamera = 0.0f;
/* draw background */
matrix = ws->switcherContext.texture.matrix;
matrix.x0 -= topLeftX * matrix.xx;
matrix.y0 -= topLeftY * matrix.yy;
box.x1 = topLeftX;
box.x2 = box.x1 + width;
box.y1 = topLeftY;
box.y2 = box.y1 + height;
enableTexture (s, &ws->switcherContext.texture, COMP_TEXTURE_FILTER_FAST);
glBegin (GL_QUADS);
wallDrawQuad (&matrix, &box);
glEnd ();
disableTexture (s, &ws->switcherContext.texture);
/* draw thumb */
width = (float) ws->thumbContext.width;
height = (float) ws->thumbContext.height;
enableTexture (s, &ws->thumbContext.texture, COMP_TEXTURE_FILTER_FAST);
glBegin (GL_QUADS);
for (i = 0; i < s->hsize; i++)
{
for (j = 0; j < s->vsize; j++)
{
if (i == ws->gotoX && j == ws->gotoY && ws->moving)
continue;
box.x1 = i * (width + border);
box.x1 += topLeftX + border;
box.x2 = box.x1 + width;
box.y1 = j * (height + border);
box.y1 += topLeftY + border;
box.y2 = box.y1 + height;
matrix = ws->thumbContext.texture.matrix;
matrix.x0 -= box.x1 * matrix.xx;
matrix.y0 -= box.y1 * matrix.yy;
wallDrawQuad (&matrix, &box);
}
}
glEnd ();
disableTexture (s, &ws->thumbContext.texture);
if (ws->moving)
{
/* draw highlight */
float angle = 0.0f;
float dx, dy;
int aW, aH;
box.x1 = s->x * (width + border) + topLeftX + border;
box.x2 = box.x1 + width;
box.y1 = s->y * (height + border) + topLeftY + border;
box.y2 = box.y1 + height;
matrix = ws->highlightContext.texture.matrix;
matrix.x0 -= box.x1 * matrix.xx;
matrix.y0 -= box.y1 * matrix.yy;
enableTexture (s, &ws->highlightContext.texture,
COMP_TEXTURE_FILTER_FAST);
glBegin (GL_QUADS);
wallDrawQuad (&matrix, &box);
glEnd ();
disableTexture (s, &ws->highlightContext.texture);
/* draw arrow */
enableTexture (s, &ws->arrowContext.texture, COMP_TEXTURE_FILTER_GOOD);
dx = ws->gotoX - ws->curPosX;
dy = ws->gotoY - ws->curPosY;
if (dx > 0 && dy == 0)
angle = 90.0f;
else if (dx < 0 && dy == 0)
angle = 270.0f;
else if (dy > 0)
{
angle = 180.0f;
if (dx < 0)
angle += 45.0f;
else if (dx > 0)
angle -= 45.0f;
}
else if (dy < 0)
{
if (dx < 0)
angle = -45.0f;
else if (dx > 0)
angle = 45.0f;
}
aW = ws->arrowContext.width;
aH = ws->arrowContext.height;
/* if we have a viewport preview we just paint the
arrow outside the switcher */
if (wallGetMiniscreen (s->display))
{
width = (float) ws->switcherContext.width;
height = (float) ws->switcherContext.height;
switch ((int)angle)
{
/* top left */
case -45:
box.x1 = topLeftX - aW - border;
box.y1 = topLeftY - aH - border;
break;
/* up */
case 0:
box.x1 = topLeftX + width / 2.0f - aW / 2.0f;
box.y1 = topLeftY - aH - border;
break;
/* top right */
case 45:
box.x1 = topLeftX + width + border;
box.y1 = topLeftY - aH - border;
break;
/* right */
case 90:
box.x1 = topLeftX + width + border;
box.y1 = topLeftY + height / 2.0f - aH / 2.0f;
break;
/* bottom right */
case 135:
box.x1 = topLeftX + width + border;
box.y1 = topLeftY + height + border;
break;
/* down */
case 180:
box.x1 = topLeftX + width / 2.0f - aW / 2.0f;
box.y1 = topLeftY + height + border;
break;
/* bottom left */
case 225:
box.x1 = topLeftX - aW - border;
box.y1 = topLeftY + height + border;
break;
/* left */
case 270:
box.x1 = topLeftX - aW - border;
box.y1 = topLeftY + height / 2.0f - aH / 2.0f;
break;
default:
break;
}
}
else
{
/* arrow is visible (no preview is painted over it) */
box.x1 = s->x * (width + border) + topLeftX + border;
box.x1 += width / 2 - aW / 2;
box.y1 = s->y * (height + border) + topLeftY + border;
box.y1 += height / 2 - aH / 2;
}
box.x2 = box.x1 + aW;
box.y2 = box.y1 + aH;
glTranslatef (box.x1 + aW / 2, box.y1 + aH / 2, 0.0f);
glRotatef (angle, 0.0f, 0.0f, 1.0f);
glTranslatef (-box.x1 - aW / 2, -box.y1 - aH / 2, 0.0f);
matrix = ws->arrowContext.texture.matrix;
matrix.x0 -= box.x1 * matrix.xx;
matrix.y0 -= box.y1 * matrix.yy;
glBegin (GL_QUADS);
wallDrawQuad (&matrix, &box);
glEnd ();
disableTexture (s, &ws->arrowContext.texture);
}
glDisable (GL_BLEND);
glEnableClientState (GL_TEXTURE_COORD_ARRAY);
screenTexEnvMode (s, GL_REPLACE);
glColor4usv (defaultColor);
}
static void
wallPaintScreen (CompScreen *s,
CompOutput *outputs,
int numOutputs,
unsigned int mask)
{
WALL_SCREEN (s);
if (ws->moving && numOutputs > 1 && wallGetMmmode(s) == MmmodeSwitchAll)
{
outputs = &s->fullscreenOutput;
numOutputs = 1;
}
UNWRAP (ws, s, paintScreen);
(*s->paintScreen) (s, outputs, numOutputs, mask);
WRAP (ws, s, paintScreen, wallPaintScreen);
}
static Bool
wallPaintOutput (CompScreen *s,
const ScreenPaintAttrib *sAttrib,
const CompTransform *transform,
Region region,
CompOutput *output,
unsigned int mask)
{
Bool status;
WALL_SCREEN (s);
ws->miniScreen = FALSE;
if (ws->moving)
mask |= PAINT_SCREEN_TRANSFORMED_MASK;
UNWRAP (ws, s, paintOutput);
status = (*s->paintOutput) (s, sAttrib, transform, region, output, mask);
WRAP (ws, s, paintOutput, wallPaintOutput);
if ((ws->moving || ws->boxTimeout) && wallGetShowSwitcher(s->display) &&
(output->id == ws->boxOutputDevice || output == &s->fullscreenOutput))
{
CompTransform sTransform = *transform;
transformToScreenSpace (s, output, -DEFAULT_Z_CAMERA, &sTransform);
glPushMatrix ();
glLoadMatrixf (sTransform.m);
wallDrawCairoTextureOnScreen (s);
glPopMatrix ();
if (wallGetMiniscreen (s->display))
{
int i, j;
int origVX = s->x;
int origVY = s->y;
moveScreenViewport (s, s->x, s->y, FALSE);
for (j = 0; j < s->vsize; j++)
{
for (i = 0; i < s->hsize; i++)
{
float mx, my, mw, mh;
unsigned int msMask;
ws->miniScreen = TRUE;
mx = ws->firstViewportX +
(i * (ws->viewportWidth + ws->viewportBorder));
my = ws->firstViewportY +
(j * (ws->viewportHeight + ws->viewportBorder));
mw = ws->viewportWidth;
mh = ws->viewportHeight;
ws->mSAttribs.xTranslate = mx / output->width;
ws->mSAttribs.yTranslate = -my / output->height;
ws->mSAttribs.xScale = mw / s->width;
ws->mSAttribs.yScale = mh / s->height;
ws->mSAttribs.opacity = OPAQUE * (1.0 + ws->mSzCamera);
ws->mSAttribs.brightness = 0.4f * BRIGHT;
ws->mSAttribs.saturation = COLOR;
if (i == origVX && j == origVY && ws->moving)
ws->mSAttribs.brightness = BRIGHT;
if (ws->boxTimeout && !ws->moving &&
i == origVX && j == origVY)
{
ws->mSAttribs.brightness = BRIGHT;
}
msMask = mask | PAINT_SCREEN_TRANSFORMED_MASK;
(*s->paintTransformedOutput) (s, sAttrib, transform,
region, output, msMask);
ws->miniScreen = FALSE;
moveScreenViewport (s, -1, 0, FALSE);
}
moveScreenViewport (s, 0, -1, FALSE);
}
moveScreenViewport (s, -origVX, -origVY, FALSE);
}
}
return status;
}
static void
wallPreparePaintScreen (CompScreen *s,
int msSinceLastPaint)
{
WALL_SCREEN (s);
if (!ws->moving && ws->boxTimeout)
ws->boxTimeout -= msSinceLastPaint;
if (ws->timer)
ws->timer -= msSinceLastPaint;
if (ws->moving)
{
wallComputeTranslation (s, &ws->curPosX, &ws->curPosY);
if (ws->moveWindow)
{
CompWindow *w;
w = findWindowAtScreen (s, ws->moveWindow);
if (w)
{
float dx, dy;
dx = ws->gotoX - ws->curPosX;
dy = ws->gotoY - ws->curPosY;
moveWindowToViewportPosition (w,
ws->moveWindowX - s->width * dx,
ws->moveWindowY - s->height * dy,
TRUE);
}
}
}
if (ws->moving && ws->curPosX == ws->gotoX && ws->curPosY == ws->gotoY)
{
ws->moving = FALSE;
ws->timer = 0;
if (ws->moveWindow)
wallReleaseMoveWindow (s);
else
focusDefaultWindow (s->display);
}
UNWRAP (ws, s, preparePaintScreen);
(*s->preparePaintScreen) (s, msSinceLastPaint);
WRAP (ws, s, preparePaintScreen, wallPreparePaintScreen);
}
static void
wallPaintTransformedOutput (CompScreen *s,
const ScreenPaintAttrib *sAttrib,
const CompTransform *transform,
Region region,
CompOutput *output,
unsigned int mask)
{
CompTransform sTransform = *transform;
WALL_SCREEN (s);
if (ws->miniScreen)
{
/* move each screen to the correct output position */
matrixTranslate (&sTransform,
-output->region.extents.x1 / output->width,
output->region.extents.y1 / output->height, 0.0f);
matrixTranslate (&sTransform, 0.0f, 0.0f, -DEFAULT_Z_CAMERA);
matrixTranslate (&sTransform,
ws->mSAttribs.xTranslate,
ws->mSAttribs.yTranslate,
ws->mSzCamera);
/* move origin to top left */
matrixTranslate (&sTransform, -0.5f, 0.5f, 0.0f);
matrixScale (&sTransform,
ws->mSAttribs.xScale, ws->mSAttribs.yScale, 1.0);
/* revert prepareXCoords region shift.
Now all screens display the same */
matrixTranslate (&sTransform, 0.5f, 0.5f, DEFAULT_Z_CAMERA);
matrixTranslate (&sTransform,
output->region.extents.x1 / output->width,
-output->region.extents.y2 / output->height, 0.0f);
UNWRAP (ws, s, paintTransformedOutput);
(*s->paintTransformedOutput) (s, sAttrib, &sTransform,
&s->region, output, mask);
WRAP (ws, s, paintTransformedOutput, wallPaintTransformedOutput);
return;
}
UNWRAP (ws, s, paintTransformedOutput);
if (!ws->moving)
(*s->paintTransformedOutput) (s, sAttrib, &sTransform,
region, output, mask);
mask &= ~PAINT_SCREEN_CLEAR_MASK;
if (ws->moving)
{
ScreenPaintAttrib sA = *sAttrib;
int origx = s->x;
int origy = s->y;
float px, py;
int tx, ty;
clearTargetOutput (s->display, GL_COLOR_BUFFER_BIT);
px = ws->curPosX;
py = ws->curPosY;
if (floor (py) != ceil (py))
{
ty = ceil (py) - s->y;
sA.yTranslate = fmod (py, 1) - 1;
if (floor (px) != ceil (px))
{
tx = ceil (px) - s->x;
moveScreenViewport (s, -tx, -ty, FALSE);
sA.xTranslate = 1 - fmod (px,1);
(*s->paintTransformedOutput) (s, &sA, &sTransform,
&output->region, output, mask);
moveScreenViewport (s, tx, ty, FALSE);
}
tx = floor (px) - s->x;
moveScreenViewport (s, -tx, -ty, FALSE);
sA.xTranslate = -fmod (px,1);
(*s->paintTransformedOutput) (s, &sA, &sTransform,
&output->region, output, mask);
moveScreenViewport (s, tx, ty, FALSE);
}
ty = floor (py) - s->y;
sA.yTranslate = fmod (py,1);
if (floor (px) != ceil (px))
{
tx = ceil (px) - s->x;
moveScreenViewport (s, -tx, -ty, FALSE);
sA.xTranslate = 1 - fmod (px,1);
(*s->paintTransformedOutput) (s, &sA, &sTransform,
&output->region, output, mask);
moveScreenViewport (s, tx, ty, FALSE);
}
tx = floor (px) - s->x;
moveScreenViewport (s, - tx, -ty, FALSE);
sA.xTranslate = -fmod (px,1);
(*s->paintTransformedOutput) (s, &sA, &sTransform,
&output->region, output, mask);
moveScreenViewport (s, tx, ty, FALSE);
while (s->x != origx)
moveScreenViewport (s, -1, 0, FALSE);
while (s->y != origy)
moveScreenViewport (s, 0, 1, FALSE);
}
WRAP (ws, s, paintTransformedOutput, wallPaintTransformedOutput);
}
static Bool
wallPaintWindow(CompWindow *w,
const WindowPaintAttrib *attrib,
const CompTransform *transform,
Region region,
unsigned int mask)
{
Bool status;
CompScreen *s = w->screen;
WALL_SCREEN (s);
if (ws->miniScreen)
{
WindowPaintAttrib pA = *attrib;
pA.opacity = attrib->opacity *
((float)ws->mSAttribs.opacity / OPAQUE);
pA.brightness = attrib->brightness *
((float)ws->mSAttribs.brightness / BRIGHT);
pA.saturation = attrib->saturation *
((float)ws->mSAttribs.saturation / COLOR);
if (!pA.opacity || !pA.brightness)
mask |= PAINT_WINDOW_NO_CORE_INSTANCE_MASK;
UNWRAP (ws, s, paintWindow);
status = (*s->paintWindow) (w, &pA, transform, region, mask);
WRAP (ws, s, paintWindow, wallPaintWindow);
}
else
{
UNWRAP (ws, s, paintWindow);
status = (*s->paintWindow) (w, attrib, transform, region, mask);
WRAP (ws, s, paintWindow, wallPaintWindow);
}
return status;
}
static void
wallDonePaintScreen (CompScreen *s)
{
WALL_SCREEN (s);
if (ws->moving || ws->boxTimeout)
damageScreen (s);
if (ws->boxTimeout < 0)
{
ws->boxTimeout = 0;
damageScreen (s);
}
UNWRAP (ws, s, donePaintScreen);
(*s->donePaintScreen) (s);
WRAP (ws, s, donePaintScreen, wallDonePaintScreen);
}
static void
wallCreateCairoContexts (CompScreen *s,
Bool initial)
{
float border = 10.0f;
float width, height;
WALL_SCREEN (s);
width = VIEWPORT_SWITCHER_SIZE * s->hsize + (2 * border * (s->hsize - 1));
width *= (float)s->width / (float)s->height;
height = VIEWPORT_SWITCHER_SIZE * s->vsize + (2 * border * (s->vsize - 1));
wallDestroyCairoContext (s, &ws->switcherContext);
ws->switcherContext.width = width;
ws->switcherContext.height = height;
wallSetupCairoContext (s, &ws->switcherContext);
wallDrawSwitcherBackground (s);
wallDestroyCairoContext (s, &ws->thumbContext);
ws->thumbContext.width = (width - border * (s->hsize + 1)) / s->hsize;
ws->thumbContext.height = (height - border * (s->vsize + 1)) / s->vsize;
wallSetupCairoContext (s, &ws->thumbContext);
wallDrawThumb (s);
wallDestroyCairoContext (s, &ws->highlightContext);
ws->highlightContext.width = (width - border * (s->hsize + 1)) / s->hsize;
ws->highlightContext.height = (height - border * (s->vsize + 1)) / s->vsize;
wallSetupCairoContext (s, &ws->highlightContext);
wallDrawHighlight (s);
if (initial)
{
ws->arrowContext.width = ARROW_SIZE;
ws->arrowContext.height = ARROW_SIZE;
wallSetupCairoContext (s, &ws->arrowContext);
wallDrawArrow (s);
}
}
static void
wallDisplayOptionChanged (CompDisplay *display,
CompOption *opt,
WallDisplayOptions num)
{
CompScreen *s;
switch(num)
{
case WallDisplayOptionOutlineColor:
for (s = display->screens; s; s = s->next)
{
wallDrawSwitcherBackground (s);
wallDrawHighlight (s);
wallDrawThumb (s);
}
break;
case WallDisplayOptionEdgeRadius:
case WallDisplayOptionBackgroundGradientBaseColor:
case WallDisplayOptionBackgroundGradientHighlightColor:
case WallDisplayOptionBackgroundGradientShadowColor:
for (s = display->screens; s; s = s->next)
wallDrawSwitcherBackground (s);
break;
case WallDisplayOptionThumbGradientBaseColor:
case WallDisplayOptionThumbGradientHighlightColor:
for (s = display->screens; s; s = s->next)
wallDrawThumb (s);
break;
case WallDisplayOptionThumbHighlightGradientBaseColor:
case WallDisplayOptionThumbHighlightGradientShadowColor:
for (s = display->screens; s; s = s->next)
wallDrawHighlight (s);
break;
case WallDisplayOptionArrowBaseColor:
case WallDisplayOptionArrowShadowColor:
for (s = display->screens; s; s = s->next)
wallDrawArrow (s);
break;
default:
break;
}
}
static Bool
wallSetScreenOptionCore (CompScreen *screen,
char *name,
CompOptionValue *value)
{
Bool status;
WALL_SCREEN (screen);
UNWRAP (ws, screen, setScreenOption);
status = (*screen->setScreenOption) (screen, name, value);
WRAP (ws, screen, setScreenOption, wallSetScreenOptionCore);
if (status)
{
if (strcmp (name, "hsize") == 0 || strcmp (name, "vsize") == 0)
wallCreateCairoContexts (screen, FALSE);
}
return status;
}
static Bool
wallInitDisplay (CompPlugin *p,
CompDisplay *d)
{
WallDisplay *wd;
wd = malloc (sizeof (WallDisplay));
if (!wd)
return FALSE;
wd->screenPrivateIndex = allocateScreenPrivateIndex (d);
if (wd->screenPrivateIndex < 0)
{
free (wd);
return FALSE;
}
wallSetLeftInitiate (d, wallLeft);
wallSetRightInitiate (d, wallRight);
wallSetUpInitiate (d, wallUp);
wallSetDownInitiate (d, wallDown);
wallSetNextInitiate (d, wallNext);
wallSetPrevInitiate (d, wallPrev);
wallSetLeftWindowInitiate (d, wallLeftWithWindow);
wallSetRightWindowInitiate (d, wallRightWithWindow);
wallSetUpWindowInitiate (d, wallUpWithWindow);
wallSetDownWindowInitiate (d, wallDownWithWindow);
wallSetFlipLeftInitiate (d, wallFlipLeft);
wallSetFlipRightInitiate (d, wallFlipRight);
wallSetFlipUpInitiate (d, wallFlipUp);
wallSetFlipDownInitiate (d, wallFlipDown);
wallSetEdgeRadiusNotify (d, wallDisplayOptionChanged);
wallSetOutlineColorNotify (d, wallDisplayOptionChanged);
wallSetBackgroundGradientBaseColorNotify (d, wallDisplayOptionChanged);
wallSetBackgroundGradientHighlightColorNotify (d, wallDisplayOptionChanged);
wallSetBackgroundGradientShadowColorNotify (d, wallDisplayOptionChanged);
wallSetThumbGradientBaseColorNotify (d, wallDisplayOptionChanged);
wallSetThumbGradientHighlightColorNotify (d, wallDisplayOptionChanged);
wallSetThumbHighlightGradientBaseColorNotify (d, wallDisplayOptionChanged);
wallSetThumbHighlightGradientShadowColorNotify (d,
wallDisplayOptionChanged);
wallSetArrowBaseColorNotify (d, wallDisplayOptionChanged);
wallSetArrowShadowColorNotify (d, wallDisplayOptionChanged);
WRAP (wd, d, handleEvent, wallHandleEvent);
d->privates[displayPrivateIndex].ptr = wd;
return TRUE;
}
static void
wallFiniDisplay (CompPlugin *p,
CompDisplay *d)
{
WALL_DISPLAY (d);
UNWRAP (wd, d, handleEvent);
freeScreenPrivateIndex (d, wd->screenPrivateIndex);
free (wd);
}
static Bool
wallInitScreen (CompPlugin *p,
CompScreen *s)
{
WallScreen *ws;
WALL_DISPLAY (s->display);
ws = malloc (sizeof (WallScreen));
if (!ws)
return FALSE;
ws->boxTimeout = 0;
ws->moving = FALSE;
memset (&ws->switcherContext, 0, sizeof (WallCairoContext));
memset (&ws->thumbContext, 0, sizeof (WallCairoContext));
memset (&ws->highlightContext, 0, sizeof (WallCairoContext));
memset (&ws->arrowContext, 0, sizeof (WallCairoContext));
WRAP (ws, s, paintScreen, wallPaintScreen);
WRAP (ws, s, paintOutput, wallPaintOutput);
WRAP (ws, s, donePaintScreen, wallDonePaintScreen);
WRAP (ws, s, paintTransformedOutput, wallPaintTransformedOutput);
WRAP (ws, s, preparePaintScreen, wallPreparePaintScreen);
WRAP (ws, s, paintWindow, wallPaintWindow);
WRAP (ws, s, setScreenOption, wallSetScreenOptionCore);
s->privates[wd->screenPrivateIndex].ptr = ws;
wallCreateCairoContexts (s, TRUE);
return TRUE;
}
static void
wallFiniScreen (CompPlugin *p,
CompScreen *s)
{
WALL_SCREEN (s);
wallDestroyCairoContext (s, &ws->switcherContext);
wallDestroyCairoContext (s, &ws->thumbContext);
wallDestroyCairoContext (s, &ws->highlightContext);
wallDestroyCairoContext (s, &ws->arrowContext);
UNWRAP (ws, s, paintScreen);
UNWRAP (ws, s, paintOutput);
UNWRAP (ws, s, donePaintScreen);
UNWRAP (ws, s, paintTransformedOutput);
UNWRAP (ws, s, preparePaintScreen);
UNWRAP (ws, s, paintWindow);
UNWRAP (ws, s, setScreenOption);
free(ws);
}
static Bool
wallInit (CompPlugin *p)
{
displayPrivateIndex = allocateDisplayPrivateIndex ();
if (displayPrivateIndex < 0)
return FALSE;
return TRUE;
}
static void
wallFini (CompPlugin *p)
{
freeDisplayPrivateIndex (displayPrivateIndex);
}
static int
wallGetVersion (CompPlugin *p,
int version)
{
return ABIVERSION;
}
CompPluginVTable wallVTable = {
"wall",
wallGetVersion,
0,
wallInit,
wallFini,
wallInitDisplay,
wallFiniDisplay,
wallInitScreen,
wallFiniScreen,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};
CompPluginVTable*
getCompPluginInfo (void)
{
return &wallVTable;
}
syntax highlighted by Code2HTML, v. 0.9.1