diff --git a/include/helper.h b/include/helper.h index eae59ce1..de6f1097 100644 --- a/include/helper.h +++ b/include/helper.h @@ -166,5 +166,10 @@ void cmd_set_arguments ( int argc, char **argv ); char *rofi_expand_path ( const char *input ); unsigned int levenshtein ( const char *needle, const char *haystack ); +/** + * Convert string to valid utf-8, replacing invalid parts with replacement character. + */ +char * rofi_force_utf8 ( gchar *data ); +char * rofi_latin_to_utf8_strdup ( const char *input, gssize length ); /*@}*/ #endif // ROFI_HELPER_H diff --git a/include/mode.h b/include/mode.h index 90bae0f3..aaf38566 100644 --- a/include/mode.h +++ b/include/mode.h @@ -7,7 +7,7 @@ * The 'object' that makes a mode in rofi. * @{ */ -typedef struct rofi_mode Mode; +typedef struct rofi_mode Mode; /** * Enum used to sum the possible states of ROFI. diff --git a/include/x11-helper.h b/include/x11-helper.h index a1435c3f..730724bc 100644 --- a/include/x11-helper.h +++ b/include/x11-helper.h @@ -36,6 +36,8 @@ void window_set_atom_prop ( xcb_window_t w, xcb_atom_t prop, xcb_atom_t *atoms, #define EWMH_ATOMS( X ) \ X ( _NET_WM_WINDOW_OPACITY ), \ X ( I3_SOCKET_PATH ), \ + X ( UTF8_STRING ), \ + X ( STRING ), \ X ( WM_WINDOW_ROLE ) enum { EWMH_ATOMS ( ATOM_ENUM ), NUM_NETATOMS }; diff --git a/source/dialogs/dmenu.c b/source/dialogs/dmenu.c index 02594572..2ca1540e 100644 --- a/source/dialogs/dmenu.c +++ b/source/dialogs/dmenu.c @@ -92,10 +92,8 @@ static char **get_dmenu ( DmenuModePrivateData *pd, FILE *fd, unsigned int *leng data[l - 1] = '\0'; l--; } - if ( !g_utf8_validate ( data, l, NULL ) ) { - fprintf ( stderr, "String: '%s' is not valid utf-8\n", data ); - continue; - } + data = rofi_force_utf8 ( data ); + printf ( "data: %s\n", data ); retv[( *length )] = data; data = NULL; diff --git a/source/dialogs/window.c b/source/dialogs/window.c index 5c2bdc11..57684151 100644 --- a/source/dialogs/window.c +++ b/source/dialogs/window.c @@ -53,8 +53,6 @@ #define WINLIST 32 -#define CLIENTTITLE 100 -#define CLIENTCLASS 50 #define CLIENTSTATE 10 #define CLIENTWINDOWTYPE 10 #define CLIENTROLE 50 @@ -285,7 +283,8 @@ static client* window_client ( xcb_window_t win ) 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 = g_strdup ( wcr.class_name ); + c->class = rofi_latin_to_utf8_strdup ( wcr.class_name, -1 ); + c->name = rofi_latin_to_utf8_strdup ( wcr.instance_name, -1 ); xcb_icccm_get_wm_class_reply_wipe ( &wcr ); } @@ -295,6 +294,14 @@ static client* window_client ( xcb_window_t win ) c->hint_flags = r.flags; } + /** Do UTF-8 Check, should not be needed, does not hurt here to be paranoid. */ + { + c->title = rofi_force_utf8 ( c->title ); + c->class = rofi_force_utf8 ( c->class ); + c->name = rofi_force_utf8 ( c->name ); + c->role = rofi_force_utf8 ( c->role ); + } + winlist_append ( cache_client, c->window, c ); g_free ( attr ); return c; diff --git a/source/helper.c b/source/helper.c index 2485e802..955939e6 100644 --- a/source/helper.c +++ b/source/helper.c @@ -659,3 +659,42 @@ unsigned int levenshtein ( const char *needle, const char *haystack ) } return column[needlelen]; } + +char * rofi_latin_to_utf8_strdup ( const char *input, gssize length ) +{ + gsize slength = 0; + return g_convert_with_fallback ( input, length, "UTF-8", "latin1", "\uFFFD", NULL, &slength, NULL ); +} + +char * rofi_force_utf8 ( gchar *start ) +{ + if ( start == NULL ) { + return NULL; + } + const char *data = start; + const char *end; + gsize length = strlen ( data ); + GString *string; + + if ( g_utf8_validate ( data, length, &end ) ) { + return start; + } + string = g_string_sized_new ( length + 16 ); + + do { + /* Valid part of the string */ + g_string_append_len ( string, data, end - data ); + /* Replacement character */ + g_string_append ( string, "\uFFFD" ); + length -= ( end - data ) + 1; + data = end + 1; + } while ( !g_utf8_validate ( data, length, &end ) ); + + if ( length ) { + g_string_append_len ( string, data, length ); + } + + // Free input string. + g_free ( start ); + return g_string_free ( string, FALSE ); +} diff --git a/source/rofi.c b/source/rofi.c index eba030ba..7f708f3b 100644 --- a/source/rofi.c +++ b/source/rofi.c @@ -626,8 +626,8 @@ int main ( int argc, char *argv[] ) // 2 the binary that executed is called dmenu (e.g. symlink to rofi) else{ // Get the base name of the executable called. - char *base_name = g_path_get_basename ( argv[0] ); - const char * const dmenu_str = "dmenu"; + char *base_name = g_path_get_basename ( argv[0] ); + const char * const dmenu_str = "dmenu"; dmenu_mode = ( strcmp ( base_name, dmenu_str ) == 0 ); // Free the basename for dmenu detection. g_free ( base_name ); diff --git a/source/x11-helper.c b/source/x11-helper.c index 9a98d507..f56122ab 100644 --- a/source/x11-helper.c +++ b/source/x11-helper.c @@ -39,6 +39,7 @@ #include "xcb-internal.h" #include "xcb.h" #include "settings.h" +#include "helper.h" #include #define OVERLAP( a, b, c, \ @@ -90,9 +91,17 @@ char* window_get_text_prop ( xcb_window_t w, xcb_atom_t atom ) xcb_get_property_reply_t *r = xcb_get_property_reply ( xcb->connection, c, NULL ); if ( r ) { if ( xcb_get_property_value_length ( r ) > 0 ) { - char *str = g_malloc ( xcb_get_property_value_length ( r ) + 1 ); - memcpy ( str, xcb_get_property_value ( r ), xcb_get_property_value_length ( r ) ); - str[xcb_get_property_value_length ( r )] = '\0'; + char *str = NULL; + if ( r->type == netatoms[UTF8_STRING] ) { + str = g_strndup ( xcb_get_property_value ( r ), xcb_get_property_value_length ( r ) ); + } + else if ( r->type == netatoms[STRING] ) { + str = rofi_latin_to_utf8_strdup ( xcb_get_property_value ( r ), xcb_get_property_value_length ( r ) ); + } + else { + str = g_strdup ( "Invalid encoding." ); + } + free ( r ); return str; }