Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
wants to merge 5 commits into
base: next
Choose a base branch
from

Conversation

giomatfois62
Copy link
Contributor

This pull request addresses issue #2032, making it possible to execute custom user commands or scripts on a variety of rofi events. The following events are covered for now:

  • Selected entry change (forward current entry name to user command)
  • Entry accepted (forward accepted entry name to user command)
  • Menu canceled
  • Mode changed
  • Menu error (forward error text to user command)
  • Screenshot taken (forward screenshot path to user command, seemed cool to play a camera shutter sound in this case)

Custom commands can be specified from command line as follows:

rofi -on-selection-changed "$HOME/play_select.sh {entry}" \
     -on-entry-accepted "$HOME/play_accept.sh {entry}" \
     -on-menu-canceled "$HOME/play_exit.sh" \
     -on-mode-changed "$HOME/play_change.sh" \
     -on-menu-error "$HOME/play_error.sh {error}" \
     -on-screenshot-taken "$HOME/play_camera.sh {path}" ...

Here's an example bash script to react to "selected entry change" playing a sound:

#!/bin/bash

coproc aplay -q /home/mat/Music/selecting_an_item.wav

Arguments to user commands enclosed in curly braces will be replaced with corresponding values (e.g. {entry} with currently selected or accepted entry, {error} with error text etc.). Among other things, this allows the use of a synthetic speaker (like espeak or piper) to read aloud rofi interactions. Here's an example bash script to read aloud currently selected entries using espeak:

#!/bin/bash

killall espeak
echo "selected: $@" | sed -e 's///g' | espeak

The use of "coproc" for playing sounds with aplay is suggested, as the first example script shows, otherwise the rofi process will wait for sounds to end playback before exiting, and that can be annoying if someone concatenate multiple rofi menus (cough cough..).

Some things I don't like with current implementation:

  • "selected entry change" user command is called inside selection_changed_callback. This function is strangely called multiple times for the same entry if icons are enabled (I guess once when the entry is selected and once when the icon is displayed but sometimes i see it called 3-4 times), thus I had to make a dirty hack to store the current index and compare it with the one passed to the function every time. I would like to find a cleaner approach, perhaps it's better to compare the current entry text instead of the current entry index.
  • There seems to be no way to get the list of available modes from RofiViewState, unless the sidebar is enabled (then i can get the mode names from the textboxes stored in the state). The mode change logic seems to be implemented in rofi.c and the view only signals it setting MENU_NEXT|MENU_PREVIOUS and the like in the return value. It would be nice to forward the mode name to the command that reacts to menu mode changed (e.g. "$HOME/play_change.sh {mode}").

For the callback names (on-selection-changed etc.) I tried to be descriptive enough without making them too long, but I'm certainly open to different suggestions, as well as for other rofi events worth provisioning with custom commands.
My first experiments were made using an actual C audio library integrated in rofi, but this implementation coupled with simple bash scripts does the job as well and is much more flexible.

Let me know what you think.

…s: entry selected, entry accepted, menu canceled, menu error, mode changed, screenshot taken
@its-19818942118
Copy link

this looks very neat. and simple to use too. exactly what i was going for. a settings option for adding sounds. i wanted to suggest espeak but wasnt sure if it was possible to implement read aloud options. thanks @giomatfois62

…entry index in selection_changed_user_callback
source/view.c Outdated Show resolved Hide resolved
@DaveDavenport DaveDavenport added this to the 1.7.7 milestone Dec 26, 2024
@DaveDavenport
Copy link
Collaborator

Can the options be documented in the manpages? then I will merge this.

@giomatfois62
Copy link
Contributor Author

Sure, do you prefer a section in the general rofi page or a different file like for xdg thumbnails?

@DaveDavenport
Copy link
Collaborator

I think I prefer a separate. It is additional functionality, the main manpage is already long enough.

@1337kerberos
Copy link

1337kerberos commented Dec 30, 2024

Hi guys! I have tried the patch and noticed that {window} argument is not implemented.
I need this to recover window position when pulling window from a scratchpad on i3 WM.
I'm having an issue when recovering window, it looses position, and is displayed 50% off the display.
So I have made a script to recover position back, using -window-command '$SCRIPTS/rofi_show.sh {window}'
@giomatfois62 could you add this feature or guide me how to implement it? Thanks!

@giomatfois62
Copy link
Contributor Author

I tried the scripts to play sound with the "window" modi and they work, window name will be passed replacing "{entry}".
For example, try something like this, changing the paths of the scripts:
rofi -show window -on-selection-changed "/home/mat/Programs/rofi-desktop/scripts/play_select.sh {entry}" -on-entry-accepted "/home/mat/Programs/rofi-desktop/scripts/play_accept.sh {entry}"

@1337kerberos
Copy link

So from what I can see, using {entry} returns a window name, but {window} returns X11 id,
which I can use with xdotool or etc ... so I would prefer to have id via {window} arg.
It should be possible to script around and parse windows by name.
But more likely, I will look into the implementation and try to add {window}.
@giomatfois62 Thanks)

@giomatfois62
Copy link
Contributor Author

@1337kerberos now I understand, I guess it could be done with some effort, here I added the call for the custom script on entry accepted, you can see that the expression "{entry}" is replaced by the currently selected text, you could easily add a "{window}" there since helper_parse_setup supports multiple arguments, but the x11 window id is not easy to get there, there's a separate c file with all that logic (source/modes/window.c) and it's kind of hidden from the generic view code.

@1337kerberos
Copy link

1337kerberos commented Dec 30, 2024

@giomatfois62 Thanks for the details; one more question - how do you debug rofi? ;)
I tried to break on that function using gdb and realized the keyboard got stuck.
It is much harder to learn the codebase without a debugger ...

Ok, I have an idea, install an Ubuntu VM and then use a remote gdb to debug it,
but that is kinda annoying/complicated, maybe you have a better advice)

Update: I have hacked around the issue by adding a script to reposition the active window after rofi,
so it is not as relevant to me anymore, but it is still interesting how you debug this

@giomatfois62
Copy link
Contributor Author

It's not easy to debug since it grabs the keyboard input and blocks it when the process is paused, take a look at this doc page rofi-debugging

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants