Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

Desktop event listener for minimal window manager users

License

NotificationsYou must be signed in to change notification settings

kriansa/wmcompanion

Repository files navigation

Build your own desktop environment using Python

You use a minimalist tiling window manager, yet you want to be able to tinker with your desktop moreeasily and implement features like the ones available in full blown desktop environments?

More specifically, you want to react to system events (such as returning from sleep, or wifi signalchange) and easily automate your workflow or power your desktop user experience using a consistentand centralized configuration so it isactually easy to maintain?

Show me

See below small examples of the broad idea that iswmcompanion and what you can achieve with smallamounts of code.

  • Send a desktop notification and updates a given module on Polybar whenever a certain connectionmanaged by NetworkManager changes statuses:

    fromwmcompanionimportuse,onfromwmcompanion.modules.polybarimportPolybarfromwmcompanion.modules.notificationsimportNotifyfromwmcompanion.events.networkimportNetworkConnectionStatus@on(NetworkConnectionStatus,connection_name="Wired-Network")@use(Polybar)@use(Notify)asyncdefnetwork_status(status:dict,polybar:Polybar,notify:Notify):color="blue"ifstatus["connected"]else"gray"awaitpolybar("eth",polybar.fmt("eth",color=color))msg="connected"ifstatus["connected"]else"disconnected"awaitnotify(f"Hey, wired network is{msg}")
  • Add a microphone volume level to Polybar:

    fromwmcompanionimportuse,onfromwmcompanion.modules.polybarimportPolybarfromwmcompanion.events.audioimportMainVolumeLevel@on(MainVolumeLevel)@use(Polybar)asyncdefvolume_level(volume:dict,polybar:Polybar):ifnotvolume["input"]["available"]:returnawaitpolybar("mic","")ifnotvolume["muted"]:level=int(volume['level']*100)text=f"[mic:{level}]"color="blue"else:text="[mic: muted]"color="gray"awaitpolybar("mic",polybar.fmt(text,color=color))
  • Set your monitor screen arrangement on plug/unplug events:

    fromwmcompanionimportuse,onfromwmcompanion.modules.notificationsimportNotifyfromwmcompanion.events.x11importDeviceState@on(DeviceState)@use(Notify)asyncdefconfigure_screens(status:dict,notify:Notify):ifstatus["event"]==DeviceState.ChangeEvent.SCREEN_CHANGE:awaitcmd("autorandr")awaitnotify("Screen layout adjusted!")
  • Amore complex example of Polybar widgets powered by wmcompanion, in less than80 lines of code:

    image

Who is this for?

It is initially built for people using tiling window managers that don't have the many of thefeatures that a fullDE provides, but still want some convenience and automation here and therewithout having to rely on lots of unorganized shell scripts running without supervision. Things likebluetooth status notifications, keyboard layout visualizer, volume, network manager status and soon.

If you already have a desktop environment such as GNOME or KDE, this tool is probably not for you,as most of its features are already built-in on those. However, there's absolutely nothing stoppingyou from using it, as it is so flexible you may find it useful for other purposes (such asnotifications, for instance).

Design rationale

You might want to ask: isn't most of that feature set already available on a status bar such asPolybar, for instance? And some of them aren't just a matter of writing a simple shell script?

Generally, yes, but then you will be limited by the features of that status bar and how they areimplemented internally, and have a small room for customization. Ever wanted to have microphonevolume on Polybar? Or akbdd widget? Or a built-indunst pause toggle? You may be well servedwith the default option your status bar provides, but you also might want more out of it and theycan not be as easily customizable or integrate well with, let's say, notifications, for instance.

Moreover,wmcompanion isn't designed to power status bars or simply serve as a notificationdaemon. Instead it is modeled around listening to events and reacting to them. One of thesereactions might be to update a status bar, of course. But it can also be to send a notification, orperhaps change a layout, update your monitor setup, etc. The important part is that it is meant tobe integrated and easily scriptable in a single service, and you won't have to maintain and manuallyorchestrate several scripts to make your desktop experience more pleasant.

