|
|
|
|
@@ -29,6 +29,10 @@
|
|
|
|
|
#include <cairo.h>
|
|
|
|
|
#include <cairo-xlib.h>
|
|
|
|
|
|
|
|
|
|
#include <X11/extensions/XShm.h>
|
|
|
|
|
#include <sys/ipc.h>
|
|
|
|
|
#include <sys/shm.h>
|
|
|
|
|
|
|
|
|
|
#include "common.h"
|
|
|
|
|
#include "window.h"
|
|
|
|
|
#include "server.h"
|
|
|
|
|
@@ -357,8 +361,146 @@ char *get_window_name(Window win)
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cairo_surface_t *get_window_thumbnail(Window win, int size)
|
|
|
|
|
void smooth_thumbnail(cairo_surface_t *image_surface)
|
|
|
|
|
{
|
|
|
|
|
u_int32_t *data = (u_int32_t *)cairo_image_surface_get_data(image_surface);
|
|
|
|
|
const size_t tw = cairo_image_surface_get_width(image_surface);
|
|
|
|
|
const size_t th = cairo_image_surface_get_height(image_surface);
|
|
|
|
|
const size_t rmask = 0xff0000;
|
|
|
|
|
const size_t gmask = 0xff00;
|
|
|
|
|
const size_t bmask = 0xff;
|
|
|
|
|
for (size_t i = 0; i < tw * (th - 1) - 1; i++) {
|
|
|
|
|
u_int32_t c1 = data[i];
|
|
|
|
|
u_int32_t c2 = data[i+1];
|
|
|
|
|
u_int32_t c3 = data[i+tw];
|
|
|
|
|
u_int32_t b = (6 * (c1 & bmask) + (c2 & bmask) + (c3 & bmask)) / 8;
|
|
|
|
|
u_int32_t g = (6 * (c1 & gmask) + (c2 & gmask) + (c3 & gmask)) / 8;
|
|
|
|
|
u_int32_t r = (6 * (c1 & rmask) + (c2 & rmask) + (c3 & rmask)) / 8;
|
|
|
|
|
data[i] = (r & rmask) | (g & gmask) | (b & bmask);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cairo_surface_t *screenshot(Window win, size_t size)
|
|
|
|
|
{
|
|
|
|
|
cairo_surface_t *result = NULL;
|
|
|
|
|
XWindowAttributes wa;
|
|
|
|
|
if (!XGetWindowAttributes(server.display, win, &wa) || wa.width <= 0 || wa.height <= 0)
|
|
|
|
|
goto err0;
|
|
|
|
|
|
|
|
|
|
size_t w, h;
|
|
|
|
|
w = (size_t)wa.width;
|
|
|
|
|
h = (size_t)wa.height;
|
|
|
|
|
size_t tw, th, fw;
|
|
|
|
|
size_t ox, oy;
|
|
|
|
|
tw = size;
|
|
|
|
|
th = h * tw / w;
|
|
|
|
|
if (th > tw * 0.618) {
|
|
|
|
|
th = (size_t)(tw * 0.618);
|
|
|
|
|
fw = w * th / h;
|
|
|
|
|
ox = (tw - fw) / 2;
|
|
|
|
|
oy = 0;
|
|
|
|
|
} else {
|
|
|
|
|
fw = tw;
|
|
|
|
|
ox = oy = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
XShmSegmentInfo shminfo;
|
|
|
|
|
XImage *ximg = XShmCreateImage(server.display,
|
|
|
|
|
wa.visual,
|
|
|
|
|
(unsigned)wa.depth,
|
|
|
|
|
ZPixmap,
|
|
|
|
|
NULL,
|
|
|
|
|
&shminfo,
|
|
|
|
|
(unsigned)wa.width,
|
|
|
|
|
(unsigned)wa.height);
|
|
|
|
|
if (!ximg) {
|
|
|
|
|
fprintf(stderr, "tint2: !ximg\n");
|
|
|
|
|
goto err0;
|
|
|
|
|
}
|
|
|
|
|
shminfo.shmid = shmget(IPC_PRIVATE, (size_t)(ximg->bytes_per_line * ximg->height), IPC_CREAT | 0777);
|
|
|
|
|
if (shminfo.shmid < 0) {
|
|
|
|
|
fprintf(stderr, "tint2: !shmget\n");
|
|
|
|
|
goto err1;
|
|
|
|
|
}
|
|
|
|
|
shminfo.shmaddr = ximg->data = (char *)shmat(shminfo.shmid, 0, 0);
|
|
|
|
|
if (!shminfo.shmaddr) {
|
|
|
|
|
fprintf(stderr, "tint2: !shmat\n");
|
|
|
|
|
goto err2;
|
|
|
|
|
}
|
|
|
|
|
shminfo.readOnly = False;
|
|
|
|
|
if (!XShmAttach(server.display, &shminfo)) {
|
|
|
|
|
fprintf(stderr, "tint2: !xshmattach\n");
|
|
|
|
|
goto err3;
|
|
|
|
|
}
|
|
|
|
|
if (!XShmGetImage(server.display, win, ximg, 0, 0, AllPlanes)) {
|
|
|
|
|
fprintf(stderr, "tint2: !xshmgetimage\n");
|
|
|
|
|
goto err4;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result = cairo_image_surface_create(CAIRO_FORMAT_RGB24, (int)tw, (int)th);
|
|
|
|
|
u_int32_t *data = (u_int32_t *)cairo_image_surface_get_data(result);
|
|
|
|
|
memset(data, 0, tw * th);
|
|
|
|
|
|
|
|
|
|
const size_t xstep = w / fw;
|
|
|
|
|
const size_t ystep = h / th;
|
|
|
|
|
const size_t offset_x1 = xstep / 4;
|
|
|
|
|
const size_t offset_y1 = ystep / 4;
|
|
|
|
|
const size_t offset_x2 = 3 * xstep / 4;
|
|
|
|
|
const size_t offset_y2 = ystep / 4;
|
|
|
|
|
const size_t offset_x3 = xstep / 4;
|
|
|
|
|
const size_t offset_y3 = 3 * ystep / 4;
|
|
|
|
|
const size_t offset_x4 = 3 * xstep / 4;
|
|
|
|
|
const size_t offset_y4 = ystep / 4;
|
|
|
|
|
const size_t offset_x5 = xstep / 2;
|
|
|
|
|
const size_t offset_y5 = ystep / 2;
|
|
|
|
|
const size_t offset_x6 = 5 * xstep / 8;
|
|
|
|
|
const size_t offset_y6 = 3 * ystep / 16;
|
|
|
|
|
const size_t offset_x7 = 3 * xstep / 16;
|
|
|
|
|
const size_t offset_y7 = 5 * ystep / 8;
|
|
|
|
|
const size_t rmask = 0xff0000;
|
|
|
|
|
const size_t gmask = 0xff00;
|
|
|
|
|
const size_t bmask = 0xff;
|
|
|
|
|
for (size_t yt = 0, y = 0; yt < th; yt++, y += ystep) {
|
|
|
|
|
for (size_t xt = 0, x = 0; xt < fw; xt++, x += xstep) {
|
|
|
|
|
size_t j = yt * tw + ox + xt;
|
|
|
|
|
u_int32_t c1 = (u_int32_t)XGetPixel(ximg, (int)(x + offset_x1), (int)(y + offset_y1));
|
|
|
|
|
u_int32_t c2 = (u_int32_t)XGetPixel(ximg, (int)(x + offset_x2), (int)(y + offset_y2));
|
|
|
|
|
u_int32_t c3 = (u_int32_t)XGetPixel(ximg, (int)(x + offset_x3), (int)(y + offset_y3));
|
|
|
|
|
u_int32_t c4 = (u_int32_t)XGetPixel(ximg, (int)(x + offset_x4), (int)(y + offset_y4));
|
|
|
|
|
u_int32_t c5 = (u_int32_t)XGetPixel(ximg, (int)(x + offset_x5), (int)(y + offset_y5));
|
|
|
|
|
u_int32_t c6 = (u_int32_t)XGetPixel(ximg, (int)(x + offset_x6), (int)(y + offset_y6));
|
|
|
|
|
u_int32_t c7 = (u_int32_t)XGetPixel(ximg, (int)(x + offset_x7), (int)(y + offset_y7));
|
|
|
|
|
u_int32_t b = ((c1 & bmask) + (c2 & bmask) + (c3 & bmask) + (c4 & bmask) + (c5 & bmask) * 2 + (c6 & bmask) +
|
|
|
|
|
(c7 & bmask)) /
|
|
|
|
|
8;
|
|
|
|
|
u_int32_t g = ((c1 & gmask) + (c2 & gmask) + (c3 & gmask) + (c4 & gmask) + (c5 & gmask) * 2 + (c6 & gmask) +
|
|
|
|
|
(c7 & gmask)) /
|
|
|
|
|
8;
|
|
|
|
|
u_int32_t r = ((c1 & rmask) + (c2 & rmask) + (c3 & rmask) + (c4 & rmask) + (c5 & rmask) * 2 + (c6 & rmask) +
|
|
|
|
|
(c7 & rmask)) /
|
|
|
|
|
8;
|
|
|
|
|
data[j] = (r & rmask) | (g & gmask) | (b & bmask);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2nd pass
|
|
|
|
|
smooth_thumbnail(result);
|
|
|
|
|
|
|
|
|
|
err4:
|
|
|
|
|
XShmDetach(server.display, &shminfo);
|
|
|
|
|
err3:
|
|
|
|
|
shmdt(shminfo.shmaddr);
|
|
|
|
|
err2:
|
|
|
|
|
shmctl(shminfo.shmid, IPC_RMID, NULL);
|
|
|
|
|
err1:
|
|
|
|
|
XDestroyImage(ximg);
|
|
|
|
|
err0:
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cairo_surface_t *get_window_thumbnail_cairo(Window win, int size)
|
|
|
|
|
{
|
|
|
|
|
static cairo_filter_t filter = CAIRO_FILTER_BEST;
|
|
|
|
|
XWindowAttributes wa;
|
|
|
|
|
if (!XGetWindowAttributes(server.display, win, &wa))
|
|
|
|
|
return NULL;
|
|
|
|
|
@@ -373,40 +515,81 @@ cairo_surface_t *get_window_thumbnail(Window win, int size)
|
|
|
|
|
th = h * tw / w;
|
|
|
|
|
if (th > tw * 0.618) {
|
|
|
|
|
th = (int)(tw * 0.618);
|
|
|
|
|
sy = th/(double)h;
|
|
|
|
|
sy = th / (double)h;
|
|
|
|
|
double fw = w * th / h;
|
|
|
|
|
sx = fw / w;
|
|
|
|
|
ox = (tw - fw) / 2;
|
|
|
|
|
oy = 0;
|
|
|
|
|
} else {
|
|
|
|
|
sx = tw/(double)w;
|
|
|
|
|
sy = th/(double)h;
|
|
|
|
|
sx = tw / (double)w;
|
|
|
|
|
sy = th / (double)h;
|
|
|
|
|
ox = oy = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cairo_surface_t *x11_surface =
|
|
|
|
|
cairo_xlib_surface_create(server.display, win, wa.visual, w, h);
|
|
|
|
|
cairo_surface_t *image_surface = cairo_surface_create_similar_image(x11_surface, CAIRO_FORMAT_ARGB32, tw, th);
|
|
|
|
|
cairo_surface_t *x11_surface = cairo_xlib_surface_create(server.display, win, wa.visual, w, h);
|
|
|
|
|
|
|
|
|
|
cairo_surface_t *full_surface = cairo_surface_create_similar_image(x11_surface, CAIRO_FORMAT_ARGB32, w, h);
|
|
|
|
|
cairo_t *cr0 = cairo_create(full_surface);
|
|
|
|
|
cairo_set_source_surface(cr0, x11_surface, 0, 0);
|
|
|
|
|
cairo_paint(cr0);
|
|
|
|
|
cairo_destroy(cr0);
|
|
|
|
|
|
|
|
|
|
cairo_surface_t *image_surface = cairo_surface_create_similar_image(full_surface, CAIRO_FORMAT_ARGB32, tw, th);
|
|
|
|
|
|
|
|
|
|
double start_time = get_time();
|
|
|
|
|
cairo_t *cr = cairo_create(image_surface);
|
|
|
|
|
cairo_translate(cr, ox, oy);
|
|
|
|
|
cairo_scale(cr, sx, sy);
|
|
|
|
|
cairo_set_source_surface(cr, x11_surface, 0, 0);
|
|
|
|
|
cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_GAUSSIAN);
|
|
|
|
|
cairo_set_source_surface(cr, full_surface, 0, 0);
|
|
|
|
|
cairo_pattern_set_filter(cairo_get_source(cr), filter);
|
|
|
|
|
cairo_paint(cr);
|
|
|
|
|
cairo_destroy(cr);
|
|
|
|
|
cairo_surface_destroy(x11_surface);
|
|
|
|
|
if (filter == CAIRO_FILTER_FAST)
|
|
|
|
|
smooth_thumbnail(image_surface);
|
|
|
|
|
double end_time = get_time();
|
|
|
|
|
|
|
|
|
|
uint32_t *pixels = (uint32_t *)cairo_image_surface_get_data(image_surface);
|
|
|
|
|
gboolean empty = TRUE;
|
|
|
|
|
for (int i = 0; empty && i < tw * th; i += 4) {
|
|
|
|
|
if (pixels[i] & 0xffFFff)
|
|
|
|
|
empty = FALSE;
|
|
|
|
|
}
|
|
|
|
|
if (empty) {
|
|
|
|
|
cairo_surface_destroy(image_surface);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
if (end_time - start_time > 0.030)
|
|
|
|
|
filter = CAIRO_FILTER_FAST;
|
|
|
|
|
else if (end_time - start_time < 0.010)
|
|
|
|
|
filter = CAIRO_FILTER_BEST;
|
|
|
|
|
|
|
|
|
|
return image_surface;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gboolean cairo_surface_is_blank(cairo_surface_t *image_surface)
|
|
|
|
|
{
|
|
|
|
|
uint32_t *pixels = (uint32_t *)cairo_image_surface_get_data(image_surface);
|
|
|
|
|
gboolean empty = TRUE;
|
|
|
|
|
int size = cairo_image_surface_get_width(image_surface) * cairo_image_surface_get_height(image_surface);
|
|
|
|
|
for (int i = 0; empty && i < size; i++) {
|
|
|
|
|
if (pixels[i] & 0xffFFff)
|
|
|
|
|
empty = FALSE;
|
|
|
|
|
}
|
|
|
|
|
return empty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cairo_surface_t *get_window_thumbnail(Window win, int size, gboolean active)
|
|
|
|
|
{
|
|
|
|
|
cairo_surface_t *image_surface = NULL;
|
|
|
|
|
if (server.has_shm && server.composite_manager) {
|
|
|
|
|
image_surface = screenshot(win, (size_t)size);
|
|
|
|
|
if (cairo_surface_is_blank(image_surface)) {
|
|
|
|
|
cairo_surface_destroy(image_surface);
|
|
|
|
|
image_surface = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!image_surface) {
|
|
|
|
|
image_surface = get_window_thumbnail_cairo(win, size);
|
|
|
|
|
if (cairo_surface_is_blank(image_surface)) {
|
|
|
|
|
cairo_surface_destroy(image_surface);
|
|
|
|
|
image_surface = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!image_surface)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
return image_surface;
|
|
|
|
|
}
|
|
|
|
|
|