INIT!
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
history.txt
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
nushell/history.txt
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
paste_actions filter
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
background-color=#000000
|
||||||
|
text-color=#FFFFFF
|
||||||
|
border-size=0
|
||||||
|
border-radius=0
|
||||||
|
padding=10
|
||||||
|
margin=10
|
||||||
|
default-timeout=5000
|
||||||
|
anchor=top-right
|
||||||
|
width=300
|
||||||
|
height=100
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
$env.PROMPT_COMMAND = {
|
||||||
|
let user = (whoami)
|
||||||
|
let host = (sys host | get hostname)
|
||||||
|
let cwd = ($env.PWD | path expand | str replace $env.HOME "~")
|
||||||
|
|
||||||
|
$"(ansi white)($user)@($host)\n(ansi white)($cwd)(ansi reset)"
|
||||||
|
}
|
||||||
|
|
||||||
|
$env.PROMPT_INDICATOR = { $"(ansi white)$ (ansi reset)" }
|
||||||
|
$env.PROMPT_INDICATOR_VI_INSERT = { $"(ansi white)$ (ansi reset)" }
|
||||||
|
$env.PROMPT_INDICATOR_VI_NORMAL = { $"(ansi white)$ (ansi reset)" }
|
||||||
|
$env.LANG = "en_US.UTF-8"
|
||||||
|
$env.LC_ALL = "en_US.UTF-8"
|
||||||
|
$env.LC_CTYPE = "en_US.UTF-8"
|
||||||
|
$env.config.color_config = {
|
||||||
|
text: light_white
|
||||||
|
shape_keyword: light_white
|
||||||
|
shape_string: light_green
|
||||||
|
shape_int: light_white
|
||||||
|
shape_float: light_white
|
||||||
|
shape_bool: light_yellow
|
||||||
|
shape_operator: light_white
|
||||||
|
shape_variable: light_white
|
||||||
|
shape_flag: light_white
|
||||||
|
shape_external: light_white
|
||||||
|
shape_path: light_white
|
||||||
|
shape_error: light_red
|
||||||
|
}
|
||||||
|
$env.config.show_banner = false
|
||||||
|
$env.PROMPT_COMMAND_RIGHT = { "" }
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# env.nu
|
||||||
|
#
|
||||||
|
# Installed by:
|
||||||
|
# version = "0.109.1"
|
||||||
|
#
|
||||||
|
# Previously, environment variables were typically configured in `env.nu`.
|
||||||
|
# In general, most configuration can and should be performed in `config.nu`
|
||||||
|
# or one of the autoload directories.
|
||||||
|
#
|
||||||
|
# This file is generated for backwards compatibility for now.
|
||||||
|
# It is loaded before config.nu and login.nu
|
||||||
|
#
|
||||||
|
# See https://www.nushell.sh/book/configuration.html
|
||||||
|
#
|
||||||
|
# Also see `help config env` for more options.
|
||||||
|
#
|
||||||
|
# You can remove these comments if you want or leave
|
||||||
|
# them for future reference.
|
||||||
|
let base_xdg = [
|
||||||
|
"/usr/local/share"
|
||||||
|
"/usr/share"
|
||||||
|
]
|
||||||
|
|
||||||
|
let flatpak_xdg = [
|
||||||
|
"/var/lib/flatpak/exports/share"
|
||||||
|
($env.HOME | path join ".local/share/flatpak/exports/share")
|
||||||
|
]
|
||||||
|
|
||||||
|
$env.XDG_DATA_DIRS = ($base_xdg | append $flatpak_xdg | str join ":")
|
||||||
|
|
||||||
|
$env.PYENV_ROOT = ($env.HOME | path join ".pyenv")
|
||||||
|
$env.PATH = ($env.PATH | split row (char env_sep) | prepend $'($env.PYENV_ROOT)/bin' | prepend $'($env.PYENV_ROOT)/shims')
|
||||||
|
alias sway = dbus-run-session sway
|
||||||
Executable
+6
@@ -0,0 +1,6 @@
|
|||||||
|
set $base ~/.config/sway
|
||||||
|
|
||||||
|
xwayland enable
|
||||||
|
include ~/.config/mute/monitors.conf
|
||||||
|
|
||||||
|
exec_always sh -c 'BASE_SWAY="$base" /usr/bin/python $base/main/boot.py'
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
from swayipc import Swayipc
|
||||||
|
|
||||||
|
|
||||||
|
def get_base():
|
||||||
|
return os.environ.get("BASE_SWAY", os.path.expanduser("~/.config/sway"))
|
||||||
|
|
||||||
|
|
||||||
|
Logs = "/tmp/sway_boot.log"
|
||||||
|
|
||||||
|
|
||||||
|
def apply_safe_config(sway):
|
||||||
|
try:
|
||||||
|
sway.cmd("floating_modifier Mod4")
|
||||||
|
sway.cmd("bindsym Mod4+Shift+grave reload")
|
||||||
|
sway.cmd("bindsym Mod4+Shift+escape exit")
|
||||||
|
sway.cmd("bindsym Mod4+q exec kitty")
|
||||||
|
sway.cmd("bindsym Mod4+c kill")
|
||||||
|
sway.cmd("exec notify-send 'Sway FUCKED!' 'Fallback active'")
|
||||||
|
sway.cmd('for_window [app_id="xpad"] floating enable')
|
||||||
|
except Exception:
|
||||||
|
print("truely fucked")
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def boot(base):
|
||||||
|
base = os.path.expanduser(base) if base else ""
|
||||||
|
|
||||||
|
with open(Logs, "w") as f:
|
||||||
|
return subprocess.run(
|
||||||
|
["python3", os.path.join(base, "main/swayconfig.py")],
|
||||||
|
stdout=f,
|
||||||
|
stderr=f
|
||||||
|
).returncode
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
sway = Swayipc()
|
||||||
|
base = get_base()
|
||||||
|
|
||||||
|
rc = boot(base)
|
||||||
|
|
||||||
|
if rc != 0:
|
||||||
|
apply_safe_config(sway)
|
||||||
|
subprocess.Popen(["xpad", "-f", Logs])
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,198 @@
|
|||||||
|
import subprocess
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import tempfile
|
||||||
|
import base64
|
||||||
|
from datetime import datetime
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
Recording_flag = "/tmp/screenshot_active"
|
||||||
|
Recordings = Path.home() / "records"
|
||||||
|
Tmp = tempfile.gettempdir()
|
||||||
|
|
||||||
|
def rand12():
|
||||||
|
return base64.urlsafe_b64encode(os.urandom(9)).decode()[:12]
|
||||||
|
|
||||||
|
|
||||||
|
def run(cmd):
|
||||||
|
return subprocess.run(cmd)
|
||||||
|
|
||||||
|
def find_focused_pid(node):
|
||||||
|
if isinstance(node, dict):
|
||||||
|
if node.get("focused") is True:
|
||||||
|
return node.get("pid")
|
||||||
|
|
||||||
|
for key in ("nodes", "floating_nodes"):
|
||||||
|
for child in node.get(key, []):
|
||||||
|
pid = find_focused_pid(child)
|
||||||
|
if pid:
|
||||||
|
return pid
|
||||||
|
|
||||||
|
elif isinstance(node, list):
|
||||||
|
for item in node:
|
||||||
|
pid = find_focused_pid(item)
|
||||||
|
if pid:
|
||||||
|
return pid
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def force_kill():
|
||||||
|
tree = json.loads(subprocess.check_output(
|
||||||
|
["swaymsg", "-t", "get_tree"],
|
||||||
|
text=True
|
||||||
|
))
|
||||||
|
pid = find_focused_pid(tree)
|
||||||
|
|
||||||
|
if not pid:
|
||||||
|
print("No focused window PID found")
|
||||||
|
return
|
||||||
|
|
||||||
|
os.kill(pid, 9)
|
||||||
|
|
||||||
|
def is_recording():
|
||||||
|
return subprocess.run(
|
||||||
|
["pgrep", "-x", "wf-recorder"],
|
||||||
|
stdout=subprocess.DEVNULL,
|
||||||
|
stderr=subprocess.DEVNULL
|
||||||
|
).returncode == 0
|
||||||
|
|
||||||
|
|
||||||
|
def record(fullscreen=False):
|
||||||
|
Recordings.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
if is_recording():
|
||||||
|
subprocess.run(["killall", "wf-recorder"])
|
||||||
|
try:
|
||||||
|
os.remove(Recording_flag)
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
return
|
||||||
|
|
||||||
|
Path(Recording_flag).write_text("1")
|
||||||
|
|
||||||
|
slurp_cmd = ["slurp"]
|
||||||
|
if fullscreen:
|
||||||
|
slurp_cmd.append("-o")
|
||||||
|
|
||||||
|
selection = subprocess.getoutput(" ".join(slurp_cmd))
|
||||||
|
|
||||||
|
if not selection.strip():
|
||||||
|
return
|
||||||
|
|
||||||
|
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
|
||||||
|
output = Recordings / f"{timestamp}.mp4"
|
||||||
|
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
["wf-recorder", "-g", selection, "-f", str(output)]
|
||||||
|
)
|
||||||
|
proc.wait()
|
||||||
|
|
||||||
|
if not output.exists():
|
||||||
|
return
|
||||||
|
|
||||||
|
compressed = output.with_name(output.stem + "_comp720p.mp4")
|
||||||
|
|
||||||
|
subprocess.run([
|
||||||
|
"ffmpeg",
|
||||||
|
"-i", str(output),
|
||||||
|
"-vf", "scale=-2:720",
|
||||||
|
"-vcodec", "libx264",
|
||||||
|
"-crf", "32",
|
||||||
|
"-preset", "fast",
|
||||||
|
str(compressed)
|
||||||
|
])
|
||||||
|
|
||||||
|
try:
|
||||||
|
output.unlink()
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def resolve_output(out):
|
||||||
|
if not out:
|
||||||
|
out = Tmp
|
||||||
|
|
||||||
|
out = os.path.expanduser(out)
|
||||||
|
|
||||||
|
if os.path.isdir(out) or out.endswith("/"):
|
||||||
|
os.makedirs(out, exist_ok=True)
|
||||||
|
return os.path.join(out, f"{rand12()}.png")
|
||||||
|
|
||||||
|
parent = os.path.dirname(out)
|
||||||
|
if parent:
|
||||||
|
os.makedirs(parent, exist_ok=True)
|
||||||
|
|
||||||
|
if not out.endswith(".png"):
|
||||||
|
out += ".png"
|
||||||
|
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def take_screenshot(output_path):
|
||||||
|
Path(Recording_flag).write_text("1")
|
||||||
|
|
||||||
|
try:
|
||||||
|
time.sleep(0.2)
|
||||||
|
|
||||||
|
freeze = subprocess.Popen(["wayfreeze"])
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
try:
|
||||||
|
region = subprocess.check_output(["slurp"]).decode().strip()
|
||||||
|
|
||||||
|
subprocess.run(
|
||||||
|
["grim", "-g", region, output_path],
|
||||||
|
check=True
|
||||||
|
)
|
||||||
|
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
finally:
|
||||||
|
freeze.terminate()
|
||||||
|
|
||||||
|
if os.path.exists(output_path):
|
||||||
|
with open(output_path, "rb") as f:
|
||||||
|
subprocess.run(
|
||||||
|
["wl-copy", "--type", "image/png"],
|
||||||
|
input=f.read()
|
||||||
|
)
|
||||||
|
|
||||||
|
subprocess.run(["notify-send", "screenshot", f"Saved: {output_path}"])
|
||||||
|
return True
|
||||||
|
|
||||||
|
finally:
|
||||||
|
try:
|
||||||
|
os.remove(Recording_flag)
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
cmd = sys.argv[1]
|
||||||
|
|
||||||
|
if cmd == "kill":
|
||||||
|
force_kill()
|
||||||
|
|
||||||
|
elif cmd == "record":
|
||||||
|
fullscreen = "--fullscreen" in sys.argv
|
||||||
|
record(fullscreen)
|
||||||
|
|
||||||
|
elif cmd == "screenshot":
|
||||||
|
out = None
|
||||||
|
|
||||||
|
if "--out" in sys.argv:
|
||||||
|
idx = sys.argv.index("--out")
|
||||||
|
if idx + 1 < len(sys.argv):
|
||||||
|
out = sys.argv[idx + 1]
|
||||||
|
|
||||||
|
output = resolve_output(out)
|
||||||
|
take_screenshot(output)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Executable
+156
@@ -0,0 +1,156 @@
|
|||||||
|
import os
|
||||||
|
import time
|
||||||
|
import json
|
||||||
|
import socket
|
||||||
|
import struct
|
||||||
|
import subprocess
|
||||||
|
import urllib.request
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
vpncache = Path("/tmp/vpn_country.cache")
|
||||||
|
screenshotflag = Path("/tmp/screenshot_active")
|
||||||
|
|
||||||
|
|
||||||
|
class Cached:
|
||||||
|
def __init__(self, ttl):
|
||||||
|
self.ttl = ttl
|
||||||
|
self.last = 0
|
||||||
|
self.value = ""
|
||||||
|
|
||||||
|
def get(self, fn):
|
||||||
|
now = time.time()
|
||||||
|
if now - self.last >= self.ttl:
|
||||||
|
try:
|
||||||
|
self.value = fn()
|
||||||
|
except Exception:
|
||||||
|
self.value = ""
|
||||||
|
self.last = now
|
||||||
|
return self.value
|
||||||
|
|
||||||
|
|
||||||
|
def http_get(url, timeout=2):
|
||||||
|
try:
|
||||||
|
with urllib.request.urlopen(url, timeout=timeout) as r:
|
||||||
|
return r.read().decode().strip()
|
||||||
|
except Exception:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
class StatusBar:
|
||||||
|
def __init__(self):
|
||||||
|
self.cpu = Cached(5)
|
||||||
|
self.bat = Cached(5)
|
||||||
|
self.vpn = Cached(60)
|
||||||
|
self.rec = Cached(1)
|
||||||
|
|
||||||
|
def run(self, cmd):
|
||||||
|
return subprocess.run(cmd, capture_output=True, text=True).stdout.strip()
|
||||||
|
|
||||||
|
def get_volume(self):
|
||||||
|
return self.run(["pamixer", "--get-volume-human"])
|
||||||
|
|
||||||
|
def get_ram(self):
|
||||||
|
out = self.run(["free", "-m"]).splitlines()
|
||||||
|
mem = next((l for l in out if l.startswith("Mem:")), "")
|
||||||
|
parts = mem.split()
|
||||||
|
if len(parts) < 7:
|
||||||
|
return "?"
|
||||||
|
total = int(parts[1])
|
||||||
|
used = int(parts[2])
|
||||||
|
return f"{used//1000}GB/{total//1000}GB"
|
||||||
|
|
||||||
|
def get_cpu(self):
|
||||||
|
def compute():
|
||||||
|
try:
|
||||||
|
out = self.run(["grep", "cpu ", "/proc/stat"])
|
||||||
|
p = out.split()
|
||||||
|
if len(p) < 8:
|
||||||
|
return "0.0%"
|
||||||
|
user, nice, system, idle = map(int, p[1:5])
|
||||||
|
total = user + nice + system + idle
|
||||||
|
busy = total - idle
|
||||||
|
return f"{(busy / total) * 100:.1f}%"
|
||||||
|
except Exception:
|
||||||
|
return "0.0%"
|
||||||
|
|
||||||
|
return self.cpu.get(compute)
|
||||||
|
|
||||||
|
def get_battery(self):
|
||||||
|
def compute():
|
||||||
|
bat = Path("/sys/class/power_supply/BAT0")
|
||||||
|
if not bat.exists():
|
||||||
|
return ""
|
||||||
|
try:
|
||||||
|
cap = (bat / "capacity").read_text().strip()
|
||||||
|
status = (bat / "status").read_text().strip()
|
||||||
|
return f"{cap}% ({status})"
|
||||||
|
except Exception:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
return self.bat.get(compute)
|
||||||
|
|
||||||
|
def get_vpn(self):
|
||||||
|
def compute():
|
||||||
|
if not self.run(["wg", "show", "interfaces"]):
|
||||||
|
if vpncache.exists():
|
||||||
|
vpncache.unlink()
|
||||||
|
return ""
|
||||||
|
|
||||||
|
country = ""
|
||||||
|
if vpncache.exists():
|
||||||
|
country = vpncache.read_text().strip()
|
||||||
|
|
||||||
|
if not country:
|
||||||
|
ip = http_get("https://ifconfig.me")
|
||||||
|
geo = http_get(f"https://iplookup.stab.ing/api/v1/lookup?ip={ip}")
|
||||||
|
|
||||||
|
if '"country"' in geo:
|
||||||
|
try:
|
||||||
|
country = geo.split('"country":"')[1].split('"')[0]
|
||||||
|
except Exception:
|
||||||
|
country = ""
|
||||||
|
|
||||||
|
if country:
|
||||||
|
vpncache.write_text(country)
|
||||||
|
|
||||||
|
return f"VPN: {country or 'Unknown'}"
|
||||||
|
|
||||||
|
return self.vpn.get(compute)
|
||||||
|
|
||||||
|
def get_recording(self):
|
||||||
|
def compute():
|
||||||
|
return "RECORDING!" if self.run(["pgrep", "-x", "wf-recorder"]) else ""
|
||||||
|
|
||||||
|
return self.rec.get(compute)
|
||||||
|
|
||||||
|
def get_time(self):
|
||||||
|
if screenshotflag.exists():
|
||||||
|
return "REDACTED"
|
||||||
|
return self.run(["date", "+%Y-%m-%d %I:%M %p"])
|
||||||
|
|
||||||
|
def render(self):
|
||||||
|
vol = self.get_volume()
|
||||||
|
ram = self.get_ram()
|
||||||
|
cpu = self.get_cpu()
|
||||||
|
bat = self.get_battery()
|
||||||
|
vpn = self.get_vpn()
|
||||||
|
rec = self.get_recording()
|
||||||
|
t = self.get_time()
|
||||||
|
|
||||||
|
extra = " / ".join([x for x in [rec, vpn, bat] if x])
|
||||||
|
|
||||||
|
if extra:
|
||||||
|
return f"CPU: {cpu} / RAM: {ram} / VOL: {vol} / {extra} / {t}"
|
||||||
|
return f"CPU: {cpu} / RAM: {ram} / VOL: {vol} / {t}"
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
bar = StatusBar()
|
||||||
|
while True:
|
||||||
|
print(bar.render(), flush=True)
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Executable
+163
@@ -0,0 +1,163 @@
|
|||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from swayipc import Swayipc
|
||||||
|
import swayworkspace
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
def get_base():
|
||||||
|
return os.environ.get("BASE_SWAY")
|
||||||
|
|
||||||
|
def run_sh(c):
|
||||||
|
subprocess.Popen(c, shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||||
|
|
||||||
|
def apply_ui_and_environment(sway):
|
||||||
|
base = os.path.expanduser(get_base())
|
||||||
|
|
||||||
|
sway.cmd("floating_modifier Mod4")
|
||||||
|
sway.cmd("default_border pixel 0")
|
||||||
|
|
||||||
|
sway.cmd("client.focused #ffffff #ffffff #ffffff #ffffff")
|
||||||
|
sway.cmd("client.focused_inactive #000000 #000000 #000000 #000000")
|
||||||
|
sway.cmd("client.unfocused #000000 #000000 #000000 #000000")
|
||||||
|
sway.cmd("client.urgent #ffffff #ffffff #ffffff #ffffff")
|
||||||
|
sway.cmd("client.placeholder #000000 #000000 #000000 #000000")
|
||||||
|
|
||||||
|
sway.cmd('for_window [app_id="xpad"] floating enable')
|
||||||
|
|
||||||
|
sway.cmd("exec_always gsettings set org.gnome.desktop.interface color-scheme 'prefer-dark'")
|
||||||
|
sway.cmd("exec_always gsettings set org.gnome.desktop.interface gtk-theme 'MonoThemeDark'")
|
||||||
|
sway.cmd("exec_always gsettings set org.gnome.desktop.interface.wm.preferences theme 'MonoThemeDark'")
|
||||||
|
sway.cmd("exec_always gsettings set org.gnome.desktop.interface cursor-size '24'")
|
||||||
|
sway.cmd("exec_always gsettings set org.gnome.desktop.interface font-name 'Noto Sans Mono 10'")
|
||||||
|
sway.cmd("exec_always gsettings set org.gnome.desktop.interface.desktop-app-info-default-handler 'librewolf.desktop'")
|
||||||
|
|
||||||
|
run_sh("/usr/lib/xdg-desktop-portal -r")
|
||||||
|
os.environ["GTK_USE_PORTAL"] = "0"
|
||||||
|
os.environ["XDG_CURRENT_DESKTOP"] = "sway"
|
||||||
|
os.environ["XDG_SESSION_TYPE"] = "wayland"
|
||||||
|
os.environ["XDG_PORTAL_BACKEND"] = "wlroots"
|
||||||
|
|
||||||
|
run_sh(f"swaybg -i {base}/wallpaper/metrowaymaka.png -m fill")
|
||||||
|
run_sh("blueman-applet")
|
||||||
|
run_sh("/usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1")
|
||||||
|
swayworkspace.SwaySome().init(1)
|
||||||
|
run_sh("dbus-update-activation-environment WAYLAND_DISPLAY XDG_CURRENT_DESKTOP SWAYSOCK")
|
||||||
|
|
||||||
|
def bind_keys(sway):
|
||||||
|
base = get_base()
|
||||||
|
mod = "Mod4"
|
||||||
|
|
||||||
|
binding_actions = {}
|
||||||
|
script_path = os.path.abspath(__file__)
|
||||||
|
|
||||||
|
def assign_key(key_combo):
|
||||||
|
def decorator(func):
|
||||||
|
result = func()
|
||||||
|
sway.cmd(f"bindsym {key_combo} {result}")
|
||||||
|
return func
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
@assign_key(f"{mod}+h")
|
||||||
|
def focus_left(): return "focus left"
|
||||||
|
|
||||||
|
@assign_key(f"{mod}+j")
|
||||||
|
def focus_down(): return "focus down"
|
||||||
|
|
||||||
|
@assign_key(f"{mod}+k")
|
||||||
|
def focus_up(): return "focus up"
|
||||||
|
|
||||||
|
@assign_key(f"{mod}+l")
|
||||||
|
def focus_right(): return "focus right"
|
||||||
|
|
||||||
|
@assign_key(f"{mod}+Shift+h")
|
||||||
|
def move_left(): return "move left"
|
||||||
|
|
||||||
|
@assign_key(f"{mod}+Shift+j")
|
||||||
|
def move_down(): return "move down"
|
||||||
|
|
||||||
|
@assign_key(f"{mod}+Shift+k")
|
||||||
|
def move_up(): return "move up"
|
||||||
|
|
||||||
|
@assign_key(f"{mod}+Shift+l")
|
||||||
|
def move_right(): return "move right"
|
||||||
|
|
||||||
|
@assign_key(f"{mod}+bracketleft")
|
||||||
|
def workspace_prev(): return "workspace prev"
|
||||||
|
|
||||||
|
@assign_key(f"{mod}+bracketright")
|
||||||
|
def workspace_next(): return "workspace next"
|
||||||
|
|
||||||
|
@assign_key(f"{mod}+Q")
|
||||||
|
def open_terminal(): return "exec kitty"
|
||||||
|
|
||||||
|
@assign_key(f"{mod}+c")
|
||||||
|
def kill_window(): return "kill"
|
||||||
|
|
||||||
|
@assign_key(f"{mod}+Shift+s")
|
||||||
|
def take_screenshot(): return f"exec python3 {base}/main/moves.py screenshot --out ~/screenshots/"
|
||||||
|
|
||||||
|
@assign_key(f"{mod}+Shift+escape")
|
||||||
|
def exit_sway(): return "exit"
|
||||||
|
|
||||||
|
@assign_key(f"{mod}+Up")
|
||||||
|
def volume_up(): return "exec pamixer --increase 5"
|
||||||
|
|
||||||
|
@assign_key(f"{mod}+Down")
|
||||||
|
def volume_down(): return "exec pamixer --decrease 5"
|
||||||
|
|
||||||
|
@assign_key(f"{mod}+Shift+grave")
|
||||||
|
def reload_sway(): return "reload"
|
||||||
|
|
||||||
|
@assign_key(f"{mod}+Shift+f")
|
||||||
|
def record_screen(): return f"exec python {base}/main/moves.py record --fullscreen"
|
||||||
|
|
||||||
|
@assign_key(f"{mod}+r")
|
||||||
|
def open_menu(): return "exec wofi --show drun --width 400 --height 500 --layer overlay --location 1"
|
||||||
|
|
||||||
|
@assign_key("Ctrl+Shift+Alt+Mod4+L")
|
||||||
|
def open_linkedin(): return "exec flatpak run io.github.ungoogled_software.ungoogled_chromium https://www.linkedin.com"
|
||||||
|
|
||||||
|
@assign_key(f"{mod}+Shift+c")
|
||||||
|
def force_kill(): return f"exec python {base}/main/moves.py kill"
|
||||||
|
|
||||||
|
@assign_key(f"{mod}+n")
|
||||||
|
def open_notes(): return "exec xpad"
|
||||||
|
|
||||||
|
@assign_key(f"{mod}+v")
|
||||||
|
def toggle_floating(): return "floating toggle"
|
||||||
|
|
||||||
|
@assign_key("XF86AudioPlay")
|
||||||
|
def media_play(): return "exec playerctl play-pause"
|
||||||
|
|
||||||
|
@assign_key("XF86AudioPause")
|
||||||
|
def media_pause(): return "exec playerctl play-pause"
|
||||||
|
|
||||||
|
for i in range(1, 11):
|
||||||
|
num = 0 if i == 10 else i
|
||||||
|
sway.cmd(f"bindsym {mod}+{num} exec python {base}/main/swayworkspace.py focus {i}")
|
||||||
|
sway.cmd(f"bindsym {mod}+Shift+{num} exec python {base}/main/swayworkspace.py move {i}")
|
||||||
|
|
||||||
|
return binding_actions
|
||||||
|
|
||||||
|
def build_bar(sway):
|
||||||
|
base = os.path.expanduser(get_base())
|
||||||
|
|
||||||
|
sway.cmd("bar bar-main position top")
|
||||||
|
sway.cmd(f"bar bar-main status_command python3 {base}/main/sway_status.py")
|
||||||
|
sway.cmd("bar bar-main font pango:Noto Sans Mono 10")
|
||||||
|
sway.cmd("bar bar-main mode dock")
|
||||||
|
|
||||||
|
sway.cmd("bar bar-main colors background #000000")
|
||||||
|
sway.cmd("bar bar-main colors statusline #ffffff")
|
||||||
|
sway.cmd("bar bar-main colors separator #ffffff")
|
||||||
|
sway.cmd("bar bar-main colors focused_workspace #000000 #ffffff #000000")
|
||||||
|
sway.cmd("bar bar-main colors active_workspace #000000 #ffffff #000000")
|
||||||
|
sway.cmd("bar bar-main colors inactive_workspace #000000 #000000 #ffffff")
|
||||||
|
sway.cmd("bar bar-main colors urgent_workspace #ffffff #ffffff #ffffff")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sway = Swayipc()
|
||||||
|
apply_ui_and_environment(sway)
|
||||||
|
bind_keys(sway)
|
||||||
|
build_bar(sway)
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
#sway socket working for working the socket for sending commands
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import socket
|
||||||
|
import struct
|
||||||
|
import sys
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
ipcmag = b"i3-ipc"
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Output:
|
||||||
|
name: str
|
||||||
|
focused: bool = False
|
||||||
|
active: bool = False
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Workspace:
|
||||||
|
num: int
|
||||||
|
output: str
|
||||||
|
visible: bool = False
|
||||||
|
|
||||||
|
class Swayipc:
|
||||||
|
def __init__(self):
|
||||||
|
self.socket = None
|
||||||
|
sock_path = None
|
||||||
|
for var in ("SWAYSOCK", "I3SOCK"):
|
||||||
|
sock_path = os.environ.get(var)
|
||||||
|
if sock_path:
|
||||||
|
break
|
||||||
|
|
||||||
|
if not sock_path:
|
||||||
|
raise RuntimeError("No socket found!")
|
||||||
|
|
||||||
|
self._connect(sock_path)
|
||||||
|
|
||||||
|
def _connect(self, path):
|
||||||
|
self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||||
|
self.socket.connect(path)
|
||||||
|
|
||||||
|
def _send(self, cmd_type: int, payload: str):
|
||||||
|
data = payload.encode()
|
||||||
|
header = (
|
||||||
|
ipcmag
|
||||||
|
+ struct.pack("=I", len(data))
|
||||||
|
+ struct.pack("=I", cmd_type)
|
||||||
|
)
|
||||||
|
self.socket.sendall(header + data)
|
||||||
|
|
||||||
|
def _read(self):
|
||||||
|
header = b""
|
||||||
|
while len(header) < 14:
|
||||||
|
chunk = self.socket.recv(14 - len(header))
|
||||||
|
if not chunk:
|
||||||
|
raise RuntimeError("Socket closed")
|
||||||
|
header += chunk
|
||||||
|
|
||||||
|
if header[:6] != ipcmag:
|
||||||
|
raise RuntimeError("Bad ipc response")
|
||||||
|
|
||||||
|
length = struct.unpack("=I", header[6:10])[0]
|
||||||
|
|
||||||
|
payload = b""
|
||||||
|
while len(payload) < length:
|
||||||
|
chunk = self.socket.recv(length - len(payload))
|
||||||
|
if not chunk:
|
||||||
|
raise RuntimeError("Socket closed during message")
|
||||||
|
payload += chunk
|
||||||
|
|
||||||
|
return payload.decode()
|
||||||
|
|
||||||
|
def cmd(self, command_string):
|
||||||
|
try:
|
||||||
|
self._send(0, command_string)
|
||||||
|
resp = json.loads(self._read())
|
||||||
|
if not resp[0].get("success", False):
|
||||||
|
print(f"Sway ipc error (skipped): '{command_string}' -> {resp[0].get('error')}", file=sys.stderr)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Exception execute command '{command_string}': {e}", file=sys.stderr)
|
||||||
|
|
||||||
|
def outputs(self):
|
||||||
|
self._send(3, "")
|
||||||
|
data = json.loads(self._read())
|
||||||
|
return [
|
||||||
|
Output(name=o["name"], focused=o.get("focused", False), active=o.get("active", False))
|
||||||
|
for o in data if o.get("active", False)
|
||||||
|
]
|
||||||
|
|
||||||
|
def workspaces(self):
|
||||||
|
self._send(1, "")
|
||||||
|
data = json.loads(self._read())
|
||||||
|
return [Workspace(num=w["num"], output=w["output"], visible=w.get("visible", False)) for w in data]
|
||||||
Executable
+81
@@ -0,0 +1,81 @@
|
|||||||
|
# implementation of swaysome in python
|
||||||
|
import sys
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from swayipc import Swayipc, Output, Workspace
|
||||||
|
import pathlib
|
||||||
|
class SwaySome(Swayipc):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def _cmd(self, cmd_str):
|
||||||
|
self.cmd(cmd_str)
|
||||||
|
|
||||||
|
def ordered_outputs(self):
|
||||||
|
outs = self.outputs()
|
||||||
|
outs.sort(key=lambda o: o.name)
|
||||||
|
return outs
|
||||||
|
|
||||||
|
def current_workspace(self):
|
||||||
|
outs = self.outputs()
|
||||||
|
wss = self.workspaces()
|
||||||
|
|
||||||
|
focused_output = next((o.name for o in outs if o.focused), None)
|
||||||
|
if not focused_output:
|
||||||
|
raise RuntimeError("No focused output")
|
||||||
|
|
||||||
|
for ws in wss:
|
||||||
|
if ws.visible and ws.output == focused_output:
|
||||||
|
return ws.num
|
||||||
|
|
||||||
|
raise RuntimeError("No active workspace found")
|
||||||
|
|
||||||
|
def group_base(self):
|
||||||
|
return (self.current_workspace() // 10) * 10
|
||||||
|
|
||||||
|
def resolve(self, n: int):
|
||||||
|
return self.group_base() + n
|
||||||
|
|
||||||
|
def focus(self, n: int):
|
||||||
|
self._cmd(f"workspace number {self.resolve(n)}")
|
||||||
|
|
||||||
|
def move(self, n: int):
|
||||||
|
self._cmd(f"move container to workspace number {self.resolve(n)}")
|
||||||
|
|
||||||
|
def focus_abs(self, n: int):
|
||||||
|
self._cmd(f"workspace number {n}")
|
||||||
|
|
||||||
|
def move_abs(self, n: int):
|
||||||
|
self._cmd(f"move container to workspace number {n}")
|
||||||
|
|
||||||
|
def init(self, n: int):
|
||||||
|
outputs = self.ordered_outputs()
|
||||||
|
current = next((o.name for o in outputs if o.focused), None)
|
||||||
|
|
||||||
|
for i, out in enumerate(outputs):
|
||||||
|
target = ((i + 1) * 10) + n
|
||||||
|
self._cmd(f"focus output {out.name}")
|
||||||
|
self._cmd(f"workspace number {target}")
|
||||||
|
|
||||||
|
if current:
|
||||||
|
self._cmd(f"focus output {current}")
|
||||||
|
|
||||||
|
def main():
|
||||||
|
cmd = sys.argv[1]
|
||||||
|
n = int(sys.argv[2])
|
||||||
|
|
||||||
|
sway = SwaySome()
|
||||||
|
|
||||||
|
if cmd == "focus":
|
||||||
|
sway.focus(n)
|
||||||
|
elif cmd == "move":
|
||||||
|
sway.move(n)
|
||||||
|
elif cmd == "focus-abs":
|
||||||
|
sway.focus_abs(n)
|
||||||
|
elif cmd == "move-abs":
|
||||||
|
sway.move_abs(n)
|
||||||
|
else:
|
||||||
|
print(f"Unknown command: {cmd}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.9 MiB |
Executable
+2
@@ -0,0 +1,2 @@
|
|||||||
|
insensitive=true
|
||||||
|
no_actions=true
|
||||||
Executable
+19
@@ -0,0 +1,19 @@
|
|||||||
|
* {
|
||||||
|
all: unset;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
window {
|
||||||
|
background-color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#input, #entry {
|
||||||
|
background-color: #000000;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
#entry:selected {
|
||||||
|
background-color: #ffffff;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import json
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
out = Path("./mute/monitors.conf")
|
||||||
|
|
||||||
|
DIRECT = {
|
||||||
|
"left": (-1, 0),
|
||||||
|
"right": (1, 0),
|
||||||
|
"above": (0, -1),
|
||||||
|
"below": (0, 1),
|
||||||
|
"primary": (0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_sway_data(target_type):
|
||||||
|
cmd = ["swaymsg", "-t", f"get_{target_type}", "-r"]
|
||||||
|
return json.loads(subprocess.check_output(cmd, text=True))
|
||||||
|
|
||||||
|
def get_best_mode(output):
|
||||||
|
modes = output.get("modes", [])
|
||||||
|
if not modes:
|
||||||
|
return (1920, 1080)
|
||||||
|
|
||||||
|
for m in modes:
|
||||||
|
if m.get("preferred"):
|
||||||
|
return m["width"], m["height"]
|
||||||
|
|
||||||
|
best = max(modes, key=lambda m: m["width"] * m["height"])
|
||||||
|
return best["width"], best["height"]
|
||||||
|
|
||||||
|
def ask(prompt, default=None):
|
||||||
|
prompt_str = f"{prompt} [{default}]: " if default else f"{prompt}: "
|
||||||
|
val = input(prompt_str).strip()
|
||||||
|
return val if val else default
|
||||||
|
|
||||||
|
def compute_positions(monitors, layout, outputs):
|
||||||
|
positions = {}
|
||||||
|
output_map = {o["name"]: o for o in outputs}
|
||||||
|
|
||||||
|
primary_name = next((name for name, dist in layout.items() if dist == "primary"), None)
|
||||||
|
if not primary_name and monitors:
|
||||||
|
primary_name = monitors[0]["name"]
|
||||||
|
layout[primary_name] = "primary"
|
||||||
|
|
||||||
|
logical_sizes = {}
|
||||||
|
|
||||||
|
for m in monitors:
|
||||||
|
name = m["name"]
|
||||||
|
|
||||||
|
try:
|
||||||
|
res_w, res_h = map(int, m["res"].lower().split("x"))
|
||||||
|
except ValueError:
|
||||||
|
res_w, res_h = get_best_mode(output_map[name])
|
||||||
|
|
||||||
|
scale = float(m["scale"])
|
||||||
|
|
||||||
|
logical_sizes[name] = {
|
||||||
|
"width": res_w / scale,
|
||||||
|
"height": res_h / scale
|
||||||
|
}
|
||||||
|
|
||||||
|
for m in monitors:
|
||||||
|
name = m["name"]
|
||||||
|
direction = layout[name]
|
||||||
|
|
||||||
|
if direction == "primary":
|
||||||
|
positions[name] = (0, 0)
|
||||||
|
continue
|
||||||
|
|
||||||
|
dx, dy = DIRECT.get(direction, (1, 0))
|
||||||
|
|
||||||
|
ref_w = logical_sizes[name]["width"] if dx < 0 else logical_sizes[primary_name]["width"]
|
||||||
|
ref_h = logical_sizes[name]["height"] if dy < 0 else logical_sizes[primary_name]["height"]
|
||||||
|
|
||||||
|
positions[name] = (int(ref_w * dx), int(ref_h * dy))
|
||||||
|
|
||||||
|
return positions
|
||||||
|
|
||||||
|
def build_config(monitors, positions):
|
||||||
|
lines = ["# generated config\n"]
|
||||||
|
|
||||||
|
for m in monitors:
|
||||||
|
name = m["name"]
|
||||||
|
mode = f"{m['res']}@{m['refresh']}" if m["refresh"] else m["res"]
|
||||||
|
x, y = positions[name]
|
||||||
|
|
||||||
|
lines.append(
|
||||||
|
f"output {name} mode {mode}\n"
|
||||||
|
f"output {name} scale {m['scale']}\n"
|
||||||
|
f"output {name} position {x} {y}\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
Path("mute").mkdir(exist_ok=True)
|
||||||
|
|
||||||
|
outputs = get_sway_data("outputs")
|
||||||
|
workspaces = get_sway_data("workspaces")
|
||||||
|
|
||||||
|
monitors = []
|
||||||
|
layout_map = {}
|
||||||
|
|
||||||
|
for o in outputs:
|
||||||
|
name = o["name"]
|
||||||
|
ws = [str(w["name"]) for w in workspaces if w.get("output") == name]
|
||||||
|
|
||||||
|
w, h = get_best_mode(o)
|
||||||
|
best_res = f"{w}x{h}"
|
||||||
|
|
||||||
|
print(f"\nMONITOR: {name}\nWORKSPACES: {ws}")
|
||||||
|
|
||||||
|
layout_map[name] = ask(
|
||||||
|
"Position relative to primary (primary/left/right/above/below)",
|
||||||
|
"right"
|
||||||
|
)
|
||||||
|
|
||||||
|
res_input = ask("Resolution (optional press enter for best)", "")
|
||||||
|
|
||||||
|
monitors.append({
|
||||||
|
"name": name,
|
||||||
|
"res": res_input if res_input else best_res,
|
||||||
|
"refresh": ask("Refresh rate (optional)", ""),
|
||||||
|
"scale": ask("Scale", "1")
|
||||||
|
})
|
||||||
|
|
||||||
|
positions = compute_positions(monitors, layout_map, outputs)
|
||||||
|
out.write_text(build_config(monitors, positions))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
default:
|
||||||
|
just --list
|
||||||
|
|
||||||
|
init-arch: install-aur install-arch stow
|
||||||
|
|
||||||
|
install-arch:
|
||||||
|
@echo "installing pkgs"
|
||||||
|
sudo pacman -S --needed --noconfirm - < packages-arch
|
||||||
|
@echo "Done. Moving onto aur..."
|
||||||
|
yay -S --noconfirm wayfreeze
|
||||||
|
|
||||||
|
install-aur:
|
||||||
|
sudo pacman -S --noconfirm debugedit fakeroot go make gcc
|
||||||
|
git clone https://aur.archlinux.org/yay.git /tmp/yay
|
||||||
|
cd /tmp/yay && makepkg -si
|
||||||
|
|
||||||
|
stow:
|
||||||
|
stow -t ~/.config config
|
||||||
|
@echo "Stoweed"
|
||||||
|
|
||||||
|
unstow:
|
||||||
|
stow -D -t ~/.config config
|
||||||
|
|
||||||
|
setup-displays:
|
||||||
|
python init/swaysetupmonitors.py
|
||||||
|
mv mute ~/.config/
|
||||||
|
|
||||||
|
restow:
|
||||||
|
stow -R -t ~/.config config
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
sway
|
||||||
|
kitty
|
||||||
|
pamixer
|
||||||
|
grim
|
||||||
|
slurp
|
||||||
|
wl-clipboard
|
||||||
|
wf-recorder
|
||||||
|
gnome-keyring
|
||||||
|
glib2
|
||||||
|
curl
|
||||||
|
jq
|
||||||
|
procps-ng
|
||||||
|
darkman
|
||||||
|
xpad
|
||||||
|
thunar-archive-plugin
|
||||||
|
thunar
|
||||||
|
noto-fonts
|
||||||
|
blueman
|
||||||
|
python-pyqt5
|
||||||
|
xdg-desktop-portal-gnome
|
||||||
|
xdg-desktop-portal-wlr
|
||||||
|
base-devel
|
||||||
|
git
|
||||||
|
dbus-python
|
||||||
|
wofi
|
||||||
|
nushell
|
||||||
|
swaybg
|
||||||
|
stow
|
||||||
|
python
|
||||||
|
just
|
||||||
|
git
|
||||||
|
vim
|
||||||
Reference in New Issue
Block a user