mirror of
https://github.com/davatorium/rofi.git
synced 2025-04-07 17:33:14 -04:00
window: Add icons support (quick and dirty)
This commit is contained in:
parent
03eb4a3abb
commit
f3b4af1da0
4 changed files with 146 additions and 5 deletions
|
@ -111,7 +111,7 @@ Settings config = {
|
|||
.dpi = -1,
|
||||
.threads = 0,
|
||||
.scroll_method = 0,
|
||||
.window_format = "{w} {c} {t}",
|
||||
.window_format = "{w} {i}{c} {t}",
|
||||
.click_to_exit = TRUE,
|
||||
.show_match = TRUE,
|
||||
.theme = NULL,
|
||||
|
|
|
@ -220,6 +220,15 @@ char * rofi_force_utf8 ( const gchar *data, ssize_t length );
|
|||
*/
|
||||
char * rofi_latin_to_utf8_strdup ( const char *input, gssize length );
|
||||
|
||||
/**
|
||||
* @param input the string to escape
|
||||
*
|
||||
* Escape XML markup from the string. @param input is freed.
|
||||
*
|
||||
* @return the escaped string
|
||||
*/
|
||||
gchar *rofi_escape_markup ( gchar *input );
|
||||
|
||||
/**
|
||||
* @param pattern The user input to match against.
|
||||
* @param plen Pattern length.
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <strings.h>
|
||||
#include <string.h>
|
||||
|
@ -77,6 +78,8 @@ typedef struct
|
|||
long hint_flags;
|
||||
uint32_t wmdesktop;
|
||||
char *wmdesktopstr;
|
||||
cairo_surface_t *icon;
|
||||
gboolean icon_checked;
|
||||
} client;
|
||||
|
||||
// window lists
|
||||
|
@ -149,6 +152,9 @@ static void winlist_empty ( winlist *l )
|
|||
while ( l->len > 0 ) {
|
||||
client *c = l->data[--l->len];
|
||||
if ( c != NULL ) {
|
||||
if ( c->icon ) {
|
||||
cairo_surface_destroy ( c->icon );
|
||||
}
|
||||
g_free ( c->title );
|
||||
g_free ( c->class );
|
||||
g_free ( c->name );
|
||||
|
@ -295,18 +301,18 @@ static client* window_client ( ModeModePrivateData *pd, xcb_window_t win )
|
|||
|
||||
c->title = window_get_text_prop ( c->window, xcb->ewmh._NET_WM_NAME );
|
||||
if ( c->title == NULL ) {
|
||||
c->title = window_get_text_prop ( c->window, XCB_ATOM_WM_NAME );
|
||||
c->title = rofi_escape_markup ( window_get_text_prop ( c->window, XCB_ATOM_WM_NAME ) );
|
||||
}
|
||||
pd->title_len = MAX ( c->title ? g_utf8_strlen ( c->title, -1 ) : 0, pd->title_len );
|
||||
|
||||
c->role = window_get_text_prop ( c->window, netatoms[WM_WINDOW_ROLE] );
|
||||
c->role = rofi_escape_markup ( window_get_text_prop ( c->window, netatoms[WM_WINDOW_ROLE] ) );
|
||||
pd->role_len = MAX ( c->role ? g_utf8_strlen ( c->role, -1 ) : 0, pd->role_len );
|
||||
|
||||
cky = xcb_icccm_get_wm_class ( xcb->connection, c->window );
|
||||
xcb_icccm_get_wm_class_reply_t wcr;
|
||||
if ( xcb_icccm_get_wm_class_reply ( xcb->connection, cky, &wcr, NULL ) ) {
|
||||
c->class = rofi_latin_to_utf8_strdup ( wcr.class_name, -1 );
|
||||
c->name = rofi_latin_to_utf8_strdup ( wcr.instance_name, -1 );
|
||||
c->class = rofi_escape_markup ( rofi_latin_to_utf8_strdup ( wcr.class_name, -1 ) );
|
||||
c->name = rofi_escape_markup ( rofi_latin_to_utf8_strdup ( wcr.instance_name, -1 ) );
|
||||
pd->name_len = MAX ( c->name ? g_utf8_strlen ( c->name, -1 ) : 0, pd->name_len );
|
||||
xcb_icccm_get_wm_class_reply_wipe ( &wcr );
|
||||
}
|
||||
|
@ -686,6 +692,9 @@ static gboolean helper_eval_cb ( const GMatchInfo *info, GString *str, gpointer
|
|||
if ( match[1] == 'w' ) {
|
||||
helper_eval_add_str ( str, d->c->wmdesktopstr, l, d->pd->wmdn_len );
|
||||
}
|
||||
else if ( match[1] == 'i' ) {
|
||||
g_string_append ( str, "<span alpha=\"1\">\uFFFC</span>" );
|
||||
}
|
||||
else if ( match[1] == 'c' ) {
|
||||
helper_eval_add_str ( str, d->c->class, l, d->pd->clf_len );
|
||||
}
|
||||
|
@ -717,6 +726,7 @@ static char *_get_display_value ( const Mode *sw, unsigned int selected_line, in
|
|||
if ( c == NULL ) {
|
||||
return get_entry ? g_strdup ( "Window has fanished" ) : NULL;
|
||||
}
|
||||
*state |= MARKUP;
|
||||
if ( c->demands ) {
|
||||
*state |= URGENT;
|
||||
}
|
||||
|
@ -726,6 +736,116 @@ static char *_get_display_value ( const Mode *sw, unsigned int selected_line, in
|
|||
return get_entry ? _generate_display_string ( rmpd, c ) : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Icon code borrowed from https://github.com/olejorgenb/extract-window-icon
|
||||
*/
|
||||
static cairo_user_data_key_t data_key;
|
||||
|
||||
/** Create a surface object from this image data.
|
||||
* \param width The width of the image.
|
||||
* \param height The height of the image
|
||||
* \param data The image's data in ARGB format, will be copied by this function.
|
||||
*/
|
||||
static cairo_surface_t * draw_surface_from_data ( int width, int height, uint32_t *data )
|
||||
{
|
||||
unsigned long int len = width * height;
|
||||
unsigned long int i;
|
||||
uint32_t *buffer = g_new0 ( uint32_t, len );
|
||||
cairo_surface_t *surface;
|
||||
|
||||
/* Cairo wants premultiplied alpha, meh :( */
|
||||
for ( i = 0; i < len; i++ ) {
|
||||
uint8_t a = ( data[i] >> 24 ) & 0xff;
|
||||
double alpha = a / 255.0;
|
||||
uint8_t r = ( ( data[i] >> 16 ) & 0xff ) * alpha;
|
||||
uint8_t g = ( ( data[i] >> 8 ) & 0xff ) * alpha;
|
||||
uint8_t b = ( ( data[i] >> 0 ) & 0xff ) * alpha;
|
||||
buffer[i] = ( a << 24 ) | ( r << 16 ) | ( g << 8 ) | b;
|
||||
}
|
||||
|
||||
surface = cairo_image_surface_create_for_data ( (unsigned char *) buffer,
|
||||
CAIRO_FORMAT_ARGB32,
|
||||
width,
|
||||
height,
|
||||
width * 4 );
|
||||
/* This makes sure that buffer will be freed */
|
||||
cairo_surface_set_user_data ( surface, &data_key, buffer, g_free );
|
||||
|
||||
return surface;
|
||||
}
|
||||
static cairo_surface_t * ewmh_window_icon_from_reply ( xcb_get_property_reply_t *r, uint32_t preferred_size )
|
||||
{
|
||||
uint32_t *data, *end, *found_data = 0;
|
||||
uint32_t found_size = 0;
|
||||
|
||||
if ( !r || r->type != XCB_ATOM_CARDINAL || r->format != 32 || r->length < 2 ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
data = (uint32_t *) xcb_get_property_value ( r );
|
||||
if ( !data ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
end = data + r->length;
|
||||
|
||||
/* Goes over the icon data and picks the icon that best matches the size preference.
|
||||
* In case the size match is not exact, picks the closest bigger size if present,
|
||||
* closest smaller size otherwise.
|
||||
*/
|
||||
while ( data + 1 < end ) {
|
||||
/* check whether the data size specified by width and height fits into the array we got */
|
||||
uint64_t data_size = (uint64_t) data[0] * data[1];
|
||||
if ( data_size > (uint64_t) ( end - data - 2 ) ) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* use the greater of the two dimensions to match against the preferred size */
|
||||
uint32_t size = MAX ( data[0], data[1] );
|
||||
|
||||
/* pick the icon if it's a better match than the one we already have */
|
||||
gboolean found_icon_too_small = found_size < preferred_size;
|
||||
gboolean found_icon_too_large = found_size > preferred_size;
|
||||
gboolean icon_empty = data[0] == 0 || data[1] == 0;
|
||||
gboolean better_because_bigger = found_icon_too_small && size > found_size;
|
||||
gboolean better_because_smaller = found_icon_too_large &&
|
||||
size >= preferred_size && size < found_size;
|
||||
if ( !icon_empty && ( better_because_bigger || better_because_smaller || found_size == 0 ) ) {
|
||||
found_data = data;
|
||||
found_size = size;
|
||||
}
|
||||
|
||||
data += data_size + 2;
|
||||
}
|
||||
|
||||
if ( !found_data ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return draw_surface_from_data ( found_data[0], found_data[1], found_data + 2 );
|
||||
}
|
||||
/** Get NET_WM_ICON. */
|
||||
static cairo_surface_t * get_net_wm_icon ( xcb_window_t xid, uint32_t preferred_size )
|
||||
{
|
||||
xcb_get_property_cookie_t cookie = xcb_get_property_unchecked (
|
||||
xcb->connection, FALSE, xid,
|
||||
xcb->ewmh._NET_WM_ICON, XCB_ATOM_CARDINAL, 0, UINT32_MAX );
|
||||
xcb_get_property_reply_t *r = xcb_get_property_reply ( xcb->connection, cookie, NULL );
|
||||
cairo_surface_t *surface = ewmh_window_icon_from_reply ( r, preferred_size );
|
||||
free ( r );
|
||||
return surface;
|
||||
}
|
||||
static cairo_surface_t *_get_icon ( const Mode *sw, unsigned int selected_line, int size )
|
||||
{
|
||||
ModeModePrivateData *rmpd = mode_get_private_data ( sw );
|
||||
client *c = window_client ( rmpd, rmpd->ids->array[selected_line] );
|
||||
if ( c->icon_checked == FALSE ) {
|
||||
c->icon = get_net_wm_icon ( rmpd->ids->array[selected_line], size );
|
||||
c->icon_checked = TRUE;
|
||||
}
|
||||
return c->icon;
|
||||
}
|
||||
|
||||
#include "mode-private.h"
|
||||
Mode window_mode =
|
||||
{
|
||||
|
@ -737,6 +857,7 @@ Mode window_mode =
|
|||
._destroy = window_mode_destroy,
|
||||
._token_match = window_match,
|
||||
._get_display_value = _get_display_value,
|
||||
._get_icon = _get_icon,
|
||||
._get_completion = NULL,
|
||||
._preprocess_input = NULL,
|
||||
.private_data = NULL,
|
||||
|
@ -752,6 +873,7 @@ Mode window_mode_cd =
|
|||
._destroy = window_mode_destroy,
|
||||
._token_match = window_match,
|
||||
._get_display_value = _get_display_value,
|
||||
._get_icon = _get_icon,
|
||||
._get_completion = NULL,
|
||||
._preprocess_input = NULL,
|
||||
.private_data = NULL,
|
||||
|
|
|
@ -730,6 +730,16 @@ char * rofi_latin_to_utf8_strdup ( const char *input, gssize length )
|
|||
return g_convert_with_fallback ( input, length, "UTF-8", "latin1", "\uFFFD", NULL, &slength, NULL );
|
||||
}
|
||||
|
||||
gchar *rofi_escape_markup ( gchar *text )
|
||||
{
|
||||
if ( text == NULL ) {
|
||||
return NULL;
|
||||
}
|
||||
gchar *ret = g_markup_escape_text ( text, -1 );
|
||||
g_free ( text );
|
||||
return ret;
|
||||
}
|
||||
|
||||
char * rofi_force_utf8 ( const gchar *data, ssize_t length )
|
||||
{
|
||||
if ( data == NULL ) {
|
||||
|
|
Loading…
Add table
Reference in a new issue