/* * * 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 #include #include #include #include #include #include #include #include #include #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; }