/* * Matchbox Window Manager - A lightweight window manager not for the * desktop. * * Authored By Matthew Allum * * Copyright (c) 2002, 2004 OpenedHand Ltd - http://o-hand.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, 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 "composite-engine.h" #include "timeline.h" #define DO_TIMINGS 0 /* enable this for lowlight timings */ #if DO_TIMINGS #include #include #endif #ifdef USE_COMPOSITE #include static XserverRegion client_win_extents (Wm *w, Client *client); static void comp_engine_add_damage (Wm *w, XserverRegion damage); typedef struct _conv { int size; double *data; } conv; typedef struct StackItem { Client *client; struct StackItem *next; } StackItem; /* XXX Ideally get rid of these globals */ static conv *gussianMap; static StackItem *comp_stack; static Bool timeline_test_func (Wm *w, int frames_total, int frame_num, void *userdata) { Client *client = (Client *)userdata; XserverRegion region; XRenderColor c; client->transparency = (frame_num * 0xffff) / frames_total; if (client->transparency == 0xffff) client->transparency = -1; dbg("Frame: %i/%i, trans: %i\n", frame_num, frames_total, client->transparency); c.red = c.green = c.blue = 0; c.alpha = client->transparency; XRenderFillRectangle (w->dpy, PictOpSrc, w->trans_picture, &c, 0, 0, 1, 1); if (client->damage != None) XDamageDestroy (w->dpy, client->damage); client->damage = XDamageCreate (w->dpy, client->frame, XDamageReportNonEmpty); region = client_win_extents (w, client); comp_engine_add_damage (w, region); /* Stop the timeline (and remove) */ if (frame_num >= frames_total) { mb_timeline_stop (w, client->timeline); free (client->timeline); client->timeline = NULL; } return True; } /* List for stack rendering of dialogs etc */ /* Shadow Generation */ static double gaussian (double r, double x, double y) { return ((1 / (sqrt (2 * M_PI * r))) * exp ((- (x * x + y * y)) / (2 * r * r))); } static conv * make_gaussian_map (double r) { conv *c; int size = ((int) ceil ((r * 3)) + 1) & ~1; int center = size / 2; int x, y; double t = 0.0; double g; c = malloc (sizeof (conv) + size * size * sizeof (double)); c->size = size; dbg("%s() map size is %i\n", __func__, size); c->data = (double *) (c + 1); for (y = 0; y < size; y++) for (x = 0; x < size; x++) { g = gaussian (r, (double) (x - center), (double) (y - center)); t += g; c->data[y * size + x] = g; } for (y = 0; y < size; y++) for (x = 0; x < size; x++) c->data[y*size + x] /= t; return c; } static unsigned char sum_gaussian (conv *map, double opacity, int x, int y, int width, int height) { int fx, fy; double *g_data; double *g_line = map->data; int g_size = map->size; int center = g_size / 2; int fx_start, fx_end; int fy_start, fy_end; double v; /* * Compute set of filter values which are "in range", * that's the set with: * 0 <= x + (fx-center) && x + (fx-center) < width && * 0 <= y + (fy-center) && y + (fy-center) < height * * 0 <= x + (fx - center) x + fx - center < width * center - x <= fx fx < width + center - x */ fx_start = center - x; if (fx_start < 0) fx_start = 0; fx_end = width + center - x; if (fx_end > g_size) fx_end = g_size; fy_start = center - y; if (fy_start < 0) fy_start = 0; fy_end = height + center - y; if (fy_end > g_size) fy_end = g_size; g_line = g_line + fy_start * g_size + fx_start; v = 0; for (fy = fy_start; fy < fy_end; fy++) { g_data = g_line; g_line += g_size; for (fx = fx_start; fx < fx_end; fx++) v += *g_data++; } if (v > 1) v = 1; return ((unsigned int) (v * opacity * 255.0)); } #define MAX_TILE_SZ 16 /* make sure size/2 < MAX_TILE_SZ */ #define WIDTH 320 #define HEIGHT 320 static void shadow_setup_part (Wm *w, XImage **ximage, Picture *pic, Pixmap *pxm, int width, int height) { *ximage = XCreateImage (w->dpy, DefaultVisual(w->dpy,DefaultScreen(w->dpy)), 8, ZPixmap, 0, 0, width, height, 8, width * sizeof (unsigned char)); (*ximage)->data = malloc (width * height * sizeof (unsigned char)); *pxm = XCreatePixmap (w->dpy, w->root, width, height, 8); *pic = XRenderCreatePicture (w->dpy, *pxm, XRenderFindStandardFormat (w->dpy, PictStandardA8), 0, 0); } static void shadow_finalise_part (Wm *w, XImage *ximage, Picture pic, Pixmap pxm, int width, int height) { GC gc = XCreateGC (w->dpy, pxm, 0, 0); XPutImage (w->dpy, pxm, gc, ximage, 0, 0, 0, 0, width, height); XDestroyImage (ximage); XFreeGC (w->dpy, gc); XFreePixmap (w->dpy, pxm); } static void shadow_setup (Wm *w) { XImage *ximage; Pixmap pxm; unsigned char *data; int size; int center; int x, y; unsigned char d; int pwidth, pheight; double opacity = SHADOW_OPACITY; if (w->config->shadow_style == SHADOW_STYLE_NONE) return; if (w->config->shadow_style == SHADOW_STYLE_SIMPLE) { w->config->shadow_padding_width = 0; w->config->shadow_padding_height = 0; return; } /* SHADOW_STYLE_GAUSSIAN */ gussianMap = make_gaussian_map (SHADOW_RADIUS); /* XXX must free */ w->config->shadow_padding_width = gussianMap->size; w->config->shadow_padding_height = gussianMap->size; size = gussianMap->size; center = size / 2; dbg("%s() gussian size is %i\n", __func__, size); /* Top & bottom */ pwidth = MAX_TILE_SZ; pheight = size/2; shadow_setup_part(w, &ximage, &w->shadow_n_pic, &pxm, pwidth, pheight); data = (unsigned char*)ximage->data; for (y = 0; y < pheight; y++) { d = sum_gaussian (gussianMap, opacity, center, y - center, WIDTH, HEIGHT); for (x = 0; x < pwidth; x++) data[y * pwidth + x] = d; } shadow_finalise_part (w, ximage, w->shadow_n_pic, pxm, pwidth, pheight); pwidth = MAX_TILE_SZ; pheight = MAX_TILE_SZ; shadow_setup_part(w, &ximage, &w->shadow_s_pic, &pxm, pwidth, pheight); data = (unsigned char*)ximage->data; for (y = 0; y < pheight; y++) { d = sum_gaussian (gussianMap, opacity, center, y - center, WIDTH, HEIGHT); for (x = 0; x < pwidth; x++) data[(pheight - y - 1) * pwidth + x] = d; } shadow_finalise_part (w, ximage, w->shadow_s_pic, pxm, pwidth, pheight); /* Sides */ pwidth = MAX_TILE_SZ; pheight = MAX_TILE_SZ; shadow_setup_part(w, &ximage, &w->shadow_w_pic, &pxm, pwidth, pheight); data = (unsigned char*)ximage->data; for (x = 0; x < pwidth; x++) { d = sum_gaussian (gussianMap, opacity, x - center, center, WIDTH, HEIGHT); for (y = 0; y < pheight; y++) data[y * pwidth + (pwidth - x - 1)] = d; } shadow_finalise_part (w, ximage, w->shadow_w_pic, pxm, pwidth, pheight); shadow_setup_part(w, &ximage, &w->shadow_e_pic, &pxm, pwidth, pheight); data = (unsigned char*)ximage->data; for (x = 0; x < pwidth; x++) { d = sum_gaussian (gussianMap, opacity, x - center, center, WIDTH, HEIGHT); for (y = 0; y < pheight; y++) data[y * pwidth + x] = d; } shadow_finalise_part (w, ximage, w->shadow_e_pic, pxm, pwidth, pheight); /* Corners */ pwidth = MAX_TILE_SZ; pheight = MAX_TILE_SZ; shadow_setup_part(w, &ximage, &w->shadow_nw_pic, &pxm, pwidth, pheight); data = (unsigned char*)ximage->data; for (x = 0; x < pwidth; x++) for (y = 0; y < pheight; y++) { d = sum_gaussian (gussianMap, opacity, x - center, y - center, WIDTH, HEIGHT); data[y * pwidth + x] = d; } shadow_finalise_part (w, ximage, w->shadow_nw_pic, pxm, pwidth, pheight); shadow_setup_part(w, &ximage, &w->shadow_sw_pic, &pxm, pwidth, pheight); data = (unsigned char*)ximage->data; for (x = 0; x < pwidth; x++) for (y = 0; y < pheight; y++) { d = sum_gaussian (gussianMap, opacity, x - center, y - center, WIDTH, HEIGHT); data[(pheight - y - 1) * pwidth + x] = d; } shadow_finalise_part (w, ximage, w->shadow_sw_pic, pxm, pwidth, pheight); shadow_setup_part(w, &ximage, &w->shadow_se_pic, &pxm, pwidth, pheight); data = (unsigned char*)ximage->data; for (x = 0; x < pwidth; x++) for (y = 0; y < pheight; y++) { d = sum_gaussian (gussianMap, opacity, x - center, y - center, WIDTH, HEIGHT); data[(pheight - y - 1) * pwidth + (pwidth - x -1)] = d; } shadow_finalise_part (w, ximage, w->shadow_se_pic, pxm, pwidth, pheight); shadow_setup_part(w, &ximage, &w->shadow_ne_pic, &pxm, pwidth, pheight); data = (unsigned char*)ximage->data; for (x = 0; x < pwidth; x++) for (y = 0; y < pheight; y++) { d = sum_gaussian (gussianMap, opacity, x - center, y - center, WIDTH, HEIGHT); data[y * pwidth + (pwidth - x -1)] = d; } shadow_finalise_part (w, ximage, w->shadow_ne_pic, pxm, pwidth, pheight); /* Finally center */ pwidth = MAX_TILE_SZ; pheight = MAX_TILE_SZ; shadow_setup_part(w, &ximage, &w->shadow_pic, &pxm, pwidth, pheight); data = (unsigned char*)ximage->data; d = sum_gaussian (gussianMap, opacity, center, center, WIDTH, HEIGHT); for (x = 0; x < pwidth; x++) for (y = 0; y < pheight; y++) data[y * pwidth + x] = d; shadow_finalise_part (w, ximage, w->shadow_pic, pxm, pwidth, pheight); } static Picture shadow_gaussian_make_picture (Wm *w, int width, int height) { Picture pic; Pixmap pxm; int pwidth, pheight, x, y, dw, dh; pxm = XCreatePixmap (w->dpy, w->root, width, height, 8); pic = XRenderCreatePicture (w->dpy, pxm, XRenderFindStandardFormat (w->dpy, PictStandardA8), 0,0); pwidth = MAX_TILE_SZ; pheight = MAX_TILE_SZ; for (x=0; x < width; x += pwidth) for (y=0; y < height; y += pheight) { if ( (y + pheight) > height ) dh = pheight - ((y + pheight)-height); else dh = pheight; if ( (x + pwidth) > width ) dw = pwidth - ((x + pwidth)-width); else dw = pwidth; XRenderComposite (w->dpy, PictOpSrc, w->shadow_pic, None, pic, 0, 0, 0, 0, x, y, dw, dh); } /* Top & bottom */ if ( width > (MAX_TILE_SZ*2) ) { pwidth = MAX_TILE_SZ; pheight = MAX_TILE_SZ; for (x=0; x < width; x += pwidth ) { if ( (x + pwidth) > width ) dw = pwidth - ((x + pwidth)-width); else dw = pwidth; XRenderComposite (w->dpy, PictOpSrc, w->shadow_n_pic, None, pic, 0, 0, 0, 0, x, 0, dw, pheight); XRenderComposite (w->dpy, PictOpSrc, w->shadow_s_pic, None, pic, 0, 0, 0, 0, x, height - pheight, dw, pheight); } } /* Sides */ if ( height > (MAX_TILE_SZ*2) ) { pwidth = MAX_TILE_SZ; pheight = MAX_TILE_SZ; for (y=0; y < height; y += pheight) { if ( (y + pheight) > height ) dh = pheight - ((y + pheight)-height); else dh = pheight; XRenderComposite (w->dpy, PictOpSrc /* PictOpIn */, w->shadow_e_pic, None, pic, 0, 0, 0, 0, 0, y, pwidth, dh); XRenderComposite (w->dpy, PictOpSrc /* PictOpIn */, w->shadow_w_pic, None, pic, 0, 0, 0, 0, width - pwidth, y, pwidth, dh); } } /* Corners */ pwidth = MAX_TILE_SZ; pheight = MAX_TILE_SZ; XRenderComposite (w->dpy, PictOpSrc, w->shadow_nw_pic, None, pic, 0, 0, 0, 0, 0, 0, pwidth, pheight); XRenderComposite (w->dpy, PictOpSrc, w->shadow_ne_pic, None, pic, 0, 0, 0, 0, width - pwidth, 0, pwidth, pheight); XRenderComposite (w->dpy, PictOpSrc, w->shadow_sw_pic, None, pic, 0, 0, 0, 0, 0, height - pheight, pwidth, pheight); XRenderComposite (w->dpy, PictOpSrc, w->shadow_se_pic, None, pic, 0, 0, 0, 0, width - pwidth, height - pheight, pwidth, pheight); XFreePixmap (w->dpy, pxm); return pic; } static XserverRegion client_win_extents (Wm *w, Client *client) { int x, y, width, height; XRectangle r; /* XXX make coverage much fast as its now getting called all the time */ client->get_coverage(client, &x, &y, &width, &height); r.x = x; r.y = y; r.width = width; r.height = height; if (w->config->shadow_style) { if (client->type == MBCLIENT_TYPE_DIALOG || client->type == MBCLIENT_TYPE_TASK_MENU || client->type == MBCLIENT_TYPE_OVERRIDE) { if (w->config->shadow_style == SHADOW_STYLE_SIMPLE) { r.width += w->config->shadow_dx; r.height += w->config->shadow_dy; } else { r.x += w->config->shadow_dx; r.y += w->config->shadow_dy; r.width += w->config->shadow_padding_width; r.height += w->config->shadow_padding_height; } } } dbg("comp %s() +%i+%i , %ix%i\n", __func__, x, y, width, height); return XFixesCreateRegion (w->dpy, &r, 1); } static XserverRegion client_border_size (Wm *w, Client *c, int x, int y) { XserverRegion border; border = XFixesCreateRegionFromWindow (w->dpy, c->frame, WindowRegionBounding ); /* translate this */ XFixesTranslateRegion (w->dpy, border, x, y); return border; } static Visual* comp_engine_get_argb32_visual(Wm *w) { XVisualInfo *xvi; XVisualInfo template; int nvi; int i; XRenderPictFormat *format; Visual *visual = NULL; template.screen = w->screen; template.depth = 32; template.class = TrueColor; if ((xvi = XGetVisualInfo (w->dpy, VisualScreenMask | VisualDepthMask | VisualClassMask, &template, &nvi)) == NULL) return NULL; for (i = 0; i < nvi; i++) { format = XRenderFindVisualFormat (w->dpy, xvi[i].visual); if (format->type == PictTypeDirect && format->direct.alphaMask) { visual = xvi[i].visual; break; } } XFree (xvi); return visual; } void comp_engine_set_defualts(Wm *w) { w->config->shadow_style = SHADOW_STYLE_GAUSSIAN; w->config->shadow_color[0] = 0; w->config->shadow_color[1] = 0; w->config->shadow_color[2] = 0; w->config->shadow_color[3] = 0xff; w->config->shadow_dx = SHADOW_OFFSET_X; w->config->shadow_dy = SHADOW_OFFSET_Y; /* Not really used yet */ w->config->shadow_padding_width = 0; w->config->shadow_padding_height = 0; } void comp_engine_theme_init(Wm *w) { Pixmap transPixmap, blackPixmap, lowlightPixmap, redPixmap; XRenderPictureAttributes pa; XRenderColor c; int i; Picture pics_to_free[] = { w->trans_picture, w->black_picture, w->lowlight_picture, w->shadow_n_pic, w->shadow_e_pic, w->shadow_s_pic, w->shadow_w_pic, w->shadow_ne_pic, w->shadow_nw_pic, w->shadow_se_pic, w->shadow_sw_pic, w->shadow_pic }; if (!w->have_comp_engine) return; for (i=0; i < (sizeof(pics_to_free)/sizeof(Picture)); i++) if (pics_to_free[i] != None) XRenderFreePicture (w->dpy, pics_to_free[i]); if (w->config->shadow_style == SHADOW_STYLE_NONE) return; if (w->config->shadow_style == SHADOW_STYLE_GAUSSIAN) shadow_setup (w); pa.subwindow_mode = IncludeInferiors; pa.repeat = True; transPixmap = XCreatePixmap (w->dpy, w->root, 1, 1, 8); w->trans_picture = XRenderCreatePicture (w->dpy, transPixmap, XRenderFindStandardFormat (w->dpy, PictStandardA8), CPRepeat, &pa); c.red = c.green = c.blue = 0; c.alpha = 0xb0b0; XRenderFillRectangle (w->dpy, PictOpSrc, w->trans_picture, &c, 0, 0, 1, 1); /* black pixmap used for shadows */ blackPixmap = XCreatePixmap (w->dpy, w->root, 1, 1, 32); w->black_picture = XRenderCreatePicture (w->dpy, blackPixmap, XRenderFindStandardFormat (w->dpy, PictStandardARGB32), CPRepeat, &pa); c.red = w->config->shadow_color[0] << 8; c.green = w->config->shadow_color[1] << 8; c.blue = w->config->shadow_color[2] << 8; if (w->config->shadow_style == SHADOW_STYLE_GAUSSIAN) c.alpha = 0xffff; else c.alpha = w->config->shadow_color[3] << 8; dbg("%s() shadow alpha is %i\n", __func__, c.alpha); XRenderFillRectangle (w->dpy, PictOpSrc, w->black_picture, &c, 0, 0, 1, 1); #if DEBUG /* for visual composite debugging */ redPixmap = XCreatePixmap (w->dpy, w->root, 1, 1, 32); w->red_picture = XRenderCreatePicture (w->dpy, redPixmap, XRenderFindStandardFormat (w->dpy, PictStandardARGB32), CPRepeat, &pa); c.red = 0xffff; c.green = 0; c.blue = 0; c.alpha = 0xffff; XRenderFillRectangle (w->dpy, PictOpSrc, w->red_picture, &c, 0, 0, 1, 1); #endif /* Used for lowlights */ lowlightPixmap = XCreatePixmap (w->dpy, w->root, 1, 1, 32); w->lowlight_picture = XRenderCreatePicture (w->dpy, lowlightPixmap, XRenderFindStandardFormat (w->dpy, PictStandardARGB32), CPRepeat, &pa); if (w->config->dialog_shade) { c.red = w->config->lowlight_params[0] << 8; c.green = w->config->lowlight_params[1] << 8; c.blue = w->config->lowlight_params[2] << 8; c.alpha = w->config->lowlight_params[3] << 8; } else { c.red = 0; c.green = 0; c.blue = 0; c.alpha = 0x8d8d; } XRenderFillRectangle (w->dpy, PictOpSrc, w->lowlight_picture, &c, 0, 0, 1, 1); } /* Shuts the compositing down */ void comp_engine_deinit(Wm *w) { Client *c = NULL; if (!w->have_comp_engine) { w->comp_engine_disabled = True; return; } if (w->comp_engine_disabled) return; /* * really shut down the composite engine. * */ XCompositeUnredirectSubwindows (w->dpy, w->root, CompositeRedirectManual); if (w->root_picture) XRenderFreePicture (w->dpy, w->root_picture); if (w->root_buffer) XRenderFreePicture (w->dpy, w->root_buffer); w->root_buffer = None; w->root_picture = None; if (w->all_damage) XDamageDestroy (w->dpy, w->all_damage); /* Free up any client composite resources */ stack_enumerate(w, c) comp_engine_client_destroy(w, c); /* XXX should free up any client picture data ? */ w->comp_engine_disabled = True; w->have_comp_engine = False; /* bad ? */ } void comp_engine_reinit(Wm *w) { Client *c = NULL; w->comp_engine_disabled = False; comp_engine_init (w); XSync(w->dpy, False); if (!stack_empty(w)) { stack_enumerate(w, c) { dbg("%s() calling init for '%s'\n", __func__, c->name); comp_engine_client_init(w, c); comp_engine_client_show(w, c); } comp_engine_render(w, None); } } Bool comp_engine_init (Wm *w) { int event_base, error_base; int damage_error; int xfixes_event, xfixes_error; int render_event, render_error; int composite_major, composite_minor; XRenderPictureAttributes pa; Pixmap tmp_pxm; dbg("%s() called\n", __func__); if (w->comp_engine_disabled) return False; if (w->have_comp_engine) return False; /* already running */ w->comp_engine_disabled = True; if (!XRenderQueryExtension (w->dpy, &render_event, &render_error)) { fprintf (stderr, "matchbox: No render extension\n"); w->have_comp_engine = False; return False; } if (!XCompositeQueryExtension (w->dpy, &event_base, &error_base)) { fprintf (stderr, "matchbox: No composite extension\n"); w->have_comp_engine = False; return False; } XCompositeQueryVersion (w->dpy, &composite_major, &composite_minor); if (composite_major == 0 && composite_minor < 2) { /* Need at least version 0.2 for named pixmaps */ fprintf (stderr, "matchbox: Composite extension too old (need >=0.2)\n"); w->have_comp_engine = False; return False; } if (!XDamageQueryExtension (w->dpy, &w->damage_event, &damage_error)) { fprintf (stderr, "matchbox: No damage extension\n"); w->have_comp_engine = False; return False; } if (!XFixesQueryExtension (w->dpy, &xfixes_event, &xfixes_error)) { fprintf (stderr, "matchbox: No XFixes extension\n"); w->have_comp_engine = False; return False; } w->have_comp_engine = True; w->comp_engine_disabled = False; wm_set_timelines_fps (w, 60); comp_stack = NULL; /* Make the shadow tiles */ pa.subwindow_mode = IncludeInferiors; w->root_picture = XRenderCreatePicture(w->dpy, w->root, XRenderFindVisualFormat (w->dpy, DefaultVisual (w->dpy, w->screen)), CPSubwindowMode, &pa); w->all_damage = None; XCompositeRedirectSubwindows (w->dpy, w->root, CompositeRedirectManual); dbg("%s() success \n", __func__); /* Setup an mbpixbuf reference for 32bit argb rendering */ w->argb_pb = mb_pixbuf_new_extended(w->dpy, w->screen, comp_engine_get_argb32_visual(w), 32); /* A little hack to get the GC right */ tmp_pxm = XCreatePixmap(w->dpy, w->root, 16, 16, 32); w->argb_pb->gc = XCreateGC(w->dpy, tmp_pxm, 0, NULL); XFreePixmap(w->dpy, tmp_pxm); return True; } void comp_engine_client_init(Wm *w, Client *client) { XRenderPictFormat *format; if (!w->have_comp_engine) return; client->damaged = 0; client->damage = None; client->picture = None; client->shadow = None; client->borderSize = None; client->extents = None; client->transparency = -1; client->is_argb32 = False; /* Check visual */ format = XRenderFindVisualFormat (w->dpy, client->visual); if (format && format->type == PictTypeDirect && format->direct.alphaMask) { dbg("%s() client is ARGB32\n", __func__); client->is_argb32 = True; } comp_engine_client_get_trans_prop(w, client); } int comp_engine_client_get_trans_prop(Wm *w, Client *client) { Atom actual; int format; unsigned long n, left; char *data; XGetWindowProperty(w->dpy, client->window, w->atoms[CM_TRANSLUCENCY], 0L, 1L, False, XA_INTEGER, &actual, &format, &n, &left, (unsigned char **) &data); if (data != None) { client->transparency = (int) *data; XFree( (void *) data); return client->transparency; } return -1; } void comp_engine_client_show(Wm *w, Client *client) { XserverRegion region; XRenderPictureAttributes pa; if (!w->have_comp_engine) return; dbg("%s() called\n", __func__); /* * Destroying / Recreating the client pictures should hopefully save * some memory in the server. */ if (client->picture == None) { pa.subwindow_mode = IncludeInferiors; if (client->named_pixmap == None) client->named_pixmap = XCompositeNameWindowPixmap (w->dpy, client->frame); client->picture = XRenderCreatePicture (w->dpy, client->named_pixmap, XRenderFindVisualFormat (w->dpy, client->visual), CPSubwindowMode, &pa); if ((client->type == MBCLIENT_TYPE_DIALOG && (client->flags & CLIENT_HAS_URGENCY_FLAG)) && client->timeline == NULL) { client->timeline = mb_timeline_new (5, timeline_test_func, (void *)client); mb_timeline_start (w, client->timeline); return; /* timeline will add damage */ } } if (client->damage != None) XDamageDestroy (w->dpy, client->damage); client->damage = XDamageCreate (w->dpy, client->frame, XDamageReportNonEmpty); region = client_win_extents (w, client); comp_engine_add_damage (w, region); } void comp_engine_client_hide(Wm *w, Client *client) { Client *t = NULL; if (!w->have_comp_engine) return; dbg("%s() called\n", __func__); if (client->flags & CLIENT_IS_MODAL_FLAG && ((t = wm_get_visible_main_client(w)) != NULL)) { /* We need to make sure the any lowlighting on a 'parent' * modal for app gets cleared. This is kind of a sledgehammer * approach to it, but more suttle attempts oddly fail at times. * * FIXME: keep an eye on this for future revisions of composite * - there may be a better way. */ comp_engine_client_repair (w, t); comp_engine_add_damage (w, t->extents); } if (client->damage != None) { XDamageDestroy (w->dpy, client->damage); client->damage = None; } if (client->extents != None) { comp_engine_add_damage (w, client->extents); client->extents = None; } if (client->named_pixmap) { XFreePixmap (w->dpy, client->named_pixmap); client->named_pixmap = None; if (client->picture) { XRenderFreePicture (w->dpy, client->picture); client->picture = None; } } } void comp_engine_client_destroy(Wm *w, Client *client) { if (!w->have_comp_engine) return; dbg("%s() called\n", __func__); comp_engine_client_hide(w, client); if (client->timeline) { mb_timeline_stop (w, client->timeline); free (client->timeline); client->timeline = NULL; } if (client->named_pixmap) XFreePixmap (w->dpy, client->named_pixmap); if (client->picture) XRenderFreePicture (w->dpy, client->picture); if (client->border_clip != None) XFixesDestroyRegion (w->dpy, client->border_clip); } static void comp_engine_add_damage (Wm *w, XserverRegion damage) { if (!w->have_comp_engine) return; dbg("%s() called\n", __func__); if (w->all_damage) { XFixesUnionRegion (w->dpy, w->all_damage, w->all_damage, damage); XFixesDestroyRegion (w->dpy, damage); } else w->all_damage = damage; } void comp_engine_client_repair (Wm *w, Client *client) { XserverRegion parts; int x, y, width, height; if (!w->have_comp_engine) return; dbg("%s() called for client '%s'\n", __func__, client->name); parts = XFixesCreateRegion (w->dpy, 0, 0); /* translate region */ dbg("%s() client damage is %li\n", __func__, client->damage); XDamageSubtract (w->dpy, client->damage, None, parts); client->get_coverage(client, &x, &y, &width, &height); XFixesTranslateRegion (w->dpy, parts, x, y); comp_engine_add_damage (w, parts); } void comp_engine_client_configure(Wm *w, Client *client) { XserverRegion damage = None; XserverRegion extents = client_win_extents(w, client); if (client->named_pixmap) { XRenderPictureAttributes pa; XFreePixmap (w->dpy, client->named_pixmap); client->named_pixmap = None; if (client->picture) { XRenderFreePicture (w->dpy, client->picture); client->picture = None; } pa.subwindow_mode = IncludeInferiors; /* Now recreate for new size */ client->named_pixmap = XCompositeNameWindowPixmap (w->dpy, client->frame); client->picture = XRenderCreatePicture (w->dpy, client->named_pixmap, XRenderFindVisualFormat (w->dpy, client->visual), CPSubwindowMode, &pa); } damage = XFixesCreateRegion (w->dpy, 0, 0); if (client->extents != None) XFixesCopyRegion (w->dpy, damage, client->extents); XFixesUnionRegion (w->dpy, damage, damage, extents); XFixesDestroyRegion (w->dpy, extents); comp_engine_add_damage (w, damage); } void comp_engine_handle_events(Wm *w, XEvent *ev) { if (!w->have_comp_engine) return; if (ev->type == w->damage_event + XDamageNotify) { XDamageNotifyEvent *de; Client *c; dbg("%s() called have damage event \n", __func__); de = (XDamageNotifyEvent *)ev; c = wm_find_client(w, de->drawable, FRAME); if (c) comp_engine_client_repair(w, c); else { dbg("%s() failed to find damaged window \n", __func__); } } } static void _render_a_client(Wm *w, Client *client, XserverRegion region, int lowlight_type) /* 0 - none, 1 app, 2 full */ { int x,y,width,height; XserverRegion winborder; if (client->picture == None) { dbg("%s() no pixture for %s\n", __func__, client->name); return; } if (client->extents) XFixesDestroyRegion (w->dpy, client->extents); client->extents = client_win_extents (w, client); client->get_coverage(client, &x, &y, &width, &height); winborder = client_border_size (w, client, x, y); /* Transparency only done for dialogs and overides */ if ( (client->transparency == -1 || client->type == MBCLIENT_TYPE_APP || client->type == MBCLIENT_TYPE_DESKTOP || client->type == MBCLIENT_TYPE_TOOLBAR || client->type == MBCLIENT_TYPE_PANEL) && !client->is_argb32) { XFixesSetPictureClipRegion (w->dpy, w->root_buffer, 0, 0, region); XFixesSubtractRegion (w->dpy, region, region, winborder); XRenderComposite (w->dpy, PictOpSrc, client->picture, None, w->root_buffer, 0, 0, 0, 0, x, y, width, height); } if (w->config->dialog_shade) { /* Render lowlight dialog modal for app */ if (lowlight_type == 1 && (client->type & (MBCLIENT_TYPE_APP|MBCLIENT_TYPE_DESKTOP))) { int title_offset = 0; dbg("%s() rendering lowlight\n", __func__); if (client->type == MBCLIENT_TYPE_APP) title_offset = main_client_title_height(client); XRenderComposite (w->dpy, PictOpOver, w->lowlight_picture, None, w->root_buffer, 0, 0, 0, 0, x, y + title_offset, width, height - title_offset); } /* Render lowlight dialog modal for root - e.g lowlight everything */ if (lowlight_type == 2 && client->win_modal_blocker == None) XRenderComposite (w->dpy, PictOpOver, w->lowlight_picture, None, w->root_buffer, 0, 0, 0, 0, x, y, width, height); } if (client->border_clip != None) { XFixesDestroyRegion (w->dpy, client->border_clip); client->border_clip = None; } client->border_clip = XFixesCreateRegion (w->dpy, 0, 0); XFixesCopyRegion (w->dpy, client->border_clip, region); XFixesDestroyRegion (w->dpy, winborder); /* XXX the leak plugged ? */ } void comp_engine_destroy_root_buffer(Wm *w) { if (w->root_buffer) { XRenderFreePicture (w->dpy, w->root_buffer); w->root_buffer = None; } } void comp_engine_render(Wm *w, XserverRegion region) { Client *client_top_app = NULL, *t = NULL; int x,y,width,height; int lowlight = 0; if (!w->have_comp_engine || stack_empty(w)) return; dbg("%s() called\n", __func__); if (!region) { XRectangle r; r.x = 0; r.y = 0; r.width = w->dpy_width; r.height = w->dpy_height; region = XFixesCreateRegion (w->dpy, &r, 1); } client_top_app = wm_get_visible_main_client(w); if (!w->root_buffer) { Pixmap rootPixmap = XCreatePixmap (w->dpy, w->root, w->dpy_width, w->dpy_height, DefaultDepth (w->dpy, w->screen)); w->root_buffer = XRenderCreatePicture(w->dpy, rootPixmap, XRenderFindVisualFormat (w->dpy, DefaultVisual (w->dpy, w->screen)), 0, 0); XRenderComposite (w->dpy, PictOpSrc, w->black_picture, None, w->root_buffer, 0, 0, 0, 0, 0, 0, w->dpy_width, w->dpy_height); XFreePixmap (w->dpy, rootPixmap); } XFixesSetPictureClipRegion (w->dpy, w->root_picture, 0, 0, region); XFixesSetPictureClipRegion (w->dpy, w->root_buffer, 0, 0, region); #if DEBUG if (w->flags & DEBUG_COMPOSITE_VISIBLE_FLAG) { XRenderComposite (w->dpy, PictOpSrc, w->red_picture, None, w->root_picture, 0, 0, 0, 0, 0, 0, w->dpy_width, w->dpy_height); XSync(w->dpy, False); /* return; */ } #endif /* Check initially to see what kind of lowlight todo ( if any ) */ stack_enumerate_reverse(w, t) { if (t->type == MBCLIENT_TYPE_DIALOG && t->flags & CLIENT_IS_MODAL_FLAG) { lowlight = ((t->win_modal_blocker) ? 2 : 1); } if (t == client_top_app) break; } /* Render top -> bottom */ stack_enumerate_reverse(w, t) { dbg("%s() rendering %s\n", __func__, t->name); _render_a_client(w, t, region, lowlight); if (t == client_top_app) break; } if (client_top_app == NULL) { /* Render block of boring black in case of no top app or desktop */ XFixesSetPictureClipRegion (w->dpy, w->root_buffer, 0, 0, region); XRenderComposite (w->dpy, PictOpSrc, w->black_picture, None, w->root_buffer, 0, 0, 0, 0, 0, 0, w->dpy_width, w->dpy_height); XFixesSetPictureClipRegion (w->dpy, w->root_buffer, 0, 0, None); client_top_app = w->stack_bottom; } XFixesSetPictureClipRegion (w->dpy, w->root_buffer, 0, 0, None); /* Now render shadows but bottom -> top this time */ for ((t)=client_top_app; (t) != NULL; (t)=(t)->above) { dbg("%s() rendering shadow for %s\n", __func__, t->name); if ((t->type == MBCLIENT_TYPE_DIALOG && t->mapped) || t->type == MBCLIENT_TYPE_TASK_MENU || t->type == MBCLIENT_TYPE_OVERRIDE) { dbg("%s() rendering shadow for %s\n", __func__, t->name); if (!t->picture) { dbg("%s() no pixture for %s\n", __func__, t->name); continue; } if (w->config->shadow_style) { Picture shadow_pic; t->get_coverage(t, &x, &y, &width, &height); if (w->config->shadow_style == SHADOW_STYLE_SIMPLE) { XserverRegion shadow_region; /* Grab 'shape' region of window */ shadow_region = client_border_size (w, t, x, y); /* Offset it. */ XFixesTranslateRegion (w->dpy, shadow_region, w->config->shadow_dx, w->config->shadow_dy); /* Intersect it, so only border remains */ XFixesIntersectRegion (w->dpy, shadow_region, t->border_clip, shadow_region ); XFixesSetPictureClipRegion (w->dpy, w->root_buffer, 0, 0, shadow_region); /* now paint them */ if (t->is_argb32) { XRenderComposite (w->dpy, PictOpOver, w->black_picture, t->picture, w->root_buffer, 0, 0, 0, 0, x + w->config->shadow_dx, y + w->config->shadow_dy, width + w->config->shadow_padding_width, height + w->config->shadow_padding_height); } else { XRenderComposite (w->dpy, PictOpOver, w->black_picture, None, w->root_buffer, 0, 0, 0, 0, x + w->config->shadow_dx, y + w->config->shadow_dy, width + w->config->shadow_padding_width, height + w->config->shadow_padding_height); } /* Paint any transparent window contents */ if (t->transparency != -1 || t->is_argb32 ) { XFixesDestroyRegion (w->dpy, shadow_region); shadow_region = client_border_size (w, t, x, y); XFixesIntersectRegion (w->dpy, shadow_region, t->border_clip, shadow_region ); XFixesSetPictureClipRegion (w->dpy, w->root_buffer, 0, 0, shadow_region); if (t->is_argb32) XRenderComposite (w->dpy, PictOpOver, t->picture, None, w->root_buffer, 0, 0, 0, 0, x, y, width, height); else XRenderComposite (w->dpy, PictOpOver, t->picture, w->trans_picture, w->root_buffer, 0, 0, 0, 0, x, y, width, height); } XFixesDestroyRegion (w->dpy, shadow_region); } else /* GAUSSIAN */ { XFixesSetPictureClipRegion (w->dpy, w->root_buffer, 0, 0, t->border_clip); if (t->transparency != -1 || t->is_argb32 ) { /* No shadows currently for transparent windows */ XRenderComposite (w->dpy, PictOpOver, t->picture, w->trans_picture, w->root_buffer, 0, 0, 0, 0, x, y, width, height); } else { /* Combine pregenerated shadow tiles */ shadow_pic = shadow_gaussian_make_picture (w, width + w->config->shadow_padding_width, height + w->config->shadow_padding_height); XRenderComposite (w->dpy, PictOpOver, w->black_picture, shadow_pic, w->root_buffer, 0, 0, 0, 0, x + w->config->shadow_dx, y + w->config->shadow_dy, width + w->config->shadow_padding_width, height + w->config->shadow_padding_height); XRenderFreePicture (w->dpy, shadow_pic); } } } } } XFixesSetPictureClipRegion (w->dpy, w->root_buffer, 0, 0, None); XRenderComposite (w->dpy, PictOpSrc, w->root_buffer, None, w->root_picture, 0, 0, 0, 0, 0, 0, w->dpy_width, w->dpy_height); XSync(w->dpy, False); } #endif void comp_engine_time(Wm *w) { #ifdef STANDALONE /* No timings for standalone */ return; #endif /* STANDALONE */ #if DO_TIMINGS struct timeval tv_start, tv_end; struct timezone tz; long diff; #ifdef USE_COMPOSITE XSync(w->dpy, False); gettimeofday(&tv_start, &tz); XFixesSetPictureClipRegion (w->dpy, w->root_buffer, 0, 0, None); XFixesSetPictureClipRegion (w->dpy, w->root_picture, 0, 0, None); XRenderComposite (w->dpy, PictOpOver, w->lowlight_picture, None, w->root_buffer, 0, 0, 0, 0, 0, 0, w->dpy_width, w->dpy_height); XRenderComposite (w->dpy, PictOpSrc, w->root_buffer, None, w->root_picture, 0, 0, 0, 0, 0, 0, w->dpy_width, w->dpy_height); XSync(w->dpy, False); gettimeofday(&tv_end, &tz); diff = ((tv_end.tv_sec * 1000000) + tv_end.tv_usec) - ((tv_start.tv_sec * 1000000) + tv_start.tv_usec); fprintf(stderr, "COMPOSITE LOWLIGHT TIMING: %li us\n", diff); sleep(1); comp_engine_render(w, None); #else #ifndef STANDALONE MBPixbufImage *img; int x, y; Pixmap pxm_tmp; Window win; XSetWindowAttributes attr; attr.override_redirect = True; attr.event_mask = ChildMask|ButtonPressMask|ExposureMask; XSync(w->dpy, False); gettimeofday(&tv_start, &tz); win = XCreateWindow(w->dpy, w->root, 0, 0, w->dpy_width, w->dpy_height, 0, CopyFromParent, CopyFromParent, CopyFromParent, CWOverrideRedirect|CWEventMask, &attr); pxm_tmp = XCreatePixmap(w->dpy, win, w->dpy_width, w->dpy_height , w->pb->depth); img = mb_pixbuf_img_new_from_x_drawable(w->pb, w->root, None, 0, 0, w->dpy_width, w->dpy_height, True); if (!img) { fprintf(stderr, "%s(): failed to grab root image\n", __func__); return; } for (x = 0; x < w->dpy_width; x++) for (y = 0; y < w->dpy_height; y++) mb_pixbuf_img_plot_pixel_with_alpha(w->pb, img, x, y, w->config->lowlight_params[0], w->config->lowlight_params[1], w->config->lowlight_params[2], w->config->lowlight_params[3] ); mb_pixbuf_img_render_to_drawable(w->pb, img, pxm_tmp, 0, 0); XSetWindowBackgroundPixmap(w->dpy, win, pxm_tmp); XClearWindow(w->dpy, win); XMapRaised(w->dpy, win); XSync(w->dpy, False); gettimeofday(&tv_end, &tz); diff = ((tv_end.tv_sec * 1000000) + tv_end.tv_usec) - ((tv_start.tv_sec * 1000000) + tv_start.tv_usec); fprintf(stderr, "XIMAGE LOWLIGHT TIMING: %li us\n", diff); mb_pixbuf_img_free(w->pb, img); XFreePixmap(w->dpy, pxm_tmp); XSync(w->dpy, False); sleep(1); XDestroyWindow(w->dpy, win); #endif /* STANDALONE */ #endif /* USE_COMPOSITE */ #else fprintf(stderr, "matchbox-window-manager: timing functionality disabled\n"); #endif /* DO TIMINGS */ }