rofi 2.0.0
run.c
Go to the documentation of this file.
1/*
2 * rofi
3 *
4 * MIT/X11 License
5 * Copyright © 2013-2023 Qball Cow <qball@gmpclient.org>
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining
8 * a copy of this software and associated documentation files (the
9 * "Software"), to deal in the Software without restriction, including
10 * without limitation the rights to use, copy, modify, merge, publish,
11 * distribute, sublicense, and/or sell copies of the Software, and to
12 * permit persons to whom the Software is furnished to do so, subject to
13 * the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be
16 * included in all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 */
27
32
34#define G_LOG_DOMAIN "Modes.Run"
35
36#include "config.h"
37#include <stdio.h>
38#include <stdlib.h>
39
40#include <dirent.h>
41#include <errno.h>
42#include <limits.h>
43#include <signal.h>
44#include <string.h>
45#include <strings.h>
46#include <sys/types.h>
47#include <unistd.h>
48
49#include "display.h"
50#include "helper.h"
51#include "history.h"
52#include "modes/filebrowser.h"
53#include "modes/run.h"
54#include "rofi.h"
55#include "settings.h"
56
57#include "mode-private.h"
58
59#include "rofi-icon-fetcher.h"
60#include "timings.h"
64#define RUN_CACHE_FILE "rofi-4.runcache"
65
66typedef struct {
67 char *entry;
68 char *exec;
72 gboolean from_history;
73 /* Surface holding the icon. */
74 cairo_surface_t *icon;
75} RunEntry;
76
94
104static gboolean exec_cmd(const char *cmd, int run_in_term, const char *orig) {
105 GError *error = NULL;
106 if (!cmd || !cmd[0]) {
107 return FALSE;
108 }
109 gsize lf_cmd_size = 0;
110 gchar *lf_cmd = g_locale_from_utf8(cmd, -1, NULL, &lf_cmd_size, &error);
111 if (error != NULL) {
112 g_warning("Failed to convert command to locale encoding: %s",
113 error->message);
114 g_error_free(error);
115 return FALSE;
116 }
117
118 char *path = g_build_filename(cache_dir, RUN_CACHE_FILE, NULL);
119 RofiHelperExecuteContext context = {.name = NULL};
120 char *hist = g_strdup_printf("%s\x1f%s", orig, cmd);
121 // FIXME: assume startup notification support for terminals
122 if (helper_execute_command(NULL, lf_cmd, run_in_term,
123 run_in_term ? &context : NULL)) {
128
129 history_set(path, hist);
130 g_free(path);
131 g_free(lf_cmd);
132 g_free(hist);
133 return TRUE;
134 }
135 history_remove(path, hist);
136 g_free(hist);
137 g_free(path);
138 g_free(lf_cmd);
139 return FALSE;
140}
141
147static void delete_entry(const RunEntry *cmd) {
148 char *path = g_build_filename(cache_dir, RUN_CACHE_FILE, NULL);
149
150 char *hist = g_strdup_printf("%s\x1f%s", cmd->entry, cmd->exec);
151 history_remove(path, hist);
152 g_free(hist);
153 g_free(path);
154}
155
166static int sort_func(const void *a, const void *b, G_GNUC_UNUSED void *data) {
167 const RunEntry *astr = (const RunEntry *)a;
168 const RunEntry *bstr = (const RunEntry *)b;
169
170 if (astr->entry == NULL && bstr->entry == NULL) {
171 return 0;
172 }
173 if (astr->entry == NULL) {
174 return 1;
175 }
176 if (bstr->entry == NULL) {
177 return -1;
178 }
179 return g_strcmp0(astr->entry, bstr->entry);
180}
181
185static RunEntry *get_apps_external(RunEntry *retv, unsigned int *length,
186 unsigned int num_favorites) {
187 int fd = execute_generator(config.run_list_command);
188 if (fd >= 0) {
189 FILE *inp = fdopen(fd, "r");
190 if (inp) {
191 char *buffer = NULL;
192 size_t buffer_length = 0;
193
194 while (getline(&buffer, &buffer_length, inp) > 0) {
195 int found = 0;
196 // Filter out line-end.
197 if (buffer[strlen(buffer) - 1] == '\n') {
198 buffer[strlen(buffer) - 1] = '\0';
199 }
200
201 // This is a nice little penalty, but doable? time will tell.
202 // given num_favorites is max 25.
203 for (unsigned int j = 0; found == 0 && j < num_favorites; j++) {
204 if (strcasecmp(buffer, retv[j].entry) == 0) {
205 found = 1;
206 }
207 }
208
209 if (found == 1) {
210 continue;
211 }
212
213 // No duplicate, add it.
214 retv = g_realloc(retv, ((*length) + 2) * sizeof(RunEntry));
215 retv[(*length)].entry = g_strdup(buffer);
216 retv[(*length)].exec = g_shell_quote(buffer);
217 retv[(*length)].from_history = FALSE;
218 retv[(*length)].icon = NULL;
219 retv[(*length)].icon_fetch_uid = 0;
220 retv[(*length)].icon_fetch_size = 0;
221
222 (*length)++;
223 }
224 if (buffer != NULL) {
225 free(buffer);
226 }
227 if (fclose(inp) != 0) {
228 g_warning("Failed to close stdout off executor script: '%s'",
229 g_strerror(errno));
230 }
231 }
232 }
233 retv[(*length)].entry = NULL;
234 retv[(*length)].exec = NULL;
235 retv[(*length)].from_history = FALSE;
236 retv[(*length)].icon = NULL;
237 retv[(*length)].icon_fetch_uid = 0;
238 retv[(*length)].icon_fetch_size = 0;
239 return retv;
240}
241
245static RunEntry *get_apps(unsigned int *length) {
246 GError *error = NULL;
247 RunEntry *retv = NULL;
248 unsigned int num_favorites = 0;
249 char *path;
250
251 if (g_getenv("PATH") == NULL) {
252 return NULL;
253 }
254 TICK_N("start");
255 path = g_build_filename(cache_dir, RUN_CACHE_FILE, NULL);
256 char **hretv = history_get_list(path, length);
257 retv = (RunEntry *)g_malloc0((*length + 1) * sizeof(RunEntry));
258 for (unsigned int i = 0; i < *length; i++) {
259 gchar **rs = g_strsplit(hretv[i], "\x1f", 2);
260 retv[i].entry = rs[0];
261 retv[i].exec = rs[1];
262 if (retv[i].exec == NULL) {
263 retv[i].exec = g_strdup(rs[0]);
264 }
265 retv[i].from_history = TRUE;
266 g_free(rs);
267 }
268 g_free(hretv);
269 g_free(path);
270 // Keep track of how many where loaded as favorite.
271 num_favorites = (*length);
272
273 path = g_strdup(g_getenv("PATH"));
274
275 gsize l = 0;
276 gchar *homedir = g_locale_to_utf8(g_get_home_dir(), -1, NULL, &l, &error);
277 if (error != NULL) {
278 g_debug("Failed to convert homedir to UTF-8: %s", error->message);
279 for (unsigned int i = 0; retv[i].entry != NULL; i++) {
280 g_free(retv[i].entry);
281 g_free(retv[i].exec);
282 }
283 g_free(retv);
284 g_clear_error(&error);
285 g_free(homedir);
286 return NULL;
287 }
288
289 const char *const sep = ":";
290 char *strtok_savepointer = NULL;
291 for (const char *dirname = strtok_r(path, sep, &strtok_savepointer);
292 dirname != NULL; dirname = strtok_r(NULL, sep, &strtok_savepointer)) {
293 char *fpath = rofi_expand_path(dirname);
294 DIR *dir = opendir(fpath);
295 g_debug("Checking path %s for executable.", fpath);
296 g_free(fpath);
297
298 if (dir != NULL) {
299 struct dirent *dent;
300 gsize dirn_len = 0;
301 gchar *dirn = g_locale_to_utf8(dirname, -1, NULL, &dirn_len, &error);
302 if (error != NULL) {
303 g_debug("Failed to convert directory name to UTF-8: %s",
304 error->message);
305 g_clear_error(&error);
306 closedir(dir);
307 continue;
308 }
309 gboolean is_homedir = g_str_has_prefix(dirn, homedir);
310 g_free(dirn);
311
312 while ((dent = readdir(dir)) != NULL) {
313 if (dent->d_type != DT_REG && dent->d_type != DT_LNK &&
314 dent->d_type != DT_UNKNOWN) {
315 continue;
316 }
317 // Skip dot files.
318 if (dent->d_name[0] == '.') {
319 continue;
320 }
321 if (is_homedir) {
322 gchar *full_path = g_build_filename(dirname, dent->d_name, NULL);
323 gboolean b = g_file_test(full_path, G_FILE_TEST_IS_EXECUTABLE);
324 g_free(full_path);
325 if (!b) {
326 continue;
327 }
328 }
329
330 gsize name_len;
331 gchar *name =
332 g_filename_to_utf8(dent->d_name, -1, NULL, &name_len, &error);
333 if (error != NULL) {
334 g_debug("Failed to convert filename to UTF-8: %s", error->message);
335 g_clear_error(&error);
336 g_free(name);
337 continue;
338 }
339 // This is a nice little penalty, but doable? time will tell.
340 // given num_favorites is max 25.
341 int found = 0;
342 for (unsigned int j = 0; found == 0 && j < num_favorites; j++) {
343 if (g_strcmp0(name, retv[j].entry) == 0) {
344 found = 1;
345 }
346 }
347
348 if (found == 1) {
349 g_free(name);
350 continue;
351 }
352
353 retv = g_realloc(retv, ((*length) + 2) * sizeof(RunEntry));
354 retv[(*length)].entry = name;
355 retv[(*length)].exec = g_shell_quote(name);
356 retv[(*length)].from_history = FALSE;
357 retv[(*length)].icon = NULL;
358 retv[(*length)].icon_fetch_uid = 0;
359 retv[(*length)].icon_fetch_size = 0;
360 retv[(*length) + 1].entry = NULL;
361 retv[(*length) + 1].exec = NULL;
362 retv[(*length) + 1].from_history = FALSE;
363 retv[(*length) + 1].icon = NULL;
364 retv[(*length) + 1].icon_fetch_uid = 0;
365 retv[(*length) + 1].icon_fetch_size = 0;
366 (*length)++;
367 }
368
369 closedir(dir);
370 }
371 }
372 g_free(homedir);
373
374 // Get external apps.
375 if (config.run_list_command != NULL && config.run_list_command[0] != '\0') {
376 retv = get_apps_external(retv, length, num_favorites);
377 }
378 // No sorting needed.
379 if ((*length) == 0) {
380 return retv;
381 }
382 // TODO: check this is still fast enough. (takes 1ms on laptop.)
383 if ((*length) > num_favorites) {
384 g_qsort_with_data(&(retv[num_favorites]), (*length) - num_favorites,
385 sizeof(RunEntry), sort_func, NULL);
386 }
387 g_free(path);
388
389 unsigned int removed = 0;
390 for (unsigned int index = num_favorites; index < ((*length) - 1); index++) {
391 if (g_strcmp0(retv[index].entry, retv[index + 1].entry) == 0) {
392 g_free(retv[index].entry);
393 retv[index].entry = NULL;
394 g_free(retv[index].exec);
395 retv[index].exec = NULL;
396 removed++;
397 }
398 }
399
400 if ((*length) > num_favorites) {
401 g_qsort_with_data(&(retv[num_favorites]), (*length) - num_favorites,
402 sizeof(RunEntry), sort_func, NULL);
403 }
404 // Reduce array length;
405 (*length) -= removed;
406
407 TICK_N("stop");
408 return retv;
409}
410
411static int run_mode_init(Mode *sw) {
412 if (sw->private_data == NULL) {
413 RunModePrivateData *pd = g_malloc0(sizeof(*pd));
414 sw->private_data = (void *)pd;
415 pd->cmd_list = get_apps(&(pd->cmd_list_length));
416 pd->completer = NULL;
417 }
418
419 return TRUE;
420}
421static void run_mode_destroy(Mode *sw) {
423 if (rmpd != NULL) {
424 for (unsigned int i = 0; i < rmpd->cmd_list_length; i++) {
425 g_free(rmpd->cmd_list[i].entry);
426 g_free(rmpd->cmd_list[i].exec);
427 if (rmpd->cmd_list[i].icon != NULL) {
428 cairo_surface_destroy(rmpd->cmd_list[i].icon);
429 }
430 }
431 g_free(rmpd->cmd_list);
432 g_free(rmpd->old_input);
433 g_free(rmpd->old_completer_input);
434 if (rmpd->completer != NULL) {
435 mode_destroy(rmpd->completer);
436 g_free(rmpd->completer);
437 }
438 g_free(rmpd);
439 sw->private_data = NULL;
440 }
441}
442
443static unsigned int run_mode_get_num_entries(const Mode *sw) {
444 const RunModePrivateData *rmpd = (const RunModePrivateData *)sw->private_data;
445 if (rmpd->file_complete) {
446 return rmpd->completer->_get_num_entries(rmpd->completer);
447 }
448 return rmpd->cmd_list_length;
449}
450
451static ModeMode run_mode_result(Mode *sw, int mretv, char **input,
452 unsigned int selected_line) {
454 ModeMode retv = MODE_EXIT;
455
456 gboolean run_in_term = ((mretv & MENU_CUSTOM_ACTION) == MENU_CUSTOM_ACTION);
457 if (rmpd->file_complete == TRUE) {
458
459 retv = RELOAD_DIALOG;
460
461 if ((mretv & (MENU_COMPLETE))) {
462 g_free(rmpd->old_completer_input);
463 rmpd->old_completer_input = *input;
464 *input = NULL;
465 if (rmpd->selected_line < rmpd->cmd_list_length) {
466 (*input) = g_strdup(rmpd->old_input);
467 }
468 rmpd->file_complete = FALSE;
469 } else if ((mretv & MENU_CANCEL)) {
470 retv = MODE_EXIT;
471 } else {
472 char *path = NULL;
473 retv = mode_completer_result(rmpd->completer, mretv, input, selected_line,
474 &path);
475 if (retv == MODE_EXIT) {
476 if (path == NULL) {
477 char *arg = rmpd->cmd_list[rmpd->selected_line].exec;
478 exec_cmd(arg, run_in_term, rmpd->cmd_list[rmpd->selected_line].entry);
479 } else {
480 char *earg = rmpd->cmd_list[rmpd->selected_line].exec;
481 char *epath = g_shell_quote(path);
482 char *arg = g_strdup_printf("%s %s", earg, epath);
483 exec_cmd(arg, run_in_term, arg);
484 g_free(arg);
485 g_free(epath);
486 }
487 }
488 g_free(path);
489 }
490 return retv;
491 }
492
493 if ((mretv & MENU_OK) && rmpd->cmd_list[selected_line].entry != NULL) {
494 char *earg = NULL;
495 earg = rmpd->cmd_list[selected_line].exec;
496 if (!exec_cmd(earg, run_in_term, rmpd->cmd_list[selected_line].entry)) {
497 retv = RELOAD_DIALOG;
498 }
499 } else if ((mretv & MENU_CUSTOM_INPUT) && *input != NULL &&
500 *input[0] != '\0') {
501 if (!exec_cmd(*input, run_in_term, *input)) {
502 retv = RELOAD_DIALOG;
503 }
504 } else if ((mretv & MENU_ENTRY_DELETE) &&
505 rmpd->cmd_list[selected_line].entry) {
506 delete_entry(&(rmpd->cmd_list[selected_line]));
507
508 // Clear the list.
509 retv = RELOAD_DIALOG;
511 run_mode_init(sw);
512 } else if (mretv & MENU_CUSTOM_COMMAND) {
513 retv = (mretv & MENU_LOWER_MASK);
514 } else if ((mretv & MENU_COMPLETE)) {
515 retv = RELOAD_DIALOG;
516 if (selected_line < rmpd->cmd_list_length) {
517 rmpd->selected_line = selected_line;
518
519 g_free(rmpd->old_input);
520 rmpd->old_input = g_strdup(*input);
521
522 if (*input)
523 g_free(*input);
524 *input = g_strdup(rmpd->old_completer_input);
525
526 const Mode *comp = rofi_get_completer();
527 if (comp) {
528 rmpd->completer = mode_create(comp);
529 mode_init(rmpd->completer);
530 rmpd->file_complete = TRUE;
531 }
532 }
533 }
534 return retv;
535}
536
537static char *_get_display_value(const Mode *sw, unsigned int selected_line,
538 G_GNUC_UNUSED int *state,
539 G_GNUC_UNUSED GList **list, int get_entry) {
540 const RunModePrivateData *rmpd = (const RunModePrivateData *)sw->private_data;
541 if (rmpd->file_complete) {
542 return rmpd->completer->_get_display_value(rmpd->completer, selected_line,
543 state, list, get_entry);
544 }
545 return get_entry ? g_strdup(rmpd->cmd_list[selected_line].entry) : NULL;
546}
547
548static int run_token_match(const Mode *sw, rofi_int_matcher **tokens,
549 unsigned int index) {
550 const RunModePrivateData *rmpd = (const RunModePrivateData *)sw->private_data;
551 if (rmpd->file_complete) {
552 return rmpd->completer->_token_match(rmpd->completer, tokens, index);
553 }
554 return helper_token_match(tokens, rmpd->cmd_list[index].entry);
555}
556static char *run_get_message(const Mode *sw) {
558 if (pd->file_complete) {
559 if (pd->selected_line < pd->cmd_list_length) {
560 char *msg = mode_get_message(pd->completer);
561 if (msg) {
562 char *retv =
563 g_strdup_printf("File complete for: %s\n%s",
564 pd->cmd_list[pd->selected_line].entry, msg);
565 g_free(msg);
566 return retv;
567 }
568 return g_strdup_printf("File complete for: %s",
569 pd->cmd_list[pd->selected_line].entry);
570 }
571 }
572 return NULL;
573}
574static cairo_surface_t *_get_icon(const Mode *sw, unsigned int selected_line,
575 unsigned int height) {
577 const guint scale = display_scale();
578 if (pd->file_complete) {
579 return pd->completer->_get_icon(pd->completer, selected_line, height);
580 }
581 g_return_val_if_fail(pd->cmd_list != NULL, NULL);
582 RunEntry *dr = &(pd->cmd_list[selected_line]);
583
584 if (dr->icon_fetch_uid > 0 && dr->icon_fetch_size == height &&
585 dr->icon_fetch_scale == scale) {
586 cairo_surface_t *icon = rofi_icon_fetcher_get(dr->icon_fetch_uid);
587 return icon;
588 }
590 char **str = g_strsplit(dr->entry, " ", 2);
591 if (str) {
592 dr->icon_fetch_uid = rofi_icon_fetcher_query(str[0], height);
593 dr->icon_fetch_size = height;
594 dr->icon_fetch_scale = scale;
595 g_strfreev(str);
596 cairo_surface_t *icon = rofi_icon_fetcher_get(dr->icon_fetch_uid);
597 return icon;
598 }
599 return NULL;
600}
601
602#include "mode-private.h"
603Mode run_mode = {.name = "run",
604 .cfg_name_key = "display-run",
605 ._init = run_mode_init,
606 ._get_num_entries = run_mode_get_num_entries,
607 ._result = run_mode_result,
608 ._destroy = run_mode_destroy,
609 ._token_match = run_token_match,
610 ._get_message = run_get_message,
611 ._get_display_value = _get_display_value,
612 ._get_icon = _get_icon,
613 ._get_completion = NULL,
614 ._preprocess_input = NULL,
615 .private_data = NULL,
616 .free = NULL,
617 .type = MODE_TYPE_SWITCHER};
618
guint display_scale(void)
Definition display.c:42
static cairo_surface_t * _get_icon(const Mode *sw, unsigned int selected_line, unsigned int height)
static char * _get_display_value(const Mode *sw, unsigned int selected_line, G_GNUC_UNUSED int *state, G_GNUC_UNUSED GList **attr_list, int get_entry)
gboolean helper_execute_command(const char *wd, const char *cmd, gboolean run_in_term, RofiHelperExecuteContext *context)
Definition helper.c:1072
int execute_generator(const char *cmd)
Definition helper.c:562
char * rofi_expand_path(const char *input)
Definition helper.c:782
int helper_token_match(rofi_int_matcher *const *tokens, const char *input)
Definition helper.c:541
void history_set(const char *filename, const char *entry)
Definition history.c:179
void history_remove(const char *filename, const char *entry)
Definition history.c:260
char ** history_get_list(const char *filename, unsigned int *length)
Definition history.c:324
cairo_surface_t * rofi_icon_fetcher_get(const uint32_t uid)
uint32_t rofi_icon_fetcher_query(const char *name, const int size)
void mode_destroy(Mode *mode)
Definition mode.c:64
int mode_init(Mode *mode)
Definition mode.c:44
struct rofi_mode Mode
Definition mode.h:49
Mode * mode_create(const Mode *mode)
Definition mode.c:225
ModeMode mode_completer_result(Mode *mode, int menu_retv, char **input, unsigned int selected_line, char **path)
Definition mode.c:232
void * mode_get_private_data(const Mode *mode)
Definition mode.c:176
char * mode_get_message(const Mode *mode)
Definition mode.c:218
ModeMode
Definition mode.h:54
@ MENU_CUSTOM_COMMAND
Definition mode.h:84
@ MENU_COMPLETE
Definition mode.h:88
@ MENU_LOWER_MASK
Definition mode.h:92
@ MENU_CANCEL
Definition mode.h:74
@ MENU_ENTRY_DELETE
Definition mode.h:80
@ MENU_CUSTOM_ACTION
Definition mode.h:90
@ MENU_OK
Definition mode.h:72
@ MENU_CUSTOM_INPUT
Definition mode.h:78
@ MODE_EXIT
Definition mode.h:56
@ RELOAD_DIALOG
Definition mode.h:60
const Mode * rofi_get_completer(void)
Definition rofi.c:1367
const char * cache_dir
Definition rofi.c:82
static int run_token_match(const Mode *sw, rofi_int_matcher **tokens, unsigned int index)
Definition run.c:548
static RunEntry * get_apps(unsigned int *length)
Definition run.c:245
static gboolean exec_cmd(const char *cmd, int run_in_term, const char *orig)
Definition run.c:104
static int sort_func(const void *a, const void *b, G_GNUC_UNUSED void *data)
Definition run.c:166
static char * run_get_message(const Mode *sw)
Definition run.c:556
static cairo_surface_t * _get_icon(const Mode *sw, unsigned int selected_line, unsigned int height)
Definition run.c:574
static RunEntry * get_apps_external(RunEntry *retv, unsigned int *length, unsigned int num_favorites)
Definition run.c:185
static void run_mode_destroy(Mode *sw)
Definition run.c:421
static unsigned int run_mode_get_num_entries(const Mode *sw)
Definition run.c:443
#define RUN_CACHE_FILE
Definition run.c:64
static ModeMode run_mode_result(Mode *sw, int mretv, char **input, unsigned int selected_line)
Definition run.c:451
static void delete_entry(const RunEntry *cmd)
Definition run.c:147
static char * _get_display_value(const Mode *sw, unsigned int selected_line, G_GNUC_UNUSED int *state, G_GNUC_UNUSED GList **list, int get_entry)
Definition run.c:537
Mode run_mode
Definition run.c:603
static int run_mode_init(Mode *sw)
Definition run.c:411
#define TICK_N(a)
Definition timings.h:69
struct _icon icon
Definition icon.h:44
@ MODE_TYPE_SWITCHER
struct rofi_int_matcher_t rofi_int_matcher
Settings config
Definition run.c:66
uint32_t icon_fetch_size
Definition run.c:70
char * exec
Definition run.c:68
gboolean from_history
Definition run.c:72
char * entry
Definition run.c:67
uint32_t icon_fetch_uid
Definition run.c:69
cairo_surface_t * icon
Definition run.c:74
guint icon_fetch_scale
Definition run.c:71
unsigned int cmd_list_length
Definition run.c:84
char * old_input
Definition run.c:89
uint32_t selected_line
Definition run.c:88
Mode * completer
Definition run.c:91
char * old_completer_input
Definition run.c:92
gboolean file_complete
Definition run.c:87
RunEntry * cmd_list
Definition run.c:82
__mode_get_num_entries _get_num_entries
_mode_token_match _token_match
_mode_get_display_value _get_display_value
_mode_get_icon _get_icon
void * private_data