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:
parent
5870040256
commit
bc67d8a8a6
8 changed files with 239 additions and 2 deletions
|
@ -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\
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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',
|
||||
|
|
89
doc/rofi-actions.5.markdown
Normal file
89
doc/rofi-actions.5.markdown
Normal 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
|
||||
```
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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},
|
||||
|
|
Loading…
Add table
Reference in a new issue