Usage

1. Install

Currently it's available as an OS package forArch Linux on AUR. On other platforms, you canpull this repository, installpoetry and runpoetry run wmcompanion.

2. Configure

First, you need to add a config file on~/.config/wmcompanion/config.py. For starters, you can usethe one below:

fromwmcompanionimportuse,onfromwmcompanion.modules.notificationsimportNotifyfromwmcompanion.events.audioimportMainVolumeLevel@on(MainVolumeLevel)@use(Notify)asyncdefvolume_level(volume:dict,notify:Notify):awaitnotify(f"Your volume levels:{volume=}")

Take a look atexamples if you want to get inspired, and you can get really creative byreading the source files underevents folder.

3. Run

You can simply runwmcompanion as it's an executable installed on your system, or usepoetry run wmcompanion in case you downloaded the codebase using git.

Most people already have many user daemons running as part of their.xinit file, and that's afine place for you to run it automatically on user login.

A recommendation is to keep it under asystemd user unit, so it's separate from your windowmanager and you can manage logs and failures a bit better.

Available event listeners

By default,wmcompanion isaccompanied by manyEventListeners already. AnEventListener isthe heart of the application. Yet, they are simple Python classes that can listen to system eventsasynchronously and notify the user configured callbacks whenever there's a change in the state.

Currently there are the following event listeners available:

  • Main audio input/output volume level with WirePlumber (events.audio.MainVolumeLevel)
  • Bluetooth status (events.bluetooth.BluetoothRadioStatus)
  • Kbdd currently selected layout (events.keyboard.KbddChangeLayout)
  • NetworkManager connection status (events.network.NetworkConnectionStatus)
  • NetworkManager Wi-Fi status/strength (events.network.WifiStatus)
  • Dunst notification pause status (events.notifications.DunstPausedStatus)
  • Power actions (events.power.PowerActions)
  • Logind Idle status (events.power.LogindIdleStatus)
  • X11 monitor and input device changes (events.x11.DeviceState)[requires python-xcffib]

The architecture allows for developing event listeners very easily and make them reusable by others,even if they are not integrated in this codebase -- they just need to be classes extendingwmcompanion.event_listening.EventListener and you can even include them in your dotfiles.

Built-in modules

Modules are built-in integrations with the most common desktop tooling so that you don't need toreimplement them for your configurations. All you need is to inject them at runtime and they will beavailable to you automatically, keeping your user configuration clean.

For instance, instead of playing withnotify-send manually, there's a builtin module that you caninvoke from within Python script and it will work as you would expect.

  • Polybar IPC(replacespolybar-msg action) (modules.polybar.Polybar)
  • Notifications(replacesnotify-send) (modules.notifications.Notify)

Polybar IPC integration

In order to use Polybar integration, you need to create a module on Polybar usingcustom/ipc asthe type and then add an initial hook to it so it reads from wmcompanion's module uponinitialization. Here's an example below:

[module/kbdd]type = custom/ipchook-0 = cat $XDG_RUNTIME_DIR/polybar/kbdd 2> /dev/nullinitial = 1

Mind you that, for that example,kbdd must be the first string argument that you pass whencallingpolybar() on a wmcompanion callback:

@use(Polybar)asyncdefmy_callback(status:dict,polybar:Polybar):awaitpolybar("kbdd","any string that will show up on polybar")

Desktop notifications

We have a full implementation of thedesktop notifications spec, and it'ssuper easy to use:

@use(Notify)asyncdefmy_callback(status:dict,notify:Notify):awaitnotify("Summary","Body")

It also provides native support for Dunst-specific behaviors, such as progress bar and colors:

awaitnotify("Volume level",dunst_progress_bar:20)

As always, refer to thesource code if you want more details.

Development

In order to run the daemon in development mode, just run:

$ poetry run wmcompanion

Acknowledgements

License

Apache V2.


[8]ページ先頭

©2009-2025 Movatter.jp