diff --git a/.config/alacritty/alacritty.toml##default b/.config/alacritty/alacritty.toml##default index 1f4c1a0..0df2ef1 100644 --- a/.config/alacritty/alacritty.toml##default +++ b/.config/alacritty/alacritty.toml##default @@ -63,6 +63,3 @@ dynamic_padding = true dynamic_title = true opacity = 0.8 title = "Terminal" - -[general] -working_directory = "/home/ezri" diff --git a/.config/eww/eww.yuck##hostname.gathering-storm b/.config/eww/eww.yuck##hostname.gathering-storm index 6f4e171..448f7a0 100644 --- a/.config/eww/eww.yuck##hostname.gathering-storm +++ b/.config/eww/eww.yuck##hostname.gathering-storm @@ -12,6 +12,76 @@ (include "./windows.yuck") +(defwindow leftbar + :monitor 1 + :geometry (geometry :width "100%" + :height "36px" + :anchor "top center") + :exclusive true + :focusable false + :stacking "fg" + (centerbox :orientation "h" + :class "bar root" + (tycho-leftbar--left) + (tycho-leftbar--center) + (tycho-leftbar--right))) + +(defwindow centerbar + :monitor 0 + :geometry (geometry :width "100%" + :height "26px" + :anchor "top center") + :exclusive true + :focusable false + :stacking "fg" + (centerbox :orientation "h" + :class "bar root" + (tycho-centerbar--left) + (tycho-centerbar--center) + (tycho-centerbar--right))) + +(defwindow rightbar + :monitor 2 + :geometry (geometry :width "100%" + :height "36px" + :anchor "top center") + :exclusive true + :focusable false + :stacking "fg" + (centerbox :orientation "h" + :class "bar root" + (tycho-rightbar--left) + (tycho-rightbar--center) + (tycho-rightbar--right))) + +(defwindow sidebar + :monitor 1 + :geometry (geometry :width "200px" + :height "100%" + :anchor "left center") + :exclusive true + :focusable false + :stacking "fg" + (tycho-sidebar)) + + + +(defwindow network-status + :monitor 0 + :geometry (geometry :width "200px" + :height "0px" + :x "300px" + :y "0px" + :anchor "top right") + :exclusive false + :focusable false + :stacking "overlay" + (box :orientation "v" + :class "bar root bg outline" + :visible {network--show-details} + (network-detail))) + + (defwindow builtinbar :monitor 0 :geometry (geometry :width "100%" diff --git a/.config/eww/modules/network.yuck##hostname.gathering-storm b/.config/eww/modules/network.yuck##hostname.gathering-storm index dca9b60..a92f2cd 100644 --- a/.config/eww/modules/network.yuck##hostname.gathering-storm +++ b/.config/eww/modules/network.yuck##hostname.gathering-storm @@ -1,77 +1,347 @@ ;; -*-lisp-*- (deflisten network--data - `~/.config/eww/scripts/network.py`) + `~/.config/eww/scripts/network.py`) -(defwidget network--wlan [device] +(defvar network--show-details false) + +(defwidget network--interface + [device] + (box :orientation "v" + :halign "center" + :space-evenly false + :spacing 0 + (label :class "offline" + :visible {!device.online} + :text "offline") + (label :visible {device.online} + :class "special" + :text "${device.addresses[0].address}/${device.addresses[0].prefixlen}"))) + +(defwidget network-big--online + [] (box :orientation "h" :halign "start" + :valign "center" :space-evenly false :spacing 10 - (label :class "offline" - :visible {network--data["network"][device]["offline"]} - :text "offline") - (label :visible {network--data["network"][device]["connecting"]} - :class "highlight" - :text "connecting...") - (label :visible {network--data["network"][device]["online"] && !network--data.network[device].connecting} - :class "special" - :text "${network--data['wifi']['ssid']}") - (label :visible {!network--data.connection.internet && network--data.network[device].online} - :class "highlight" - :text "- no internet" - ))) + (label :class "medium green nebula" + :visible {network--data.online} + :text "online") + (label :class "medium offline nebula" + :visible {network--data.offline} + :text "offline") + (label :class "medium highlight nebula" + :visible {network--data.configuring} + :text "configuring"))) -(defwidget network--lan [device] +(defwidget network-big--lan + [device] + (label :visible {network--data + [device] + .online} + :class "" + :text "${network--data.interfaces[device].addresses[0].address}/${network--data.interfaces[device].addresses[0].prefixlen}")) + +(defwidget network--wlan + [device] (box :orientation "h" :halign "start" :space-evenly false :spacing 0 (label :class "offline" - :visible {network--data["network"][device]["offline"]} - :text "offline") - (label :visible {network--data["network"][device]["connecting"]} - :class "highlight" - :text "connecting...") - (label :visible {network--data["network"][device]["online"] && !network--data.network[device].connecting} - :class "special" - :text "${network--data['network'][device]['ip4_addr']}/${network--data['network'][device]['ip4_prefix']}"))) + :visible {network--data + ["network"] + [device] + ["offline"] + } + :text "offline") + (label :visible {network--data + ["network"] + [device] + ["connecting"] + } + :class "highlight" + :text "connecting...") + (label :visible {network--data + ["network"] + [device] + ["online"] + } + :class "special" + :text "${network--data['wifi']['ssid']}"))) -(defwidget network--proxy-vpn [device ?required] +(defwidget network--lan + [device] + (box :orientation "h" + :halign "start" + :space-evenly false + :spacing 0 + (label :class "offline" + :visible {!device.online} + :text "offline") + (label :visible {device.online} + :class {network--data.last_update.unix < clock--data.unix - 30 ? "highlight" : "special"} + :text "${device.addresses[0].address}/${device.addresses[0].prefixlen}"))) + +(defwidget network--secure + [] (box :orientation "h" :halign "start" :space-evenly false :spacing 0 - :visible {network--data.connection.internet && (network--data["network"][device]["exists"] || required)} (label :class "highlight" - :text "- insecure" - :visible {! network--data["network"][device]["exists"]}) + :text "- insecure" + :visible {! + network--data.secure }) (label :class "green" - :text "- secured" - :visible {network--data["network"][device]["exists"]}))) + :text "- secured via ${network--data.secure_msg}" + :visible {network--data.secure}))) -(defwidget vpn-network [] +(defwidget vpn-network + [] (box :orientation "v" :halign "start" :space-evenly false :spacing 0 + :visible {network--data + != + ''} (label :class "highlight" - :text "offline" - :visible {!network--data.connection.ezrinet}) + :text "offline" + :visible {! + network--data.interfaces.ezrinet.online }) (label :class "green" - :text "connected" - :visible {network--data.connection.ezrinet}) + :text "connected" + :visible {network--data.interfaces.ezrinet.online}) "personal network")) -(defwidget network [] +(defwidget network + [] + (button :onclick "eww update network--show-details=${!network--show-details}" + (box :orientation "v" + :halign "start" + :space-evenly false + :spacing 0 + :visible {network--data != ''} + (box :orientation "h" + :halign "center" + :space-evenly false + :spacing 10 + (network--lan :device {network--data.default_interface})) + "communications"))) + +(defwidget network--public-ip + [] + (centerbox :orientation "h" + :halign "start" + :width 200 + (box :halign "start" + "public ip:") + "" + (box :halign "end" + :spacing 0 + :space-evenly false + (label :class "highlight" + :text "offline" + :visible {!network--data.online}) + (label :class "highlight" + :text "no response" + :visible {network--data.online && !network--data.have_public_ip}) + (label :class "special" + :text {network--data.public_ip.ip} + :visible {network--data.online && network--data.have_public_ip})))) + +(defwidget network--default-route + [] + (centerbox :orientation "h" + :halign "start" + :width 200 + (box :halign "start" + "route on:") + "" + (box :halign "end" + :spacing 0 + :space-evenly false + (label :class "highlight" + :text "offline" + :visible {!network--data.online}) + (label :class "highlight" + :text "no route" + :visible {network--data.online && !network--data.have_default_route}) + (label :class "special" + :text {network--data.default_route} + :visible {network--data.online && network--data.have_default_route})))) + +(defwidget network--gateway + [] + (centerbox :orientation "h" + :halign "start" + :width 200 + (box :halign "start" + "gateway:") + "" + (box :halign "end" + :spacing 0 + :space-evenly false + (label :class "highlight" + :text "no response" + :visible {!network--data.have_gateway}) + (label :class "special" + :text {network--data.gateway} + :visible {network--data.have_gateway})))) + +(defwidget network--status-summary + [] + (centerbox :orientation "h" + :halign "start" + :width 200 + (box :halign "start" + "status:") + "" + (box :halign "end" + :spacing 0 + :space-evenly false + (label :class "highlight" + :text "offline" + :visible {!network--data.online}) + (label :class "special" + :text "online" + :visible {network--data.online && !network--data.secure}) + (label :class "green" + :text {network--data.secure_msg == "usu" ? "vpn online" : "secure"} + :visible {network--data.online && network--data.secure})))) + + +(defwidget network-detail + [] (box :orientation "v" :halign "start" :space-evenly false :spacing 0 - (box :orientation "h" - :halign "center" - :space-evenly false - :spacing 10 - (network--wlan :device "wlan0") - (network--proxy-vpn :device "wg-mullvad" :required {!network--data.trusted})) - "communications")) - + (network--status-summary) + (network--public-ip) + (network--default-route) + (network--gateway) + (centerbox :orientation "h" + :halign "start" + :width 200 + (box :halign "start" + "last update:") + "" + (box :halign "end" + :spacing 0 + :space-evenly false + (label :class {network--data.last_update.unix < clock--data.unix - 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}"))) + )) + +(defwidget network-sidebar-details [] + (box :orientation "v" + :halign "start" + :class "module" + :space-evenly false + :spacing 5 + :width 200 + (box :orientation "h" + :halign "center" + :class "nebula" + :spacing 10 + :space-evenly false + (label :class "medium special" + :text "Comms")) + (box :orientation "h" + :halign "center" + :class "nebula" + :spacing 10 + :space-evenly false + (label :class "nebula green" + :text "Online" + :visible {network--data.online}) + (label :class "nebula highlight" + :text "Offline" + :visible {!network--data.online})) + (centerbox :orientation "h" + :halign "start" + :spacing 10 + :width 200 + :space-evenly false + (box :halign "start" + :class "nebula" + "Route On:") + "" + (box :halign "end" + (label :text "No Route" + :class "highlight" + :visible {network--data.online && !network--data.have_default_route}) + (label :text "${network--data.default_route}" + :class "special" + :visible {network--data.online && network--data.have_default_route}))) + (centerbox :orientation "h" + :halign "start" + :spacing 10 + :width 200 + :space-evenly false + (box :halign "start" + :valign "start" + :class "nebula" + "Address:") + "" + (box :halign "end" + :orientation "v" + (label :text "${network--data.default_interface.addresses[0].address}" + :class "special") + (label :text "/${network--data.default_interface.addresses[0].prefixlen}" + :halign "end" + :class "special"))) + (centerbox :orientation "h" + :halign "start" + :spacing 10 + :width 200 + :space-evenly false + (box :halign "start" + :class "nebula" + "Public IP:") + "" + (box :halign "end" + (label :text "Offline" + :class "highlight" + :visible {!network--data.have_public_ip}) + (label :text {network--data.public_ip.ip} + :class "special" + :visible {network--data.have_public_ip}))) + (centerbox :orientation "h" + :halign "start" + :spacing 10 + :width 200 + :space-evenly false + (box :halign "start" + :class "nebula" + "Gateway:") + "" + (box :halign "end" + (label :text "Error" + :class "highlight nebula" + :visible {!network--data.have_gateway}) + (label :text {network--data.gateway} + :class "special" + :visible {network--data.have_gateway}))) + (centerbox :orientation "h" + :halign "start" + :class "nebula" + :spacing 10 + :width 200 + :space-evenly false + (box :halign "start" + "VPN:") + "" + (box :halign "end" + (label :text "Offline" + :class "highlight" + :visible {!network--data.interfaces.ezrinet.online}) + (label :text "Online" + :class "green" + :visible {network--data.interfaces.ezrinet.online}))) + + )) + + diff --git a/.config/eww/scripts/network.py##hostname.gathering-storm b/.config/eww/scripts/network.py##hostname.gathering-storm index 742aa39..99db78e 100755 --- a/.config/eww/scripts/network.py##hostname.gathering-storm +++ b/.config/eww/scripts/network.py##hostname.gathering-storm @@ -1,81 +1,306 @@ #!/usr/bin/env python3 -import subprocess +import trparse +from subprocess import run, PIPE, DEVNULL import json -from time import sleep +import sys +import os +import re +from time import sleep, time as now +from ipaddress import ( + IPv4Address as IPAddress, + IPv4Network as IPNetwork, + IPv4Interface as IPInterface, + AddressValueError, +) +from netifaces import interfaces, ifaddresses, AF_INET, AF_LINK, gateways +import dbus +from datetime import datetime -TRUSTED_NETWORKS = ['gayer people'] -def wifi(): - ssid_cmd = subprocess.run(['iwgetid', '-r'], capture_output = True) - if ssid_cmd.returncode != 0: - return {"connected": False, "ssid": None} - ssid = ssid_cmd.stdout.decode('utf-8').strip() - ssid = ssid if len(ssid) <= 15 else f"{ssid[:14]}…" - return {"connected": True, "ssid": ssid} +# These are networks that are considered secure. All traffic being routed +# through one of these networks is being encrypted and routed through an +# anonymizing service. +SECURE_FIRSTHOP_NETWORKS = { + "home": IPNetwork("10.242.0.0/16"), # Personal network + "mullvad": IPNetwork("10.64.0.1/32"), # Mullvad VPN + "usu": IPNetwork("129.123.8.126/32"), # USU VPN +} -def netdev(device: str): - ip_cmd = subprocess.run(['ip', '-j', 'addr', 'show', device], capture_output = True) - if ip_cmd.returncode != 0: - return {"exists": False} - ip_data = json.loads(ip_cmd.stdout.decode('utf-8'))[0] - online = ip_data["operstate"] == "UP" - ip4_addr = "" - ip4_prefix_length = 24 - addr4_info = list(filter(lambda addr: addr.get("family") == "inet", ip_data["addr_info"])) - if len(addr4_info) >= 1: - ip4_addr = addr4_info[0]["local"] - ip4_prefix_length = addr4_info[0]["prefixlen"] - connecting = ip_data["operstate"] != "DOWN" and (ip4_addr == "" or not online) +# Interfaces that we should check +DEFAULT_INTERFACES_TO_WATCH = ["lan", "ezrinet"] +# Interfaces that we should use when determining if we are online +DEFAULT_INTERFACES_TO_COUNT = ["lan"] +# Overrides for IP addresses to ping to determine if an interface is +# online This is useful for interfaces that don't have or report a +# gateway, but we still want to check if they're online, such as the +# ezrinet interface. +INTERFACE_PING_OVERRIDES = {"ezrinet": IPAddress("10.242.3.1")} + +INTERFACES_TO_WATCH = os.environ.get( + "NETWORK_INTERFACES_TO_WATCH", ",".join(DEFAULT_INTERFACES_TO_WATCH) +).split(",") +INTERFACES_TO_COUNT = os.environ.get( + "NETWORK_INTERFACES_TO_COUNT", ",".join(DEFAULT_INTERFACES_TO_COUNT) +).split(",") + +for key, value in os.environ.items(): + if key[: len("NETWORK_PING_OVERRIDE_")] == "NETWORK_PING_OVERRIDE_": + INTERFACE_PING_OVERRIDES[key[len("NETWORK_PING_OVERRIDE_") :]] = IPAddress( + value + ) + + +class Interface: + name: str + mac: str + addresses: list[IPInterface] + gateway: IPAddress | None + + @property + def online(self): + # If we have no addresses, we're not online + if len(self.addresses) == 0: + return False + # If we have an override, ping that + if self.name in INTERFACE_PING_OVERRIDES: + return ping(INTERFACE_PING_OVERRIDES[self.name]) + # If we have no gateway, this interface is likely not intended + # to route beyond the local network, so we'll assume it's + # online, at least for whatever it's used for. + if self.gateway is None: + return True + return ping(self.gateway) + + def asdict(self): + return { + "name": self.name, + "mac": self.mac, + "addresses": [ + { + "address": str(addr.ip), + "netmask": str(addr.netmask), + "network": str(addr.network), + "prefixlen": addr._prefixlen, + } + for addr in self.addresses + ], + "online": self.online, + } + + +def get_first_hop() -> IPAddress | None: + """Get the first network hop.""" + # Use ping to get the first hop + cmd = ["/usr/bin/ping", "-c1", "-W0.3", "-t1", "1.1.1.1"] + result = run(cmd, stdout=PIPE, stderr=PIPE) + try: + ip = IPAddress(result.stdout.decode("utf-8").split("\n")[1].split()[1]) + except (IndexError, AddressValueError): + # If we can't parse the output, return None + return None + return ip + + +def validate_first_hop_is_secure(first_hop: IPAddress): + """Check if the first hop is in a secure network.""" + for name, network in SECURE_FIRSTHOP_NETWORKS.items(): + if first_hop in network: + return name + + +def get_gateways(): + """Get gateways on each interface.""" + gw = gateways().get(AF_INET, []) + result = {} + for iface in INTERFACES_TO_WATCH: + for gateway in gw: + if gateway[1] == iface: + result[iface] = gateway[0] + + return result + + +def interface_status(interface: str, gw): + """Get the status of an interface.""" + try: + addrs = ifaddresses(interface) + except: + addrs = {} + result = Interface() + result.name = interface + result.gateway = gw.get(interface, None) + if AF_LINK in addrs: + result.mac = addrs[AF_LINK][0]["addr"] + else: + result.mac = None + if AF_INET in addrs: + result.addresses = [ + IPInterface(f'{addr["addr"]}/{addr["netmask"]}') + for addr in addrs.get(AF_INET, []) + ] + else: + result.addresses = [] + return result + + +def ping(host: IPAddress) -> bool: + cmd = ["/usr/bin/ping", "-c1", "-w1", str(host)] + result = run(cmd, stdout=DEVNULL, stderr=DEVNULL) + return result.returncode == 0 + + +def get_public_ip(): + """Get the public IP address.""" + cmd = ["/usr/bin/curl", "-s", "https://ipinfo.io"] + result = run(cmd, stdout=PIPE, stderr=PIPE) + try: + data = json.loads(result.stdout.decode("utf-8")) + except (IndexError, ValueError): + return None + try: + # If the IP address is invalid, don't return anything + IPAddress(data["ip"]) + except (AddressValueError, KeyError): + if data.get("status", None) == 429: + # We're rate limited, so return something indicating that + return {"rate_limited": True} + return None + return data + + +def get_default_route(): + """Get the default route.""" + cmd = ["/usr/bin/ip", "route", "show", "default"] + result = run(cmd, stdout=PIPE, stderr=PIPE) + try: + # Get first line (might have multiple gateway routes) + line = result.stdout.decode("utf-8").split("\n")[0] + # Get the gateway link (following "dev") + link = re.search(r"dev\s+(\S+)", line).group(1) + # We already know the gateway IP based on our firsthop check earlier, and that's more reliable since it will respect any routing rules + except (IndexError, AttributeError): + return None + return link + + +def format_time(time: datetime) -> dict[str, str | int]: + """Format a datetime object for display.""" return { - "exists": True, - "online": online, - "connecting": connecting, - "offline": not online and not connecting, - "ip4_addr": ip4_addr, - "ip4_prefix": ip4_prefix_length + "hour": f"{time.hour:02}", + "minute": f"{time.minute:02}", + "second": f"{time.second:02}", + "year": f"{time.year:04}", + "month": f"{time.month:02}", + "day": f"{time.day:02}", + "unix": int(time.timestamp()), } -def trusted(ssid: str | None): - # Don't throw up an "INSECURE" alert when offline - if not ssid: - return True - return ssid in TRUSTED_NETWORKS -def ping(host: str): - ping_cmd = subprocess.Popen(['/usr/bin/ping', '-c1', '-W2', host], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) - return ping_cmd +# system_bus = dbus.SystemBus() +# networkd = system_bus.get_object( +# "org.freedesktop.network1", "/org/freedesktop/network1" +# ) +# manager = dbus.Interface(networkd, "org.freedesktop.network1.Manager") -counter = 0 -ezrinet_last = False -internet_last = False -ezrinet_ping = None -internet_ping = None + +# def get_dbus_interfaces(): +# for iface in INTERFACES_TO_WATCH: +# dbus_object = system_bus.get_object( +# "org.freedesktop.network1", manager.GetLinkByName(iface) +# ) +# dbus_interface = dbus.Interface(dbus_object, "org.freedesktop.network1.Link") +# yield dbus_interface + +runtime_dir = os.environ.get("XDG_RUNTIME_DIR", "/tmp") + + +def load_last_data(var_name: str): + try: + if os.path.exists(f"{runtime_dir}/{var_name}.json"): + with open(f"{runtime_dir}/{var_name}.json", "r") as f: + return json.load(f), os.path.getmtime(f"{runtime_dir}/{var_name}.json") + + except: + pass + return None, None + + +def store_last_data(var_name: str, data): + with open(f"{runtime_dir}/{var_name}.json", "w") as f: + json.dump(data, f) + + +last_default_route = load_last_data("default_route")[0] +last_ip_data, last_request = load_last_data("ip_data") +try: + last_first_hop = IPAddress(load_last_data("first_hop")[0]) +except: + last_first_hop = None while True: - if counter == 0: - ezrinet_ping = ping('10.242.3.1') - internet_ping = ping('1.1.1.1') - if ezrinet_ping.poll() is not None: - ezrinet_last = ezrinet_ping.returncode == 0 - if internet_ping.poll() is not None: - internet_last = internet_ping.returncode == 0 - wifi_data = wifi() - result = { - "wifi": wifi_data, - "network": { - "wlan0": netdev("wlan0"), - "ezrinet": netdev("ezrinet"), - "wg-mullvad": netdev("mullvad"), - }, - "connection": { - "ezrinet": ezrinet_last, - "internet": internet_last - }, - "trusted": trusted(wifi_data.get("ssid", None)) - } - print(json.dumps(result), flush=True) - counter += 1 - # Send pings every 10 seconds - counter %= 3 - sleep(1) + try: + online = ping("1.1.1.1") + except: + online = False + hop = get_first_hop() + gw = get_gateways() + default_route = get_default_route() + # public IP shouldn't change often, so only check every 2 hours or + # if the default route or first hop changes + if ( + default_route != last_default_route + or last_ip_data is None + or last_request is None + or now() - last_request > (2 * 60 * 60) + or (hop != last_first_hop and hop is not None) + ): + print("refreshing public IP", file=sys.stderr, flush=True) + public_ip_data = get_public_ip() + if public_ip_data is None: + print("failed to get public IP", file=sys.stderr, flush=True) + elif public_ip_data.get("rate_limited", False): + last_ip_data = public_ip_data + public_ip_data = None + else: + last_ip_data = public_ip_data + # Write the public IP data to a file + store_last_data("ip_data", public_ip_data) + last_default_route = default_route + store_last_data("default_route", default_route) + last_request = now() + last_first_hop = hop + store_last_data("first_hop", str(hop)) + else: + public_ip_data = last_ip_data + if hop is not None: + secure_msg = validate_first_hop_is_secure(hop) + secure = secure_msg is not None + else: + # If we can't reach the router, assume insecure + secure = False + iface_data = [interface_status(iface, gw) for iface in INTERFACES_TO_WATCH] + iface_dict = {iface.name: iface.asdict() for iface in iface_data} + + default_route_iface_data = interface_status(default_route, gw) + + print( + json.dumps( + { + "online": online, + "secure": secure, + "interfaces": iface_dict, + "public_ip": public_ip_data or {}, + "have_public_ip": public_ip_data is not None and "ip" in public_ip_data, + "default_route": default_route, + "default_interface": default_route_iface_data.asdict(), + "have_default_route": default_route is not None, + "gateway": str(hop), + "have_gateway": hop is not None, + "last_update": format_time(datetime.now()), + } + ), + flush=True, + ) + + sleep(5) diff --git a/.config/htop/htoprc b/.config/htop/htoprc index d673940..e918bed 100644 --- a/.config/htop/htoprc +++ b/.config/htop/htoprc @@ -38,20 +38,20 @@ column_meters_0=Hostname Blank Tasks LoadAverage Uptime Clock column_meter_modes_0=2 2 2 2 2 2 column_meters_1=AllCPUs2 Blank MemorySwap column_meter_modes_1=1 2 1 -tree_view=1 -sort_key=0 -tree_sort_key=0 -sort_direction=1 -tree_sort_direction=1 +tree_view=0 +sort_key=46 +tree_sort_key=46 +sort_direction=-1 +tree_sort_direction=-1 tree_view_always_by_pid=0 all_branches_collapsed=0 screen:Main=PID USER PRIORITY NICE M_VIRT M_RESIDENT M_SHARE STATE PERCENT_CPU PERCENT_MEM TIME Command -.sort_key=PID -.tree_sort_key=PID +.sort_key=PERCENT_CPU +.tree_sort_key=PERCENT_CPU .tree_view_always_by_pid=0 -.tree_view=1 -.sort_direction=1 -.tree_sort_direction=1 +.tree_view=0 +.sort_direction=-1 +.tree_sort_direction=-1 .all_branches_collapsed=0 screen:I/O=PID USER IO_PRIORITY IO_RATE IO_READ_RATE IO_WRITE_RATE PERCENT_SWAP_DELAY PERCENT_IO_DELAY Command .sort_key=IO_RATE diff --git a/.config/sway/config b/.config/sway/config index 59b8f97..1f90343 100644 --- a/.config/sway/config +++ b/.config/sway/config @@ -199,6 +199,9 @@ for_window [floating] border normal 1 ## Thunderbird New.* windows should float for_window [app_id="thunderbird" title="(New|Write)"] floating enable +## Bitwarden extension windows should float by default +for_window [app_id="firefox" title="Extension: \(Bitwarden Password Manager\)"] floating enable + ### ### # Application Keybinds # ### ### diff --git a/.config/sway/local-config.d/10-gtk-theme.conf##hostname.gathering-storm b/.config/sway/local-config.d/10-gtk-theme.conf##hostname.gathering-storm deleted file mode 100644 index 472de82..0000000 --- a/.config/sway/local-config.d/10-gtk-theme.conf##hostname.gathering-storm +++ /dev/null @@ -1,3 +0,0 @@ -#-*-conf-space-*- - -exec_always gsettings set org.gnome.desktop.interface text-scaling-factor 1.25 diff --git a/.config/sway/workspace-arrangement.conf##class.single-monitor b/.config/sway/workspace-arrangement.conf##class.single-monitor index f8c15d9..163810c 100644 --- a/.config/sway/workspace-arrangement.conf##class.single-monitor +++ b/.config/sway/workspace-arrangement.conf##class.single-monitor @@ -20,30 +20,3 @@ workspace { 10 output $display } -## Workspace Switching Keybinds -bindsym { - $mod+1 exec swaymsg workspace 1 - $mod+2 exec swaymsg workspace 2 - $mod+3 exec swaymsg workspace 3 - $mod+4 exec swaymsg workspace 4 - $mod+5 exec swaymsg workspace 5 - $mod+6 exec swaymsg workspace 6 - $mod+7 exec swaymsg workspace 7 - $mod+8 exec swaymsg workspace 8 - $mod+9 exec swaymsg workspace 9 - $mod+0 exec swaymsg workspace 10 -} - -## Window Reassignment Keybinds -bindsym { - $mod+Shift+1 exec swaymsg move container to workspace 1 - $mod+Shift+2 exec swaymsg move container to workspace 2 - $mod+Shift+3 exec swaymsg move container to workspace 3 - $mod+Shift+4 exec swaymsg move container to workspace 4 - $mod+Shift+5 exec swaymsg move container to workspace 5 - $mod+Shift+6 exec swaymsg move container to workspace 6 - $mod+Shift+7 exec swaymsg move container to workspace 7 - $mod+Shift+8 exec swaymsg move container to workspace 8 - $mod+Shift+9 exec swaymsg move container to workspace 9 - $mod+Shift+0 exec swaymsg move container to workspace 10 -} diff --git a/.config/sway/workspaces.json##hostname.gathering-storm b/.config/sway/workspaces.json##hostname.gathering-storm index ba2ca28..fa70f06 100644 --- a/.config/sway/workspaces.json##hostname.gathering-storm +++ b/.config/sway/workspaces.json##hostname.gathering-storm @@ -1,76 +1,249 @@ { - "default_context": "personal", - "display_ordering": ["builtin"], - "display_layout": { - "builtin": "eDP-1" - }, + "default_context": "personal-portable", + "workspaces": [ + { + "index": 1, + "name": "console", + "exec": "console", + "program_name": "console" + }, + { + "index": 2, + "name": "code", + "exec": "emacsclient", + "args": ["-nc"], + "program_name": "emacsclient" + }, + { + "index": 3, + "name": "documentation", + "exec": "firefox", + "args": ["--new-window"], + "environ": {}, + "program_name": "firefox" + }, + { + "index": 4, + "name": "project", + "exec": "firefox", + "args": ["--new-window"], + "program_name": "firefox" + }, + { + "index": 5, + "name": "discord", + "exec": "discord", + "program_name": "discord" + }, + { + "index": 6, + "name": "console", + "exec": "console", + "program_name": "console" + }, + { + "index": 7, + "name": "code", + "exec": "emacsclient", + "args": ["-nc"], + "program_name": "emacsclient" + }, + { + "index": 8, + "name": "internet", + "exec": "firefox", + "args": ["--new-window"], + "program_name": "firefox" + }, + { + "index": 9, + "name": "project", + "exec": "firefox-developer-edition", + "args": ["-start-debugger-server", "--new-window"], + "program_name": "firefox-developer-edition" + }, + { + "index": 10, + "name": "server management", + "exec": "virt-manager", + "program_name": "virt-manager", + "systemd": false + }, + { + "index": 11, + "name": "password management", + "exec": "bitwarden-desktop", + "program_name": "bitwarden" + }, + { + "index": 12, + "name": "video", + "exec": "jellyfinmediaplayer", + "program_name": "jellyfinmediaplayer" + }, + { + "index": 13, + "name": "edex-ui", + "exec": "edex-ui", + "program_name": "edex-ui" + }, + { + "index": 14, + "name": "mail", + "exec": "thunderbird", + "program_name": "thunderbird", + "environ": { + "MOZ_ENABLE_WAYLAND": "1" + } + }, + { + "index": 15, + "name": "config", + "exec": "console", + "program_name": "console" + }, + { + "index": 16, + "name": "console", + "exec": "console", + "program_name": "console" + }, + { + "index": 17, + "name": "code", + "exec": "emacsclient", + "args": ["-nc"], + "program_name": "emacsclient" + }, + { + "index": 18, + "name": "music", + "exec": "feishin", + "program_name": "feishin" + }, + { + "index": 19, + "name": "project", + "exec": "firefox", + "args": ["--new-window"], + "program_name": "firefox" + }, + { + "index": 20, + "name": "slack", + "exec": "slack", + "program_name": "slack", + "args": [ + "--enable-features=UseOzonePlatform", + "--ozone-platform=wayland", + "--enable-gpu-rasterization" + ], + "memory_profile": { + "high": "800M", + "max": "1.2G" + } + }, + { + "index": 21, + "name": "ferrets", + "exec": "firefox", + "args": ["--new-window", "https://www.twitch.tv/ferretsoftware"], + "program_name": "firefox" + } + ], "contexts": { - "personal": { - "builtin": [ + "docked": { + "primary": "center", + "outputs": [ { - "index": 1, - "name": "terminal", - "exec": "console", - "program_name": "console" + "names": ["eDP-1"], + "group": "builtin", + "position": [0, 600], + "eww_windows": ["builtinbar"], + "mode": "2560x1600@165Hz scale 1.5 color_profile icc /usr/share/color/icc/colord/BOE_CQ_______NE160QDM_NZ6.icm" }, { - "index": 2, - "name": "code", - "exec": "emacsclient", - "args": ["-nc"], - "program_name": "emacsclient" + "make": "Acer Technologies", + "model": "XV271U M3", + "serial": "1322131231233", + "group": "left", + "position": [1707, 0], + "mode": "2560x1440@120Hz", + "eww_windows": ["leftbar", "sidebar"] }, { - "index": 3, - "name": "internet", - "exec": "firefox", - "args": ["--new-window"], - "program_name": "firefox" + "make": "Dell Inc.", + "model": "DELL U3818DW", + "serial": "97F8P9350W0L", + "group": "center", + "position": [4267, 0], + "mode": "3840x1600", + "eww_windows": ["centerbar"] }, { - "index": 4, - "name": "project", - "exec": "firefox", - "args": ["--new-window"], - "program_name": "firefox" - }, - { - "index": 5, - "name": "servers", - "exec": "virt-manager", - "program_name": "virt-manager" - }, - { - "index": 6, - "name": "steam", - "exec": "steam", - "program_name": "steam" - }, - { - "index": 7, - "name": "media", - "exec": "jellyfinmediaplayer", - "program_name": "jellyfinmediaplayer" - }, - { - "index": 8, - "name": "real-time comms", - "exec": "discord", - "program_name": "discord" - }, - { - "index": 9, - "name": "messages", - "exec": "thunderbird", - "program_name": "thunderbird" - }, - { - "index": 10, - "name": "config", - "exec": "pavucontrol", - "program_name": "pavucontrol" + "make": "Acer Technologies", + "model": "XV271U M3", + "serial": "1431038964205", + "group": "right", + "position": [8107, 0], + "mode": "2560x1440@120Hz", + "eww_windows": ["rightbar"] } - ] + ], + "groups": { + "builtin": { + "workspaces": [21], + "reverse": false + }, + "left": { + "workspaces": [1, 2, 3, 4, 5], + "reverse": true + }, + "center": { + "workspaces": [6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + "reverse": false + }, + "right": { + "workspaces": [16, 17, 18, 19, 20], + "reverse": false + } + } + }, + "work-portable": { + "primary": "builtin", + "outputs": [ + { + "names": ["eDP-1"], + "group": "builtin", + "position": [0, 0], + "eww_windows": ["builtinbar"], + "mode": "2560x1600@165Hz scale 1.25 color_profile icc /usr/share/color/icc/colord/BOE_CQ_______NE160QDM_NZ6.icm" + } + ], + "groups": { + "builtin": { + "workspaces": [6, 7, 8, 9, 10, 11, 18, 20, 14, 15], + "reverse": false + } + } + }, + "personal-portable": { + "primary": "builtin", + "outputs": [ + { + "names": ["eDP-1"], + "group": "builtin", + "position": [0, 0], + "eww_windows": ["builtinbar"], + "mode": "2560x1600@165Hz scale 1.25 color_profile icc /usr/share/color/icc/colord/BOE_CQ_______NE160QDM_NZ6.icm" + } + ], + "groups": { + "builtin": { + "workspaces": [6, 7, 8, 9, 10, 11, 18, 5, 14, 15], + "reverse": false + } + } } } } diff --git a/.config/systemd/user/emacs.service b/.config/systemd/user/emacs.service index da18f78..056cba3 100644 --- a/.config/systemd/user/emacs.service +++ b/.config/systemd/user/emacs.service @@ -13,8 +13,8 @@ Restart=on-failure Slice=session.slice MemoryAccounting=yes # Tide eats memory like crazy, so lets put it on a diet. -MemoryHigh=3G -MemoryMax=5G +MemoryHigh=8G +MemoryMax=10G [Install] WantedBy=graphical-session.target diff --git a/.config/systemd/user/eww.service b/.config/systemd/user/eww.service index 36510b3..55e5b2c 100644 --- a/.config/systemd/user/eww.service +++ b/.config/systemd/user/eww.service @@ -3,8 +3,8 @@ Description=eww status bars PartOf=sway-session.target [Service] -Type=forking -ExecStart=eww daemon +Type=simple +ExecStart=eww daemon --no-daemonize ExecReload=eww reload ExecStop=eww kill ExecStopPost=sleep 1 diff --git a/.config/zsh/alias.zsh b/.config/zsh/alias.zsh index eadfc2b..9e51fd3 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="source $HOME/.zshrc" +alias reconf="exec $SHELL" alias cp="cp -i --reflink=auto" alias mv="mv -i" diff --git a/.config/zsh/env.zsh b/.config/zsh/env.zsh index 0eccad7..1b6b5a2 100644 --- a/.config/zsh/env.zsh +++ b/.config/zsh/env.zsh @@ -11,7 +11,7 @@ PATH_wincmake=/opt/msvcmake/bin:/opt/msvc/bin/x64 cdpath=(~ ~/src) -export PATH=.:$PATH_asdf:$PATH_node:$PATH_pyenv:$PATH_java:$PATH_user:$PATH_system +export PATH=.:$PATH_node:$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/.config/zsh/zz-asdf.zsh b/.config/zsh/zz-asdf.zsh deleted file mode 100644 index fdba167..0000000 --- a/.config/zsh/zz-asdf.zsh +++ /dev/null @@ -1,2 +0,0 @@ -# source $HOME/.local/lib/asdf/asdf.sh -# source $HOME/.local/lib/asdf/completions/asdf.bash diff --git a/.emacs.d/settings.org b/.emacs.d/settings.org index 66cced0..8f8919a 100644 --- a/.emacs.d/settings.org +++ b/.emacs.d/settings.org @@ -513,26 +513,29 @@ Indent using tabs, render with tab-width of 2. :config (setq company-idle-delay 0.3)) #+END_SRC -** LSP Mode +** LSP #+BEGIN_SRC emacs-lisp - (use-package lsp-mode - :ensure t - :init - ;; set prefix for lsp-command-keymap (few alternatives - "C-l", "C-c l") - (setq lsp-keymap-prefix "C-c l") - :hook ((python-mode . lsp) ;; pip install python-lsp-server pyls-black pyls-isort pyls-mypy - (elixir-mode . lsp) - (rust-mode . lsp) - (java-mode . lsp) - (php-mode . lsp) - (typescript-mode . lsp) ;; npm install -g typescript typescript-language-server - (lsp-mode . lsp-enable-which-key-integration)) - :config (lsp-register-custom-settings - '(("pyls.plugins.pyls_mypy.enabled" t t) - ("pyls.plugins.pyls_mypy.live_mode" nil t) - ("pyls.plugins.pyls_black.enabled" t t) - ("pyls.plugins.pyls_isort.enabled" t t))) - :commands lsp) + ;; (use-package lsp-mode + ;; :ensure t + ;; :init + ;; ;; set prefix for lsp-command-keymap (few alternatives - "C-l", "C-c l") + ;; (setq lsp-keymap-prefix "C-c l") + ;; :hook ((python-mode . lsp) ;; pip install python-lsp-server pyls-black pyls-isort pyls-mypy + ;; (elixir-mode . lsp) + ;; (rust-mode . lsp) + ;; (java-mode . lsp) + ;; (php-mode . lsp) + ;; (typescript-mode . lsp) ;; npm install -g typescript typescript-language-server + ;; ) + ;; :config (lsp-register-custom-settings + ;; '(("pyls.plugins.pyls_mypy.enabled" t t) + ;; ("pyls.plugins.pyls_mypy.live_mode" nil t) + ;; ("pyls.plugins.pyls_black.enabled" t t) + ;; ("pyls.plugins.pyls_isort.enabled" t t))) + ;; :commands lsp) + + (use-package eglot + :ensure t) #+END_SRC ** Languages @@ -570,11 +573,11 @@ After installing the ~rust-analyzer~ program, the following can be used: (add-to-list 'auto-mode-alist '("\\.tsx\\'" . tsx-ts-mode)) - (add-hook 'tsx-ts-mode-hook #'lsp) - (add-hook 'tsx-ts-mode-hook #'setup-tide-mode) + ;; (add-hook 'tsx-ts-mode-hook #'eglot) + ;; (add-hook 'tsx-ts-mode-hook #'setup-tide-mode) - (add-hook 'typescript-ts-mode-hook #'lsp) - (add-hook 'typescript-ts-mode-hook #'setup-tide-mode) + ;; (add-hook 'typescript-ts-mode-hook #'eglot) + ;; (add-hook 'typescript-ts-mode-hook #'setup-tide-mode) #+END_SRC **** TIDE #+BEGIN_SRC emacs-lisp