Updated for normandy
This commit is contained in:
parent
59695984c5
commit
9801adfbc2
1
.config/eww##hostname.normandy/.python-version
Normal file
1
.config/eww##hostname.normandy/.python-version
Normal file
@ -0,0 +1 @@
|
||||
eww-modules
|
||||
13
.config/eww##hostname.normandy/colors.scss
Normal file
13
.config/eww##hostname.normandy/colors.scss
Normal file
@ -0,0 +1,13 @@
|
||||
$wallpaper: #1e1e1e;
|
||||
$foreground: #9a7c9d;
|
||||
|
||||
$bg0: #2d272f;
|
||||
$bg1: #3f3242;
|
||||
|
||||
$red: #cf6a4c;
|
||||
$green: #8f9d6a;
|
||||
$yellow: #f9ee98;
|
||||
$blue: #7587a6;
|
||||
$magenta: #9b859d;
|
||||
$cyan: #afc4db;
|
||||
$white: #a7a7a7;
|
||||
115
.config/eww##hostname.normandy/eww.scss
Normal file
115
.config/eww##hostname.normandy/eww.scss
Normal file
@ -0,0 +1,115 @@
|
||||
@import 'colors';
|
||||
|
||||
* {
|
||||
all: unset;
|
||||
}
|
||||
|
||||
.root {
|
||||
color: $foreground;
|
||||
font-family: "Source Code Pro";
|
||||
font-size: 7.5pt;
|
||||
background-color: rgba(0,0,0,0);
|
||||
// Probably needs to change
|
||||
&.bar {
|
||||
margin: 10px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
&.side {
|
||||
margin: 10px;
|
||||
margin-right: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.gauge {
|
||||
color: $foreground;
|
||||
}
|
||||
|
||||
.gauge-hole {
|
||||
color: $wallpaper;
|
||||
}
|
||||
|
||||
.gauge-gutter {
|
||||
color: $bg0;
|
||||
}
|
||||
|
||||
.icon {
|
||||
font-family: "Font Awesome 5 Free Solid";
|
||||
}
|
||||
|
||||
.invisible {
|
||||
color: $wallpaper;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
color: $red;
|
||||
}
|
||||
|
||||
.offline {
|
||||
color: $bg1;
|
||||
}
|
||||
|
||||
.special {
|
||||
color: $blue;
|
||||
}
|
||||
|
||||
.green {
|
||||
color: $green;
|
||||
}
|
||||
|
||||
.message-overlay {
|
||||
background-color: $wallpaper;
|
||||
}
|
||||
|
||||
.nebula {
|
||||
font-family: "Nebula";
|
||||
font-size: 1.2em;
|
||||
&.big, .big {
|
||||
font-size: 2.4em;
|
||||
}
|
||||
&.bigger, .bigger {
|
||||
font-size: 2.9em;
|
||||
}
|
||||
&.medium, .medium {
|
||||
font-size: 1.6em;
|
||||
}
|
||||
&.medium-big, .medium-big {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.big .normal, .bigger .normal {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
// sway module
|
||||
|
||||
.sway--ws {
|
||||
|
||||
.fill {
|
||||
background-color: $foreground;
|
||||
}
|
||||
color: $bg1;
|
||||
|
||||
&.sway--active {
|
||||
color: $foreground;
|
||||
}
|
||||
|
||||
&.sway--visible {
|
||||
color: $blue;
|
||||
}
|
||||
|
||||
&.sway--focused {
|
||||
}
|
||||
}
|
||||
|
||||
// clock module
|
||||
|
||||
.clock--time {
|
||||
font-size: 20pt;
|
||||
}
|
||||
|
||||
.clock--date {
|
||||
font-size: 9pt;
|
||||
}
|
||||
130
.config/eww##hostname.normandy/eww.yuck
Normal file
130
.config/eww##hostname.normandy/eww.yuck
Normal file
@ -0,0 +1,130 @@
|
||||
;; Include modules
|
||||
(include "./modules/workspaces/workspaces.yuck")
|
||||
(include "./modules/clock/clock.yuck")
|
||||
(include "./modules/system/system.yuck")
|
||||
(include "./modules/network/network.yuck")
|
||||
(include "./modules/volume/volume.yuck")
|
||||
(include "./modules/timer/timer.yuck")
|
||||
|
||||
;; Windows
|
||||
(defwidget leftbar--left []
|
||||
(box :orientation "h"
|
||||
:halign "start"
|
||||
:space-evenly false
|
||||
:spacing 20
|
||||
:class "leftbox"
|
||||
(system-name)))
|
||||
|
||||
|
||||
(defwidget leftbar--center []
|
||||
(box :orientation "h"
|
||||
:halign "center"
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
:class "centerbox"
|
||||
(met)
|
||||
))
|
||||
|
||||
(defwidget leftbar--right []
|
||||
(box :orientation "h"
|
||||
:halign "end"
|
||||
:space-evenly false
|
||||
:spacing 20
|
||||
:class "rightbox"
|
||||
(sway-workspace :group "left")
|
||||
(sway-workspaces :group "left")))
|
||||
|
||||
(defwidget rightbar--left []
|
||||
(box :orientation "h"
|
||||
:halign "start"
|
||||
:space-evenly false
|
||||
:spacing 20
|
||||
:class "leftbox"
|
||||
(sway-workspaces :group "right")
|
||||
(sway-workspace :group "right")))
|
||||
|
||||
(defwidget rightbar--hypr-left []
|
||||
(box :orientation "h"
|
||||
:halign "start"
|
||||
:space-evenly false
|
||||
:spacing 20
|
||||
:class "leftbox"
|
||||
(hypr-workspaces :group "main")
|
||||
(hypr-workspace :group "main")))
|
||||
|
||||
(defwidget rightbar--center []
|
||||
(box :orientation "h"
|
||||
:halign "center"
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
:class "centerbox"
|
||||
(met)
|
||||
))
|
||||
|
||||
(defwidget rightbar--right []
|
||||
(box :orientation "h"
|
||||
:halign "end"
|
||||
:space-evenly false
|
||||
:spacing 20
|
||||
:class "leftbox"
|
||||
(system-name)))
|
||||
|
||||
(defwidget sidebar []
|
||||
(box :orientation "v"
|
||||
:valign "start"
|
||||
:space-evenly false
|
||||
:spacing 20
|
||||
:class "bar root"
|
||||
(sideclock)
|
||||
(network-details)
|
||||
(system-gauges)
|
||||
))
|
||||
|
||||
(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"
|
||||
(leftbar--left)
|
||||
(leftbar--center)
|
||||
(leftbar--right)))
|
||||
|
||||
(defwindow rightbar
|
||||
:monitor 0
|
||||
:geometry (geometry :width "100%"
|
||||
:height "26px"
|
||||
:anchor "top center")
|
||||
:exclusive true
|
||||
:focusable false
|
||||
:stacking "fg"
|
||||
(centerbox :orientation "h" :class "bar root"
|
||||
(rightbar--left)
|
||||
(rightbar--center)
|
||||
(rightbar--right)))
|
||||
|
||||
(defwindow hypr-mainbar
|
||||
:monitor 0
|
||||
:geometry (geometry :width "100%"
|
||||
:height "36px"
|
||||
:anchor "top center")
|
||||
:exclusive true
|
||||
:focusable false
|
||||
:stacking "overlay"
|
||||
(centerbox :orientation "h" :class "bar root"
|
||||
(rightbar--hypr-left)
|
||||
(rightbar--center)
|
||||
(rightbar--right)))
|
||||
|
||||
(defwindow sidebar
|
||||
:monitor 1
|
||||
:geometry (geometry :width "210px"
|
||||
:height "1044px"
|
||||
:anchor "left bottom")
|
||||
:exclusive true
|
||||
:focusable false
|
||||
:stacking "fg"
|
||||
(sidebar))
|
||||
11
.config/eww##hostname.normandy/modules/angles/angles.yuck
Normal file
11
.config/eww##hostname.normandy/modules/angles/angles.yuck
Normal file
@ -0,0 +1,11 @@
|
||||
(defwidget left-angle []
|
||||
(image :path "/home/ezri/.config/eww.new/modules/angles/left.png"
|
||||
:width 150
|
||||
:height 36))
|
||||
|
||||
(defwidget right-angle []
|
||||
(image :path "/home/ezri/.config/eww.new/modules/angles/right.png"
|
||||
:width 150
|
||||
:height 36))
|
||||
|
||||
|
||||
BIN
.config/eww##hostname.normandy/modules/angles/left.png
Normal file
BIN
.config/eww##hostname.normandy/modules/angles/left.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
BIN
.config/eww##hostname.normandy/modules/angles/right.png
Normal file
BIN
.config/eww##hostname.normandy/modules/angles/right.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
34
.config/eww##hostname.normandy/modules/audio.py
Executable file
34
.config/eww##hostname.normandy/modules/audio.py
Executable file
@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import asyncio
|
||||
import signal
|
||||
from contextlib import suppress
|
||||
import pulsectl_asyncio
|
||||
import json
|
||||
|
||||
async def get_values(pulse):
|
||||
sink = await pulse.get_sink_by_name("@DEFAULT_SINK@")
|
||||
result = {
|
||||
"mute": sink.mute == 1,
|
||||
"volume": f"{int(sink.volume.value_flat * 100):2}"
|
||||
}
|
||||
print(json.dumps(result), flush=True)
|
||||
return result
|
||||
|
||||
async def listen():
|
||||
async with pulsectl_asyncio.PulseAsync("volume-monitor") as pulse:
|
||||
await get_values(pulse)
|
||||
async for _ in pulse.subscribe_events('sink'):
|
||||
await get_values(pulse)
|
||||
|
||||
async def main():
|
||||
listen_task = asyncio.create_task(listen())
|
||||
|
||||
for sig in (signal.SIGTERM, signal.SIGHUP, signal.SIGINT):
|
||||
loop.add_signal_handler(sig, listen_task.cancel)
|
||||
|
||||
with suppress(asyncio.CancelledError):
|
||||
await listen_task
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
loop.run_until_complete(main())
|
||||
17
.config/eww##hostname.normandy/modules/audio.yuck
Normal file
17
.config/eww##hostname.normandy/modules/audio.yuck
Normal file
@ -0,0 +1,17 @@
|
||||
(deflisten audio-data
|
||||
`~/.config/eww/modules/audio.py`)
|
||||
|
||||
(defwidget volume [align]
|
||||
(box :orientation "h"
|
||||
:halign align
|
||||
:class "module volume ${audio-data['mute'] ? 'muted' : ''}"
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
(revealer :transition "none"
|
||||
:reveal {!audio-data["mute"]}
|
||||
"")
|
||||
(revealer :transition "none"
|
||||
:reveal {audio-data["mute"]}
|
||||
"")
|
||||
' ${audio-data["volume"]}%'))
|
||||
|
||||
67
.config/eww##hostname.normandy/modules/clock/clock.yuck
Normal file
67
.config/eww##hostname.normandy/modules/clock/clock.yuck
Normal file
@ -0,0 +1,67 @@
|
||||
(defpoll clock--data :interval "500ms"
|
||||
`date +'{"hour": "%H", "minute": "%M", "second": "%S", "year": "%Y", "day": "%d", "month": "%m", "dow": "%A", "month_name": "%B", "unix": "%s"}'`)
|
||||
|
||||
(defvar clock--show "clock")
|
||||
|
||||
(defwidget clock []
|
||||
(box :class "module text"
|
||||
:spacing 0
|
||||
:orientation "v"
|
||||
(label :class "special"
|
||||
:text "${clock--data['hour']}:${clock--data['minute']}")
|
||||
(label :text "${clock--data['year']}-${clock--data['month']}-${clock--data['day']}")))
|
||||
|
||||
(defwidget sideclock []
|
||||
(button :onclick "echo -n $(date +%Y-%d-%m) | wl-copy"
|
||||
:onrightclick "echo -n $(date +%s) | wl-copy && eww update clock--show=unixtime && sleep 2 && eww update clock--show=clock"
|
||||
(overlay
|
||||
(box :class "module text nebula"
|
||||
:spacing 0
|
||||
:space-evenly false
|
||||
:orientation "v"
|
||||
(centerbox :class "bigger nebula"
|
||||
:space-evenly false
|
||||
:width 150
|
||||
:halign "center"
|
||||
:orientation "h"
|
||||
(box :halign "start" "${clock--data.hour}")
|
||||
(box :halign "center" ":")
|
||||
(box :halign "end" "${clock--data.minute}"))
|
||||
(label :text "${clock--data.dow}"
|
||||
:class "special")
|
||||
(label :text "${clock--data.month_name} ${clock--data.day}"))
|
||||
(box
|
||||
:height 100
|
||||
(revealer :transition "crossfade"
|
||||
:reveal {clock--show == "unixtime"}
|
||||
:duration {clock--show == "unixtime" ? "2s" : "500ms"}
|
||||
(box :width 200
|
||||
:height 50
|
||||
:class "message-overlay nebula special"
|
||||
(box
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
:halign "center"
|
||||
:valign "center"
|
||||
:orientation "v"
|
||||
"Copied UNIX"
|
||||
"timestamp"
|
||||
))))
|
||||
(box
|
||||
:height 100
|
||||
(revealer :transition "crossfade"
|
||||
:reveal {clock--show == "date"}
|
||||
:duration {clock--show == "date" ? "2s" : "500ms"}
|
||||
(box :width 200
|
||||
:height 50
|
||||
:class "message-overlay nebula special"
|
||||
(box
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
:halign "center"
|
||||
:valign "center"
|
||||
:orientation "v"
|
||||
"Copied"
|
||||
"current date"
|
||||
|
||||
)))))))
|
||||
102
.config/eww##hostname.normandy/modules/i3.json
Normal file
102
.config/eww##hostname.normandy/modules/i3.json
Normal file
@ -0,0 +1,102 @@
|
||||
{
|
||||
"1": {
|
||||
"name": "1",
|
||||
"use": "terminal",
|
||||
"groups": ["left"]
|
||||
},
|
||||
"2": {
|
||||
"name": "2",
|
||||
"use": "code",
|
||||
"groups": ["left"]
|
||||
},
|
||||
"3": {
|
||||
"name": "3",
|
||||
"use": "web",
|
||||
"groups": ["left"]
|
||||
},
|
||||
"4": {
|
||||
"groups": ["left"],
|
||||
"name": "4",
|
||||
"use": "project"
|
||||
},
|
||||
"5": {
|
||||
"groups": ["left"],
|
||||
"name": "5",
|
||||
"use": "vms"
|
||||
},
|
||||
"6": {
|
||||
"groups": ["left"],
|
||||
"name": "6",
|
||||
"use": "documents"
|
||||
},
|
||||
"7": {
|
||||
"groups": ["left"],
|
||||
"name": "7",
|
||||
"use": "media"
|
||||
},
|
||||
"8": {
|
||||
"groups": ["left"],
|
||||
"name": "8",
|
||||
"use": "discord"
|
||||
},
|
||||
"9": {
|
||||
"groups": ["left"],
|
||||
"name": "9",
|
||||
"use": "zoom"
|
||||
},
|
||||
"10": {
|
||||
"groups": ["left"],
|
||||
"name": "10",
|
||||
"use": "config"
|
||||
},
|
||||
"11": {
|
||||
"groups": ["right"],
|
||||
"name": "1",
|
||||
"use": "terminal"
|
||||
},
|
||||
"12": {
|
||||
"groups": ["right"],
|
||||
"name": "2",
|
||||
"use": "code"
|
||||
},
|
||||
"13": {
|
||||
"groups": ["right"],
|
||||
"name": "3",
|
||||
"use": "web"
|
||||
},
|
||||
"14": {
|
||||
"groups": ["right"],
|
||||
"name": "4",
|
||||
"use": "project"
|
||||
},
|
||||
"15": {
|
||||
"groups": ["right"],
|
||||
"name": "5",
|
||||
"use": "misc"
|
||||
},
|
||||
"16": {
|
||||
"groups": ["right"],
|
||||
"name": "6",
|
||||
"use": "documents"
|
||||
},
|
||||
"17": {
|
||||
"groups": ["right"],
|
||||
"name": "7",
|
||||
"use": "media"
|
||||
},
|
||||
"18": {
|
||||
"groups": ["right"],
|
||||
"name": "8",
|
||||
"use": "slack"
|
||||
},
|
||||
"19": {
|
||||
"groups": ["right"],
|
||||
"name": "9",
|
||||
"use": "email"
|
||||
},
|
||||
"20": {
|
||||
"groups": ["right"],
|
||||
"name": "10",
|
||||
"use": "config"
|
||||
}
|
||||
}
|
||||
114
.config/eww##hostname.normandy/modules/i3.py
Executable file
114
.config/eww##hostname.normandy/modules/i3.py
Executable file
@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
from i3ipc.aio import Connection
|
||||
from i3ipc import Event
|
||||
from typing import List
|
||||
|
||||
class WorkspaceConfig:
|
||||
|
||||
i3_index: str
|
||||
sorting_index: int
|
||||
display_index: str
|
||||
display_name: str
|
||||
display_groups: List[str]
|
||||
|
||||
def __init__(self, i3_index: str, dictionary: dict):
|
||||
self.i3_index = i3_index
|
||||
self.sorting_index = dictionary.get("sort", int(i3_index))
|
||||
self.display_index = dictionary["name"]
|
||||
self.display_name = dictionary.get("use", "???")
|
||||
self.display_groups = dictionary.get("groups", ['default'])
|
||||
|
||||
@classmethod
|
||||
def parse_file(cls: type, filename: str):
|
||||
result = []
|
||||
dictionary: dict = None
|
||||
|
||||
with open(filename, 'r') as file:
|
||||
dictionary = json.load(file)
|
||||
|
||||
for index, data in dictionary.items():
|
||||
result.append(cls(index, data))
|
||||
|
||||
result.sort(key=lambda config: config.sorting_index)
|
||||
|
||||
return result
|
||||
|
||||
class WorkspaceStatus:
|
||||
|
||||
config: WorkspaceConfig
|
||||
expanded: bool
|
||||
state: str
|
||||
active: bool
|
||||
|
||||
def __init__(self, config, state = None):
|
||||
self.config = config
|
||||
self.state = state or ""
|
||||
self.active = state != None
|
||||
self.expanded = state == "focused" or state == "visible"
|
||||
|
||||
def dict_dump(self):
|
||||
return {
|
||||
"i3_index": self.config.i3_index,
|
||||
"name": self.config.display_index,
|
||||
"purpose": self.config.display_name,
|
||||
"expand": self.expanded,
|
||||
"state": self.state,
|
||||
"active": self.active
|
||||
}
|
||||
|
||||
config = WorkspaceConfig.parse_file(f"{os.environ['HOME']}/.config/eww/modules/i3.json")
|
||||
|
||||
groups = []
|
||||
|
||||
for ws in config:
|
||||
for group in ws.display_groups:
|
||||
if not group in groups:
|
||||
groups.append(group)
|
||||
|
||||
data = {
|
||||
"ws": {},
|
||||
"mode": "default"
|
||||
}
|
||||
|
||||
def write_data():
|
||||
global data
|
||||
print(json.dumps(data), flush=True)
|
||||
|
||||
async def workspace_event(self: Connection, _):
|
||||
global config, data
|
||||
|
||||
ws_i3_dict = { ws.name: ws for ws in await self.get_workspaces()}
|
||||
status = []
|
||||
for ws_def in config:
|
||||
ws_i3 = ws_i3_dict.get(ws_def.i3_index, None)
|
||||
if ws_i3:
|
||||
state = ws_i3.focused and "focused" or ws_i3.visible and "visible" or ws_i3.urgent and "urgent" or "unfocused"
|
||||
status.append(WorkspaceStatus(ws_def, state))
|
||||
else:
|
||||
status.append(WorkspaceStatus(ws_def))
|
||||
|
||||
for group in groups:
|
||||
data['ws'][group] = [ ws.dict_dump() for ws in filter(lambda ws: group in ws.config.display_groups, status) ]
|
||||
# data['ws']['left'] = [ ws.dict_dump() for ws in filter(lambda ws: ws.config.sorting_index in range(1, 11), status) ]
|
||||
# data['ws']['right'] = [ ws.dict_dump() for ws in filter(lambda ws: ws.config.sorting_index in range(11, 21), status) ]
|
||||
write_data()
|
||||
|
||||
async def mode_event(self: Connection, event):
|
||||
global data
|
||||
data['mode'] = event.change;
|
||||
write_data()
|
||||
|
||||
async def main():
|
||||
i3 = await Connection(auto_reconnect = True).connect()
|
||||
|
||||
i3.on(Event.WORKSPACE_FOCUS, workspace_event)
|
||||
i3.on(Event.WORKSPACE_URGENT, workspace_event)
|
||||
i3.on(Event.MODE, mode_event)
|
||||
await workspace_event(i3, None)
|
||||
await i3.main()
|
||||
|
||||
asyncio.run(main())
|
||||
47
.config/eww##hostname.normandy/modules/i3.yuck
Normal file
47
.config/eww##hostname.normandy/modules/i3.yuck
Normal file
@ -0,0 +1,47 @@
|
||||
(defwidget workspace [description]
|
||||
(revealer :transition "slideright"
|
||||
:reveal {description["active"]}
|
||||
:duration "500ms"
|
||||
(button :onclick "i3-msg workspace ${description['i3_index']}"
|
||||
(box :orientation "h"
|
||||
:halign "start"
|
||||
:class '${description["state"]} workspace'
|
||||
:space-evenly false
|
||||
:spacing 1
|
||||
(revealer :transition "slideleft" :reveal {description["state"] == "urgent"} :duration "500ms"
|
||||
" ")
|
||||
{description["name"]}
|
||||
(revealer :transition "slideright" :reveal {description["expand"]} :duration "500ms"
|
||||
': ${description["purpose"]}')))))
|
||||
|
||||
(deflisten i3-data :initial "[]"
|
||||
`~/.config/eww/modules/i3.py`)
|
||||
|
||||
(defwidget i3-mode [align]
|
||||
(box :orientation "h"
|
||||
:halign align
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
:class "module i3-mode"
|
||||
:visible {i3-data["mode"] != "default"}
|
||||
"mode: ${i3-data['mode']}"))
|
||||
|
||||
|
||||
(defwidget i3 [align group]
|
||||
(box :orientation "h"
|
||||
:halign align
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
:class "module i3"
|
||||
(workspace :description {i3-data["ws"][group][0]})
|
||||
(workspace :description {i3-data["ws"][group][1]})
|
||||
(workspace :description {i3-data["ws"][group][2]})
|
||||
(workspace :description {i3-data["ws"][group][3]})
|
||||
(workspace :description {i3-data["ws"][group][4]})
|
||||
(workspace :description {i3-data["ws"][group][5]})
|
||||
(workspace :description {i3-data["ws"][group][6]})
|
||||
(workspace :description {i3-data["ws"][group][7]})
|
||||
(workspace :description {i3-data["ws"][group][8]})
|
||||
(workspace :description {i3-data["ws"][group][9]})
|
||||
))
|
||||
|
||||
111
.config/eww##hostname.normandy/modules/mpris.py
Executable file
111
.config/eww##hostname.normandy/modules/mpris.py
Executable file
@ -0,0 +1,111 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import gi
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import time
|
||||
|
||||
try:
|
||||
gi.require_version('Playerctl', '2.0')
|
||||
except:
|
||||
sys.exit(1)
|
||||
|
||||
from gi.repository import Playerctl, GLib
|
||||
|
||||
title_maxlen = 30;
|
||||
artist_maxlen = 30;
|
||||
|
||||
class Status:
|
||||
|
||||
def __init__(self):
|
||||
self.running = False
|
||||
self.playing = False
|
||||
self.title = None
|
||||
self.artist = None
|
||||
self.album = None
|
||||
self.album_artist = None
|
||||
|
||||
def _dict_dump(self):
|
||||
return {
|
||||
"running": self.running,
|
||||
"playing": self.playing,
|
||||
"title": self.title,
|
||||
"artist": self.artist,
|
||||
"album": self.album,
|
||||
"album_artist": self.album_artist
|
||||
}
|
||||
|
||||
def __str__(self):
|
||||
return json.dumps(self._dict_dump())
|
||||
|
||||
class StatusDisplay:
|
||||
|
||||
def __init__(self):
|
||||
self._player = None
|
||||
|
||||
def show(self):
|
||||
self._init_player()
|
||||
|
||||
main = GLib.MainLoop()
|
||||
main.run()
|
||||
|
||||
def _get_status(self, playing = None):
|
||||
if self._player:
|
||||
status = Status()
|
||||
status.running = self._player.props.playback_status != 2
|
||||
if playing == None:
|
||||
status.playing = self._player.props.playback_status == 0
|
||||
else:
|
||||
status.playing = playing
|
||||
status.title = self._player.get_title() or ""
|
||||
status.artist = self._player.get_artist() or ""
|
||||
status.album = self._player.get_album() or ""
|
||||
|
||||
if len(status.title) > title_maxlen:
|
||||
status.title = status.title[:title_maxlen-1] + '…'
|
||||
if len(status.artist) > artist_maxlen:
|
||||
status.artist = status.artist[:artist_maxlen-1] + '…'
|
||||
|
||||
return status
|
||||
else:
|
||||
return Status()
|
||||
|
||||
def _print_status(self, status):
|
||||
print(status, flush=True)
|
||||
|
||||
def _init_player(self):
|
||||
|
||||
success = False
|
||||
|
||||
while not success:
|
||||
try:
|
||||
self._player = Playerctl.Player()
|
||||
self._player.connect('metadata', self._on_update)
|
||||
self._player.connect('play', self._on_play)
|
||||
self._player.connect('pause', self._on_pause)
|
||||
self._player.connect('exit', self._on_exit)
|
||||
|
||||
self._print_status(self._get_status())
|
||||
|
||||
success = True
|
||||
except:
|
||||
|
||||
self._print_status(Status())
|
||||
time.sleep(2)
|
||||
|
||||
def _on_update(self, *args):
|
||||
self._print_status(self._get_status())
|
||||
|
||||
def _on_play(self, *args):
|
||||
self._print_status(self._get_status(playing=True))
|
||||
|
||||
def _on_pause(self, *args):
|
||||
self._print_status(self._get_status(playing=False))
|
||||
|
||||
def _on_exit(self, player):
|
||||
del self._player
|
||||
self._player = None
|
||||
self._init_player()
|
||||
|
||||
StatusDisplay().show()
|
||||
17
.config/eww##hostname.normandy/modules/mpris.yuck
Normal file
17
.config/eww##hostname.normandy/modules/mpris.yuck
Normal file
@ -0,0 +1,17 @@
|
||||
(deflisten mpris-data
|
||||
`~/.config/eww/modules/mpris.py`)
|
||||
|
||||
(defwidget mpris [align]
|
||||
(box :orientation "h"
|
||||
:halign align
|
||||
:class "module mpris ${mpris-data['running'] ? '' : 'offline'} ${mpris-data['playing'] ? 'playing' : 'paused'}"
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
(label :class "fontawesome"
|
||||
:text "")
|
||||
(revealer :transition "none"
|
||||
:reveal {!mpris-data['running']}
|
||||
" players offline")
|
||||
(revealer :transition "none"
|
||||
:reveal {mpris-data['running']}
|
||||
" ${mpris-data['title']} by ${mpris-data['artist']}")))
|
||||
64
.config/eww##hostname.normandy/modules/network/network.py
Executable file
64
.config/eww##hostname.normandy/modules/network/network.py
Executable file
@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import subprocess
|
||||
import json
|
||||
import sys
|
||||
from time import sleep
|
||||
|
||||
TRUSTED_NETWORKS = ['honnouji', 'honnouji_2.4']
|
||||
|
||||
def wifi():
|
||||
ssid_cmd = subprocess.run(['iwgetid', '-r'], capture_output = True)
|
||||
if ssid_cmd.returncode != 0:
|
||||
return {"connected": False, "ssid": None}
|
||||
return {"connected": True, "ssid": ssid_cmd.stdout.decode('utf-8').strip()}
|
||||
|
||||
def netdev(device: str):
|
||||
ip_cmd = subprocess.run(['ip', '-j', 'addr', 'show', device], capture_output = True)
|
||||
if ip_cmd.returncode != 0:
|
||||
sys.stderr.write(ip_cmd.stdout.decode('utf-8'))
|
||||
return {"exists": False, "online": False}
|
||||
ip_data = json.loads(ip_cmd.stdout.decode('utf-8'))[0]
|
||||
ip4_addr = ""
|
||||
ip4_prefix_length = 24
|
||||
addr4_info = list(filter(lambda addr: addr.get("family") == "inet", ip_data["addr_info"]))
|
||||
if len(addr4_info) >= 1:
|
||||
ip4_addr = addr4_info[0]["local"]
|
||||
ip4_prefix_length = addr4_info[0]["prefixlen"]
|
||||
online = ip_data["operstate"] == "UP" or ip4_addr != ""
|
||||
connecting = ip_data["operstate"] != "DOWN" and (ip4_addr == "" or not online)
|
||||
return {
|
||||
"exists": True,
|
||||
"online": online,
|
||||
"connecting": connecting,
|
||||
"offline": not online and not connecting,
|
||||
"ip4_addr": ip4_addr,
|
||||
"ip4_prefix": ip4_prefix_length
|
||||
}
|
||||
|
||||
def trusted(ssid: str | None):
|
||||
# Don't throw up an "INSECURE" alert when offline
|
||||
if not ssid:
|
||||
return True
|
||||
return ssid in TRUSTED_NETWORKS
|
||||
|
||||
while True:
|
||||
insight = netdev('insight')
|
||||
wlan0 = netdev('wlan0')
|
||||
wifi_data = wifi()
|
||||
connected = insight['exists'] and insight['online'] or wifi_data['connected']
|
||||
networks = {
|
||||
"insight": insight,
|
||||
"wlan0": netdev("wlan0"),
|
||||
"ezrinet": netdev("ezrinet"),
|
||||
"wg-mullvad": netdev("wg-mullvad"),
|
||||
}
|
||||
result = {
|
||||
'connected': connected,
|
||||
"network": networks,
|
||||
'wifi': wifi_data,
|
||||
"trusted": True,
|
||||
"ip4_addrs": [ network.get('ip4_addr') for network in networks.values() if network.get('ip4_addr', "") != "" ]
|
||||
}
|
||||
print(json.dumps(result), flush=True)
|
||||
sleep(1)
|
||||
194
.config/eww##hostname.normandy/modules/network/network.yuck
Normal file
194
.config/eww##hostname.normandy/modules/network/network.yuck
Normal file
@ -0,0 +1,194 @@
|
||||
(deflisten network--data
|
||||
`~/.config/eww/modules/network/network.py`)
|
||||
|
||||
(defwidget network--wlan [device]
|
||||
(box :orientation "h"
|
||||
:halign "start"
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
(label :class "offline"
|
||||
:visible {network--data["network"][device]["offline"]}
|
||||
:text "offline")
|
||||
(label :visible {network--data["network"][device]["connecting"]}
|
||||
:class "highlight"
|
||||
:text "connecting...")
|
||||
(label :visible {network--data["network"][device]["online"]}
|
||||
:class "special"
|
||||
:text "${network--data['wifi']['ssid']}")))
|
||||
|
||||
(defwidget network--lan [device]
|
||||
(box :orientation "h"
|
||||
:halign "start"
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
(label :class "offline"
|
||||
:visible {network--data["network"][device]["offline"]}
|
||||
:text "offline")
|
||||
(label :visible {network--data["network"][device]["connecting"]}
|
||||
:class "highlight"
|
||||
:text "connecting...")
|
||||
(label :visible {network--data["network"][device]["online"] && !network--data.network[device].connecting}
|
||||
:class "special"
|
||||
:text "${network--data['network'][device]['ip4_addr']}/${network--data['network'][device]['ip4_prefix']}")))
|
||||
|
||||
(defwidget network--proxy-vpn [device ?required]
|
||||
(box :orientation "h"
|
||||
:halign "start"
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
:visible {network--data["network"][device]["exists"] || required}
|
||||
(label :class "highlight"
|
||||
:text "- insecure"
|
||||
:visible {! network--data["network"][device]["exists"]})
|
||||
(label :class "green"
|
||||
:text "- secured"
|
||||
:visible {network--data["network"][device]["exists"]})))
|
||||
|
||||
(defwidget proxy-network [device]
|
||||
(box :orientation "v"
|
||||
:halign "start"
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
(label :class "green"
|
||||
:text "connected"
|
||||
:visible {network--data.network[device].exists})
|
||||
(label :class "offline"
|
||||
:text "offline"
|
||||
:visible {!network--data.network[device].exists})
|
||||
"proxy vpn"))
|
||||
|
||||
(defwidget vpn-network []
|
||||
(box :orientation "v"
|
||||
:halign "start"
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
(label :class "highlight"
|
||||
:text "offline"
|
||||
:visible {! network--data["network"]["ezrinet"]["exists"] || ! network--data["connected"]})
|
||||
(label :class "green"
|
||||
:text "connected"
|
||||
:visible {network--data["network"]["ezrinet"]["exists"] && network--data["connected"]})
|
||||
"personal network"))
|
||||
|
||||
(defwidget network []
|
||||
(box :orientation "v"
|
||||
:halign "start"
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
(box :orientation "h"
|
||||
:halign "center"
|
||||
:space-evenly false
|
||||
:spacing 10
|
||||
(network--lan :device "insight")
|
||||
(network--proxy-vpn :device "wg-mullvad" :required {!network--data.trusted}))
|
||||
"communications"))
|
||||
|
||||
(defwidget network-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 :text "Comms"
|
||||
:class "medium special"))
|
||||
(centerbox :orientation "h"
|
||||
:halign "start"
|
||||
:class "nebula"
|
||||
:spacing 10
|
||||
:width 200
|
||||
:space-evenly false
|
||||
(box :halign "start"
|
||||
"Status:")
|
||||
""
|
||||
(box :halign "end"
|
||||
(label :text "Online"
|
||||
:class "green"
|
||||
:visible {network--data.connected})
|
||||
(label :text "Offline"
|
||||
:class "highlight"
|
||||
:visible {!network--data.connected})))
|
||||
(centerbox :orientation "h"
|
||||
:halign "start"
|
||||
:class "nebula"
|
||||
:spacing 10
|
||||
:width 200
|
||||
:space-evenly false
|
||||
(box :halign "start"
|
||||
"VPN:")
|
||||
""
|
||||
(box :halign "end"
|
||||
(label :text "Connected"
|
||||
:class "green"
|
||||
:visible {network--data.network.ezrinet.online && network--data.connected})
|
||||
(label :text "Offline"
|
||||
:class "highlight"
|
||||
:visible {!network--data.network.ezrinet.exists})))
|
||||
(centerbox :orientation "h"
|
||||
:halign "start"
|
||||
:class "nebula"
|
||||
:spacing 10
|
||||
:width 200
|
||||
:space-evenly false
|
||||
(box :halign "start"
|
||||
"Proxy:")
|
||||
""
|
||||
(box :halign "end"
|
||||
(label :text "Connected"
|
||||
:class "green"
|
||||
:visible {network--data.network.wg-mullvad.online})
|
||||
(label :text "Offline"
|
||||
:class "highlight"
|
||||
:visible {!network--data.network.wg-mullvad.exists})))
|
||||
(centerbox :orientation "h"
|
||||
:halign "start"
|
||||
:class "nebula"
|
||||
:spacing 10
|
||||
:width 200
|
||||
:space-evenly false
|
||||
(box :halign "start"
|
||||
"Wi-Fi:")
|
||||
""
|
||||
(box :halign "end"
|
||||
(label :text {network--data.wifi.ssid}
|
||||
:limit-width 12
|
||||
:class "green"
|
||||
:visible {network--data.wifi.connected})
|
||||
(label :text "Offline"
|
||||
:class "offline"
|
||||
:visible {!network--data.wifi.connected})))
|
||||
(centerbox :orientation "h"
|
||||
:halign "start"
|
||||
:spacing 10
|
||||
:width 200
|
||||
:class "nebula"
|
||||
:space-evenly false
|
||||
(box :valign "start"
|
||||
:halign "start"
|
||||
"Addrs:")
|
||||
""
|
||||
(box :valign "start"
|
||||
:halign "end"
|
||||
:visible {network--data.connected}
|
||||
:orientation "v"
|
||||
(for addr in {network--data.ip4_addrs}
|
||||
(box :class "special"
|
||||
:halign "end"
|
||||
{addr}))
|
||||
(box :visible {!network--data.connected}
|
||||
:class "offline"
|
||||
"none")))
|
||||
;; (box :orientation "h"
|
||||
;; :halign "start"
|
||||
;; :class "nebula"
|
||||
;; :spacing 10
|
||||
;; :space-evenly false
|
||||
;; (label :text "Addr:")
|
||||
;; (label :text {network--data.network[device].ip4_addr}
|
||||
;; :class "medium special"))))
|
||||
))
|
||||
10
.config/eww##hostname.normandy/modules/reboot.sh
Executable file
10
.config/eww##hostname.normandy/modules/reboot.sh
Executable file
@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env zsh
|
||||
|
||||
installed="$(file /boot/vmlinuz-linux | cut -d',' -f2 | cut -d' ' -f3)"
|
||||
running="$(uname -r)"
|
||||
|
||||
if [[ $installed == $running ]]; then
|
||||
echo "false"
|
||||
else
|
||||
echo "true"
|
||||
fi
|
||||
66
.config/eww##hostname.normandy/modules/system.py
Executable file
66
.config/eww##hostname.normandy/modules/system.py
Executable file
@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from time import sleep
|
||||
import subprocess
|
||||
import json
|
||||
|
||||
def static(**kwargs):
|
||||
def decorate(func):
|
||||
for k in kwargs:
|
||||
setattr(func, k, kwargs[k])
|
||||
return func
|
||||
return decorate
|
||||
|
||||
@static(idle = 0, total = 0)
|
||||
def cpu_util():
|
||||
with open('/proc/stat') as file:
|
||||
fields = [float(column) for column in file.readline().strip().split()[1:]]
|
||||
idle = fields[3]
|
||||
total = sum(fields)
|
||||
idle_delta = idle - cpu_util.idle
|
||||
total_delta = total - cpu_util.total
|
||||
utilization = 100 * (1 - idle_delta / total_delta)
|
||||
cpu_util.idle = idle
|
||||
cpu_util.total = total
|
||||
return f"{int(utilization):2}"
|
||||
|
||||
def wifi():
|
||||
ssid_cmd = subprocess.run(['iwgetid', '-r'], capture_output = True)
|
||||
if ssid_cmd.returncode != 0:
|
||||
return {"connected": False, "ssid": ""}
|
||||
return {"connected": True, "ssid": ssid_cmd.stdout.decode('utf-8').strip()}
|
||||
|
||||
def netdev(device: str):
|
||||
ip_cmd = subprocess.run(['ip', '-j', 'addr', 'show', device], capture_output = True)
|
||||
if ip_cmd.returncode != 0:
|
||||
return {"exists": False}
|
||||
ip_data = json.loads(ip_cmd.stdout.decode('utf-8'))[0]
|
||||
online = ip_data["operstate"] == "UP"
|
||||
ip4_addr = ""
|
||||
ip4_prefix_length = 24
|
||||
addr4_info = list(filter(lambda addr: addr.get("family") == "inet", ip_data["addr_info"]))
|
||||
if len(addr4_info) >= 1:
|
||||
ip4_addr = addr4_info[0]["local"]
|
||||
ip4_prefix_length = addr4_info[0]["prefixlen"]
|
||||
connecting = ip_data["operstate"] != "DOWN" and (ip4_addr == "" or not online)
|
||||
return {
|
||||
"exists": True,
|
||||
"online": online,
|
||||
"connecting": connecting,
|
||||
"ip4_addr": ip4_addr,
|
||||
"ip4_prefix": ip4_prefix_length
|
||||
}
|
||||
|
||||
# Prime cpu_util() with starting values
|
||||
cpu_util()
|
||||
sleep(0.1)
|
||||
|
||||
while True:
|
||||
result = {
|
||||
"cpu": cpu_util(),
|
||||
"network": {
|
||||
"br0": netdev("br0")
|
||||
}
|
||||
}
|
||||
print(json.dumps(result), flush=True)
|
||||
sleep(1)
|
||||
79
.config/eww##hostname.normandy/modules/system.yuck
Normal file
79
.config/eww##hostname.normandy/modules/system.yuck
Normal file
@ -0,0 +1,79 @@
|
||||
(deflisten system-data
|
||||
`~/.config/eww/modules/system.py`)
|
||||
(defpoll reboot-needed :interval "10s"
|
||||
:initial false
|
||||
`~/.config/eww/modules/reboot.sh`)
|
||||
|
||||
(defwidget reboot [align]
|
||||
(box :orientation "h"
|
||||
:halign align
|
||||
:class "module reboot"
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
:visible {reboot-needed}
|
||||
(button :onclick "~/.local/bin/i3-reboot"
|
||||
:timeout "60s"
|
||||
" reboot required")))
|
||||
|
||||
(defwidget memory [align]
|
||||
(box :orientation "h"
|
||||
:halign align
|
||||
:class "module memory"
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
' ${round(EWW_RAM["used_mem"]/1024/1024, 2)} GiB'))
|
||||
|
||||
(defwidget cpu [align]
|
||||
(box :orientation "h"
|
||||
:halign align
|
||||
:class "module cpu"
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
' ${system-data["cpu"]}%'))
|
||||
|
||||
(defwidget wlan-dev [align device]
|
||||
(box :orientation "h"
|
||||
:halign align
|
||||
:class "module wlan-dev network ${system-data['network'][device]['online'] ? 'online' : 'offline'} ${system-data['network'][device]['connecting'] ? 'connecting' : ''}"
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
""
|
||||
(revealer :transition "slideright"
|
||||
:reveal {!system-data["network"][device]["online"] && !system-data["network"][device]["connecting"]}
|
||||
:duration "500ms"
|
||||
" offline")
|
||||
(revealer :transition "slideright"
|
||||
:reveal {system-data["network"][device]["connecting"]}
|
||||
:duration "500ms"
|
||||
" connecting...")
|
||||
(revealer :transition "slideright"
|
||||
:reveal {system-data["network"][device]["online"] && system-data["network"][device]["ip4_addr"] != ""}
|
||||
:duration "500ms"
|
||||
' ${system-data["wifi"]["ssid"]}')
|
||||
))
|
||||
|
||||
(defwidget lan-dev [align device]
|
||||
(box :orientation "h"
|
||||
:halign align
|
||||
:class "module lan-dev network ${system-data['network'][device]['online'] ? 'online' : 'offline'} ${system-data['network'][device]['connecting'] ? 'connecting' : ''}"
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
(revealer :transition "none"
|
||||
:reveal {!system-data["network"][device]["online"] && !system-data["network"][device]["connecting"]}
|
||||
"")
|
||||
(revealer :transition "none"
|
||||
:reveal {system-data["network"][device]["online"] || system-data["network"][device]["connecting"]}
|
||||
"")
|
||||
(revealer :transition "slideright"
|
||||
:reveal {!system-data["network"][device]["online"] && !system-data["network"][device]["connecting"]}
|
||||
:duration "500ms"
|
||||
" offline")
|
||||
(revealer :transition "slideright"
|
||||
:reveal {system-data["network"][device]["connecting"]}
|
||||
:duration "500ms"
|
||||
" connecting...")
|
||||
(revealer :transition "slideright"
|
||||
:reveal {system-data["network"][device]["online"] && system-data["network"][device]["ip4_addr"] != ""}
|
||||
:duration "500ms"
|
||||
' ${system-data["network"][device]["ip4_addr"]}')
|
||||
))
|
||||
111
.config/eww##hostname.normandy/modules/system/system.py
Executable file
111
.config/eww##hostname.normandy/modules/system/system.py
Executable file
@ -0,0 +1,111 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from time import sleep
|
||||
import json
|
||||
import psutil
|
||||
import subprocess
|
||||
import os
|
||||
import sys
|
||||
|
||||
amdgpuinfo_installed = False
|
||||
# Try to import GPU info
|
||||
try:
|
||||
import pyamdgpuinfo as gpuinfo
|
||||
amdgpuinfo_installed = True
|
||||
except:
|
||||
pass
|
||||
|
||||
def cpu():
|
||||
util = psutil.cpu_percent(percpu=False)
|
||||
per_core = psutil.cpu_percent(percpu=True)
|
||||
return {
|
||||
"avg_display": f"{int(util):2}",
|
||||
"avg": util,
|
||||
"cores": [ { "value": core, "display": f"{int(core):2}" } for core in per_core ],
|
||||
"cores_display": "".join([ f"{int(core):3}" for core in per_core ]),
|
||||
"core_count": len(per_core)
|
||||
}
|
||||
|
||||
def gpu():
|
||||
if not amdgpuinfo_installed or gpuinfo.detect_gpus() <= 0:
|
||||
return {
|
||||
"available": False,
|
||||
"load": 0,
|
||||
"power_state": 0,
|
||||
"memory": 0
|
||||
}
|
||||
gpu = gpuinfo.get_gpu(0)
|
||||
return {
|
||||
"available": True,
|
||||
"load": gpu.query_load(),
|
||||
"power_state": gpu.query_power(),
|
||||
"memory": gpu.query_vram_usage() / gpu.memory_info['vram_size']
|
||||
}
|
||||
|
||||
def sensor(chip: str):
|
||||
return { temp.label: temp.current for temp in psutil.sensors_temperatures().get(chip) }
|
||||
|
||||
def memory():
|
||||
mem = psutil.virtual_memory()
|
||||
return {
|
||||
"total": mem.total,
|
||||
"available": mem.available,
|
||||
"percent": mem.percent,
|
||||
"used": mem.used,
|
||||
"free": mem.free,
|
||||
"active": mem.active,
|
||||
"inactive": mem.inactive,
|
||||
"buffers": mem.buffers,
|
||||
"cached": mem.cached,
|
||||
"shared": mem.shared,
|
||||
"slab": mem.slab
|
||||
}
|
||||
|
||||
def swap():
|
||||
swap = psutil.swap_memory()
|
||||
return {
|
||||
"total": swap.total,
|
||||
"used": swap.used,
|
||||
"free": swap.free,
|
||||
"percent": swap.percent
|
||||
}
|
||||
|
||||
def reboot():
|
||||
running = os.uname().release
|
||||
version_proc = subprocess.run(["pacman", "-Q", "linux"], capture_output=True)
|
||||
installed = version_proc.stdout.strip().decode('utf-8').split(' ')[1]
|
||||
return running != installed
|
||||
|
||||
def get_processes():
|
||||
return [ process for process in psutil.process_iter() ]
|
||||
|
||||
def cpu_top(processes):
|
||||
# Get the top 5 CPU usage processes
|
||||
try:
|
||||
result = [ { 'name': ps.name(), 'cpu': ps.cpu_percent() } for ps in processes ]
|
||||
result.sort(key=lambda ps : -ps.get('cpu'))
|
||||
return result[0:5]
|
||||
except:
|
||||
print("[ ERR ] missing process", file=sys.stderr)
|
||||
|
||||
def mem_top(processes):
|
||||
try:
|
||||
result = [ { 'name': ps.name(), 'memory': ps.memory_info().rss } for ps in processes ]
|
||||
result.sort(key=lambda ps : -ps.get('memory'))
|
||||
return result[0:5]
|
||||
except:
|
||||
print("[ ERR ] missing process", file=sys.stderr)
|
||||
|
||||
sensor_list = ['nvme', 'k10temp', 'amdgpu']
|
||||
|
||||
while True:
|
||||
result = {
|
||||
"cpu": cpu(),
|
||||
"gpu": gpu(),
|
||||
"sensors": { chip: sensor(chip) for chip in sensor_list },
|
||||
"memory": memory(),
|
||||
"swap": swap(),
|
||||
"reboot": reboot(),
|
||||
}
|
||||
print(json.dumps(result), flush=True)
|
||||
sleep(1)
|
||||
276
.config/eww##hostname.normandy/modules/system/system.yuck
Normal file
276
.config/eww##hostname.normandy/modules/system/system.yuck
Normal file
@ -0,0 +1,276 @@
|
||||
(defpoll system--hostname :interval "30s"
|
||||
`hostnamectl hostname --pretty`)
|
||||
|
||||
(deflisten system--data :initial "{}"
|
||||
`~/.config/eww/modules/system/system.py`)
|
||||
|
||||
(defwidget system-battery [battery]
|
||||
(box :orientation "v"
|
||||
:halign "start"
|
||||
:class "module text"
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
(label :class {EWW_BATTERY[battery].status == 'Charging' || EWW_BATTERY[battery].status == 'Full' ? 'green' : EWW_BATTERY[battery].capacity <= 25 ? 'highlight' : 'special'}
|
||||
:text "${EWW_BATTERY[battery].capacity}%")
|
||||
(label :text "${EWW_BATTERY[battery].status == 'Charging' || EWW_BATTERY[battery].status == 'Full' ? 'external' : 'internal'} power")))
|
||||
|
||||
(defwidget system-name []
|
||||
(label :halign "center"
|
||||
:valign "center"
|
||||
:class "module text big nebula"
|
||||
:text {system--hostname}))
|
||||
|
||||
(defwidget system-cpu-avg []
|
||||
(box :orientation "v"
|
||||
:halign "start"
|
||||
:class "module text"
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
(label :class {system--data.cpu.avg > 90 ? 'highlight' : 'special'}
|
||||
:text "${system--data.cpu.avg_display}%")
|
||||
(label :text "cpu utilization")))
|
||||
|
||||
(defwidget system--cpu-core [core]
|
||||
(label :class {core.value > 90 ? "highlight" : "special"}
|
||||
:text "${core.display}%"))
|
||||
|
||||
(defwidget system-cpu-percore []
|
||||
(box :orientation "v"
|
||||
:halign "start"
|
||||
:class "module text"
|
||||
:space-evenly false
|
||||
;; (box :orientation "h"
|
||||
;; :halign "center"
|
||||
;; :space-evenly false
|
||||
;; (for core in {system--data.cpu.cores}
|
||||
;; (system--cpu-core :core core)))
|
||||
(label :class "special"
|
||||
:text {system--data.cpu.cores_display})
|
||||
(label :text "per-core cpu utilization")
|
||||
))
|
||||
|
||||
(defwidget system-memory []
|
||||
(box :orientation "v"
|
||||
:halign "start"
|
||||
:class "module text"
|
||||
:space-evenly false
|
||||
(label :class {system--data.memory.percent > 85 ? "highlight" : "special"}
|
||||
:text "${round(system--data.memory.used / 1024 / 1024 / 1024, 2)} / ${round(system--data.memory.total / 1024 / 1024 / 1024, 2)} GiB")
|
||||
(label :text "system memory")))
|
||||
|
||||
(defwidget system--gauge [value ?threshold]
|
||||
(overlay :width 80
|
||||
:height 80
|
||||
(circular-progress :value 75
|
||||
:class "gauge-gutter"
|
||||
:start-at 37.5
|
||||
:thickness 2)
|
||||
(circular-progress :value {value * 0.75}
|
||||
:class 'gauge ${value > (threshold ?: 80) ? "highlight" : ""}'
|
||||
:start-at 37.5
|
||||
:thickness 2)))
|
||||
|
||||
(defwidget system-gauges []
|
||||
(box :orientation "h"
|
||||
:space-evenly false
|
||||
:width 200
|
||||
:halign "start"
|
||||
:spacing 10
|
||||
(box :orientation "v"
|
||||
:space-evenly false
|
||||
:spacing 20
|
||||
:width 95
|
||||
:halign "start"
|
||||
(system--cpu-gauge)
|
||||
(system--gpu-gauge)
|
||||
)
|
||||
(box :orientation "v"
|
||||
:space-evenly false
|
||||
:spacing 20
|
||||
:width 95
|
||||
:halign "end"
|
||||
(system--memory-gauge)
|
||||
(system--vram-gauge))))
|
||||
|
||||
(defwidget system--memory-gauge []
|
||||
(box :orientation "v"
|
||||
:halign "end"
|
||||
:width 95
|
||||
:space-evenly false
|
||||
:spacing 10
|
||||
:class "nebula"
|
||||
(label :text "RAM"
|
||||
:class "medium special")
|
||||
(box :halign "center"
|
||||
(overlay
|
||||
(system--gauge :value {system--data.memory.percent})
|
||||
(transform
|
||||
:translate-y "-2px"
|
||||
(box :halign "center"
|
||||
:valign "center"
|
||||
:orientation "h"
|
||||
:space-evenly false
|
||||
(label :text "%"
|
||||
:class "invisible")
|
||||
(label :text {round(system--data.memory.percent, 0)}
|
||||
:class "medium special")
|
||||
(transform
|
||||
:translate-y "3px"
|
||||
(label :text "%"
|
||||
:class "offline"))))))))
|
||||
|
||||
(defwidget system--cpu-gauge []
|
||||
(box :orientation "v"
|
||||
:halign "start"
|
||||
:width 95
|
||||
:space-evenly false
|
||||
:spacing 10
|
||||
:class "nebula"
|
||||
(label :text "CPU"
|
||||
:class "medium special")
|
||||
(box :halign "center"
|
||||
(overlay
|
||||
(system--gauge :value {system--data.cpu.avg})
|
||||
(transform
|
||||
:translate-y "-2px"
|
||||
(box :halign "center"
|
||||
:valign "center"
|
||||
:orientation "h"
|
||||
:space-evenly false
|
||||
(label :text "%"
|
||||
:class "invisible")
|
||||
(label :text {system--data.cpu.avg_display}
|
||||
:class "medium special")
|
||||
(transform
|
||||
:translate-y "3px"
|
||||
(label :text "%"
|
||||
:class "offline"))))))
|
||||
;; (box :orientation "v"
|
||||
;; :halign "end"
|
||||
;; :valign "start"
|
||||
;; :width 100
|
||||
;; :space-evenly false
|
||||
;; (for ps in {system--data.cpu_top}
|
||||
;; (centerbox :width 100
|
||||
;; :orientation "h"
|
||||
;; (box :halign "start"
|
||||
;; (label :text {ps.name}
|
||||
;; :limit-width 7))
|
||||
;; ""
|
||||
;; (box :halign "end"
|
||||
;; (label :text '${round(ps.cpu, 0)}%')))
|
||||
;; )
|
||||
))
|
||||
|
||||
(defwidget system--gpu-gauge []
|
||||
(box :orientation "v"
|
||||
:halign "start"
|
||||
:width 95
|
||||
:space-evenly false
|
||||
:spacing 10
|
||||
:class "nebula"
|
||||
(label :text "GPU"
|
||||
:class "medium special")
|
||||
(box :halign "center"
|
||||
(overlay
|
||||
(system--gauge :value {system--data.gpu.load * 100})
|
||||
(transform
|
||||
:translate-y "-2px"
|
||||
(box :halign "center"
|
||||
:valign "center"
|
||||
:orientation "h"
|
||||
:space-evenly false
|
||||
(label :text "%"
|
||||
:class "invisible")
|
||||
(label :text {round(system--data.gpu.load * 100, 0)}
|
||||
:class "medium special")
|
||||
(transform
|
||||
:translate-y "3px"
|
||||
(label :text "%"
|
||||
:class "offline"))))))))
|
||||
|
||||
(defwidget system--vram-gauge []
|
||||
(box :orientation "v"
|
||||
:halign "start"
|
||||
:width 95
|
||||
:space-evenly false
|
||||
:spacing 10
|
||||
:class "nebula"
|
||||
(label :text "VRAM"
|
||||
:class "medium special")
|
||||
(box :halign "center"
|
||||
(overlay
|
||||
(system--gauge :value {system--data.gpu.memory * 100})
|
||||
(transform
|
||||
:translate-y "-2px"
|
||||
(box :halign "center"
|
||||
:valign "center"
|
||||
:orientation "h"
|
||||
:space-evenly false
|
||||
(label :text "%"
|
||||
:class "invisible")
|
||||
(label :text {round(system--data.gpu.memory * 100, 0)}
|
||||
:class "medium special")
|
||||
(transform
|
||||
:translate-y "3px"
|
||||
(label :text "%"
|
||||
:class "offline"))))))))
|
||||
|
||||
|
||||
|
||||
(defwidget system--gauge-generic [value value-fmt big-text little-text ?subscript ?threshold]
|
||||
(box :orientation "h"
|
||||
:halign "start"
|
||||
:class "module gauge-widget"
|
||||
:space-evenly true
|
||||
:spacing 0
|
||||
(overlay :width 100
|
||||
(circular-progress :value 100
|
||||
:class "gauge-hole"
|
||||
:start-at 0
|
||||
:thickness 50
|
||||
)
|
||||
(circular-progress :value 80
|
||||
:class "gauge-gutter"
|
||||
:start-at 35
|
||||
:thickness 2)
|
||||
(circular-progress :value {value * 0.8}
|
||||
:class 'gauge ${value > (threshold ?: 80) ? "highlight" : ""}'
|
||||
:start-at 35
|
||||
:thickness 2)
|
||||
(transform :translate-y "-5px"
|
||||
:translate-x "3px"
|
||||
(box :halign "center"
|
||||
:valign "center"
|
||||
:class "big nebula"
|
||||
:space-evenly false
|
||||
"${value-fmt}"
|
||||
(box :class "normal offline"
|
||||
:halign "start"
|
||||
:valign "end"
|
||||
(transform :translate-y "-2px"
|
||||
:translate-x "1px"
|
||||
{subscript})))))
|
||||
(transform :translate-y "-5px"
|
||||
(box :orientation "v"
|
||||
:halign "center"
|
||||
:valign "center"
|
||||
:space-evenly false
|
||||
(label :text {big-text}
|
||||
:class "big nebula")
|
||||
(label :text {little-text}
|
||||
:class "nebula special")))))
|
||||
|
||||
;; (defwidget system-cpu-gauge []
|
||||
;; (system--gauge-generic :value {system--data.cpu.avg}
|
||||
;; :value-fmt {system--data.cpu.avg_display}
|
||||
;; :big-text "CPU"
|
||||
;; :little-text "usage"
|
||||
;; :subscript "%"))
|
||||
|
||||
;; (defwidget system-memory-gauge []
|
||||
;; (system--gauge-generic :value {system--data.memory.percent}
|
||||
;; :value-fmt {round(system--data.memory.percent, 0)}
|
||||
;; :big-text "RAM"
|
||||
;; :little-text "${round(system--data.memory.used / 1024 / 1024 / 1024, 1)} GiB"
|
||||
;; :subscript "%"))
|
||||
44
.config/eww##hostname.normandy/modules/timer/timer.py
Executable file
44
.config/eww##hostname.normandy/modules/timer/timer.py
Executable file
@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import datetime
|
||||
import os
|
||||
import json
|
||||
from time import sleep
|
||||
import sys
|
||||
|
||||
def get_time():
|
||||
try:
|
||||
with open(f'{os.environ["HOME"]}/.timer', 'rb') as timestamp_file:
|
||||
return datetime.datetime.fromtimestamp(int.from_bytes(timestamp_file.read(8), 'little'))
|
||||
except Exception as exc:
|
||||
return None
|
||||
|
||||
while True:
|
||||
now = datetime.datetime.now()
|
||||
end = get_time()
|
||||
if end is None:
|
||||
print(json.dumps({
|
||||
'hours': '00',
|
||||
'minutes': '00',
|
||||
'seconds': '00',
|
||||
'future': False,
|
||||
'timer': False
|
||||
}), flush=True)
|
||||
sleep(0.5)
|
||||
continue
|
||||
delta = int((now - get_time()).total_seconds())
|
||||
future = False
|
||||
if delta < 0:
|
||||
future = True
|
||||
delta = -delta
|
||||
hours = int(delta / 3600)
|
||||
minutes = int((delta % 3600) / 60)
|
||||
seconds = delta % 60
|
||||
print(json.dumps({
|
||||
'hours': hours,
|
||||
'minutes': f'{minutes:02}',
|
||||
'seconds': f'{seconds:02}',
|
||||
'future': future,
|
||||
'timer': True
|
||||
}), flush=True)
|
||||
sleep(0.5)
|
||||
29
.config/eww##hostname.normandy/modules/timer/timer.yuck
Normal file
29
.config/eww##hostname.normandy/modules/timer/timer.yuck
Normal file
@ -0,0 +1,29 @@
|
||||
(deflisten timer--data :initial '{"timer": false}'
|
||||
`~/.config/eww/modules/timer/timer.py`)
|
||||
|
||||
(defwidget timer--gauge [value green]
|
||||
(overlay :width 180
|
||||
:height 180
|
||||
(circular-progress :value 75
|
||||
:class "gauge-gutter"
|
||||
:start-at 37.5
|
||||
:thickness 2)
|
||||
(circular-progress :value {value * 0.75}
|
||||
:class 'gauge ${green ? "green" : ""}'
|
||||
:start-at 37.5
|
||||
:thickness 2)))
|
||||
|
||||
|
||||
(defwidget met []
|
||||
(box :orientation "h"
|
||||
:halign "center"
|
||||
:space-evenly false
|
||||
:spacing 5
|
||||
:class "nebula medium"
|
||||
:visible {timer--data.timer}
|
||||
"Baldur's Gate 3: "
|
||||
(label :text 'T${timer--data.future ? "-" : "+"}'
|
||||
:class {timer--data.future ? "highlight" : "green"})
|
||||
(label :text '${timer--data.hours}:${timer--data.minutes}:${timer--data.seconds}'
|
||||
:class "special")))
|
||||
|
||||
50
.config/eww##hostname.normandy/modules/volume/volume.py
Executable file
50
.config/eww##hostname.normandy/modules/volume/volume.py
Executable file
@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import asyncio
|
||||
import signal
|
||||
from contextlib import suppress
|
||||
import pulsectl_asyncio
|
||||
import json
|
||||
import sys
|
||||
import os
|
||||
|
||||
async def get_values(pulse):
|
||||
sink = None
|
||||
try:
|
||||
sink = await pulse.get_sink_by_name("@DEFAULT_SINK@")
|
||||
source = await pulse.get_source_by_name("@DEFAULT_SOURCE@")
|
||||
except:
|
||||
# reload the script in the event of an exception
|
||||
os.execv(sys.argv[0], sys.argv)
|
||||
sink_result = {
|
||||
"mute": sink.mute == 1,
|
||||
"volume": f"{int(sink.volume.value_flat * 100):2}"
|
||||
}
|
||||
source_result = {
|
||||
"mute": source.mute == 1,
|
||||
"volume": f"{int(source.volume.value_flat * 100):2}"
|
||||
}
|
||||
result = {
|
||||
"output": sink_result,
|
||||
"input": source_result
|
||||
}
|
||||
print(json.dumps(result), flush=True)
|
||||
return result
|
||||
|
||||
async def listen():
|
||||
async with pulsectl_asyncio.PulseAsync("volume-monitor") as pulse:
|
||||
await get_values(pulse)
|
||||
async for _ in pulse.subscribe_events('all'):
|
||||
await get_values(pulse)
|
||||
|
||||
async def main():
|
||||
listen_task = asyncio.create_task(listen())
|
||||
|
||||
for sig in (signal.SIGTERM, signal.SIGHUP, signal.SIGINT):
|
||||
loop.add_signal_handler(sig, listen_task.cancel)
|
||||
|
||||
with suppress(asyncio.CancelledError):
|
||||
await listen_task
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
loop.run_until_complete(main())
|
||||
28
.config/eww##hostname.normandy/modules/volume/volume.yuck
Normal file
28
.config/eww##hostname.normandy/modules/volume/volume.yuck
Normal file
@ -0,0 +1,28 @@
|
||||
(deflisten volume--data
|
||||
`~/.config/eww/modules/volume/volume.py`)
|
||||
|
||||
(defwidget volume-h []
|
||||
(box :orientation "h"
|
||||
:halign "start"
|
||||
:class "module ${volume--data['mute'] ? 'offline' : ''}"
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
(label :visible {!volume--data["mute"]} :text "")
|
||||
(label :visible { volume--data["mute"]} :text "")
|
||||
(label :text " ${volume--data['volume']}")))
|
||||
|
||||
(defwidget audio []
|
||||
(box :orientation "v"
|
||||
:halign "center"
|
||||
:class "module text"
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
(box :orientation "h"
|
||||
:halign "center"
|
||||
:space-evenly false
|
||||
:spacing 15
|
||||
(label :class {volume--data.output.mute ? "offline" : "special"}
|
||||
:text "${volume--data.output.volume}%")
|
||||
(label :class {volume--data.input.mute ? "offline" : "special"}
|
||||
:text "${volume--data.input.volume}%"))
|
||||
"audio system"))
|
||||
213
.config/eww##hostname.normandy/modules/workspaces/hyprland.py
Executable file
213
.config/eww##hostname.normandy/modules/workspaces/hyprland.py
Executable file
@ -0,0 +1,213 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
WorkspaceTree = dict[str, dict[str, list['Workspace']]]
|
||||
WorkspaceList = dict[str, 'Workspace']
|
||||
|
||||
socketdir = f"/tmp/hypr/{os.environ['HYPRLAND_INSTANCE_SIGNATURE']}"
|
||||
socket1 = f"{socketdir}/.socket.sock"
|
||||
socket2 = f"{socketdir}/.socket2.sock"
|
||||
|
||||
listened_events = ['workspace', 'movewindow', 'createworkspace', 'destroyworkspace']
|
||||
|
||||
class Workspace:
|
||||
|
||||
index: str = None
|
||||
name: str = None
|
||||
exec_cmd: str = None
|
||||
group: str = None
|
||||
|
||||
active: bool = False
|
||||
visible: bool = False
|
||||
focused: bool = False
|
||||
alerted: bool = False
|
||||
|
||||
def __init__(self, dictionary: dict[str, str | int], group: str):
|
||||
self.index = str(dictionary.get('index'))
|
||||
self.name = dictionary.get('name')
|
||||
self.exec_cmd = dictionary.get('exec')
|
||||
self.group = group
|
||||
|
||||
self.active = dictionary.get('active', False)
|
||||
self.visible = dictionary.get('visible', False)
|
||||
self.focused = dictionary.get('focused', False)
|
||||
self.alerted = dictionary.get('alerted', False)
|
||||
|
||||
def update_state(self, state_update, monitor_dict):
|
||||
self.active = True
|
||||
self.visible = monitor_dict[state_update['monitor']]['activeWorkspace']['name'] == state_update['name']
|
||||
self.focused = self.visible and monitor_dict[state_update['monitor']]['focused']
|
||||
self.alerted = False
|
||||
|
||||
def deactivate(self):
|
||||
self.active = False
|
||||
self.visible = False
|
||||
self.focused = False
|
||||
self.alerted = False
|
||||
|
||||
@classmethod
|
||||
def parse_file(cls: type, filename: str) -> tuple[WorkspaceTree, WorkspaceList]:
|
||||
|
||||
result = {}
|
||||
|
||||
initial: dict = None
|
||||
with open(filename, 'r') as file:
|
||||
initial = json.load(file)
|
||||
|
||||
workspaces: WorkspaceList = {}
|
||||
|
||||
def find_workspace(workspace: dict[str, str|int], group: str) -> 'Workspace':
|
||||
index = str(workspace.get('index'))
|
||||
if index in workspaces:
|
||||
return workspaces[index]
|
||||
else:
|
||||
workspaces[index] = cls(workspace, group)
|
||||
return workspaces[index]
|
||||
|
||||
result = {
|
||||
context: {
|
||||
group: [
|
||||
find_workspace(workspace, group) for workspace in workspaces
|
||||
] for group, workspaces in groups.items()
|
||||
} for context, groups in initial.items()
|
||||
}
|
||||
|
||||
return result, workspaces
|
||||
|
||||
@staticmethod
|
||||
def full_dictify(tree: WorkspaceTree):
|
||||
return {
|
||||
context: {
|
||||
group: [
|
||||
workspace.dictify() for workspace in workspaces
|
||||
] for group, workspaces in groups.items()
|
||||
} for context, groups in tree.items()
|
||||
}
|
||||
|
||||
def dictify(self):
|
||||
|
||||
return {
|
||||
'index': self.index,
|
||||
'name': self.name,
|
||||
'exec': self.exec_cmd,
|
||||
'active': self.active,
|
||||
'visible': self.visible,
|
||||
'focused': self.focused,
|
||||
'alerted': self.alerted
|
||||
}
|
||||
|
||||
workspace_tree, workspace_list = Workspace.parse_file(f"{os.environ['HOME']}/.config/hypr/workspaces.json")
|
||||
|
||||
data = {
|
||||
"ws": {},
|
||||
"current": {},
|
||||
"context": "personal",
|
||||
"visible": {},
|
||||
}
|
||||
|
||||
def write_data():
|
||||
global data
|
||||
print(json.dumps(data), flush=True)
|
||||
|
||||
async def get_workspace_data():
|
||||
request = b"[[BATCH]] j/workspaces; j/monitors"
|
||||
reader = None
|
||||
writer = None
|
||||
data = None
|
||||
try:
|
||||
reader, writer = await asyncio.open_unix_connection(socket1)
|
||||
writer.write(request)
|
||||
await writer.drain()
|
||||
data = await reader.read()
|
||||
finally:
|
||||
if writer is not None:
|
||||
writer.close()
|
||||
if data is None:
|
||||
return;
|
||||
|
||||
workspace_data = None
|
||||
monitor_data = None
|
||||
try:
|
||||
workspace_data, monitor_data = data.decode().split("}][{")
|
||||
workspace_data += "}]"
|
||||
monitor_data = "[{" + monitor_data
|
||||
except ValueError:
|
||||
print("Error unpacking response", file=sys.stderr)
|
||||
return
|
||||
|
||||
return [json.loads(workspace_data), json.loads(monitor_data)]
|
||||
|
||||
async def update_workspaces():
|
||||
global workspace_tree, workspace_list, data
|
||||
|
||||
workspaces, monitors = await get_workspace_data()
|
||||
|
||||
ws_hypr_dict = { ws["name"]: ws for ws in workspaces }
|
||||
monitor_dict = { monitor["name"]: monitor for monitor in monitors }
|
||||
|
||||
touched = []
|
||||
|
||||
for index, ws_data in ws_hypr_dict.items():
|
||||
|
||||
# Update data for all active workspaces
|
||||
ws_state = workspace_list.get(index)
|
||||
if ws_state is None:
|
||||
# If the workspace isn't configured, note it and handle gracefully
|
||||
print(f"Warning: found unconfigured workspace {index}", file=sys.stderr, flush=True)
|
||||
if monitor_dict[ws_data['monitor']]['activeWorkspace']['name'] == ws_data['name']:
|
||||
data['current'] = {
|
||||
'index': index,
|
||||
'name': 'undefined',
|
||||
'exec': 'alacritty',
|
||||
'active': True,
|
||||
'visible': True,
|
||||
'focused': True,
|
||||
'alerted': False
|
||||
}
|
||||
else:
|
||||
# Otherwise, 'touch' it and update status data
|
||||
touched.append(index)
|
||||
ws_state.update_state(ws_data, monitor_dict)
|
||||
if ws_state.focused:
|
||||
data['current'] = ws_state.dictify()
|
||||
if ws_state.visible:
|
||||
data['visible'][ws_state.group] = ws_state.dictify()
|
||||
|
||||
inactives = [
|
||||
v for k, v in workspace_list.items() if v.active and k not in touched
|
||||
]
|
||||
|
||||
for workspace in inactives:
|
||||
workspace.deactivate()
|
||||
|
||||
# Create an 'unknown' workspace object for each group that doesn't yet have a listed visible workspace
|
||||
for group in workspace_tree[data['context'] or 'personal'].keys():
|
||||
if group not in data['visible']:
|
||||
data['visible'][group] = {
|
||||
'index': 'U',
|
||||
'name': 'undefined',
|
||||
'exec': 'alacritty',
|
||||
'active': True,
|
||||
'visible': True,
|
||||
'focused': False,
|
||||
'alerted': False
|
||||
}
|
||||
data['ws'] = Workspace.full_dictify(workspace_tree)
|
||||
write_data()
|
||||
|
||||
async def main():
|
||||
reader, writer = await asyncio.open_unix_connection(socket2)
|
||||
|
||||
await update_workspaces()
|
||||
|
||||
while True:
|
||||
event = (await reader.readline()).decode()
|
||||
eventType, eventData = event.split(">>")
|
||||
if eventType in listened_events:
|
||||
await update_workspaces()
|
||||
|
||||
asyncio.run(main())
|
||||
184
.config/eww##hostname.normandy/modules/workspaces/sway.py
Executable file
184
.config/eww##hostname.normandy/modules/workspaces/sway.py
Executable file
@ -0,0 +1,184 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from i3ipc.aio import Connection
|
||||
from i3ipc import Event
|
||||
from typing import List, Dict, Any, Union, Tuple
|
||||
|
||||
WorkspaceTree = Dict[str, Dict[str, List['Workspace']]]
|
||||
WorkspaceList = Dict[str, 'Workspace']
|
||||
|
||||
class Workspace:
|
||||
|
||||
index: str = None
|
||||
name: str = None
|
||||
exec_cmd: str = None
|
||||
group: str = None
|
||||
|
||||
active: bool = False
|
||||
visible: bool = False
|
||||
focused: bool = False
|
||||
alerted: bool = False
|
||||
|
||||
def __init__(self, dictionary: Dict[str, Union[str, int]], group: str):
|
||||
self.index = str(dictionary.get('index'))
|
||||
self.name = dictionary.get('name')
|
||||
self.exec_cmd = dictionary.get('exec')
|
||||
self.group = group
|
||||
|
||||
self.active = dictionary.get('active', False)
|
||||
self.visible = dictionary.get('visible', False)
|
||||
self.focused = dictionary.get('focused', False)
|
||||
self.alerted = dictionary.get('alerted', False)
|
||||
|
||||
def update_state(self, state_update: Any):
|
||||
self.active = True
|
||||
self.visible = state_update.visible
|
||||
self.focused = state_update.focused
|
||||
self.alerted = state_update.urgent
|
||||
|
||||
def deactivate(self):
|
||||
self.active = False
|
||||
self.visible = False
|
||||
self.focused = False
|
||||
self.alerted = False
|
||||
|
||||
@classmethod
|
||||
def parse_file(cls: type, filename: str) -> Tuple[WorkspaceTree, WorkspaceList]:
|
||||
|
||||
result = {}
|
||||
|
||||
initial: dict = None
|
||||
with open(filename, 'r') as file:
|
||||
initial = json.load(file)
|
||||
|
||||
workspaces: WorkspaceList = {}
|
||||
|
||||
def find_workspace(workspace: Dict[str, Union[str, int]], group: str) -> 'Workspace':
|
||||
index = str(workspace.get('index'))
|
||||
if index in workspaces:
|
||||
return workspaces[index]
|
||||
else:
|
||||
workspaces[index] = cls(workspace, group)
|
||||
return workspaces[index]
|
||||
|
||||
result = {
|
||||
context: {
|
||||
group: [
|
||||
find_workspace(workspace, group) for workspace in workspaces
|
||||
] for group, workspaces in groups.items()
|
||||
} for context, groups in initial.items()
|
||||
}
|
||||
|
||||
return result, workspaces
|
||||
|
||||
@staticmethod
|
||||
def full_dictify(tree: WorkspaceTree):
|
||||
return {
|
||||
context: {
|
||||
group: [
|
||||
workspace.dictify() for workspace in workspaces
|
||||
] for group, workspaces in groups.items()
|
||||
} for context, groups in tree.items()
|
||||
}
|
||||
|
||||
def dictify(self):
|
||||
|
||||
return {
|
||||
'index': self.index,
|
||||
'name': self.name,
|
||||
'exec': self.exec_cmd,
|
||||
'active': self.active,
|
||||
'visible': self.visible,
|
||||
'focused': self.focused,
|
||||
'alerted': self.alerted
|
||||
}
|
||||
|
||||
workspace_tree, workspace_list = Workspace.parse_file(f"{os.environ['HOME']}/.config/sway/workspaces.json")
|
||||
|
||||
data = {
|
||||
"ws": {},
|
||||
"mode": "default",
|
||||
"current": {},
|
||||
"context": "personal",
|
||||
"visible": {},
|
||||
}
|
||||
|
||||
def write_data():
|
||||
global data
|
||||
print(json.dumps(data), flush=True)
|
||||
|
||||
async def workspace_event(self: Connection, _):
|
||||
global workspace_tree, workspace_list, data
|
||||
|
||||
ws_sway_dict = { ws.name: ws for ws in await self.get_workspaces()}
|
||||
|
||||
touched = []
|
||||
|
||||
for index, ws_data in ws_sway_dict.items():
|
||||
|
||||
# Update data for all active workspaces
|
||||
ws_state = workspace_list.get(index)
|
||||
if ws_state is None:
|
||||
# If the workspace isn't configured, note it and handle gracefully
|
||||
print(f"Warning: found unconfigured workspace {index}", file=sys.stderr, flush=True)
|
||||
if ws_data.focused:
|
||||
data['current'] = {
|
||||
'index': index,
|
||||
'name': 'undefined',
|
||||
'exec': 'alacritty',
|
||||
'active': True,
|
||||
'visible': True,
|
||||
'focused': True,
|
||||
'alerted': False
|
||||
}
|
||||
else:
|
||||
# Otherwise, 'touch' it and update the status data
|
||||
touched.append(index)
|
||||
ws_state.update_state(ws_data)
|
||||
if ws_state.focused:
|
||||
data['current'] = ws_state.dictify()
|
||||
if ws_state.visible:
|
||||
data['visible'][ws_state.group] = ws_state.dictify()
|
||||
|
||||
# 'Deactivate' any untouched workspaces
|
||||
inactives = [
|
||||
v for k, v in workspace_list.items() if v.active and k not in touched
|
||||
]
|
||||
|
||||
for workspace in inactives:
|
||||
workspace.deactivate()
|
||||
|
||||
# Create an 'unknown' workspace object for each group that doesn't yet have a listed visible workspace
|
||||
for group in workspace_tree[data['context'] or 'personal'].keys():
|
||||
if group not in data['visible']:
|
||||
data['visible'][group] = {
|
||||
'index': 'U',
|
||||
'name': 'undefined',
|
||||
'exec': 'alacritty',
|
||||
'active': True,
|
||||
'visible': True,
|
||||
'focused': False,
|
||||
'alerted': False
|
||||
}
|
||||
|
||||
data['ws'] = Workspace.full_dictify(workspace_tree)
|
||||
write_data()
|
||||
|
||||
async def mode_event(self: Connection, event):
|
||||
global data
|
||||
data['mode'] = event.change
|
||||
write_data()
|
||||
|
||||
async def main():
|
||||
sway = await Connection(auto_reconnect = True).connect()
|
||||
|
||||
sway.on(Event.WORKSPACE, workspace_event)
|
||||
sway.on(Event.MODE, mode_event)
|
||||
await workspace_event(sway, None)
|
||||
await sway.main()
|
||||
|
||||
asyncio.run(main())
|
||||
@ -0,0 +1,75 @@
|
||||
(deflisten sway--data :initial '{mode: "default"}'
|
||||
`~/.config/eww/modules/workspaces/sway.py`)
|
||||
|
||||
(defvar hypr--data '{}')
|
||||
|
||||
(defwidget sway--workspace [ws]
|
||||
(button :onclick "swaymsg workspace ${ws['index']}"
|
||||
(circular-progress
|
||||
:class 'indicator-circle sway--ws ${ws.active ? "sway--active" : ""} ${ws.visible ? "sway--visible" : ""} ${ws.focused ? "sway--focused" : ""} ${ws.alerted ? "sway--alerted" : ""}'
|
||||
:value 100
|
||||
:start-at 0
|
||||
:clockwise true
|
||||
:width 16
|
||||
:thickness 1
|
||||
(box :class 'fill' :visible {ws.visible}))))
|
||||
|
||||
(defwidget hypr--workspace [ws]
|
||||
(button :onclick "hyprmsg dispatch workspace ${ws['index']}"
|
||||
(circular-progress
|
||||
:class 'indicator-circle sway--ws ${ws.active ? "sway--active" : ""} ${ws.visible ? "sway--visible" : ""} ${ws.focused ? "sway--focused": ""}'
|
||||
:value 100
|
||||
:start-at 0
|
||||
:clockwise true
|
||||
:width 15
|
||||
:thickness 1
|
||||
(box :class 'fill' :visible {ws.focused}))))
|
||||
|
||||
(defwidget sway-mode []
|
||||
(box :orientation "v"
|
||||
:halign "center"
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
:class "module text"
|
||||
:visible {hypr--data.mode != "default"}
|
||||
(label :class "special"
|
||||
:text "${hypr--data.mode}")
|
||||
"sway mode"))
|
||||
|
||||
(defwidget sway-workspace [group]
|
||||
(box :orientation "v"
|
||||
:halign "center"
|
||||
:valign "center"
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
:class 'module text nebula medium'
|
||||
(label :class '${sway--data.visible[group].focused ? "special" : "offline"}'
|
||||
:text '${sway--data.visible[group].name}')))
|
||||
|
||||
(defwidget hypr-workspace [group]
|
||||
(box :orientation "v"
|
||||
:halign "center"
|
||||
:space-evenly false
|
||||
:spacing 0
|
||||
:class 'module text'
|
||||
(label :class '${hypr--data.visible[group].focused ? "special" : "offline"}'
|
||||
:text '${hypr--data.visible[group].name}')
|
||||
(label :text "current workspace")))
|
||||
|
||||
(defwidget sway-workspaces [group]
|
||||
(box :orientation "h"
|
||||
:halign "start"
|
||||
:space-evenly false
|
||||
:spacing 5
|
||||
:class "sway--root"
|
||||
(for workspace in {sway--data.ws[sway--data.context ?: "personal"][group]}
|
||||
(sway--workspace :ws workspace))))
|
||||
|
||||
(defwidget hypr-workspaces [group]
|
||||
(box :orientation "h"
|
||||
:halign "start"
|
||||
:space-evenly false
|
||||
:spacing 5
|
||||
:class "sway--root"
|
||||
(for workspace in {hypr--data.ws[hypr--data.context ?: "personal"][group]}
|
||||
(hypr--workspace :ws workspace))))
|
||||
@ -1,19 +1,249 @@
|
||||
#### ####
|
||||
## Triskelion sway Config File ##
|
||||
#### ####
|
||||
|
||||
### ###
|
||||
# Tycho Station Sway Config #
|
||||
# Display settings #
|
||||
### ###
|
||||
|
||||
# This config file is modular. Set variables here, then use them in
|
||||
# drop-in config files located in ~/.config/sway/config.d/
|
||||
# This section controls how connected displays are treated
|
||||
|
||||
set $leftdisplay HDMI-A-1
|
||||
set $rightdisplay DP-2
|
||||
|
||||
output {
|
||||
$leftdisplay pos 0 0 mode --custom 1920x1080@75Hz
|
||||
$rightdisplay pos 1920 0 mode --custom 1920x1080@75Hz
|
||||
* bg "#1e1e1e" solid_color
|
||||
}
|
||||
|
||||
input '*' xkb_file ~/.config/sway/keymap.xkb
|
||||
|
||||
### ###
|
||||
# Window Manager settings #
|
||||
### ###
|
||||
|
||||
# This section controls how the window manager functions. It does
|
||||
# not include keybinds that are not directly related to the window
|
||||
# manager's window management functions.
|
||||
|
||||
set $mod Mod4
|
||||
set $alt Mod1
|
||||
|
||||
set $colors.primary '#815986'
|
||||
set $colors.background '#2d272f'
|
||||
# Set font
|
||||
font pango:Source Code Pro 8
|
||||
|
||||
# Reload configuration
|
||||
bindsym $mod+Shift+r reload
|
||||
|
||||
# Exit
|
||||
bindsym $mod+Shift+e exec i3-logout
|
||||
|
||||
### ###
|
||||
# HERE BE DRAGONS #
|
||||
# Window Management Settings #
|
||||
### ###
|
||||
|
||||
# Do not edit anything below these comments. You have been warned.
|
||||
include ~/.config/sway/config.d/*.conf
|
||||
# This section controls window behavior and management keybinds.
|
||||
# It also includes the resize mode.
|
||||
|
||||
# Set monitor vars
|
||||
set $monitor_1 $leftdisplay
|
||||
set $monitor_2 $rightdisplay
|
||||
|
||||
# Set keybinds to switch workspaces
|
||||
bindsym $mod+1 exec swaymsg workspace $(i3-sensible-workspaces 1)
|
||||
bindsym $mod+2 exec swaymsg workspace $(i3-sensible-workspaces 2)
|
||||
bindsym $mod+3 exec swaymsg workspace $(i3-sensible-workspaces 3)
|
||||
bindsym $mod+4 exec swaymsg workspace $(i3-sensible-workspaces 4)
|
||||
bindsym $mod+5 exec swaymsg workspace $(i3-sensible-workspaces 5)
|
||||
bindsym $mod+6 exec swaymsg workspace $(i3-sensible-workspaces 6)
|
||||
bindsym $mod+7 exec swaymsg workspace $(i3-sensible-workspaces 7)
|
||||
bindsym $mod+8 exec swaymsg workspace $(i3-sensible-workspaces 8)
|
||||
bindsym $mod+9 exec swaymsg workspace $(i3-sensible-workspaces 9)
|
||||
bindsym $mod+0 exec swaymsg workspace $(i3-sensible-workspaces 10)
|
||||
|
||||
# Set keybinds to move windows between workspaces
|
||||
bindsym $mod+Shift+1 exec i3-move-container 1
|
||||
bindsym $mod+Shift+2 exec i3-move-container 2
|
||||
bindsym $mod+Shift+3 exec i3-move-container 3
|
||||
bindsym $mod+Shift+4 exec i3-move-container 4
|
||||
bindsym $mod+Shift+5 exec i3-move-container 5
|
||||
bindsym $mod+Shift+6 exec i3-move-container 6
|
||||
bindsym $mod+Shift+7 exec i3-move-container 7
|
||||
bindsym $mod+Shift+8 exec i3-move-container 8
|
||||
bindsym $mod+Shift+9 exec i3-move-container 9
|
||||
bindsym $mod+Shift+0 exec i3-move-container 10
|
||||
|
||||
# Bind workspaces to monitors
|
||||
workspace 1 output $monitor_1
|
||||
workspace 2 output $monitor_1
|
||||
workspace 3 output $monitor_1
|
||||
workspace 4 output $monitor_1
|
||||
workspace 5 output $monitor_1
|
||||
workspace 6 output $monitor_1
|
||||
workspace 7 output $monitor_1
|
||||
workspace 8 output $monitor_1
|
||||
workspace 9 output $monitor_1
|
||||
workspace 10 output $monitor_1
|
||||
|
||||
workspace 11 output $monitor_2
|
||||
workspace 12 output $monitor_2
|
||||
workspace 13 output $monitor_2
|
||||
workspace 14 output $monitor_2
|
||||
workspace 15 output $monitor_2
|
||||
workspace 16 output $monitor_2
|
||||
workspace 17 output $monitor_2
|
||||
workspace 18 output $monitor_2
|
||||
workspace 19 output $monitor_2
|
||||
workspace 20 output $monitor_2
|
||||
|
||||
# Allow dragging of floating windows whilst holding $mod
|
||||
floating_modifier $mod
|
||||
|
||||
# Resize mode
|
||||
mode "resize" {
|
||||
bindsym Left resize shrink width 10 px or 1 ppt
|
||||
bindsym Down resize grow height 10 px or 1 ppt
|
||||
bindsym Up resize shrink height 10 px or 1 ppt
|
||||
bindsym f resize grow width 10 px or 1 ppt
|
||||
|
||||
# Exit resize mode
|
||||
bindsym Return mode "default"
|
||||
bindsym Escape mode "default"
|
||||
bindsym $mod+r mode "default"
|
||||
}
|
||||
|
||||
bindsym $mod+r mode "resize"
|
||||
|
||||
# Move windows
|
||||
bindsym $mod+Shift+b move left
|
||||
bindsym $mod+Shift+f move right
|
||||
bindsym $mod+Shift+p move up
|
||||
bindsym $mod+Shift+n move down
|
||||
|
||||
bindsym $mod+Shift+Left move left
|
||||
bindsym $mod+Shift+Right move right
|
||||
bindsym $mod+Shift+Up move up
|
||||
bindsym $mod+Shift+Down move down
|
||||
|
||||
# Shift focus
|
||||
bindsym $mod+b focus left
|
||||
bindsym $mod+f focus right
|
||||
bindsym $mod+p focus up
|
||||
bindsym $mod+n focus down
|
||||
bindsym $mod+a focus parent
|
||||
|
||||
# Splits
|
||||
bindsym $mod+h split h
|
||||
bindsym $mod+v split v
|
||||
|
||||
# Fullscreen
|
||||
bindsym $mod+$alt+f fullscreen toggle
|
||||
|
||||
# Floating
|
||||
bindsym $mod+Shift+space floating toggle
|
||||
bindsym $mod+space focus mode_toggle
|
||||
|
||||
# Close window
|
||||
bindsym $mod+Shift+q kill
|
||||
|
||||
# Change window arrangement
|
||||
bindsym $mod+e layout toggle split
|
||||
bindsym $mod+t layout tabbed
|
||||
bindsym $mod+s layout stacking
|
||||
|
||||
### ###
|
||||
# Appearance #
|
||||
### ###
|
||||
|
||||
default_border pixel 1
|
||||
|
||||
# Colors
|
||||
set $primary #815986
|
||||
set $background #2d272f
|
||||
set $wallpaper #242933
|
||||
|
||||
client.focused $primary $background $primary $primary $primary
|
||||
client.focused_inactive $primary $background $primary $background $background
|
||||
client.unfocused $background $background $primary $background $background
|
||||
|
||||
# Gaps
|
||||
gaps inner 10
|
||||
|
||||
workspace 1 gaps left 210
|
||||
workspace 2 gaps left 210
|
||||
workspace 3 gaps left 210
|
||||
workspace 4 gaps left 210
|
||||
workspace 5 gaps left 210
|
||||
workspace 6 gaps left 210
|
||||
workspace 7 gaps left 210
|
||||
workspace 8 gaps left 210
|
||||
workspace 9 gaps left 210
|
||||
workspace 10 gaps left 210
|
||||
|
||||
# Themes
|
||||
|
||||
set $gnome-schema org.gnome.desktop.interface
|
||||
|
||||
exec_always gsettings set $gnome-schema {
|
||||
gtk-theme 'Nordic-darker'
|
||||
icon-theme 'ePapirus-Dark'
|
||||
font-name 'Source Sans Pro:12'
|
||||
}
|
||||
|
||||
### ###
|
||||
# Desktop Features #
|
||||
### ###
|
||||
|
||||
# This section includes basic desktop features, such as
|
||||
# the ability to launch programs and functional media keys
|
||||
|
||||
# Program launchers
|
||||
bindsym {
|
||||
# "Sensible" launcher -- auto-launch default program for workspace
|
||||
$mod+Return exec i3-sensible-launcher
|
||||
# Terminal launcher
|
||||
$mod+Shift+Return exec alacritty
|
||||
# Program menu launcher
|
||||
$mod+d exec wofi --show drun
|
||||
}
|
||||
|
||||
# Media keys (function while locked, be careful with these)
|
||||
bindsym --locked {
|
||||
# Playback Control
|
||||
XF86AudioPlay exec playerctl play-pause
|
||||
XF86AudioNext exec playerctl next
|
||||
XF86AudioPrev exec playerctl previous
|
||||
}
|
||||
|
||||
# Utility keybinds
|
||||
bindsym {
|
||||
# Screen locker
|
||||
$mod+l exec loginctl lock-session
|
||||
# Dismiss notification
|
||||
$mod+Ctrl+Space exec dunstctl close
|
||||
}
|
||||
|
||||
# Start daemons
|
||||
exec_always /usr/bin/systemctl --user import-environment DISPLAY WAYLAND_DISPLAY SWAYSOCK
|
||||
exec hash dbus-update-activation-environment 2>/dev/null && \
|
||||
dbus-update-activation-environment --systemd DISPLAY WAYLAND_DISPLAY SWAYSOCK SSH_AUTH_SOCK GPG_TTY
|
||||
exec_always /usr/bin/systemctl --user start sway-session.target
|
||||
|
||||
exec noisetorch -i -t 95
|
||||
|
||||
# Start status bars
|
||||
exec eww open-many leftbar rightbar sidebar
|
||||
|
||||
# Keyboard settings
|
||||
input * {
|
||||
xkb_numlock enabled
|
||||
xkb_options compose:menu
|
||||
}
|
||||
|
||||
# Key overrides for G-keys
|
||||
bindsym XF86Tools exec wtype -k F13
|
||||
bindsym XF86Launch5 exec wtype -k F14
|
||||
bindsym XF86Launch6 exec wtype -k F15
|
||||
bindsym XF86Launch7 exec wtype -k F16
|
||||
bindsym XF86Launch8 exec wtype -k F17
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
# Display Arrangement #
|
||||
|
||||
set $leftdisplay 'HDMI-A-1'
|
||||
set $centerdisplay 'DP-2'
|
||||
|
||||
output {
|
||||
$leftdisplay pos 0 0 mode 1920x1200
|
||||
$centerdisplay pos 1920 0 mode 1920x1200
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
set $leftdisplay 'Hewlett Packard HP Z24n CN47090537'
|
||||
set $centerdisplay 'Hewlett Packard HP Z24n CN4709053L'
|
||||
|
||||
output {
|
||||
$leftdisplay pos 0 0 mode 1920x1200
|
||||
$centerdisplay pos 1920 0 mode 1920x1200
|
||||
}
|
||||
@ -2,15 +2,7 @@
|
||||
# Display Settings #
|
||||
### ###
|
||||
|
||||
set $leftdisplay 'Hewlett Packard HP Z24n CN47090537'
|
||||
set $centerdisplay 'Hewlett Packard HP Z24n CN4709053L'
|
||||
|
||||
output {
|
||||
$leftdisplay pos 0 0 mode 1920x1200
|
||||
$centerdisplay pos 1920 0 mode 1920x1200
|
||||
# * bg '/home/ezri/2023-09-28T14:06:22,599070355-06:00.png' center '#1e1e1e'
|
||||
* bg '#1e1e1e' solid_color
|
||||
}
|
||||
output * bg '#1e1e1e' solid_color
|
||||
|
||||
mode "output-switching" {
|
||||
bindsym 1 mode output-1
|
||||
@ -1,9 +1,9 @@
|
||||
# -*-conf-*-
|
||||
|
||||
timeout 30 'if pgrep swaylock; then swaymsg "output * power off"; pkill -SIGSTOP electron; pkill -SIGSTOP slack; fi' resume 'if pgrep swaylock; then swaymsg "output * power on"; pkill -SIGCONT electron; pkill -SIGCONT slack; fi'
|
||||
timeout 300 'if pgrep swaylock; then lock-keyring; fi'
|
||||
timeout 10 'pgrep swaylock &> /dev/null && swaymsg "output * power off"' resume 'pgrep swaylock &> /dev/null && swaymsg "output * power on"'
|
||||
timeout 15 'pgrep swaylock &> /dev/null && pkill -u 1000 -SIGSTOP electron' resume 'pgrep swaylock &> /dev/null && pkill -u 1000 -SIGCONT electron'
|
||||
lock ~/.local/bin/screenlock
|
||||
before-sleep 'loginctl lock-session'
|
||||
unlock 'pkill -USR1 swaylock'
|
||||
idlehint 300
|
||||
unlock 'pkill -9 swaylock'
|
||||
idlehint 600
|
||||
|
||||
|
||||
109
.config/sway/workspaces.json##hostname.normandy
Normal file
109
.config/sway/workspaces.json##hostname.normandy
Normal file
@ -0,0 +1,109 @@
|
||||
{
|
||||
"personal": {
|
||||
"left": [
|
||||
{
|
||||
"index": 1,
|
||||
"name": "terminal",
|
||||
"exec": "alacritty"
|
||||
},
|
||||
{
|
||||
"index": 2,
|
||||
"name": "code",
|
||||
"exec": "alacritty"
|
||||
},
|
||||
{
|
||||
"index": 3,
|
||||
"name": "web",
|
||||
"exec": "firefox --new-window"
|
||||
},
|
||||
{
|
||||
"index": 4,
|
||||
"name": "project",
|
||||
"exec": "firefox --new-window"
|
||||
},
|
||||
{
|
||||
"index": 5,
|
||||
"name": "images",
|
||||
"exec": "dolphin"
|
||||
},
|
||||
{
|
||||
"index": 6,
|
||||
"name": "documents",
|
||||
"exec": "alacritty"
|
||||
},
|
||||
{
|
||||
"index": 7,
|
||||
"name": "music",
|
||||
"exec": "spotify"
|
||||
},
|
||||
{
|
||||
"index": 8,
|
||||
"name": "discord",
|
||||
"exec": "discord",
|
||||
"systemd": false
|
||||
},
|
||||
{
|
||||
"index": 9,
|
||||
"name": "email",
|
||||
"exec": "alacritty"
|
||||
},
|
||||
{
|
||||
"index": 10,
|
||||
"name": "config",
|
||||
"exec": "pavucontrol"
|
||||
}
|
||||
],
|
||||
"right": [
|
||||
{
|
||||
"index": 11,
|
||||
"name": "terminal",
|
||||
"exec": "alacritty"
|
||||
},
|
||||
{
|
||||
"index": 12,
|
||||
"name": "code",
|
||||
"exec": "alacritty"
|
||||
},
|
||||
{
|
||||
"index": 13,
|
||||
"name": "web",
|
||||
"exec": "firefox --new-window"
|
||||
},
|
||||
{
|
||||
"index": 14,
|
||||
"name": "steam",
|
||||
"exec": "steam"
|
||||
},
|
||||
{
|
||||
"index": 15,
|
||||
"name": "minecraft",
|
||||
"exec": "polymc"
|
||||
},
|
||||
{
|
||||
"index": 16,
|
||||
"name": "virtual machines",
|
||||
"exec": "virt-manager"
|
||||
},
|
||||
{
|
||||
"index": 17,
|
||||
"name": "video",
|
||||
"exec": "vlc"
|
||||
},
|
||||
{
|
||||
"index": 18,
|
||||
"name": "slack",
|
||||
"exec": "slack"
|
||||
},
|
||||
{
|
||||
"index": 19,
|
||||
"name": "zoom",
|
||||
"exec": "zoom"
|
||||
},
|
||||
{
|
||||
"index": 20,
|
||||
"name": "config",
|
||||
"exec": "pavucontrol"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,16 @@
|
||||
#!/usr/bin/env zsh
|
||||
|
||||
output_sets=("DP-1" "DP-2")
|
||||
output_sets=()
|
||||
|
||||
current_output=$(swaymsg -t get_workspaces | jq '.[] | select(.focused==true).output' | cut -d'"' -f2)
|
||||
current_workspace=$(swaymsg -t get_workspaces | jq '.[] | select(.focused==true).name' | cut -d'"' -f2)
|
||||
# Order outputs by x position (left to right)
|
||||
for output in $(swaymsg -t get_outputs | jq '[ (.[] | select(.active) | pick(.name, .rect)) ] | sort_by(.rect.x) | .[] | .name' -r); do
|
||||
output_sets+=$output
|
||||
done
|
||||
|
||||
workspaces=$(swaymsg -t get_workspaces)
|
||||
|
||||
current_output=$(echo $workspaces | jq '.[] | select(.focused).output' -r)
|
||||
current_workspace=$(echo $workspaces | jq '.[] | select(.focused).name' -r)
|
||||
|
||||
function get_ws {
|
||||
ws_set=$1
|
||||
|
||||
@ -1 +1 @@
|
||||
Subproject commit c3d4e576c9c86eac62884bd47c01f6faed043fc5
|
||||
Subproject commit a411ef3e0992d4839f0732ebeb9823024afaaaa8
|
||||
@ -1 +1 @@
|
||||
Subproject commit 8dd05bfcc12b0cd1ee9ea64be725b3d9f713cf64
|
||||
Subproject commit 4abed97b6e67eb5590b39bcd59080aa23192f25d
|
||||
@ -1 +1 @@
|
||||
Subproject commit e0165eaa730dd0fa321a6a6de74f092fe87630b0
|
||||
Subproject commit c7caf57ca805abd54f11f756fda6395dd4187f8a
|
||||
Loading…
x
Reference in New Issue
Block a user