/* Copyright Robert Carr 2007 blablabla GPL etc * racarr@beryl-project.org */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "resizeinfo_options.h" #define PI 3.1415926 static int displayPrivateIndex; typedef struct _InfoDisplay { int screenPrivateIndex; HandleEventProc handleEvent; Atom resizeNotifyAtom; } InfoDisplay; typedef struct _InfoLayer { Pixmap pixmap; CompTexture texture; cairo_surface_t *surface; cairo_t *cr; } InfoLayer; typedef struct _InfoScreen { WindowGrabNotifyProc windowGrabNotify; WindowUngrabNotifyProc windowUngrabNotify; PaintOutputProc paintOutput; PreparePaintScreenProc preparePaintScreen; DonePaintScreenProc donePaintScreen; CompWindow *pWindow; Bool drawing; int fadeTime; InfoLayer backgroundLayer; InfoLayer textLayer; XRectangle resizeGeometry; } InfoScreen; #define RESIZE_POPUP_WIDTH 85 #define RESIZE_POPUP_HEIGHT 50 #define GET_INFO_DISPLAY(d) \ ((InfoDisplay *) (d)->privates[displayPrivateIndex].ptr) #define INFO_DISPLAY(d) \ InfoDisplay *id = GET_INFO_DISPLAY (d) #define GET_INFO_SCREEN(s, id) \ ((InfoScreen *) (s)->privates[(id)->screenPrivateIndex].ptr) #define INFO_SCREEN(s) \ InfoScreen *is = GET_INFO_SCREEN (s, GET_INFO_DISPLAY (s->display)) /* Set up an InfoLayer to build a cairo->opengl texture pipeline */ static void setupCairoLayer (CompScreen *s, InfoLayer *il) { XRenderPictFormat *format; Screen *screen; int w, h; screen = ScreenOfDisplay (s->display->display, s->screenNum); w = RESIZE_POPUP_WIDTH; h = RESIZE_POPUP_HEIGHT; format = XRenderFindStandardFormat (s->display->display, PictStandardARGB32); il->pixmap = XCreatePixmap (s->display->display, s->root, w, h, 32); if (!bindPixmapToTexture (s, &il->texture, il->pixmap, w, h, 32)) { compLogMessage (s->display, "resizeinfo", CompLogLevelWarn, "Bind Pixmap to Texture failure"); XFreePixmap (s->display->display, il->pixmap); return; } il->surface = cairo_xlib_surface_create_with_xrender_format (s->display->display, il->pixmap, screen, format, w, h); il->cr = cairo_create (il->surface); if (cairo_status (il->cr) != CAIRO_STATUS_SUCCESS) { compLogMessage (s->display, "resizeinfo", CompLogLevelWarn, "Could not create cairo context"); cairo_destroy (il->cr); cairo_surface_destroy (il->surface); XFreePixmap (s->display->display, il->pixmap); il->cr = NULL; return; } } /* Draw the window "size" derived from the window hints. We calculate width or height - base_width or base_height and divide it by the increment in each direction. For windows like terminals setting the proper size hints this gives us the number of columns/rows. */ static void updateTextLayer (CompScreen *s) { int baseWidth, baseHeight; int widthInc, heightInc; int width, height, xv, yv; unsigned short *color; char *info; cairo_t *cr; PangoLayout *layout; PangoFontDescription *font; int w, h; INFO_SCREEN (s); if (!is->textLayer.cr) return; baseWidth = is->pWindow->sizeHints.base_width; baseHeight = is->pWindow->sizeHints.base_height; widthInc = is->pWindow->sizeHints.width_inc; heightInc = is->pWindow->sizeHints.height_inc; width = is->resizeGeometry.width; height = is->resizeGeometry.height; color = resizeinfoGetTextColor (s->display); xv = (widthInc > 1) ? (width - baseWidth) / widthInc : width; yv = (heightInc > 1) ? (height - baseHeight) / heightInc : height; cr = is->textLayer.cr; /* Clear the context. */ cairo_save (cr); cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR); cairo_paint (cr); cairo_restore (cr); cairo_set_operator (cr, CAIRO_OPERATOR_OVER); asprintf (&info, "%d x %d", xv, yv); font = pango_font_description_new (); layout = pango_cairo_create_layout (is->textLayer.cr); pango_font_description_set_family (font,"Sans"); pango_font_description_set_absolute_size (font, 12 * PANGO_SCALE); pango_font_description_set_style (font, PANGO_STYLE_NORMAL); pango_font_description_set_weight (font, PANGO_WEIGHT_BOLD); pango_layout_set_font_description (layout, font); pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END); pango_layout_set_text (layout, info, -1); pango_layout_get_pixel_size (layout, &w, &h); cairo_move_to (cr, RESIZE_POPUP_WIDTH / 2.0f - w / 2.0f, RESIZE_POPUP_HEIGHT / 2.0f - h / 2.0f); pango_layout_set_width (layout, RESIZE_POPUP_WIDTH * PANGO_SCALE); pango_cairo_update_layout (cr, layout); cairo_set_source_rgba (cr, *(color) / (float)0xffff, *(color + 1) / (float)0xffff, *(color + 2) / (float)0xffff, *(color + 3) / (float)0xffff); pango_cairo_show_layout (cr, layout); pango_font_description_free (font); g_object_unref (layout); } /* Draw the background. We draw this on a second layer so that we do not have to draw it each time we have to update. Granted we could use some cairo trickery for this... */ static void drawCairoBackground (CompScreen *s) { cairo_t *cr; cairo_pattern_t *pattern; float border = 7.5; int height = RESIZE_POPUP_HEIGHT; int width = RESIZE_POPUP_WIDTH; float r, g, b, a; INFO_SCREEN (s); cr = is->backgroundLayer.cr; if (!cr) return; cairo_set_line_width (cr, 1.0f); /* Clear */ cairo_save (cr); cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR); cairo_paint (cr); cairo_restore (cr); cairo_set_operator (cr, CAIRO_OPERATOR_OVER); /* Setup Gradient */ pattern = cairo_pattern_create_linear (0, 0, width, height); r = resizeinfoGetGradient1Red (s->display) / (float)0xffff; g = resizeinfoGetGradient1Green (s->display) / (float)0xffff; b = resizeinfoGetGradient1Blue (s->display) / (float)0xffff; a = resizeinfoGetGradient1Alpha (s->display) / (float)0xffff; cairo_pattern_add_color_stop_rgba (pattern, 0.00f, r, g, b, a); r = resizeinfoGetGradient1Red (s->display) / (float)0xffff; g = resizeinfoGetGradient1Green (s->display) / (float)0xffff; b = resizeinfoGetGradient1Blue (s->display) / (float)0xffff; a = resizeinfoGetGradient1Alpha (s->display) / (float)0xffff; cairo_pattern_add_color_stop_rgba (pattern, 0.65f, r, g, b, a); r = resizeinfoGetGradient1Red (s->display) / (float)0xffff; g = resizeinfoGetGradient1Green (s->display) / (float)0xffff; b = resizeinfoGetGradient1Blue (s->display) / (float)0xffff; a = resizeinfoGetGradient1Alpha (s->display) / (float)0xffff; cairo_pattern_add_color_stop_rgba (pattern, 0.85f, r, g, b, a); cairo_set_source (cr, pattern); /* Rounded Rectangle! */ cairo_arc (cr, border, border, border, PI, 1.5f * PI); cairo_arc (cr, border + width - 2 * border, border, border, 1.5f * PI, 2.0 * PI); cairo_arc (cr, width - border, height - border, border, 0, PI / 2.0f); cairo_arc (cr, border, height - border, border, PI / 2.0f, PI); cairo_close_path (cr); cairo_fill_preserve (cr); /* Outline */ cairo_set_source_rgba (cr, 0.9f, 0.9f, 0.9f, 1.0f); cairo_stroke (cr); cairo_pattern_destroy (pattern); } static void gradientChanged (CompDisplay *d, CompOption *o, ResizeinfoDisplayOptions num) { CompScreen *s; for (s = d->screens; s; s = s->next) drawCairoBackground (s); } /* Handle the fade in /fade out. */ static void infoPreparePaintScreen (CompScreen *s, int ms) { INFO_SCREEN (s); if (is->fadeTime) { is->fadeTime -= ms; if (is->fadeTime < 0) is->fadeTime = 0; } UNWRAP (is, s, preparePaintScreen); (*s->preparePaintScreen) (s, ms); WRAP (is, s, preparePaintScreen, infoPreparePaintScreen); } static void infoDonePaintScreen (CompScreen *s) { INFO_SCREEN (s); if (is->pWindow) { if (is->fadeTime || is->drawing) { REGION reg; int x, y; x = is->resizeGeometry.x + is->resizeGeometry.width / 2.0f - RESIZE_POPUP_WIDTH / 2.0f; y = is->resizeGeometry.y + is->resizeGeometry.height / 2.0f - RESIZE_POPUP_HEIGHT / 2.0f; reg.rects = ®.extents; reg.numRects = 1; reg.extents.x1 = x - 5; reg.extents.y1 = y - 5; reg.extents.x2 = x + RESIZE_POPUP_WIDTH + 5; reg.extents.y2 = y + RESIZE_POPUP_HEIGHT + 5; damageScreenRegion (s, ®); } if (!is->fadeTime && !is->drawing) is->pWindow = 0; } UNWRAP (is, s, donePaintScreen); (*s->donePaintScreen) (s); WRAP (is, s, donePaintScreen, infoDonePaintScreen); } static void infoWindowGrabNotify (CompWindow *w, int x, int y, unsigned int state, unsigned int mask) { CompScreen * s = w->screen; INFO_SCREEN (s); if (!is->pWindow && !(w->state & MAXIMIZE_STATE)) { Bool showInfo; showInfo = ((w->sizeHints.width_inc != 1) && (w->sizeHints.height_inc != 1)) || resizeinfoGetAlwaysShow (s->display); if (showInfo && (mask & CompWindowGrabResizeMask)) { is->pWindow = w; is->drawing = TRUE; is->fadeTime = resizeinfoGetFadeTime (s->display); is->resizeGeometry.x = w->attrib.x; is->resizeGeometry.y = w->attrib.y; is->resizeGeometry.width = w->attrib.width; is->resizeGeometry.height = w->attrib.height; } } UNWRAP (is, s, windowGrabNotify); (*s->windowGrabNotify) (w, x, y, state, mask); WRAP (is, s, windowGrabNotify, infoWindowGrabNotify); } static void infoWindowUngrabNotify (CompWindow *w) { CompScreen *s = w->screen; INFO_SCREEN (s); if (w == is->pWindow) { is->drawing = FALSE; is->fadeTime = resizeinfoGetFadeTime (s->display); damageScreen (s); } UNWRAP (is, s, windowUngrabNotify); (*s->windowUngrabNotify) (w); WRAP (is, s, windowUngrabNotify, infoWindowUngrabNotify); } /* Draw a texture at x/y on a quad of RESIZE_POPUP_WIDTH / RESIZE_POPUP_HEIGHT with the opacity in InfoScreen. */ static void drawLayer (CompScreen *s, int x, int y, CompMatrix matrix, CompTexture *t) { BOX box; float opacity; INFO_SCREEN (s); enableTexture (s, t, COMP_TEXTURE_FILTER_FAST); matrix.x0 -= x * matrix.xx; matrix.y0 -= y * matrix.yy; box.x1 = x; box.x2 = x + RESIZE_POPUP_WIDTH; box.y1 = y; box.y2 = y + RESIZE_POPUP_HEIGHT; opacity = (float)is->fadeTime / resizeinfoGetFadeTime (s->display); if (is->drawing) opacity = 1.0f - opacity; glColor4f (opacity, opacity, opacity, opacity); glBegin (GL_QUADS); 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); glEnd (); glColor4usv (defaultColor); disableTexture (s, t); } static Bool infoPaintOutput (CompScreen *s, const ScreenPaintAttrib *sAttrib, const CompTransform *transform, Region region, CompOutput *output, unsigned int mask) { Bool status; INFO_SCREEN (s); UNWRAP (is, s, paintOutput); status = (*s->paintOutput) (s, sAttrib, transform, region, output, mask); WRAP (is, s, paintOutput, infoPaintOutput); if ((is->drawing || is->fadeTime) && is->pWindow) { CompMatrix matrix; CompTransform sTransform = *transform; int x, y; x = is->resizeGeometry.x + is->resizeGeometry.width / 2.0f - RESIZE_POPUP_WIDTH / 2.0f; y = is->resizeGeometry.y + is->resizeGeometry.height / 2.0f - RESIZE_POPUP_HEIGHT / 2.0f; matrix = is->backgroundLayer.texture.matrix; transformToScreenSpace (s, output, -DEFAULT_Z_CAMERA, &sTransform); glPushMatrix (); glLoadMatrixf (sTransform.m); glDisableClientState (GL_TEXTURE_COORD_ARRAY); glEnable (GL_BLEND); screenTexEnvMode (s, GL_MODULATE); drawLayer (s, x, y, matrix, &is->backgroundLayer.texture); drawLayer (s, x, y, is->textLayer.texture.matrix, &is->textLayer.texture); glDisable (GL_BLEND); glEnableClientState (GL_TEXTURE_COORD_ARRAY); glPopMatrix (); } return status; } static void infoHandleEvent (CompDisplay *d, XEvent *event) { INFO_DISPLAY (d); switch (event->type) { case ClientMessage: if (event->xclient.message_type == id->resizeNotifyAtom) { CompWindow *w; w = findWindowAtDisplay (d, event->xclient.window); if (w) { INFO_SCREEN (w->screen); if (w == is->pWindow) { is->resizeGeometry.x = event->xclient.data.l[0]; is->resizeGeometry.y = event->xclient.data.l[1]; is->resizeGeometry.width = event->xclient.data.l[2]; is->resizeGeometry.height = event->xclient.data.l[3]; updateTextLayer (w->screen); } } } break; default: break; } UNWRAP (id, d, handleEvent); (*d->handleEvent) (d, event); WRAP (id, d, handleEvent, infoHandleEvent); } static Bool infoInitDisplay (CompPlugin *p, CompDisplay *d) { InfoDisplay *id; id = malloc (sizeof (InfoDisplay)); if (!id) return FALSE; id->screenPrivateIndex = allocateScreenPrivateIndex (d); if (id->screenPrivateIndex < 0) { free (id); return FALSE; } resizeinfoSetGradient1Notify (d, gradientChanged); resizeinfoSetGradient2Notify (d, gradientChanged); resizeinfoSetGradient3Notify (d, gradientChanged); id->resizeNotifyAtom = XInternAtom (d->display, "_COMPIZ_RESIZE_NOTIFY", 0); d->privates[displayPrivateIndex].ptr = id; WRAP (id, d, handleEvent, infoHandleEvent); return TRUE; } static void infoFiniDisplay (CompPlugin *p, CompDisplay *d) { INFO_DISPLAY (d); freeScreenPrivateIndex (d, id->screenPrivateIndex); UNWRAP (id, d, handleEvent); free (id); } static Bool infoInitScreen (CompPlugin *p, CompScreen *s) { InfoScreen * is; INFO_DISPLAY (s->display); is = malloc (sizeof (InfoScreen)); if (!is) return FALSE; is->pWindow = 0; is->fadeTime = 0; is->drawing = FALSE; is->resizeGeometry.x = 0; is->resizeGeometry.y = 0; is->resizeGeometry.width = 0; is->resizeGeometry.height = 0; initTexture (s, &is->backgroundLayer.texture); initTexture (s, &is->textLayer.texture); WRAP (is, s, windowGrabNotify, infoWindowGrabNotify); WRAP (is, s, windowUngrabNotify, infoWindowUngrabNotify); WRAP (is, s, preparePaintScreen, infoPreparePaintScreen); WRAP (is, s, paintOutput, infoPaintOutput); WRAP (is, s, donePaintScreen, infoDonePaintScreen); s->privates[id->screenPrivateIndex].ptr = is; /* setup and draw cairo background */ setupCairoLayer (s, &is->backgroundLayer); drawCairoBackground (s); /* setup text layer */ setupCairoLayer (s, &is->textLayer); return TRUE; } static void freeInfoLayer (CompScreen *s, InfoLayer *is) { if (is->cr) cairo_destroy (is->cr); if (is->surface) cairo_surface_destroy (is->surface); finiTexture (s, &is->texture); if (is->pixmap) XFreePixmap (s->display->display, is->pixmap); } static void infoFiniScreen (CompPlugin *p, CompScreen *s) { INFO_SCREEN (s); freeInfoLayer (s, &is->backgroundLayer); freeInfoLayer (s, &is->textLayer); UNWRAP (is, s, windowGrabNotify); UNWRAP (is, s, windowUngrabNotify); UNWRAP (is, s, preparePaintScreen); UNWRAP (is, s, paintOutput); UNWRAP (is, s, donePaintScreen); free (is); } static Bool infoInit (CompPlugin *p) { displayPrivateIndex = allocateDisplayPrivateIndex (); if (displayPrivateIndex < 0) return FALSE; return TRUE; } static void infoFini (CompPlugin *p) { freeDisplayPrivateIndex(displayPrivateIndex); } static int infoGetVersion(CompPlugin *plugin, int version) { return ABIVERSION; } static CompPluginVTable infoVTable = { "resizeinfo", infoGetVersion, 0, infoInit, infoFini, infoInitDisplay, infoFiniDisplay, infoInitScreen, infoFiniScreen, 0, /* InitWindow */ 0, /* FiniWindow */ 0, /* GetDisplayOptions */ 0, /* SetDisplayOption */ 0, /* GetScreenOptions */ 0 /* SetScreenOption */ }; CompPluginVTable* getCompPluginInfo (void) { return &infoVTable; }