aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjaseg <jaseg@jaseg.net>2014-01-03 00:10:20 +0100
committerjaseg <jaseg@jaseg.net>2014-01-03 00:10:20 +0100
commit916aced1bef07001ef4eee2a1cde6cd6e33b4bc3 (patch)
treea8af06f0dcc65645801a2e20db64ca877b288f98
parent34def4f660108632a9526c9ad3195fdb883d206d (diff)
downloadmatelight-916aced1bef07001ef4eee2a1cde6cd6e33b4bc3.tar.gz
matelight-916aced1bef07001ef4eee2a1cde6cd6e33b4bc3.tar.bz2
matelight-916aced1bef07001ef4eee2a1cde6cd6e33b4bc3.zip
Now with even more abstract art.
-rw-r--r--host/matelight/Makefile4
-rw-r--r--host/matelight/color.c291
-rw-r--r--host/matelight/color.h21
-rw-r--r--host/matelight/font.c5
-rw-r--r--host/matelight/font.h5
-rw-r--r--host/matelight/font.py5
-rwxr-xr-xhost/matelight/genpal.py35
-rw-r--r--host/matelight/host.py19
-rwxr-xr-xhost/matelight/listeners.py98
-rw-r--r--host/matelight/main.c256
-rwxr-xr-xhost/matelight/nyancat-test.py29
-rw-r--r--host/matelight/queuemgr.py52
-rw-r--r--host/matelight/renderers.py154
-rwxr-xr-xhost/matelight/scroll-test.py14
14 files changed, 600 insertions, 388 deletions
diff --git a/host/matelight/Makefile b/host/matelight/Makefile
index 2a7e971..1f90145 100644
--- a/host/matelight/Makefile
+++ b/host/matelight/Makefile
@@ -1,6 +1,6 @@
-all: main.c font.c font.h
- gcc -std=gnu11 -Wall -lm -o matelight -g -O0 main.c font.c
+all: main.c font.c font.h color.c color.h
+ gcc -std=gnu11 -Wall -lm -o matelight -g -O0 main.c font.c color.c
clean:
rm matelight
diff --git a/host/matelight/color.c b/host/matelight/color.c
new file mode 100644
index 0000000..938bdf1
--- /dev/null
+++ b/host/matelight/color.c
@@ -0,0 +1,291 @@
+
+#include "color.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+int xterm_color_index(color_t c){
+ int candidate = 0;
+ int best_distance = 0x7fffffff;
+ for(int i=0; i<256; i++){
+ color_t k = colortable[i];
+ int tmp = abs(c.r - k.r);
+ tmp *= tmp;
+ int distance = tmp;
+ if(distance > best_distance)
+ continue;
+ tmp = abs(c.g - k.g);
+ tmp *= tmp;
+ distance += tmp;
+ if(distance > best_distance)
+ continue;
+ tmp = abs(c.b - k.b);
+ tmp *= tmp;
+ distance += tmp;
+ if(distance > best_distance)
+ continue;
+ best_distance = distance;
+ candidate = i;
+ }
+ return candidate;
+}
+
+color_t colortable[256] = {
+ {0x00, 0x00, 0x00},
+ {0xa8, 0x00, 0x00},
+ {0x00, 0xa8, 0x00},
+ {0xa8, 0x54, 0x00},
+ {0x00, 0x00, 0xa8},
+ {0xa8, 0x00, 0xa8},
+ {0x00, 0xa8, 0xa8},
+ {0xa8, 0xa8, 0xa8},
+ {0x54, 0x54, 0x54},
+ {0xfc, 0x54, 0x54},
+ {0x54, 0xfc, 0x54},
+ {0xfc, 0xfc, 0x54},
+ {0x54, 0x54, 0xfc},
+ {0xfc, 0x54, 0xfc},
+ {0x54, 0xfc, 0xfc},
+ {0xfc, 0xfc, 0xfc},
+ {0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x5f},
+ {0x00, 0x00, 0x87},
+ {0x00, 0x00, 0xaf},
+ {0x00, 0x00, 0xd7},
+ {0x00, 0x00, 0xff},
+ {0x00, 0x5f, 0x00},
+ {0x00, 0x5f, 0x5f},
+ {0x00, 0x5f, 0x87},
+ {0x00, 0x5f, 0xaf},
+ {0x00, 0x5f, 0xd7},
+ {0x00, 0x5f, 0xff},
+ {0x00, 0x87, 0x00},
+ {0x00, 0x87, 0x5f},
+ {0x00, 0x87, 0x87},
+ {0x00, 0x87, 0xaf},
+ {0x00, 0x87, 0xd7},
+ {0x00, 0x87, 0xff},
+ {0x00, 0xaf, 0x00},
+ {0x00, 0xaf, 0x5f},
+ {0x00, 0xaf, 0x87},
+ {0x00, 0xaf, 0xaf},
+ {0x00, 0xaf, 0xd7},
+ {0x00, 0xaf, 0xff},
+ {0x00, 0xd7, 0x00},
+ {0x00, 0xd7, 0x5f},
+ {0x00, 0xd7, 0x87},
+ {0x00, 0xd7, 0xaf},
+ {0x00, 0xd7, 0xd7},
+ {0x00, 0xd7, 0xff},
+ {0x00, 0xff, 0x00},
+ {0x00, 0xff, 0x5f},
+ {0x00, 0xff, 0x87},
+ {0x00, 0xff, 0xaf},
+ {0x00, 0xff, 0xd7},
+ {0x00, 0xff, 0xff},
+ {0x5f, 0x00, 0x00},
+ {0x5f, 0x00, 0x5f},
+ {0x5f, 0x00, 0x87},
+ {0x5f, 0x00, 0xaf},
+ {0x5f, 0x00, 0xd7},
+ {0x5f, 0x00, 0xff},
+ {0x5f, 0x5f, 0x00},
+ {0x5f, 0x5f, 0x5f},
+ {0x5f, 0x5f, 0x87},
+ {0x5f, 0x5f, 0xaf},
+ {0x5f, 0x5f, 0xd7},
+ {0x5f, 0x5f, 0xff},
+ {0x5f, 0x87, 0x00},
+ {0x5f, 0x87, 0x5f},
+ {0x5f, 0x87, 0x87},
+ {0x5f, 0x87, 0xaf},
+ {0x5f, 0x87, 0xd7},
+ {0x5f, 0x87, 0xff},
+ {0x5f, 0xaf, 0x00},
+ {0x5f, 0xaf, 0x5f},
+ {0x5f, 0xaf, 0x87},
+ {0x5f, 0xaf, 0xaf},
+ {0x5f, 0xaf, 0xd7},
+ {0x5f, 0xaf, 0xff},
+ {0x5f, 0xd7, 0x00},
+ {0x5f, 0xd7, 0x5f},
+ {0x5f, 0xd7, 0x87},
+ {0x5f, 0xd7, 0xaf},
+ {0x5f, 0xd7, 0xd7},
+ {0x5f, 0xd7, 0xff},
+ {0x5f, 0xff, 0x00},
+ {0x5f, 0xff, 0x5f},
+ {0x5f, 0xff, 0x87},
+ {0x5f, 0xff, 0xaf},
+ {0x5f, 0xff, 0xd7},
+ {0x5f, 0xff, 0xff},
+ {0x87, 0x00, 0x00},
+ {0x87, 0x00, 0x5f},
+ {0x87, 0x00, 0x87},
+ {0x87, 0x00, 0xaf},
+ {0x87, 0x00, 0xd7},
+ {0x87, 0x00, 0xff},
+ {0x87, 0x5f, 0x00},
+ {0x87, 0x5f, 0x5f},
+ {0x87, 0x5f, 0x87},
+ {0x87, 0x5f, 0xaf},
+ {0x87, 0x5f, 0xd7},
+ {0x87, 0x5f, 0xff},
+ {0x87, 0x87, 0x00},
+ {0x87, 0x87, 0x5f},
+ {0x87, 0x87, 0x87},
+ {0x87, 0x87, 0xaf},
+ {0x87, 0x87, 0xd7},
+ {0x87, 0x87, 0xff},
+ {0x87, 0xaf, 0x00},
+ {0x87, 0xaf, 0x5f},
+ {0x87, 0xaf, 0x87},
+ {0x87, 0xaf, 0xaf},
+ {0x87, 0xaf, 0xd7},
+ {0x87, 0xaf, 0xff},
+ {0x87, 0xd7, 0x00},
+ {0x87, 0xd7, 0x5f},
+ {0x87, 0xd7, 0x87},
+ {0x87, 0xd7, 0xaf},
+ {0x87, 0xd7, 0xd7},
+ {0x87, 0xd7, 0xff},
+ {0x87, 0xff, 0x00},
+ {0x87, 0xff, 0x5f},
+ {0x87, 0xff, 0x87},
+ {0x87, 0xff, 0xaf},
+ {0x87, 0xff, 0xd7},
+ {0x87, 0xff, 0xff},
+ {0xaf, 0x00, 0x00},
+ {0xaf, 0x00, 0x5f},
+ {0xaf, 0x00, 0x87},
+ {0xaf, 0x00, 0xaf},
+ {0xaf, 0x00, 0xd7},
+ {0xaf, 0x00, 0xff},
+ {0xaf, 0x5f, 0x00},
+ {0xaf, 0x5f, 0x5f},
+ {0xaf, 0x5f, 0x87},
+ {0xaf, 0x5f, 0xaf},
+ {0xaf, 0x5f, 0xd7},
+ {0xaf, 0x5f, 0xff},
+ {0xaf, 0x87, 0x00},
+ {0xaf, 0x87, 0x5f},
+ {0xaf, 0x87, 0x87},
+ {0xaf, 0x87, 0xaf},
+ {0xaf, 0x87, 0xd7},
+ {0xaf, 0x87, 0xff},
+ {0xaf, 0xaf, 0x00},
+ {0xaf, 0xaf, 0x5f},
+ {0xaf, 0xaf, 0x87},
+ {0xaf, 0xaf, 0xaf},
+ {0xaf, 0xaf, 0xd7},
+ {0xaf, 0xaf, 0xff},
+ {0xaf, 0xd7, 0x00},
+ {0xaf, 0xd7, 0x5f},
+ {0xaf, 0xd7, 0x87},
+ {0xaf, 0xd7, 0xaf},
+ {0xaf, 0xd7, 0xd7},
+ {0xaf, 0xd7, 0xff},
+ {0xaf, 0xff, 0x00},
+ {0xaf, 0xff, 0x5f},
+ {0xaf, 0xff, 0x87},
+ {0xaf, 0xff, 0xaf},
+ {0xaf, 0xff, 0xd7},
+ {0xaf, 0xff, 0xff},
+ {0xd7, 0x00, 0x00},
+ {0xd7, 0x00, 0x5f},
+ {0xd7, 0x00, 0x87},
+ {0xd7, 0x00, 0xaf},
+ {0xd7, 0x00, 0xd7},
+ {0xd7, 0x00, 0xff},
+ {0xd7, 0x5f, 0x00},
+ {0xd7, 0x5f, 0x5f},
+ {0xd7, 0x5f, 0x87},
+ {0xd7, 0x5f, 0xaf},
+ {0xd7, 0x5f, 0xd7},
+ {0xd7, 0x5f, 0xff},
+ {0xd7, 0x87, 0x00},
+ {0xd7, 0x87, 0x5f},
+ {0xd7, 0x87, 0x87},
+ {0xd7, 0x87, 0xaf},
+ {0xd7, 0x87, 0xd7},
+ {0xd7, 0x87, 0xff},
+ {0xd7, 0xaf, 0x00},
+ {0xd7, 0xaf, 0x5f},
+ {0xd7, 0xaf, 0x87},
+ {0xd7, 0xaf, 0xaf},
+ {0xd7, 0xaf, 0xd7},
+ {0xd7, 0xaf, 0xff},
+ {0xd7, 0xd7, 0x00},
+ {0xd7, 0xd7, 0x5f},
+ {0xd7, 0xd7, 0x87},
+ {0xd7, 0xd7, 0xaf},
+ {0xd7, 0xd7, 0xd7},
+ {0xd7, 0xd7, 0xff},
+ {0xd7, 0xff, 0x00},
+ {0xd7, 0xff, 0x5f},
+ {0xd7, 0xff, 0x87},
+ {0xd7, 0xff, 0xaf},
+ {0xd7, 0xff, 0xd7},
+ {0xd7, 0xff, 0xff},
+ {0xff, 0x00, 0x00},
+ {0xff, 0x00, 0x5f},
+ {0xff, 0x00, 0x87},
+ {0xff, 0x00, 0xaf},
+ {0xff, 0x00, 0xd7},
+ {0xff, 0x00, 0xff},
+ {0xff, 0x5f, 0x00},
+ {0xff, 0x5f, 0x5f},
+ {0xff, 0x5f, 0x87},
+ {0xff, 0x5f, 0xaf},
+ {0xff, 0x5f, 0xd7},
+ {0xff, 0x5f, 0xff},
+ {0xff, 0x87, 0x00},
+ {0xff, 0x87, 0x5f},
+ {0xff, 0x87, 0x87},
+ {0xff, 0x87, 0xaf},
+ {0xff, 0x87, 0xd7},
+ {0xff, 0x87, 0xff},
+ {0xff, 0xaf, 0x00},
+ {0xff, 0xaf, 0x5f},
+ {0xff, 0xaf, 0x87},
+ {0xff, 0xaf, 0xaf},
+ {0xff, 0xaf, 0xd7},
+ {0xff, 0xaf, 0xff},
+ {0xff, 0xd7, 0x00},
+ {0xff, 0xd7, 0x5f},
+ {0xff, 0xd7, 0x87},
+ {0xff, 0xd7, 0xaf},
+ {0xff, 0xd7, 0xd7},
+ {0xff, 0xd7, 0xff},
+ {0xff, 0xff, 0x00},
+ {0xff, 0xff, 0x5f},
+ {0xff, 0xff, 0x87},
+ {0xff, 0xff, 0xaf},
+ {0xff, 0xff, 0xd7},
+ {0xff, 0xff, 0xff},
+ {0x00, 0x00, 0x00},
+ {0x12, 0x12, 0x12},
+ {0x1c, 0x1c, 0x1c},
+ {0x26, 0x26, 0x26},
+ {0x30, 0x30, 0x30},
+ {0x3a, 0x3a, 0x3a},
+ {0x44, 0x44, 0x44},
+ {0x4e, 0x4e, 0x4e},
+ {0x58, 0x58, 0x58},
+ {0x62, 0x62, 0x62},
+ {0x6c, 0x6c, 0x6c},
+ {0x76, 0x76, 0x76},
+ {0x80, 0x80, 0x80},
+ {0x8a, 0x8a, 0x8a},
+ {0x94, 0x94, 0x94},
+ {0x9e, 0x9e, 0x9e},
+ {0xa8, 0xa8, 0xa8},
+ {0xb2, 0xb2, 0xb2},
+ {0xbc, 0xbc, 0xbc},
+ {0xc6, 0xc6, 0xc6},
+ {0xd0, 0xd0, 0xd0},
+ {0xda, 0xda, 0xda},
+ {0xe4, 0xe4, 0xe4},
+ {0xee, 0xee, 0xee}
+};
+
+
diff --git a/host/matelight/color.h b/host/matelight/color.h
new file mode 100644
index 0000000..48f325b
--- /dev/null
+++ b/host/matelight/color.h
@@ -0,0 +1,21 @@
+#ifndef __COLOR_H__
+#define __COLOR_H__
+
+#include <stdint.h>
+
+typedef struct {
+ uint8_t r;
+ uint8_t g;
+ uint8_t b;
+} color_t;
+
+int xterm_color_index(color_t c);
+
+// gray
+#define DEFAULT_FG_COLOR 7
+// black
+#define DEFAULT_BG_COLOR 0
+
+extern color_t colortable[256];
+
+#endif//__COLOR_H__
diff --git a/host/matelight/font.c b/host/matelight/font.c
index 53d27b9..f22f05b 100644
--- a/host/matelight/font.c
+++ b/host/matelight/font.c
@@ -5,7 +5,7 @@
#include <stdlib.h>
#include <string.h>
-void render_glyph(glyph_t *g, uint8_t *buf, unsigned int bufwidth, unsigned int offx, unsigned int offy){
+void render_glyph(glyph_t *g, uint8_t *buf, unsigned int bufwidth, unsigned int offx, unsigned int offy, color_t fg, color_t bg){
unsigned int bitmap_row_width = g->width/8;
uint8_t *bitmap = ((uint8_t *)g) + sizeof(glyph_t);
for(unsigned int y=0; y < g->height; y++){
@@ -16,7 +16,8 @@ void render_glyph(glyph_t *g, uint8_t *buf, unsigned int bufwidth, unsigned int
}
uint8_t *p = buf + (offy+y)*bufwidth + offx;
for(unsigned int x=0; x < g->width; x++){
- *p++ = (data&(1<<(g->width-1))) ? 1 : 0;
+ color_t c = (data&(1<<(g->width-1))) ? fg : bg;
+ *((color_t *)(p++)) = c;
data <<= 1;
}
}
diff --git a/host/matelight/font.h b/host/matelight/font.h
index e3e2e60..d21100c 100644
--- a/host/matelight/font.h
+++ b/host/matelight/font.h
@@ -4,6 +4,7 @@
#include <stdint.h>
#include <stdio.h>
+#include "color.h"
// CAUTION: A glyph struct is always followed by the glyph's bitmap.
typedef struct {
@@ -16,10 +17,12 @@ typedef struct {
// Size of Unicode's basic multilingual plane
#define BLP_SIZE 65536
+#define MAX_CSI_ELEMENTS 8
+
// We could also use some fancy hashtable here, but unifont includes about 57k glyphs so we would hardly save any memory.
int read_bdf(FILE *f, glyph_t **glyph_table, unsigned int glyph_table_size);
// Requires buf to point to a buffer at least of size glyph->width*glyph->height.
-void render_glyph(glyph_t *glyph, uint8_t *buf, unsigned int bufwidth, unsigned int offx, unsigned int offy);
+void render_glyph(glyph_t *glyph, uint8_t *buf, unsigned int bufwidth, unsigned int offx, unsigned int offy, color_t fg, color_t bg);
#endif//__FONT_H__
diff --git a/host/matelight/font.py b/host/matelight/font.py
deleted file mode 100644
index a1c1dd6..0000000
--- a/host/matelight/font.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from bdflib import reader as bdfreader # Used to read the bitmap font
-
-FONT = bdfreader.read_bdf(iter(open('fonts/unifont-6.3.20131020.bdf').readlines()))
-FONT_HEIGHT = 16
-
diff --git a/host/matelight/genpal.py b/host/matelight/genpal.py
new file mode 100755
index 0000000..01fe611
--- /dev/null
+++ b/host/matelight/genpal.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python3
+
+xterm_colors = [
+ (0x00, 0x00, 0x00),
+ (0xa8, 0x00, 0x00),
+ (0x00, 0xa8, 0x00),
+ (0xa8, 0x54, 0x00),
+ (0x00, 0x00, 0xa8),
+ (0xa8, 0x00, 0xa8),
+ (0x00, 0xa8, 0xa8),
+ (0xa8, 0xa8, 0xa8),
+ (0x54, 0x54, 0x54),
+ (0xfc, 0x54, 0x54),
+ (0x54, 0xfc, 0x54),
+ (0xfc, 0xfc, 0x54),
+ (0x54, 0x54, 0xfc),
+ (0xfc, 0x54, 0xfc),
+ (0x54, 0xfc, 0xfc),
+ (0xfc, 0xfc, 0xfc)]
+
+# colors 16..232: the 6x6x6 color cube
+_valuerange = (0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff)
+for i in range(217):
+ r = _valuerange[(i // 36) % 6]
+ g = _valuerange[(i // 6) % 6]
+ b = _valuerange[i % 6]
+ xterm_colors.append((r, g, b))
+
+# colors 233..253: grayscale
+for i in range(1, 24):
+ v = 8 + i * 10
+ xterm_colors.append((v, v, v))
+
+for r,g,b in xterm_colors:
+ print("\t{{{:#04x}, {:#04x}, {:#04x}}},".format(r,g,b))
diff --git a/host/matelight/host.py b/host/matelight/host.py
deleted file mode 100644
index 4fa8578..0000000
--- a/host/matelight/host.py
+++ /dev/null
@@ -1,19 +0,0 @@
-import usb
-import colorsys
-import numpy as np
-from config import *
-import itertools
-
-dev = usb.core.find(idVendor=0x1cbe, idProduct=0x0003)
-
-def sendframe(framedata):
- # not isinstance(framedata, np.array) or
- if framedata.shape != (DISPLAY_HEIGHT, DISPLAY_WIDTH, 3) or framedata.dtype != np.uint8:
- raise ValueError('framedata must be a ({}, {}, 3)-numpy array of uint8s. Got a {}-numpy array of {}'.format(DISPLAY_HEIGHT, DISPLAY_WIDTH, framedata.shape, framedata.dtype))
-
- for cy, cx in itertools.product(range(CRATES_Y), range(CRATES_X)):
- cratedata = framedata[cy*CRATE_HEIGHT:(cy+1)*CRATE_HEIGHT, cx*CRATE_WIDTH:(cx+1)*CRATE_WIDTH]
- # Send framebuffer data
- dev.write(0x01, bytes([0, cx, cy])+bytes(list(cratedata.flatten())))
- # Send latch command
- dev.write(0x01, b'\x01')
diff --git a/host/matelight/listeners.py b/host/matelight/listeners.py
deleted file mode 100755
index f390389..0000000
--- a/host/matelight/listeners.py
+++ /dev/null
@@ -1,98 +0,0 @@
-#!/usr/bin/env python3
-from socketserver import *
-import socket
-import threading
-import zlib
-import random
-import struct
-import host
-import numpy as np
-import time
-import sys
-import traceback
-import renderers
-from PIL import Image, ImageSequence
-from config import *
-# Loading frame (for the big font file)
-img = Image.open(open('../nyancat.png', 'rb'))
-frame = np.array(img.convert('RGB').getdata(), dtype=np.uint8).reshape((DISPLAY_HEIGHT, DISPLAY_WIDTH, 3))
-host.sendframe(frame)
-from font import *
-
-UDP_THRES = 1.0
-
-class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass
-class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
-
-default_renderers = [renderers.TextRenderer('\x1B[92mMate Light\x1B[93m@\x1B[92mPlay store or \x1B[94;101mtcp://ml.jaseg.net:1337\x1B[0;91m ♥ '),
- renderers.TextRenderer('\x1B[92mMate Light\x1B[0;91m ♥ \x1B[92mUnicode'),
- renderers.TextRenderer('\x1B[92mMate Light\x1B[0m powered by \x1B[95mMicrosoft™ \x1B[96mMarquee Manager® Pro')]
-global renderer, count
-renderer = default_renderers[0]
-count = 0
-lastudp = 0
-
-class MateLightUDPHandler(BaseRequestHandler):
- def handle(self):
- try:
- global lastudp
- 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)
- lastudp = time.time()
- host.sendframe(a.reshape((DISPLAY_HEIGHT, DISPLAY_WIDTH, 3)))
- except Exception as e:
- print('Error receiving UDP frame:', e)
- ex_type, ex, tb = sys.exc_info()
- traceback.print_tb(tb)
-
-class MateLightTCPTextHandler(BaseRequestHandler):
- def handle(self):
- try:
- data = str(self.request.recv(1024).strip(), 'UTF-8')
- if len(data) > 140:
- self.request.sendall('TOO MUCH INFORMATION!\n')
- return
- global renderer, count
- print(data+'\x1B[0m')
- renderer = renderers.TextRenderer(data)
- count = 3
- self.request.sendall(b'KTHXBYE!\n')
- except:
- pass
-
-TCPServer.allow_reuse_address = True
-server = TCPServer(('', 1337), MateLightTCPTextHandler)
-t = threading.Thread(target=server.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()
-
-while True:
- global renderer, count, lastudp
- foo = renderer
- if count == 0:
- renderer = random.choice(default_renderers)
- else:
- count = count - 1
- for frame, delay in foo.frames():
- #print(list(frame.flatten()))
- now = time.time()
- if now-lastudp > UDP_THRES:
- host.sendframe(np.swapaxes(frame, 0, 1))
- else:
- time.sleep(0.1)
-
diff --git a/host/matelight/main.c b/host/matelight/main.c
index 7a23a0a..48c50d9 100644
--- a/host/matelight/main.c
+++ b/host/matelight/main.c
@@ -1,5 +1,6 @@
#include "font.h"
+#include "color.h"
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
@@ -7,6 +8,7 @@
#include <string.h>
#include <wchar.h>
#include <locale.h>
+#include <sys/timeb.h>
/* CAUTION: REQUIRES INPUT TO BE \0-TERMINATED */
int console_render(char *s, glyph_t **glyph_table, unsigned int glyph_table_size){
@@ -53,7 +55,8 @@ int console_render(char *s, glyph_t **glyph_table, unsigned int glyph_table_size
// For easier rendering on the terminal, round up to multiples of two
gbufheight += gbufheight&1;
- unsigned int gbufsize = gbufwidth*gbufheight;
+ // gbuf uses 3 bytes per color for r, g and b
+ unsigned int gbufsize = gbufwidth*3*gbufheight;
gbuf = malloc(gbufsize);
if(gbuf == 0){
fprintf(stderr, "Cannot malloc() %d bytes.\n", gbufsize);
@@ -64,31 +67,260 @@ int console_render(char *s, glyph_t **glyph_table, unsigned int glyph_table_size
unsigned int x = 0;
p = s;
memset(&ps, 0, sizeof(mbstate_t));
+ struct {
+ color_t fg;
+ color_t bg;
+ int blink:4;
+ int bold:1; // TODO
+ int underline:1;
+ int strikethrough:1;
+ int fraktur:1; // TODO See: Flat10 Fraktur font
+ int invert:1;
+ } style = {
+ colortable[DEFAULT_FG_COLOR], colortable[DEFAULT_BG_COLOR], 0, 0, 0, 0, 0, 0
+ };
for(;;){
+ // NOTE: This nested escape sequence parsing does not contain any unicode-awareness whatsoever
+ if(*p == '\033'){ // Escape sequence YAY
+ char *sequence_start = p++;
+ if(*p == '['){ // This was a CSI!
+ long elems[MAX_CSI_ELEMENTS];
+ int nelems;
+ for(nelems = 0; nelems<MAX_CSI_ELEMENTS; nelems++){
+ p++;
+ char *endptr;
+ elems[nelems] = strtol(p, &endptr, 10);
+ if(p == endptr){
+ fprintf(stderr, "Invalid ANSI escape code: \"\\e%s\"\n", sequence_start);
+ goto error;
+ }
+ if(*endptr == 'm')
+ break;
+ if(*endptr != ';'){
+ fprintf(stderr, "Invalid ANSI escape code: \"\\e%s\"\n", sequence_start);
+ goto error;
+ }
+ }
+ // By now we know it's a SGR since we error'ed out on anything else
+ if(nelems < 1){
+ fprintf(stderr, "Unsupported escape sequence: \"\\e%s\"\n", sequence_start);
+ goto error;
+ }
+ for(int i=0; i<nelems; i++){
+ switch(elems[i]){
+ case 0: // reset style
+ style.fg = colortable[DEFAULT_FG_COLOR];
+ style.bg = colortable[DEFAULT_BG_COLOR];
+ style.bold = 0;
+ style.underline = 0;
+ style.blink = 0;
+ style.strikethrough = 0;
+ style.fraktur = 0;
+ style.invert = 0;
+ break;
+ case 1: // bold
+ style.bold = 1;
+ break;
+ case 4: // underline
+ style.underline = 1;
+ break;
+ case 5: // slow blink
+ style.blink = 1;
+ break;
+ case 6: // rapid blink
+ style.blink = 8;
+ break;
+ case 7: // color invert on
+ style.invert = 1;
+ break;
+ case 9: // strike-through
+ style.strikethrough = 1;
+ break;
+ case 20:// Fraktur
+ style.fraktur = 1;
+ break;
+ case 22:// Bold off
+ style.bold = 0;
+ break;
+ case 24:// Underline off
+ style.underline = 0;
+ break;
+ case 25:// Blink off
+ style.blink = 0;
+ break;
+ case 27:// color invert off
+ style.invert = 0;
+ break;
+ case 29:// strike-through off
+ style.strikethrough = 0;
+ break;
+ case 30: // Set foreground color, "dim" colors
+ case 31:
+ case 32:
+ case 33:
+ case 34:
+ case 35:
+ case 36:
+ case 37:
+ style.fg = colortable[elems[i]-30];
+ break;
+ case 38: // Set xterm-256 foreground color
+ i++;
+ if(nelems-i < 2 || elems[i] != 5){
+ fprintf(stderr, "Invalid ANSI escape code: \"\\e%s\"\n", sequence_start);
+ goto error;
+ }
+ style.fg = colortable[elems[i++]];
+ break;
+ case 39: // Reset foreground color to default
+ style.bg = colortable[DEFAULT_FG_COLOR];
+ break;
+ case 40: // Set background color, "dim" colors
+ case 41:
+ case 42:
+ case 43:
+ case 44:
+ case 45:
+ case 46:
+ case 47:
+ style.bg = colortable[elems[i]-40];
+ break;
+ case 48: // Set xterm-256 background color
+ i++;
+ if(nelems-i < 2 || elems[i] != 5){
+ fprintf(stderr, "Invalid ANSI escape code: \"\\e%s\"\n", sequence_start);
+ goto error;
+ }
+ style.bg = colortable[elems[i++]];
+ break;
+ case 49: // Reset background color to default
+ style.bg = colortable[DEFAULT_BG_COLOR];
+ break;
+ case 90: // Set foreground color, "bright" colors
+ case 91:
+ case 92:
+ case 93:
+ case 94:
+ case 95:
+ case 96:
+ case 97:
+ style.fg = colortable[elems[i]-90+8];
+ break;
+ case 100: // Set background color, "bright" colors
+ case 101:
+ case 102:
+ case 103:
+ case 104:
+ case 105:
+ case 106:
+ case 107:
+ style.bg = colortable[elems[i]-100+8];
+ break;
+ default:
+ fprintf(stderr, "Unsupported escape sequence: \"\\e%s\"\n", sequence_start);
+ goto error;
+ }
+ }
+
+ }else{
+ fprintf(stderr, "Unsupported escape sequence: \"\\e%s\"\n", sequence_start);
+ goto error;
+ }
+
+ continue;
+ }
+
size_t inc = mbrtowc(&c, p, (s+len+1)-p, NULL);
// If p contained
if(inc == 0) // Reached end of string
break;
p += inc;
+ struct timeb time = {0};
+ ftime(&time);
+ unsigned long int t = time.time*1000 + time.millitm;
+ int blink = style.blink && (t % (1000/style.blink) < (333/style.blink));
+ int inv = style.invert ^ blink;
+ color_t fg = inv ? style.fg : style.bg;
+ color_t bg = inv ? style.bg : style.fg;
+
+ printf("Rendering glyph %lc: Strikethrough %d Underline %d Blink %d Inverted %d Bold %d Fraktur %d FG (%d, %d, %d) BG (%d, %d, %d)\n",
+ c,
+ style.strikethrough,
+ style.underline,
+ style.blink,
+ style.invert,
+ style.bold,
+ style.fraktur,
+ style.fg.r,
+ style.fg.g,
+ style.fg.b,
+ style.bg.r,
+ style.bg.g,
+ style.bg.b);
glyph_t *g = glyph_table[c];
- render_glyph(g, gbuf, gbufwidth, x, 0);
+ render_glyph(g, gbuf, gbufwidth, x, 0, fg, bg);
+ if(style.strikethrough || style.underline){
+ int sty = gbufheight/2;
+ // g->y usually is a negative index of the glyph's baseline measured from the glyph's bottom
+ int uly = gbufheight + g->y;
+ for(int i=0; i<g->width; i++){
+ if(style.strikethrough)
+ *((color_t *)(gbuf + (sty*gbufwidth + i)*3)) = fg;
+ if(style.underline)
+ *((color_t *)(gbuf + (uly*gbufwidth + i)*3)) = fg;
+ }
+ }
x += g->width;
}
+ for(unsigned int y=0; y < gbufheight; y++){
+ for(unsigned int x=0; x < gbufwidth; x++){
+ color_t c = *((color_t *)(gbuf + (y*gbufwidth + x)*3));
+ //printf("\033[0m%02x,%02x,%02x-%02x\033[38;5;%dm█", c.r, c.g, c.b, xterm_color_index(c), xterm_color_index(c));
+ printf("\033[38;5;%dm█", xterm_color_index(c));
+ }
+ printf("\n");
+ }
+ return 0;
+ color_t lastfg = {0, 0, 0}, lastbg = {0, 0, 0};
+ printf("\e[38;5;0;48;5;0m");
for(unsigned int y=0; y < gbufheight; y+=2){
for(unsigned int x=0; x < gbufwidth; x++){
//Da magicks: ▀█▄
- char c1 = gbuf[y*gbufwidth + x];
- char c2 = gbuf[(y+1)*gbufwidth + x];
- if(c1 && c2)
- printf("█");
- else if(c1 && !c2)
- printf("▀");
- else if(!c1 && c2)
- printf("▄");
- else
- printf(" ");
+ color_t ct = *((color_t *)(gbuf + (y*gbufwidth + x)*3)); // Top pixel
+ color_t cb = *((color_t *)(gbuf + ((y+1)*gbufwidth + x)*3)); // Bottom pixel
+ if(!memcmp(&ct, &lastfg, sizeof(color_t))){
+ if(!memcmp(&cb, &lastbg, sizeof(color_t))){
+ printf("▀");
+ }else if(!memcmp(&cb, &lastfg, sizeof(color_t))){
+ printf("█");
+ }else{
+ printf("\033[48;5;%dm▀", xterm_color_index(cb));
+ lastbg = cb;
+ }
+ }else if(!memcmp(&ct, &lastbg, sizeof(color_t))){
+ if(!memcmp(&cb, &lastfg, sizeof(color_t))){
+ printf("▄");
+ }else if(!memcmp(&cb, &lastbg, sizeof(color_t))){
+ printf(" ");
+ }else{
+ printf("\033[38;5;%dm▄", xterm_color_index(cb));
+ lastfg = cb;
+ }
+ }else{ // No matches for the upper pixel
+ if(!memcmp(&cb, &lastfg, sizeof(color_t))){
+ printf("\033[48;5;%dm▄", xterm_color_index(ct));
+ lastbg = ct;
+ }else if(!memcmp(&cb, &lastbg, sizeof(color_t))){
+ printf("\033[38;5;%dm▀", xterm_color_index(ct));
+ lastfg = ct;
+ }else{
+ printf("\033[38;5;%d;48;5;%dm▀", xterm_color_index(ct), xterm_color_index(cb));
+ lastfg = ct;
+ lastbg = cb;
+ }
+ }
}
printf("\n");
}
diff --git a/host/matelight/nyancat-test.py b/host/matelight/nyancat-test.py
deleted file mode 100755
index 73fd28e..0000000
--- a/host/matelight/nyancat-test.py
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/usr/bin/env python3
-import host
-import numpy as np
-from config import *
-from PIL import Image, ImageSequence
-import time
-
-img1 = Image.open(open('../nyancat.png', 'rb'))
-img2 = Image.open(open('../nyancat2.png', 'rb'))
-scroller = Image.open(open('../scroller.png', 'rb'))
-datas = []
-for img in [img1, img2]:
- im = img.convert("RGB")
- im.thumbnail((DISPLAY_WIDTH, DISPLAY_HEIGHT), Image.NEAREST)
- data = np.array(im.getdata(), dtype=np.uint8)
- datas += [data.reshape((DISPLAY_HEIGHT, DISPLAY_WIDTH, 3))]
-
-im = scroller.convert("RGB")
-bar = np.array(im.getdata(), dtype=np.uint8)
-foo = bar.reshape((DISPLAY_HEIGHT, 300, 3))
-
-while True:
- for i in range(60):
- for data in datas:
- host.sendframe(data)
- time.sleep(0.1)
- for i in range(260):
- host.sendframe(foo[:, i:i+40, :])
-
diff --git a/host/matelight/queuemgr.py b/host/matelight/queuemgr.py
deleted file mode 100644
index df75309..0000000
--- a/host/matelight/queuemgr.py
+++ /dev/null
@@ -1,52 +0,0 @@
-#!/usr/bin/env python3
-
-from renderers import TextRenderer, ImageRenderer
-import host, config
-import time, math
-
-score = lambda now, last, lifetime, priority, item: priority*math.log(now-last)/(10.0+item.duration)
-
-class FuzzyQueue:
- def __init__(self, default):
- self._default = default
- self.put(default, 0.0, 0)
- self._l = []
-
- def put(self, item, priority=1.0, lifetime=0):
- lifetime += time.time()
- self._l.append((0, lifetime, priority, item))
-
- def pop(self):
- """ Get an item from the queue
-
- NOTE: This is *not* a regular pop, as it does not necessarily remove the item from the queue.
- """
- now = time.time()
- # Choose item based on last used and priority
- _, index, (_, lifetime, priority, item) = max(sorted([(score(now, *v), i, v) for i, v in self._l]))
- # If item's lifetime is exceeded, remove
- if lifetime < now and item is not self._default:
- del self._l[index]
- # Otherwise, set item's last played time
- self._l[index] = (now, lifetime, prioity, item)
- # Finally, return
- return item
-
-q = FuzzyQueue()
-
-def insert_text(text, priority=1.0, lifetime=0, escapes=True):
- q.put(TextRenderer(text, escapes), priority, lifetime)
-
-def insert_image(image, priority=1.0, lifetime=0):
- q.put(ImageRenderer(image), priority, lifetime)
-
-def render_thread():
- while True:
- start = time.time()
- for frame, delay in q.pop().frames(start):
- then = time.time()
- if then-start+delay > RENDERER_TIMEOUT:
- break
- sendframe(frame)
- now = time.time()
- time.sleep(min(RENDERER_TIMEOUT, delay - (now-then)))
diff --git a/host/matelight/renderers.py b/host/matelight/renderers.py
deleted file mode 100644
index f507513..0000000
--- a/host/matelight/renderers.py
+++ /dev/null
@@ -1,154 +0,0 @@
-import time
-try:
- import re2 as re
-except ImportError:
- import re
-import numpy as np
-from PIL import Image
-from pixelterm import xtermcolors
-from config import *
-from font import *
-
-default_palette = [
- (0x00, 0x00, 0x00), # 0 normal colors
- (0xcd, 0x00, 0x00), # 1
- (0x00, 0xcd, 0x00), # 2
- (0xcd, 0xcd, 0x00), # 3
- (0x00, 0x00, 0xee), # 4
- (0xcd, 0x00, 0xcd), # 5
- (0x00, 0xcd, 0xcd), # 6
- (0xe5, 0xe5, 0xe5), # 7
- (0x7f, 0x7f, 0x7f), # 8 bright colors
- (0xff, 0x00, 0x00), # 9
- (0x00, 0xff, 0x00), # 10
- (0xff, 0xff, 0x00), # 11
- (0x5c, 0x5c, 0xff), # 12
- (0xff, 0x00, 0xff), # 13
- (0x00, 0xff, 0xff), # 14
- (0xff, 0xff, 0xff)] # 15
-default_colors = (default_palette[8], default_palette[0])
-
-class CharGenerator:
- def __init__(self, seq=None, lg=None, text=''):
- settings = False, False, False, default_colors
- if lg:
- settings = lg.bold, lg.blink, lg.underscore, (lg.fg, lg.bg)
- self.bold, self.blink, self.underscore, (self.fg, self.bg) = settings
- self.text = text
- if seq:
- self.parse_escape_sequence(seq)
-
- def parse_escape_sequence(self, seq):
- codes = list(map(int, seq[2:-1].split(';')))
- fg, bg, reverse, i = self.fg, self.bg, False, 0
- while i<len(codes):
- a = codes[i]
- if a in [38, 48]:
- if codes[i+1] == 5:
- c = xtermcolors.xterm_colors[codes[i+2]]
- fg, bg = (c, bg) if a == 38 else (fg, c)
- i += 2
- elif a == 39:
- fg = (0,0,0)
- elif a == 49:
- bg = (0,0,0)
- elif a == 0:
- fg, bg = default_colors
- self.bold, self.blink, self.underscore = False, False, False
- elif a in range(30, 38):
- fg = default_palette[a-30]
- elif a in range(90, 98):
- fg = default_palette[a-90+8]
- elif a in range(40, 48):
- bg = default_palette[a-40]
- elif a in range(101, 108):
- bg = default_palette[a-100+8]
- elif a == 7:
- reverse = True
- elif a == 5:
- self.blink = True
- elif a == 4:
- self.underscore = True
- elif a == 1: # Literally "bright", not bold.
- self.bold = True
- i += 1
- fg, bg = (bg, fg) if reverse else (fg, bg)
- self.fg, self.bg = fg, bg
-
- def generate_char(self, c, now):
- fg, bg = (self.bg, self.fg) if self.blink and now%1.0 < 0.3 else (self.fg, self.bg)
- glyph = FONT.glyphs_by_codepoint[ord(c)]
- # Please forgive the string manipulation below.
- lookup = {'0': bg, '1': fg}
- FONT_PADDED_BINARY = ('{:0'+str(glyph.bbW)+'b}').format
- FONT_Y_PAD = [[bg]*glyph.bbW]*(DISPLAY_HEIGHT-FONT_HEIGHT)
- return np.swapaxes(np.array([ list(map(lookup.get, FONT_PADDED_BINARY(int(row, 16))[:glyph.bbW])) for row in glyph.get_data() ] + FONT_Y_PAD, dtype=np.uint8), 0, 1)
-
- def generate(self, now):
- chars = [self.generate_char(c, now) for c in self.text]
- # This refers to inter-letter spacing
- space = np.zeros((LETTER_SPACING, DISPLAY_HEIGHT, 3), dtype=np.uint8)
- spaces = [space]*(len(chars)-1)
- everything = chars + spaces
- everything[::2] = chars
- everything[1::2] = spaces
- return np.concatenate(everything)
-
-class TextRenderer:
- def __init__(self, text, escapes=True):
- """Renders text into a frame buffer
-
- "escapes" tells the renderer whether to interpret escape sequences (True) or not (False).
- """
- generators = []
- current_generator = CharGenerator()
- for match in re.finditer('(\x1B\[[0-9;]+m)|(.)', text):
- esc, char = match.groups()
- if esc:
- if current_generator.text != '':
- generators.append(current_generator)
- current_generator = CharGenerator(esc, current_generator)
- elif char:
- current_generator.text += char
- generators = generators + [current_generator]
- # Generate the actual frame buffer
- zeros = [np.zeros((DISPLAY_WIDTH, DISPLAY_HEIGHT, 3), dtype=np.uint8)]
- # Pad the array with one screen's worth of zeros on both sides so the text fully scrolls through.
- now = time.time()
- self.raw = np.concatenate(zeros+[g.generate(now) for g in generators]+zeros)
-
- def frames(self):
- w,h,_ = self.raw.shape
- for i in range(0, w-DISPLAY_WIDTH, 2):
- frame = self.raw[i:i+DISPLAY_WIDTH, :, :]
- yield frame, 1/DEFAULT_SCROLL_SPEED
-
-class ImageRenderer:
- def __new__(cls, image_data):
- img = Image.open(io.BytesIO(image_data))
- self.img = img
-
- def frames(self):
- img = self.img
- palette = img.getpalette()
- last_frame = Image.new("RGB", img.size)
- # FIXME set delay to 1/10s if the image is animated, only use DEFAULT_IMAGE_DURATION for static images.
- delay = img.info.get('duration', DEFAULT_IMAGE_DURATION*1000.0)/1000.0
-
- for frame in ImageSequence.Iterator(img):
- #This works around a known bug in Pillow
- #See also: http://stackoverflow.com/questions/4904940/python-converting-gif-frames-to-png
- frame.putpalette(palette)
- c = frame.convert("RGB")
-
- if img.info['background'] != img.info['transparency']:
- last_frame.paste(c, c)
- else:
- last_frame = c
-
- im = last_frame.copy()
- im.thumbnail((DISPLAY_WIDTH, DISPLAY_HEIGHT), Image.NEAREST)
- data = np.array(im.getdata(), dtype=np.int8)
- data.reshape((DISPLAY_WIDTH, DISPLAY_HEIGHT, 3))
- yield data, delay
-
diff --git a/host/matelight/scroll-test.py b/host/matelight/scroll-test.py
deleted file mode 100755
index f962416..0000000
--- a/host/matelight/scroll-test.py
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/env python3
-import host
-import numpy as np
-from config import *
-import time
-import renderers
-
-while True:
- renderer = renderers.TextRenderer('\x1B[91mThe \x1B[92;105mquick\x1B[0m brown \x1B[96;5mfox jumps over\x1B[0m the lazy dog.')
- for frame, delay in renderer.frames():
- #print(list(frame.flatten()))
- host.sendframe(np.swapaxes(frame, 0, 1))
- #time.sleep(delay)
-