systemdify all the things

This commit is contained in:
Ezri Brimhall 2025-09-03 11:05:45 -06:00
parent 49d6627ee1
commit 58c5211d19
Signed by: ezri
GPG Key ID: 058A78E5680C6F24
41 changed files with 602 additions and 73 deletions

1
.config/eww/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
__pycache__/

View File

@ -168,7 +168,21 @@
:visible {invert ?: false} :visible {invert ?: false}
:class "special"))) :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 [] (defwidget system--cpu-gauge []
(box :orientation "v" (box :orientation "v"

View File

@ -1,6 +1,6 @@
;; -*-lisp-*- ;; -*-lisp-*-
(deflisten volume--data (deflisten volume--data
`~/.config/eww/scripts/volume.py`) `~/.config/eww/scripts/volume2.py`)
(defwidget volume-h [] (defwidget volume-h []
(box :orientation "h" (box :orientation "h"
@ -112,4 +112,5 @@
:halign "start" :halign "start"
:spacing 10 :spacing 10
(volume--gauge :io "out") (volume--gauge :io "out")
(volume--gauge :io "in")))) (volume--gauge :io "in"))
))

View File

@ -8,6 +8,7 @@ import json
import sys import sys
import os import os
async def get_values(pulse): async def get_values(pulse):
sink = None sink = None
try: try:
@ -18,27 +19,27 @@ async def get_values(pulse):
os.execv(sys.argv[0], sys.argv) os.execv(sys.argv[0], sys.argv)
sink_result = { sink_result = {
"mute": sink.mute == 1, "mute": sink.mute == 1,
"volume": int(sink.volume.value_flat * 100 + 0.5) "volume": int(sink.volume.value_flat * 100 + 0.5),
} }
source_result = { source_result = {
"mute": source.mute == 1, "mute": source.mute == 1,
"volume": int(source.volume.value_flat * 100 + 0.5) "volume": int(source.volume.value_flat * 100 + 0.5),
}
result = {
"output": sink_result,
"input": source_result
} }
result = {"output": sink_result, "input": source_result}
print(json.dumps(result), flush=True) print(json.dumps(result), flush=True)
return result return result
async def listen(): async def listen():
async with pulsectl_asyncio.PulseAsync("volume-monitor") as pulse: async with pulsectl_asyncio.PulseAsync("volume-monitor") as pulse:
await get_values(pulse) await get_values(pulse)
async for _ in pulse.subscribe_events('all'): async for _ in pulse.subscribe_events("all"):
await get_values(pulse) await get_values(pulse)
async def main(): async def main():
listen_task = asyncio.create_task(listen()) listen_task = asyncio.create_task(listen())
loop = asyncio.get_running_loop()
for sig in (signal.SIGTERM, signal.SIGHUP, signal.SIGINT): for sig in (signal.SIGTERM, signal.SIGHUP, signal.SIGINT):
loop.add_signal_handler(sig, listen_task.cancel) loop.add_signal_handler(sig, listen_task.cancel)
@ -46,5 +47,5 @@ async def main():
with suppress(asyncio.CancelledError): with suppress(asyncio.CancelledError):
await listen_task await listen_task
loop = asyncio.get_event_loop()
loop.run_until_complete(main()) asyncio.run(main())

128
.config/eww/scripts/volume2.py Executable file
View File

@ -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()

View File

@ -39,6 +39,7 @@
(system--cpu-gauge-small) (system--cpu-gauge-small)
(system--memory-gauge-small) (system--memory-gauge-small)
(system--swap-gauge-small)
(system--gpu-gauge-small) (system--gpu-gauge-small)
(system--vram-gauge-small) (system--vram-gauge-small)
(volume-small-gauge) (volume-small-gauge)
@ -132,6 +133,7 @@
:class "rightbox" :class "rightbox"
(system--cpu-gauge-small :invert {false}) (system--cpu-gauge-small :invert {false})
(system--memory-gauge-small :invert {false}) (system--memory-gauge-small :invert {false})
(system--swap-gauge-small :invert {false})
(system--gpu-gauge-small :invert {false}) (system--gpu-gauge-small :invert {false})
(system--vram-gauge-small :invert {false}) (system--vram-gauge-small :invert {false})
(volume-small-gauge :invert {false}))) (volume-small-gauge :invert {false})))

View File

