Ah, the packaging question. No one has ever asked me about that yet, that I can recall. In its current state I’m not sure how well it qualifies as something to be packaged in a typical repo. I’ll describe how it works. This may be much more info than anyone will ever care about.
The project Toshy evolved from was a custom-install sort of situation, with a systemd
service unit that ran the keymapper command via sudo
(yes, as root). Because that was how xkeysnail
(the actual keymapper) was supposed to be run. At least, that was one way it could be run, so that the process would have access to the necessary /dev/uinput
device for input/output. And Kinto used xhost
as part of the command to open up the X server so that the root process could do its thing.
My project uses a fork of the keymapper called keyszer
, which kind of deprecated the run-as-root stuff and promoted a few options such as using a udev
rules file to make sure the process has access to the device it needs. That’s what I’ve gone with. So my installer makes sure your user is in the input
group, and then installs the appropriate udev
rules file depending on whether the system supports ACL permissions or just standard Unix permissions. This of course requires the user to reboot, which is prompted by the installer.
The Toshy installer is completely written from scratch in Python, with a philosophy of “let’s keep as much as possible inside the user’s home instead of messing with the system”. So it literally installs pretty much everything in places like ~/.config/toshy
, including the Python venv
it uses to keep itself from messing with system Python packages, and helps it work on systems with really old Python, by using a newer Python interpreter if possible when creating the virtual environment.
Technically there are a couple of “apps” that come with the keymapper config that does all the work, but they are just Python scripts also, and are installed alongside the config file in ~/.config/toshy
. Because I couldn’t figure out how to do a real Python package for them and install them elsewhere while keeping them working. (Path issues.) One creates an indicator tray icon with menu (works in all the DEs that still support an indicator tray), and the other is a simple GUI preferences app using tkinter
. They have the ability to load and save shared settings to an sqlite3
file living in the same folder as the config file, which also watches that file and loads settings from it.
The desktop entry files that add the “apps” to the menu are also installed in the user’s home folder location, so nothing will appear in any other user’s menu unless they also run the installer. And the desktop files point at scripts that only exist in the user’s home. So there is a lot of isolation going on between users. Kind of like a Flatpak installed from a “user” repo instead of systemd-wide.
Because of the manual nature of installing, I’ve added the ability to kind of do upgrades in-place while saving some user-customized sections of the config file, which itself is also a Python module that does quite a lot of complicated stuff. Every time the installer is run, it saves a backup of the config files and then creates a whole new folder for itself with a new venv
(the old venv
is discarded since it tends to be a few dozen MB in size).
I should probably note that you can actually use Toshy without all the Mac shortcut stuff, by giving the installer the option “–barebones-config”. This is a recent addition. In this case it becomes just a way to install the keyszer
keymapper with tools that will make it easy to auto-start, with a collection of terminal management commands, the tray icon with menu, and the GUI window.
I just got finished making the tray icon menu and GUI app automatically simplify themselves when the “barebones” config is in use, so the irrelevant options that only apply to the full config file disappear. You can then do as much or as little as you want with the keymapper’s abilities, starting from a config file that does nothing (besides a simple example keymap that adds a couple of currency symbols to the keyboard).
This is meaningful not just for the management commands (stop/start, enable/disable, verbose debugging output, logging) that make it easier to install and set up the keymapper, but also because Toshy is using a custom development branch of the keymapper, where I was able to add support for the two Wayland environments that currently work (Wayland+GNOME and Wayland+KDE). The mainline keymapper, just like its predecessor, only supports X11/Xorg sessions at this time. I did my testing on OpenMandriva in a Wayland+KDE session, and Wayland+GNOME should work on OpenMandriva just as well as it does on all other distros. (Wayland+GNOME requires a shell extension that you have to install yourself.)
The keymapper has a function that processes strings and Unicode characters, so it can do some kind of advanced output despite just being a simple “virtual keyboard” device. Some of that depends on the environment. Unicode character entry in particular requires ibus
, or possibly fcitx
(unconfirmed).
The main motivator behind the project was to make sure those who like macOS shortcuts could have access to something like Kinto on additional distros and in at least the primary Wayland environments. Kinto also messed with the sudoers
file, which caused it to be incompatible with some distros like antiX. Toshy doesn’t have any need of running anything as root, so it works on antiX. I made sure of that.
One of the most awkward things about the original project is that the keymapper, uses evdev
to “grab” the /dev/uinput
device, and only one process can do that at a time. So in the original project if you logged out or switched users, the keymapper would continue to affect the keyboard in another user account, or even the login screen. This made it largely incompatible with a multi-user system, or at least weird.
It was a royal pain, but I was finally able to find an obscure bit of info exposed by loginctl
(part of systemd
) that allowed me to use a separate systemd user service to stop the keymapper service whenever the “session” became inactive. It’s complicated and somewhat delicate, but it works in testing, preventing the keymapper from interfering with another user account. But, it only works if at least the background account is running the keymapper with the systemd user services. Manual usage, which is required on systems that don’t use systemd
or while debugging the config, will still prevent another user from trying to run their own keymapper that uses the same /dev/uinput
device.
I’ve been meaning to look into whether Toshy could be packaged in a Flatpak, but with all of the groups, permissions and the device it needs, I’m not sure if it is a “Flatpak-able” project. I assume that those would might also be a concern when trying to package it in other ways.
One thing specific to how it installs on OpenMandriva was that I unfortunately had to add task-devel
to the package list, so that dbus-python
would successfully install (via pip
) in the venv
. I have always had the most trouble with that Python package, figuring out which native packages will allow it to build/install without an error. In this case I could not pinpoint exactly which of the packages in task-devel
actually allowed the install to succeed, but without the whole thing I kept getting errors from the meson
build process like “gcc cannot compile programs” during that stage of the install process.
I say “unfortunately” only because that OpenMandriva meta-package downloads around 230MB of additional packages, and the usual list of native dependencies for Toshy only downloads 55-110MB of new packages in total.
Below is the package list I ended up with for getting it working on OpenMandriva. It may have one or two redundancies, but I got tired of resetting the VM to double-check whether each one was absolutely necessary on its own and not automatically installed as a dependency of something else on the list. I did remove gcc
from the list since that’s part of task-devel
.
["git", "cairo-devel", "dbus-daemon", "dbus-devel",
"gobject-introspection-devel", "lib64ayatana-appindicator3_1",
"lib64ayatana-appindicator3-gir0.1", "lib64cairo-gobject2",
"lib64python-devel", "lib64systemd-devel", "libnotify",
"python-dbus-devel", "python-dbus", "python-ensurepip",
"python3-pip", "task-devel", "tkinter", "xset", "zenity"],
I’ll be amazed if anyone wades through all that junk to read this sentence.