diff --git a/.config/eww/.gitignore b/.config/eww/.gitignore new file mode 100644 index 0000000..c18dd8d --- /dev/null +++ b/.config/eww/.gitignore @@ -0,0 +1 @@ +__pycache__/ diff --git a/.config/eww/modules/system.yuck b/.config/eww/modules/system.yuck index fcc034f..17d3dc4 100644 --- a/.config/eww/modules/system.yuck +++ b/.config/eww/modules/system.yuck @@ -167,8 +167,22 @@ (label :text "MEM" :visible {invert ?: false} :class "special"))) - - + +(defwidget system--swap-gauge-small [?invert] + (box :orientation "v" + :halign "center" + :width 35 + :space-evenly false + :spacing 10 + :class "" + (label :text "SWAP" + :visible {!(invert ?: false)} + :class "special") + (box :halign "center" + (system--small-gauge :value {system--data.swap.percent})) + (label :text "SWAP" + :visible {invert ?: false} + :class "special"))) (defwidget system--cpu-gauge [] (box :orientation "v" diff --git a/.config/eww/modules/volume.yuck b/.config/eww/modules/volume.yuck index 4beaad1..cbf976a 100644 --- a/.config/eww/modules/volume.yuck +++ b/.config/eww/modules/volume.yuck @@ -1,6 +1,6 @@ ;; -*-lisp-*- (deflisten volume--data - `~/.config/eww/scripts/volume.py`) + `~/.config/eww/scripts/volume2.py`) (defwidget volume-h [] (box :orientation "h" @@ -112,4 +112,5 @@ :halign "start" :spacing 10 (volume--gauge :io "out") - (volume--gauge :io "in")))) + (volume--gauge :io "in")) + )) diff --git a/.config/eww/scripts/volume.py b/.config/eww/scripts/volume.py index ae4d9b1..1dcce95 100755 --- a/.config/eww/scripts/volume.py +++ b/.config/eww/scripts/volume.py @@ -8,6 +8,7 @@ import json import sys import os + async def get_values(pulse): sink = None try: @@ -18,27 +19,27 @@ async def get_values(pulse): os.execv(sys.argv[0], sys.argv) sink_result = { "mute": sink.mute == 1, - "volume": int(sink.volume.value_flat * 100 + 0.5) + "volume": int(sink.volume.value_flat * 100 + 0.5), } source_result = { "mute": source.mute == 1, - "volume": int(source.volume.value_flat * 100 + 0.5) - } - result = { - "output": sink_result, - "input": source_result + "volume": int(source.volume.value_flat * 100 + 0.5), } + result = {"output": sink_result, "input": source_result} print(json.dumps(result), flush=True) return result + async def listen(): async with pulsectl_asyncio.PulseAsync("volume-monitor") as pulse: await get_values(pulse) - async for _ in pulse.subscribe_events('all'): + async for _ in pulse.subscribe_events("all"): await get_values(pulse) + async def main(): listen_task = asyncio.create_task(listen()) + loop = asyncio.get_running_loop() for sig in (signal.SIGTERM, signal.SIGHUP, signal.SIGINT): loop.add_signal_handler(sig, listen_task.cancel) @@ -46,5 +47,5 @@ async def main(): with suppress(asyncio.CancelledError): await listen_task -loop = asyncio.get_event_loop() -loop.run_until_complete(main()) + +asyncio.run(main()) diff --git a/.config/eww/scripts/volume2.py b/.config/eww/scripts/volume2.py new file mode 100755 index 0000000..7f4b548 --- /dev/null +++ b/.config/eww/scripts/volume2.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python3 + +from gi import require_version + +require_version("AstalWp", "0.1") +from gi.repository import AstalWp as Wp, Gio, GObject +from gi.events import GLibEventLoopPolicy +import json +import asyncio +import sys +from typing import Any, Literal +from enum import IntEnum +import signal +import math + + +class EndpointBinding: + """Collection of connection IDs for an audio endpoint.""" + + def __init__(self, endpoint: Wp.Endpoint, callback): + self.callback = callback + self.volume_binding = endpoint.connect_after("notify::volume", callback) + self.mute_binding = endpoint.connect_after("notify::mute", callback) + self.id = endpoint.props.id + self.endpoint = endpoint + + def hash(self) -> int: + return self.volume_binding + (self.mute_binding << 32) + + def __del__(self): + """Object cleanup.""" + self.endpoint.disconnect_by_func(self.callback) + + +class AudioController(Gio.Application): + """Audio controller application.""" + + def __init__(self): + super().__init__() + self.wp = Wp.get_default() + self.bindings = dict[int, EndpointBinding]() + + def do_activate(self): + self.wp.connect("ready", self._on_ready) + self.hold() + loop = asyncio.get_running_loop() + for sig in (signal.SIGTERM, signal.SIGHUP, signal.SIGINT): + loop.add_signal_handler(sig, self.release) + + def _on_ready(self, wp: Wp.Wp): + self.audio = self.wp.get_audio() + if len(sys.argv) > 1: + # this is a set-default call, so lets do that. + self.set_default(sys.argv[1], " ".join(sys.argv[2:])) + else: + self.audio.connect("microphone-added", self._device_added) + self.audio.connect("speaker-added", self._device_added) + self.audio.connect("microphone-removed", self._device_removed) + self.audio.connect("speaker-removed", self._device_removed) + + for mic in self.audio.props.microphones: + self.bindings[mic.props.id] = EndpointBinding(mic, self._dump_info) + for sink in self.audio.props.speakers: + self.bindings[sink.props.id] = EndpointBinding(sink, self._dump_info) + + self._dump_info() + + def set_default(self, dir: Literal["in"] | Literal["out"], description: str): + """Set the default source or sink.""" + if dir == "in": + arr: list[Wp.Endpoint] = self.audio.get_microphones() + else: + arr = self.audio.get_speakers() + + for dev in arr: + if dev.get_description() == description: + dev.set_is_default(True) + break + + self.release() + + def _device_added(self, audio: Wp.Audio, device: Wp.Endpoint): + self.bindings[device.props.id] = EndpointBinding(device, self._dump_info) + self._dump_info() + + def _device_removed(self, audio: Wp.Audio, device: Wp.Endpoint): + try: + del self.bindings[device.props.id] + except KeyError: + pass + self._dump_info() + + def _dump_info(self, *args): + asyncio.ensure_future(self._do_dump()) + + async def _do_dump(self): + + json.dump( + { + "inputs": [ + mic.get_description() for mic in self.audio.get_microphones() + ], + "outputs": [ + sink.get_description() for sink in self.audio.get_speakers() + ], + "input": { + "volume": round( + self.audio.get_default_microphone().get_volume() * 100 + ), + "mute": self.audio.get_default_microphone().get_mute(), + }, + "output": { + "volume": round( + self.audio.get_default_speaker().get_volume() * 100 + ), + "mute": self.audio.get_default_speaker().get_mute(), + }, + }, + sys.stdout, + ) + print() + sys.stdout.flush() + + +if __name__ == "__main__": + asyncio.set_event_loop_policy(GLibEventLoopPolicy()) + app = AudioController() + app.run() diff --git a/.config/eww/windows.yuck b/.config/eww/windows.yuck index 0308a9d..37edb32 100644 --- a/.config/eww/windows.yuck +++ b/.config/eww/windows.yuck @@ -39,6 +39,7 @@ (system--cpu-gauge-small) (system--memory-gauge-small) + (system--swap-gauge-small) (system--gpu-gauge-small) (system--vram-gauge-small) (volume-small-gauge) @@ -132,6 +133,7 @@ :class "rightbox" (system--cpu-gauge-small :invert {false}) (system--memory-gauge-small :invert {false}) + (system--swap-gauge-small :invert {false}) (system--gpu-gauge-small :invert {false}) (system--vram-gauge-small :invert {false}) (volume-small-gauge :invert {false}))) diff --git a/.config/sway/config b/.config/sway/config index 16468d3..deb2c9e 100644 --- a/.config/sway/config +++ b/.config/sway/config @@ -22,13 +22,6 @@ set $colorprimary '#815986' set $colorbackground '#2d272f' set $colorwallpaper '#1e1e1e' -## ## -# Configure displays and workspaces # -## ## - -## Set Background -output * bg '#1e1e1e' solid_color - ### ### # Window Management Keybinds # ### ### @@ -216,13 +209,13 @@ for_window [app_id="rg.kde.kwalletd6"] floating enable bindsym $mod+Shift+r reload ## Default Application Launcher -bindsym $mod+Return exec ~/.local/bin/default-application-launcher +bindsym $mod+Return exec ~/.local/lib/voidshell/default-application-launcher ## Terminal Launcher bindsym $mod+Shift+Return exec alacritty msg create-window || alacritty ## Program Menu -bindsym $mod+d exec wofi --show drun +bindsym $mod+d exec systemd-run --user --scope --slice=app-sway.slice --unit=app-sway-wofilaunch-$RANDOM.scope -- wofi --show drun ## Media Controls bindsym --locked { @@ -230,9 +223,9 @@ bindsym --locked { XF86AudioNext exec playerctl next XF86AudioPrev exec playerctl previous - XF86AudioRaiseVolume exec pactl set-sink-volume @DEFUALT_SINK@ +5% - XF86AudioLowerVolume exec pactl set-sink-volume @DEFUALT_SINK@ -5% - XF86AudioMute exec pactl set-sink-mute @DEFUALT_SINK@ toggle + XF86AudioRaiseVolume exec wpctl set-volume @DEFAULT_SINK@ 5%+ + XF86AudioLowerVolume exec wpctl set-volume @DEFAULT_SINK@ 5%- + XF86AudioMute exec wpctl set-mute @DEFAULT_SINK@ toggle } ## Screen Locking @@ -260,7 +253,7 @@ include input.conf # Service Management # ### ### -exec exec ~/.local/bin/sway-session-manager.sh setup +exec exec ~/.local/lib/voidshell/sway-session-manager setup ### ### # Generic Local Configuration # diff --git a/.config/sway/swayidle##class.laptop b/.config/sway/swayidle##class.laptop index 84b95a7..8fd3132 100644 --- a/.config/sway/swayidle##class.laptop +++ b/.config/sway/swayidle##class.laptop @@ -1,8 +1,7 @@ # -*-conf-*- -timeout 10 'pgrep swaylock &> /dev/null && swaymsg output "*" power off' resume 'pgrep swaylock &> /dev/null && swaymsg output "*" power on' -lock 'systemctl --user start swaylock.service' -before-sleep 'loginctl lock-session' -unlock 'pkill -SIGUSR1 swaylock' -idlehint 600 - +timeout 10 'systemctl --user is-active screenlock.service && swaymsg output "*" power off' resume 'systemctl --user is-active screenlock.service && swaymsg output "*" power on' +lock 'systemctl --user start screenlock.service' +before-sleep 'systemctl --user start screenlock.service' +unlock 'systemctl --user stop screenlock.service' +timeout 600 'systemctl --user start idle.target' resume 'systemctl --user stop idle.target' diff --git a/.config/sway/workspaces.json##hostname.gathering-storm b/.config/sway/workspaces.json##hostname.gathering-storm index e1d46dc..fdb8fb3 100644 --- a/.config/sway/workspaces.json##hostname.gathering-storm +++ b/.config/sway/workspaces.json##hostname.gathering-storm @@ -33,6 +33,7 @@ "index": 5, "name": "discord", "exec": "vesktop", + "comms": true, "program_name": "discord" }, { @@ -85,7 +86,7 @@ "index": 13, "name": "comms", "exec": "firefoxpwa", - "args": ["site", "launch", "01K1BG1J945NG7HEKV95M5WM0E", "--protocol"], + "args": ["site", "launch", "01K233XSC3TE6CM7ZQZ1X303JX", "--protocol"], "forking": true, "program_name": "zoom" }, @@ -120,7 +121,7 @@ { "index": 18, "name": "music", - "exec": "feishin", + "exec": "feishin-electron", "program_name": "feishin" }, { @@ -134,6 +135,7 @@ "index": 20, "name": "slack", "exec": "slack", + "comms": true, "program_name": "slack", "args": [ "--enable-features=UseOzonePlatform", @@ -152,6 +154,7 @@ "index": 22, "name": "encrypted comms", "exec": "signal-desktop", + "comms": true, "program_name": "signal", "environ": { "ELECTRON_OZONE_PLATFORM_HINT": "wayland" @@ -177,8 +180,8 @@ { "names": ["eDP-1"], "group": "builtin", - "position": [0, 600], - "eww_windows": { + "position": [0, 1100], + "eww_windows": { "laptopbar": { "battery": "BAT1" }, @@ -189,27 +192,27 @@ }, "mode": "2560x1600@165Hz scale 1.5 color_profile icc /usr/share/color/icc/colord/BOE_CQ_______NE160QDM_NZ6.icm" }, - { - "make": "Dell Inc.", - "model": "DELL U2722D", - "serial": "DV3MGH3", - "group": "left", - "position": [1707, 0], - "mode": "2560x1440 color_profile icc /usr/share/color/icc/colord/DCI-P3.icd", - "eww_windows": { - "desktop-leftbar": {}, - "sidebar": { - "side": "left" - } - } - }, + { + "make": "Dell Inc.", + "model": "DELL U2715H", + "serial": "H7YCC64Q0WFL", + "group": "left", + "position": [1706, 500], + "mode": "2560x1440 color_profile icc /usr/share/color/icc/colord/sRGB.icc", + "eww_windows": { + "desktop-leftbar": {}, + "sidebar": { + "side": "left" + } + } + }, { "make": "Dell Inc.", "model": "DELL U3818DW", "serial": "97F8P9350W0L", "group": "center", - "position": [4267, 0], - "mode": "3840x1600 color_profile icc /usr/share/color/icc/colord/DCI-P3.icd", + "position": [4266, 500], + "mode": "3840x1600 color_profile icc /usr/share/color/icc/colord/sRGB.icc", "eww_windows": ["desktop-mainbar"] }, { @@ -217,8 +220,8 @@ "model": "DELL U2722D", "serial": "5X3MGH3", "group": "right", - "position": [8107, -500], - "mode": "2560x1440 transform 270 color_profile icc /usr/share/color/icc/colord/DCI-P3.icd", + "position": [8106, 0], + "mode": "2560x1440 transform 270 color_profile icc /usr/share/color/icc/colord/sRGB.icc", "eww_windows": { "desktop-rightbar": {}, "vertical-bottombar": {} diff --git a/.config/swaylock/config b/.config/swaylock/config new file mode 100644 index 0000000..e0ca4d1 --- /dev/null +++ b/.config/swaylock/config @@ -0,0 +1,28 @@ +font="Source Sans Pro" +font-size=15 +indicator-radius=80 +indicator-thickness=10 +inside-color=#1e1e1e +inside-clear-color=#1e1e1e +inside-caps-lock-color=#1e1e1e +inside-ver-color=#1e1e1e +inside-wrong-color=#1e1e1e +key-hl-color=#815986 +bs-hl-color=#cf6a4c +caps-lock-key-hl-color=#f9ee98 +caps-lock-bs-hl-color=#cf6a4c +ring-color=#815986 +ring-clear-color=#f9ee98 +ring-caps-lock-color=#f9ee98 +ring-ver-color=#815986 +ring-wrong-color=#cf6a4c +line-uses-inside +text-color=#815986 +text-clear-color=#f9ee98 +text-caps-lock-color=#f9ee98 +text-ver-color=#815986 +text-wrong-color=#cf6a4c +color=#1e1e1e +show-failed-attempts +ignore-empty-password +indicator-idle-visible diff --git a/.config/systemd/.gitignore b/.config/systemd/.gitignore new file mode 100644 index 0000000..2357016 --- /dev/null +++ b/.config/systemd/.gitignore @@ -0,0 +1,3 @@ +/user/*.wants +/user/*.requires +/user/*.upholds diff --git a/.config/systemd/user.conf b/.config/systemd/user.conf new file mode 100644 index 0000000..2b7a484 --- /dev/null +++ b/.config/systemd/user.conf @@ -0,0 +1,2 @@ +[Manager] +DefaultEnvironment=ELECTRON_OZONE_PLATFORM_HINT=wayland diff --git a/.config/systemd/user/alacritty-proxy.service b/.config/systemd/user/alacritty-proxy.service new file mode 100644 index 0000000..de67bbd --- /dev/null +++ b/.config/systemd/user/alacritty-proxy.service @@ -0,0 +1,10 @@ +[Unit] +Description=Socket-activation proxy for alacritty +PartOf=graphical-session.target +BindsTo=alacritty.service +After=alacritty.service + +[Service] +Type=notify +ExecStart=/usr/lib/systemd/systemd-socket-proxyd %t/alacritty-direct +Slice=session.slice diff --git a/.config/systemd/user/alacritty.service b/.config/systemd/user/alacritty.service index 5ee7f99..625d743 100644 --- a/.config/systemd/user/alacritty.service +++ b/.config/systemd/user/alacritty.service @@ -7,7 +7,3 @@ Type=simple ExecStart=alacritty --daemon --socket=%t/alacritty ExecStopPost=rm %t/alacritty Slice=app.slice - -[Install] -WantedBy=graphical-session.target - diff --git a/.config/systemd/user/alacritty.socket b/.config/systemd/user/alacritty.socket new file mode 100644 index 0000000..d8f279d --- /dev/null +++ b/.config/systemd/user/alacritty.socket @@ -0,0 +1,7 @@ +[Unit] +Description=Socket activation for alacritty +PartOf=graphical-session + +[Socket] +ListenStream=%t/alacritty +SocketMode=0600 diff --git a/.config/systemd/user/app-shell.slice b/.config/systemd/user/app-shell.slice new file mode 100644 index 0000000..58f7a7f --- /dev/null +++ b/.config/systemd/user/app-shell.slice @@ -0,0 +1,2 @@ +[Unit] +Description=Command-line shells diff --git a/.config/systemd/user/emacs.socket b/.config/systemd/user/emacs.socket new file mode 100644 index 0000000..bd6fc4c --- /dev/null +++ b/.config/systemd/user/emacs.socket @@ -0,0 +1,6 @@ +[Socket] +ListenStream=%t/emacs/server +DirectoryMode=0700 + +[Install] +WantedBy=sockets.target diff --git a/.config/systemd/user/gpg-agent.service.d/ssh-auth-sock.conf b/.config/systemd/user/gpg-agent.service.d/ssh-auth-sock.conf new file mode 100644 index 0000000..e69de29 diff --git a/.config/systemd/user/gpg-ssh-auth-sock.service b/.config/systemd/user/gpg-ssh-auth-sock.service new file mode 100644 index 0000000..8e7d8a1 --- /dev/null +++ b/.config/systemd/user/gpg-ssh-auth-sock.service @@ -0,0 +1,13 @@ +[Unit] +Description=Ensure the service manager has an SSH agent available -- GnuPG Edition +BindsTo=gpg-agent-ssh.socket +After=gpg-agent-ssh.socket + +[Service] +Type=oneshot +ExecStart= systemctl --user set-environment SSH_AUTH_SOCK=%t/gnupg/S.gpg-agent.ssh +ExecStop=systemctl --user unset-environment SSH_AUTH_SOCK=%t/gnupg/S.gpg-agent.ssh +RemainAfterExit=yes + +[Install] +UpheldBy=gpg-agent-ssh.socket diff --git a/.config/systemd/user/idle.target b/.config/systemd/user/idle.target new file mode 100644 index 0000000..3184c62 --- /dev/null +++ b/.config/systemd/user/idle.target @@ -0,0 +1,12 @@ +[Unit] +Description=Graphical session is idle +# Pull in the idle hint sync service +Wants=logind-idlehint.service +Requisite=graphical-session.target +After=screenlock.service +# Ensure that when the screen unlocks, the session is also no longer considered idle. +StopPropagatedFrom=screenlock.service + +[Install] +# If "enabled", the graphical session will be considered idle while the screen is locked. +WantedBy=screenlock.service diff --git a/.config/systemd/user/kdeconnect.service b/.config/systemd/user/kdeconnect.service index ec0cf3b..3901d4f 100644 --- a/.config/systemd/user/kdeconnect.service +++ b/.config/systemd/user/kdeconnect.service @@ -4,10 +4,10 @@ Description=KDE Connect Daemon [Service] Type=simple ExecStart=/usr/bin/kdeconnectd -Restart=on-failed +Restart=on-failure RestartSec=5s RestartMaxDelaySec=1min RestartSteps=5 [Install] -WantedBy=sway-session.target +WantedBy=graphical-session.target diff --git a/.config/systemd/user/logind-idlehint.service b/.config/systemd/user/logind-idlehint.service new file mode 100644 index 0000000..84ea7b6 --- /dev/null +++ b/.config/systemd/user/logind-idlehint.service @@ -0,0 +1,15 @@ +# This unit is pulled in by idle.target, but can be masked if idle hinting is not desired or if another program already provides it. +# This service should be preferred to other mechanisms, however, as it provides synchronization between the idle target and the logind +# state, which is generally what is desired. + +[Unit] +Description=systemd-logind idle hint synchronization +PartOf=idle.target +Before=idle.target + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=busctl call org.freedesktop.login1 /org/freedesktop/login1/session/auto org.freedesktop.login1.Session SetIdleHint b 1 +ExecStop=busctl call org.freedesktop.login1 /org/freedesktop/login1/session/auto org.freedesktop.login1.Session SetIdleHint b 0 +StopWhenUnneeded=yes diff --git a/.config/systemd/user/logind-lockhint.service b/.config/systemd/user/logind-lockhint.service new file mode 100644 index 0000000..abf5bb2 --- /dev/null +++ b/.config/systemd/user/logind-lockhint.service @@ -0,0 +1,15 @@ +# This unit should be pulled in by a provider of screenlock.service if the service doesn't provide lock hinting support natively. + +[Unit] +Description=systemd-logind lock hint synchronization +PartOf=screenlock.service +After=screenlock.service +StopWhenUnneeded=yes +RefuseManualStart=yes +RefuseManualStop=yes + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=busctl call org.freedesktop.login1 /org/freedesktop/login1/session/auto org.freedesktop.login1.Session SetLockedHint b 1 +ExecStop=busctl call org.freedesktop.login1 /org/freedesktop/login1/session/auto org.freedesktop.login1.Session SetLockedHint b 0 diff --git a/.config/systemd/user/pipewire-session-manager.service b/.config/systemd/user/pipewire-session-manager.service new file mode 120000 index 0000000..a092a62 --- /dev/null +++ b/.config/systemd/user/pipewire-session-manager.service @@ -0,0 +1 @@ +/usr/lib/systemd/user/wireplumber.service \ No newline at end of file diff --git a/.config/systemd/user/screenlock.service b/.config/systemd/user/screenlock.service new file mode 120000 index 0000000..969751b --- /dev/null +++ b/.config/systemd/user/screenlock.service @@ -0,0 +1 @@ +/home/ezri/.config/systemd/user/swaylock.service \ No newline at end of file diff --git a/.config/systemd/user/screenlock.target b/.config/systemd/user/screenlock.target new file mode 100644 index 0000000..9dd5c14 --- /dev/null +++ b/.config/systemd/user/screenlock.target @@ -0,0 +1,7 @@ +[Unit] +Description=Screen locked +Requires=screenlock.service +After=screenlock.service +Requisite=graphical-session.target +AllowIsolate=yes +StopPropagatedFrom=screenlock.service diff --git a/.config/systemd/user/suspend-comms-apps.service b/.config/systemd/user/suspend-comms-apps.service new file mode 100644 index 0000000..f0a9e44 --- /dev/null +++ b/.config/systemd/user/suspend-comms-apps.service @@ -0,0 +1,14 @@ +[Unit] +Description=Suspend comms apps +PartOf=idle.target +Before=idle.target + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=bash -c 'echo 1 > /sys/fs/cgroup/user.slice/user-%U.slice/user@%U.service/app.slice/app-comms.slice/cgroup.freeze' +ExecStop=bash -c 'echo 0 > /sys/fs/cgroup/user.slice/user-%U.slice/user@%U.service/app.slice/app-comms.slice/cgroup.freeze' +StopWhenUnneeded=yes + +[Install] +WantedBy=idle.target diff --git a/.config/systemd/user/sway-env-cleanup.service b/.config/systemd/user/sway-env-cleanup.service new file mode 100644 index 0000000..b4fb7fa --- /dev/null +++ b/.config/systemd/user/sway-env-cleanup.service @@ -0,0 +1,9 @@ +[Unit] +Description=Clean up sway session enviornment variables from the service manager + +[Service] +Type=oneshot +ExecStart=%h/.local/bin/sway-session-manager.sh + +[Install] +WantedBy=sway-session-shutdown.target diff --git a/.config/systemd/user/sway-session.target b/.config/systemd/user/sway-session.target index 72757c5..7940487 100644 --- a/.config/systemd/user/sway-session.target +++ b/.config/systemd/user/sway-session.target @@ -4,5 +4,6 @@ Documentation=man:systemd.special(7) BindsTo=graphical-session.target Wants=sway-session-pre.target After=sway-session-pre.target +Requisite=sway.scope RefuseManualStart=yes StopWhenUnneeded=yes diff --git a/.config/systemd/user/swaylock.service b/.config/systemd/user/swaylock.service new file mode 100644 index 0000000..7bb779a --- /dev/null +++ b/.config/systemd/user/swaylock.service @@ -0,0 +1,13 @@ +[Unit] +Description=Sway screen locker +Documentation=man:swaylock(1) +Requisite=sway-session.target +Wants=logind-lockhint.service + +[Service] +Type=forking +ExecStart=swaylock -f +KillSignal=SIGUSR1 + +[Install] +Alias=screenlock.service diff --git a/.config/systemd/user/wallpaper.service b/.config/systemd/user/wallpaper.service new file mode 100644 index 0000000..edb7fa0 --- /dev/null +++ b/.config/systemd/user/wallpaper.service @@ -0,0 +1,13 @@ +[Unit] +Description=Wallpaper daemon +PartOf=sway-session.target +Before=sway-session.target + +[Service] +Type=notify-reload +ExecStart=%h/.local/lib/voidshell/background +NotifyAccess=all +Scope=background.scope + +[Install] +WantedBy=sway-session.target diff --git a/.config/systemd/user/zsh-.scope.d/resource-limits.conf b/.config/systemd/user/zsh-.scope.d/resource-limits.conf new file mode 100644 index 0000000..89c5da7 --- /dev/null +++ b/.config/systemd/user/zsh-.scope.d/resource-limits.conf @@ -0,0 +1,9 @@ +[Scope] +CPUAccounting=yes +MemoryAccounting=yes + +CPUQuota=400% +MemoryHigh=1G +MemoryMax=8G + +TasksMax=100 diff --git a/.config/zsh/00-scope.zsh b/.config/zsh/00-scope.zsh new file mode 100644 index 0000000..1e03932 --- /dev/null +++ b/.config/zsh/00-scope.zsh @@ -0,0 +1,125 @@ +# Move shell into its own systemd scope if possible +if ! hash busctl &>/dev/null; then + return +fi +if [[ $_scope_loaded == 1 ]]; then + # shell reload, ignore + return +fi + +_scope_loaded=1 + +# Load new scope unit +busctl -q --user call org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager StartTransientUnit 'ssa(sv)a(sa(sv))' \ + "zsh-$$.scope" \ + replace \ + 2 \ + PIDs au 1 $$ \ + Slice s app-shell.slice \ + 0 \ + &>/dev/null # this call fails on a shell re-exec, but we don't really care since the scope will already exist in that case, so forward to dave null + +# Set and unset variables +export INVOCATION_ID=$(systemctl --user show "zsh-$$.scope" | awk -F= '/InvocationID=/{ print $2 }') +export SYSTEMD_UNIT="zsh-$$.scope" +unset SYSTEMD_EXEC_PID +unset JOURNAL_STREAM +unset MEMORY_PRESSURE_WATCH +unset MEMORY_PRESSURE_WRITE + + +function lift-constraints() { + # Don't lift the PID constraint since there's really no reason a shell should have more than 100 active PIDs. Reap what you sow! + env systemctl --user set-property zsh-$$.scope CPUQuota= + env systemctl --user set-property zsh-$$.scope MemoryHigh= + env systemctl --user set-property zsh-$$.scope MemoryMax= +} + +function detach() { + local args slice type unit passed_envs + passed_envs=(SUDO_PROMPT EDITOR SUDO_EDITOR VISUAL) + args=(--user --same-dir -q --collect) + slice="app-shell.slice" + type="scope" + while true; do + case $1 in + --help) + cat < [ARGS...] + +Invoke a command outside of the shell's execution context, detaching it from any resource +constraints on the shell itself. + +It will be run by the user service manager using systemd-run. By default, the command +is invoked in the app-shells slice, so is still subject to any restrictions placed +on the set of all user shells. + +This is intended to simplify the act of intentionally launching resource-intensive programs +(such as compilers) while still limiting most programs in terms of how much they are allowed +to consume. + +Some environment variables are inherited by default, which can be disabled with --clean-env: + $passed_envs + +OPTIONS: + --help - display this help message and exit + --app - launch in the main app slice rather than the app-shell slice. Equivalent to --slice app.slice + --background - launch in the background slice. Equivalent to --slice background.slice + --slice - launch in the specified user slice. + --service - create a service unit, rather than a scope, producing a truly clean environment. This separates + the program from the shell's process tree, preventing the shell's job management from managing it. + --env [=VALUE] - inherit the given environment variable from the shell, or set it if a value is given. + --clean-env - disable the default set of inherited environment variables +EOF + return 0 + ;; + --app) + slice="app.slice" + ;; + --background) + slice="background.slice" + ;; + --slice) + slice=$2 + shift + ;; + --service) + type="service" + ;; + --env) + args+="-E" + args+=$2 + shift + ;; + --clean-env) + passed_envs=() + ;; + *) + break + ;; + esac + shift + done + for env in $passed_envs; do + args+="--setenv=$env" + done + case $type in + service) + unit="invoke-$$@$RANDOM.service" + args+=(--service-type=exec --pty --pipe --wait) + ;; + scope) + unit="invoke-$$-$RANDOM.scope" + args+="--scope" + ;; + esac + systemd-run $args --slice=${slice} --unit=${unit} -- "$@" +} + +for cmd in $always_detach; do + eval $(echo alias $cmd=\"detach $cmd\") +done + +for cmd in $always_detach_as_service; do + eval $(echo alias $cmd=\"detach --service $cmd\") +done diff --git a/.config/zsh/alias.zsh b/.config/zsh/alias.zsh index b0925bd..baf809a 100644 --- a/.config/zsh/alias.zsh +++ b/.config/zsh/alias.zsh @@ -9,9 +9,6 @@ alias ujournalctl="\journalctl --user" alias reconf="exec zsh" -# Use safeexec to ensure we are using system binaries (required for some AUR packages) -alias paru="safeexec paru" - alias cp="cp -i --reflink=auto" alias mv="mv -i" alias rm="rm -i" @@ -25,13 +22,13 @@ alias l='ls -lh' alias nuke='echo "Are you sure?"; read -q && rm -rf' -alias sudo='sudo ' - alias sign='gpg --sign-with ezri@ezri.dev --detach-sign' alias verify='gpg --verify' alias dnslookup="resolvectl query" +alias ip="ip -color=auto" + alias bw-personal='BITWARDENCLI_APPDATA_DIR=~/.config/bw-cli-personal bw' function didifuckingstutter { diff --git a/.config/zsh/zz-plugins.zsh b/.config/zsh/zz-plugins.zsh index 69117ea..e4fd31c 100644 --- a/.config/zsh/zz-plugins.zsh +++ b/.config/zsh/zz-plugins.zsh @@ -1,8 +1,13 @@ +PLUGINS=(zsh-autosuggestions zsh-syntax-highlighting zsh-history-substring-search) + if ! [[ $TERM == "dumb" ]]; then # Don't load if running in dumb terminal, they don't work properly - source $HOME/.local/lib/zsh/zsh-autosuggestions/zsh-autosuggestions.zsh - source $HOME/.local/lib/zsh/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh - source $HOME/.local/lib/zsh/zsh-history-substring-search/zsh-history-substring-search.zsh + for plugin in $PLUGINS; do + source $HOME/.local/lib/zsh/$plugin/$plugin.zsh + done + + ZSH_HIGHLIGHT_STYLES[single-hyphen-option]="fg=blue" + ZSH_HIGHLIGHT_STYLES[double-hyphen-option]="fg=blue" bindkey '^P' history-substring-search-up bindkey '^N' history-substring-search-down diff --git a/.emacs.d/settings.org b/.emacs.d/settings.org index 9a1ffa5..fe11539 100644 --- a/.emacs.d/settings.org +++ b/.emacs.d/settings.org @@ -19,7 +19,7 @@ Shamelessly stole this from Simponic, made some modifications myself. #+END_SRC ** Configure some packages for internal only #+BEGIN_SRC emacs-lisp - (use-package eldoc :straight (:type built-in)) + ;; (use-package eldoc :straight (:type built-in)) (use-package flymake :straight (:type built-in)) #+END_SRC * General emacs diff --git a/.local/lib/voidshell/background b/.local/lib/voidshell/background new file mode 100755 index 0000000..fe0420c --- /dev/null +++ b/.local/lib/voidshell/background @@ -0,0 +1,70 @@ +#!/usr/bin/env zsh + +function parse-config { + + if ! [[ -f $HOME/.config/wallpaper/config ]]; then + echo "<3>Config does not exist, cannot operate." + return 6 + fi + + source $HOME/.config/wallpaper/config + + args=() + + good_config=0 + + if (( ${+COLOR} )); then + args+="--color=${COLOR}" + good_config=1 + fi + if (( ${+IMAGE} )); then + args+="--image=$IMAGE" + good_config=1 + fi + if (( ${+MODE} )); then + args+="--mode=$MODE" + fi + + if ! [[ $good_config == '1' ]]; then + echo "<3>Missing COLOR or IMAGE, cannot set background." + return 78 + fi + +} + +function reload { + systemd-notify --reloading --status "Reloading config" + parse-config + if (( ? != 0 )); then + echo "<3>Reload failed" + systemd-notify --ready --status "Config reload failed. Background not changed." + return + fi + kill $swaybg_pid + # now will return to the while loop where swaybg will be relaunched with new $args +} + +function stop { + systemd-notify --stopping + should_exit=1 + kill $swaybg_pid +} + +parse-config +exit_code=$? + +if (( exit_code )); then + exit $exit_code +fi + +trap reload 1 +trap stop 15 + +should_exit=0 +while [[ $should_exit == 0 ]]; do + (swaybg --output='*' $args |& awk -F" " '{$1="<6>"; $2=""; $3=""; print}') & + swaybg_pid=$! + sleep 0.1 + systemd-notify --ready --status "Background set" + wait +done diff --git a/.local/bin/default-application-launcher b/.local/lib/voidshell/default-application-launcher similarity index 85% rename from .local/bin/default-application-launcher rename to .local/lib/voidshell/default-application-launcher index f8153ce..1b297d8 100755 --- a/.local/bin/default-application-launcher +++ b/.local/lib/voidshell/default-application-launcher @@ -17,6 +17,7 @@ memory_profile=$(echo $ws_data | jq '.memory_profile' -c) run_with_systemd=$(echo $ws_data | jq '.systemd' -r) is_forking=$(echo $ws_data | jq '.forking' -r) void_output=$(echo $ws_data | jq '.void_output' -r) +is_comms=$(echo $ws_data | jq '.comms' -r) IFS=$'\n' environ=($(echo $ws_data | jq '.environ | to_entries | map("\(.key)=\(.value|tostring)") | .[]' -r 2>/dev/null)) IFS=$'\n' args=($(echo $ws_data | jq '.args | .[]' -r 2>/dev/null)) @@ -40,6 +41,15 @@ if [[ $program == "" ]]; then exec error fi +slice=app-sway.slice + +if [[ $is_comms == 'true' ]]; then + # This is to create a cgroup containing all our comms apps that can be suspended in one go + # when the session goes idle, preventing the issue that happens where notifications go to a system + # that's always on. + slice=app-comms.slice +fi + echo "Launching application $program" if [[ $environ != "" ]]; then @@ -69,7 +79,7 @@ if [[ $void_output == "true" ]]; then # difficult to use. exec >/dev/null exec 2>/dev/null - exec systemd-run --user $=systemd_run_args --scope --unit="app-sway-$program_name-$RANDOM" --description="$program_name" --slice=app-sway.slice -- $=program + exec systemd-run --user $=systemd_run_args --scope --unit="app-sway-$program_name-$RANDOM" --description="$program_name" --slice=$slice -- $=program fi if [[ $run_with_systemd == "false" ]]; then @@ -81,6 +91,6 @@ fi # Run programs using systemd-run to allow for resource control and capture of stdout/stderr # We're using a service instead of a scope because scopes don't capture output, and wrapping # the call in systemd-cat is more complicated than just using a service. -echo exec systemd-run --user $=systemd_run_args --unit="app-sway-$program_name@$RANDOM" --description="$program_name" --slice=app-sway.slice -- $program $args +echo exec systemd-run --user $=systemd_run_args --unit="app-sway-$program_name@$RANDOM" --description="$program_name" --slice=$slice -- $program $args -exec systemd-run --user $=systemd_run_args --unit="app-sway-$program_name@$RANDOM" --description="$program_name" --slice=app-sway.slice -- $program $args +exec systemd-run --user $=systemd_run_args --unit="app-sway-$program_name@$RANDOM" --description="$program_name" --slice=$slice -- $program $args diff --git a/.local/bin/sway-session-manager.sh b/.local/lib/voidshell/sway-session-manager similarity index 100% rename from .local/bin/sway-session-manager.sh rename to .local/lib/voidshell/sway-session-manager diff --git a/.local/lib/zsh/zsh-syntax-highlighting b/.local/lib/zsh/zsh-syntax-highlighting index c7caf57..5eb677b 160000 --- a/.local/lib/zsh/zsh-syntax-highlighting +++ b/.local/lib/zsh/zsh-syntax-highlighting @@ -1 +1 @@ -Subproject commit c7caf57ca805abd54f11f756fda6395dd4187f8a +Subproject commit 5eb677bb0fa9a3e60f0eff031dc13926e093df92 diff --git a/.zshrc b/.zshrc index bf28d25..5754910 100644 --- a/.zshrc +++ b/.zshrc @@ -1,6 +1,9 @@ # Execute additional config scripts autoload colors; colors +always_detach=(make) +always_detach_as_service=(paru makepkg) + for file in $HOME/.config/zsh/*.zsh; do if (( ${+LC_DEBUG} )); then echo "running $file"; fi source $file