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"
86 h = g_hash_table_new ( g_str_hash, g_str_equal );
92 va_start ( ap, length );
94 char * key = va_arg ( ap,
char * );
95 if ( key == (
char *) 0 ) {
98 char *value = va_arg ( ap,
char * );
99 if ( value == (
char *) 0 ) {
102 g_hash_table_insert ( h, key, value );
108 g_hash_table_destroy ( h );
110 if ( g_shell_parse_argv ( res, length, output, &error ) ) {
117 char *msg = g_strdup_printf (
"Failed to parse: '%s'\nError: '%s'",
string, error->message );
121 g_error_free ( error );
128 for (
size_t i = 0; tokens && tokens[i]; i++ ) {
129 g_regex_unref ( (GRegex *) tokens[i]->regex );
130 g_free ( tokens[i] );
137 gchar *r = g_regex_escape_string ( input, -1 );
138 size_t str_l = strlen ( r );
139 for (
size_t i = 0; i < str_l; i++ ) {
140 if ( r[i] ==
'\\' ) {
141 if ( r[i + 1] ==
'*' ) {
144 else if ( r[i + 1] ==
'?' ) {
154 GString *str = g_string_new (
"" );
155 gchar *r = g_regex_escape_string ( input, -1 );
158 for ( iter = r; iter && *iter !=
'\0'; iter = g_utf8_next_char ( iter ) ) {
160 g_string_append ( str,
"(" );
163 g_string_append ( str,
".*?(" );
165 if ( *iter ==
'\\' ) {
166 g_string_append_c ( str,
'\\' );
167 iter = g_utf8_next_char ( iter );
169 if ( ( *iter ) ==
'\0' ) {
173 g_string_append_unichar ( str, g_utf8_get_char ( iter ) );
174 g_string_append ( str,
")" );
178 char *retv = str->str;
179 g_string_free ( str, FALSE );
184 static inline GRegex *
R (
const char *s,
int case_sensitive )
186 return g_regex_new ( s, G_REGEX_OPTIMIZE | ( ( case_sensitive ) ? 0 : G_REGEX_CASELESS ), 0, NULL );
191 GRegex * retv = NULL;
202 retv =
R ( r, case_sensitive );
206 retv =
R ( input, case_sensitive );
207 if ( retv == NULL ) {
208 r = g_regex_escape_string ( input, -1 );
209 retv =
R ( r, case_sensitive );
215 retv =
R ( r, case_sensitive );
219 r = g_regex_escape_string ( input, -1 );
220 retv =
R ( r, case_sensitive );
229 if ( input == NULL ) {
232 size_t len = strlen ( input );
237 char *saveptr = NULL, *token;
249 char *str = g_strdup ( input );
253 const char *
const sep =
" ";
254 for ( token = strtok_r ( str, sep, &saveptr ); token != NULL; token = strtok_r ( NULL, sep, &saveptr ) ) {
255 retv = g_realloc ( retv,
sizeof (
rofi_int_matcher* ) * ( num_tokens + 2 ) );
256 retv[num_tokens] =
create_regex ( token, case_sensitive );
257 retv[num_tokens + 1] = NULL;
280 if ( val != NULL && i > 0 && i <
stored_argc - 1 ) {
289 const char **retv = NULL;
297 retv = g_malloc0 ( ( length + 1 ) *
sizeof (
char* ) );
312 if ( val != NULL && i > 0 && i < (
stored_argc - 1 ) ) {
322 if ( val != NULL && i > 0 && i < (
stored_argc - 1 ) ) {
331 const size_t len = strlen ( arg );
337 if ( len == 2 && arg[0] ==
'\\' ) {
341 case 'n':
return '\n';
343 case 'a':
return '\a';
345 case 'b':
return '\b';
347 case 't':
return '\t';
349 case 'v':
return '\v';
351 case 'f':
return '\f';
353 case 'r':
return '\r';
355 case '\\':
return '\\';
357 case '0':
return '\0';
362 if ( len > 2 && arg[0] ==
'\\' && arg[1] ==
'x' ) {
363 return (
char) strtol ( &arg[2], NULL, 16 );
365 g_warning (
"Failed to parse character string: \"%s\"", arg );
374 if ( val != NULL && i > 0 && i < (
stored_argc - 1 ) ) {
385 for (
int j = 0; tokens[j]; j++ ) {
386 GMatchInfo *gmi = NULL;
387 if ( tokens[j]->invert ) {
390 g_regex_match ( tokens[j]->regex, input, G_REGEX_MATCH_PARTIAL, &gmi );
391 while ( g_match_info_matches ( gmi ) ) {
392 int count = g_match_info_get_match_count ( gmi );
393 for (
int index = (
count > 1 ) ? 1 : 0; index <
count; index++ ) {
395 g_match_info_fetch_pos ( gmi, index, &start, &end );
397 PangoAttribute *pa = pango_attr_weight_new ( PANGO_WEIGHT_BOLD );
398 pa->start_index = start;
400 pango_attr_list_insert ( retv, pa );
403 PangoAttribute *pa = pango_attr_underline_new ( PANGO_UNDERLINE_SINGLE );
404 pa->start_index = start;
406 pango_attr_list_insert ( retv, pa );
409 PangoAttribute *pa = pango_attr_strikethrough_new ( TRUE );
410 pa->start_index = start;
412 pango_attr_list_insert ( retv, pa );
415 PangoAttribute *pa = pango_attr_variant_new ( PANGO_VARIANT_SMALL_CAPS );
416 pa->start_index = start;
418 pango_attr_list_insert ( retv, pa );
421 PangoAttribute *pa = pango_attr_style_new ( PANGO_STYLE_ITALIC );
422 pa->start_index = start;
424 pango_attr_list_insert ( retv, pa );
427 PangoAttribute *pa = pango_attr_foreground_new (
431 pa->start_index = start;
433 pango_attr_list_insert ( retv, pa );
436 pa = pango_attr_foreground_alpha_new ( th.
color.
alpha * 65535 );
437 pa->start_index = start;
439 pango_attr_list_insert ( retv, pa );
443 g_match_info_next ( gmi, NULL );
445 g_match_info_free ( gmi );
456 for (
int j = 0; match && tokens[j]; j++ ) {
457 match = g_regex_match ( tokens[j]->regex, input, 0, NULL );
458 match ^= tokens[j]->invert;
471 GError *error = NULL;
472 g_spawn_async_with_pipes ( NULL, args, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL, &fd, NULL, &error );
474 if ( error != NULL ) {
475 char *msg = g_strdup_printf (
"Failed to execute: '%s'\nError: '%s'", cmd, error->message );
479 g_error_free ( error );
492 int fd = g_open (
pidfile, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR );
494 g_warning (
"Failed to create pid file: '%s'.",
pidfile );
498 int flags = fcntl ( fd, F_GETFD, NULL );
500 if ( fcntl ( fd, F_SETFD,
flags, NULL ) < 0 ) {
501 g_warning (
"Failed to set CLOEXEC on pidfile." );
506 int retv = flock ( fd, LOCK_EX | LOCK_NB );
508 g_warning (
"Failed to set lock on pidfile: Rofi already running?" );
509 g_warning (
"Got error: %d %s", retv, g_strerror ( errno ) );
513 if ( ftruncate ( fd, (off_t) 0 ) == 0 ) {
516 int length = snprintf ( buffer, 64,
"%i", getpid () );
518 while ( l < length ) {
519 l += write ( fd, &buffer[l], length - l );
528 if ( close ( fd ) ) {
529 g_warning (
"Failed to close pidfile: '%s'", g_strerror ( errno ) );
536 const char *fam = pango_font_description_get_family ( pfd );
537 int size = pango_font_description_get_size ( pfd );
538 if ( fam == NULL || size == 0 ) {
539 g_debug (
"Pango failed to parse font: '%s'", font );
540 g_debug (
"Got family: <b>%s</b> at size: <b>%d</b>", fam ? fam :
"{unknown}", size );
554 int found_error = FALSE;
555 GString *msg = g_string_new (
556 "<big><b>The configuration failed to validate:</b></big>\n" );
569 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",
589 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",
596 g_string_append_printf ( msg,
"\t<b>config.element_height</b>=%d is invalid. An element needs to be atleast 1 line high.\n",
602 g_string_append_printf ( msg,
"\t<b>config.menu_columns</b>=%d is invalid. You need at least one visible column.\n",
608 g_string_append_printf ( msg,
"<b>config.menu_width</b>=0 is invalid. You cannot have a window with no width." );
613 g_string_append_printf ( msg,
"\t<b>config.location</b>=%d is invalid. Value should be between %d and %d.\n",
624 if ( name && name[0] ==
'-' ) {
625 int index = name[1] -
'0';
626 if ( index < 5 && index > 0 ) {
630 g_string_append_printf ( msg,
"\t<b>config.monitor</b>=%s Could not find monitor.\n", name );
636 PangoFontDescription *pfd = pango_font_description_from_string (
config.
menu_font );
637 const char *fam = pango_font_description_get_family ( pfd );
638 int size = pango_font_description_get_size ( pfd );
639 if ( fam == NULL || size == 0 ) {
640 g_string_append_printf ( msg,
"Pango failed to parse font: '%s'\n",
config.
menu_font );
641 g_string_append_printf ( msg,
"Got font family: <b>%s</b> at size <b>%d</b>\n", fam ? fam :
"{unknown}", size );
645 pango_font_description_free ( pfd );
655 g_string_append ( msg,
"Please update your configuration." );
660 g_string_free ( msg, TRUE );
666 char **str = g_strsplit ( input, G_DIR_SEPARATOR_S, -1 );
667 for (
unsigned int i = 0; str && str[i]; i++ ) {
669 if ( str[i][0] ==
'~' && str[i][1] ==
'\0' ) {
671 str[i] = g_strdup ( g_get_home_dir () );
674 else if ( str[i][0] ==
'~' ) {
675 struct passwd *p = getpwnam ( &( str[i][1] ) );
678 str[i] = g_strdup ( p->pw_dir );
683 if ( input[0] == G_DIR_SEPARATOR ) {
684 str[i] = g_strdup_printf (
"%s%s", G_DIR_SEPARATOR_S, s );
689 char *retv = g_build_filenamev ( str );
695 #define MIN3( a, b, c ) ( ( a ) < ( b ) ? ( ( a ) < ( c ) ? ( a ) : ( c ) ) : ( ( b ) < ( c ) ? ( b ) : ( c ) ) )
697 unsigned int levenshtein (
const char *needle,
const glong needlelen,
const char *haystack,
const glong haystacklen )
699 if ( needlelen == G_MAXLONG ) {
703 unsigned int column[needlelen + 1];
704 for ( glong y = 0; y < needlelen; y++ ) {
709 column[needlelen] = needlelen;
710 for ( glong x = 1; x <= haystacklen; x++ ) {
711 const char *needles = needle;
713 gunichar haystackc = g_utf8_get_char ( haystack );
715 haystackc = g_unichar_tolower ( haystackc );
717 for ( glong y = 1, lastdiag = x - 1; y <= needlelen; y++ ) {
718 gunichar needlec = g_utf8_get_char ( needles );
720 needlec = g_unichar_tolower ( needlec );
722 unsigned int olddiag = column[y];
723 column[y] =
MIN3 ( column[y] + 1, column[y - 1] + 1, lastdiag + ( needlec == haystackc ? 0 : 1 ) );
725 needles = g_utf8_next_char ( needles );
727 haystack = g_utf8_next_char ( haystack );
729 return column[needlelen];
735 return g_convert_with_fallback ( input, length,
"UTF-8",
"latin1",
"\uFFFD", NULL, &slength, NULL );
740 if ( text == NULL ) {
743 gchar *ret = g_markup_escape_text ( text, -1 );
750 if ( data == NULL ) {
756 if ( g_utf8_validate ( data, length, &end ) ) {
757 return g_memdup ( data, length + 1 );
759 string = g_string_sized_new ( length + 16 );
763 g_string_append_len (
string, data, end - data );
765 g_string_append (
string,
"\uFFFD" );
766 length -= ( end - data ) + 1;
768 }
while ( !g_utf8_validate ( data, length, &end ) );
771 g_string_append_len (
string, data, length );
774 return g_string_free (
string, FALSE );
782 #define FUZZY_SCORER_MAX_LENGTH 256
784 #define MIN_SCORE ( INT_MIN / 2 )
786 #define LEADING_GAP_SCORE -4
790 #define WORD_START_SCORE 50
792 #define NON_WORD_SCORE 40
794 #define CAMEL_SCORE ( WORD_START_SCORE + GAP_SCORE - 1 )
796 #define CONSECUTIVE_SCORE ( WORD_START_SCORE + GAP_SCORE )
798 #define PATTERN_NON_START_MULTIPLIER 1
800 #define PATTERN_START_MULTIPLIER 2
824 if ( g_unichar_islower ( c ) ) {
827 if ( g_unichar_isupper ( c ) ) {
830 if ( g_unichar_isdigit ( c ) ) {
893 gboolean pfirst = TRUE;
895 gboolean pstart = TRUE;
897 int *score = g_malloc_n ( slen,
sizeof (
int ) );
899 int *dp = g_malloc_n ( slen,
sizeof (
int ) );
902 int uleft = 0, ulefts = 0, left, lefts;
903 const gchar *pit = pattern, *sit;
905 for ( si = 0, sit = str; si < slen; si++, sit = g_utf8_next_char ( sit ) ) {
911 for ( pi = 0; pi < plen; pi++, pit = g_utf8_next_char ( pit ) ) {
912 gunichar pc = g_utf8_get_char ( pit ), sc;
913 if ( g_unichar_isspace ( pc ) ) {
918 for ( si = 0, sit = str; si < slen; si++, sit = g_utf8_next_char ( sit ) ) {
921 sc = g_utf8_get_char ( sit );
924 : g_unichar_tolower ( pc ) == g_unichar_tolower ( sc ) ) {
936 pfirst = pstart = FALSE;
939 for ( si = 0; si < slen; si++ ) {
940 lefts = MAX ( lefts +
GAP_SCORE, dp[si] );
960 char *na = g_utf8_normalize ( a, -1, G_NORMALIZE_ALL_COMPOSE );
961 char *nb = g_utf8_normalize ( b, -1, G_NORMALIZE_ALL_COMPOSE );
962 *g_utf8_offset_to_pointer ( na, n ) =
'\0';
963 *g_utf8_offset_to_pointer ( nb, n ) =
'\0';
964 int r = g_utf8_collate ( na, nb );
972 gboolean retv = TRUE;
973 GError *error = NULL;
975 GSpawnChildSetupFunc child_setup = NULL;
976 gpointer user_data = NULL;
980 g_spawn_async ( wd, args, NULL, G_SPAWN_SEARCH_PATH, child_setup, user_data, NULL, &error );
981 if ( error != NULL ) {
982 char *msg = g_strdup_printf (
"Failed to execute: '%s%s'\nError: '%s'", error_precmd, error_cmd, error->message );
986 g_error_free ( error );
1000 if ( run_in_term ) {
1007 if ( args == NULL ) {
1011 if ( context != NULL ) {
1012 if ( context->
name == NULL ) {
1013 context->
name = args[0];
1015 if ( context->
binary == NULL ) {
1016 context->
binary = args[0];
1019 gsize l = strlen (
"Launching '' via rofi" ) + strlen ( cmd ) + 1;
1020 gchar *description = g_newa ( gchar, l );
1022 g_snprintf ( description, l,
"Launching '%s' via rofi", cmd );
1025 if ( context->
command == NULL ) {
1036 g_debug (
"Opening theme, testing: %s\n", filename );
1037 if ( g_file_test ( filename, G_FILE_TEST_EXISTS ) ) {
1040 g_free ( filename );
1042 if ( g_str_has_suffix ( file,
".rasi" ) ) {
1043 filename = g_strdup ( file );
1046 filename = g_strconcat ( file,
".rasi", NULL );
1049 const char *cpath = g_get_user_config_dir ();
1051 char *themep = g_build_filename ( cpath,
"rofi",
"themes", filename, NULL );
1052 g_debug (
"Opening theme, testing: %s\n", themep );
1053 if ( themep && g_file_test ( themep, G_FILE_TEST_EXISTS ) ) {
1054 g_free ( filename );
1061 char *themep = g_build_filename ( cpath,
"rofi", filename, NULL );
1062 g_debug (
"Opening theme, testing: %s\n", themep );
1063 if ( g_file_test ( themep, G_FILE_TEST_EXISTS ) ) {
1064 g_free ( filename );
1069 const char * datadir = g_get_user_data_dir ();
1071 char *theme_path = g_build_filename ( datadir,
"rofi",
"themes", filename, NULL );
1072 g_debug (
"Opening theme, testing: %s\n", theme_path );
1074 if ( g_file_test ( theme_path, G_FILE_TEST_EXISTS ) ) {
1075 g_free ( filename );
1078 g_free ( theme_path );
1082 char *theme_path = g_build_filename ( THEME_DIR, filename, NULL );
1084 g_debug (
"Opening theme, testing: %s\n", theme_path );
1085 if ( g_file_test ( theme_path, G_FILE_TEST_EXISTS ) ) {
1086 g_free ( filename );
1089 g_free ( theme_path );
1096 GError *error = NULL;
1097 cairo_surface_t *surface = NULL;
1098 RsvgHandle * handle;
1100 handle = rsvg_handle_new_from_file ( file, &error );
1101 if ( G_LIKELY ( handle != NULL ) ) {
1102 RsvgDimensionData dimensions;
1104 rsvg_handle_set_dpi ( handle,
config.
dpi );
1106 rsvg_handle_get_dimensions ( handle, &dimensions );
1108 double scale = (double) height / dimensions.height;
1109 surface = cairo_image_surface_create ( CAIRO_FORMAT_ARGB32,
1110 (
double) dimensions.width * scale,
1111 (double) dimensions.height * scale );
1112 gboolean failed = cairo_surface_status ( surface ) != CAIRO_STATUS_SUCCESS;
1113 if ( G_LIKELY ( failed == FALSE ) ) {
1114 cairo_t *cr = cairo_create ( surface );
1115 cairo_scale ( cr, scale, scale );
1116 failed = rsvg_handle_render_cairo ( handle, cr ) == FALSE;
1117 cairo_destroy ( cr );
1120 rsvg_handle_close ( handle, &error );
1121 g_object_unref ( handle );
1124 if ( G_UNLIKELY ( failed ) ) {
1125 g_warning (
"Failed to render file: '%s'", file );
1126 cairo_surface_destroy ( surface );
1130 if ( G_UNLIKELY ( error != NULL ) ) {
1131 g_warning (
"Failed to render SVG file: '%s': %s", file, error->message );
1132 g_error_free ( error );
1141 while ( input != NULL && isblank ( *input ) ) {
1145 const char *sep[] = {
"-",
":" };
1146 int pythonic = ( strchr ( input,
':' ) || input[0] ==
'-' ) ? 1 : 0;
1149 for (
char *token = strsep ( &input, sep[pythonic] ); token != NULL; token = strsep ( &input, sep[pythonic] ) ) {
1151 item->
start = item->
stop = (int) strtol ( token, NULL, 10 );
1156 if ( token[0] ==
'\0' ) {
1161 item->
stop = (int) strtol ( token, NULL, 10 );
1170 if ( input == NULL ) {
1173 const char *
const sep =
",";
1174 for (
char *token = strtok_r ( input, sep, &endp ); token != NULL; token = strtok_r ( NULL, sep, &endp ) ) {
1176 *list = g_realloc ( ( *list ), ( ( *length ) + 1 ) *
sizeof (
struct rofi_range_pair ) );
1178 parse_pair ( token, &( ( *list )[*length] ) );
1203 for (
int i = 0; format && format[i]; i++ ) {
1204 if ( format[i] ==
'i' ) {
1205 fprintf ( stdout,
"%d", selected_line );
1207 else if ( format[i] ==
'd' ) {
1208 fprintf ( stdout,
"%d", ( selected_line + 1 ) );
1210 else if ( format[i] ==
's' ) {
1211 fputs (
string, stdout );
1213 else if ( format[i] ==
'p' ) {
1215 pango_parse_markup (
string, -1, 0, NULL, &esc, NULL, NULL );
1217 fputs ( esc, stdout );
1221 fputs (
"invalid string", stdout );
1224 else if ( format[i] ==
'q' ) {
1225 char *quote = g_shell_quote (
string );
1226 fputs ( quote, stdout );
1229 else if ( format[i] ==
'f' ) {
1231 fputs ( filter, stdout );
1234 else if ( format[i] ==
'F' ) {
1236 char *quote = g_shell_quote ( filter );
1237 fputs ( quote, stdout );
1242 fputc ( format[i], stdout );
1245 fputc (
'\n', stdout );
1253 int num_match = g_match_info_get_match_count ( info );
1255 if ( num_match == 5 ) {
1256 match = g_match_info_fetch ( info, 4 );
1257 if ( match != NULL ) {
1259 gchar *r = g_hash_table_lookup ( (GHashTable *) data, match );
1262 g_string_append ( res, r );
1269 else if ( num_match == 4 ) {
1270 match = g_match_info_fetch ( info, 2 );
1271 if ( match != NULL ) {
1273 gchar *r = g_hash_table_lookup ( (GHashTable *) data, match );
1276 gchar *prefix = g_match_info_fetch ( info, 1 );
1277 g_string_append ( res, prefix );
1280 g_string_append ( res, r );
1282 gchar *post = g_match_info_fetch ( info, 3 );
1283 g_string_append ( res, post );
1298 h = g_hash_table_new ( g_str_hash, g_str_equal );
1300 va_start ( ap,
string );
1303 char * key = va_arg ( ap,
char * );
1304 if ( key == (
char *) 0 ) {
1307 char *value = va_arg ( ap,
char * );
1308 g_hash_table_insert ( h, key, value );
1313 g_hash_table_destroy ( h );
1331 GError *error = NULL;
1335 GRegex *reg = g_regex_new (
"\\[(.*)({[-\\w]+})(.*)\\]|({[\\w-]+})", 0, 0, &error );
1336 if ( error == NULL ) {
1337 res = g_regex_replace_eval ( reg,
string, -1, 0, 0,
helper_eval_cb2, h, &error );
1340 g_regex_unref ( reg );
1342 if ( error != NULL ) {
1343 char *msg = g_strdup_printf (
"Failed to parse: '%s'\nError: '%s'",
string, error->message );
1347 g_error_free ( error );