aboutsummaryrefslogtreecommitdiff
path: root/host/server.py
diff options
context:
space:
mode:
Diffstat (limited to 'host/server.py')
-rwxr-xr-xhost/server.py249
1 files changed, 137 insertions, 112 deletions
diff --git a/host/server.py b/host/server.py
index ab3f7b5..7efca5e 100755
--- a/host/server.py
+++ b/host/server.py
@@ -1,22 +1,22 @@
#!/usr/bin/env python
from socketserver import *
+import socket
+import struct
+import zlib
from time import time, strftime, sleep
-from collections import namedtuple
-from itertools import product
+from collections import namedtuple, deque
+import itertools
import threading
import random
+import os
-from ctypes import CDLL, POINTER, c_void_p, Structure, c_uint8, c_size_t, cast, addressof
-
-import numpy as np
-
-from matelight import sendframe, DISPLAY_WIDTH, DISPLAY_HEIGHT
+from ctypes import *
+from matelight import sendframe, DISPLAY_WIDTH, DISPLAY_HEIGHT, FRAME_SIZE
UDP_TIMEOUT = 3.0
-
class COLOR(Structure):
_fields_ = [('r', c_uint8), ('g', c_uint8), ('b', c_uint8), ('a', c_uint8)]
@@ -26,134 +26,159 @@ class FRAMEBUFFER(Structure):
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 render_text(text):
+def compute_text_bounds(text):
assert unifont
- fb = bdf.framebuffer_render_text(bytes(str(text), 'UTF-8'), unifont)
- fbd = fb.contents
- buf = np.ctypeslib.as_array(cast(fbd.data, POINTER(c_uint8)), shape=(fbd.h, fbd.w, 4))
- # Set data pointer to NULL before freeing framebuffer struct to prevent free_framebuffer from also freeing the data
- # buffer that is now used by numpy
- #bdf.console_render_buffer(fb)
- fbd.data = cast(c_void_p(), POINTER(COLOR))
- bdf.free_framebuffer(fb)
- return buf
+ 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):
- h,w,_ = fb.shape
- print('\033[s\033[H', end='')
- bdf.console_render_buffer(fb.ctypes.data_as(POINTER(c_uint8)), w, h)
- print('\033[0m\033[u', end='')
-
-def scroll(text):
- """ Returns whether it could scroll all the text uninterrupted """
- fb = render_text(text);
- h,w,_ = fb.shape
- for i in range(-DISPLAY_WIDTH,w+1):
- if current_entry.entrytype == 'udp' or (current_entry in defaulttexts and textqueue):
- return False
- leftpad = np.zeros((DISPLAY_HEIGHT, max(-i, 0), 4), dtype=np.uint8)
- framedata = fb[:, max(0, i):min(i+DISPLAY_WIDTH, w)]
- rightpad = np.zeros((DISPLAY_HEIGHT, min(DISPLAY_WIDTH, max(0, i+DISPLAY_WIDTH-w)), 4), dtype=np.uint8)
- dice = np.concatenate((leftpad, framedata, rightpad), 1)
- sendframe(dice)
- #printframe(dice)
- fb = render_text(text);
- return True
-
-QueueEntry = namedtuple('QueueEntry', ['entrytype', 'remote', 'timestamp', 'text'])
-defaulttexts = [QueueEntry('text', '127.0.0.1', 0, t) for t in [
- '\x1B[92mMate Light\x1B[93m@\x1B[92mPlay store or \x1B[94;101mtcp://matelight.cbrp3.c-base.org:1337\x1B[0;91m ♥',
- '\x1B[92mMate Light\x1B[0;91m ♥ \x1B[92mUnicode',
- '\x1B[92mMate Light\x1B[0m powered by \x1B[95mMicrosoft™ \x1B[96mMarquee Manager® Professional OEM',
- '\x1B[92mMate Light\x1B[0m powered by \x1B[95mData Becker™ \x1B[96mLaufschriftstudio 2000® Platinum Edition',
- '\x1B[92mMate Light\x1B[0m powered by \x1B[95mApple™ \x1B[96miScroll®',
- '\x1B[92mMate Light\x1B[0m powered by \x1B[95mSiemens™ \x1B[96mLaufschrift® v.0.1.2b fuer Intel™ Pentium®',
- ]]
-current_entry = random.choice(defaulttexts)
-conns = {}
-textqueue = []
+ 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):
- print(strftime('[%m-%d %H:%M:%S]'), *args)
+ printlock.acquire()
+ print(strftime('\x1B[93m[%m-%d %H:%M:%S]\x1B[0m'), ' '.join(str(arg) for arg in args), '\x1B[0m')
+ printlock.release()
+
+class TextRenderer:
+ def __init__(self, text):
+ self.text = text
+ self.width, _ = 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.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
+
+ 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
+ yield frame
+
+ def udp_receive(self):
+ while True:
+ try:
+ data, (addr, sport) = self.socket.recvfrom(FRAME_SIZE*3+4)
+ timestamp = time()
+ if timestamp - self.last_timestamp > UDP_TIMEOUT:
+ self.current_client = addr
+ 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)
-class MateLightUDPHandler(BaseRequestHandler):
- def handle(self):
- try:
- global current_entry, conns
- data = self.request[0].strip()
- if len(data) != FRAME_SIZE+4:
- #raise ValueError('Invalid frame size: Expected {}, got {}'.format(FRAME_SIZE+4, len(data)))
- return
- frame = data[:-4]
- #crc1, = struct.unpack('!I', data[-4:])
- #crc2, = zlib.crc32(frame, 0),
- #if crc1 != crc2:
- # raise ValueError('Invalid frame CRC checksum: Expected {}, got {}'.format(crc2, crc1))
- #socket.sendto(b'ACK', self.client_address)
- a = np.array(list(frame), dtype=np.uint8)
- timestamp = time()
- addr = self.client_address[0]
- if addr not in conns:
- current_entry = QueueEntry('udp', addr, timestamp, '')
- conns[addr] = current_entry
- log('New UDP connection from', addr)
- else:
- conns[addr].timestamp = timestamp
- if current_entry.entrytype == 'udp' and current_entry.remote == addr:
- frame = a.reshape((DISPLAY_HEIGHT, DISPLAY_WIDTH, 3))
- sendframe(frame)
- #printframe(frame)
- except Exception as e:
- print('Error receiving UDP frame:', e)
- ex_type, ex, tb = sys.exc_info()
- traceback.print_tb(tb)
+renderqueue = deque()
class MateLightTCPTextHandler(BaseRequestHandler):
def handle(self):
- global current_entry, conns
+ global render_deque
data = str(self.request.recv(1024).strip(), 'UTF-8')
addr = self.client_address[0]
- timestamp = time()
if len(data) > 140:
- self.request.sendall('TOO MUCH INFORMATION!\n')
+ self.request.sendall(b'TOO MUCH INFORMATION!\n')
return
- log('Text from {}: {}\x1B[0m'.format(addr, data))
- textqueue.append(QueueEntry('text', addr, timestamp, data))
+ 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
-server = TCPServer(('', 1337), MateLightTCPTextHandler)
-t = threading.Thread(target=server.serve_forever)
+tserver = TCPServer(('', 1337), MateLightTCPTextHandler)
+t = threading.Thread(target=tserver.serve_forever)
t.daemon = True
t.start()
-UDPServer.allow_reuse_address = True
-userver = UDPServer(('', 1337), MateLightUDPHandler)
-t = threading.Thread(target=userver.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)
if __name__ == '__main__':
- print('\n'*7)
+ print('\033[?1049h'+'\n'*9)
while True:
- if current_entry.entrytype == 'text':
- if scroll(current_entry.text):
- textqueue.remove(current_entry)
- if textqueue:
- current_entry = textqueue[0]
- else:
- if conns:
- current_entry = random.choice(conns.values())
- else:
- current_entry = random.choice(defaulttexts)
- if current_entry.entrytype != 'udp' and textqueue:
- current_entry = textqueue[0]
- if current_entry.entrytype == 'udp':
- if time() - current_entry.timestamp > UDP_TIMEOUT:
- current_entry = random.choice(defaulttexts)
+ if renderqueue:
+ renderer = renderqueue.popleft()
+ elif userver.frame_da():
+ renderer = userver
+ else:
+ static_noise = False #time() % 300 < 60
+ if static_noise:
+ foo = os.urandom(640)
+ frame = bytes([v for c in zip(list(foo), list(foo), list(foo)) for v in c ])
else:
- sleep(0.2)
+ 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)