summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config/config.c2
-rw-r--r--include/helper.h9
-rw-r--r--source/dialogs/window.c130
-rw-r--r--source/helper.c10
4 files changed, 146 insertions, 5 deletions
diff --git a/config/config.c b/config/config.c
index 1792583c..8fc258f6 100644
--- a/config/config.c
+++ b/config/config.c
@@ -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,
diff --git a/include/helper.h b/include/helper.h
index 03d4515f..85034288 100644
--- a/include/helper.h
+++ b/include/helper.h
@@ -221,6 +221,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.
* @param str The input to match against pattern.
diff --git a/source/dialogs/window.c b/source/dialogs/window.c
index 535c2e72..1e3611e6 100644
--- a/source/dialogs/window.c
+++ b/source/dialogs/window.c
@@ -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,
diff --git a/source/helper.c b/source/helper.c
index f6b3b74d..5299ced3 100644
--- a/source/helper.c
+++ b/source/helper.c
@@ -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 ) {