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