aboutsummaryrefslogtreecommitdiff
path: root/host/server.py
diff options
context:
space:
mode:
Diffstat (limited to 'host/server.py')
-rwxr-xr-xhost/server.py219
1 files changed, 56 insertions, 163 deletions
diff --git a/host/server.py b/host/server.py
index 072b8ba..3ce1bdc 100755
--- a/host/server.py
+++ b/host/server.py
@@ -1,191 +1,84 @@
#!/usr/bin/env python
-from socketserver import *
import socket
-import struct
-import zlib
-from time import time, strftime, sleep
-from collections import namedtuple, deque
+from time import strftime
import itertools
-import threading
-import random
-import os
import sys
+from contextlib import suppress
-from ctypes import *
+from config import *
-from matelight import sendframe, DISPLAY_WIDTH, DISPLAY_HEIGHT, FRAME_SIZE
+import matelight
+import bdf
+import crap
-UDP_TIMEOUT = 3.0
-UDP_SWITCH_INTERVAL = 30.0
-
-class COLOR(Structure):
- _fields_ = [('r', c_uint8), ('g', c_uint8), ('b', c_uint8), ('a', c_uint8)]
-
-class FRAMEBUFFER(Structure):
- _fields_ = [('data', POINTER(COLOR)), ('w', c_size_t), ('h', c_size_t)]
-
-bdf = CDLL('./libbdf.so')
-bdf.read_bdf_file.restype = c_void_p
-bdf.framebuffer_render_text.restype = POINTER(FRAMEBUFFER)
-bdf.framebuffer_render_text.argtypes= [c_char_p, c_void_p, c_void_p, c_size_t, c_size_t, c_size_t]
-
-unifont = bdf.read_bdf_file('unifont.bdf')
-
-def compute_text_bounds(text):
- assert unifont
- textbytes = bytes(str(text), 'UTF-8')
- textw, texth = c_size_t(0), c_size_t(0)
- res = bdf.framebuffer_get_text_bounds(textbytes, unifont, byref(textw), byref(texth))
- if res:
- raise ValueError('Invalid text')
- return textw.value, texth.value
-
-cbuf = create_string_buffer(FRAME_SIZE*sizeof(COLOR))
-cbuflock = threading.Lock()
-def render_text(text, offset):
- global cbuf
- cbuflock.acquire()
- textbytes = bytes(str(text), 'UTF-8')
- res = bdf.framebuffer_render_text(textbytes, unifont, cbuf, DISPLAY_WIDTH, DISPLAY_HEIGHT, offset)
- if res:
- raise ValueError('Invalid text')
- cbuflock.release()
- return cbuf
-
-printlock = threading.Lock()
-
-def printframe(fb):
- printlock.acquire()
- print('\0337\033[H', end='')
- print('Rendering frame @{}'.format(time()))
- bdf.console_render_buffer(fb, DISPLAY_WIDTH, DISPLAY_HEIGHT)
- #print('\033[0m\033[KCurrently rendering', current_entry.entrytype, 'from', current_entry.remote, ':', current_entry.text, '\0338', end='')
- printlock.release()
def log(*args):
- printlock.acquire()
print(strftime('\x1B[93m[%m-%d %H:%M:%S]\x1B[0m'), ' '.join(str(arg) for arg in args), '\x1B[0m')
sys.stdout.flush()
- printlock.release()
class TextRenderer:
def __init__(self, text):
self.text = text
- self.width, _ = compute_text_bounds(text)
+ self.width, _ = unifont.compute_text_bounds(text)
def __iter__(self):
for i in range(-DISPLAY_WIDTH, self.width):
- #print('Rendering text @ pos {}'.format(i))
yield render_text(self.text, i)
-class MateLightUDPServer:
- def __init__(self, port=1337, ip=''):
- self.current_client = None
- self.last_timestamp = 0
- self.begin_timestamp = 0
- self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- self.socket.bind((ip, port))
- self.thread = threading.Thread(target = self.udp_receive)
- self.thread.daemon = True
- self.start = self.thread.start
- self.frame_condition = threading.Condition()
- self.frame = None
-
- def frame_da(self):
- return self.frame is not None
+class MatelightTCPServer:
+ def __init__(self, port, ip):
+ self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ self.sock.setblocking(blocking)
+ self.sock.bind((ip, port))
+ self.conns = set()
+ self.renderqueue = []
def __iter__(self):
- while True:
- with self.frame_condition:
- if not self.frame_condition.wait_for(self.frame_da, timeout=UDP_TIMEOUT):
- raise StopIteration()
- frame, self.frame = self.frame, None
+ q, self.renderqueue = self.renderqueue, []
+ for frame in itertools.chain(*q):
yield frame
- def udp_receive(self):
- while True:
+ def handle_connections(self):
+ for conn in self.conns:
try:
- data, (addr, sport) = self.socket.recvfrom(FRAME_SIZE*3+4)
- timestamp = time()
- if timestamp - self.last_timestamp > UDP_TIMEOUT \
- or timestamp - self.begin_timestamp > UDP_SWITCH_INTERVAL:
- self.current_client = addr
- self.begin_timestamp = timestamp
- log('\x1B[91mAccepting UDP data from\x1B[0m', addr)
- if addr == self.current_client:
- if len(data) == FRAME_SIZE*3+4:
- frame = data[:-4]
- crc1, = struct.unpack('!I', data[-4:])
- if crc1:
- crc2, = zlib.crc32(frame, 0),
- if crc1 != crc2:
- raise ValueError('Invalid frame CRC checksum: Expected {}, got {}'.format(crc2, crc1))
- elif len(data) == FRAME_SIZE*3:
- frame = data
- else:
- raise ValueError('Invalid frame size: {}'.format(len(data)))
- self.last_timestamp = timestamp
- with self.frame_condition:
- self.frame = frame
- self.frame_condition.notify()
- except Exception as e:
- log('Error receiving UDP frame:', e)
-
-renderqueue = deque()
-
-class MateLightTCPTextHandler(BaseRequestHandler):
- def handle(self):
- global render_deque
- data = str(self.request.recv(1024).strip(), 'UTF-8')
- addr = self.client_address[0]
- if len(data) > 140:
- self.request.sendall(b'TOO MUCH INFORMATION!\n')
- return
- log('\x1B[95mText from\x1B[0m {}: {}\x1B[0m'.format(addr, data))
- renderqueue.append(TextRenderer(data))
- self.request.sendall(b'KTHXBYE!\n')
-
-TCPServer.allow_reuse_address = True
-tserver = TCPServer(('', 1337), MateLightTCPTextHandler)
-t = threading.Thread(target=tserver.serve_forever)
-t.daemon = True
-t.start()
-
-userver = MateLightUDPServer()
-userver.start()
-
-defaultlines = [ TextRenderer(l[:-1].replace('\\x1B', '\x1B')) for l in open('default.lines').readlines() ]
-#random.shuffle(defaultlines)
-defaulttexts = itertools.chain(*defaultlines)
+ line = conn.recv(1024).decode('UTF-8').strip()
+ if len(data) > 140: # Unicode string length, *not* byte length of encoded UTF-8
+ conn.sendall(b'TOO MUCH INFORMATION!\n')
+ else:
+ log('\x1B[95mText from\x1B[0m {}: {}\x1B[0m'.format(addr, data))
+ renderqueue.append(TextRenderer(data))
+ conn.sendall(b'KTHXBYE!\n')
+ except socket.error, e:
+ if err == errno.EAGAIN or err == errno.EWOULDBLOCK:
+ continue
+ with suppress(socket.error):
+ conn.close()
+ self.conns.remove(conn)
+
+def _fallbackiter(it, fallback):
+ for fel in fallback:
+ for el in it:
+ yield el
+ yield fel
if __name__ == '__main__':
- print('\033[?1049h'+'\n'*9)
- while True:
- if renderqueue:
- renderer = renderqueue.popleft()
- elif userver.frame_da():
- renderer = userver
- else:
- static_noise = time() % 300 < 60
- if False:
- foo = os.urandom(640)
- frame = bytes([v for c in zip(list(foo), list(foo), list(foo)) for v in c ])
- sleep(0.05)
- else:
- try:
- frame = next(defaulttexts)
- except StopIteration:
- defaultlines = [ TextRenderer(l[:-1].replace('\\x1B', '\x1B')) for l in open('default.lines').readlines() ]
- #random.shuffle(defaultlines)
- defaulttexts = itertools.chain(*defaultlines)
- sendframe(frame)
-# printframe(frame)
- continue
-# sleep(0.1)
- for frame in renderer:
- sendframe(frame)
-# printframe(frame)
-# sleep(0.1)
-
+ tcp_server = MatelightTCPServer(config.tcp_addr, config.tcp_port)
+ udp_server = crap.CRAPServer(config.udp_addr, config.udp_port)
+ forwarder = crap.CRAPClient(config.crap_fw_addr, config.crap_fw_port) if config.crap_fw_addr is not None else None
+
+ def defaulttexts(filename='default.lines'):
+ with open(filename) as f:
+ return itertools.chain.from_iterable(( TextRenderer(l[:-1].replace('\\x1B', '\x1B')) for l in f.readlines() ))
+
+ with suppress(KeyboardInterrupt):
+ for renderer in _fallbackiter(tcp_server, defaulttexts()):
+ for frame in _fallbackiter(udp_server, renderer):
+ matelight.sendframe(frame)
+ if forwarder:
+ forwarder.sendframe(frame)
+
+ tcp_server.close()
+ udp_server.close()
+ forwarder.close()