Compare commits
No commits in common. "64641a34026be172309f193ca5376833098c665a" and "c90248a597c265f84f6cd88d74c210e2b232537d" have entirely different histories.
64641a3402
...
c90248a597
1 changed files with 27 additions and 132 deletions
159
src/hyprman.nim
159
src/hyprman.nim
|
@ -1,13 +1,28 @@
|
||||||
import std/[
|
import std/[os,net, json, sugar, strformat, strutils, tables]
|
||||||
asyncdispatch, asyncnet, json,
|
|
||||||
net, os, osproc,
|
|
||||||
strformat, strutils, sugar,
|
const icons = {
|
||||||
tables
|
"[Running] - Oracle VM VirtualBox": "",
|
||||||
]
|
"Visual Studio Code": "",
|
||||||
|
"vivaldi-stable": "",
|
||||||
|
"LibreOffice": "",
|
||||||
|
"Geneious Prime": "",
|
||||||
|
"Firefox": "",
|
||||||
|
"- NVIM": "",
|
||||||
|
"Alacritty": "",
|
||||||
|
"- Wezterm": "",
|
||||||
|
"org.wezfurlong.wezterm":""
|
||||||
|
}.toTable
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
HyprlandDefect* = Defect
|
HyprlandDefect* = Defect
|
||||||
|
|
||||||
|
# Workspace = object
|
||||||
|
# id, monitorID, windows: int
|
||||||
|
# name, monitor, lastwindow, lastwindowtitle: string
|
||||||
|
# hasfullscreen: bool
|
||||||
|
|
||||||
WorkspaceIcon = object
|
WorkspaceIcon = object
|
||||||
id: int
|
id: int
|
||||||
icon, class: string
|
icon, class: string
|
||||||
|
@ -24,15 +39,10 @@ type
|
||||||
workspace: ActiveWorkspace
|
workspace: ActiveWorkspace
|
||||||
class, title: string
|
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 =
|
proc getData(data: string): string =
|
||||||
let
|
let
|
||||||
|
his = getenv("HYPRLAND_INSTANCE_SIGNATURE")
|
||||||
socketPath = "/tmp/hypr" / his / ".socket.sock"
|
socketPath = "/tmp/hypr" / his / ".socket.sock"
|
||||||
|
|
||||||
let socket = newSocket(AF_UNIX, SOCK_STREAM, IPPROTO_IP)
|
let socket = newSocket(AF_UNIX, SOCK_STREAM, IPPROTO_IP)
|
||||||
|
@ -53,10 +63,9 @@ proc getData(data: string): string =
|
||||||
|
|
||||||
proc getDefaultWorkspaces(): seq[WorkspaceIcon] =
|
proc getDefaultWorkspaces(): seq[WorkspaceIcon] =
|
||||||
let clients = parseJson(getData("[-j]/clients")).to(seq[Client])
|
let clients = parseJson(getData("[-j]/clients")).to(seq[Client])
|
||||||
result = collect(for i in 1..9: WorkspaceIcon(id: i, icon: "",
|
result = collect(for i in 1..9: WorkspaceIcon(id: i, icon:"",class:fmt"ws-button-{i - 1}"))
|
||||||
class: fmt"ws-button-{i - 1}"))
|
|
||||||
for client in clients:
|
for client in clients:
|
||||||
let match = ctx.icons.getOrDefault($(client.class), "")
|
let match = icons.getOrDefault($(client.class),"")
|
||||||
if client.workspace.id < 0: continue
|
if client.workspace.id < 0: continue
|
||||||
result[client.workspace.id - 1].icon &= match
|
result[client.workspace.id - 1].icon &= match
|
||||||
for ws in result.mitems:
|
for ws in result.mitems:
|
||||||
|
@ -68,130 +77,16 @@ proc getActive(m: Monitor): int {.inline.} = m.activeWorkspace.id
|
||||||
proc getState(): seq[seq[WorkspaceIcon]] =
|
proc getState(): seq[seq[WorkspaceIcon]] =
|
||||||
let
|
let
|
||||||
monitors = parseJson(getData("[-j]/monitors")).to(seq[Monitor])
|
monitors = parseJson(getData("[-j]/monitors")).to(seq[Monitor])
|
||||||
|
|
||||||
defaultWorkspaces = getDefaultWorkspaces()
|
defaultWorkspaces = getDefaultWorkspaces()
|
||||||
for m in monitors:
|
for m in monitors:
|
||||||
result.add defaultWorkspaces
|
result.add defaultWorkspaces
|
||||||
result[m.id][getActive(m) - 1].class &=
|
result[m.id][getActive(m) - 1].class &=
|
||||||
" " & "ws-button-open"
|
" " & "ws-button-open"
|
||||||
|
|
||||||
proc workspaceMonitor() =
|
when isMainModule:
|
||||||
while true:
|
while true:
|
||||||
sleep ctx.delay
|
sleep 500
|
||||||
try:
|
try:
|
||||||
echo ( %* getState())
|
echo (%* getState())
|
||||||
except JsonParsingError as e:
|
except JsonParsingError as e:
|
||||||
writeLine(stderr, e.msg)
|
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 initEww() =
|
|
||||||
let monitors = parseJson(getData("[-j]/monitors")).to(seq[Monitor])
|
|
||||||
discard execCmd "eww daemon"
|
|
||||||
discard execCmd "eww open bar0"
|
|
||||||
if monitors.len > 1:
|
|
||||||
discard execCmd "eww open bar1"
|
|
||||||
|
|
||||||
proc watchHyprlandEvents() {.async.} =
|
|
||||||
initEww()
|
|
||||||
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 <cmd> [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
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue