integrate home-manager config

This commit is contained in:
jaina heartles 2024-07-21 22:36:29 -07:00
parent a470338c43
commit 826a0e5525
45 changed files with 5308 additions and 1 deletions

View file

@ -0,0 +1 @@

View file

@ -0,0 +1,30 @@
from i3sway.protocol import Connection, ConnType
class Client:
conn = None
def __init__(self):
self.conn = Connection()
def __enter__(self):
return self
def __exit__(self, *details):
self.conn.close()
def cmd_on_window(self, id, cmd):
if self.conn.type == ConnType.SWAY:
criteria = '[con_id={}]'.format(id)
else:
criteria = '[id={}]'.format(id)
return self.conn.exec('{} {}'.format(criteria, cmd))
def show_window(self, id):
self.cmd_on_window(id, "focus")
def hide_window(self, id):
self.cmd_on_window(id, "move to scratchpad")
def run(self, command):
self.conn.exec("exec {}".format(command))

View file

@ -0,0 +1,112 @@
from i3sway.protocol import Connection, MessageType
class Monitor:
tracked_classes = None
cmd_sock = None
subscription_sock = None
event_handler = None
conn_type = None
def __enter__(self):
return self
def __exit__(self, *details):
self.cmd_sock.close()
self.subscription_sock.close()
def __init__(self, event_handler, tracked_classes):
self.event_handler = event_handler
self.tracked_classes = tracked_classes
self.cmd_sock = Connection()
self.subscription_sock = Connection()
tree = self.cmd_sock.request(MessageType.GET_TREE).payload
def parse_tree(root):
if root.get('window') is not None or root.get('app_id') is not None:
# Application window
for name, classes in tracked_classes.items():
if self.container_matches_classes(root, classes):
id = str(root['id'])
self.event_handler.on_create(name, id)
if root['visible']:
self.event_handler.on_visible(name, id)
if root['type'] == 'floating_con':
self.event_handler.on_float(name, id)
elif root['type'] == 'con':
self.event_handler.on_tile(name, id)
else:
for node in root['nodes']:
parse_tree(node)
for node in root['floating_nodes']:
parse_tree(node)
parse_tree(tree)
def container_matches_classes(self, container, classes):
normalize = lambda v: v.lower() if v is not None else ''
app_id = container.get('app_id')
window_props = container.get('window_properties')
instance = window_props.get('instance') if window_props is not None else None
window_class = window_props.get('class') if window_props is not None else None
app_id = normalize(app_id)
instance = normalize(instance)
window_class = normalize(window_class)
for keyword in classes:
kw = normalize(keyword)
if kw in app_id or kw in instance or kw in window_class:
return True
return False
def listen(self):
for event in self.subscription_sock.subscribe(['window']):
self.handle_event(event)
def is_window_in_scratchpad(self, id):
def scan_tree(root, is_in_scratch):
if root['id'] == id:
return is_in_scratch
is_scratch = is_in_scratch or (
root['type'] == 'workspace' and root['name'] == '__i3_scratch'
)
for node in root['nodes'] + root['floating_nodes']:
result = scan_tree(node, is_scratch)
if result is not None:
return result
tree = self.cmd_sock.request(MessageType.GET_TREE)
return scan_tree(tree.payload, False)
def handle_event(self, event):
if event.type == MessageType.window:
return self.handle_window_event(event)
def handle_window_event(self, event):
container = event['container']
for name, classes in self.tracked_classes.items():
change = event['change']
if self.container_matches_classes(container, classes):
id = str(container['id'])
if change == 'new':
self.event_handler.on_create(name, id)
elif change == 'focus':
self.event_handler.on_visible(name, id)
elif change == 'close':
self.event_handler.on_destroy(name, id)
elif change == 'move':
if container['visible']:
self.event_handler.on_visible(name, id)
else:
self.event_handler.on_invisible(name, id)
elif change == 'floating':
if container['type'] == 'floating_con':
self.event_handler.on_float(name, id)
elif container['type'] == 'con':
self.event_handler.on_tile(name, id)

