diff --git a/yambar/config.yml b/yambar/config.yml index 61d2ff4..095170c 100644 --- a/yambar/config.yml +++ b/yambar/config.yml @@ -75,27 +75,122 @@ bar: spacing: 10 left: - - i3: - sort: ascending + #- i3: + #sort: ascending + #content: + #"": + #map: + #default: {string: {text: "{name}", foreground: *col_fg_alt, margin: 7}} + #conditions: + #state == focused: {string: {text: "{name}", margin: 7}} + + - script: + path: /home/midgard/.config/yambar/sway.py + anchors: + monitor_sep: &monitor_sep {string: {text: "|"}} + ws_common: &ws_common {left-margin: 7, right-margin: 7} + ws_default: &ws_default {foreground: *col_fg_alt, <<: *ws_common} + ws_focused: &ws_focused {foreground: *col_fg, <<: *ws_common} + ws_visible: &ws_visible {foreground: *col_visible_ws, <<: *ws_common} content: - "": - map: - tag: state - default: {string: {text: "{name}", foreground: *col_fg_alt, margin: 7}} - values: - "focused": {string: {text: "{name}", margin: 7}} - #current: - #list: - #left-margin: 25 - #spacing: 0 - #items: - #- { string: {text: "{application}", foreground: *col_fg_alt}} - #- map: - #tag: application - #default: { string: {text: ": ", foreground: *col_fg_alt}} - #values: - #"": {empty: {}} - #- { string: {text: "{title}", foreground: *col_fg_alt}} + - list: + spacing: 0 + items: + - map: + default: {string: {text: "{ws0}", <<: *ws_default}} + conditions: + ws0_state == unused: {empty: {}} + ws0_state == focused: {string: {text: "{ws0}", <<: *ws_focused}} + ws0_state == visible: {string: {text: "{ws0}", <<: *ws_visible}} + - map: {default: {empty: {}}, conditions: {ws1_new_monitor: *monitor_sep}} + - map: + default: {string: {text: "{ws1}", <<: *ws_default}} + conditions: + ws1_state == unused: {empty: {}} + ws1_state == focused: {string: {text: "{ws1}", <<: *ws_focused}} + ws1_state == visible: {string: {text: "{ws1}", <<: *ws_visible}} + - map: {default: {empty: {}}, conditions: {ws2_new_monitor: *monitor_sep}} + - map: + default: {string: {text: "{ws2}", <<: *ws_default}} + conditions: + ws2_state == unused: {empty: {}} + ws2_state == focused: {string: {text: "{ws2}", <<: *ws_focused}} + ws2_state == visible: {string: {text: "{ws2}", <<: *ws_visible}} + - map: {default: {empty: {}}, conditions: {ws3_new_monitor: *monitor_sep}} + - map: + default: {string: {text: "{ws3}", <<: *ws_default}} + conditions: + ws3_state == unused: {empty: {}} + ws3_state == focused: {string: {text: "{ws3}", <<: *ws_focused}} + ws3_state == visible: {string: {text: "{ws3}", <<: *ws_visible}} + - map: {default: {empty: {}}, conditions: {ws4_new_monitor: *monitor_sep}} + - map: + default: {string: {text: "{ws4}", <<: *ws_default}} + conditions: + ws4_state == unused: {empty: {}} + ws4_state == focused: {string: {text: "{ws4}", <<: *ws_focused}} + ws4_state == visible: {string: {text: "{ws4}", <<: *ws_visible}} + - map: {default: {empty: {}}, conditions: {ws5_new_monitor: *monitor_sep}} + - map: + default: {string: {text: "{ws5}", <<: *ws_default}} + conditions: + ws5_state == unused: {empty: {}} + ws5_state == focused: {string: {text: "{ws5}", <<: *ws_focused}} + ws5_state == visible: {string: {text: "{ws5}", <<: *ws_visible}} + - map: {default: {empty: {}}, conditions: {ws6_new_monitor: *monitor_sep}} + - map: + default: {string: {text: "{ws6}", <<: *ws_default}} + conditions: + ws6_state == unused: {empty: {}} + ws6_state == focused: {string: {text: "{ws6}", <<: *ws_focused}} + ws6_state == visible: {string: {text: "{ws6}", <<: *ws_visible}} + - map: {default: {empty: {}}, conditions: {ws7_new_monitor: *monitor_sep}} + - map: + default: {string: {text: "{ws7}", <<: *ws_default}} + conditions: + ws7_state == unused: {empty: {}} + ws7_state == focused: {string: {text: "{ws7}", <<: *ws_focused}} + ws7_state == visible: {string: {text: "{ws7}", <<: *ws_visible}} + - map: {default: {empty: {}}, conditions: {ws8_new_monitor: *monitor_sep}} + - map: + default: {string: {text: "{ws8}", <<: *ws_default}} + conditions: + ws8_state == unused: {empty: {}} + ws8_state == focused: {string: {text: "{ws8}", <<: *ws_focused}} + ws8_state == visible: {string: {text: "{ws8}", <<: *ws_visible}} + - map: {default: {empty: {}}, conditions: {ws9_new_monitor: *monitor_sep}} + - map: + default: {string: {text: "{ws9}", <<: *ws_default}} + conditions: + ws9_state == unused: {empty: {}} + ws9_state == focused: {string: {text: "{ws9}", <<: *ws_focused}} + ws9_state == visible: {string: {text: "{ws9}", <<: *ws_visible}} + - map: {default: {empty: {}}, conditions: {ws10_new_monitor: *monitor_sep}} + - map: + default: {string: {text: "{ws10}", <<: *ws_default}} + conditions: + ws10_state == unused: {empty: {}} + ws10_state == focused: {string: {text: "{ws10}", <<: *ws_focused}} + ws10_state == visible: {string: {text: "{ws10}", <<: *ws_visible}} + - map: {default: {empty: {}}, conditions: {ws11_new_monitor: *monitor_sep}} + - map: + default: {string: {text: "{ws11}", <<: *ws_default}} + conditions: + ws11_state == unused: {empty: {}} + ws11_state == focused: {string: {text: "{ws11}", <<: *ws_focused}} + ws11_state == visible: {string: {text: "{ws11}", <<: *ws_visible}} + - map: + default: + - {empty: {left-margin: 5}} + - string: + text: "{mode}" + left-margin: 5 + right-margin: 5 + deco: + background: + color: 9c5842d8 + conditions: + mode == default: {empty: {}} - mem: interval: 1500 diff --git a/yambar/sway.py b/yambar/sway.py new file mode 100755 index 0000000..6b0442f --- /dev/null +++ b/yambar/sway.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python3 + +import os +import sys +import socket +import struct +import json +import traceback +from time import sleep +from typing import List + + +MSGTYPE_GET_WORKSPACES = 1 +MSGTYPE_SUBSCRIBE = 2 +MSGTYPE_GET_BINDING_STATE = 12 +MSGTYPE_EVENT_WORKSPACE = 0x80000000 +MSGTYPE_EVENT_MODE = 0x80000002 + +magic = b"i3-ipc" +msg_header = struct.Struct("II") + +s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) +s.connect(os.getenvb(b"SWAYSOCK")) + +def recv(): + reply_magic = s.recv(6) + assert reply_magic == magic + reply_header = s.recv(msg_header.size) + reply_length, reply_type = msg_header.unpack(reply_header) + reply_payload = s.recv(reply_length) + return reply_type, reply_payload + +def msg(msg_type: int, payload: bytes): + global s + message = ( + magic + + msg_header.pack(len(payload), msg_type) + + payload + ) + n_sent_bytes = s.send(message) + assert n_sent_bytes == len(message) + +def subscribe(types: List[str]): + msg(MSGTYPE_SUBSCRIBE, json.dumps(types).encode("utf-8")) + reply = recv() + success = json.loads(reply[1])["success"] + if not success: + raise Exception("Could not subscribe") + + +def format_workspaces(workspaces): + result = [] + last_output = None + for i, ws in enumerate(sorted(workspaces, key=lambda x: (x["output"], x["num"], x["name"]))): + name = ws["name"] + + new_monitor = last_output is not None and last_output != ws["output"] + last_output = ws["output"] + + state = ( + "focused" if ws["focused"] else + "visible" if ws["visible"] else + "" + ) + + result.append(f"ws{i}|string|{ws['name']}") + result.append(f"ws{i}_state|string|{state}") + result.append(f"ws{i}_new_monitor|bool|{'true' if new_monitor else 'false'}") + + while i <= 11: + result.append(f"ws{i}|string|") + result.append(f"ws{i}_state|string|unused") + result.append(f"ws{i}_new_monitor|bool|false") + i += 1 + + return "\n".join(result) + + +msg(MSGTYPE_GET_BINDING_STATE, b'') +state_mode = json.loads(recv()[1])["name"] +msg(MSGTYPE_GET_WORKSPACES, b'') +state_workspaces = json.loads(recv()[1]) + + +def print_state(): + global state_workspaces + global state_mode + print(f"""{format_workspaces(state_workspaces)} +mode|string|{state_mode} +""", flush=True) + sleep(0.01) + +print_state() + +subscribe(["workspace", "mode"]) +while True: + try: + item_type, item = recv() + + if item_type == MSGTYPE_GET_WORKSPACES: + state_workspaces = json.loads(item) + print_state() + elif item_type == MSGTYPE_EVENT_WORKSPACE: + msg(MSGTYPE_GET_WORKSPACES, b'') + elif item_type == MSGTYPE_EVENT_MODE: + state_mode = json.loads(item)["change"] + print_state() + else: + print(f"Unhandled message of type 0x{item_type:x}", file=sys.stderr, flush=True) + except: + traceback.print_exc()