From 5a867a83c645f5f797c33115c1716c93d632207e Mon Sep 17 00:00:00 2001 From: o9000 Date: Tue, 14 Nov 2017 10:34:57 +0100 Subject: [PATCH] Taskbar: thumbnails --- src/main.c | 2 +- src/taskbar/task.c | 28 ++++++++++++++++++++++++- src/taskbar/task.h | 1 + src/tooltip/tooltip.c | 37 +++++++++++++++++++++++++++------ src/tooltip/tooltip.h | 3 ++- src/util/area.h | 1 + src/util/window.c | 48 +++++++++++++++++++++++++++++++++++++++---- src/util/window.h | 3 ++- 8 files changed, 109 insertions(+), 14 deletions(-) diff --git a/src/main.c b/src/main.c index 1c53da2..b1feb47 100644 --- a/src/main.c +++ b/src/main.c @@ -262,7 +262,7 @@ void handle_event_property_notify(XEvent *e) if (at == server.atom._NET_WM_VISIBLE_NAME || at == server.atom._NET_WM_NAME || at == server.atom.WM_NAME) { if (task_update_title(task)) { if (g_tooltip.mapped && (g_tooltip.area == (Area *)task)) { - tooltip_copy_text((Area *)task); + tooltip_update_contents_for((Area *)task); tooltip_update(); } if (taskbar_sort_method == TASKBAR_SORT_TITLE) diff --git a/src/taskbar/task.c b/src/taskbar/task.c index 0f4c011..c0d11cd 100644 --- a/src/taskbar/task.c +++ b/src/taskbar/task.c @@ -47,6 +47,12 @@ char *task_get_tooltip(void *obj) return strdup(t->title); } +cairo_surface_t *task_get_thumbnail(void *obj) +{ + Task *t = (Task *)obj; + return t->thumbnail; +} + Task *add_task(Window win) { if (!win) @@ -122,8 +128,10 @@ Task *add_task(Window win) task_instance->area.on_screen = always_show_all_desktop_tasks; } task_instance->title = task_template.title; - if (panels[monitor].g_task.tooltip_enabled) + if (panels[monitor].g_task.tooltip_enabled) { task_instance->area._get_tooltip_text = task_get_tooltip; + task_instance->area._get_tooltip_image = task_get_thumbnail; + } for (int k = 0; k < TASK_STATE_COUNT; ++k) { task_instance->icon[k] = task_template.icon[k]; task_instance->icon_hover[k] = task_template.icon_hover[k]; @@ -201,6 +209,8 @@ void remove_task(Task *task) // fprintf(stderr, "tint2: remove_task %s %d\n", task->title, task->desktop); if (task->title) free(task->title); + if (task->thumbnail) + cairo_surface_destroy(task->thumbnail); task_remove_icon(task); GPtrArray *task_buttons = g_hash_table_lookup(win_to_task, &win); @@ -212,6 +222,8 @@ void remove_task(Task *task) task_drag = 0; if (g_slist_find(urgent_list, task2)) del_urgent(task2); + if (g_tooltip.area == &task2->area) + tooltip_hide(NULL); remove_area((Area *)task2); free(task2); } @@ -603,11 +615,25 @@ void reset_active_task() } } +void task_refresh_thumbnail(Task *task) +{ + cairo_surface_t *thumbnail = get_window_thumbnail(task->win); + if (!thumbnail) + return; + if (task->thumbnail) + cairo_surface_destroy(task->thumbnail); + task->thumbnail = thumbnail; +} + void set_task_state(Task *task, TaskState state) { if (!task || state == TASK_UNDEFINED || state >= TASK_STATE_COUNT) return; + if (state != TASK_ICONIFIED) { + task_refresh_thumbnail(task); + } + if (state == TASK_ACTIVE && task->current_state != state) { clock_gettime(CLOCK_MONOTONIC, &task->last_activation_time); if (taskbar_sort_method == TASKBAR_SORT_LRU || taskbar_sort_method == TASKBAR_SORT_MRU) { diff --git a/src/taskbar/task.h b/src/taskbar/task.h index 0c24646..b52f975 100644 --- a/src/taskbar/task.h +++ b/src/taskbar/task.h @@ -74,6 +74,7 @@ typedef struct Task { double _text_posy; int _icon_x; int _icon_y; + cairo_surface_t *thumbnail; } Task; extern timeout *urgent_timeout; diff --git a/src/tooltip/tooltip.c b/src/tooltip/tooltip.c index 7cc16b9..90b6885 100644 --- a/src/tooltip/tooltip.c +++ b/src/tooltip/tooltip.c @@ -55,7 +55,7 @@ void cleanup_tooltip() { stop_tooltip_timeout(); tooltip_hide(NULL); - tooltip_copy_text(NULL); + tooltip_update_contents_for(NULL); if (g_tooltip.window) XDestroyWindow(server.display, g_tooltip.window); g_tooltip.window = 0; @@ -118,7 +118,7 @@ void tooltip_trigger_show(Area *area, Panel *p, XEvent *e) just_shown = TRUE; g_tooltip.panel = p; if (g_tooltip.mapped && g_tooltip.area != area) { - tooltip_copy_text(area); + tooltip_update_contents_for(area); tooltip_update(); stop_tooltip_timeout(); } else if (!g_tooltip.mapped) { @@ -133,7 +133,7 @@ void tooltip_show(void *arg) XTranslateCoordinates(server.display, server.root_win, g_tooltip.panel->main_win, x, y, &mx, &my, &w); Area *area = find_area_under_mouse(g_tooltip.panel, mx, my); if (!g_tooltip.mapped && area->_get_tooltip_text) { - tooltip_copy_text(area); + tooltip_update_contents_for(area); g_tooltip.mapped = True; XMapWindow(server.display, g_tooltip.window); tooltip_update(); @@ -162,6 +162,12 @@ void tooltip_update_geometry() pango_layout_get_pixel_extents(layout, &r1, &r2); width = left_right_bg_border_width(g_tooltip.bg) + 2 * g_tooltip.paddingx + r2.width; height = top_bottom_bg_border_width(g_tooltip.bg) + 2 * g_tooltip.paddingy + r2.height; + if (g_tooltip.image) { + width = MAX(width, + left_right_bg_border_width(g_tooltip.bg) + 2 * g_tooltip.paddingx + + cairo_image_surface_get_width(g_tooltip.image)); + height += g_tooltip.paddingy + cairo_image_surface_get_height(g_tooltip.image); + } if (panel_horizontal && panel_position & BOTTOM) y = panel->posy - height; @@ -278,8 +284,16 @@ void tooltip_update() -r1.x / 2 + left_bg_border_width(g_tooltip.bg) + g_tooltip.paddingx, -r1.y / 2 + 1 + top_bg_border_width(g_tooltip.bg) + g_tooltip.paddingy); pango_cairo_show_layout(c, layout); - g_object_unref(layout); + + if (g_tooltip.image) { + cairo_translate(c, + g_tooltip.paddingx, + height - g_tooltip.paddingy - cairo_image_surface_get_height(g_tooltip.image)); + cairo_set_source_surface(c, g_tooltip.image, 0, 0); + cairo_paint(c); + } + cairo_destroy(c); cairo_surface_destroy(cs); } @@ -287,7 +301,7 @@ void tooltip_update() void tooltip_trigger_hide() { if (g_tooltip.mapped) { - tooltip_copy_text(0); + tooltip_update_contents_for(NULL); start_hide_timeout(); } else { // tooltip not visible yet, but maybe a timeout is still pending @@ -319,12 +333,23 @@ void stop_tooltip_timeout() stop_timeout(g_tooltip.timeout); } -void tooltip_copy_text(Area *area) +void tooltip_update_contents_for(Area *area) { free(g_tooltip.tooltip_text); if (area && area->_get_tooltip_text) g_tooltip.tooltip_text = area->_get_tooltip_text(area); else g_tooltip.tooltip_text = NULL; + if (area && area->_get_tooltip_image) { + if (g_tooltip.image) + cairo_surface_destroy(g_tooltip.image); + g_tooltip.image = area->_get_tooltip_image(area); + if (g_tooltip.image) + cairo_surface_reference(g_tooltip.image); + } else { + if (g_tooltip.image) + cairo_surface_destroy(g_tooltip.image); + g_tooltip.image = NULL; + } g_tooltip.area = area; } diff --git a/src/tooltip/tooltip.h b/src/tooltip/tooltip.h index a4294ec..56d4a38 100644 --- a/src/tooltip/tooltip.h +++ b/src/tooltip/tooltip.h @@ -37,6 +37,7 @@ typedef struct { Color font_color; Background *bg; timeout *timeout; + cairo_surface_t *image; } Tooltip; extern Tooltip g_tooltip; @@ -53,7 +54,7 @@ void tooltip_show(void * /*arg*/); void tooltip_update(); void tooltip_trigger_hide(); void tooltip_hide(void * /*arg*/); -void tooltip_copy_text(Area *area); +void tooltip_update_contents_for(Area *area); void tooltip_default_font_changed(); #endif // TOOLTIP_H diff --git a/src/util/area.h b/src/util/area.h index 8eb8361..2ea6345 100644 --- a/src/util/area.h +++ b/src/util/area.h @@ -233,6 +233,7 @@ typedef struct Area { // Returns a copy of the tooltip to be displayed for this widget. // The caller takes ownership of the pointer. char *(*_get_tooltip_text)(void *obj); + cairo_surface_t *(*_get_tooltip_image)(void *obj); // Returns true if the Area handles a mouse event at the given x, y coordinates relative to the window. // Leave this to NULL to use a default implementation. diff --git a/src/util/window.c b/src/util/window.c index 861cd0b..5c58875 100644 --- a/src/util/window.c +++ b/src/util/window.c @@ -157,7 +157,8 @@ int get_window_desktop(Window win) if (best_match < 0) best_match = 0; - // fprintf(stderr, "tint2: window %lx %s : viewport %d, (%d, %d)\n", win, get_task(win) ? get_task(win)->title : "??", + // fprintf(stderr, "tint2: window %lx %s : viewport %d, (%d, %d)\n", win, get_task(win) ? get_task(win)->title : + // "??", // best_match+1, x, y); return best_match; } @@ -190,15 +191,18 @@ int get_window_monitor(Window win) return best_match; } -void get_window_coordinates(Window win, int *x, int *y, int *w, int *h) +gboolean get_window_coordinates(Window win, int *x, int *y, int *w, int *h) { int dummy_int; unsigned ww, wh, bw, bh; Window src; - XTranslateCoordinates(server.display, win, server.root_win, 0, 0, x, y, &src); - XGetGeometry(server.display, win, &src, &dummy_int, &dummy_int, &ww, &wh, &bw, &bh); + if (!XTranslateCoordinates(server.display, win, server.root_win, 0, 0, x, y, &src)) + return FALSE; + if (!XGetGeometry(server.display, win, &src, &dummy_int, &dummy_int, &ww, &wh, &bw, &bh)) + return FALSE; *w = ww + bw; *h = wh + bh; + return TRUE; } gboolean window_is_iconified(Window win) @@ -352,3 +356,39 @@ char *get_window_name(Window win) XFree(text_property.value); return result; } + +cairo_surface_t *get_window_thumbnail(Window win) +{ + int x, y, w, h; + if (!get_window_coordinates(win, &x, &y, &w, &h)) + return NULL; + + int tw, th; + th = 128; + tw = w * th / h; + + cairo_surface_t *x11_surface = + cairo_xlib_surface_create(server.display, win, DefaultVisual(server.display, server.screen), w, h); + cairo_surface_t *image_surface = cairo_surface_create_similar_image(x11_surface, CAIRO_FORMAT_ARGB32, tw, th); + + cairo_t *cr = cairo_create(image_surface); + cairo_scale(cr, tw/(double)w, th/(double)h); + cairo_set_source_surface(cr, x11_surface, 0, 0); + cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_BEST); + cairo_paint(cr); + cairo_destroy(cr); + cairo_surface_destroy(x11_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) { + if (pixels[i] & 0xffFFff) + empty = FALSE; + } + if (empty) { + cairo_surface_destroy(image_surface); + return NULL; + } + + return image_surface; +} diff --git a/src/util/window.h b/src/util/window.h index 8df2e77..58ad7b4 100644 --- a/src/util/window.h +++ b/src/util/window.h @@ -25,7 +25,7 @@ int get_window_monitor(Window win); void activate_window(Window win); void close_window(Window win); -void get_window_coordinates(Window win, int *x, int *y, int *w, int *h); +gboolean get_window_coordinates(Window win, int *x, int *y, int *w, int *h); void toggle_window_maximized(Window win); void toggle_window_shade(Window win); void change_window_desktop(Window win, int desktop); @@ -34,5 +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); #endif