View file

@ -0,0 +1,133 @@
import json
import socket
import struct
import os
from enum import Enum, IntEnum
from typing import NamedTuple, Any
class ConnType(Enum):
SWAY = 'sway'
I3 = 'i3'
SWAYSOCK = os.getenv('SWAYSOCK')
I3SOCK = os.getenv('I3SOCK')
class MessageType(IntEnum):
# Commands / Replies
RUN_COMMAND = 0
GET_WORKSPACES = 1
SUBSCRIBE = 2
GET_OUTPUTS = 3
GET_TREE = 4
GET_MARKS = 5
GET_BAR_CONFIG = 6
GET_VERSION = 7
GET_BINDING_NODES = 8
GET_CONFIG = 9
SEND_TICK = 10
SYNC = 11
GET_BINDING_STATE = 12
GET_INPUTS = 100
GET_SEATS = 101
# Events
workspace = 0x80000000
mode = 0x80000002
window = 0x80000003
barconfig_update = 0x80000004
binding = 0x80000005
shutdown = 0x80000006
tick = 0x80000007
bar_state_update = 0x80000014
input = 0x80000015
class Message(NamedTuple):
type : MessageType
payload : Any
MAGIC = 'i3-ipc'.encode('utf-8')
HEADER_FORMAT = '=6sII'
HEADER_LEN = struct.calcsize(HEADER_FORMAT)
def accept(sock):
header = sock.recvmsg(Message.HEADER_LEN)[0]
magic, len, type = struct.unpack(Message.HEADER_FORMAT, header)
if magic != Message.MAGIC:
raise ValueError('Protocol error, expected magic value {}, got magic value {}' % (Message.MAGIC, magic))
payload_buf = sock.recvmsg(len)[0]
payload = json.loads(payload_buf)
return Message(type=type, payload=payload)
def send(self, sock):
if self.payload is None:
payload_buf = bytes([])
elif isinstance(self.payload, str):
payload_buf = self.payload.encode('utf-8')
else:
payload_buf = json.dumps(self.payload).encode('utf-8')
payload_len = len(payload_buf)
header = struct.pack(Message.HEADER_FORMAT, Message.MAGIC, payload_len, self.type)
sock.sendmsg([header, payload_buf])
def __getitem__(self, key):
return self.payload[key]
class Connection:
sock: socket.socket
type: ConnType
subscription_open: bool = False
def __init__(self, type=None, sockfile=None):
self.type = type
if sockfile is None:
if type == ConnType.SWAY or (type is None and SWAYSOCK is not None):
sockfile = SWAYSOCK
self.type = ConnType.SWAY
elif type == ConnType.I3 or (type is None and I3SOCK is not None):
sockfile = I3SOCK
self.type = ConnType.I3
else:
raise ValueError('No compatible window managers found')
self.sock = socket.socket(
socket.AF_UNIX,
socket.SOCK_STREAM,# | socket.SOCK_NONBLOCK | socket.SOCK_CLOEXEC,
)
self.sock.connect(sockfile)
def __enter__(self):
return self
def __exit__(self, *details):
self.close()
def close(self):
self.sock.close()
def subscribe(self, events: list):
if self.subscription_open:
raise ValueError('Subscription already open on socket')
reply = self.request(MessageType.SUBSCRIBE, events)
if not reply['success']:
raise ValueError('Subscription failed')
self.subscription_open = True
while True:
yield Message.accept(self.sock)
def exec(self, command: str):
return self.request(MessageType.RUN_COMMAND, command)
def request(self, type: MessageType, payload: Any = None) -> Message:
if self.subscription_open:
raise ValueError('Subscription open on socket')
message = Message(type=type, payload=payload)
message.send(self.sock)
reply = Message.accept(self.sock)
if reply.type != type:
print('Incorrect reply type', reply.type)
return reply