From aeecf34c279bbbf035f6d7be179d37936c5fe1a6 Mon Sep 17 00:00:00 2001 From: Dave Davenport Date: Thu, 17 Oct 2019 21:19:19 +0200 Subject: [Window] Add thumbnail of windows as option. --- config/config.c | 1 + doc/default_theme.rasi | 70 +++++++++++++++++++-------------------- doc/old-theme-convert-output.rasi | 70 +++++++++++++++++++-------------------- doc/test_xr.txt | 2 ++ include/settings.h | 3 ++ include/xcb.h | 9 +++++ source/dialogs/window.c | 7 +++- source/xcb.c | 68 +++++++++++++++++++++++++++++++++++++ source/xrmoptions.c | 2 ++ 9 files changed, 161 insertions(+), 71 deletions(-) diff --git a/config/config.c b/config/config.c index 32d13228..c885447e 100644 --- a/config/config.c +++ b/config/config.c @@ -159,4 +159,5 @@ Settings config = { .matching_negate_char = '-', .cache_dir = NULL, + .window_thumbnail = FALSE, }; diff --git a/doc/default_theme.rasi b/doc/default_theme.rasi index 65736b08..345749c0 100644 --- a/doc/default_theme.rasi +++ b/doc/default_theme.rasi @@ -2,39 +2,39 @@ * rofi -dump-theme output. **/ * { - selected-normal-foreground: var(lightbg); - foreground: rgba ( 0, 43, 54, 100 % ); - normal-foreground: var(foreground); - alternate-normal-background: var(lightbg); red: rgba ( 220, 50, 47, 100 % ); - selected-urgent-foreground: var(background); - blue: rgba ( 38, 139, 210, 100 % ); + selected-active-foreground: var(background); + lightfg: rgba ( 88, 104, 117, 100 % ); + separatorcolor: var(foreground); urgent-foreground: var(red); alternate-urgent-background: var(lightbg); - active-foreground: var(blue); lightbg: rgba ( 238, 232, 213, 100 % ); - selected-active-foreground: var(background); - alternate-active-background: var(lightbg); - background: rgba ( 253, 246, 227, 100 % ); - alternate-normal-foreground: var(foreground); - normal-background: var(background); - lightfg: rgba ( 88, 104, 117, 100 % ); - selected-normal-background: var(lightfg); - border-color: var(foreground); spacing: 2; - separatorcolor: var(foreground); + border-color: var(foreground); + normal-background: var(background); + background-color: rgba ( 0, 0, 0, 0 % ); + alternate-active-background: var(lightbg); + active-foreground: var(blue); + blue: rgba ( 38, 139, 210, 100 % ); urgent-background: var(background); + alternate-normal-foreground: var(foreground); + selected-active-background: var(blue); + background: rgba ( 253, 246, 227, 100 % ); + selected-normal-foreground: var(lightbg); + active-background: var(background); + alternate-active-foreground: var(blue); + alternate-normal-background: var(lightbg); + foreground: rgba ( 0, 43, 54, 100 % ); selected-urgent-background: var(red); + selected-urgent-foreground: var(background); + normal-foreground: var(foreground); alternate-urgent-foreground: var(red); - background-color: rgba ( 0, 0, 0, 0 % ); - alternate-active-foreground: var(blue); - active-background: var(background); - selected-active-background: var(blue); + selected-normal-background: var(lightfg); } element { - border: 0; - spacing: 5px ; padding: 1px ; + spacing: 5px ; + border: 0; } element normal.normal { background-color: var(normal-background); @@ -82,40 +82,40 @@ element-icon { text-color: inherit; } window { + padding: 5; background-color: var(background); border: 1; - padding: 5; } mainbox { - border: 0; padding: 0; + border: 0; } message { - border: 2px dash 0px 0px ; - border-color: var(separatorcolor); padding: 1px ; + border-color: var(separatorcolor); + border: 2px dash 0px 0px ; } textbox { text-color: var(foreground); } listview { - fixed-height: 0; - border: 2px dash 0px 0px ; + padding: 2px 0px 0px ; + scrollbar: true; border-color: var(separatorcolor); spacing: 2px ; - scrollbar: true; - padding: 2px 0px 0px ; + fixed-height: 0; + border: 2px dash 0px 0px ; } scrollbar { width: 4px ; + padding: 0; + handle-width: 8px ; border: 0; handle-color: var(normal-foreground); - handle-width: 8px ; - padding: 0; } sidebar { - border: 2px dash 0px 0px ; border-color: var(separatorcolor); + border: 2px dash 0px 0px ; } button { spacing: 0; @@ -126,9 +126,9 @@ button selected { text-color: var(selected-normal-foreground); } inputbar { + padding: 1px ; spacing: 0px ; text-color: var(normal-foreground); - padding: 1px ; children: [ prompt,textbox-prompt-colon,entry,case-indicator ]; } case-indicator { @@ -144,8 +144,8 @@ prompt { text-color: var(normal-foreground); } textbox-prompt-colon { + margin: 0px 0.3000em 0.0000em 0.0000em ; expand: false; str: ":"; - margin: 0px 0.3000em 0.0000em 0.0000em ; text-color: inherit; } diff --git a/doc/old-theme-convert-output.rasi b/doc/old-theme-convert-output.rasi index bb64476c..86b9b89c 100644 --- a/doc/old-theme-convert-output.rasi +++ b/doc/old-theme-convert-output.rasi @@ -2,39 +2,39 @@ * rofi -dump-theme output. **/ * { - selected-normal-foreground: rgba ( 2, 20, 63, 100 % ); - foreground: rgba ( 219, 223, 188, 100 % ); - normal-foreground: var(foreground); - alternate-normal-background: rgba ( 0, 0, 0, 0 % ); red: rgba ( 220, 50, 47, 100 % ); - selected-urgent-foreground: rgba ( 2, 20, 63, 100 % ); - blue: rgba ( 38, 139, 210, 100 % ); + selected-active-foreground: rgba ( 2, 20, 63, 100 % ); + lightfg: rgba ( 88, 104, 117, 100 % ); + separatorcolor: rgba ( 219, 223, 188, 100 % ); urgent-foreground: rgba ( 255, 129, 255, 100 % ); alternate-urgent-background: rgba ( 0, 0, 0, 0 % ); - active-foreground: rgba ( 138, 196, 255, 100 % ); lightbg: rgba ( 238, 232, 213, 100 % ); - selected-active-foreground: rgba ( 2, 20, 63, 100 % ); - alternate-active-background: rgba ( 0, 0, 0, 0 % ); - background: rgba ( 0, 0, 33, 87 % ); - alternate-normal-foreground: var(foreground); - normal-background: rgba ( 0, 0, 0, 0 % ); - lightfg: rgba ( 88, 104, 117, 100 % ); - selected-normal-background: rgba ( 219, 223, 188, 100 % ); - border-color: rgba ( 219, 223, 188, 100 % ); spacing: 2; - separatorcolor: rgba ( 219, 223, 188, 100 % ); + border-color: rgba ( 219, 223, 188, 100 % ); + normal-background: rgba ( 0, 0, 0, 0 % ); + background-color: rgba ( 0, 0, 0, 0 % ); + alternate-active-background: rgba ( 0, 0, 0, 0 % ); + active-foreground: rgba ( 138, 196, 255, 100 % ); + blue: rgba ( 38, 139, 210, 100 % ); urgent-background: rgba ( 0, 0, 0, 0 % ); + alternate-normal-foreground: var(foreground); + selected-active-background: rgba ( 138, 196, 255, 100 % ); + background: rgba ( 0, 0, 33, 87 % ); + selected-normal-foreground: rgba ( 2, 20, 63, 100 % ); + active-background: rgba ( 0, 0, 0, 0 % ); + alternate-active-foreground: var(active-foreground); + alternate-normal-background: rgba ( 0, 0, 0, 0 % ); + foreground: rgba ( 219, 223, 188, 100 % ); selected-urgent-background: rgba ( 255, 129, 127, 100 % ); + selected-urgent-foreground: rgba ( 2, 20, 63, 100 % ); + normal-foreground: var(foreground); alternate-urgent-foreground: var(urgent-foreground); - background-color: rgba ( 0, 0, 0, 0 % ); - alternate-active-foreground: var(active-foreground); - active-background: rgba ( 0, 0, 0, 0 % ); - selected-active-background: rgba ( 138, 196, 255, 100 % ); + selected-normal-background: rgba ( 219, 223, 188, 100 % ); } element { - border: 0; - spacing: 5px ; padding: 1px ; + spacing: 5px ; + border: 0; } element normal.normal { background-color: var(normal-background); @@ -82,40 +82,40 @@ element-icon { text-color: inherit; } window { + padding: 5; background-color: var(background); border: 1; - padding: 5; } mainbox { - border: 0; padding: 0; + border: 0; } message { - border: 2px 0px 0px ; - border-color: var(separatorcolor); padding: 1px ; + border-color: var(separatorcolor); + border: 2px 0px 0px ; } textbox { text-color: var(foreground); } listview { - fixed-height: 0; - border: 2px 0px 0px ; + padding: 2px 0px 0px ; + scrollbar: true; border-color: var(separatorcolor); spacing: 2px ; - scrollbar: true; - padding: 2px 0px 0px ; + fixed-height: 0; + border: 2px 0px 0px ; } scrollbar { width: 4px ; + padding: 0; + handle-width: 8px ; border: 0; handle-color: var(normal-foreground); - handle-width: 8px ; - padding: 0; } sidebar { - border: 2px dash 0px 0px ; border-color: var(separatorcolor); + border: 2px dash 0px 0px ; } button { spacing: 0; @@ -126,9 +126,9 @@ button selected { text-color: var(selected-normal-foreground); } inputbar { + padding: 1px ; spacing: 0px ; text-color: var(normal-foreground); - padding: 1px ; children: [ prompt,textbox-prompt-colon,entry,case-indicator ]; } case-indicator { @@ -144,9 +144,9 @@ prompt { text-color: var(normal-foreground); } textbox-prompt-colon { + margin: 0px 0.3000em 0.0000em 0.0000em ; expand: false; str: ":"; - margin: 0px 0.3000em 0.0000em 0.0000em ; text-color: inherit; } mode-switcher { diff --git a/doc/test_xr.txt b/doc/test_xr.txt index faadacc6..695e47ee 100644 --- a/doc/test_xr.txt +++ b/doc/test_xr.txt @@ -126,6 +126,8 @@ rofi.scroll-method: 0 ! rofi.matching-negate-char: - ! "Directory where history and temporary files are stored." Set from: Default ! rofi.cache-dir: +! "Show window thumbnail in window switcher if availalbe." Set from: Default +! rofi.window-thumbnail: false ! "Pidfile location" Set from: File rofi.pid: /tmp/rofi.pid ! "Paste primary selection" Set from: File diff --git a/include/settings.h b/include/settings.h index 0c575f19..88b63a32 100644 --- a/include/settings.h +++ b/include/settings.h @@ -189,6 +189,9 @@ typedef struct /** Cache directory. */ char *cache_dir; + + /** Window Thumbnails */ + gboolean window_thumbnail; } Settings; /** Global Settings structure. */ extern Settings config; diff --git a/include/xcb.h b/include/xcb.h index c956b37f..b68caad5 100644 --- a/include/xcb.h +++ b/include/xcb.h @@ -181,4 +181,13 @@ typedef enum */ extern WindowManagerQuirk current_window_manager; +/** + * @param window the window the screenshot + * @param size Size of the thumbnail + * + * Creates a thumbnail of the window. + * + * @returns NULL if window was not found, or unmapped, otherwise returns a cairo_surface. + */ +cairo_surface_t *x11_helper_get_screenshot_surface_window ( xcb_window_t window, int size ); #endif diff --git a/source/dialogs/window.c b/source/dialogs/window.c index f1512eb5..ba244a03 100644 --- a/source/dialogs/window.c +++ b/source/dialogs/window.c @@ -112,6 +112,7 @@ typedef struct cairo_surface_t *icon; gboolean icon_checked; uint32_t icon_fetch_uid; + gboolean thumbnail_checked; } client; // window lists @@ -910,7 +911,11 @@ static cairo_surface_t *_get_icon ( const Mode *sw, unsigned int selected_line, { ModeModePrivateData *rmpd = mode_get_private_data ( sw ); client *c = window_client ( rmpd, rmpd->ids->array[selected_line] ); - if ( c->icon_checked == FALSE ) { + if ( config.window_thumbnail && c->thumbnail_checked == FALSE ) { + c->icon = x11_helper_get_screenshot_surface_window ( c->window, size ); + c->thumbnail_checked = TRUE; + } + if ( c->icon == NULL && c->icon_checked == FALSE ) { c->icon = get_net_wm_icon ( rmpd->ids->array[selected_line], size ); c->icon_checked = TRUE; } diff --git a/source/xcb.c b/source/xcb.c index f211da0e..9e941acc 100644 --- a/source/xcb.c +++ b/source/xcb.c @@ -99,6 +99,74 @@ static xcb_visualtype_t *root_visual = NULL; xcb_atom_t netatoms[NUM_NETATOMS]; const char *netatom_names[] = { EWMH_ATOMS ( ATOM_CHAR ) }; +static xcb_visualtype_t * lookup_visual (xcb_screen_t *s, xcb_visualid_t visual) +{ + xcb_depth_iterator_t d; + d = xcb_screen_allowed_depths_iterator (s); + for (; d.rem; xcb_depth_next (&d)) { + xcb_visualtype_iterator_t v = xcb_depth_visuals_iterator (d.data); + for (; v.rem; xcb_visualtype_next (&v)) { + if (v.data->visual_id == visual) + return v.data; + } + } + return 0; +} + + +cairo_surface_t *x11_helper_get_screenshot_surface_window ( xcb_window_t window, int size ) +{ + xcb_get_geometry_cookie_t cookie; + xcb_get_geometry_reply_t *reply; + + cookie = xcb_get_geometry(xcb->connection, window); + reply = xcb_get_geometry_reply(xcb->connection, cookie, NULL); + if ( reply == NULL ) { + return NULL; + } + + xcb_get_window_attributes_cookie_t attributesCookie = xcb_get_window_attributes (xcb->connection, window); + xcb_get_window_attributes_reply_t *attributes = xcb_get_window_attributes_reply (xcb->connection, + attributesCookie, + NULL ); + if ( attributes == NULL || ( attributes->map_state != XCB_MAP_STATE_VIEWABLE ) ) { + free(reply); + if ( attributes ) free(attributes); + return NULL; + } + // Create a cairo surface for the window. + xcb_visualtype_t * vt = lookup_visual(xcb->screen, attributes->visual); + free ( attributes ); + + cairo_surface_t *t = cairo_xcb_surface_create ( xcb->connection, window, vt , reply->width, reply->height ); + + if ( cairo_surface_status ( t ) != CAIRO_STATUS_SUCCESS ) { + cairo_surface_destroy(t); + free(reply); + return NULL; + } + + // Scale the image, as we don't want to keep large one around. + int max = MAX(reply->width, reply->height); + double scale = (double)size/ max; + + cairo_surface_t *s2 = cairo_surface_create_similar_image ( t, CAIRO_FORMAT_ARGB32, reply->width*scale,reply->height*scale ); + free ( reply ); + + if ( cairo_surface_status ( s2 ) != CAIRO_STATUS_SUCCESS ) { + cairo_surface_destroy ( t ) ; + return NULL; + } + // Paint it in. + cairo_t *d = cairo_create (s2); + cairo_scale ( d, scale, scale ); + cairo_set_source_surface ( d, t, 0, 0 ); + cairo_paint ( d); + cairo_destroy(d); + + cairo_surface_destroy(t); + return s2; +} /** * Holds for each supported modifier the possible modifier mask. * Check x11_mod_masks[MODIFIER]&mask != 0 to see if MODIFIER is activated. diff --git a/source/xrmoptions.c b/source/xrmoptions.c index ce0c5056..d81c46cd 100644 --- a/source/xrmoptions.c +++ b/source/xrmoptions.c @@ -223,6 +223,8 @@ static XrmOption xrmOptions[] = { "Set the character used to negate the matching. ('\\0' to disable)", CONFIG_DEFAULT }, { xrm_String, "cache-dir", { .str = &config.cache_dir }, NULL, "Directory where history and temporary files are stored.", CONFIG_DEFAULT }, + { xrm_Boolean, "window-thumbnail", { .snum = &config.window_thumbnail }, NULL, + "Show window thumbnail in window switcher if availalbe.", CONFIG_DEFAULT }, }; /** Dynamic array of extra options */ -- cgit v1.2.3