Updates for gathering storm

This commit is contained in:
Ezri Brimhall 2024-11-07 13:38:53 -07:00
parent 06b1f4fb40
commit a279fc4923
15 changed files with 958 additions and 249 deletions

View File

@ -63,6 +63,3 @@ dynamic_padding = true
dynamic_title = true dynamic_title = true
opacity = 0.8 opacity = 0.8
title = "Terminal" title = "Terminal"
[general]
working_directory = "/home/ezri"

View File

@ -12,6 +12,76 @@
(include "./windows.yuck") (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 (defwindow builtinbar
:monitor 0 :monitor 0
:geometry (geometry :width "100%" :geometry (geometry :width "100%"

View File

@ -1,77 +1,347 @@
;; -*-lisp-*- ;; -*-lisp-*-
(deflisten network--data (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" (box :orientation "h"
:halign "start" :halign "start"
:valign "center"
:space-evenly false :space-evenly false
:spacing 10 :spacing 10
(label :class "offline" (label :class "medium green nebula"
:visible {network--data["network"][device]["offline"]} :visible {network--data.online}
:text "offline") :text "online")
(label :visible {network--data["network"][device]["connecting"]} (label :class "medium offline nebula"
:class "highlight" :visible {network--data.offline}
:text "connecting...") :text "offline")
(label :visible {network--data["network"][device]["online"] && !network--data.network[device].connecting} (label :class "medium highlight nebula"
:class "special" :visible {network--data.configuring}
:text "${network--data['wifi']['ssid']}") :text "configuring")))
(label :visible {!network--data.connection.internet && network--data.network[device].online}
:class "highlight"
:text "- no internet"
)))
(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" (box :orientation "h"
:halign "start" :halign "start"
:space-evenly false :space-evenly false
:spacing 0 :spacing 0
(label :class "offline" (label :class "offline"
:visible {network--data["network"][device]["offline"]} :visible {network--data
:text "offline") ["network"]
(label :visible {network--data["network"][device]["connecting"]} [device]
:class "highlight" ["offline"]
:text "connecting...") }
(label :visible {network--data["network"][device]["online"] && !network--data.network[device].connecting} :text "offline")
:class "special" (label :visible {network--data
:text "${network--data['network'][device]['ip4_addr']}/${network--data['network'][device]['ip4_prefix']}"))) ["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" (box :orientation "h"
:halign "start" :halign "start"
:space-evenly false :space-evenly false
:spacing 0 :spacing 0
:visible {network--data.connection.internet && (network--data["network"][device]["exists"] || required)}
(label :class "highlight" (label :class "highlight"
:text "- insecure" :text "- insecure"
:visible {! network--data["network"][device]["exists"]}) :visible {!
network--data.secure })
(label :class "green" (label :class "green"
:text "- secured" :text "- secured via ${network--data.secure_msg}"
:visible {network--data["network"][device]["exists"]}))) :visible {network--data.secure})))
(defwidget vpn-network [] (defwidget vpn-network
[]
(box :orientation "v" (box :orientation "v"
:halign "start" :halign "start"
:space-evenly false :space-evenly false
:spacing 0 :spacing 0
:visible {network--data
!=
''}
(label :class "highlight" (label :class "highlight"
:text "offline" :text "offline"
:visible {!network--data.connection.ezrinet}) :visible {!
network--data.interfaces.ezrinet.online })
(label :class "green" (label :class "green"
:text "connected" :text "connected"
:visible {network--data.connection.ezrinet}) :visible {network--data.interfaces.ezrinet.online})
"personal network")) "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" (box :orientation "v"
:halign "start" :halign "start"
:space-evenly false :space-evenly false
:spacing 0 :spacing 0
(box :orientation "h" (network--status-summary)
:halign "center" (network--public-ip)
:space-evenly false (network--default-route)
:spacing 10 (network--gateway)
(network--wlan :device "wlan0") (centerbox :orientation "h"
(network--proxy-vpn :device "wg-mullvad" :required {!network--data.trusted})) :halign "start"
"communications")) :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})))
))

View File

