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( FindPkgConfig )
|
||||||
include( CheckLibraryExists )
|
include( CheckLibraryExists )
|
||||||
include( CheckCSourceCompiles )
|
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( PANGOCAIRO REQUIRED pangocairo )
|
||||||
pkg_check_modules( PANGO REQUIRED pango )
|
pkg_check_modules( PANGO REQUIRED pango )
|
||||||
pkg_check_modules( CAIRO REQUIRED cairo )
|
pkg_check_modules( CAIRO REQUIRED cairo )
|
||||||
|
|
|
@ -6,6 +6,11 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.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 "config.h"
|
||||||
#include "drag_and_drop.h"
|
#include "drag_and_drop.h"
|
||||||
#include "fps_distribution.h"
|
#include "fps_distribution.h"
|
||||||
|
@ -193,6 +198,7 @@ void init_X11_pre_config()
|
||||||
server.screen = DefaultScreen(server.display);
|
server.screen = DefaultScreen(server.display);
|
||||||
server.root_win = RootWindow(server.display, server.screen);
|
server.root_win = RootWindow(server.display, server.screen);
|
||||||
server.desktop = get_current_desktop();
|
server.desktop = get_current_desktop();
|
||||||
|
server.has_shm = XShmQueryExtension(server.display);
|
||||||
|
|
||||||
// Needed since the config file uses '.' as decimal separator
|
// Needed since the config file uses '.' as decimal separator
|
||||||
setlocale(LC_ALL, "");
|
setlocale(LC_ALL, "");
|
||||||
|
|
|
@ -147,6 +147,7 @@ typedef struct Server {
|
||||||
Global_atom atom;
|
Global_atom atom;
|
||||||
int xdamage_event_type;
|
int xdamage_event_type;
|
||||||
int xdamage_event_error_type;
|
int xdamage_event_error_type;
|
||||||
|
gboolean has_shm;
|
||||||
#ifdef HAVE_SN
|
#ifdef HAVE_SN
|
||||||
SnDisplay *sn_display;
|
SnDisplay *sn_display;
|
||||||
GTree *pids;
|
GTree *pids;
|
||||||
|
|
|
@ -629,16 +629,15 @@ void task_refresh_thumbnail(Task *task)
|
||||||
double now = get_time();
|
double now = get_time();
|
||||||
if (now - task->thumbnail_last_update < 0.1)
|
if (now - task->thumbnail_last_update < 0.1)
|
||||||
return;
|
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)
|
if (!thumbnail)
|
||||||
return;
|
return;
|
||||||
if (task->thumbnail)
|
if (task->thumbnail)
|
||||||
cairo_surface_destroy(task->thumbnail);
|
cairo_surface_destroy(task->thumbnail);
|
||||||
task->thumbnail = thumbnail;
|
task->thumbnail = thumbnail;
|
||||||
task->thumbnail_last_update = get_time();
|
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)) {
|
if (g_tooltip.mapped && (g_tooltip.area == &task->area)) {
|
||||||
tooltip_update_contents_for(&task->area);
|
tooltip_update_contents_for(&task->area);
|
||||||
tooltip_update();
|
tooltip_update();
|
||||||
|
|
|
@ -29,6 +29,10 @@
|
||||||
#include <cairo.h>
|
#include <cairo.h>
|
||||||
#include <cairo-xlib.h>
|
#include <cairo-xlib.h>
|
||||||
|
|
||||||
|
#include <X11/extensions/XShm.h>
|
||||||
|
#include <sys/ipc.h>
|
||||||
|
#include <sys/shm.h>
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "window.h"
|
#include "window.h"
|
||||||
#include "server.h"
|
#include "server.h"
|
||||||
|
@ -357,8 +361,146 @@ char *get_window_name(Window win)
|
||||||
return result;
|
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;
|
XWindowAttributes wa;
|
||||||
if (!XGetWindowAttributes(server.display, win, &wa))
|
if (!XGetWindowAttributes(server.display, win, &wa))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -384,29 +526,70 @@ cairo_surface_t *get_window_thumbnail(Window win, int size)
|
||||||
ox = oy = 0;
|
ox = oy = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
cairo_surface_t *x11_surface =
|
cairo_surface_t *x11_surface = cairo_xlib_surface_create(server.display, win, wa.visual, w, h);
|
||||||
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 *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_t *cr = cairo_create(image_surface);
|
||||||
cairo_translate(cr, ox, oy);
|
cairo_translate(cr, ox, oy);
|
||||||
cairo_scale(cr, sx, sy);
|
cairo_scale(cr, sx, sy);
|
||||||
cairo_set_source_surface(cr, x11_surface, 0, 0);
|
cairo_set_source_surface(cr, full_surface, 0, 0);
|
||||||
cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_GAUSSIAN);
|
cairo_pattern_set_filter(cairo_get_source(cr), filter);
|
||||||
cairo_paint(cr);
|
cairo_paint(cr);
|
||||||
cairo_destroy(cr);
|
cairo_destroy(cr);
|
||||||
cairo_surface_destroy(x11_surface);
|
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);
|
if (end_time - start_time > 0.030)
|
||||||
gboolean empty = TRUE;
|
filter = CAIRO_FILTER_FAST;
|
||||||
for (int i = 0; empty && i < tw * th; i += 4) {
|
else if (end_time - start_time < 0.010)
|
||||||
if (pixels[i] & 0xffFFff)
|
filter = CAIRO_FILTER_BEST;
|
||||||
empty = FALSE;
|
|
||||||
}
|
return image_surface;
|
||||||
if (empty) {
|
}
|
||||||
cairo_surface_destroy(image_surface);
|
|
||||||
return NULL;
|
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;
|
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);
|
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);
|
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
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue