1bf215546Sopenharmony_ci#!/usr/bin/env python3 2bf215546Sopenharmony_ciimport os 3bf215546Sopenharmony_ciimport socket 4bf215546Sopenharmony_ciimport sys 5bf215546Sopenharmony_ciimport select 6bf215546Sopenharmony_cifrom select import EPOLLIN, EPOLLPRI, EPOLLERR 7bf215546Sopenharmony_ciimport time 8bf215546Sopenharmony_cifrom collections import namedtuple 9bf215546Sopenharmony_ciimport argparse 10bf215546Sopenharmony_ci 11bf215546Sopenharmony_ciTIMEOUT = 1.0 # seconds 12bf215546Sopenharmony_ci 13bf215546Sopenharmony_ciVERSION_HEADER = bytearray('MesaOverlayControlVersion', 'utf-8') 14bf215546Sopenharmony_ciDEVICE_NAME_HEADER = bytearray('DeviceName', 'utf-8') 15bf215546Sopenharmony_ciMESA_VERSION_HEADER = bytearray('MesaVersion', 'utf-8') 16bf215546Sopenharmony_ci 17bf215546Sopenharmony_ciDEFAULT_SERVER_ADDRESS = "\0mesa_overlay" 18bf215546Sopenharmony_ci 19bf215546Sopenharmony_ciclass Connection: 20bf215546Sopenharmony_ci def __init__(self, path): 21bf215546Sopenharmony_ci # Create a Unix Domain socket and connect 22bf215546Sopenharmony_ci sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 23bf215546Sopenharmony_ci try: 24bf215546Sopenharmony_ci sock.connect(path) 25bf215546Sopenharmony_ci except socket.error as msg: 26bf215546Sopenharmony_ci print(msg) 27bf215546Sopenharmony_ci sys.exit(1) 28bf215546Sopenharmony_ci 29bf215546Sopenharmony_ci self.sock = sock 30bf215546Sopenharmony_ci 31bf215546Sopenharmony_ci # initialize poll interface and register socket 32bf215546Sopenharmony_ci epoll = select.epoll() 33bf215546Sopenharmony_ci epoll.register(sock, EPOLLIN | EPOLLPRI | EPOLLERR) 34bf215546Sopenharmony_ci self.epoll = epoll 35bf215546Sopenharmony_ci 36bf215546Sopenharmony_ci def recv(self, timeout): 37bf215546Sopenharmony_ci ''' 38bf215546Sopenharmony_ci timeout as float in seconds 39bf215546Sopenharmony_ci returns: 40bf215546Sopenharmony_ci - None on error or disconnection 41bf215546Sopenharmony_ci - bytes() (empty) on timeout 42bf215546Sopenharmony_ci ''' 43bf215546Sopenharmony_ci 44bf215546Sopenharmony_ci events = self.epoll.poll(timeout) 45bf215546Sopenharmony_ci for ev in events: 46bf215546Sopenharmony_ci (fd, event) = ev 47bf215546Sopenharmony_ci if fd != self.sock.fileno(): 48bf215546Sopenharmony_ci continue 49bf215546Sopenharmony_ci 50bf215546Sopenharmony_ci # check for socket error 51bf215546Sopenharmony_ci if event & EPOLLERR: 52bf215546Sopenharmony_ci return None 53bf215546Sopenharmony_ci 54bf215546Sopenharmony_ci # EPOLLIN or EPOLLPRI, just read the message 55bf215546Sopenharmony_ci msg = self.sock.recv(4096) 56bf215546Sopenharmony_ci 57bf215546Sopenharmony_ci # socket disconnected 58bf215546Sopenharmony_ci if len(msg) == 0: 59bf215546Sopenharmony_ci return None 60bf215546Sopenharmony_ci 61bf215546Sopenharmony_ci return msg 62bf215546Sopenharmony_ci 63bf215546Sopenharmony_ci return bytes() 64bf215546Sopenharmony_ci 65bf215546Sopenharmony_ci def send(self, msg): 66bf215546Sopenharmony_ci self.sock.send(msg) 67bf215546Sopenharmony_ci 68bf215546Sopenharmony_ciclass MsgParser: 69bf215546Sopenharmony_ci MSGBEGIN = bytes(':', 'utf-8')[0] 70bf215546Sopenharmony_ci MSGEND = bytes(';', 'utf-8')[0] 71bf215546Sopenharmony_ci MSGSEP = bytes('=', 'utf-8')[0] 72bf215546Sopenharmony_ci 73bf215546Sopenharmony_ci def __init__(self, conn): 74bf215546Sopenharmony_ci self.cmdpos = 0 75bf215546Sopenharmony_ci self.parampos = 0 76bf215546Sopenharmony_ci self.bufferpos = 0 77bf215546Sopenharmony_ci self.reading_cmd = False 78bf215546Sopenharmony_ci self.reading_param = False 79bf215546Sopenharmony_ci self.buffer = None 80bf215546Sopenharmony_ci self.cmd = bytearray(4096) 81bf215546Sopenharmony_ci self.param = bytearray(4096) 82bf215546Sopenharmony_ci 83bf215546Sopenharmony_ci self.conn = conn 84bf215546Sopenharmony_ci 85bf215546Sopenharmony_ci def readCmd(self, ncmds, timeout=TIMEOUT): 86bf215546Sopenharmony_ci ''' 87bf215546Sopenharmony_ci returns: 88bf215546Sopenharmony_ci - None on error or disconnection 89bf215546Sopenharmony_ci - bytes() (empty) on timeout 90bf215546Sopenharmony_ci ''' 91bf215546Sopenharmony_ci 92bf215546Sopenharmony_ci parsed = [] 93bf215546Sopenharmony_ci 94bf215546Sopenharmony_ci remaining = timeout 95bf215546Sopenharmony_ci 96bf215546Sopenharmony_ci while remaining > 0 and ncmds > 0: 97bf215546Sopenharmony_ci now = time.monotonic() 98bf215546Sopenharmony_ci 99bf215546Sopenharmony_ci if self.buffer == None: 100bf215546Sopenharmony_ci self.buffer = self.conn.recv(remaining) 101bf215546Sopenharmony_ci self.bufferpos = 0 102bf215546Sopenharmony_ci 103bf215546Sopenharmony_ci # disconnected or error 104bf215546Sopenharmony_ci if self.buffer == None: 105bf215546Sopenharmony_ci return None 106bf215546Sopenharmony_ci 107bf215546Sopenharmony_ci for i in range(self.bufferpos, len(self.buffer)): 108bf215546Sopenharmony_ci c = self.buffer[i] 109bf215546Sopenharmony_ci self.bufferpos += 1 110bf215546Sopenharmony_ci if c == self.MSGBEGIN: 111bf215546Sopenharmony_ci self.cmdpos = 0 112bf215546Sopenharmony_ci self.parampos = 0 113bf215546Sopenharmony_ci self.reading_cmd = True 114bf215546Sopenharmony_ci self.reading_param = False 115bf215546Sopenharmony_ci elif c == self.MSGEND: 116bf215546Sopenharmony_ci if not self.reading_cmd: 117bf215546Sopenharmony_ci continue 118bf215546Sopenharmony_ci self.reading_cmd = False 119bf215546Sopenharmony_ci self.reading_param = False 120bf215546Sopenharmony_ci 121bf215546Sopenharmony_ci cmd = self.cmd[0:self.cmdpos] 122bf215546Sopenharmony_ci param = self.param[0:self.parampos] 123bf215546Sopenharmony_ci self.reading_cmd = False 124bf215546Sopenharmony_ci self.reading_param = False 125bf215546Sopenharmony_ci 126bf215546Sopenharmony_ci parsed.append((cmd, param)) 127bf215546Sopenharmony_ci ncmds -= 1 128bf215546Sopenharmony_ci if ncmds == 0: 129bf215546Sopenharmony_ci break 130bf215546Sopenharmony_ci elif c == self.MSGSEP: 131bf215546Sopenharmony_ci if self.reading_cmd: 132bf215546Sopenharmony_ci self.reading_param = True 133bf215546Sopenharmony_ci else: 134bf215546Sopenharmony_ci if self.reading_param: 135bf215546Sopenharmony_ci self.param[self.parampos] = c 136bf215546Sopenharmony_ci self.parampos += 1 137bf215546Sopenharmony_ci elif self.reading_cmd: 138bf215546Sopenharmony_ci self.cmd[self.cmdpos] = c 139bf215546Sopenharmony_ci self.cmdpos += 1 140bf215546Sopenharmony_ci 141bf215546Sopenharmony_ci # if we read the entire buffer and didn't finish the command, 142bf215546Sopenharmony_ci # throw it away 143bf215546Sopenharmony_ci self.buffer = None 144bf215546Sopenharmony_ci 145bf215546Sopenharmony_ci # check if we have time for another iteration 146bf215546Sopenharmony_ci elapsed = time.monotonic() - now 147bf215546Sopenharmony_ci remaining = max(0, remaining - elapsed) 148bf215546Sopenharmony_ci 149bf215546Sopenharmony_ci # timeout 150bf215546Sopenharmony_ci return parsed 151bf215546Sopenharmony_ci 152bf215546Sopenharmony_cidef control(args): 153bf215546Sopenharmony_ci if args.socket: 154bf215546Sopenharmony_ci address = '\0' + args.socket 155bf215546Sopenharmony_ci else: 156bf215546Sopenharmony_ci address = DEFAULT_SERVER_ADDRESS 157bf215546Sopenharmony_ci 158bf215546Sopenharmony_ci conn = Connection(address) 159bf215546Sopenharmony_ci msgparser = MsgParser(conn) 160bf215546Sopenharmony_ci 161bf215546Sopenharmony_ci version = None 162bf215546Sopenharmony_ci name = None 163bf215546Sopenharmony_ci mesa_version = None 164bf215546Sopenharmony_ci 165bf215546Sopenharmony_ci msgs = msgparser.readCmd(3) 166bf215546Sopenharmony_ci 167bf215546Sopenharmony_ci for m in msgs: 168bf215546Sopenharmony_ci cmd, param = m 169bf215546Sopenharmony_ci if cmd == VERSION_HEADER: 170bf215546Sopenharmony_ci version = int(param) 171bf215546Sopenharmony_ci elif cmd == DEVICE_NAME_HEADER: 172bf215546Sopenharmony_ci name = param.decode('utf-8') 173bf215546Sopenharmony_ci elif cmd == MESA_VERSION_HEADER: 174bf215546Sopenharmony_ci mesa_version = param.decode('utf-8') 175bf215546Sopenharmony_ci 176bf215546Sopenharmony_ci if version != 1 or name == None or mesa_version == None: 177bf215546Sopenharmony_ci print('ERROR: invalid protocol') 178bf215546Sopenharmony_ci sys.exit(1) 179bf215546Sopenharmony_ci 180bf215546Sopenharmony_ci 181bf215546Sopenharmony_ci if args.info: 182bf215546Sopenharmony_ci info = "Protocol Version: {}\n" 183bf215546Sopenharmony_ci info += "Device Name: {}\n" 184bf215546Sopenharmony_ci info += "Mesa Version: {}" 185bf215546Sopenharmony_ci print(info.format(version, name, mesa_version)) 186bf215546Sopenharmony_ci 187bf215546Sopenharmony_ci if args.cmd == 'start-capture': 188bf215546Sopenharmony_ci conn.send(bytearray(':capture=1;', 'utf-8')) 189bf215546Sopenharmony_ci elif args.cmd == 'stop-capture': 190bf215546Sopenharmony_ci conn.send(bytearray(':capture=0;', 'utf-8')) 191bf215546Sopenharmony_ci 192bf215546Sopenharmony_ciif __name__ == '__main__': 193bf215546Sopenharmony_ci parser = argparse.ArgumentParser(description='MESA_overlay control client') 194bf215546Sopenharmony_ci parser.add_argument('--info', action='store_true', help='Print info from socket') 195bf215546Sopenharmony_ci parser.add_argument('--socket', '-s', type=str, help='Path to socket') 196bf215546Sopenharmony_ci 197bf215546Sopenharmony_ci commands = parser.add_subparsers(help='commands to run', dest='cmd') 198bf215546Sopenharmony_ci commands.add_parser('start-capture') 199bf215546Sopenharmony_ci commands.add_parser('stop-capture') 200bf215546Sopenharmony_ci 201bf215546Sopenharmony_ci args = parser.parse_args() 202bf215546Sopenharmony_ci 203bf215546Sopenharmony_ci control(args) 204