Package: py-rofi-bus
This package provides a DBus foundation forrofi.Current version is a skeleton API with a simple window switcher. The API will potentially change drastically.Features(These are all planned;0.2.0doesn’t expose much of an API)Spawn and populate scripts/modi viapy-rofi-busSave information from scripts/modiSplit massive scripts/modi into smaller components that pass state throughpy-rofi-hubSetupSystem DependenciesTherofiutilityXCB Python bindings (preferablyviaxcffib)Installation$pipinstall--userpy-rofi-busUsageAs of0.2.0, logging and help menus are pretty sparse. Expect things to break without a clear reason.MainDbusDaemonThe (current) core ofpy-rofi-busisMainDbusDaemon, which combines all of the important features in some manner without implementing any of them very well.MainDbusDaemonforks to become a daemon and runs in the background. It publishes a very simple interface to theSessionBusand waits for interaction from the user. It currently cannot resuscitate itself should its main loop be killed or exited.CLI Interactionpy-rofi-busexposes a very simple CLI to manageMainDbusDaemon.$whichpy-rofi-bus~/.local/bin/py-rofi-bus$py-rofi-busdaemon-husage: py-rofi-bus daemon [-h] {start,status,stop} ...

positional arguments:
  {start,status,stop}  Available actions
    start              Start the daemon
    status             Check the status of the daemon
    stop               Stop the daemon

optional arguments:
  -h, --help           show this help message and exitThe CLI is independent of the daemon so it can be used to restart the daemon.DBus InterfacestartStarts the daemon. Doesn’t actually do anything (except I don’t think I’m properly watching the PID file so it actually just restarts the daemon).stopStops the daemon. This kills the daemon’s process.is_runningTrueif the daemon is running;GDBus.Error:org.freedesktop.DBus.Error.ServiceUnknownif the daemon is not running.load_appsThis is an experimental feature that attempts to run any executable found in the configuredload_fromdirectory. Files must be marked as executable for the script to be able to load them. So far my cursory tests have demonstrated an ability to load and control both simple scripts and more complicated things like daemons. They’ve also revealed that I should have planned a bit better and will probably face some refactoring soon.systemdIntegrationMainDbusDaemoncan easily be run as ausersystemdunit. First atargetthat binds to thegraphical-session.targetmust be made.$cat$XDG_CONF_HOME/systemd/user/my-first.target[Unit]
Description=Lives and dies with the graphical session
BindsTo=graphical-session.targetWe can now bind aunitto thetarget, meaning it will also be dependent on thegraphical-session. Note that the paths below assume a--userinstall. You’ll need to update them ifpy-rofi-buswas installed somewhere else.$cat$XDG_CONF_HOME/systemd/user/pyrofibus.service[Unit]
Description=py-rofi-bus
PartOf=graphical-session.target

[Service]
Type=forking
ExecStart=%h/.local/bin/py-rofi-bus daemon start
ExecStop=%h/.local/bin/py-rofi-bus daemon stop
PIDFile=%h/.config/wotw/py-rofi-bus/.pid

[Install]
WantedBy=my-first.target#Starttheservicetomakesureitworks$systemctl--userstartpyrofibus.service#Assumingitdoes,youcanenableittorunautomatically$systemctl--userenablepyrofibus.serviceFinally, to triggermy-first.target, add these commands somewhere in your startup files. I runi3and these are executed at the end of myi3config file. The tail end of your.whateverrcfile would work well too.# Some of these might not be necessary. I never weeded out the duds.
# You'll need some of these variables to be able to trigger the target.systemctl--userimport-environmentUSERHOMEPATHDISPLAYXAUTHORITYsystemctl--userstartmy-first.targetExample AppI updated the proof-of-concept example. It cast some light on the package’s deficiencies. Use it with a grain of salt. Many things that are manual now aren’t planned to be manual forever.Initial SetupAssuming you’ve installedpy-rofi-bus, you’ll need to create the configuration directory.$mkdir-p"$XDG_CONFIG_HOME/wotw/py-rofi-bus/{apps-enabled,pids}"To run the daemons, they must be in theload_fromconfig directory, which is probably the one above unless you changed things.$cdpath/to/repo/or/package$ls-lexamples/rofi-alt-tabtotal 16
-rw-r--r--. 1 cjharries cjharries 2457 Jun  3 13:06 active_window_monitor_daemon.py
-rw-r--r--. 1 cjharries cjharries 2231 Jun  3 13:06 dbus_window_daemon.py
-rw-r--r--. 1 cjharries cjharries 4826 Jun  3 13:06 ordered_window_script.py$chmodu+xexamples/rofi-alt-tab/*.py$source<(realpath examples/rofi-alt-tab/*daemon.py | \
        awk '{ print "ln -s "$0" $XDG_CONFIG_HOME/wotw/py-rofi-bus/apps-enabled"; }' \
    )$ls-l~/.config/wotw/py-rofi-bus/apps-enabledtotal 12
lrwxrwxrwx. 1 cjharries cjharries 103 Jun  3 18:00 active_window_monitor_daemon.py -> <snip>/examples/rofi-alt-tab/active_window_monitor_daemon.py
lrwxrwxrwx. 1 cjharries cjharries  93 Jun  3 18:00 dbus_window_daemon.py -> <snip>/examples/rofi-alt-tab/dbus_window_daemon.pyIf you’re not comfortable symlinking the files or don’t feel like going to the trouble, you can always do a vanilla copy.You’ll also need to expose the script in some way. A generally recommended idea is to store scripts in a common location.$mkdir-p$XDG_CONFIG_HOME/rofi/scripts$cdpath/to/repo/or/package$ln-s$(realpathexamples/rofi-alt-tab/ordered_window_script.py)$XDG_CONFIG_HOME/rofi/scriptsLaunching the Main DaemonRun the following command.$py-rofi-busdaemonstartLaunching the ExampleOnce the files are in theload_fromdirectory and the daemon is running, you’ll have to either add another file or pop open a REPL.$python>>> import pydbus
>>> bus = pydbus.SessionBus()
>>> loader = bus.get('pro.wizardsoftheweb.pyrofibus.daemon.window_properties')
>>> loader.load_apps()
>>> exit()Running the ModiWith the script accessible and the daemons running, you can either execute it as a one-off or add it to your configuration.#Runsitasaone-off$rofi-modialttab:~/.config/rofi/scripts/ordered_window_script.py-showalttab#Addsittotheexistingconfig$exportROFI_CONFIG_FILE=$(rofi--help|awk'BEGIN{ IGNORECASE = 1 };/configuration file/{ print $3; }')$[-f$ROFI_CONFIG_FILE]||rofi-dump-config>$ROFI_CONFIG_FILE$sed\-i=.bak\-e's@\([^-]modi:.*\)";@\1,alttab:~/.config/rofi/scripts/ordered_window_script.py";@g'\$ROFI_CONFIG_FILE$rofi-showalttabConclusionLike its predecessor, this example (and the package it’s from) is still very much in its infancy. Expect things to change. This is too much work to do when Python could it for me.