Working on the new UDP protocol
This commit is contained in:
parent
5a77d3a0e0
commit
809f8ac2e0
5 changed files with 206 additions and 247 deletions
12
host/config.h
Normal file
12
host/config.h
Normal file
|
|
@ -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__
|
||||||
190
host/gif.c
190
host/gif.c
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
13
host/gif.h
13
host/gif.h
|
|
@ -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__
|
|
||||||
220
host/main.c
220
host/main.c
|
|
@ -1,8 +1,9 @@
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "color.h"
|
#include "color.h"
|
||||||
#include "font.h"
|
#include "font.h"
|
||||||
#include "gif.h"
|
#include "net.h"
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
@ -11,8 +12,13 @@
|
||||||
#include <wchar.h>
|
#include <wchar.h>
|
||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
#include <sys/timeb.h>
|
#include <sys/timeb.h>
|
||||||
|
#include <sys/queue.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
|
||||||
/* CAUTION: REQUIRES INPUT TO BE \0-TERMINATED
|
/* CAUTION: REQUIRES INPUT TO BE \0-TERMINATED
|
||||||
* ...also, it does a hardcodes setlocale of LC_CTYPE to en_US.utf8 for... reasons. */
|
* ...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){
|
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){
|
int main(int argc, char **argv){
|
||||||
FILE *f = NULL;
|
FILE *f = NULL;
|
||||||
|
int udpfd = 0;
|
||||||
|
int udp6fd = 0;
|
||||||
|
uint8_t *udpbuf = NULL;
|
||||||
|
uint8_t *fbdata = NULL;
|
||||||
|
framebuffer_t *fb = NULL;
|
||||||
|
|
||||||
if(argc != 2){
|
if(argc != 2){
|
||||||
fprintf(stderr, "No or too much input text given\n");
|
fprintf(stderr, "No or too much input text given\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Read font file */
|
||||||
FILE *fontfile = fopen("unifont.bdf", "r");
|
FILE *fontfile = fopen("unifont.bdf", "r");
|
||||||
if(!fontfile){
|
if(!fontfile){
|
||||||
fprintf(stderr, "Error opening font file: %s\n", strerror(errno));
|
fprintf(stderr, "Error opening font file: %s\n", strerror(errno));
|
||||||
|
|
@ -366,61 +378,181 @@ int main(int argc, char **argv){
|
||||||
}
|
}
|
||||||
fclose(fontfile);
|
fclose(fontfile);
|
||||||
|
|
||||||
int delay = 20;
|
/* Set up framebuffer */
|
||||||
framebuffer_t *fb;
|
fbdata = malloc(DISPLAY_WIDTH*DISPLAY_HEIGHT*sizeof(color_t));
|
||||||
gifAnimationState_t *gifstate = NULL;
|
if(!fbdata){
|
||||||
char *p = strstr(argv[1], ".gif");
|
fprintf(stderr, "Cannot alloccate framebuffer\n");
|
||||||
if(p && p[4] == '\0'){ /* Argument ends with ".gif", try to read it as a gif file */
|
goto error;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
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! */
|
for(;;){ /* Never gonna give you up, never gonna let you down! */
|
||||||
if(!fb){
|
struct timeb time = {0};
|
||||||
fprintf(stderr, "Error rendering.\n");
|
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;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("\033[2J");
|
/* Receive pending IPv6 UDP packets */
|
||||||
|
/* FIXME */
|
||||||
|
|
||||||
|
/* Render frame buffer */
|
||||||
|
printf("\033[2J");
|
||||||
console_render_buffer(fb);
|
console_render_buffer(fb);
|
||||||
printf("\n");
|
printf("\n");
|
||||||
usleep(delay*1000);
|
|
||||||
|
|
||||||
if(gifstate){
|
usb_send_buffer(fb);
|
||||||
fb = framebuffer_render_gif(NULL, 0, &gifstate, &delay);
|
usleep(1000000/FRAMERATE);
|
||||||
if(delay == -1)
|
|
||||||
delay = 20;
|
|
||||||
}else{
|
|
||||||
fb = framebuffer_render_text(argv[1], glyph_table, BLP_SIZE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
error:
|
error:
|
||||||
|
fclose(f);
|
||||||
|
close(udpfd);
|
||||||
|
close(udp6fd);
|
||||||
|
free(udpbuf);
|
||||||
|
free(fbdata);
|
||||||
|
free(fb);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
18
host/net.h
Normal file
18
host/net.h
Normal file
|
|
@ -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__
|
||||||
Loading…
Add table
Add a link
Reference in a new issue