test_version

This commit is contained in:
2026-07-11 16:41:17 +04:00
parent f0106699b9
commit d9c9bf546b
5 changed files with 120 additions and 123 deletions
+114 -118
View File
@@ -1,10 +1,9 @@
#!/usr/bin/env python3
import asyncio
import json import json
import subprocess
import os import os
import sys import sys
import socket
import threading
import time
class UnifiedWorkspaceManager: class UnifiedWorkspaceManager:
def __init__(self): def __init__(self):
@@ -16,11 +15,8 @@ class UnifiedWorkspaceManager:
self.monitors_cache = {} self.monitors_cache = {}
self.load_icon_configuration() self.load_icon_configuration()
self.clients_refresh()
self.monitors_refresh()
def load_icon_configuration(self): def load_icon_configuration(self):
# тянем иконки из конфига
config_path = os.path.join(self.script_directory, "workspaces_map.json") config_path = os.path.join(self.script_directory, "workspaces_map.json")
try: try:
with open(config_path, 'r') as file: with open(config_path, 'r') as file:
@@ -28,53 +24,59 @@ class UnifiedWorkspaceManager:
except: except:
self.icon_map = {"default": "", "no-icon": ""} self.icon_map = {"default": "", "no-icon": ""}
def monitors_refresh(self): async def monitors_refresh(self):
# обновляем инфу по мониторам
try: try:
raw = subprocess.check_output(["hyprctl", "-j", "monitors"], stderr=subprocess.DEVNULL) proc = await asyncio.create_subprocess_exec(
monitors = json.loads(raw) "hyprctl", "-j", "monitors",
self.monitors_cache = { stdout=asyncio.subprocess.PIPE,
m['name']: { stderr=asyncio.subprocess.DEVNULL
"id": m['id'], )
"active_workspace_id": m['activeWorkspace']['id'], stdout, _ = await proc.communicate()
"focused": m.get('focused', False) if proc.returncode == 0:
} for m in monitors monitors = json.loads(stdout.decode())
} self.monitors_cache = {
m['name']: {
"id": m['id'],
"active_workspace_id": m['activeWorkspace']['id'],
"focused": m.get('focused', False)
} for m in monitors
}
except: except:
self.monitors_cache = {} self.monitors_cache = {}
def clients_refresh(self): async def clients_refresh(self):
# полный сброс и пересборка кэша окон
try: try:
raw = subprocess.check_output(["hyprctl", "-j", "clients"], stderr=subprocess.DEVNULL) proc = await asyncio.create_subprocess_exec(
clients = json.loads(raw) "hyprctl", "-j", "clients",
self.clients_cache = { stdout=asyncio.subprocess.PIPE,
c['address'].strip().replace('0x', ''): { stderr=asyncio.subprocess.DEVNULL
"class": c['class'], )
"workspace_id": c['workspace']['id'], stdout, _ = await proc.communicate()
"monitor_id": c['monitor'] if proc.returncode == 0:
} for c in clients clients = json.loads(stdout.decode())
} self.clients_cache = {
c['address'].strip().replace('0x', ''): {
"class": c['class'],
"workspace_id": c['workspace']['id'],
"monitor_id": c['monitor']
} for c in clients
}
except: except:
self.clients_cache = {} self.clients_cache = {}
def get_current_state_json(self): def get_current_state_json(self):
# собираем финальный json
try: try:
output = {} output = {}
for mon_name, mon_data in self.monitors_cache.items(): for mon_name, mon_data in self.monitors_cache.items():
m_id = mon_data["id"] m_id = mon_data["id"]
active_id = mon_data["active_workspace_id"] active_id = mon_data["active_workspace_id"]
focused = mon_data["focused"] focused = mon_data["focused"]
# готовим 10 слотов
workspaces = { workspaces = {
str(i): {"exists": False, "active": False, "focused": False, "urgent": False, "icons": []} str(i): {"exists": False, "active": False, "focused": False, "urgent": False, "icons": []}
for i in range(1, 11) for i in range(1, 11)
} }
# раскидываем окна по воркспейсам
for addr, client in self.clients_cache.items(): for addr, client in self.clients_cache.items():
if client['monitor_id'] == m_id: if client['monitor_id'] == m_id:
ws_id = str(client['workspace_id']) ws_id = str(client['workspace_id'])
@@ -88,12 +90,10 @@ class UnifiedWorkspaceManager:
if icon not in slot["icons"]: if icon not in slot["icons"]:
slot["icons"].append(icon) slot["icons"].append(icon)
# помечаем текущий активный воркспейс
curr_ws = str(active_id) curr_ws = str(active_id)
if curr_ws in workspaces: if curr_ws in workspaces:
workspaces[curr_ws].update({"active": True, "focused": focused, "exists": True}) workspaces[curr_ws].update({"active": True, "focused": focused, "exists": True})
# рисуем no-icon
for ws in workspaces.values(): for ws in workspaces.values():
if ws["exists"] and not ws["icons"]: if ws["exists"] and not ws["icons"]:
ws["icons"] = [self.icon_map.get("no-icon", "")] ws["icons"] = [self.icon_map.get("no-icon", "")]
@@ -105,109 +105,105 @@ class UnifiedWorkspaceManager:
return "{}" return "{}"
def push_update(self): def push_update(self):
# отдаём json в stdout при изменении
current = self.get_current_state_json() current = self.get_current_state_json()
if current != self.last_sent_json: if current != self.last_sent_json:
self.last_sent_json = current self.last_sent_json = current
sys.stdout.write(current + '\n') sys.stdout.write(current + '\n')
sys.stdout.flush() sys.stdout.flush()
def update_client(self, window_address): async def update_client(self, window_address):
# обновляем окно
try: try:
clean_addr = window_address.replace('0x', '') clean_addr = window_address.replace('0x', '')
search_addr = f"0x{clean_addr}" search_addr = f"0x{clean_addr}"
raw = subprocess.check_output(["hyprctl", "-j", "clients"], stderr=subprocess.DEVNULL) proc = await asyncio.create_subprocess_exec(
clients = json.loads(raw) "hyprctl", "-j", "clients",
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.DEVNULL
)
stdout, _ = await proc.communicate()
target = next((c for c in clients if c['address'] == search_addr), None) if proc.returncode == 0:
clients = json.loads(stdout.decode())
if target: target = next((c for c in clients if c['address'] == search_addr), None)
self.clients_cache[clean_addr] = {
"class": target.get('class', 'unknown'),
"workspace_id": target['workspace']['id'],
"monitor_id": target['monitor']
}
else:
self.clients_cache.pop(clean_addr, None)
if target:
self.clients_cache[clean_addr] = {
"class": target.get('class', 'unknown'),
"workspace_id": target['workspace']['id'],
"monitor_id": target['monitor']
}
return
self.clients_cache.pop(clean_addr, None)
except: except:
# если что-то отвалилось, то просто пересобираем всё await self.clients_refresh()
self.clients_refresh()
def run(self): async def run(self):
await self.clients_refresh()
await self.monitors_refresh()
self.push_update() self.push_update()
def event_socket_listener(): signature = os.environ.get('HYPRLAND_INSTANCE_SIGNATURE')
# слушаем сокет хайпера socket_path = f"/run/user/{os.getuid()}/hypr/{signature}/.socket2.sock"
signature = os.environ.get('HYPRLAND_INSTANCE_SIGNATURE')
socket_path = f"/run/user/{os.getuid()}/hypr/{signature}/.socket2.sock"
while True: while True:
try: try:
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as sock: reader, writer = await asyncio.open_unix_connection(socket_path)
sock.connect(socket_path)
buf = "" while True:
while True: line_bytes = await reader.readline()
data = sock.recv(4096).decode('utf-8', errors='ignore') if not line_bytes:
if not data: break break
buf += data
line = line_bytes.decode('utf-8', errors='ignore').strip()
if '\n' in buf: if ">>" not in line:
lines = buf.split('\n') continue
buf = lines.pop()
event, args = line.split('>>')
trigger = False trigger = False
for line in lines:
if ">>" not in line: continue if event in ["openwindow", "movewindow"]:
event, args = line.split('>>') addr = args.split(',')[0].replace('0x', '')
await self.update_client(addr)
# ивенты с окнами trigger = True
if event in ["openwindow", "movewindow"]:
addr = args.split(',')[0].replace('0x', '') elif event == "closewindow":
self.update_client(addr) addr = args.replace('0x', '')
trigger = True self.clients_cache.pop(addr, None)
self.urgent_window_addresses.discard(addr)
elif event == "closewindow": trigger = True
addr = args.replace('0x', '')
self.clients_cache.pop(addr, None)
self.urgent_window_addresses.discard(addr)
trigger = True
# ивенты с мониторами elif event in ["workspace", "workspacev2", "monitoradded", "monitorremoved"]:
elif event in ["workspace", "workspacev2", "monitoradded", "monitorremoved"]: await self.monitors_refresh()
self.monitors_refresh() trigger = True
trigger = True
elif event in ["focusedmon", "focusedmonv2"]: elif event in ["focusedmon", "focusedmonv2"]:
mon_name = args.split(',')[0] mon_name = args.split(',')[0]
for name in self.monitors_cache: for name in self.monitors_cache:
self.monitors_cache[name]["focused"] = (name == mon_name) self.monitors_cache[name]["focused"] = (name == mon_name)
trigger = True trigger = True
# алерты elif event == "urgent":
elif event == "urgent": self.urgent_window_addresses.add(args.replace('0x', ''))
self.urgent_window_addresses.add(args.replace('0x', '')) trigger = True
trigger = True
elif event in ["activewindow", "activewindowv2"]:
elif event in ["activewindow", "activewindowv2"]: addr = args.split(',')[0].replace('0x', '')
addr = args.split(',')[0].replace('0x', '') if addr in self.urgent_window_addresses:
if addr in self.urgent_window_addresses: self.urgent_window_addresses.remove(addr)
self.urgent_window_addresses.remove(addr) trigger = True
trigger = True
if trigger: if trigger:
self.push_update() self.push_update()
except:
time.sleep(2) # реконнект, если хайпер отвалился except (asyncio.CancelledError, KeyboardInterrupt):
break
# слушаем поток в фоне except Exception as e:
threading.Thread(target=event_socket_listener, daemon=True).start() await asyncio.sleep(2)
try:
while True: time.sleep(100)
except KeyboardInterrupt: sys.exit(0)
if __name__ == "__main__": if __name__ == "__main__":
UnifiedWorkspaceManager().run() try:
asyncio.run(UnifiedWorkspaceManager().run())
except KeyboardInterrupt:
sys.exit(0)
+1 -1
View File
@@ -4,7 +4,7 @@
"firefox": "", "firefox": "",
"code-oss": "󰨞", "code-oss": "󰨞",
"org.telegram.desktop": "", "org.telegram.desktop": "",
"kitty": "", "foot": "",
"steam": "", "steam": "",
"Spotify": "", "Spotify": "",
"discord": "󰙯", "discord": "󰙯",
+1
View File
@@ -6,3 +6,4 @@ exec-once = gsettings set org.gnome.desktop.interface gtk-theme "Adwaita-dark"
exec-once = gsettings set org.gnome.desktop.interface color-scheme "prefer-dark" exec-once = gsettings set org.gnome.desktop.interface color-scheme "prefer-dark"
exec-once = eww daemon && sleep 1 && eww open-many bar_0 bar_1 wallpaper_0 wallpaper_1 exec-once = eww daemon && sleep 1 && eww open-many bar_0 bar_1 wallpaper_0 wallpaper_1
exec-once = hypridle --grace 15 exec-once = hypridle --grace 15
exec-once = python3 /home/pyrschtjag/Documents/.ntfy-notify/ntfy-notify.py
+2 -2
View File
@@ -1,4 +1,4 @@
$mainMod = SUPER $mainMod = SUPER
$terminal = kitty $terminal = foot
$fileManager = kitty mc //$fileManager = kitty mc
$menu = rofi -show drun $menu = rofi -show drun
+2 -2
View File
@@ -6,9 +6,9 @@ source = ~/.config/hypr/config/env.conf
source = ~/.config/hypr/config/general.conf source = ~/.config/hypr/config/general.conf
source = ~/.config/hypr/config/decoration.conf source = ~/.config/hypr/config/decoration.conf
source = ~/.config/hypr/config/animations.conf source = ~/.config/hypr/config/animations.conf
source = ~/.config/hypr/config/dwindle.conf //source = ~/.config/hypr/config/dwindle.conf
source = ~/.config/hypr/config/master.conf source = ~/.config/hypr/config/master.conf
source = ~/.config/hypr/config/misc.conf source = ~/.config/hypr/config/misc.conf
source = ~/.config/hypr/config/input.conf source = ~/.config/hypr/config/input.conf
source = ~/.config/hypr/config/bind.conf source = ~/.config/hypr/config/bind.conf
source = ~/.config/hypr/config/windowrule.conf source = ~/.config/hypr/config/windowrule.conf