/*
*
* Beryl thumbnail plugin
*
* thumbnail.c
*
* Copyright : (C) 2007 by Dennis Kasprzyk
* E-mail : onestone@beryl-project.org
*
* Based on thumbnail.c:
* Copyright : (C) 2007 Stjepan Glavina
* E-mail : stjepang@gmail.com
*
* 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.
*
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <sys/types.h>
#include <unistd.h>
#include <X11/Xatom.h>
#include <X11/extensions/Xrender.h>
#include <beryl.h>
#include <beryl-text.h>
#include "thumbnail_tex.h"
#define GET_THUMB_DISPLAY(d) \
((ThumbDisplay *) (d)->privates[displayPrivateIndex].ptr)
#define THUMB_DISPLAY(d) \
ThumbDisplay *td = GET_THUMB_DISPLAY (d)
#define GET_THUMB_SCREEN(s, td) \
((ThumbScreen *) (s)->privates[(td)->screenPrivateIndex].ptr)
#define THUMB_SCREEN(s) \
ThumbScreen *ts = GET_THUMB_SCREEN (s, GET_THUMB_DISPLAY (s->display))
#define GET_THUMB_WINDOW(w, ts) \
((ThumbWindow *) (w)->privates[(ts)->windowPrivateIndex].ptr)
#define THUMB_WINDOW_PTR(w) \
GET_THUMB_WINDOW (w, \
GET_THUMB_SCREEN (w->screen, \
GET_THUMB_DISPLAY (w->screen->display)))
#define THUMB_WINDOW(w) \
ThumbWindow *tw = THUMB_WINDOW_PTR(w)
#define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption))
#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 THUMB_SIZE_DEFAULT 200
#define THUMB_SIZE_MIN 50
#define THUMB_SIZE_MAX 1500
#define THUMB_DELAY_DEFAULT 100
#define THUMB_DELAY_MIN 100
#define THUMB_DELAY_MAX 10000
#define THUMB_FADE_DEFAULT 0.5
#define THUMB_FADE_MIN 0.0
#define THUMB_FADE_MAX 5.0
#define THUMB_FADE_PRECISION 0.1
#define THUMB_COLOR_RED_DEFAULT 0x0000
#define THUMB_COLOR_GREEN_DEFAULT 0x0000
#define THUMB_COLOR_BLUE_DEFAULT 0x0000
#define THUMB_COLOR_ALPHA_DEFAULT 0x7fff
#define THUMB_BORDER_DEFAULT 16
#define THUMB_BORDER_MIN 1
#define THUMB_BORDER_MAX 32
#define THUMB_CURRENT_VIEWPORT_DEFAULT TRUE
#define THUMB_ALWAYS_TOP_DEFAULT FALSE
#define THUMB_WINDOW_LIKE_DEFAULT TRUE
#define THUMB_MIPMAP_DEFAULT FALSE
#define THUMB_TITLE_DEFAULT TRUE
#define THUMB_FONT_BOLD_DEFAULT TRUE
#define THUMB_FONT_SIZE_DEFAULT 12
#define THUMB_FONT_SIZE_MIN 6
#define THUMB_FONT_SIZE_MAX 36
#define THUMB_FONT_COLOR_RED 0x0
#define THUMB_FONT_COLOR_GREEN 0x0
#define THUMB_FONT_COLOR_BLUE 0x0
#define THUMB_FONT_COLOR_ALPHA 0xffff
#define THUMB_SCREEN_OPTION_SIZE 0
#define THUMB_SCREEN_OPTION_DELAY 1
#define THUMB_SCREEN_OPTION_COLOR 2
#define THUMB_SCREEN_OPTION_BORDER 3
#define THUMB_SCREEN_OPTION_FADE 4
#define THUMB_SCREEN_OPTION_CURRENT_VIEWPORT 5
#define THUMB_SCREEN_OPTION_ALWAYS_TOP 6
#define THUMB_SCREEN_OPTION_WINDOW_LIKE 7
#define THUMB_SCREEN_OPTION_MIPMAP 8
#define THUMB_SCREEN_OPTION_TITLE 9
#define THUMB_SCREEN_OPTION_FONT_BOLD 10
#define THUMB_SCREEN_OPTION_FONT_SIZE 11
#define THUMB_SCREEN_OPTION_FONT_COLOR 12
#define THUMB_SCREEN_OPTION_NUM 13
#define THUMB_MOUSE_UPDATE_SPEED 100
#define TEXT_DISTANCE 10
static int displayPrivateIndex;
typedef struct _ThumbDisplay {
int screenPrivateIndex;
HandleEventProc handleEvent;
Atom winIconGeometryAtom;
} ThumbDisplay;
typedef struct _Thumbnail {
int x;
int y;
int width;
int height;
float scale;
float opacity;
int offset;
CompWindow *win;
CompWindow *dock;
CompTexture textTexture;
Pixmap textPixmap;
int tWidth;
int tHeight;
} Thumbnail;
typedef struct _ThumbScreen {
int windowPrivateIndex;
CompTimeoutHandle mouseTimeout;
CompTimeoutHandle displayTimeout;
PreparePaintScreenProc preparePaintScreen;
PaintScreenProc paintScreen;
PaintWindowProc paintWindow;
DonePaintScreenProc donePaintScreen;
DamageWindowRectProc damageWindowRect;
WindowResizeNotifyProc windowResizeNotify;
PaintTransformedScreenProc paintTransformedScreen;
CompWindow *dock;
CompWindow *pointedWin;
Bool showingThumb;
Thumbnail thumb;
Thumbnail oldThumb;
Bool painted;
CompTexture glowTexture;
CompTexture windowTexture;
int x;
int y;
CompOption opt[THUMB_SCREEN_OPTION_NUM];
} ThumbScreen;
typedef struct _IconGeometry {
int x;
int y;
int width;
int height;
Bool isSet;
} IconGeometry;
typedef struct _ThumbWindow {
IconGeometry ig;
} ThumbWindow;
char * getUtf8Property(CompDisplay *d,Window id,Atom atom)
{
Atom type;
int format;
unsigned long nitems;
unsigned long bytes_after;
char *val;
int result;
char *retval;
type = None;
val = NULL;
result = XGetWindowProperty (d->display, id, atom, 0L, 65536, False,
d->utf8StringAtom, &type, &format, &nitems,
&bytes_after, (unsigned char **)&val);
if (result != Success)
return NULL;
if (type != d->utf8StringAtom || format != 8 || nitems == 0)
{
if (val)
XFree (val);
return NULL;
}
if ((retval = (char *) malloc(nitems + 1)) != NULL) {
bzero (retval, nitems + 1);
strncpy (retval, val, nitems);
}
XFree (val);
return retval;
}
char * getTextProperty(CompDisplay *d,Window id,Atom atom)
{
XTextProperty text;
char *retval;
text.nitems = 0;
if (XGetTextProperty (d->display, id, &text, atom))
{
if ((retval = (char *) malloc(text.nitems + 1)) != NULL) {
bzero (retval, text.nitems + 1);
strncpy (retval, (char *)text.value, text.nitems);
}
if (text.value)
XFree (text.value);
}
else
{
retval = NULL;
}
return retval;
}
char * getWindowName(CompWindow *w)
{
char *name = NULL;
CompDisplay *d = w->screen->display;
Atom visName = XInternAtom(d->display, "_NET_WM_VISIBLE_NAME", FALSE);
name = getUtf8Property (d, w->id, visName);
if (name == NULL)
name = getUtf8Property(d, w->id, d->wmNameAtom);
if (name == NULL)
name = getTextProperty (d, w->id, XA_WM_NAME);
return name;
}
static void freeThumbText(CompScreen *s,Thumbnail *t)
{
if (!t->textPixmap)
return;
releasePixmapFromTexture(s,&t->textTexture);
initTexture (s, &t->textTexture);
XFreePixmap(s->display->display,t->textPixmap);
t->textPixmap = None;
}
static void renderThumbText(CompScreen *s,Thumbnail *t, Bool freeThumb)
{
THUMB_SCREEN(s);
if (freeThumb)
freeThumbText(s,t);
CompTextAttrib tA;
tA.maxwidth = t->width;
tA.maxheight = 100;
tA.screen = s;
tA.size = ts->opt[THUMB_SCREEN_OPTION_FONT_SIZE].value.i;
tA.color[0] = ts->opt[THUMB_SCREEN_OPTION_FONT_COLOR].value.c[0];
tA.color[1] = ts->opt[THUMB_SCREEN_OPTION_FONT_COLOR].value.c[1];
tA.color[2] = ts->opt[THUMB_SCREEN_OPTION_FONT_COLOR].value.c[2];
tA.color[3] = ts->opt[THUMB_SCREEN_OPTION_FONT_COLOR].value.c[3];
tA.style = (ts->opt[THUMB_SCREEN_OPTION_FONT_BOLD].value.b)?
TEXT_STYLE_BOLD : TEXT_STYLE_NORMAL;
tA.family = "Sans";
tA.ellipsize = TRUE;
tA.text = getWindowName(t->win);
if (!tA.text)
return;
int stride;
void *data;
initTexture (s, &t->textTexture);
if ((*s->display->fileToImage)(s->display,TEXT_ID,(char *)&tA,
&t->tWidth,&t->tHeight,&stride,&data))
{
t->textPixmap = (Pixmap)data;
bindPixmapToTexture(s,&t->textTexture,t->textPixmap,
t->tWidth,t->tHeight,32);
}
else
{
t->textPixmap = None;
t->tWidth = 0;
t->tHeight = 0;
}
free (tA.text);
}
static void
updateWindowIconGeometry (CompWindow *w)
{
THUMB_DISPLAY(w->screen->display);
THUMB_WINDOW(w);
Atom actual;
int result, format;
unsigned long n, left;
unsigned char *data;
unsigned long *mydata;
result = XGetWindowProperty (w->screen->display->display,
w->id, td->winIconGeometryAtom, 0L, 4L,
FALSE, XA_CARDINAL, &actual, &format, &n,
&left, &data);
mydata = (unsigned long *) data;
tw->ig.isSet = FALSE;
if (result == Success && actual == XA_CARDINAL && n == 4)
{
tw->ig.x = mydata[0];
tw->ig.y = mydata[1];
tw->ig.width = mydata[2];
tw->ig.height = mydata[3];
tw->ig.isSet = TRUE;
XFree (data);
}
}
static void
damageThumbRegion (CompScreen *s, Thumbnail *t)
{
REGION region;
region.extents.x1 = t->x - t->offset;
region.extents.y1 = t->y - t->offset;
region.extents.x2 = region.extents.x1 + t->width + (t->offset * 2);
region.extents.y2 = region.extents.y1 + t->height + (t->offset * 2);
if (t->textPixmap)
region.extents.y2 += t->tHeight + TEXT_DISTANCE;
region.rects = ®ion.extents;
region.numRects = region.size = 1;
damageScreenRegion(s, ®ion);
}
#define GET_DISTANCE(a,b) \
(sqrt((((a)[0] - (b)[0]) * ((a)[0] - (b)[0])) + \
(((a)[1] - (b)[1]) * ((a)[1] - (b)[1]))))
static void
thumbUpdateThumbnail( CompScreen *s)
{
THUMB_SCREEN(s);
if (ts->thumb.win == ts->pointedWin)
return;
if (ts->thumb.opacity > 0.0 && ts->oldThumb.opacity > 0.0)
return;
if (ts->thumb.win)
damageThumbRegion(s,&ts->thumb);
freeThumbText(s,&ts->oldThumb);
ts->oldThumb = ts->thumb;
ts->thumb.win = ts->pointedWin;
ts->thumb.dock = ts->dock;
if (!ts->thumb.win)
return;
float maxSize = ts->opt[THUMB_SCREEN_OPTION_SIZE].value.i;
double scale = 1.0;
// do we nee to scale the window down?
if (WIN_W(ts->thumb.win) > maxSize || WIN_H(ts->thumb.win) > maxSize)
{
if (WIN_W(ts->thumb.win) >= WIN_H(ts->thumb.win))
scale = maxSize / WIN_W(ts->thumb.win);
else
scale = maxSize / WIN_H(ts->thumb.win);
}
ts->thumb.width = WIN_W(ts->thumb.win) * scale;
ts->thumb.height = WIN_H(ts->thumb.win) * scale;
ts->thumb.scale = scale;
THUMB_WINDOW(ts->thumb.win);
if (ts->opt[THUMB_SCREEN_OPTION_TITLE].value.b)
renderThumbText(s,&ts->thumb,FALSE);
else
freeThumbText(s,&ts->thumb);
int igMidPoint[2] = { tw->ig.x + (tw->ig.width / 2),
tw->ig.y + (tw->ig.height / 2)};
int tMidPoint[2];
int tPos[2];
int tmpPos[2];
float distance = 1000000;
int off = ts->opt[THUMB_SCREEN_OPTION_BORDER].value.i;
int oDev = screenGetOutputDev(s,tw->ig.x,tw->ig.y,tw->ig.width,tw->ig.height);
int ox1,oy1,ox2,oy2,ow,oh;
if (s->nOutputDev == 1 || oDev > s->nOutputDev)
{
ox1 = 0;
oy1 = 0;
ox2 = s->width;
oy2 = s->height;
ow = s->width;
oh = s->height;
}
else
{
ox1 = s->outputDev[oDev].region.extents.x1;
ox2 = s->outputDev[oDev].region.extents.x2;
oy1 = s->outputDev[oDev].region.extents.y1;
oy2 = s->outputDev[oDev].region.extents.y2;
ow = ox2 - ox1;
oh = oy2 - oy1;
}
int tHeight = ts->thumb.height;
if (ts->thumb.textPixmap)
tHeight += ts->thumb.tHeight + TEXT_DISTANCE;
// failsave position
tPos[0] = igMidPoint[0] - (ts->thumb.width / 2.0);
if (tw->ig.y - tHeight >= 0)
{
tPos[1] = tw->ig.y - tHeight;
}
else
{
tPos[1] = tw->ig.y + tw->ig.height;
}
// above
tmpPos[0] = igMidPoint[0] - (ts->thumb.width / 2.0);
if (tmpPos[0] - off < ox1)
tmpPos[0] = ox1 + off;
if (tmpPos[0] + off + ts->thumb.width > ox2)
{
if (ts->thumb.width + (2*off) <= ow)
tmpPos[0] = ox2 - ts->thumb.width - off;
else
tmpPos[0] = ox1 + off;
}
tMidPoint[0] = tmpPos[0] + (ts->thumb.width / 2.0);
tmpPos[1] = WIN_Y(ts->dock) - tHeight - off;
tMidPoint[1] = tmpPos[1] + (tHeight / 2.0);
if (tmpPos[1] > oy1)
{
tPos[0] = tmpPos[0];
tPos[1] = tmpPos[1];
distance = GET_DISTANCE(igMidPoint,tMidPoint);
}
// below
tmpPos[1] = WIN_Y(ts->dock) + WIN_H(ts->dock) + off;
tMidPoint[1] = tmpPos[1] + (tHeight / 2.0);
if (tmpPos[1] + tHeight + off < oy2 &&
GET_DISTANCE(igMidPoint,tMidPoint) < distance)
{
tPos[0] = tmpPos[0];
tPos[1] = tmpPos[1];
distance = GET_DISTANCE(igMidPoint,tMidPoint);
}
// left
tmpPos[1] = igMidPoint[1] - (tHeight / 2.0);
if (tmpPos[1] - off < oy1)
tmpPos[1] = oy1 + off;
if (tmpPos[1] + off + tHeight > oy2)
{
if (tHeight + (2*off) <= oh)
tmpPos[1] = oy2 - ts->thumb.height - off;
else
tmpPos[1] = oy1 + off;
}
tMidPoint[1] = tmpPos[1] + (tHeight / 2.0);
tmpPos[0] = WIN_X(ts->dock) - ts->thumb.width - off;
tMidPoint[0] = tmpPos[0] + (ts->thumb.width / 2.0);
if (tmpPos[0] > ox1 &&
GET_DISTANCE(igMidPoint,tMidPoint) < distance)
{
tPos[0] = tmpPos[0];
tPos[1] = tmpPos[1];
distance = GET_DISTANCE(igMidPoint,tMidPoint);
}
// right
tmpPos[0] = WIN_X(ts->dock) + WIN_W(ts->dock) + off;
tMidPoint[0] = tmpPos[0] + (ts->thumb.width / 2.0);
if (tmpPos[0] + ts->thumb.width + off < ox2 &&
GET_DISTANCE(igMidPoint,tMidPoint) < distance)
{
tPos[0] = tmpPos[0];
tPos[1] = tmpPos[1];
distance = GET_DISTANCE(igMidPoint,tMidPoint);
}
ts->thumb.x = tPos[0];
ts->thumb.y = tPos[1];
ts->thumb.offset = off;
ts->thumb.opacity = 0.0;
damageThumbRegion(s,&ts->thumb);
}
static Bool
thumbShowThumbnail (void *vs)
{
CompScreen *s = (CompScreen *) vs;
THUMB_SCREEN(s);
ts->showingThumb = TRUE;
thumbUpdateThumbnail(s);
damageThumbRegion(s,&ts->thumb);
return TRUE;
}
static Bool
checkPosition (CompWindow *w)
{
THUMB_SCREEN(w->screen);
if (ts->opt[THUMB_SCREEN_OPTION_CURRENT_VIEWPORT].value.b)
{
// TODO: We need a faster calculation here
Bool onViewport = FALSE;
Region reg = XCreateRegion();
XIntersectRegion(w->region,&w->screen->region,reg);
if (reg->numRects)
onViewport = TRUE;
XDestroyRegion(reg);
if (!onViewport)
return FALSE;
}
return TRUE;
}
static Bool
thumbUpdateMouse (void *vs)
{
CompScreen *s = (CompScreen *) vs;
THUMB_SCREEN(s);
int winX, winY;
int rootX, rootY;
unsigned int mask_return;
Window root_return;
Window child_return;
XQueryPointer(s->display->display, s->root,
&root_return, &child_return,
&rootX, &rootY, &winX,
&winY, &mask_return);
CompWindow *w = findWindowAtDisplay(s->display, child_return);
if (w && w->type & CompWindowTypeDockMask)
{
if (ts->dock != w)
{
ts->dock = w;
if (ts->displayTimeout)
{
compRemoveTimeout(ts->displayTimeout);
ts->displayTimeout = 0;
}
ts->pointedWin = NULL;
ts->showingThumb = FALSE;
}
CompWindow *cw = s->windows;
CompWindow *found = NULL;
for (;cw && !found;cw = cw->next)
{
THUMB_WINDOW(cw);
if (!tw->ig.isSet)
continue;
if (cw->attrib.map_state != IsViewable && !cw->thumbPixmap)
continue;
if (cw->state & CompWindowStateSkipTaskbarMask)
continue;
if (cw->state & CompWindowStateSkipPagerMask)
continue;
if (cw->state & CompWindowStateOffscreenMask)
continue;
if (!cw->managed)
continue;
if (rootX >= tw->ig.x && rootX < tw->ig.x + tw->ig.width &&
rootY >= tw->ig.y && rootY < tw->ig.y + tw->ig.height &&
checkPosition(cw))
{
found = cw;
}
}
if (found)
{
if (!ts->showingThumb &&
!(ts->thumb.opacity != 0.0 && ts->thumb.win == found))
{
if (ts->displayTimeout)
{
if (ts->pointedWin != found)
{
compRemoveTimeout(ts->displayTimeout);
ts->displayTimeout = compAddTimeout(
ts->opt[THUMB_SCREEN_OPTION_DELAY].value.i,
thumbShowThumbnail, s);
}
}
else
{
ts->displayTimeout =
compAddTimeout(ts->opt[THUMB_SCREEN_OPTION_DELAY].value.i,
thumbShowThumbnail, s);
}
}
ts->pointedWin = found;
thumbUpdateThumbnail(s);
}
else
{
ts->dock = NULL;
if (ts->displayTimeout)
{
compRemoveTimeout(ts->displayTimeout);
ts->displayTimeout = 0;
}
ts->pointedWin = 0;
ts->showingThumb = FALSE;
}
} else
{
ts->dock = NULL;
if (ts->displayTimeout)
{
compRemoveTimeout(ts->displayTimeout);
ts->displayTimeout = 0;
}
ts->pointedWin = NULL;
ts->showingThumb = FALSE;
}
ts->mouseTimeout = compAddTimeout(THUMB_MOUSE_UPDATE_SPEED, thumbUpdateMouse, s);
return FALSE;
}
static void
thumbScreenInitOptions (ThumbScreen *ts)
{
CompOption *o;
o = &ts->opt[THUMB_SCREEN_OPTION_SIZE];
o->name = "thumb_size";
o->shortDesc = N_("Thumbnail Size");
o->longDesc = N_("Thumbnail window Size.");
o->type = CompOptionTypeInt;
o->value.i = THUMB_SIZE_DEFAULT;
o->rest.i.min = THUMB_SIZE_MIN;
o->rest.i.max = THUMB_SIZE_MAX;
o->group = N_("Settings");
o->subGroup = "";
o->advanced = False;
o->displayHints = "";
o = &ts->opt[THUMB_SCREEN_OPTION_DELAY];
o->name = "show_delay";
o->shortDesc = N_("Show Delay");
o->longDesc = N_("Time (in ms) before Thumbnail is shown "
"when hovering over a taskbar button (10-10000).");
o->type = CompOptionTypeInt;
o->value.i = THUMB_DELAY_DEFAULT;
o->rest.i.min = THUMB_DELAY_MIN;
o->rest.i.max = THUMB_DELAY_MAX;
o->group = N_("Settings");
o->subGroup = "";
o->advanced = False;
o->displayHints = "";
o = &ts->opt[THUMB_SCREEN_OPTION_BORDER];
o->name = "border";
o->shortDesc = N_("Thumbnail Border Size");
o->longDesc = N_("Size of Thumbnail Border.");
o->type = CompOptionTypeInt;
o->value.i = THUMB_BORDER_DEFAULT;
o->rest.i.min = THUMB_BORDER_MIN;
o->rest.i.max = THUMB_BORDER_MAX;
o->group = N_("Settings");
o->subGroup = "";
o->advanced = False;
o->displayHints = "";
o = &ts->opt[THUMB_SCREEN_OPTION_COLOR];
o->name = "thumb_color";
o->group = N_("Settings");
o->subGroup = N_("");
o->advanced = False;
o->shortDesc = N_("Thumbnail Border Glow Color");
o->longDesc = N_("Thumbnail Background and Border Glow Color.");
o->displayHints = "";
o->type = CompOptionTypeColor;
o->value.c[0] = THUMB_COLOR_RED_DEFAULT;
o->value.c[1] = THUMB_COLOR_GREEN_DEFAULT;
o->value.c[2] = THUMB_COLOR_BLUE_DEFAULT;
o->value.c[3] = THUMB_COLOR_ALPHA_DEFAULT;
o = &ts->opt[THUMB_SCREEN_OPTION_FADE];
o->name = "fade_speed";
o->group = N_("Settings");
o->subGroup = N_("");
o->advanced = False;
o->shortDesc = N_("Fade In/Out Duration");
o->longDesc = N_("Fade In/Out Duration in seconds.");
o->displayHints = "";
o->type = CompOptionTypeFloat;
o->value.f = THUMB_FADE_DEFAULT;
o->rest.f.min = THUMB_FADE_MIN;
o->rest.f.max = THUMB_FADE_MAX;
o->rest.f.precision = THUMB_FADE_PRECISION;
o = &ts->opt[THUMB_SCREEN_OPTION_CURRENT_VIEWPORT];
o->name = "current_viewport";
o->shortDesc = N_("Taskbar Shows Only Windows of Current Viewport");
o->longDesc = N_("Set it if the Taskbar shows only Windows of Current Viewport.");
o->type = CompOptionTypeBool;
o->value.b = THUMB_CURRENT_VIEWPORT_DEFAULT;
o->group = N_("Settings");
o->subGroup = N_("Taskbar");
o->advanced = False;
o->displayHints = "";
o = &ts->opt[THUMB_SCREEN_OPTION_ALWAYS_TOP];
o->name = "always_on_top";
o->shortDesc = N_("Thumbnails Always on Top");
o->longDesc = N_("Paint Thumbnails Always on Top.");
o->type = CompOptionTypeBool;
o->value.b = THUMB_ALWAYS_TOP_DEFAULT;
o->group = N_("Settings");
o->subGroup = N_("Taskbar");
o->advanced = False;
o->displayHints = "";
o = &ts->opt[THUMB_SCREEN_OPTION_WINDOW_LIKE];
o->name = "window_like";
o->shortDesc = N_("Paint Window Like Background");
o->longDesc = N_("Paint Window Like Background instead of Glow.");
o->type = CompOptionTypeBool;
o->value.b = THUMB_WINDOW_LIKE_DEFAULT;
o->group = N_("Settings");
o->subGroup = "";
o->advanced = False;
o->displayHints = "";
o = &ts->opt[THUMB_SCREEN_OPTION_MIPMAP];
o->name = "mipmap";
o->shortDesc = N_("Generate Mipmaps");
o->longDesc = N_("Generate mipmaps when possible for higher quality "
"scaling.");
o->type = CompOptionTypeBool;
o->value.b = THUMB_MIPMAP_DEFAULT;
o->group = N_("Settings");
o->subGroup = "";
o->advanced = TRUE;
o->displayHints = "";
o = &ts->opt[THUMB_SCREEN_OPTION_TITLE];
o->name = "title_enabled";
o->shortDesc = N_("Enable Titles");
o->longDesc = N_("Show Window Title in Thumbnail.");
o->type = CompOptionTypeBool;
o->value.b = THUMB_TITLE_DEFAULT;
o->group = N_("Settings");
o->subGroup = N_("Window title");
o->advanced = FALSE;
o->displayHints = "";
o = &ts->opt[THUMB_SCREEN_OPTION_FONT_BOLD];
o->name = "font_bold";
o->shortDesc = N_("Bold Font");
o->longDesc = N_("Should be the window title Bold.");
o->type = CompOptionTypeBool;
o->value.b = THUMB_FONT_BOLD_DEFAULT;
o->group = N_("Settings");
o->subGroup = N_("Window title");
o->advanced = FALSE;
o->displayHints = "";
o = &ts->opt[THUMB_SCREEN_OPTION_FONT_SIZE];
o->name = "font_size";
o->shortDesc = N_("Font Size");
o->longDesc = N_("Window title Font Size.");
o->type = CompOptionTypeInt;
o->group = N_("Settings");
o->subGroup = N_("Window title");
o->advanced = FALSE;
o->displayHints = "";
o->value.i = THUMB_FONT_SIZE_DEFAULT;
o->rest.i.min = THUMB_FONT_SIZE_MIN;
o->rest.i.max = THUMB_FONT_SIZE_MAX;
o = &ts->opt[THUMB_SCREEN_OPTION_FONT_COLOR];
o->name = "font_color";
o->group = N_("Settings");
o->subGroup = N_("Window title");
o->advanced = False;
o->shortDesc = N_("Font Color");
o->longDesc = N_("Window title Font Color.");
o->advanced = FALSE;
o->displayHints = "";
o->type = CompOptionTypeColor;
o->value.c[0] = THUMB_FONT_COLOR_RED;
o->value.c[1] = THUMB_FONT_COLOR_GREEN;
o->value.c[2] = THUMB_FONT_COLOR_BLUE;
o->value.c[3] = THUMB_FONT_COLOR_ALPHA;
}
static CompOption *
thumbGetScreenOptions (CompScreen *screen,
int *count)
{
if (screen)
{
THUMB_SCREEN (screen);
*count = NUM_OPTIONS (ts);
return ts->opt;
}
else
{
ThumbScreen * ts = malloc(sizeof(ThumbScreen));
thumbScreenInitOptions(ts);
*count = NUM_OPTIONS (ts);
return ts->opt;
}
}
static Bool
thumbSetScreenOption (CompScreen *screen,
char *name,
CompOptionValue *value)
{
CompOption *o;
int index;
THUMB_SCREEN (screen);
o = compFindOption (ts->opt, NUM_OPTIONS (ts), name, &index);
if (!o)
return FALSE;
switch (index)
{
case THUMB_SCREEN_OPTION_SIZE:
case THUMB_SCREEN_OPTION_DELAY:
case THUMB_SCREEN_OPTION_BORDER:
case THUMB_SCREEN_OPTION_FONT_SIZE:
if (compSetIntOption (o, value))
{
return TRUE;
}
break;
case THUMB_SCREEN_OPTION_COLOR:
case THUMB_SCREEN_OPTION_FONT_COLOR:
if (compSetColorOption(o, value))
{
return TRUE;
}
break;
case THUMB_SCREEN_OPTION_FADE:
if (compSetFloatOption(o, value))
{
return TRUE;
}
break;
case THUMB_SCREEN_OPTION_ALWAYS_TOP:
case THUMB_SCREEN_OPTION_CURRENT_VIEWPORT:
case THUMB_SCREEN_OPTION_WINDOW_LIKE:
case THUMB_SCREEN_OPTION_MIPMAP:
case THUMB_SCREEN_OPTION_TITLE:
case THUMB_SCREEN_OPTION_FONT_BOLD:
if (compSetBoolOption(o, value))
{
return TRUE;
}
default:
break;
}
return FALSE;
}
static void
thumbWindowResizeNotify(CompWindow *w, int dx, int dy, int dwidth, int dheight,
Bool preview)
{
THUMB_SCREEN ( w->screen );
if (!preview)
thumbUpdateThumbnail(w->screen);
UNWRAP(ts,w->screen,windowResizeNotify);
(*w->screen->windowResizeNotify) (w, dx, dy, dwidth, dheight, preview);
WRAP(ts,w->screen,windowResizeNotify,thumbWindowResizeNotify);
}
static void
thumbHandleEvent (CompDisplay *d,
XEvent *event)
{
THUMB_DISPLAY (d);
UNWRAP (td, d, handleEvent);
(*d->handleEvent) (d, event);
WRAP (td, d, handleEvent, thumbHandleEvent);
CompWindow *w;
switch (event->type)
{
case PropertyNotify:
if (event->xproperty.atom == td->winIconGeometryAtom)
{
w = findWindowAtDisplay(d, event->xproperty.window);
if (w)
{
updateWindowIconGeometry(w);
}
}
break;
case ButtonPress:
{
CompScreen *s = findScreenAtDisplay (d, event->xbutton.root);
if (!s)
break;
THUMB_SCREEN(s);
ts->dock = NULL;
if (ts->displayTimeout)
{
compRemoveTimeout(ts->displayTimeout);
ts->displayTimeout = 0;
}
ts->pointedWin = 0;
ts->showingThumb = FALSE;
}
break;
default:
break;
}
}
static void
thumbPaintThumb(CompScreen *s,Thumbnail *t)
{
THUMB_SCREEN(s);
DrawWindowGeometryProc oldDrawWindowGeometry;
AddWindowGeometryProc oldAddWindowGeometry;
CompWindow *w = t->win;
if (!w)
return;
int wx = t->x;
int wy = t->y;
float width = t->width;
float height = t->height;
WindowPaintAttrib sAttrib = w->paint;
if (t->textPixmap)
height += t->tHeight + TEXT_DISTANCE;
/* Wrap drawWindowGeometry to make sure the general
drawWindowGeometry function is used */
oldDrawWindowGeometry = w->screen->drawWindowGeometry;
w->screen->drawWindowGeometry = getBaseDrawWindowGeometry();
oldAddWindowGeometry = w->screen->addWindowGeometry;
w->screen->addWindowGeometry = getBaseAddWindowGeometry();
unsigned int mask = PAINT_WINDOW_TRANSFORMED_MASK;
if (w->attrib.map_state == IsViewable || w->thumbPixmap)
{
int off = t->offset;
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
if (ts->opt[THUMB_SCREEN_OPTION_WINDOW_LIKE].value.b)
{
glColor4f(1.0,1.0,1.0,t->opacity);
enableTexture(s, &ts->windowTexture, COMP_TEXTURE_FILTER_GOOD);
}
else
{
glColor4us(ts->opt[THUMB_SCREEN_OPTION_COLOR].value.c[0],
ts->opt[THUMB_SCREEN_OPTION_COLOR].value.c[1],
ts->opt[THUMB_SCREEN_OPTION_COLOR].value.c[2],
ts->opt[THUMB_SCREEN_OPTION_COLOR].value.c[3] * t->opacity);
enableTexture(s, &ts->glowTexture, COMP_TEXTURE_FILTER_GOOD);
}
glBegin(GL_QUADS);
glTexCoord2f(1,0);
glVertex2f(wx,wy);
glVertex2f(wx,wy+height);
glVertex2f(wx+width,wy+height);
glVertex2f(wx+width,wy);
glTexCoord2f(0,1);
glVertex2f(wx-off,wy-off);
glTexCoord2f(0,0);
glVertex2f(wx-off,wy);
glTexCoord2f(1,0);
glVertex2f(wx,wy);
glTexCoord2f(1,1);
glVertex2f(wx,wy-off);
glTexCoord2f(1,1);
glVertex2f(wx+width,wy-off);
glTexCoord2f(1,0);
glVertex2f(wx+width,wy);
glTexCoord2f(0,0);
glVertex2f(wx+width+off,wy);
glTexCoord2f(0,1);
glVertex2f(wx+width+off,wy-off);
glTexCoord2f(0,0);
glVertex2f(wx-off,wy+height);
glTexCoord2f(0,1);
glVertex2f(wx-off,wy+height+off);
glTexCoord2f(1,1);
glVertex2f(wx,wy+height+off);
glTexCoord2f(1,0);
glVertex2f(wx,wy+height);
glTexCoord2f(1,0);
glVertex2f(wx+width,wy+height);
glTexCoord2f(1,1);
glVertex2f(wx+width,wy+height+off);
glTexCoord2f(0,1);
glVertex2f(wx+width+off,wy+height+off);
glTexCoord2f(0,0);
glVertex2f(wx+width+off,wy+height);
glTexCoord2f(1,1);
glVertex2f(wx,wy-off);
glTexCoord2f(1,0);
glVertex2f(wx,wy);
glTexCoord2f(1,0);
glVertex2f(wx+width,wy);
glTexCoord2f(1,1);
glVertex2f(wx+width,wy-off);
glTexCoord2f(1,0);
glVertex2f(wx,wy+height);
glTexCoord2f(1,1);
glVertex2f(wx,wy+height+off);
glTexCoord2f(1,1);
glVertex2f(wx+width,wy+height+off);
glTexCoord2f(1,0);
glVertex2f(wx+width,wy+height);
glTexCoord2f(0,0);
glVertex2f(wx-off,wy);
glTexCoord2f(0,0);
glVertex2f(wx-off,wy+height);
glTexCoord2f(1,0);
glVertex2f(wx,wy+height);
glTexCoord2f(1,0);
glVertex2f(wx,wy);
glTexCoord2f(1,0);
glVertex2f(wx+width,wy);
glTexCoord2f(1,0);
glVertex2f(wx+width,wy+height);
glTexCoord2f(0,0);
glVertex2f(wx+width+off,wy+height);
glTexCoord2f(0,0);
glVertex2f(wx+width+off,wy);
glEnd();
if (ts->opt[THUMB_SCREEN_OPTION_WINDOW_LIKE].value.b)
{
disableTexture(s, &ts->windowTexture);
}
else
{
disableTexture(s, &ts->glowTexture);
}
glColor4usv(defaultColor);
glBlendFunc(GL_ONE,GL_ONE_MINUS_SRC_ALPHA);
if (t->textPixmap)
{
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glColor4f(1.0,1.0,1.0,t->opacity);
enableTexture(s, &t->textTexture,COMP_TEXTURE_FILTER_GOOD);
CompMatrix *m = &t->textTexture.matrix;
float ox = 0.0;
if (t->tWidth < width)
ox = (width - t->tWidth) / 2.0;
float w = MIN(width,t->tWidth);
float h = t->tHeight;
glBegin(GL_QUADS);
glTexCoord2f(COMP_TEX_COORD_X(m,0),COMP_TEX_COORD_Y(m,0));
glVertex2f(wx+ox,wy+height-h);
glTexCoord2f(COMP_TEX_COORD_X(m,0),COMP_TEX_COORD_Y(m,h));
glVertex2f(wx+ox,wy+height);
glTexCoord2f(COMP_TEX_COORD_X(m,w),COMP_TEX_COORD_Y(m,h));
glVertex2f(wx+ox+w,wy+height);
glTexCoord2f(COMP_TEX_COORD_X(m,w),COMP_TEX_COORD_Y(m,0));
glVertex2f(wx+ox+w,wy+height-h);
glEnd();
disableTexture(s, &t->textTexture);
glColor4usv(defaultColor);
}
glDisable(GL_BLEND);
screenTexEnvMode (s,GL_REPLACE);
glColor4usv(defaultColor);
sAttrib.opacity *= t->opacity;
sAttrib.yScale = t->scale;
sAttrib.xScale = t->scale;
sAttrib.xTranslate =
wx - w->attrib.x + w->input.left * sAttrib.xScale;
sAttrib.yTranslate = wy - w->attrib.y + w->input.top * sAttrib.yScale;
GLenum filter = s->display->textureFilter;
if (ts->opt[THUMB_SCREEN_OPTION_MIPMAP].value.b)
s->display->textureFilter = GL_LINEAR_MIPMAP_LINEAR;
(w->screen->drawWindow) (w, &sAttrib, getInfiniteRegion(), mask);
s->display->textureFilter = filter;
}
w->screen->drawWindowGeometry = oldDrawWindowGeometry;
w->screen->addWindowGeometry = oldAddWindowGeometry;
}
static void thumbPreparePaintScreen(CompScreen * s, int ms)
{
THUMB_SCREEN(s);
float val = ms;
val /= 1000;
val /= ts->opt[THUMB_SCREEN_OPTION_FADE].value.f;
if (otherScreenGrabExist(s, 0))
{
ts->dock = NULL;
if (ts->displayTimeout)
{
compRemoveTimeout(ts->displayTimeout);
ts->displayTimeout = 0;
}
ts->pointedWin = 0;
ts->showingThumb = FALSE;
}
if (ts->showingThumb && ts->thumb.win == ts->pointedWin)
{
ts->thumb.opacity = MIN(1.0,ts->thumb.opacity + val);
}
if (!ts->showingThumb || ts->thumb.win != ts->pointedWin)
{
ts->thumb.opacity = MAX(0.0,ts->thumb.opacity - val);
}
ts->oldThumb.opacity = MAX(0.0,ts->oldThumb.opacity - val);
UNWRAP(ts, s, preparePaintScreen);
(*s->preparePaintScreen) (s,ms);
WRAP(ts, s, preparePaintScreen, thumbPreparePaintScreen);
}
static void thumbDonePaintScreen(CompScreen * s)
{
THUMB_SCREEN(s);
if (ts->thumb.opacity > 0.0 && ts->thumb.opacity < 1.0)
damageThumbRegion(s,&ts->thumb);
if (ts->oldThumb.opacity > 0.0 && ts->oldThumb.opacity < 1.0)
damageThumbRegion(s,&ts->oldThumb);
UNWRAP(ts, s, donePaintScreen);
(*s->donePaintScreen) (s);
WRAP(ts, s, donePaintScreen, thumbDonePaintScreen);
}
static Bool
thumbPaintScreen (CompScreen *s,
const ScreenPaintAttrib *sAttrib,
Region region,
int output,
unsigned int mask)
{
Bool status;
THUMB_SCREEN (s);
ts->painted = FALSE;
ts->x = s->x;
ts->y = s->y;
unsigned int newMask = mask;
if ((ts->oldThumb.opacity > 0.0 && ts->oldThumb.win) ||
(ts->thumb.opacity > 0.0 && ts->thumb.win))
newMask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK;
UNWRAP (ts, s, paintScreen);
status = (*s->paintScreen) (s, sAttrib, region, output, newMask);
WRAP (ts, s, paintScreen, thumbPaintScreen);
if (ts->opt[THUMB_SCREEN_OPTION_ALWAYS_TOP].value.b && !ts->painted)
{
if (ts->oldThumb.opacity > 0.0 && ts->oldThumb.win)
{
glPushMatrix();
prepareXCoords(s, output,-DEFAULT_Z_CAMERA);
thumbPaintThumb(s,&ts->oldThumb);
glPopMatrix();
}
if (ts->thumb.opacity > 0.0 && ts->thumb.win)
{
glPushMatrix();
prepareXCoords(s, output,-DEFAULT_Z_CAMERA);
thumbPaintThumb(s,&ts->thumb);
glPopMatrix();
}
}
return status;
}
static void
thumbPaintTransformedScreen (CompScreen *s,
const ScreenPaintAttrib *sAttrib,
Region region,
int output,
unsigned int mask)
{
THUMB_SCREEN (s);
UNWRAP (ts, s, paintTransformedScreen);
(*s->paintTransformedScreen) (s, sAttrib, region, output, mask);
WRAP (ts, s, paintTransformedScreen, thumbPaintTransformedScreen);
if (ts->opt[THUMB_SCREEN_OPTION_ALWAYS_TOP].value.b &&
ts->x == s->x && ts->y == s->y)
{
ts->painted = TRUE;
if (ts->oldThumb.opacity > 0.0 && ts->oldThumb.win)
{
glPushMatrix();
(s->applyScreenTransform) (s, sAttrib, output);
prepareXCoords(s, output,-sAttrib->zTranslate);
thumbPaintThumb(s,&ts->oldThumb);
glPopMatrix();
}
if (ts->thumb.opacity > 0.0 && ts->thumb.win)
{
glPushMatrix();
(s->applyScreenTransform) (s, sAttrib, output);
prepareXCoords(s, output,-sAttrib->zTranslate);
thumbPaintThumb(s,&ts->thumb);
glPopMatrix();
}
}
}
static Bool
thumbPaintWindow (CompWindow *w,
const WindowPaintAttrib *attrib,
Region region,
unsigned int mask)
{
CompScreen *s = w->screen;
Bool status;
THUMB_SCREEN (s);
UNWRAP (ts, s, paintWindow);
status = (*s->paintWindow) (w, attrib, region, mask);
WRAP (ts, s, paintWindow, thumbPaintWindow);
if (!ts->opt[THUMB_SCREEN_OPTION_ALWAYS_TOP].value.b &&
ts->x == s->x && ts->y == s->y)
{
if (ts->oldThumb.opacity > 0.0 && ts->oldThumb.win &&
ts->oldThumb.dock == w)
{
thumbPaintThumb(s,&ts->oldThumb);
}
if (ts->thumb.opacity > 0.0 && ts->thumb.win && ts->thumb.dock == w)
{
thumbPaintThumb(s,&ts->thumb);
}
}
return status;
}
static Bool
thumbDamageWindowRect (CompWindow *w,
Bool initial,
BoxPtr rect)
{
Bool status;
THUMB_SCREEN (w->screen);
if (ts->thumb.win == w && ts->thumb.opacity > 0.0)
damageThumbRegion(w->screen,&ts->thumb);
if (ts->oldThumb.win == w && ts->oldThumb.opacity > 0.0)
damageThumbRegion(w->screen,&ts->oldThumb);
UNWRAP (ts, w->screen, damageWindowRect);
status = (*w->screen->damageWindowRect) (w, initial, rect);
WRAP (ts, w->screen, damageWindowRect, thumbDamageWindowRect);
return status;
}
static Bool
thumbInitDisplay (CompPlugin *p,
CompDisplay *d)
{
ThumbDisplay *td;
td = malloc (sizeof (ThumbDisplay));
if (!td)
return FALSE;
td->screenPrivateIndex = allocateScreenPrivateIndex (d);
if (td->screenPrivateIndex < 0)
{
free (td);
return FALSE;
}
td->winIconGeometryAtom = XInternAtom (d->display,
"_NET_WM_ICON_GEOMETRY", FALSE);
WRAP (td, d, handleEvent, thumbHandleEvent);
d->privates[displayPrivateIndex].ptr = td;
return TRUE;
}
static void
thumbFiniDisplay (CompPlugin *p,
CompDisplay *d)
{
THUMB_DISPLAY (d);
freeScreenPrivateIndex (d, td->screenPrivateIndex);
UNWRAP (td, d, handleEvent);
free (td);
}
static Bool thumbInitWindow(CompPlugin * p, CompWindow * w)
{
ThumbWindow *tw;
THUMB_SCREEN(w->screen);
// create window
tw = calloc(1, sizeof(ThumbWindow));
if (!tw)
return FALSE;
w->privates[ts->windowPrivateIndex].ptr = tw;
updateWindowIconGeometry(w);
return TRUE;
}
static void thumbFiniWindow(CompPlugin * p, CompWindow * w)
{
THUMB_WINDOW(w);
THUMB_SCREEN(w->screen);
if (ts->thumb.win == w)
{
damageThumbRegion(w->screen,&ts->thumb);
ts->thumb.win = NULL;
ts->thumb.opacity = 0;
}
if (ts->oldThumb.win == w)
{
damageThumbRegion(w->screen,&ts->oldThumb);
ts->oldThumb.win = NULL;
ts->oldThumb.opacity = 0;
}
// free window pointer
free(tw);
}
static Bool
thumbInitScreen (CompPlugin *p,
CompScreen *s)
{
ThumbScreen *ts;
THUMB_DISPLAY (s->display);
ts = calloc (1,sizeof (ThumbScreen));
if (!ts)
return FALSE;
ts->windowPrivateIndex = allocateWindowPrivateIndex(s);
thumbScreenInitOptions (ts);
WRAP (ts, s, paintScreen, thumbPaintScreen);
WRAP (ts, s, damageWindowRect, thumbDamageWindowRect);
WRAP (ts, s, preparePaintScreen, thumbPreparePaintScreen);
WRAP (ts, s, donePaintScreen, thumbDonePaintScreen);
WRAP (ts, s, paintWindow, thumbPaintWindow);
WRAP (ts,s,windowResizeNotify, thumbWindowResizeNotify);
WRAP (ts, s, paintTransformedScreen, thumbPaintTransformedScreen);
ts->dock = NULL;
ts->pointedWin = NULL;
ts->displayTimeout = 0;
ts->thumb.win = NULL;
ts->oldThumb.win = NULL;
ts->showingThumb = FALSE;
s->privates[td->screenPrivateIndex].ptr = ts;
ts->mouseTimeout =
compAddTimeout(THUMB_MOUSE_UPDATE_SPEED, thumbUpdateMouse, s);
initTexture (s, &ts->glowTexture);
initTexture (s, &ts->windowTexture);
imageToTexture (s, &ts->glowTexture,glowTex,32,32);
imageToTexture (s, &ts->windowTexture,windowTex,32,32);
ts->thumb.textPixmap = None;
ts->oldThumb.textPixmap = None;
return TRUE;
}
static void
thumbFiniScreen (CompPlugin *p,
CompScreen *s)
{
THUMB_SCREEN (s);
UNWRAP (ts, s, paintScreen);
UNWRAP (ts, s, damageWindowRect);
UNWRAP (ts, s, preparePaintScreen);
UNWRAP (ts, s, donePaintScreen);
UNWRAP (ts, s, paintWindow);
UNWRAP (ts,s,windowResizeNotify);
UNWRAP (ts, s, paintTransformedScreen);
if (ts->mouseTimeout)
compRemoveTimeout (ts->mouseTimeout);
freeThumbText (s,&ts->thumb);
freeThumbText (s,&ts->oldThumb);
finiTexture (s, &ts->glowTexture);
finiTexture (s, &ts->windowTexture);
free (ts);
}
static Bool
thumbInit (CompPlugin *p)
{
displayPrivateIndex = allocateDisplayPrivateIndex ();
if (displayPrivateIndex < 0)
return FALSE;
return TRUE;
}
static void
thumbFini (CompPlugin *p)
{
if (displayPrivateIndex >= 0)
freeDisplayPrivateIndex (displayPrivateIndex);
}
CompPluginVTable thumbVTable = {
"thumbnail",
N_("Window Previews"),
N_("Window thumbnails at the taskbar"),
thumbInit,
thumbFini,
thumbInitDisplay,
thumbFiniDisplay,
thumbInitScreen,
thumbFiniScreen,
thumbInitWindow,
thumbFiniWindow,
0, /* thumbGetDisplayOptions */
0, /* thumbSetDisplayOption */
thumbGetScreenOptions,
thumbSetScreenOption,
0, /* Deps */
0, /* nDeps */
0, /* Features */
0, /* nFeatures */
BERYL_ABI_INFO,
"thumbnail",
"misc",
0,
0,
True,
};
CompPluginVTable *
getCompPluginInfo (void)
{
return &thumbVTable;
}
syntax highlighted by Code2HTML, v. 0.9.1