--- /dev/null
+#!/usr/bin/env python3
+import os
+import socket
+import sys
+import select
+from select import EPOLLIN, EPOLLPRI, EPOLLERR
+import time
+from collections import namedtuple
+import argparse
+
+TIMEOUT = 1.0 # seconds
+
+VERSION_HEADER = bytearray('MesaOverlayControlVersion', 'utf-8')
+DEVICE_NAME_HEADER = bytearray('DeviceName', 'utf-8')
+MESA_VERSION_HEADER = bytearray('MesaVersion', 'utf-8')
+
+DEFAULT_SERVER_ADDRESS = "\0mesa_overlay"
+
+class Connection:
+ def __init__(self, path):
+ # Create a Unix Domain socket and connect
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ try:
+ sock.connect(path)
+ except socket.error as msg:
+ print(msg)
+ sys.exit(1)
+
+ self.sock = sock
+
+ # initialize poll interface and register socket
+ epoll = select.epoll()
+ epoll.register(sock, EPOLLIN | EPOLLPRI | EPOLLERR)
+ self.epoll = epoll
+
+ def recv(self, timeout):
+ '''
+ timeout as float in seconds
+ returns:
+ - None on error or disconnection
+ - bytes() (empty) on timeout
+ '''
+
+ events = self.epoll.poll(timeout)
+ for ev in events:
+ (fd, event) = ev
+ if fd != self.sock.fileno():
+ continue
+
+ # check for socket error
+ if event & EPOLLERR:
+ return None
+
+ # EPOLLIN or EPOLLPRI, just read the message
+ msg = self.sock.recv(4096)
+
+ # socket disconnected
+ if len(msg) == 0:
+ return None
+
+ return msg
+
+ return bytes()
+
+ def send(self, msg):
+ self.sock.send(msg)
+
+class MsgParser:
+ MSGBEGIN = bytes(':', 'utf-8')[0]
+ MSGEND = bytes(';', 'utf-8')[0]
+ MSGSEP = bytes('=', 'utf-8')[0]
+
+ def __init__(self, conn):
+ self.cmdpos = 0
+ self.parampos = 0
+ self.bufferpos = 0
+ self.reading_cmd = False
+ self.reading_param = False
+ self.buffer = None
+ self.cmd = bytearray(4096)
+ self.param = bytearray(4096)
+
+ self.conn = conn
+
+ def readCmd(self, ncmds, timeout=TIMEOUT):
+ '''
+ returns:
+ - None on error or disconnection
+ - bytes() (empty) on timeout
+ '''
+
+ parsed = []
+
+ remaining = timeout
+
+ while remaining > 0 and ncmds > 0:
+ now = time.monotonic()
+
+ if self.buffer == None:
+ self.buffer = self.conn.recv(remaining)
+ self.bufferpos = 0
+
+ # disconnected or error
+ if self.buffer == None:
+ return None
+
+ for i in range(self.bufferpos, len(self.buffer)):
+ c = self.buffer[i]
+ self.bufferpos += 1
+ if c == self.MSGBEGIN:
+ self.cmdpos = 0
+ self.parampos = 0
+ self.reading_cmd = True
+ self.reading_param = False
+ elif c == self.MSGEND:
+ if not self.reading_cmd:
+ continue
+ self.reading_cmd = False
+ self.reading_param = False
+
+ cmd = self.cmd[0:self.cmdpos]
+ param = self.param[0:self.parampos]
+ self.reading_cmd = False
+ self.reading_param = False
+
+ parsed.append((cmd, param))
+ ncmds -= 1
+ if ncmds == 0:
+ break
+ elif c == self.MSGSEP:
+ if self.reading_cmd:
+ self.reading_param = True
+ else:
+ if self.reading_param:
+ self.param[self.parampos] = c
+ self.parampos += 1
+ elif self.reading_cmd:
+ self.cmd[self.cmdpos] = c
+ self.cmdpos += 1
+
+ # if we read the entire buffer and didn't finish the command,
+ # throw it away
+ self.buffer = None
+
+ # check if we have time for another iteration
+ elapsed = time.monotonic() - now
+ remaining = max(0, remaining - elapsed)
+
+ # timeout
+ return parsed
+
+def control(args):
+ if args.socket:
+ address = '\0' + args.socket
+ else:
+ address = DEFAULT_SERVER_ADDRESS
+
+ conn = Connection(address)
+ msgparser = MsgParser(conn)
+
+ version = None
+ name = None
+ mesa_version = None
+
+ msgs = msgparser.readCmd(3)
+
+ for m in msgs:
+ cmd, param = m
+ if cmd == VERSION_HEADER:
+ version = int(param)
+ elif cmd == DEVICE_NAME_HEADER:
+ name = param.decode('utf-8')
+ elif cmd == MESA_VERSION_HEADER:
+ mesa_version = param.decode('utf-8')
+
+ if version != 1 or name == None or mesa_version == None:
+ print('ERROR: invalid protocol')
+ sys.exit(1)
+
+
+ if args.info:
+ info = "Protocol Version: {}\n"
+ info += "Device Name: {}\n"
+ info += "Mesa Version: {}"
+ print(info.format(version, name, mesa_version))
+
+ if args.cmd == 'start-capture':
+ conn.send(bytearray(':capture=1;', 'utf-8'))
+ elif args.cmd == 'stop-capture':
+ conn.send(bytearray(':capture=0;', 'utf-8'))
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description='MESA_overlay control client')
+ parser.add_argument('--info', action='store_true', help='Print info from socket')
+ parser.add_argument('--socket', '-s', type=str, help='Path to socket')
+
+ commands = parser.add_subparsers(help='commands to run', dest='cmd')
+ commands.add_parser('start-capture')
+ commands.add_parser('stop-capture')
+
+ args = parser.parse_args()
+
+ control(args)