@ -22,13 +22,6 @@ set $colorprimary '#815986'
set $colorbackground '#2d272f' set $colorbackground '#2d272f'
set $colorwallpaper '#1e1e1e' set $colorwallpaper '#1e1e1e'
## ##
# Configure displays and workspaces #
## ##
## Set Background
output * bg '#1e1e1e' solid_color
### ### ### ###
# Window Management Keybinds # # Window Management Keybinds #
### ### ### ###
@ -216,13 +209,13 @@ for_window [app_id="rg.kde.kwalletd6"] floating enable
bindsym $mod+Shift+r reload bindsym $mod+Shift+r reload
## Default Application Launcher ## Default Application Launcher
bindsym $mod+Return exec ~/.local/bin/default-application-launcher bindsym $mod+Return exec ~/.local/lib/voidshell/default-application-launcher
## Terminal Launcher ## Terminal Launcher
bindsym $mod+Shift+Return exec alacritty msg create-window || alacritty bindsym $mod+Shift+Return exec alacritty msg create-window || alacritty
## Program Menu ## 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 ## Media Controls
bindsym --locked { bindsym --locked {
@ -230,9 +223,9 @@ bindsym --locked {
XF86AudioNext exec playerctl next XF86AudioNext exec playerctl next
XF86AudioPrev exec playerctl previous XF86AudioPrev exec playerctl previous
XF86AudioRaiseVolume exec pactl set-sink-volume @DEFUALT_SINK@ +5% XF86AudioRaiseVolume exec wpctl set-volume @DEFAULT_SINK@ 5%+
XF86AudioLowerVolume exec pactl set-sink-volume @DEFUALT_SINK@ -5% XF86AudioLowerVolume exec wpctl set-volume @DEFAULT_SINK@ 5%-
XF86AudioMute exec pactl set-sink-mute @DEFUALT_SINK@ toggle XF86AudioMute exec wpctl set-mute @DEFAULT_SINK@ toggle
} }
## Screen Locking ## Screen Locking
@ -260,7 +253,7 @@ include input.conf
# Service Management # # Service Management #
### ### ### ###
exec exec ~/.local/bin/sway-session-manager.sh setup exec exec ~/.local/lib/voidshell/sway-session-manager setup
### ### ### ###
# Generic Local Configuration # # Generic Local Configuration #

View File

@ -1,8 +1,7 @@
# -*-conf-*- # -*-conf-*-
timeout 10 'pgrep swaylock &> /dev/null && swaymsg output "*" power off' resume 'pgrep swaylock &> /dev/null && swaymsg output "*" power on' 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 swaylock.service' lock 'systemctl --user start screenlock.service'
before-sleep 'loginctl lock-session' before-sleep 'systemctl --user start screenlock.service'
unlock 'pkill -SIGUSR1 swaylock' unlock 'systemctl --user stop screenlock.service'
idlehint 600 timeout 600 'systemctl --user start idle.target' resume 'systemctl --user stop idle.target'

View File

@ -33,6 +33,7 @@
"index": 5, "index": 5,
"name": "discord", "name": "discord",
"exec": "vesktop", "exec": "vesktop",
"comms": true,
"program_name": "discord" "program_name": "discord"
}, },
{ {
@ -85,7 +86,7 @@
"index": 13, "index": 13,
"name": "comms", "name": "comms",
"exec": "firefoxpwa", "exec": "firefoxpwa",
"args": ["site", "launch", "01K1BG1J945NG7HEKV95M5WM0E", "--protocol"], "args": ["site", "launch", "01K233XSC3TE6CM7ZQZ1X303JX", "--protocol"],
"forking": true, "forking": true,
"program_name": "zoom" "program_name": "zoom"
}, },
@ -120,7 +121,7 @@
{ {
"index": 18, "index": 18,
"name": "music", "name": "music",
"exec": "feishin", "exec": "feishin-electron",
"program_name": "feishin" "program_name": "feishin"
}, },
{ {
@ -134,6 +135,7 @@
"index": 20, "index": 20,
"name": "slack", "name": "slack",
"exec": "slack", "exec": "slack",
"comms": true,
"program_name": "slack", "program_name": "slack",
"args": [ "args": [
"--enable-features=UseOzonePlatform", "--enable-features=UseOzonePlatform",
@ -152,6 +154,7 @@
"index": 22, "index": 22,
"name": "encrypted comms", "name": "encrypted comms",
"exec": "signal-desktop", "exec": "signal-desktop",
"comms": true,
"program_name": "signal", "program_name": "signal",
"environ": { "environ": {
"ELECTRON_OZONE_PLATFORM_HINT": "wayland" "ELECTRON_OZONE_PLATFORM_HINT": "wayland"
@ -177,7 +180,7 @@
{ {
"names": ["eDP-1"], "names": ["eDP-1"],
"group": "builtin", "group": "builtin",
"position": [0, 600], "position": [0, 1100],
"eww_windows": { "eww_windows": {
"laptopbar": { "laptopbar": {
"battery": "BAT1" "battery": "BAT1"
@ -191,11 +194,11 @@
}, },
{ {
"make": "Dell Inc.", "make": "Dell Inc.",
"model": "DELL U2722D", "model": "DELL U2715H",
"serial": "DV3MGH3", "serial": "H7YCC64Q0WFL",
"group": "left", "group": "left",
"position": [1707, 0], "position": [1706, 500],
"mode": "2560x1440 color_profile icc /usr/share/color/icc/colord/DCI-P3.icd", "mode": "2560x1440 color_profile icc /usr/share/color/icc/colord/sRGB.icc",
"eww_windows": { "eww_windows": {
"desktop-leftbar": {}, "desktop-leftbar": {},
"sidebar": { "sidebar": {
@ -208,8 +211,8 @@
"model": "DELL U3818DW", "model": "DELL U3818DW",
"serial": "97F8P9350W0L", "serial": "97F8P9350W0L",
"group": "center", "group": "center",
"position": [4267, 0], "position": [4266, 500],
"mode": "3840x1600 color_profile icc /usr/share/color/icc/colord/DCI-P3.icd", "mode": "3840x1600 color_profile icc /usr/share/color/icc/colord/sRGB.icc",
"eww_windows": ["desktop-mainbar"] "eww_windows": ["desktop-mainbar"]
}, },
{ {
@ -217,8 +220,8 @@
"model": "DELL U2722D", "model": "DELL U2722D",
"serial": "5X3MGH3", "serial": "5X3MGH3",
"group": "right", "group": "right",
"position": [8107, -500], "position": [8106, 0],
"mode": "2560x1440 transform 270 color_profile icc /usr/share/color/icc/colord/DCI-P3.icd", "mode": "2560x1440 transform 270 color_profile icc /usr/share/color/icc/colord/sRGB.icc",
"eww_windows": { "eww_windows": {
"desktop-rightbar": {}, "desktop-rightbar": {},
"vertical-bottombar": {} "vertical-bottombar": {}

28
.config/swaylock/config Normal file
View File

@ -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

3
.config/systemd/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/user/*.wants
/user/*.requires
/user/*.upholds

View File

@ -0,0 +1,2 @@
[Manager]
DefaultEnvironment=ELECTRON_OZONE_PLATFORM_HINT=wayland

View File

@ -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

View File

@ -7,7 +7,3 @@ Type=simple
ExecStart=alacritty --daemon --socket=%t/alacritty ExecStart=alacritty --daemon --socket=%t/alacritty
ExecStopPost=rm %t/alacritty ExecStopPost=rm %t/alacritty
Slice=app.slice Slice=app.slice
[Install]
WantedBy=graphical-session.target

View File

@ -0,0 +1,7 @@
[Unit]
Description=Socket activation for alacritty
PartOf=graphical-session
[Socket]
ListenStream=%t/alacritty
SocketMode=0600

View File

@ -0,0 +1,2 @@
[Unit]
Description=Command-line shells

View File

@ -0,0 +1,6 @@
[Socket]
ListenStream=%t/emacs/server
DirectoryMode=0700
[Install]
WantedBy=sockets.target

View File

@ -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

View File

@ -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

View File

@ -4,10 +4,10 @@ Description=KDE Connect Daemon
[Service] [Service]
Type=simple Type=simple
ExecStart=/usr/bin/kdeconnectd ExecStart=/usr/bin/kdeconnectd
Restart=on-failed Restart=on-failure
RestartSec=5s RestartSec=5s
RestartMaxDelaySec=1min RestartMaxDelaySec=1min
RestartSteps=5 RestartSteps=5
[Install] [Install]
WantedBy=sway-session.target WantedBy=graphical-session.target

View File

@ -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

View File

@ -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

View File

@ -0,0 +1 @@
/usr/lib/systemd/user/wireplumber.service

View File

@ -0,0 +1 @@
/home/ezri/.config/systemd/user/swaylock.service

View File

@ -0,0 +1,7 @@
[Unit]
Description=Screen locked
Requires=screenlock.service
After=screenlock.service
Requisite=graphical-session.target
AllowIsolate=yes
StopPropagatedFrom=screenlock.service

View File

@ -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

View File

@ -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

View File

@ -4,5 +4,6 @@ Documentation=man:systemd.special(7)
BindsTo=graphical-session.target BindsTo=graphical-session.target
Wants=sway-session-pre.target Wants=sway-session-pre.target
After=sway-session-pre.target After=sway-session-pre.target
Requisite=sway.scope
RefuseManualStart=yes RefuseManualStart=yes
StopWhenUnneeded=yes StopWhenUnneeded=yes

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,9 @@
[Scope]
CPUAccounting=yes
MemoryAccounting=yes
CPUQuota=400%
MemoryHigh=1G
MemoryMax=8G
TasksMax=100

125
.config/zsh/00-scope.zsh Normal file
View File

@ -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 <<EOF
Usage: detach [OPTIONS] <COMMAND> [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 <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 <VAR>[=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

View File

@ -9,9 +9,6 @@ alias ujournalctl="\journalctl --user"
alias reconf="exec zsh" 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 cp="cp -i --reflink=auto"
alias mv="mv -i" alias mv="mv -i"
alias rm="rm -i" alias rm="rm -i"
@ -25,13 +22,13 @@ alias l='ls -lh'
alias nuke='echo "Are you sure?"; read -q && rm -rf' alias nuke='echo "Are you sure?"; read -q && rm -rf'
alias sudo='sudo '
alias sign='gpg --sign-with ezri@ezri.dev --detach-sign' alias sign='gpg --sign-with ezri@ezri.dev --detach-sign'
alias verify='gpg --verify' alias verify='gpg --verify'
alias dnslookup="resolvectl query" alias dnslookup="resolvectl query"
alias ip="ip -color=auto"
alias bw-personal='BITWARDENCLI_APPDATA_DIR=~/.config/bw-cli-personal bw' alias bw-personal='BITWARDENCLI_APPDATA_DIR=~/.config/bw-cli-personal bw'
function didifuckingstutter { function didifuckingstutter {

View File

@ -1,8 +1,13 @@
PLUGINS=(zsh-autosuggestions zsh-syntax-highlighting zsh-history-substring-search)
if ! [[ $TERM == "dumb" ]]; then if ! [[ $TERM == "dumb" ]]; then
# Don't load if running in dumb terminal, they don't work properly # Don't load if running in dumb terminal, they don't work properly
source $HOME/.local/lib/zsh/zsh-autosuggestions/zsh-autosuggestions.zsh for plugin in $PLUGINS; do
source $HOME/.local/lib/zsh/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh source $HOME/.local/lib/zsh/$plugin/$plugin.zsh
source $HOME/.local/lib/zsh/zsh-history-substring-search/zsh-history-substring-search.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 '^P' history-substring-search-up
bindkey '^N' history-substring-search-down bindkey '^N' history-substring-search-down

View File

@ -19,7 +19,7 @@ Shamelessly stole this from Simponic, made some modifications myself.
#+END_SRC #+END_SRC
** Configure some packages for internal only ** Configure some packages for internal only
#+BEGIN_SRC emacs-lisp #+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)) (use-package flymake :straight (:type built-in))
#+END_SRC #+END_SRC
* General emacs * General emacs

70
.local/lib/voidshell/background Executable file
View File

@ -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

View File

@ -17,6 +17,7 @@ memory_profile=$(echo $ws_data | jq '.memory_profile' -c)
run_with_systemd=$(echo $ws_data | jq '.systemd' -r) run_with_systemd=$(echo $ws_data | jq '.systemd' -r)
is_forking=$(echo $ws_data | jq '.forking' -r) is_forking=$(echo $ws_data | jq '.forking' -r)
void_output=$(echo $ws_data | jq '.void_output' -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' 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)) IFS=$'\n' args=($(echo $ws_data | jq '.args | .[]' -r 2>/dev/null))
@ -40,6 +41,15 @@ if [[ $program == "" ]]; then
exec error exec error
fi 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" echo "Launching application $program"
if [[ $environ != "" ]]; then if [[ $environ != "" ]]; then
@ -69,7 +79,7 @@ if [[ $void_output == "true" ]]; then
# difficult to use. # difficult to use.
exec >/dev/null exec >/dev/null
exec 2>/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 fi
if [[ $run_with_systemd == "false" ]]; then 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 # 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 # 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. # 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

@ -1 +1 @@
Subproject commit c7caf57ca805abd54f11f756fda6395dd4187f8a Subproject commit 5eb677bb0fa9a3e60f0eff031dc13926e093df92

3
.zshrc
View File

@ -1,6 +1,9 @@
# Execute additional config scripts # Execute additional config scripts
autoload colors; colors autoload colors; colors
always_detach=(make)
always_detach_as_service=(paru makepkg)
for file in $HOME/.config/zsh/*.zsh; do for file in $HOME/.config/zsh/*.zsh; do
if (( ${+LC_DEBUG} )); then echo "running $file"; fi if (( ${+LC_DEBUG} )); then echo "running $file"; fi
source $file source $file