30 #define G_LOG_DOMAIN "Helper"
41 #include <glib/gstdio.h>
42 #include <sys/types.h>
47 #include <pango/pango.h>
48 #include <pango/pango-fontmap.h>
49 #include <pango/pangocairo.h>
50 #include <librsvg/rsvg.h>
66 "on monitor with focused window",
67 "on monitor that has mouse pointer"
87 h = g_hash_table_new ( g_str_hash, g_str_equal );
93 va_start ( ap, length );
95 char * key = va_arg ( ap,
char * );
96 if ( key == (
char *) 0 ) {
99 char *value = va_arg ( ap,
char * );
100 if ( value == (
char *) 0 ) {
103 g_hash_table_insert ( h, key, value );
109 g_hash_table_destroy ( h );
111 if ( g_shell_parse_argv ( res, length, output, &error ) ) {
118 char *msg = g_strdup_printf (
"Failed to parse: '%s'\nError: '%s'",
string, error->message );
122 g_error_free ( error );
129 for (
size_t i = 0; tokens && tokens[i]; i++ ) {
130 g_regex_unref ( (GRegex *) tokens[i]->regex );
131 g_free ( tokens[i] );
138 gchar *r = g_regex_escape_string ( input, -1 );
139 size_t str_l = strlen ( r );
140 for (
size_t i = 0; i < str_l; i++ ) {
141 if ( r[i] ==
'\\' ) {
142 if ( r[i + 1] ==
'*' ) {
145 else if ( r[i + 1] ==
'?' ) {
155 GString *str = g_string_new (
"" );
156 gchar *r = g_regex_escape_string ( input, -1 );
159 for ( iter = r; iter && *iter !=
'\0'; iter = g_utf8_next_char ( iter ) ) {
161 g_string_append ( str,
"(" );
164 g_string_append ( str,
".*(" );
166 if ( *iter ==
'\\' ) {
167 g_string_append_c ( str,
'\\' );
168 iter = g_utf8_next_char ( iter );
170 if ( ( *iter ) ==
'\0' ) {
174 g_string_append_unichar ( str, g_utf8_get_char ( iter ) );
175 g_string_append ( str,
")" );
179 char *retv = str->str;
180 g_string_free ( str, FALSE );
185 static inline GRegex *
R (
const char *s,
int case_sensitive )
187 return g_regex_new ( s, G_REGEX_OPTIMIZE | ( ( case_sensitive ) ? 0 : G_REGEX_CASELESS ), 0, NULL );
192 GRegex * retv = NULL;
203 retv =
R ( r, case_sensitive );
207 retv =
R ( input, case_sensitive );
208 if ( retv == NULL ) {
209 r = g_regex_escape_string ( input, -1 );
210 retv =
R ( r, case_sensitive );
216 retv =
R ( r, case_sensitive );
220 r = g_regex_escape_string ( input, -1 );
221 retv =
R ( r, case_sensitive );
230 if ( input == NULL ) {
233 size_t len = strlen ( input );
238 char *saveptr = NULL, *token;
250 char *str = g_strdup ( input );
254 const char *
const sep =
" ";
255 for ( token = strtok_r ( str, sep, &saveptr ); token != NULL; token = strtok_r ( NULL, sep, &saveptr ) ) {
256 retv = g_realloc ( retv,
sizeof (
rofi_int_matcher* ) * ( num_tokens + 2 ) );
257 retv[num_tokens] =
create_regex ( token, case_sensitive );
258 retv[num_tokens + 1] = NULL;
281 if ( val != NULL && i > 0 && i <
stored_argc - 1 ) {
290 const char **retv = NULL;
298 retv = g_malloc0 ( ( length + 1 ) *
sizeof (
char* ) );
313 if ( val != NULL && i > 0 && i < (
stored_argc - 1 ) ) {
323 if ( val != NULL && i > 0 && i < (
stored_argc - 1 ) ) {
332 const size_t len = strlen ( arg );
338 if ( len == 2 && arg[0] ==
'\\' ) {
342 case 'n':
return '\n';
344 case 'a':
return '\a';
346 case 'b':
return '\b';
348 case 't':
return '\t';
350 case 'v':
return '\v';
352 case 'f':
return '\f';
354 case 'r':
return '\r';
356 case '\\':
return '\\';
358 case '0':
return '\0';
363 if ( len > 2 && arg[0] ==
'\\' && arg[1] ==
'x' ) {
364 return (
char) strtol ( &arg[2], NULL, 16 );
366 g_warning (
"Failed to parse character string: \"%s\"", arg );
375 if ( val != NULL && i > 0 && i < (
stored_argc - 1 ) ) {
386 for (
int j = 0; tokens[j]; j++ ) {
387 GMatchInfo *gmi = NULL;
388 if ( tokens[j]->invert ) {
391 g_regex_match ( tokens[j]->regex, input, G_REGEX_MATCH_PARTIAL, &gmi );
392 while ( g_match_info_matches ( gmi ) ) {
393 int count = g_match_info_get_match_count ( gmi );
394 for (
int index = (
count > 1 ) ? 1 : 0; index <
count; index++ ) {
396 g_match_info_fetch_pos ( gmi, index, &start, &end );
398 PangoAttribute *pa = pango_attr_weight_new ( PANGO_WEIGHT_BOLD );
399 pa->start_index = start;
401 pango_attr_list_insert ( retv, pa );
404 PangoAttribute *pa = pango_attr_underline_new ( PANGO_UNDERLINE_SINGLE );
405 pa->start_index = start;
407 pango_attr_list_insert ( retv, pa );
410 PangoAttribute *pa = pango_attr_strikethrough_new ( TRUE );
411 pa->start_index = start;
413 pango_attr_list_insert ( retv, pa );
416 PangoAttribute *pa = pango_attr_variant_new ( PANGO_VARIANT_SMALL_CAPS );
417 pa->start_index = start;
419 pango_attr_list_insert ( retv, pa );
422 PangoAttribute *pa = pango_attr_style_new ( PANGO_STYLE_ITALIC );
423 pa->start_index = start;
425 pango_attr_list_insert ( retv, pa );
428 PangoAttribute *pa = pango_attr_foreground_new (
432 pa->start_index = start;
434 pango_attr_list_insert ( retv, pa );
437 g_match_info_next ( gmi, NULL );
439 g_match_info_free ( gmi );
450 for (
int j = 0; match && tokens[j]; j++ ) {
451 match = g_regex_match ( tokens[j]->regex, input, 0, NULL );
452 match ^= tokens[j]->invert;
465 GError *error = NULL;
466 g_spawn_async_with_pipes ( NULL, args, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL, &fd, NULL, &error );
468 if ( error != NULL ) {
469 char *msg = g_strdup_printf (
"Failed to execute: '%s'\nError: '%s'", cmd, error->message );
473 g_error_free ( error );
486 int fd = g_open (
pidfile, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR );
488 g_warning (
"Failed to create pid file: '%s'.",
pidfile );
492 int flags = fcntl ( fd, F_GETFD, NULL );
494 if ( fcntl ( fd, F_SETFD,
flags, NULL ) < 0 ) {
495 g_warning (
"Failed to set CLOEXEC on pidfile." );
500 int retv = flock ( fd, LOCK_EX | LOCK_NB );
502 g_warning (
"Failed to set lock on pidfile: Rofi already running?" );
503 g_warning (
"Got error: %d %s", retv, g_strerror ( errno ) );
507 if ( ftruncate ( fd, (off_t) 0 ) == 0 ) {
510 int length = snprintf ( buffer, 64,
"%i", getpid () );
512 while ( l < length ) {
513 l += write ( fd, &buffer[l], length - l );
522 if ( close ( fd ) ) {
523 g_warning (
"Failed to close pidfile: '%s'", g_strerror ( errno ) );
530 const char *fam = pango_font_description_get_family ( pfd );
531 int size = pango_font_description_get_size ( pfd );
532 if ( fam == NULL || size == 0 ) {
533 g_debug (
"Pango failed to parse font: '%s'", font );
534 g_debug (
"Got family: <b>%s</b> at size: <b>%d</b>", fam ? fam :
"{unknown}", size );
548 int found_error = FALSE;
549 GString *msg = g_string_new (
550 "<big><b>The configuration failed to validate:</b></big>\n" );
563 g_string_append_printf ( msg,
"\t<b>config.sorting_method</b>=%s is not a valid sorting strategy.\nValid options are: normal or fzf.\n",
583 g_string_append_printf ( msg,
"\t<b>config.matching</b>=%s is not a valid matching strategy.\nValid options are: glob, regex, fuzzy or normal.\n",
590 g_string_append_printf ( msg,
"\t<b>config.element_height</b>=%d is invalid. An element needs to be atleast 1 line high.\n",
596 g_string_append_printf ( msg,
"\t<b>config.menu_columns</b>=%d is invalid. You need at least one visible column.\n",
602 g_string_append_printf ( msg,
"<b>config.menu_width</b>=0 is invalid. You cannot have a window with no width." );
607 g_string_append_printf ( msg,
"\t<b>config.location</b>=%d is invalid. Value should be between %d and %d.\n",
618 if ( name && name[0] ==
'-' ) {
619 int index = name[1] -
'0';
620 if ( index < 5 && index > 0 ) {
624 g_string_append_printf ( msg,
"\t<b>config.monitor</b>=%s Could not find monitor.\n", name );
630 PangoFontDescription *pfd = pango_font_description_from_string (
config.
menu_font );
631 const char *fam = pango_font_description_get_family ( pfd );
632 int size = pango_font_description_get_size ( pfd );
633 if ( fam == NULL || size == 0 ) {
634 g_string_append_printf ( msg,
"Pango failed to parse font: '%s'\n",
config.
menu_font );
635 g_string_append_printf ( msg,
"Got font family: <b>%s</b> at size <b>%d</b>\n", fam ? fam :
"{unknown}", size );
639 pango_font_description_free ( pfd );
649 g_string_append ( msg,
"Please update your configuration." );
654 g_string_free ( msg, TRUE );
660 char **str = g_strsplit ( input, G_DIR_SEPARATOR_S, -1 );
661 for (
unsigned int i = 0; str && str[i]; i++ ) {
663 if ( str[i][0] ==
'~' && str[i][1] ==
'\0' ) {
665 str[i] = g_strdup ( g_get_home_dir () );
668 else if ( str[i][0] ==
'~' ) {
669 struct passwd *p = getpwnam ( &( str[i][1] ) );
672 str[i] = g_strdup ( p->pw_dir );
677 if ( input[0] == G_DIR_SEPARATOR ) {
678 str[i] = g_strdup_printf (
"%s%s", G_DIR_SEPARATOR_S, s );
683 char *retv = g_build_filenamev ( str );
689 #define MIN3( a, b, c ) ( ( a ) < ( b ) ? ( ( a ) < ( c ) ? ( a ) : ( c ) ) : ( ( b ) < ( c ) ? ( b ) : ( c ) ) )
691 unsigned int levenshtein (
const char *needle,
const glong needlelen,
const char *haystack,
const glong haystacklen )
693 if ( needlelen == G_MAXLONG ) {
697 unsigned int column[needlelen + 1];
698 for ( glong y = 0; y < needlelen; y++ ) {
703 column[needlelen] = needlelen;
704 for ( glong x = 1; x <= haystacklen; x++ ) {
705 const char *needles = needle;
707 gunichar haystackc = g_utf8_get_char ( haystack );
709 haystackc = g_unichar_tolower ( haystackc );
711 for ( glong y = 1, lastdiag = x - 1; y <= needlelen; y++ ) {
712 gunichar needlec = g_utf8_get_char ( needles );
714 needlec = g_unichar_tolower ( needlec );
716 unsigned int olddiag = column[y];
717 column[y] =
MIN3 ( column[y] + 1, column[y - 1] + 1, lastdiag + ( needlec == haystackc ? 0 : 1 ) );
719 needles = g_utf8_next_char ( needles );
721 haystack = g_utf8_next_char ( haystack );
723 return column[needlelen];
729 return g_convert_with_fallback ( input, length,
"UTF-8",
"latin1",
"\uFFFD", NULL, &slength, NULL );
734 if ( text == NULL ) {
737 gchar *ret = g_markup_escape_text ( text, -1 );
744 if ( data == NULL ) {
750 if ( g_utf8_validate ( data, length, &end ) ) {
751 return g_memdup ( data, length + 1 );
753 string = g_string_sized_new ( length + 16 );
757 g_string_append_len (
string, data, end - data );
759 g_string_append (
string,
"\uFFFD" );
760 length -= ( end - data ) + 1;
762 }
while ( !g_utf8_validate ( data, length, &end ) );
765 g_string_append_len (
string, data, length );
768 return g_string_free (
string, FALSE );
776 #define FUZZY_SCORER_MAX_LENGTH 256
778 #define MIN_SCORE ( INT_MIN / 2 )
780 #define LEADING_GAP_SCORE -4
784 #define WORD_START_SCORE 50
786 #define NON_WORD_SCORE 40
788 #define CAMEL_SCORE ( WORD_START_SCORE + GAP_SCORE - 1 )
790 #define CONSECUTIVE_SCORE ( WORD_START_SCORE + GAP_SCORE )
792 #define PATTERN_NON_START_MULTIPLIER 1
794 #define PATTERN_START_MULTIPLIER 2
818 if ( g_unichar_islower ( c ) ) {
821 if ( g_unichar_isupper ( c ) ) {
824 if ( g_unichar_isdigit ( c ) ) {
887 gboolean pfirst = TRUE;
889 gboolean pstart = TRUE;
891 int *score = g_malloc_n ( slen,
sizeof (
int ) );
893 int *dp = g_malloc_n ( slen,
sizeof (
int ) );
896 int uleft = 0, ulefts = 0, left, lefts;
897 const gchar *pit = pattern, *sit;
899 for ( si = 0, sit = str; si < slen; si++, sit = g_utf8_next_char ( sit ) ) {
905 for ( pi = 0; pi < plen; pi++, pit = g_utf8_next_char ( pit ) ) {
906 gunichar pc = g_utf8_get_char ( pit ), sc;
907 if ( g_unichar_isspace ( pc ) ) {
912 for ( si = 0, sit = str; si < slen; si++, sit = g_utf8_next_char ( sit ) ) {
915 sc = g_utf8_get_char ( sit );
918 : g_unichar_tolower ( pc ) == g_unichar_tolower ( sc ) ) {
930 pfirst = pstart = FALSE;
933 for ( si = 0; si < slen; si++ ) {
934 lefts = MAX ( lefts +
GAP_SCORE, dp[si] );
954 char *na = g_utf8_normalize ( a, -1, G_NORMALIZE_ALL_COMPOSE );
955 char *nb = g_utf8_normalize ( b, -1, G_NORMALIZE_ALL_COMPOSE );
956 *g_utf8_offset_to_pointer ( na, n ) =
'\0';
957 *g_utf8_offset_to_pointer ( nb, n ) =
'\0';
958 int r = g_utf8_collate ( na, nb );
966 gboolean retv = TRUE;
967 GError *error = NULL;
969 GSpawnChildSetupFunc child_setup = NULL;
970 gpointer user_data = NULL;
974 g_spawn_async ( wd, args, NULL, G_SPAWN_SEARCH_PATH, child_setup, user_data, NULL, &error );
975 if ( error != NULL ) {
976 char *msg = g_strdup_printf (
"Failed to execute: '%s%s'\nError: '%s'", error_precmd, error_cmd, error->message );
980 g_error_free ( error );
1001 if ( context != NULL ) {
1002 if ( context->
name == NULL ) {
1003 context->
name = args[0];
1005 if ( context->
binary == NULL ) {
1006 context->
binary = args[0];
1009 gsize l = strlen (
"Launching '' via rofi" ) + strlen ( cmd ) + 1;
1010 gchar *description = g_newa ( gchar, l );
1012 g_snprintf ( description, l,
"Launching '%s' via rofi", cmd );
1015 if ( context->
command == NULL ) {
1026 g_debug (
"Opening theme, testing: %s\n", filename );
1027 if ( g_file_test ( filename, G_FILE_TEST_EXISTS ) ) {
1030 g_free ( filename );
1032 if ( g_str_has_suffix ( file,
".rasi" ) ) {
1033 filename = g_strdup ( file );
1036 filename = g_strconcat ( file,
".rasi", NULL );
1039 const char *cpath = g_get_user_config_dir ();
1041 char *themep = g_build_filename ( cpath,
"rofi", filename, NULL );
1042 g_debug (
"Opening theme, testing: %s\n", themep );
1043 if ( g_file_test ( themep, G_FILE_TEST_EXISTS ) ) {
1044 g_free ( filename );
1049 const char * datadir = g_get_user_data_dir ();
1051 char *theme_path = g_build_filename ( datadir,
"rofi",
"themes", filename, NULL );
1052 g_debug (
"Opening theme, testing: %s\n", theme_path );
1054 if ( g_file_test ( theme_path, G_FILE_TEST_EXISTS ) ) {
1055 g_free ( filename );
1058 g_free ( theme_path );
1062 char *theme_path = g_build_filename ( THEME_DIR, filename, NULL );
1064 g_debug (
"Opening theme, testing: %s\n", theme_path );
1065 if ( g_file_test ( theme_path, G_FILE_TEST_EXISTS ) ) {
1066 g_free ( filename );
1069 g_free ( theme_path );
1076 GError *error = NULL;
1077 cairo_surface_t *surface = NULL;
1078 RsvgHandle * handle;
1080 handle = rsvg_handle_new_from_file ( file, &error );
1081 if ( G_LIKELY ( handle != NULL ) ) {
1082 RsvgDimensionData dimensions;
1084 rsvg_handle_set_dpi ( handle,
config.
dpi );
1086 rsvg_handle_get_dimensions ( handle, &dimensions );
1088 double scale = (double) height / dimensions.height;
1089 surface = cairo_image_surface_create ( CAIRO_FORMAT_ARGB32,
1090 (
double) dimensions.width * scale,
1091 (double) dimensions.height * scale );
1092 gboolean failed = cairo_surface_status ( surface ) != CAIRO_STATUS_SUCCESS;
1093 if ( G_LIKELY ( failed == FALSE ) ) {
1094 cairo_t *cr = cairo_create ( surface );
1095 cairo_scale ( cr, scale, scale );
1096 failed = rsvg_handle_render_cairo ( handle, cr ) == FALSE;
1097 cairo_destroy ( cr );
1100 rsvg_handle_close ( handle, &error );
1101 g_object_unref ( handle );
1104 if ( G_UNLIKELY ( failed ) ) {
1105 g_warning (
"Failed to render file: '%s'", file );
1106 cairo_surface_destroy ( surface );
1110 if ( G_UNLIKELY ( error != NULL ) ) {
1111 g_warning (
"Failed to render SVG file: '%s': %s", file, error->message );
1112 g_error_free ( error );
1121 const char *
const sep =
"-";
1122 for (
char *token = strsep ( &input, sep ); token != NULL; token = strsep ( &input, sep ) ) {
1124 item->
start = item->
stop = (
unsigned int) strtoul ( token, NULL, 10 );
1128 if ( token[0] ==
'\0' ) {
1129 item->
stop = 0xFFFFFFFF;
1132 item->
stop = (
unsigned int) strtoul ( token, NULL, 10 );
1140 if ( input == NULL ) {
1143 const char *
const sep =
",";
1144 for (
char *token = strtok_r ( input, sep, &endp ); token != NULL; token = strtok_r ( NULL, sep, &endp ) ) {
1146 *list = g_realloc ( ( *list ), ( ( *length ) + 1 ) *
sizeof (
struct rofi_range_pair ) );
1148 parse_pair ( token, &( ( *list )[*length] ) );
1173 for (
int i = 0; format && format[i]; i++ ) {
1174 if ( format[i] ==
'i' ) {
1175 fprintf ( stdout,
"%d", selected_line );
1177 else if ( format[i] ==
'd' ) {
1178 fprintf ( stdout,
"%d", ( selected_line + 1 ) );
1180 else if ( format[i] ==
's' ) {
1181 fputs (
string, stdout );
1183 else if ( format[i] ==
'p' ) {
1185 pango_parse_markup(
string, -1, 0, NULL, &esc, NULL, NULL);
1187 fputs ( esc, stdout );
1190 fputs (
"invalid string" , stdout );
1193 else if ( format[i] ==
'q' ) {
1194 char *quote = g_shell_quote (
string );
1195 fputs ( quote, stdout );
1198 else if ( format[i] ==
'f' ) {
1200 fputs ( filter, stdout );
1203 else if ( format[i] ==
'F' ) {
1205 char *quote = g_shell_quote ( filter );
1206 fputs ( quote, stdout );
1211 fputc ( format[i], stdout );
1214 fputc (
'\n', stdout );
1223 int num_match = g_match_info_get_match_count(info);
1225 if ( num_match == 5 ) {
1226 match = g_match_info_fetch ( info, 4);
1227 if ( match != NULL ) {
1229 gchar *r = g_hash_table_lookup ( (GHashTable *) data, match );
1232 g_string_append ( res, r );
1239 else if ( num_match == 4 ) {
1240 match = g_match_info_fetch ( info, 2);
1241 if ( match != NULL ) {
1243 gchar *r = g_hash_table_lookup ( (GHashTable *) data, match );
1246 gchar *prefix = g_match_info_fetch (info, 1);
1247 g_string_append ( res, prefix );
1250 g_string_append ( res, r );
1252 gchar *post = g_match_info_fetch (info, 3);
1253 g_string_append ( res, post );
1268 h = g_hash_table_new ( g_str_hash, g_str_equal );
1270 va_start ( ap,
string );
1273 char * key = va_arg ( ap,
char * );
1274 if ( key == (
char *) 0 ) {
1277 char *value = va_arg ( ap,
char * );
1278 g_hash_table_insert ( h, key, value );
1283 g_hash_table_destroy ( h );
1301 GError *error = NULL;
1305 GRegex *reg = g_regex_new (
"\\[(.*)({[-\\w]+})(.*)\\]|({[\\w-]+})", 0, 0, &error );
1306 if ( error == NULL ){
1307 res = g_regex_replace_eval ( reg,
string, -1, 0, 0,
helper_eval_cb2, h, &error );
1310 g_regex_unref ( reg );
1312 if ( error != NULL ) {
1313 char *msg = g_strdup_printf (
"Failed to parse: '%s'\nError: '%s'",
string, error->message );
1317 g_error_free ( error );