diff --git a/src/hyprman.nim b/src/hyprman.nim index b7facd5..d2c269f 100644 --- a/src/hyprman.nim +++ b/src/hyprman.nim @@ -1,28 +1,13 @@ -import std/[os,net, json, sugar, strformat, strutils, tables] - - -const icons = { - "[Running] - Oracle VM VirtualBox": "", - "Visual Studio Code": "", - "vivaldi-stable": "󰖟", - "LibreOffice": "", - "Geneious Prime": "󰚄", - "Firefox": "", - "- NVIM": "", - "Alacritty": "", - "- Wezterm": "", - "org.wezfurlong.wezterm":"" -}.toTable - +import std/[ + asyncdispatch, asyncnet, json, + net, os, osproc, + strformat, strutils, sugar, + tables +] type HyprlandDefect* = Defect - # Workspace = object - # id, monitorID, windows: int - # name, monitor, lastwindow, lastwindowtitle: string - # hasfullscreen: bool - WorkspaceIcon = object id: int icon, class: string @@ -39,10 +24,15 @@ type workspace: ActiveWorkspace class, title: string + Context = object + icons: Table[string, string] + delay = 500 + +var ctx = Context() +let his = getenv("HYPRLAND_INSTANCE_SIGNATURE") proc getData(data: string): string = let - his = getenv("HYPRLAND_INSTANCE_SIGNATURE") socketPath = "/tmp/hypr" / his / ".socket.sock" let socket = newSocket(AF_UNIX, SOCK_STREAM, IPPROTO_IP) @@ -63,9 +53,10 @@ proc getData(data: string): string = proc getDefaultWorkspaces(): seq[WorkspaceIcon] = let clients = parseJson(getData("[-j]/clients")).to(seq[Client]) - result = collect(for i in 1..9: WorkspaceIcon(id: i, icon:"",class:fmt"ws-button-{i - 1}")) + result = collect(for i in 1..9: WorkspaceIcon(id: i, icon: "", + class: fmt"ws-button-{i - 1}")) for client in clients: - let match = icons.getOrDefault($(client.class),"") + let match = ctx.icons.getOrDefault($(client.class), "") if client.workspace.id < 0: continue result[client.workspace.id - 1].icon &= match for ws in result.mitems: @@ -83,10 +74,115 @@ proc getState(): seq[seq[WorkspaceIcon]] = result[m.id][getActive(m) - 1].class &= " " & "ws-button-open" -when isMainModule: +proc workspaceMonitor() = while true: - sleep 500 + sleep ctx.delay try: - echo (%* getState()) + echo ( %* getState()) except JsonParsingError as e: writeLine(stderr, e.msg) + + +proc parseEvent(line: string): (string, string) {.inline.} = + let s = line.split(">>", 1) + return (s[0], s[1]) + +proc reactEvent(line: string) = + let (event, _) = parseEvent(line) + case event + of "monitorremoved": + echo "monitor removed" + discard execCmd "eww close bar1" + of "monitoradded": + echo "monitor added" + discard execCmd "eww open bar1" + sleep 1000 + discard execCmd "eww reload" + else: discard + +proc watchHyprlandEvents() {.async.} = + let + socketPath = "/tmp/hypr" / his / ".socket2.sock" + socket = newAsyncSocket(AF_UNIX, SOCK_STREAM, IPPROTO_IP) + try: + socket.connectUnix(socketPath) + except OSError: + raise newException(HyprlandDefect, "Could not connect to Hyprland IPC UNIX path; is Hyprland running?") + + while true: + let line = await socket.recvLine() + if line.len == 0: break + reactEvent(line) + socket.close() + +proc loadIcons(iconPath: string): Table[string, string] = + if not fileExists iconPath: + echo "icon file: ", iconPath, " not found" + quit 1 + result = parseFile(iconPath).to(Table[string, string]) + +proc noBlank(key: string, val: string) = + const longflags = ["icons", "delay"] + const shortflags = ['i', 'd'] + if ( + (key in longflags or + key[0] in shortflags) and + val == "" + ): + echo "ERROR: expected value for: ", key + quit 1 + +proc loadDefaultIcons() = + let + configHome = getEnv("XDG_CONFIG_HOME", getEnv("HOME") / ".config") + iconPath = configHome / "hyprman" / "icons.json" + if fileExists iconPath: + ctx.icons = loadIcons(iconPath) + +when isMainModule: + import std/parseopt + const usage = """ +hyprman [opts] + +commands: + workspace (w) + watch workspace/client activity and write to stdout + monitors (m) + reflect monitor changes to eww + +options: + -h, --help + show this help + -i, --icons + path to icons.json [default: $XDG_CONFIG_HOME/hyprman/icons.json] + -d, --delay + time between socket poll in ms [default: 500] +""" + + loadDefaultIcons() + var cmd: string + for kind, key, val in getopt(shortNoVal = {'h'}, longNoVal = @["help"]): + case kind + of cmdArgument: + cmd = key + of cmdShortOption, cmdLongOption: + noBlank(key, val) + case key + of "h", "help": + echo usage; quit 0; + of "i", "icons": + ctx.icons = loadIcons(val) + of "d", "delay": + ctx.delay = parseInt(val) + of cmdEnd: discard + + case cmd + of "w", "workspace": + workspaceMonitor() + of "m", "monitors": + waitFor watchHyprlandEvents() + of "": + echo "please specify a command"; quit 1 + else: + echo "unknown cmd: ", cmd; quit 1 +