1
0
Fork 0
mirror of https://github.com/davatorium/rofi.git synced 2025-01-27 15:25:24 -05:00

[WIP] Execute custom user commands or scripts on a variety of rofi events (#2053)

* Implemented custom user command execution on the following menu events: entry selected, entry accepted, menu canceled, menu error, mode changed, screenshot taken

* fixed different signedness comparison warning and compare unfiltered entry index in selection_changed_user_callback

* track previously selected line in RofiViewState

* added documentation about custom scripts to run on certain actions

---------

Co-authored-by: Matteo <giomatfois62@yahoo.it>
This commit is contained in:
giomatfois62 2025-01-24 11:19:48 +01:00 committed by GitHub
parent 5870040256
commit bc67d8a8a6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 239 additions and 2 deletions

View file

@ -182,6 +182,7 @@ if FOUND_PANDOC
generate-manpage: doc/rofi.1\
doc/rofi-sensible-terminal.1\
doc/rofi-theme-selector.1\
doc/rofi-actions.5\
doc/rofi-debugging.5\
doc/rofi-dmenu.5\
doc/rofi-keys.5\

View file

@ -49,6 +49,18 @@ Settings config = {
/** Custom command to generate preview icons */
.preview_cmd = NULL,
/** Custom command to call when menu selection changes */
.on_selection_changed = NULL,
/** Custom command to call when menu mode changes */
.on_mode_changed = NULL,
/** Custom command to call when menu entry is accepted */
.on_entry_accepted = NULL,
/** Custom command to call when menu is canceled */
.on_menu_canceled = NULL,
/** Custom command to call when menu finds errors */
.on_menu_error = NULL,
/** Custom command to call when menu screenshot is taken */
.on_screenshot_taken = NULL,
/** Terminal to use. (for ssh and open in terminal) */
.terminal_emulator = "rofi-sensible-terminal",
.ssh_client = "ssh",

View file

@ -2,6 +2,7 @@ man_files = [
'rofi.1',
'rofi-sensible-terminal.1',
'rofi-theme-selector.1',
'rofi-actions.5',
'rofi-debugging.5',
'rofi-dmenu.5',
'rofi-keys.5',

View file

@ -0,0 +1,89 @@
# rofi-actions(5)
## NAME
**rofi-actions** - Custom commands following interaction with rofi menus
## DESCRIPTION
**rofi** allows to set custom commands or scripts to be executed when some actions are performed in the menu, such as changing selection, accepting an entry or canceling.
This makes it possible for example to play sound effects or read aloud menu entries on selection.
## USAGE
Following is the list of rofi flags for specifying custom commands or scripts to execute on supported actions:
`-on-selection-changed` *cmd*
Command or script to run when the current selection changes. Selected text is forwarded to the command replacing the pattern *{entry}*.
`-on-entry-accepted` *cmd*
Command or script to run when a menu entry is accepted. Accepted text is forwarded to the command replacing the pattern *{entry}*.
`-on-mode-changed` *cmd*
Command or script to run when the menu mode (e.g. drun,window,ssh...) is changed.
`-on-menu-canceled` *cmd*
Command or script to run when the menu is canceled.
`-on-menu-error` *cmd*
Command or script to run when an error menu is shown (e.g. `rofi -e "error message"`). Error text is forwarded to the command replacing the pattern *{error}*.
`-on-screenshot-taken` *cmd*
Command or script to run when a screenshot of rofi is taken. Screenshot path is forwarded to the command replacing the pattern *{path}*.
### Example usage
Rofi command line:
```bash
rofi -on-selection-changed "/path/to/select.sh {entry}" \
-on-entry-accepted "/path/to/accept.sh {entry}" \
-on-menu-canceled "/path/to/exit.sh" \
-on-mode-changed "/path/to/change.sh" \
-on-menu-error "/path/to/error.sh {error}" \
-on-screenshot-taken "/path/to/camera.sh {path}" \
-show drun
```
Rofi config file:
```css
configuration {
on-selection-changed: "/path/to/select.sh {entry}";
on-entry-accepted: "/path/to/accept.sh {entry}";
on-menu-canceled: "/path/to/exit.sh";
on-mode-changed: "/path/to/change.sh";
on-menu-error: "/path/to/error.sh {error}";
on-screenshot-taken: "/path/to/camera.sh {path}";
}
```
### Play sound effects
Here's an example bash script that plays a sound effect using `aplay` when the current selection is changed:
```bash
#!/bin/bash
coproc aplay -q $HOME/Music/selecting_an_item.wav
```
The use of `coproc` for playing sounds is suggested, otherwise the rofi process will wait for sounds to end playback before exiting.
### Read aloud
Here's an example bash script that reads aloud currently selected entries using `espeak`:
```bash
#!/bin/bash
killall espeak
echo "selected: $@" | espeak
```

View file

@ -64,6 +64,18 @@ typedef struct {
/** Custom command to generate preview icons */
char *preview_cmd;
/** Custom command to call when menu selection changes */
char *on_selection_changed;
/** Custom command to call when menu mode changes */
char *on_mode_changed;
/** Custom command to call when menu entry is accepted */
char *on_entry_accepted;
/** Custom command to call when menu is canceled */
char *on_menu_canceled;
/** Custom command to call when menu finds errors */
char *on_menu_error;
/** Custom command to call when menu screenshot is taken */
char *on_screenshot_taken;
/** Terminal to use */
char *terminal_emulator;
/** SSH client to use */

View file

@ -90,6 +90,8 @@ struct RofiViewState {
int skip_absorb;
/** The selected line (in the unfiltered list) */
unsigned int selected_line;
/** The previously selected line (in the unfiltered list) */
unsigned int previous_line;
/** The return state of the view */
MenuReturn retv;
/** Monitor #workarea the view is displayed on */

View file

@ -212,6 +212,18 @@ static int lev_sort(const void *p1, const void *p2, void *arg) {
return distances[*a] - distances[*b];
}
static void screenshot_taken_user_callback(const char *path) {
if (config.on_screenshot_taken == NULL)
return;
char **args = NULL;
int argv = 0;
helper_parse_setup(config.on_screenshot_taken, &args, &argv, "{path}",
path, (char *)0);
if (args != NULL)
helper_execute(NULL, args, "", config.on_screenshot_taken, NULL);
}
/**
* Stores a screenshot of Rofi at that point in time.
*/
@ -271,6 +283,7 @@ void rofi_capture_screenshot(void) {
g_warning("Failed to produce screenshot '%s', got error: '%s'", fpath,
cairo_status_to_string(status));
}
screenshot_taken_user_callback(fpath);
}
cairo_destroy(draw);
}
@ -1285,9 +1298,30 @@ inline static void rofi_view_nav_last(RofiViewState *state) {
// state->selected = state->filtered_lines - 1;
listview_set_selected(state->list_view, -1);
}
static void selection_changed_user_callback(unsigned int index, RofiViewState *state) {
if (config.on_selection_changed == NULL)
return;
int fstate = 0;
char *text = mode_get_display_value(state->sw, state->line_map[index],
&fstate, NULL, TRUE);
char **args = NULL;
int argv = 0;
helper_parse_setup(config.on_selection_changed, &args, &argv, "{entry}",
text, (char *)0);
if (args != NULL)
helper_execute(NULL, args, "", config.on_selection_changed, NULL);
g_free(text);
}
static void selection_changed_callback(G_GNUC_UNUSED listview *lv,
unsigned int index, void *udata) {
RofiViewState *state = (RofiViewState *)udata;
if (index < state->filtered_lines) {
if (state->previous_line != state->line_map[index]) {
selection_changed_user_callback(index, state);
state->previous_line = state->line_map[index];
}
}
if (state->tb_current_entry) {
if (index < state->filtered_lines) {
int fstate = 0;
@ -1295,7 +1329,6 @@ static void selection_changed_callback(G_GNUC_UNUSED listview *lv,
&fstate, NULL, TRUE);
textbox_text(state->tb_current_entry, text);
g_free(text);
} else {
textbox_text(state->tb_current_entry, "");
}
@ -1882,7 +1915,6 @@ static void rofi_view_trigger_global_action(KeyBindingAction action) {
// Nothing entered and nothing selected.
state->retv = MENU_CUSTOM_INPUT;
}
state->quit = TRUE;
break;
}
@ -2086,8 +2118,43 @@ void rofi_view_handle_mouse_motion(RofiViewState *state, gint x, gint y,
}
}
static void rofi_quit_user_callback(RofiViewState *state) {
if (state->retv & MENU_OK) {
if (config.on_entry_accepted == NULL)
return;
int fstate = 0;
unsigned int selected = listview_get_selected(state->list_view);
// TODO: handle custom text
if (selected >= state->filtered_lines)
return;
// Pass selected text to custom command
char *text = mode_get_display_value(state->sw, state->line_map[selected],
&fstate, NULL, TRUE);
char **args = NULL;
int argv = 0;
helper_parse_setup(config.on_entry_accepted, &args, &argv, "{entry}",
text, (char *)0);
if (args != NULL)
helper_execute(NULL, args, "", config.on_entry_accepted, NULL);
g_free(text);
} else if(state->retv & MENU_CANCEL) {
if (config.on_menu_canceled == NULL)
return;
helper_execute_command(NULL, config.on_menu_canceled, FALSE, NULL);
} else if (state->retv & MENU_NEXT ||
state->retv & MENU_PREVIOUS ||
state->retv & MENU_QUICK_SWITCH ||
state->retv & MENU_COMPLETE) {
if (config.on_mode_changed == NULL)
return;
// TODO: pass mode name to custom command
helper_execute_command(NULL, config.on_mode_changed, FALSE, NULL);
}
}
void rofi_view_maybe_update(RofiViewState *state) {
if (rofi_view_get_completed(state)) {
// Exec custom user commands
rofi_quit_user_callback(state);
// This menu is done.
rofi_view_finalize(state);
// If there a state. (for example error) reload it.
@ -2482,6 +2549,7 @@ RofiViewState *rofi_view_create(Mode *sw, const char *input,
state->menu_flags = menu_flags;
state->sw = sw;
state->selected_line = UINT32_MAX;
state->previous_line = UINT32_MAX;
state->retv = MENU_CANCEL;
state->distance = NULL;
state->quit = FALSE;
@ -2571,6 +2639,18 @@ RofiViewState *rofi_view_create(Mode *sw, const char *input,
return state;
}
static void rofi_error_user_callback(const char *msg) {
if (config.on_menu_error == NULL)
return;
char **args = NULL;
int argv = 0;
helper_parse_setup(config.on_menu_error, &args, &argv, "{error}",
msg, (char *)0);
if (args != NULL)
helper_execute(NULL, args, "", config.on_menu_error, NULL);
}
int rofi_view_error_dialog(const char *msg, int markup) {
RofiViewState *state = __rofi_view_state_create();
state->retv = MENU_CANCEL;
@ -2608,6 +2688,9 @@ int rofi_view_error_dialog(const char *msg, int markup) {
sn_launchee_context_complete(xcb->sncontext);
}
// Exec custom command
rofi_error_user_callback(msg);
// Set it as current window.
rofi_view_set_active(state);
return TRUE;

View file

@ -136,6 +136,43 @@ static XrmOption xrmOptions[] = {
"Custom command to generate preview icons",
CONFIG_DEFAULT},
{xrm_String,
"on-selection-changed",
{.str = &config.on_selection_changed},
NULL,
"Custom command to call when menu selection changes",
CONFIG_DEFAULT},
{xrm_String,
"on-mode-changed",
{.str = &config.on_mode_changed},
NULL,
"Custom command to call when menu mode changes",
CONFIG_DEFAULT},
{xrm_String,
"on-entry-accepted",
{.str = &config.on_entry_accepted},
NULL,
"Custom command to call when menu entry is accepted",
CONFIG_DEFAULT},
{xrm_String,
"on-menu-canceled",
{.str = &config.on_menu_canceled},
NULL,
"Custom command to call when menu is canceled",
CONFIG_DEFAULT},
{xrm_String,
"on-menu-error",
{.str = &config.on_menu_error},
NULL,
"Custom command to call when menu finds errors",
CONFIG_DEFAULT},
{xrm_String,
"on-screenshot-taken",
{.str = &config.on_screenshot_taken},
NULL,
"Custom command to call when menu screenshot is taken",
CONFIG_DEFAULT},
{xrm_String,
"terminal",
{.str = &config.terminal_emulator},