Commit 1b6fd916 authored by o9000's avatar o9000

Taskbar: thumbnails (optimizations)

parent 57307257
......@@ -25,7 +25,7 @@ endif()
include( FindPkgConfig )
include( CheckLibraryExists )
include( CheckCSourceCompiles )
pkg_check_modules( X11 REQUIRED x11 xcomposite xdamage xinerama xrender xrandr>=1.3 )
pkg_check_modules( X11 REQUIRED x11 xcomposite xdamage xinerama xext xrender xrandr>=1.3 )
pkg_check_modules( PANGOCAIRO REQUIRED pangocairo )
pkg_check_modules( PANGO REQUIRED pango )
pkg_check_modules( CAIRO REQUIRED cairo )
......
......@@ -6,6 +6,11 @@
#include <string.h>
#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/extensions/XShm.h>
#include "config.h"
#include "drag_and_drop.h"
#include "fps_distribution.h"
......@@ -193,6 +198,7 @@ void init_X11_pre_config()
server.screen = DefaultScreen(server.display);
server.root_win = RootWindow(server.display, server.screen);
server.desktop = get_current_desktop();
server.has_shm = XShmQueryExtension(server.display);
// Needed since the config file uses '.' as decimal separator
setlocale(LC_ALL, "");
......
......@@ -147,6 +147,7 @@ typedef struct Server {
Global_atom atom;
int xdamage_event_type;
int xdamage_event_error_type;
gboolean has_shm;
#ifdef HAVE_SN
SnDisplay *sn_display;
GTree *pids;
......
......@@ -629,16 +629,15 @@ void task_refresh_thumbnail(Task *task)
double now = get_time();
if (now - task->thumbnail_last_update < 0.1)
return;
cairo_surface_t *thumbnail = get_window_thumbnail(task->win, panel_config.g_task.thumbnail_width);
fprintf(stderr, "tint2: thumbnail for window: %s" RESET "\n", task->title ? task->title : "");
cairo_surface_t *thumbnail = get_window_thumbnail(task->win, panel_config.g_task.thumbnail_width, task->current_state == TASK_ACTIVE);
if (!thumbnail)
return;
if (task->thumbnail)
cairo_surface_destroy(task->thumbnail);
task->thumbnail = thumbnail;
task->thumbnail_last_update = get_time();
if (task->thumbnail_last_update - now > 0.01) {
fprintf(stderr, YELLOW "tint2: %s took %f ms (window: %s)" RESET "\n", __func__, 1000 * (task->thumbnail_last_update - now), task->title ? task->title : "");
}
fprintf(stderr, YELLOW "tint2: %s took %f ms (window: %s)" RESET "\n", __func__, 1000 * (task->thumbnail_last_update - now), task->title ? task->title : "");
if (g_tooltip.mapped && (g_tooltip.area == &task->area)) {
tooltip_update_contents_for(&task->area);
tooltip_update();
......
......@@ -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();
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;
for (int i = 0; empty && i < tw * th; i += 4) {
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;
}
if (empty) {
cairo_surface_destroy(image_surface);
return NULL;
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;
}
......@@ -34,6 +34,6 @@ int get_icon_count(gulong *data, int num);
gulong *get_best_icon(gulong *data, int icon_count, int num, int *iw, int *ih, int best_icon_size);
char *get_window_name(Window win);
cairo_surface_t *get_window_thumbnail(Window win, int size);
cairo_surface_t *get_window_thumbnail(Window win, int size, gboolean active);
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment