bots | Loading last commit info... | |
.editorconfig | ||
.gitignore | ||
LICENSE | ||
README.md | ||
bot |
libinput
Bot
bot
is a simple Zsh script framework that uses
a Zsh coprocess to read key input from libinput record
and trigger
user-defined functions upon certain combinations being pressed. It can be used
for anything requiring global keyboard shortcuts to trigger actions, under any
system configured to use libinput
devices.
Why?
bot
was created so that I could trigger keyboard macros for games running
under Wayland. Most macro systems that were around when bot
was created only
worked under Xorg, or were too annoying to work with.
However, bot
is not very performant and clunky to work with in its own ways.
But on the upside, that makes it is very simple, and as such is easy to modify
and extend. bot
does not have any logic itself to determine when to run
actions, it only keeps track of key state, so there is a lot of flexibility in
writing scripts for it.
Should I use bot
?
If you need a quick and easy way to perform actions when a keyboard or mouse
button is pressed, held, or released, and need decent control on when effects
stop and start, sure. But if you need precise timing, out-of-box support for
checking the position of the mouse, or don't use libinput
, this script is not
for you.
Do note most desktop environments come with global keyboard shortcuts, like KDE and Gnome. Check your system's documentation.
Usage
The bot
script first loads sub-scripts (bots) from the bots/
directory as
specified on the command line as arguments. For example, if you have two
files bots/bot1
and bots/bot2
, and run the command bot bot1 bot2
, both
files will be dot-sourced and added to the array of enabled bots
(bot_enabled
).
Creating your own bot
Since no bots except for the example clicker
are included by default with
this distribution, you need to create your own if you want any more advanced
effects.
Defining hooks
By itself, bot
only keeps track of the state of keys and the bots registered
on startup. To actually act on this information, each bot should add a function
name or other eval
able command to the bot_hooks
array.
Each item in this array is eval
uated repeatedly in a loop, without arguments.
This loop runs in the main thread, while the libinput
events are handled in
the coprocess, so you don't have to worry about waiting on event input, and you
can still do things when no events are being sent.
For example, if you were to write the below script to bots/example
and run
bot example
, you would get "hello world" repeatedly printed to standard
output:
function example {
echo 'hello world'
}
bot_hooks+=example
You can technically add any valid Zsh command list to the array, though this is not supported. It would be best to use function names. But to play the devil's advocate, this unfortunately works:
myscript='echo hello world'
bot_hooks+=$myscript
Running hooks
When going through the bot_hooks
array to run hooks every cycle, bot
will
look in the bot_enabled
array to see if the hook is there. If it isn't, the
hook is not run. Bots that are specified on the command line are also added to
the bot_enabled
array, so adding a hook named after your bot will have that
hook be enabled and run automatically. If your bot has a different name, you
should enable it manually by adding it to the array when your script is
sourced. Naturally, this also means that renaming bots can result in their
hooks not being enabled automatically, without also renaming the hook.
Do note that the arguments of bot
are added to the bot_enabled
array even
if there is no matching filename. This is on purpose, so that you can modify
bots by checking the contents of bot_enabled
for specific items.
Writing a bot
Each bot is expected to add a list of key constants to the bot_keys
array to enable the key state to be tracked. For example, to add the
F1 key to the list:
bot_keys+=(KEY_F1)
When libinput record
sends a key event, the key in question (hereafter $k
)
is checked against the array. If a key matches, the following arrays are
updated:
$s[$k]
: The current state of the key.$c[$k]
: The count of loops since the key was pressed.$t[$k]
: A toggle that gets flipped every time the key is pressed.
Aside from the $s
, $c
, and $t
arrays, there are a few functions available
that you can use to quickly check the state of keys:
key_pressed
key_released
key_toggled_on
key_toggled_off
Each of these functions accepts two arguments: the key to check, and a count after which the key will be considered actice again. Let's look at an example, with the comments explaining when each block is processed:
if {key_pressed KEY_H} {
# Happens once on initial press of the H key.
}
if {key_pressed KEY_J 3000} {
# Happens once on initial press of the J key.
# This block will run again after 3000 cycles if the key is still pressed.
# It is up to the hook to reset the counter for the key. Set it to 0 to
# show that you've handled the event:
c[KEY_J]=0
# If you don't reset the cycle to 0, this block will run every loop after
# the counter hits 3000. This is an intentional feature.
}
if {key_toggled_on KEY_K} {
# This block will run once on initial press of K, and K is now "on".
# Pressing K again will turn the key "off", and won't run this block.
# Pressing the key again will toggle it back on and run this block again.
}
if {key_toggled_on KEY_L 3000} {
# L is toggled on for the initial press, and this block will run.
# After 3000 cycles, it will run again, and will continue to do so until
# the key is toggled back off with another press.
# As with key_pressed, make sure to reset the counter, with same reasoning:
c[KEY_L]=0
}
The key_released
and key_toggle_off
functions work exactly the same as
key_pressed
and key_toggle_on
, except they trigger when the key is
released or the key toggled off, respectively. Do note that keys are not
tracked until the first press, so key_toggle_off
will not return success
until the key has already been pressed twice to toggle it on then off.
Performing automated inputs
It is expected that bots will want to send key and mouse input with ydotool
.
To help make this process easier, bot
attempts to start
ydotool
with systemd --user
by
default. Look at the included clicker script for an example of
sending input with ydotool
, specifically mouse clicks.
If you don't need ydotool
, or don't use systemd
service management, feel
free to comment that line out of the script. If you're on Xorg, you can use
xdotool
instead, which doesn't need a daemon or service.
Of course, you don't even need to send inputs at all if you don't want to—while
that is what bot
was created to do, it is purpose-agnostic and you can use
whatever tool or run whatever script or program you want as part of your bot.
Known issues
- Timing: One reason I never mention time when discussing repeat actions is
because
bot
does not keep track of time, only the count of cycles the main loop has gone through. Large, demanding scripts will slow down loop execution, as will loading many bots at once. Future versions of this project may refactor this system or add an additional system to track actual time elapsed.
License
This project is licensed under the Apache License, Version 2.0 (the "License"); you may not use it except in compliance with the License. A copy of the License is made available in the LICENSE file. You may also obtain a copy of the license at the Apache website.
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.