@ -1,81 +1,306 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import subprocess import trparse
from subprocess import run, PIPE, DEVNULL
import json 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(): # These are networks that are considered secure. All traffic being routed
ssid_cmd = subprocess.run(['iwgetid', '-r'], capture_output = True) # through one of these networks is being encrypted and routed through an
if ssid_cmd.returncode != 0: # anonymizing service.
return {"connected": False, "ssid": None} SECURE_FIRSTHOP_NETWORKS = {
ssid = ssid_cmd.stdout.decode('utf-8').strip() "home": IPNetwork("10.242.0.0/16"), # Personal network
ssid = ssid if len(ssid) <= 15 else f"{ssid[:14]}…" "mullvad": IPNetwork("10.64.0.1/32"), # Mullvad VPN
return {"connected": True, "ssid": ssid} "usu": IPNetwork("129.123.8.126/32"), # USU VPN
}
def netdev(device: str): # Interfaces that we should check
ip_cmd = subprocess.run(['ip', '-j', 'addr', 'show', device], capture_output = True) DEFAULT_INTERFACES_TO_WATCH = ["lan", "ezrinet"]
if ip_cmd.returncode != 0: # Interfaces that we should use when determining if we are online
return {"exists": False} DEFAULT_INTERFACES_TO_COUNT = ["lan"]
ip_data = json.loads(ip_cmd.stdout.decode('utf-8'))[0] # Overrides for IP addresses to ping to determine if an interface is
online = ip_data["operstate"] == "UP" # online This is useful for interfaces that don't have or report a
ip4_addr = "" # gateway, but we still want to check if they're online, such as the
ip4_prefix_length = 24 # ezrinet interface.
addr4_info = list(filter(lambda addr: addr.get("family") == "inet", ip_data["addr_info"])) INTERFACE_PING_OVERRIDES = {"ezrinet": IPAddress("10.242.3.1")}
if len(addr4_info) >= 1:
ip4_addr = addr4_info[0]["local"] INTERFACES_TO_WATCH = os.environ.get(
ip4_prefix_length = addr4_info[0]["prefixlen"] "NETWORK_INTERFACES_TO_WATCH", ",".join(DEFAULT_INTERFACES_TO_WATCH)
connecting = ip_data["operstate"] != "DOWN" and (ip4_addr == "" or not online) ).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 { return {
"exists": True, "hour": f"{time.hour:02}",
"online": online, "minute": f"{time.minute:02}",
"connecting": connecting, "second": f"{time.second:02}",
"offline": not online and not connecting, "year": f"{time.year:04}",
"ip4_addr": ip4_addr, "month": f"{time.month:02}",
"ip4_prefix": ip4_prefix_length "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): # system_bus = dbus.SystemBus()
ping_cmd = subprocess.Popen(['/usr/bin/ping', '-c1', '-W2', host], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) # networkd = system_bus.get_object(
return ping_cmd # "org.freedesktop.network1", "/org/freedesktop/network1"
# )
# manager = dbus.Interface(networkd, "org.freedesktop.network1.Manager")
counter = 0
ezrinet_last = False # def get_dbus_interfaces():
internet_last = False # for iface in INTERFACES_TO_WATCH:
ezrinet_ping = None # dbus_object = system_bus.get_object(
internet_ping = None # "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: while True:
if counter == 0: try:
ezrinet_ping = ping('10.242.3.1') online = ping("1.1.1.1")
internet_ping = ping('1.1.1.1') except:
if ezrinet_ping.poll() is not None: online = False
ezrinet_last = ezrinet_ping.returncode == 0 hop = get_first_hop()
if internet_ping.poll() is not None: gw = get_gateways()
internet_last = internet_ping.returncode == 0 default_route = get_default_route()
wifi_data = wifi() # public IP shouldn't change often, so only check every 2 hours or
result = { # if the default route or first hop changes
"wifi": wifi_data, if (
"network": { default_route != last_default_route
"wlan0": netdev("wlan0"), or last_ip_data is None
"ezrinet": netdev("ezrinet"), or last_request is None
"wg-mullvad": netdev("mullvad"), or now() - last_request > (2 * 60 * 60)
}, or (hop != last_first_hop and hop is not None)
"connection": { ):
"ezrinet": ezrinet_last, print("refreshing public IP", file=sys.stderr, flush=True)
"internet": internet_last public_ip_data = get_public_ip()
}, if public_ip_data is None:
"trusted": trusted(wifi_data.get("ssid", None)) print("failed to get public IP", file=sys.stderr, flush=True)
} elif public_ip_data.get("rate_limited", False):
print(json.dumps(result), flush=True) last_ip_data = public_ip_data
counter += 1 public_ip_data = None
# Send pings every 10 seconds else:
counter %= 3 last_ip_data = public_ip_data
sleep(1) # 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)

View File

@ -38,20 +38,20 @@ column_meters_0=Hostname Blank Tasks LoadAverage Uptime Clock
column_meter_modes_0=2 2 2 2 2 2 column_meter_modes_0=2 2 2 2 2 2
column_meters_1=AllCPUs2 Blank MemorySwap column_meters_1=AllCPUs2 Blank MemorySwap
column_meter_modes_1=1 2 1 column_meter_modes_1=1 2 1
tree_view=1 tree_view=0
sort_key=0 sort_key=46
tree_sort_key=0 tree_sort_key=46
sort_direction=1 sort_direction=-1
tree_sort_direction=1 tree_sort_direction=-1
tree_view_always_by_pid=0 tree_view_always_by_pid=0
all_branches_collapsed=0 all_branches_collapsed=0
screen:Main=PID USER PRIORITY NICE M_VIRT M_RESIDENT M_SHARE STATE PERCENT_CPU PERCENT_MEM TIME Command screen:Main=PID USER PRIORITY NICE M_VIRT M_RESIDENT M_SHARE STATE PERCENT_CPU PERCENT_MEM TIME Command
.sort_key=PID .sort_key=PERCENT_CPU
.tree_sort_key=PID .tree_sort_key=PERCENT_CPU
.tree_view_always_by_pid=0 .tree_view_always_by_pid=0
.tree_view=1 .tree_view=0
.sort_direction=1 .sort_direction=-1
.tree_sort_direction=1 .tree_sort_direction=-1
.all_branches_collapsed=0 .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 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 .sort_key=IO_RATE

View File

@ -199,6 +199,9 @@ for_window [floating] border normal 1
## Thunderbird New.* windows should float ## Thunderbird New.* windows should float
for_window [app_id="thunderbird" title="(New|Write)"] floating enable 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 # # Application Keybinds #
### ### ### ###

View File

@ -1,3 +0,0 @@
#-*-conf-space-*-
exec_always gsettings set org.gnome.desktop.interface text-scaling-factor 1.25

View File

@ -20,30 +20,3 @@ workspace {
10 output $display 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
}

View File

@ -1,76 +1,249 @@
{ {
"default_context": "personal", "default_context": "personal-portable",
"display_ordering": ["builtin"], "workspaces": [
"display_layout": { {
"builtin": "eDP-1" "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": { "contexts": {
"personal": { "docked": {
"builtin": [ "primary": "center",
"outputs": [
{ {
"index": 1, "names": ["eDP-1"],
"name": "terminal", "group": "builtin",
"exec": "console", "position": [0, 600],
"program_name": "console" "eww_windows": ["builtinbar"],
"mode": "2560x1600@165Hz scale 1.5 color_profile icc /usr/share/color/icc/colord/BOE_CQ_______NE160QDM_NZ6.icm"
}, },
{ {
"index": 2, "make": "Acer Technologies",
"name": "code", "model": "XV271U M3",
"exec": "emacsclient", "serial": "1322131231233",
"args": ["-nc"], "group": "left",
"program_name": "emacsclient" "position": [1707, 0],
"mode": "2560x1440@120Hz",
"eww_windows": ["leftbar", "sidebar"]
}, },
{ {
"index": 3, "make": "Dell Inc.",
"name": "internet", "model": "DELL U3818DW",
"exec": "firefox", "serial": "97F8P9350W0L",
"args": ["--new-window"], "group": "center",
"program_name": "firefox" "position": [4267, 0],
"mode": "3840x1600",
"eww_windows": ["centerbar"]
}, },
{ {
"index": 4, "make": "Acer Technologies",
"name": "project", "model": "XV271U M3",
"exec": "firefox", "serial": "1431038964205",
"args": ["--new-window"], "group": "right",
"program_name": "firefox" "position": [8107, 0],
}, "mode": "2560x1440@120Hz",
{ "eww_windows": ["rightbar"]
"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"
} }
] ],
"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
}
}
} }
} }
} }

View File

@ -13,8 +13,8 @@ Restart=on-failure
Slice=session.slice Slice=session.slice
MemoryAccounting=yes MemoryAccounting=yes
# Tide eats memory like crazy, so lets put it on a diet. # Tide eats memory like crazy, so lets put it on a diet.
MemoryHigh=3G MemoryHigh=8G
MemoryMax=5G MemoryMax=10G
[Install] [Install]
WantedBy=graphical-session.target WantedBy=graphical-session.target

View File

@ -3,8 +3,8 @@ Description=eww status bars
PartOf=sway-session.target PartOf=sway-session.target
[Service] [Service]
Type=forking Type=simple
ExecStart=eww daemon ExecStart=eww daemon --no-daemonize
ExecReload=eww reload ExecReload=eww reload
ExecStop=eww kill ExecStop=eww kill
ExecStopPost=sleep 1 ExecStopPost=sleep 1

View File

@ -7,7 +7,7 @@ alias userctl="\systemctl --user"
alias ujournalctl="\journalctl --user" alias ujournalctl="\journalctl --user"
alias reconf="source $HOME/.zshrc" alias reconf="exec $SHELL"
alias cp="cp -i --reflink=auto" alias cp="cp -i --reflink=auto"
alias mv="mv -i" alias mv="mv -i"

View File

@ -11,7 +11,7 @@ PATH_wincmake=/opt/msvcmake/bin:/opt/msvc/bin/x64
cdpath=(~ ~/src) 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 to run a command with only the "safe" system PATH element
function safeexec { function safeexec {

View File

@ -1,2 +0,0 @@
# source $HOME/.local/lib/asdf/asdf.sh
# source $HOME/.local/lib/asdf/completions/asdf.bash

View File

@ -513,26 +513,29 @@ Indent using tabs, render with tab-width of 2.
:config :config
(setq company-idle-delay 0.3)) (setq company-idle-delay 0.3))
#+END_SRC #+END_SRC
** LSP Mode ** LSP
#+BEGIN_SRC emacs-lisp #+BEGIN_SRC emacs-lisp
(use-package lsp-mode ;; (use-package lsp-mode
:ensure t ;; :ensure t
:init ;; :init
;; set prefix for lsp-command-keymap (few alternatives - "C-l", "C-c l") ;; ;; set prefix for lsp-command-keymap (few alternatives - "C-l", "C-c l")
(setq lsp-keymap-prefix "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 ;; :hook ((python-mode . lsp) ;; pip install python-lsp-server pyls-black pyls-isort pyls-mypy
(elixir-mode . lsp) ;; (elixir-mode . lsp)
(rust-mode . lsp) ;; (rust-mode . lsp)
(java-mode . lsp) ;; (java-mode . lsp)
(php-mode . lsp) ;; (php-mode . lsp)
(typescript-mode . lsp) ;; npm install -g typescript typescript-language-server ;; (typescript-mode . lsp) ;; npm install -g typescript typescript-language-server
(lsp-mode . lsp-enable-which-key-integration)) ;; )
:config (lsp-register-custom-settings ;; :config (lsp-register-custom-settings
'(("pyls.plugins.pyls_mypy.enabled" t t) ;; '(("pyls.plugins.pyls_mypy.enabled" t t)
("pyls.plugins.pyls_mypy.live_mode" nil t) ;; ("pyls.plugins.pyls_mypy.live_mode" nil t)
("pyls.plugins.pyls_black.enabled" t t) ;; ("pyls.plugins.pyls_black.enabled" t t)
("pyls.plugins.pyls_isort.enabled" t t))) ;; ("pyls.plugins.pyls_isort.enabled" t t)))
:commands lsp) ;; :commands lsp)
(use-package eglot
:ensure t)
#+END_SRC #+END_SRC
** Languages ** 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-to-list 'auto-mode-alist '("\\.tsx\\'" . tsx-ts-mode))
(add-hook 'tsx-ts-mode-hook #'lsp) ;; (add-hook 'tsx-ts-mode-hook #'eglot)
(add-hook 'tsx-ts-mode-hook #'setup-tide-mode) ;; (add-hook 'tsx-ts-mode-hook #'setup-tide-mode)
(add-hook 'typescript-ts-mode-hook #'lsp) ;; (add-hook 'typescript-ts-mode-hook #'eglot)
(add-hook 'typescript-ts-mode-hook #'setup-tide-mode) ;; (add-hook 'typescript-ts-mode-hook #'setup-tide-mode)
#+END_SRC #+END_SRC
**** TIDE **** TIDE
#+BEGIN_SRC emacs-lisp #+BEGIN_SRC emacs-lisp