aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjaseg <jaseg@jaseg.net>2014-02-01 22:07:03 +0100
committerjaseg <jaseg@jaseg.net>2014-02-01 22:07:03 +0100
commit809f8ac2e0fc17e956022051782a3f2203cdbfb4 (patch)
treeb1c2dddd621348d5c02d85e29323365b9d33d70e
parent5a77d3a0e0a24c2c637aec0195919a13d42ac769 (diff)
downloadmatelight-809f8ac2e0fc17e956022051782a3f2203cdbfb4.tar.gz
matelight-809f8ac2e0fc17e956022051782a3f2203cdbfb4.tar.bz2
matelight-809f8ac2e0fc17e956022051782a3f2203cdbfb4.zip
Working on the new UDP protocol
-rw-r--r--host/config.h12
-rw-r--r--host/gif.c190
-rw-r--r--host/gif.h13
-rw-r--r--host/main.c222
-rw-r--r--host/net.h18
5 files changed, 207 insertions, 248 deletions
diff --git a/host/config.h b/host/config.h
new file mode 100644
index 0000000..64a792c
--- /dev/null
+++ b/host/config.h
@@ -0,0 +1,12 @@
+#ifndef __CONFIG_H__
+#define __CONFIG_H__
+
+#define DISPLAY_WIDTH 40
+#define DISPLAY_HEIGHT 16
+#define FRAME_TIMEOUT 5000 /* ms */
+#define UDP_PORT 2323
+#define FRAMERATE 20 /* fps */
+#define TEXT_LOOP_COUNT 3
+#define TEXT_SCROLL_SPEED 3
+
+#endif//__CONFIG_H__
diff --git a/host/gif.c b/host/gif.c
deleted file mode 100644
index c62a5e2..0000000
--- a/host/gif.c
+++ /dev/null
@@ -1,190 +0,0 @@
-
-#include "gif.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-typedef struct {
- uint8_t *data;
- unsigned int index;
- unsigned int length;
-} readBuffer_t;
-
-int gif_buffer_input (GifFileType *gif, GifByteType *dest, int n){
- readBuffer_t *rb = gif->UserData;
- if(rb->index+n > rb->length)
- n = rb->length - rb->index;
- memcpy(dest, rb->data + rb->index, n);
- rb->index += n;
- return n;
-}
-
-struct _gifAnimationState {
- int idx;
- GifFileType *gif;
- framebuffer_t *frame;
-};
-
-void color_fill(color_t *dest, color_t src, size_t size){
- /* Some magic to reduce the amount of calls to small-block memcpys */
- dest[0] = src;
- size_t i = 1;
- size_t j = 2;
- while(j < size){
- memcpy(dest+i, dest, i*sizeof(color_t));
- i = j;
- j *= 2;
- }
- memcpy(dest+i, dest, (size-i)*sizeof(color_t));
-}
-
-gifAnimationState_t *gif_read(uint8_t *buf, size_t buflength){
- color_t *fb = 0;
- gifAnimationState_t *st = malloc(sizeof(gifAnimationState_t));
- if(!st){
- fprintf(stderr, "Failed to allocate %lu bytes\n", sizeof(*st));
- return 0;
- }
-
- readBuffer_t readBuf = {buf, 0, buflength};
- int err = 0;
- GifFileType *gif = DGifOpen(&readBuf, gif_buffer_input, &err);
- if(err){
- fprintf(stderr, "Could not open GIF: %s\n", GifErrorString(err));
- goto error;
- }
-
- err = DGifSlurp(gif);
- if(err){
- fprintf(stderr, "Could not read GIF data: %s\n", GifErrorString(err));
- goto error;
- }
-
- unsigned int framesize = gif->SWidth*gif->SHeight;
- if(framesize == 0){ /* Can this actually happen? */
- fprintf(stderr, "Invalid 0*0px gif\n");
- goto error;
- }
- fb = calloc(framesize, sizeof(color_t));
- if(!fb){
- fprintf(stderr, "Failed to allocate framebuffer for GIF (%lu bytes)\n", framesize*sizeof(color_t));
- goto error;
- }
- if(gif->SColorMap){ /* Initially fill framebuffer with background color */
- GifColorType *col = gif->SColorMap[gif->SBackGroundColor].Colors;
- color_t c = {col->Red, col->Green, col->Blue, 255};
- color_fill(fb, c, framesize);
- }else{
- /* Set all pixels to a transparent black */
- memset(fb, 0, framesize*sizeof(color_t));
- }
-
- st->gif = gif;
- st->idx = 0;
- st->frame = malloc(sizeof(framebuffer_t));
- if(!st->frame){
- fprintf(stderr, "Failed to allocate %lu bytes\n", sizeof(framebuffer_t));
- goto error;
- }
- st->frame->data = fb;
- st->frame->w = gif->SWidth;
- st->frame->h = gif->SHeight;
- return st;
-error:
- free(fb);
- free(st);
- return 0;
-}
-
-void gifAnimationState_free(gifAnimationState_t *st){
- if(st)
- framebuffer_free(st->frame);
- free(st);
-}
-
-/* buf ⇜ input data
- * state ⇜ internal state, initialize as NULL
- * delay ⇜ is filled with this frame's delay value in milliseconds (if it is not NULL). -1 means "No delay given".
- * Small note: I mean, rendering a GIF in python is not quite trivial, either (about 20loc). But come on, this is just ridiculous. */
-framebuffer_t *framebuffer_render_gif(uint8_t *buf, size_t buflength, gifAnimationState_t **state, int *delay){
- gifAnimationState_t *st;
- if(*state == NULL){
- /* On first invocation, parse gif and store it in the state struct */
- st = gif_read(buf, buflength);
- if(!st)
- goto error;
- *state = st;
- }else{
- st = *state;
- }
-
- GifFileType *gif = st->gif;
-
- /* Find this image's color map */
- SavedImage *img = gif->SavedImages + st->idx;
- ColorMapObject *cmo = img->ImageDesc.ColorMap;
- if(!cmo){
- cmo = gif->SColorMap;
- if(!cmo){
- fprintf(stderr, "Missing color table for GIF frame %d\n", st->idx);
- goto error;
- }
- }
-
- /* Extract and validate image's bounds*/
- unsigned int ix = img->ImageDesc.Left;
- unsigned int iy = img->ImageDesc.Top;
- unsigned int iw = img->ImageDesc.Width;
- unsigned int ih = img->ImageDesc.Height;
- if((iy+ih)*gif->SWidth + ix+iw > st->frame->w*st->frame->h){
- fprintf(stderr, "Invalid gif: Image %d (x %d y %d w %d h %d) out of bounds (w %d h %d)\n",
- st->idx, ix, iy, iw, ih, gif->SWidth, gif->SHeight);
- goto error;
- }
-
- /* Find and parse this image's GCB (if it exists) */
- int transparent = -1;
- int disposal = DISPOSAL_UNSPECIFIED;
- if(delay)
- *delay = -1; /* in milliseconds */
- for(unsigned int i=0; i<img->ExtensionBlockCount; i++){
- ExtensionBlock *eb = img->ExtensionBlocks + i;
- if(eb->Function == GRAPHICS_EXT_FUNC_CODE){
- GraphicsControlBlock *gcb = (GraphicsControlBlock *)eb->Bytes;
- transparent = gcb->TransparentColor;
- disposal = gcb->DisposalMode;
- if(delay)
- *delay = gcb->DelayTime * 10; /* 10ms → 1ms */
- }
- }
-
- color_t *fb = st->frame->data;
- if(disposal == DISPOSE_BACKGROUND || disposal == DISPOSAL_UNSPECIFIED){
- GifColorType *gc = gif->SColorMap[gif->SBackGroundColor].Colors;
- color_t c = {gc->Red, gc->Green, gc->Blue, 255};
- color_fill(fb, c, st->frame->w*st->frame->h);
- }
- /* FIXME DISPOSE_PREVIOUS is currently unhandled. */
-
- /* Render gif bitmap to RGB */
- uint8_t *p = img->RasterBits;
- for(unsigned int y = iy; y < iy+ih; y++){
- for(unsigned int x = ix; x < ix+iw; x++){
- int c = *p++;
- if(c != transparent){
- GifColorType *col = cmo[c].Colors;
- color_t ct = {col->Red, col->Green, col->Blue, 255};
- fb[y*st->frame->w + x] = ct;
- }
- }
- }
-
- st->idx++;
- if(st->idx >= gif->ImageCount)
- st->idx = 0;
- return st->frame;
-error:
- gifAnimationState_free(st);
- return 0;
-}
-
diff --git a/host/gif.h b/host/gif.h
deleted file mode 100644
index fde10b4..0000000
--- a/host/gif.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#ifndef __GIF_H__
-#define __GIF_H__
-
-#include <gif_lib.h>
-#include "color.h"
-
-typedef struct _gifAnimationState gifAnimationState_t;
-
-gifAnimationState_t *gif_read(uint8_t *buf, size_t buflength);
-framebuffer_t* framebuffer_render_gif(uint8_t *buf, size_t buflength, gifAnimationState_t **state, int *delay);
-void gifAnimationState_free(gifAnimationState_t *st);
-
-#endif//__GIF_H__
diff --git a/host/main.c b/host/main.c
index aa782aa..d8f5062 100644
--- a/host/main.c
+++ b/host/main.c
@@ -1,8 +1,9 @@
+#include "config.h"
#include "main.h"
#include "color.h"
#include "font.h"
-#include "gif.h"
+#include "net.h"
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
@@ -11,8 +12,13 @@
#include <wchar.h>
#include <locale.h>
#include <sys/timeb.h>
+#include <sys/queue.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
#include <unistd.h>
+
/* CAUTION: REQUIRES INPUT TO BE \0-TERMINATED
* ...also, it does a hardcodes setlocale of LC_CTYPE to en_US.utf8 for... reasons. */
framebuffer_t *framebuffer_render_text(char *s, glyph_t **glyph_table, unsigned int glyph_table_size){
@@ -347,12 +353,18 @@ void console_render_buffer(framebuffer_t *fb){
int main(int argc, char **argv){
FILE *f = NULL;
+ int udpfd = 0;
+ int udp6fd = 0;
+ uint8_t *udpbuf = NULL;
+ uint8_t *fbdata = NULL;
+ framebuffer_t *fb = NULL;
if(argc != 2){
fprintf(stderr, "No or too much input text given\n");
return 1;
}
+ /* Read font file */
FILE *fontfile = fopen("unifont.bdf", "r");
if(!fontfile){
fprintf(stderr, "Error opening font file: %s\n", strerror(errno));
@@ -366,61 +378,181 @@ int main(int argc, char **argv){
}
fclose(fontfile);
- int delay = 20;
- framebuffer_t *fb;
- gifAnimationState_t *gifstate = NULL;
- char *p = strstr(argv[1], ".gif");
- if(p && p[4] == '\0'){ /* Argument ends with ".gif", try to read it as a gif file */
- f = fopen(argv[1], "r");
- if(!f){
- fprintf(stderr, "Error opening gif file from argument (\"%s\"): %s\n", argv[1], strerror(errno));
- goto error;
- }
- uint8_t *buf = NULL;
- size_t size = 0;
- size_t read = 0;
- const size_t READ_INC = 1024;
- do{
- size_t newsize = size+READ_INC;
- uint8_t *oldbuf = buf;
- buf = realloc(buf, newsize);
- if(!buf){
- free(oldbuf);
- fclose(f);
- fprintf(stderr, "Error opening gif file from argument (\"%s\"): Cannot allocate %lu bytes.\n", argv[1], newsize);
- goto error;
- }
- read += fread(buf+size, 1, READ_INC, f);
- size = newsize;
- }while(read == size);
- fb = framebuffer_render_gif(buf, read, &gifstate, &delay);
- free(buf);
- fclose(f);
- }else{
- fb = framebuffer_render_text(argv[1], glyph_table, BLP_SIZE);
+ /* Set up framebuffer */
+ fbdata = malloc(DISPLAY_WIDTH*DISPLAY_HEIGHT*sizeof(color_t));
+ if(!fbdata){
+ fprintf(stderr, "Cannot alloccate framebuffer\n");
+ goto error;
}
+ fb = malloc(sizeof(*fb));
+ if(!fb){
+ fprintf(stderr, "Cannot alloccate framebuffer\n");
+ goto error;
+ }
+ fb->w = DISPLAY_WIDTH;
+ fb->h = DISPLAY_HEIGHT;
+ fb->data = fbdata;
+
+ /* Set up UDP server */
+ udp4fd = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP);
+ udp6fd = socket(AF_INET6, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP);
+ if(udp4fd < 0 || udp6fd < 0){
+ fprintf(stderr, "Cannot open UDP sockets: %s\n", strerror(errno));
+ goto error;
+ }
+ struct sockaddr_in udp_addr = {
+ AF_INET,
+ INADDR_ANY,
+ htons(UDP_PORT)
+ };
+ bind(udp4fd, &udp_addr, sizeof(udp_addr));
+ struct sockaddr_in udp6_addr = {
+ AF_INET6,
+ IN6ADDR_ANY_INIT,
+ htons(UDP_PORT)
+ };
+ bind(udp6fd, &udp_addr, sizeof(udp_addr));
+ /* Set up UDP receive buffer */
+ struct msghdr msg;
+ memset(&msg, 0, sizeof(msg));
+ udpbuf = malloc(UDP_BUF_SIZE);
+ if(!buf){
+ fprintf(stderr, "Cannot allocate UDP buffer");
+ goto error;
+ }
+ struct iovec iov[1] = {{udpbuf, sizeof(UDP_BUF_SIZE)}};
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1;
+
+ /* List of currently active UDP streams */
+ LIST_HEAD(udp_client_entry, udp_client) udp_client_head;
+ struct udp_client {
+ uint64_t last_timestamp;
+ uint64_t last_sequence_number;
+ struct sockaddr remote_addr;
+ int active:1;
+ LIST_ENTRY(udp_client) entries;
+ };
+ LIST_INIT(&udp_client_head);
+
+ LIST_HEAD(text_queue_head_t, text_queue_entry) text_queue_head;
+ struct text_queue_entry {
+ char *text;
+ int current_position;
+ unsigned int loop_count;
+ LIST_ENTRY(text_queue_entry) entries;
+ };
+ LIST_INIT(&text_queue_head);
for(;;){ /* Never gonna give you up, never gonna let you down! */
- if(!fb){
- fprintf(stderr, "Error rendering.\n");
+ struct timeb time = {0};
+ ftime(&time);
+ uint64_t now = time.time*1000 + time.millitm;
+
+ int made_active = 0;
+ /* Choose next active client */
+ for(struct udp_client *p = udp_client_head.lh_first; p != NULL; p = p->entries.le_next){
+ if(now - p->last_frame_received > FRAME_TIMEOUT){ /* Connection timeout */
+ LIST_REMOVE(p, entries);
+ continue;
+ }
+ if(!made_active){
+ p->active = 1;
+ made_active = 1;
+ }else{
+ p->active = 0;
+ }
+ }
+
+ if(!made_active){ /* No active streams, render marquee */
+ text_queue_entry *entry = text_queue_head.lh_first;
+ framebuffer_t *text = framebuffer_render_text(entry, glyph_table, BLP_SIZE);
+ /* COPY TEXT DATA */
+ for(size_t line = 0; line < DISPLAY_HEIGHT; line++){
+ color_t *datarow = fb->data+(line*DISPLAY_WIDTH);
+ color_t *textrow = text->data+(line*text->w);
+ if(entry->current_position > 0)
+ memset(datarow, 0, sizeof(color_t)*entry->current_position);
+ if(entry->current_position+text->w < DISPLAY_WIDTH)
+ memset(datarow+(entry->curent_position+text->w), 0, DISPLAY_WIDTH-(entry->curent_position+text->w));
+ memcpy(datarow+(entry->current_position>0 ? entry->current_position : 0),
+ textrow+(entry->current_position>0 ? 0 : -entry->current_position),
+ //DISPLAY_WIDTH, text->w-current_position FIXME
+ }
+ entry->current_position++;
+ if(entry->current_position <= -text->w){
+ entry->loop_count++;
+ if(entry->loop_count > TEXT_LOOP_COUNT){
+ LIST_REMOVE(entry, entries);
+ }
+ }
+ }
+
+ /* Receive pending IPv4 UDP packets */
+ ssize_t received = 0;
+ while((received = recvmsg(udp4fd, &msg, 0)) > 0){
+ if(received < UDP_BUF_SIZE) /* Packet too short */
+ continue;
+ ml_packet_t *pkt = (ml_packet_t*)udpbuf;
+ if(ntohl(pkt->magic) != 0xDEADBEEF)
+ continue;
+ if(ntohs(pkt->width) != DISPLAY_WIDTH || ntohs(pkt->height) != DISPLAY_HEIGHT)
+ continue;
+ struct sockaddr_in *src = msg.msg_name;
+ struct udp_client *entry = NULL;
+ for(struct udp_client *p = udp_client_head.lh_first; p != NULL; p = p->entries.le_next){
+ if(!memcmp(&p->remote_addr, src, sizeof(sockaddr))){ /* found */
+ entry = p;
+ break;
+ }
+ }
+ if(!entry){
+ entry = malloc(sizeof(struct udp_client));
+ if(!entry){
+ fprintf(stderr, "Cannot allocate UDP connection entry\n");
+ goto error;
+ }
+ memset(entry, 0, sizeof(entry));
+ LIST_INSERT_HEAD(&udp_client_head, entry, entries);
+ memcpy(entry->remote_addr, src, sizeof(sockaddr));
+ }
+ if(entry->last_sequence_number > pkt->seq) /* Delayed packet containing old data */
+ continue;
+ entry->last_timestamp = now;
+ entry->last_sequence_number = pkt->seq;
+ if(entry->active){
+ /* Copy frame, extending RGB UDP packet data to RGBA framebuffer format */
+ color_t *p = fb->data;
+ rgb_t *q = pkt->data;
+ while(p < fb->data+fb->w*fb->h){
+ p->a = 0;
+ *(rgb_t *)p++ = *q++;
+ }
+ }
+ }
+ if(received < 0){
+ fprintf(stderr, "Error receiving UDP datagram: %s\n", strerror(errno));
goto error;
}
- printf("\033[2J");
+ /* Receive pending IPv6 UDP packets */
+ /* FIXME */
+ /* Render frame buffer */
+ printf("\033[2J");
console_render_buffer(fb);
printf("\n");
- usleep(delay*1000);
-
- if(gifstate){
- fb = framebuffer_render_gif(NULL, 0, &gifstate, &delay);
- if(delay == -1)
- delay = 20;
- }else{
- fb = framebuffer_render_text(argv[1], glyph_table, BLP_SIZE);
- }
+
+ usb_send_buffer(fb);
+ usleep(1000000/FRAMERATE);
}
return 0;
error:
+ fclose(f);
+ close(udpfd);
+ close(udp6fd);
+ free(udpbuf);
+ free(fbdata);
+ free(fb);
return 1;
}
diff --git a/host/net.h b/host/net.h
new file mode 100644
index 0000000..d8ec949
--- /dev/null
+++ b/host/net.h
@@ -0,0 +1,18 @@
+#ifndef __NET_H__
+#define __NET_H__
+
+#include "config.h"
+
+typedef struct {
+ uint8_t r, g, b;
+} rgb_t;
+
+typedef struct {
+ uint32_t magic, seq;
+ uint16_t width, height;
+ rgb_t data[DISPLAY_WIDTH*DISPLAY_HEIGHT];
+} ml_packet_t;
+
+#define UDP_BUF_SIZE sizeof(ml_packet_t)
+
+#endif//__NET_H__