Taskbar: thumbnails (optimizations)
This commit is contained in:
parent
5730725762
commit
1b6fd91611
6 changed files with 214 additions and 25 deletions
|
@ -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();
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue