diff --git a/flake.nix b/flake.nix index 957bb57..9761bea 100644 --- a/flake.nix +++ b/flake.nix @@ -48,7 +48,7 @@ pname = "hyprman"; version = "${self.shortRev or "dirty"}"; src = cleanSource ./.; - nimbleDepsHash = "sha256-EQ3gdxIluE+e3Qzqk9v0Q3mBa6nOKR1C1EWyjwSmQSQ="; + nimbleDepsHash = "sha256-JSbUockdzyD8Qfn1Wl5zXCH5SbV1NkS+JdUuvISfnXk="; }; }); formatter = forAllSystems (pkgs: pkgs.nixfmt-rfc-style); diff --git a/hyprman.nimble b/hyprman.nimble index 4c8fe92..36cbcf9 100644 --- a/hyprman.nimble +++ b/hyprman.nimble @@ -13,4 +13,5 @@ bin = @["hyprman"] requires "nim >= 2.0.8" requires "cligen" requires "yaml" +requires "jsony" requires "https://github.com/daylinmorgan/hwylterm#HEAD" diff --git a/nimble.lock b/nimble.lock index 61f468a..5a893ac 100644 --- a/nimble.lock +++ b/nimble.lock @@ -21,6 +21,16 @@ "sha1": "30f8f61787c36b63d484f3d5e149995fad16c63c" } }, + "jsony": { + "version": "1.1.5", + "vcsRevision": "ea811bec7fa50f5abd3088ba94cda74285e93f18", + "url": "https://github.com/treeform/jsony", + "downloadMethod": "git", + "dependencies": [], + "checksums": { + "sha1": "6aeb83e7481ca8686396a568096054bc668294df" + } + }, "yaml": { "version": "2.1.1", "vcsRevision": "48a90e36e82bd97457dae87e86efe423dcd3bb40", diff --git a/src/hyprland.nim b/src/hyprland.nim new file mode 100644 index 0000000..f1fc228 --- /dev/null +++ b/src/hyprland.nim @@ -0,0 +1,154 @@ +import std/[os, strformat, strutils, streams, tables, net, sugar] +import ./lib +export tables +import yaml, jsony + +type + # Remove effects? + HyprlandDefect* = Defect + Workspace = object + name*: string + id*: int + Client = object + class*: string + workspace*: Workspace + Icons = Table[string, string] + Config = object # config vs icons? + classes*: Icons + `no-client`*: string + `default-icon`*: string + ActiveWorkspace = object + name*: string + id*: int + Monitor = object + activeWorkspace*: ActiveWorkspace + id*: int + + + +proc loadConfig*(): Config = + let configPath = getConfigDir() / "hyprman" / "config.yml" + if fileExists(configPath): + var s = newFileStream(configPath) + load(s, result) + + +proc hyprSocketPath(): string = + let runtimeDir = getEnv("XDG_RUNTIME_DIR") + let instancSig = getEnv("HYPRLAND_INSTANCE_SIGNATURE") + result = runtimeDir / "hypr" / instancSig + +let + hyprSocket = hyprSocketPath() / ".socket.sock" + hyprSocket2 = hyprSocketPath() / ".socket2.sock" + +# TODO: revamp? +proc getData[T](msg: string, to: typedesc[T]): T = + let socket = newSocket(AF_UNIX, SOCK_STREAM, IPPROTO_IP) + try: + socket.connectUnix(hyprSocket) + except OSError: + raise newException( + HyprlandDefect, "Could not connect to Hyprland IPC UNIX path; is Hyprland running?" + ) + + socket.send msg + var recvData: string + while true: + let response = socket.recv(4096) + if response == "": break + recvData.add response + socket.close() # is this necessary? + result = recvData.fromJson(T) + + +proc getMonitors*(): seq[Monitor] = + result = getData("[-j]/monitors", seq[Monitor]) + +proc getClients(): seq[Client] = + result = getData("[-j]/clients", seq[Client]) + +type + EwwWorkspace = object + icon*, class*: string + id*: int + +let config = loadConfig() + +func pickIcon(c: Config, class: string): string = + for k, v in c.classes: + if class in k: + result = v + +proc add(ws: var EwwWorkspace, client: Client) = + let clientIcon = config.pickIcon(client.class) + if ws.icon == config.`no-client`: + ws.icon = clientIcon + else: + ws.icon.add clientIcon + +func openWorkspaces(monitors: seq[Monitor]): seq[int] = + for m in monitors: + result.add m.activeWorkspace.id + +func setActive(workspaces: seq[EwwWorkspace], m: Monitor): seq[EwwWorkspace] = + let id = m.activeWorkspace.id + result = workspaces + result[id - 1].class.add " ws-button-active-" & $(id) + +proc writeEwwClasses*() = + let + monitors = getMonitors() + clients = getClients() + + var workspaces= + collect: + for i in 0..9: + EwwWorkspace( + icon: config.`no-client`, class: fmt"ws-button-{i+1}", id: i+1 + ) + + for client in clients: + let id = client.workspace.id - 1 + workspaces[id].add client + + for id in openWorkspaces(monitors): + workspaces[id - 1].class.add " ws-button-open" + + var ewwClasses = + collect: + for m in monitors: + workspaces.setActive m + + stdout.write ewwClasses.toJson() & "\n" + # flush? + +proc handleHyprEvent(event: string) = + let + s = event.split(">>", 1) + event = s[0] + # use enum? + case event: + of "monitoraddedv2": + # TODO: open as many bars as necessary depending on num monitors + # is it ok to just call open bar again? + notify("monitor added") + of "openwindow", "workspacev2": + writeEwwClasses() + else: discard + + +proc watchHyprland*() = + let socket = newSocket(AF_UNIX, SOCK_STREAM, IPPROTO_IP) + try: + socket.connectUnix(hyprSocket2) + except OSError: + raise newException( + HyprlandDefect, "Could not connect to Hyprland IPC UNIX path; is Hyprland running?" + ) + + while true: + var line: string + socket.readLine line + handleHyprEvent line + diff --git a/src/hyprman.nim b/src/hyprman.nim index 85049db..333bfde 100644 --- a/src/hyprman.nim +++ b/src/hyprman.nim @@ -1,10 +1,24 @@ ## hyprman, the hyprland companion -import ./mako +import std/[osproc, strformat] +import hwylterm/cligen, cligen +import ./[mako,hyprland, lib]# state] + +hwylCli(clCfg) + +proc start() = + ## launch eww + notify("starting eww") + for i in 0..