diff --git a/.config/eww/eww.scss b/.config/eww/eww.scss index 879d5b6..383cb3c 100644 --- a/.config/eww/eww.scss +++ b/.config/eww/eww.scss @@ -108,6 +108,12 @@ background-color: $wallpaper; } +// mpris module + +.mpris--miniplayer-album { + border-radius: 10%; +} + // sway module .sway--root.sway--vertical { @@ -152,3 +158,24 @@ .clock--date { font-size: 9pt; } + +menu { + padding: 3px; + background-color: $bg0; + font-family: "Armstrong"; + border: 1px solid $foreground; + + menuitem { + padding-top: 2px; + padding-bottom: 2px; + border-bottom: 1px solid $bg1; + } + + menuitem:last-child { + border-bottom: none; + } + + menuitem:hover { + background-color: $bg1; + } +} diff --git a/.config/eww/eww.yuck b/.config/eww/eww.yuck index 2e5db15..cd5c905 100644 --- a/.config/eww/eww.yuck +++ b/.config/eww/eww.yuck @@ -58,7 +58,16 @@ :exclusive true :focusable false :stacking "fg" - (sidebar :orientation {side})) + (system-sidebar :orientation {side})) + +(defwindow user-sidebar [group side] + :geometry (geometry :width "200px" + :height "100%" + :anchor "${side} center") + :exclusive true + :focusable false + :stacking "fg" + (user-sidebar :orientation {side})) (defwindow laptopbar [group battery] @@ -85,3 +94,15 @@ :battery {battery} :workspace-group {group})) +(defwindow vertical-bottombar [group] + :geometry (geometry :width "100%" + :height "65px" + :anchor "bottom center") + :exclusive true + :focusable false + :stacking "fg" + (centerbox :orientation "h" + :class "bar root" + (date) + (big-clock) + (horizontal-minigauges-rightalign))) diff --git a/.config/eww/modules/clock.yuck b/.config/eww/modules/clock.yuck index 7662c82..b8bbcee 100644 --- a/.config/eww/modules/clock.yuck +++ b/.config/eww/modules/clock.yuck @@ -12,15 +12,41 @@ :spacing -20 :space-evenly true :class "special" - "${clock--data.hour}" + "${formattime(clock--data, '%H')}" ":" - "${clock--data.minute}" + "${formattime(clock--data, '%M')}" ":" - "${clock--data.second}" - ;; (label :class "special" - ;; :text "${clock--data.hour}:${clock--data.minute}:${clock--data.second}") + "${formattime(clock--data, '%S')}" ) - (label :text "${clock--data.year}-${clock--data.month}-${clock--data.day}"))) + (label :text {formattime(clock--data, "%Y-%m-%d")}))) + + +(defwidget big-clock [] + (centerbox :class "bigger nebula" + :space-evenly false + :halign "center" + :valign "center" + :width 100 + :orientation "h" + (box :halign "start" {formattime(clock--data, "%H")}) + (box :halign "center" ":") + (box :halign "end" {formattime(clock--data, "%M")}))) + + +(defwidget date [] + (box :class "nebula module text" + :space-evenly false + :spacing 20 + :halign "start" + :valign "center" + :orientation "h" + (label :text {formattime(clock--data, "%A")} + :class "medium" + :valign "center") + (label :text {formattime(clock--data, "%B %d")} + :class "special" + :valign "center"))) + (defwidget sideclock [] @@ -36,12 +62,12 @@ :width 150 :halign "center" :orientation "h" - (box :halign "start" "${clock--data.hour}") + (box :halign "start" {formattime(clock--data, "%H")}) (box :halign "center" ":") - (box :halign "end" "${clock--data.minute}")) - (label :text "${clock--data.dow}" + (box :halign "end" {formattime(clock--data, "%M")})) + (label :text {formattime(clock--data, "%A")} :class "special") - (label :text "${clock--data.month_name} ${clock--data.day}")) + (label :text {formattime(clock--data, "%B %d")})) (box :height 100 (revealer :transition "crossfade" diff --git a/.config/eww/modules/mpris.yuck b/.config/eww/modules/mpris.yuck index d655ea3..55e6965 100644 --- a/.config/eww/modules/mpris.yuck +++ b/.config/eww/modules/mpris.yuck @@ -1,29 +1,61 @@ ;; -*-lisp-*- (deflisten mpris--data :initial "{}" - `~/.config/eww/scripts/mpris.py`) + `~/.config/eww/scripts/mpris2.py`) (defwidget mpris2 - [] + [] + (button :onclick "astal-mpris --player '${mpris--data.active_player}' play-pause" (box :class "module text" :spacing 0 :orientation "v" - (label :class {mpris--data.playing ? "special" : "offline"} - :visible {mpris--data.running} - :text "${mpris--data.title} by ${mpris--data.artist}") + (box :spacing 5 + :space-evenly false + :halign "center" + :orientation "h" + :visible {mpris--data.running} + :class {mpris--data.playing ? "special" : "offline"} + {mpris--data.title} + "--" + (box :space-evenly false + :orientation "h" + {mpris--data.position_minutes} + ":" + {mpris--data.position_seconds} + "/" + {mpris--data.length_minutes} + ":" + {mpris--data.length_seconds}) + ) (label :class "offline" :visible {!mpris--data.running} :text "player offline") (label :visible {mpris--data.running} :text "now playing from ${mpris--data.album}") (label :visible {!mpris--data.running} - :text "player offline"))) + :text "player offline")))) (defwidget mpris-miniplayer [] (box :class "miniplayer" :orientation "v" + :space-evenly false :spacing 10 - (image :path {mpris--data.album_art} - :image-width 100 - :image-height 100) - (label))) + (label :class "nebula medium special" + :text "Music") + (box :orientation "v" + :space-evenly false + :spacing 20 + :visible {mpris--data.running} + (image :path {mpris--data.album_art} + :class "mpris--miniplayer-album" + :image-width 150 + :image-height 150) + (box :orientation "v" + :space-evenly false + :spacing 5 + (label :text {mpris--data.title} + :class "large special") + (label :text {mpris--data.artist}))) + (label :class "nebula offline" + :visible {!mpris--data.running} + :text "Offline"))) diff --git a/.config/eww/modules/network.yuck b/.config/eww/modules/network.yuck index e11aea7..04e3542 100644 --- a/.config/eww/modules/network.yuck +++ b/.config/eww/modules/network.yuck @@ -67,7 +67,7 @@ :visible {!device.online} :text "offline") (label :visible {device.online} - :class {network--data.last_update.unix < clock--data.unix - 30 ? "highlight" : "special"} + :class {network--data.last_update.unix < clock--data - 30 ? "highlight" : "special"} :text "${device.addresses[0].address}/${device.addresses[0].prefixlen}"))) (defwidget network--secure @@ -237,7 +237,7 @@ (box :halign "end" :spacing 0 :space-evenly false - (label :class {network--data.last_update.unix < clock--data.unix - 30 ? "highlight" : "special"} + (label :class {network--data.last_update.unix < clock--data - 30 ? "highlight" : "special"} :text "${network--data.last_update.month}-${network--data.last_update.day} ${network--data.last_update.hour}:${network--data.last_update.minute}:${network--data.last_update.second}"))) )) diff --git a/.config/eww/modules/system.yuck b/.config/eww/modules/system.yuck index 5ab5caa..fcc034f 100644 --- a/.config/eww/modules/system.yuck +++ b/.config/eww/modules/system.yuck @@ -151,7 +151,7 @@ (label :text "%" :class "offline")))))))) -(defwidget system--memory-gauge-small [] +(defwidget system--memory-gauge-small [?invert] (box :orientation "v" :halign "center" :width 35 @@ -159,10 +159,14 @@ :spacing 10 :class "" (label :text "MEM" + :visible {!(invert ?: false)} :class "special") (box :halign "center" (overlay - (system--small-gauge :value {system--data.memory.percent}))))) + (system--small-gauge :value {system--data.memory.percent}))) + (label :text "MEM" + :visible {invert ?: false} + :class "special"))) @@ -194,7 +198,7 @@ :class "offline")))))) )) -(defwidget system--cpu-gauge-small [] +(defwidget system--cpu-gauge-small [?invert] (box :orientation "v" :halign "center" :width 35 @@ -202,11 +206,15 @@ :spacing 10 :class "" (label :text "CPU" + :visible {!(invert ?: false)} :class "special") (box :halign "center" (overlay (system--small-gauge :value {system--data.cpu.avg} - :threshold 80))))) + :threshold 80))) + (label :text "CPU" + :visible {(invert ?: false)} + :class "special"))) (defwidget system--gpu-gauge [] (box :orientation "v" @@ -235,7 +243,7 @@ (label :text "%" :class "offline")))))))) -(defwidget system--gpu-gauge-small [] +(defwidget system--gpu-gauge-small [?invert] (box :orientation "v" :halign "center" :width 35 @@ -243,10 +251,14 @@ :spacing 10 :class "" (label :text "GPU" + :visible {!(invert ?: false)} :class "special") (box :halign "center" (overlay - (system--small-gauge :value {system--data.gpu.load * 100}))))) + (system--small-gauge :value {system--data.gpu.load * 100}))) + (label :text "GPU" + :visible {invert ?: false} + :class "special"))) (defwidget system--vram-gauge [] (box :orientation "v" @@ -275,7 +287,7 @@ (label :text "%" :class "offline")))))))) -(defwidget system--vram-gauge-small [] +(defwidget system--vram-gauge-small [?invert] (box :orientation "v" :halign "center" :width 35 @@ -283,10 +295,14 @@ :spacing 10 :class "" (label :text "VMEM" + :visible {!(invert ?: false)} :class "special") (box :halign "center" (overlay - (system--small-gauge :value {system--data.gpu.memory * 100}))))) + (system--small-gauge :value {system--data.gpu.memory * 100}))) + (label :text "VMEM" + :visible {invert ?: false} + :class "special"))) (defwidget system--gauge-generic [value value-fmt big-text little-text ?subscript ?threshold] diff --git a/.config/eww/modules/volume.yuck b/.config/eww/modules/volume.yuck index b0c25b7..4beaad1 100644 --- a/.config/eww/modules/volume.yuck +++ b/.config/eww/modules/volume.yuck @@ -28,7 +28,7 @@ :text "${volume--data.input.volume}%")) "audio system")) -(defwidget volume-small-gauge [] +(defwidget volume-small-gauge [?invert] (box :orientation "v" :halign "center" :width 35 @@ -36,6 +36,7 @@ :spacing 10 :class "" (label :text "VOL" + :visible {!(invert ?: false)} :class "special") (overlay :width 30 :height 30 @@ -56,7 +57,10 @@ :class 'gauge ${volume--data.input.mute ? "green" : volume--data.input.volume > 100 ? "highlight" : ""}' :start-at 12.5 :clockwise false - :thickness 2)))) + :thickness 2)) + (label :text "VOL" + :visible {invert ?: false} + :class "special"))) (defwidget volume--gauge [io] (box :orientation "v" diff --git a/.config/eww/scripts/date.py b/.config/eww/scripts/date.py index 6bd98b0..8db2b8f 100755 --- a/.config/eww/scripts/date.py +++ b/.config/eww/scripts/date.py @@ -12,9 +12,7 @@ import time def get_date(): """Return the current date as a JSON object string.""" - return datetime.datetime.now().strftime( - '{"hour": "%H", "minute": "%M", "second": "%S", "day": "%d", "month": "%m", "year": "%Y", "dow": "%A","month_name": "%B", "unix": %s}' - ) + return int(datetime.datetime.now().timestamp()) if __name__ == "__main__": diff --git a/.config/eww/scripts/mpris2.py b/.config/eww/scripts/mpris2.py new file mode 100755 index 0000000..dabc5b8 --- /dev/null +++ b/.config/eww/scripts/mpris2.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python3 + +from gi.repository import AstalMpris as Mpris, Gio +from gi.events import GLibEventLoopPolicy +import json +import asyncio +import sys + + +PRIORITY_PLAYERS = {"Feishin"} + + +class MprisMonitor(Gio.Application): + """MPRIS monitor application.""" + + def __init__(self): + super().__init__() + self.mpris = Mpris.Mpris.new() + self.players: dict[str, Mpris.Player] = {} # type: ignore[annotation-unchecked] + self.display_player: Mpris.Player | None = None # type: ignore[annotation-unchecked] + + def connect_player(self, mpris: Mpris.Mpris, player: Mpris.Player): + """Connect a new player.""" + self.players[player.props.bus_name] = player + if self.display_player is None: + self.display_player = player + + elif ( + player.props.identity in PRIORITY_PLAYERS + and self.display_player.props.identity not in PRIORITY_PLAYERS + ): + self.display_player = player + + player.connect("notify", self.on_status_update) + + def disconnect_player(self, mpris: Mpris.Mpris, player: Mpris.Player): + """Disconnect a closed player.""" + print(f"player disconnected: {player.props.bus_name}", file=sys.stderr) + existing_player = self.players.get(player.props.bus_name) + self.players = { + key: val for key, val in self.players.items() if val is not existing_player + } + if existing_player is self.display_player: + self.display_player = None + for player in self.players.values(): + if not self.display_player: + self.display_player = player + elif ( + self.display_player.props.identity not in PRIORITY_PLAYERS + and player.props.identity in PRIORITY_PLAYERS + ): + self.display_player = player + + if existing_player is not None: + existing_player.disconnect_by_func(self.on_status_update) + del existing_player + + self.output_status() + + def do_activate(self): + """Activate the application.""" + self.mpris.connect("player-added", self.connect_player) + self.mpris.connect("player-closed", self.disconnect_player) + for player in self.mpris.get_players(): + self.connect_player(self.mpris, player) + self.output_status() + self.hold() + + def on_status_update(self, player: Mpris.Player, *args): + """Perform status update tasks.""" + if ( + player.props.identity in PRIORITY_PLAYERS + and player.props.playback_status == Mpris.PlaybackStatus.PLAYING + ): + self.display_player = player + elif ( + player.props.playback_status == Mpris.PlaybackStatus.PLAYING + and self.display_player != Mpris.PlaybackStatus.PLAYING + ): + self.display_player = player + self.output_status() + + def output_status(self): + """Print the status of the currently active player, or an offline status if no active player exists.""" + if self.display_player is not None: + self.print_player_status(self.display_player) + else: + self.print_offline_status() + + def print_player_status(self, player: Mpris.Player): + """Print a player's status.""" + print( + json.dumps( + { + "running": player.props.available + and player.props.playback_status != Mpris.PlaybackStatus.STOPPED, + "playing": player.props.playback_status + == Mpris.PlaybackStatus.PLAYING, + "title": player.props.title, + "artist": player.props.artist, + "album": player.props.album, + "album_artist": player.props.album_artist, + "position": player.props.position, + "position_minutes": int((player.props.position) // 60), + "position_seconds": f"{int((player.props.position + 0) % 60):02}", + "length": player.props.length, + "length_minutes": int((player.props.length + 0) // 60), + "length_seconds": f"{int((player.props.length + 0) % 60):02}", + "active_player": player.props.identity, + } + ), + flush=True, + ) + + def print_offline_status(self): + """Print an offline status for when we have no players.""" + print( + json.dumps( + { + "running": False, + "playing": False, + "title": None, + "artist": None, + "album": None, + "album_artist": None, + "position": None, + "position_minutes": None, + "position_seconds": None, + "length": None, + "length_minutes": None, + "length_seconds": None, + "active_player": "", + } + ), + flush=True, + ) + + +if __name__ == "__main__": + asyncio.set_event_loop_policy(GLibEventLoopPolicy()) + app = MprisMonitor() + app.run() diff --git a/.config/eww/scripts/network2.py b/.config/eww/scripts/network2.py new file mode 100644 index 0000000..fc8f3d0 --- /dev/null +++ b/.config/eww/scripts/network2.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +"""Asynchronous network monitoring script, reports current state every 5 seconds.""" + +from ipaddress import ( + IPv4Address as IPAddress, + IPv4Network as IPNetwork, + IPv4Interface as IPInterface, + AddressValueError, +) + +from dbus_fast.aio import MessageBus +from dbus_fast import Variant, BusType +import re +import asyncio +from subprocess import PIPE +import functools + +MONITOR_IPS = {"ezrinet": IPAddress("10.242.3.1"), "internet": IPAddress("1.1.1.1")} + +INTERFACE_IGNORE_PATTERNS = [ + re.compile(r"^vb-"), + re.compile(r"^vz-"), + re.compile(r"^virbr[0-9]+"), + re.compile(r"^lo$"), +] + + +def timer(interval: float, initial_delay: float = 0): + """Decorate a function or coroutine to run it on the given interval.""" + + def decorator(func): + + async def do_interval(): + try: + await asyncio.sleep(initial_delay) + while True: + await func() + await asyncio.sleep(interval) + except asyncio.CancelledError: + return + + def start(): + loop = asyncio.get_running_loop() + if "_timer_task" in func.__dict__: + raise Exception("Timer is already running.") + func.__dict__["_timer_task"] = loop.create_task(do_interval()) + + func.__dict__["start"] = start + return func + + return decorator + + +async def ping(address: IPAddress) -> tuple[bool, float | None]: + """Ping a host and return a tuple of [success, ping time].""" + proc = await asyncio.create_subprocess_exec( + "ping", "-c1", "-w1", "-n", str(address), stdout=PIPE + ) + stdout, stderr = await proc.communicate() + + _match = re.search( + r"icmp_seq=1 ttl=[0-9]+ time=([0-9]+\.?[0-9]*) ms", stdout.decode("utf-8") + ) + if _match is None: + return (False, None) + else: + return (True, float(_match.group(1))) + + +@timer(5) +async def ping_checks(): + """Recurring ping checks for configured IP addresses.""" diff --git a/.config/eww/scripts/polkitagent.py b/.config/eww/scripts/polkitagent.py new file mode 100755 index 0000000..06cb68d --- /dev/null +++ b/.config/eww/scripts/polkitagent.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 + +"""Polkit agent.""" +import json +import gi +from gi.repository import Gtk, GLib, Gdk +from gi.events import GLibEventLoopPolicy +import asyncio +from asyncio import subprocess +import sys + +policy = GLibEventLoopPolicy() +asyncio.set_event_loop_policy(policy) +loop = asyncio.get_event_loop() + + +class AuthHandler(Gtk.Application): + def __init__(self): + super().__init__(application_id="dev.ezri.PolkitAuthHandler") + GLib.set_application_name("Polkit Auth Handler") + sasscmd = loop.run_until_complete( + asyncio.create_subprocess_shell( + "sass ~/.config/eww/eww.scss -I ~/.config/eww", stdout=subprocess.PIPE + ) + ) + css = loop.run_until_complete(sasscmd.stdout.read()) + self.style_provider = Gtk.CssProvider() + self.style_provider.load_from_string(css.decode("utf-8")) + Gtk.StyleContext.add_provider_for_display( + Gdk.Display.get_default(), + self.style_provider, + Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION, + ) + + async def handle_input(self): + reader = asyncio.StreamReader() + protocol = asyncio.StreamReaderProtocol(reader) + await loop.connect_read_pipe(lambda: protocol, sys.stdin) + while True: + try: + line = await reader.readline() + except: + break + decoded = json.loads(line.decode("utf-8")) + if decoded["action"] == "request password": + self.message_label.set_label( + decoded.get("polkit action", decoded)["message"] + ) + self.prompt_label.set_label(decoded.get("prompt", "Password:")) + self.password_field.set_editable(True) + elif decoded["action"] == "authorization response": + if decoded["authorized"]: + break + + def send_response(self, password_field): + password_field.set_editable(False) + password = password_field.get_text() + print(json.dumps({"action": "authenticate", "password": password}), flush=True) + + def do_activate(self): + window = Gtk.ApplicationWindow( + application=self, + title="Password prompt", + css_classes=["root", "darkbg"], + default_width=600, + default_height=350, + ) + # window.get_style_context().add_provider(self.style_provider, 801) + box = Gtk.Box( + spacing=20, homogeneous=False, orientation=Gtk.Orientation.VERTICAL + ) + window.set_child(box) + box.append( + Gtk.Label(label="Authentication Required", css_classes=["nebula", "big"]) + ) + self.message_label = Gtk.Label(css_classes=["paragraph"]) + box.append(self.message_label) + pwbox = Gtk.Box( + spacing=5, homogeneous=False, orientation=Gtk.Orientation.HORIZONTAL + ) + self.prompt_label = Gtk.Label() + pwbox.append(self.prompt_label) + self.password_field = Gtk.PasswordEntry(hexpand=True) + pwbox.append(self.password_field) + self.password_field.connect("activate", self.send_response) + box.append(pwbox) + window.present() + loop.create_task(self.handle_input()) + + +app = AuthHandler() +exit_status = app.run() +sys.exit(exit_status) diff --git a/.config/eww/windows.yuck b/.config/eww/windows.yuck index 6dade84..0308a9d 100644 --- a/.config/eww/windows.yuck +++ b/.config/eww/windows.yuck @@ -6,7 +6,7 @@ ;; Generic Windows ;; ;;; ;;; -(defwidget sidebar [orientation] +(defwidget system-sidebar [orientation] (box :orientation "v" :valign "start" :space-evenly false @@ -17,6 +17,16 @@ (system-gauges) (volume-gauges))) +(defwidget user-sidebar [orientation] + (box :orientation "v" + :valign "start" + :space-evenly false + :spacing 20 + :class "root ${orientation}-side" + (sideclock) + (mpris-miniplayer) + (volume-gauges))) + (defwidget small-sidebar [orientation battery workspace-group] (centerbox :orientation "v" :class "root ${orientation}-side" @@ -113,7 +123,18 @@ (network) (wifi) (clock))) - + +(defwidget horizontal-minigauges-rightalign [] + (box :orientation "h" + :halign "end" + :space-evenly false + :spacing 20 + :class "rightbox" + (system--cpu-gauge-small :invert {false}) + (system--memory-gauge-small :invert {false}) + (system--gpu-gauge-small :invert {false}) + (system--vram-gauge-small :invert {false}) + (volume-small-gauge :invert {false}))) ;;; ;;; ;; Bars for Rocinante ;; diff --git a/.config/pip/eww-modules-requirements.txt b/.config/pip/eww-modules-requirements.txt index 8b0888b..0d7f93f 100644 --- a/.config/pip/eww-modules-requirements.txt +++ b/.config/pip/eww-modules-requirements.txt @@ -4,6 +4,7 @@ certifi charset-normalizer dbus-python decorator +exceptiongroup executing i3ipc idna @@ -14,10 +15,13 @@ netifaces parso pexpect pickleshare -prompt-toolkit +prompt_toolkit psutil ptyprocess -pure-eval +pulsectl +pulsectl-asyncio +pure_eval +pyamdgpuinfo pycairo Pygments PyGObject @@ -28,5 +32,6 @@ six stack-data traitlets trparse +typing_extensions urllib3 wcwidth diff --git a/.config/sway/config b/.config/sway/config index 8047b13..83110bd 100644 --- a/.config/sway/config +++ b/.config/sway/config @@ -71,6 +71,13 @@ bindsym { $mod+0 exec busctl --user call dev.ezri.sway /ContextManager dev.ezri.sway.ContextManager FocusWorkspace y 10 } +bindgesture { + swipe:3:left exec busctl --user call dev.ezri.sway /ContextManager dev.ezri.sway.ContextManager FocusNextWorkspace + swipe:4:left exec busctl --user call dev.ezri.sway /ContextManager dev.ezri.sway.ContextManager FocusNextWorkspace + swipe:3:right exec busctl --user call dev.ezri.sway /ContextManager dev.ezri.sway.ContextManager FocusPreviousWorkspace + swipe:4:right exec busctl --user call dev.ezri.sway /ContextManager dev.ezri.sway.ContextManager FocusPreviousWorkspace +} + ## Window Reassignment Keybinds bindsym { $mod+Shift+1 exec busctl --user call dev.ezri.sway /ContextManager dev.ezri.sway.ContextManager MoveContainer y 1 @@ -209,7 +216,7 @@ for_window [app_id="rg.kde.kwalletd6"] floating enable bindsym $mod+Shift+r reload ## Default Application Launcher -bindsym $mod+Return exec default-application-launcher +bindsym $mod+Return exec ~/.local/bin/default-application-launcher ## Terminal Launcher bindsym $mod+Shift+Return exec alacritty msg create-window || alacritty @@ -253,14 +260,7 @@ include input.conf # Service Management # ### ### -## Import Environment Variables -exec_always XDG_CURRENT_DESKTOP=sway ELECTRON_OZONE_PLATFORM_HINT=wayland { - systemctl --user import-environment DISPLAY WAYLAND_DISPLAY SWAYSOCK XDG_CURRENT_DESKTOP PATH ASDF_DATA_DIR ELECTRON_OZONE_PLATFORM_HINT - dbus-update-activation-environment DISPLAY WAYLAND_DISPLAY SWAYSOCK XDG_CURRENT_DESKTOP PATH ASDF_DATA_DIR ELECTRON_OZONE_PLATFORM_HINT -} - -## Start Session Target -exec_always systemctl --user start sway-session.target +exec ~/.local/lib/voidshell/sway-systemd-start.sh ### ### # Generic Local Configuration # diff --git a/.config/sway/workspaces.json##hostname.gathering-storm b/.config/sway/workspaces.json##hostname.gathering-storm index 8b599cb..93a869b 100644 --- a/.config/sway/workspaces.json##hostname.gathering-storm +++ b/.config/sway/workspaces.json##hostname.gathering-storm @@ -32,7 +32,7 @@ { "index": 5, "name": "discord", - "exec": "discord", + "exec": "vesktop", "program_name": "discord" }, { @@ -160,7 +160,13 @@ "name": "steam", "exec": "steam", "program_name": "steam" - } + }, + { + "index": 24, + "name": "telephony", + "exec": "telephony-launcher.sh", + "program_name": "telephony" + } ], "contexts": { "docked": { @@ -209,13 +215,11 @@ "model": "DELL U2722D", "serial": "5X3MGH3", "group": "right", - "position": [8107, 0], - "mode": "2560x1440 color_profile icc /usr/share/color/icc/colord/DCI-P3.icd", + "position": [8107, -500], + "mode": "2560x1440 transform 270 color_profile icc /usr/share/color/icc/colord/DCI-P3.icd", "eww_windows": { "desktop-rightbar": {}, - "sidebar": { - "side": "right" - } + "vertical-bottombar": {} } } ], @@ -225,15 +229,15 @@ "reverse": false }, "left": { - "workspaces": [6, 2, 3, 4, 5], + "workspaces": [6, 2, 3, 13, 5], "reverse": true }, "center": { - "workspaces": [1, 7, 8, 9, 10, 11, 12, 13, 14, 15], + "workspaces": [1, 7, 8, 9, 10, 11, 12, 24, 14, 15], "reverse": false }, "right": { - "workspaces": [16, 17, 19, 18, 20, 13], + "workspaces": [16, 17, 19, 18, 20], "reverse": false } } @@ -241,27 +245,12 @@ "battlestation": { "primary": "center", "outputs": [ - { - "names": ["eDP-1"], - "group": "builtin", - "position": [0, 600], - "eww_windows": { - "laptopbar": { - "battery": "BAT1" - }, - "laptopsidebar": { - "battery": "BAT1", - "side": "left" - } - }, - "mode": "2560x1600@165Hz scale 1.5 color_profile icc /usr/share/color/icc/colord/BOE_CQ_______NE160QDM_NZ6.icm" - }, { "make": "ASUSTek COMPUTER INC", "model": "VG245", "serial": "L7LMQS132447", "group": "left", - "position": [1707, 200], + "position": [0, 200], "mode": "1920x1080@75Hz", "eww_windows": { "desktop-leftbar": {}, @@ -275,7 +264,7 @@ "model": "VG32AQA1A", "serial": "S5LMQS033656", "group": "center", - "position": [3627, 0], + "position": [1920, 0], "mode": "2560x1440@165Hz", "eww_windows": ["desktop-mainbar"] }, @@ -284,9 +273,29 @@ "model": "VG245", "serial": "L6LMQS065439", "group": "right", - "position": [6187, 200], + "position": [4480, 200], "mode": "1920x1080@75Hz", - "eww_windows": ["desktop-rightbar"] + "eww_windows": { + "desktop-rightbar": {}, + "sidebar": { + "side": "right" + } + } + }, + { + "names": ["eDP-1"], + "group": "builtin", + "position": [6400, 600], + "eww_windows": { + "laptopbar": { + "battery": "BAT1" + }, + "laptopsidebar": { + "battery": "BAT1", + "side": "left" + } + }, + "mode": "2560x1600@165Hz scale 1.5 color_profile icc /usr/share/color/icc/colord/BOE_CQ_______NE160QDM_NZ6.icm" } ], "groups": { @@ -299,7 +308,7 @@ "reverse": true }, "center": { - "workspaces": [1, 7, 8, 9, 10, 11, 12, 4, 14, 15], + "workspaces": [1, 7, 8, 9, 10, 11, 12, 24, 14, 15], "reverse": false }, "right": { diff --git a/.config/systemd/user/eww.service b/.config/systemd/user/eww.service index 55e5b2c..ac248a8 100644 --- a/.config/systemd/user/eww.service +++ b/.config/systemd/user/eww.service @@ -4,6 +4,10 @@ PartOf=sway-session.target [Service] Type=simple +ExecSearchPath=%h/.local/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin +Environment=PATH=%h/.pyenv/shims:%h/.local/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin +Environment=PYENV_ROOT=%h/.pyenv +Environment=PYENV_VERSION=eww-modules ExecStart=eww daemon --no-daemonize ExecReload=eww reload ExecStop=eww kill diff --git a/.config/systemd/user/sway-context-manager.service b/.config/systemd/user/sway-context-manager.service index 8a8ab2e..482d4a1 100644 --- a/.config/systemd/user/sway-context-manager.service +++ b/.config/systemd/user/sway-context-manager.service @@ -5,6 +5,11 @@ PartOf=sway-session.target [Service] Type=dbus BusName=dev.ezri.sway +ExecSearchPath= +ExecSearchPath=%h/.pyenv/shims:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin +Environment=PATH=%h/.pyenv/shims:%h/.local/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin +Environment=PYENV_ROOT=%h/.pyenv +Environment=PYENV_VERSION=sway ExecStart=sway_context_manager Slice=session.slice Restart=on-abnormal diff --git a/.config/zsh/alias.zsh b/.config/zsh/alias.zsh index 33f5827..253154f 100644 --- a/.config/zsh/alias.zsh +++ b/.config/zsh/alias.zsh @@ -7,7 +7,7 @@ alias userctl="\systemctl --user" alias ujournalctl="\journalctl --user" -alias reconf="exec $SHELL" +alias reconf="exec zsh" # Use safeexec to ensure we are using system binaries (required for some AUR packages) alias paru="safeexec paru" diff --git a/.config/zsh/env.zsh b/.config/zsh/env.zsh index 1b6b5a2..bbe7443 100644 --- a/.config/zsh/env.zsh +++ b/.config/zsh/env.zsh @@ -1,5 +1,8 @@ export ASDF_DATA_DIR=$HOME/.local/share/asdf-vm +export RUSTUP_HOME=$HOME/.local/lib/rustup +export CARGO_HOME=$HOME/.local/lib/cargo + PATH_pyenv=$HOME/.pyenv/bin:$HOME/.pyenv/shims:/opt/pyenv/pyenv-virtualenv/shims PATH_asdf=$ASDF_DATA_DIR/shims:/opt/asdf-vm/bin PATH_node=$HOME/.opt/npm/bin @@ -8,10 +11,11 @@ PATH_perl=/usr/bin/vendor_perl:/usr/bin/core_perl PATH_system=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin PATH_user=$HOME/.local/bin PATH_wincmake=/opt/msvcmake/bin:/opt/msvc/bin/x64 +PATH_cargo=$CARGO_HOME/bin cdpath=(~ ~/src) -export PATH=.:$PATH_node:$PATH_pyenv:$PATH_java:$PATH_user:$PATH_system +export PATH=.:$PATH_node:$PATH_cargo:$PATH_pyenv:$PATH_java:$PATH_user:$PATH_system # Function to run a command with only the "safe" system PATH element function safeexec { diff --git a/.emacs.d/settings.org b/.emacs.d/settings.org index 223d3c0..cc8cd8c 100644 --- a/.emacs.d/settings.org +++ b/.emacs.d/settings.org @@ -143,6 +143,13 @@ Indent using tabs, render with tab-width of 2. `(("irc.libera.chat" nickserv "netsorc" ,(secrets-get-secret "Default keyring" "libera.chat")))) #+END_SRC +** Terminal + +#+BEGIN_SRC emacs-lisp + (use-package vterm + :ensure t) +#+END_SRC + * Theming ** Highlight current line #+BEGIN_SRC emacs-lisp @@ -422,11 +429,14 @@ Indent using tabs, render with tab-width of 2. ** Markdown *** Auto Text Wrap #+BEGIN_SRC emacs-lisp - (add-hook 'markdown-mode-hook (lambda () - (setq fill-column 85) - (visual-fill-column-mode) - (visual-line-mode) - (display-fill-column-indicator-mode))) + (use-package visual-fill-column + :ensure t + :init + (add-hook 'markdown-mode-hook (lambda () + (setq fill-column 85) + (visual-fill-column-mode) + (visual-line-mode) + (display-fill-column-indicator-mode)))) #+END_SRC ** YAML #+BEGIN_SRC emacs-lisp @@ -653,11 +663,11 @@ Indent using tabs, render with tab-width of 2. :bind (:map eglot-mode-map ("C-c C-e" . eglot-rename) ("C-." . eglot-code-actions) - ("" . eglot-find-declaration)) - :hook ((python-mode . eglot-ensure) - (python-mode . flyspell-prog-mode) - (python-mode . superword-mode) - (python-mode . hs-minor-mode) + ("" . eglot-find-implementation)) + :hook ((python-base-mode . eglot-ensure) + (python-base-mode . flyspell-prog-mode) + (python-base-mode . superword-mode) + (python-base-mode . hs-minor-mode) (typescript-ts-mode . eglot-ensure) (typescript-ts-mode . flyspell-prog-mode) (typescript-ts-mode . superword-mode) @@ -671,9 +681,10 @@ Indent using tabs, render with tab-width of 2. (typescript-mode . superword-mode) (typescript-mode . hs-minor-mode)) :config + (add-to-list 'eglot-server-programs '(python-base-mode . ("pylsp"))) (setq-default eglot-workspace-configuration '((:pylsp . (:configurationSources ["flake8"] - :plugins ( + :plugins ( :mccabe (:enabled :json-false) :pyflakes (:enabled :json-false) :flake8 (:enabled :json-false @@ -682,13 +693,39 @@ Indent using tabs, render with tab-width of 2. :pydocstyle (:enabled t :convention "numpy") :yapf (:enabled :json-false) :autopep8 (:enabled :json-false) - :black (:enabled t :line_length 88 :cache_config t))))))) + :black (:enabled t :line_length 88 :cache_config t) + :mypy (:enabled t)) + ))))) #+END_SRC ** Git +*** Magit #+BEGIN_SRC emacs-lisp (use-package magit :ensure t) #+END_SRC + +*** Blame +#+BEGIN_SRC emacs-lisp + (use-package blamer + :straight (:host github :repo "artawower/blamer.el") + :bind (("s-i" . blamer-show-commit-info)) + :custom + (blamer-idle-time 0.3) + (blamer-min-offset 70) + :custom-face + (blamer-face ((t :foreground "#7a88cf" + :background nil + :italic t))) + :config + (global-blamer-mode 1)) +#+END_SRC ** Languages +*** Python +#+BEGIN_SRC emacs-lisp + (use-package pet + :config + (add-hook 'python-base-mode-hook 'pet-mode -10) + ) +#+END_SRC *** Elixir #+BEGIN_SRC emacs-lisp (use-package elixir-mode