mirror of
https://github.com/vincentbernat/i3wm-configuration.git
synced 2025-07-01 13:54:22 +02:00
i3-companion: add a companion for i3
This is a Python script implementing what's missing from i3. Currently, it will only rename workspace to add icons with their content.
This commit is contained in:
parent
662db00e36
commit
43ab22dee5
5 changed files with 129 additions and 4 deletions
116
bin/i3-companion
Executable file
116
bin/i3-companion
Executable file
|
@ -0,0 +1,116 @@
|
||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
"""Personal i3 companion
|
||||||
|
|
||||||
|
This script will listen to i3 events and react accordingly. Currently,
|
||||||
|
it will rename workspaces using icons according to their content.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import logging
|
||||||
|
import logging.handlers
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import i3ipc
|
||||||
|
import re
|
||||||
|
|
||||||
|
logger = logging.getLogger(os.path.splitext(os.path.basename(sys.argv[0]))[0])
|
||||||
|
|
||||||
|
|
||||||
|
class CustomFormatter(argparse.RawDescriptionHelpFormatter,
|
||||||
|
argparse.ArgumentDefaultsHelpFormatter):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args(args=sys.argv[1:]):
|
||||||
|
"""Parse arguments."""
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description=sys.modules[__name__].__doc__,
|
||||||
|
formatter_class=CustomFormatter)
|
||||||
|
|
||||||
|
g = parser.add_mutually_exclusive_group()
|
||||||
|
g.add_argument("--debug", "-d", action="store_true",
|
||||||
|
default=False,
|
||||||
|
help="enable debugging")
|
||||||
|
g.add_argument("--silent", "-s", action="store_true",
|
||||||
|
default=False,
|
||||||
|
help="don't log")
|
||||||
|
|
||||||
|
return parser.parse_args(args)
|
||||||
|
|
||||||
|
|
||||||
|
def setup_logging(options):
|
||||||
|
"""Configure logging."""
|
||||||
|
root = logging.getLogger("")
|
||||||
|
root.setLevel(logging.WARNING)
|
||||||
|
logger.setLevel(options.debug and logging.DEBUG or logging.INFO)
|
||||||
|
if not options.silent:
|
||||||
|
ch = logging.StreamHandler()
|
||||||
|
ch.setFormatter(logging.Formatter(
|
||||||
|
"%(levelname)s[%(name)s] %(message)s"))
|
||||||
|
root.addHandler(ch)
|
||||||
|
|
||||||
|
|
||||||
|
# See https://fontawesome.com/v5.15/icons
|
||||||
|
application_icons = {
|
||||||
|
"chromium": "",
|
||||||
|
"emacs": "",
|
||||||
|
"firefox": "",
|
||||||
|
"gimp": "",
|
||||||
|
"google-chrome": "",
|
||||||
|
"spotify": "",
|
||||||
|
"vbeterm": "",
|
||||||
|
"NOMATCH": ""
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def application_icon(window):
|
||||||
|
"""Get application icon for a window."""
|
||||||
|
for attr in ('name', 'window_title',
|
||||||
|
'window_instance', 'window_class'):
|
||||||
|
name = getattr(window, attr, None)
|
||||||
|
if name is None:
|
||||||
|
continue
|
||||||
|
for k, v in application_icons.items():
|
||||||
|
if re.match(k, name, re.IGNORECASE):
|
||||||
|
logger.debug(f"in {attr}, found '{name}', matching {k}")
|
||||||
|
return v
|
||||||
|
return application_icons["NOMATCH"]
|
||||||
|
|
||||||
|
|
||||||
|
def workspace_rename(i3, event):
|
||||||
|
"""Rename workspaces using icons to match what's inside it."""
|
||||||
|
workspaces = i3.get_tree().workspaces()
|
||||||
|
commands = []
|
||||||
|
for workspace in workspaces:
|
||||||
|
icons = set()
|
||||||
|
for window in workspace.leaves():
|
||||||
|
icon = application_icon(window)
|
||||||
|
if icon is not None:
|
||||||
|
icons.add(icon)
|
||||||
|
new_name = f"{workspace.num}:{'|'.join(icons)}"
|
||||||
|
if workspace.name != new_name:
|
||||||
|
logger.debug(f"rename workspace {workspace.num}")
|
||||||
|
command = f'rename workspace "{workspace.name}" to "{new_name}"'
|
||||||
|
commands.append(command)
|
||||||
|
i3.command(';'.join(commands))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
options = parse_args()
|
||||||
|
setup_logging(options)
|
||||||
|
|
||||||
|
try:
|
||||||
|
i3 = i3ipc.Connection()
|
||||||
|
|
||||||
|
# Rename workspace depending on what is inside
|
||||||
|
for event in ('window::move', 'window::new',
|
||||||
|
'window::title', 'window::close'):
|
||||||
|
i3.on(event, workspace_rename)
|
||||||
|
|
||||||
|
i3.main()
|
||||||
|
except Exception as e:
|
||||||
|
logger.exception("%s", e)
|
||||||
|
sys.exit(1)
|
||||||
|
sys.exit(0)
|
|
@ -43,15 +43,15 @@ enable-scroll = false
|
||||||
|
|
||||||
label-mode-background = ${colors.highlight}
|
label-mode-background = ${colors.highlight}
|
||||||
label-mode-padding = 1
|
label-mode-padding = 1
|
||||||
label-focused = %index%
|
label-focused = %name%
|
||||||
label-focused-background = ${colors.highlight}
|
label-focused-background = ${colors.highlight}
|
||||||
label-focused-padding = 1
|
label-focused-padding = 1
|
||||||
label-unfocused = %index%
|
label-unfocused = %name%
|
||||||
label-unfocused-padding = 1
|
label-unfocused-padding = 1
|
||||||
label-visible = %index%
|
label-visible = %name%
|
||||||
label-visible-background = ${self.label-focused-background}
|
label-visible-background = ${self.label-focused-background}
|
||||||
label-visible-padding = ${self.label-focused-padding}
|
label-visible-padding = ${self.label-focused-padding}
|
||||||
label-urgent = %index%
|
label-urgent = %name%
|
||||||
label-urgent-background = #a00000
|
label-urgent-background = #a00000
|
||||||
label-urgent-padding = 1
|
label-urgent-padding = 1
|
||||||
|
|
||||||
|
|
7
dotfiles/systemd/i3-companion.service
Normal file
7
dotfiles/systemd/i3-companion.service
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
[Unit]
|
||||||
|
Description=i3 companion
|
||||||
|
PartOf=graphical-session.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStart=%h/.config/i3/bin/i3-companion
|
||||||
|
Restart=on-failure
|
|
@ -8,6 +8,7 @@ After=tmux.service
|
||||||
After=ssh-agent.service
|
After=ssh-agent.service
|
||||||
Before=wallpaper.service
|
Before=wallpaper.service
|
||||||
Before=polybar.service
|
Before=polybar.service
|
||||||
|
Before=i3-companion.service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
ExecStart=/usr/bin/i3
|
ExecStart=/usr/bin/i3
|
||||||
|
|
|
@ -4,6 +4,7 @@ BindsTo=graphical-session.target
|
||||||
Wants=autorandr.service
|
Wants=autorandr.service
|
||||||
Wants=dunst.socket
|
Wants=dunst.socket
|
||||||
Wants=i3.service
|
Wants=i3.service
|
||||||
|
Wants=i3-companion.service
|
||||||
Wants=inputplug.service
|
Wants=inputplug.service
|
||||||
Wants=misc-x.service
|
Wants=misc-x.service
|
||||||
Wants=picom.service
|
Wants=picom.service
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue