X-Git-Url: http://git.joshuawise.com/netwatch.git/blobdiff_plain/b80e06bab89f413f40553f85490e0b8ce3225df7..cdde55f54a0880bde5694fcdea8e386ada0cd5ce:/net/rfb.c diff --git a/net/rfb.c b/net/rfb.c index fbd14b1..5fe1a11 100644 --- a/net/rfb.c +++ b/net/rfb.c @@ -3,6 +3,8 @@ #include #include +#include "../aseg-paging/keyboard.h" + #include "lwip/tcp.h" #include "rfb.h" @@ -16,6 +18,9 @@ #define RFB_BUF_SIZE 64 +#define SCREEN_CHUNKS_X 16 +#define SCREEN_CHUNKS_Y 8 + struct pixel_format { uint8_t bpp; uint8_t depth; @@ -75,6 +80,17 @@ struct text_event_pkt { char text[]; }; +struct update_header { + uint8_t msgtype; + uint8_t padding; + uint16_t nrects; + uint16_t xpos; + uint16_t ypos; + uint16_t width; + uint16_t height; + int32_t enctype; +}; + struct rfb_state { enum { ST_BEGIN, @@ -89,10 +105,26 @@ struct rfb_state { int writepos; char next_update_incremental; + char update_requested; + struct fb_update_req client_interest_area; - uint8_t update_allowed; - uint8_t sending; + enum { + SST_IDLE, + SST_NEEDS_UPDATE, + SST_SENDING + } send_state; + + uint32_t checksums[SCREEN_CHUNKS_Y][SCREEN_CHUNKS_X]; + + uint32_t chunk_xnum; + uint32_t chunk_ynum; + uint32_t chunk_xpos; + uint32_t chunk_ypos; + uint32_t chunk_width; + uint32_t chunk_height; + + uint32_t chunk_lindex; }; static struct server_init_message server_info; @@ -129,8 +161,165 @@ static void update_server_info() { } } -static void start_send(struct tcp_pcb *pcb, struct rfb_state *state) { - /* ... */ +static void send_fsm(struct tcp_pcb *pcb, struct rfb_state *state) { + struct update_header hdr; + int lines_left; + char * lptr; + int totaldim; + err_t err; + + switch (state->send_state) { + case SST_IDLE: + /* Nothing to do */ + if (state->update_requested) { + outputf("RFB send: update requested"); + state->update_requested = 0; + state->send_state = SST_NEEDS_UPDATE; + } else { + break; + } + + /* FALL THROUGH to SST_NEEDS_UPDATE */ + + case SST_NEEDS_UPDATE: + + state->chunk_xnum = 0; + state->chunk_ynum = 0; + state->chunk_width = 0; + state->chunk_height = 0; + state->chunk_lindex = 0; + state->send_state = SST_SENDING; + + /* FALL THROUGH to SST_SENDING */ + + case SST_SENDING: + + while (1) { + lines_left = state->chunk_height - state->chunk_lindex; + + if (lines_left == 0) { + outputf("RFB: (%d [%d], %d [%d]), advancing", + state->chunk_xnum, state->chunk_xpos, + state->chunk_ynum, state->chunk_ypos); + + /* Advance to the next chunk if necessary. If + * state->chunk_height is zero, then we are + * arriving here for the first time from + * SST_NEEDS_UPDATE. */ + + if (state->chunk_height != 0) { + state->chunk_xnum += 1; + } + + if (state->chunk_xnum == SCREEN_CHUNKS_X) { + state->chunk_ynum += 1; + state->chunk_xnum = 0; + } + + if (state->chunk_ynum == SCREEN_CHUNKS_Y) { + state->send_state = SST_IDLE; + break; + } + + outputf("RFB send: sending header"); + + /* Calculate the width and height for this chunk, remembering + * that if SCREEN_CHUNKS_[XY] do not evenly divide the width and + * height, we may need to have shorter chunks at the edge of + * the screen. */ + + state->chunk_width = fb->curmode.xres / SCREEN_CHUNKS_X; + if (fb->curmode.xres % SCREEN_CHUNKS_X != 0) + state->chunk_width += 1; + state->chunk_xpos = state->chunk_width * state->chunk_xnum; + totaldim = state->chunk_width * (state->chunk_xnum + 1); + if (totaldim > fb->curmode.xres) { + state->chunk_width -= (totaldim - fb->curmode.xres); + } + + state->chunk_height = fb->curmode.yres / SCREEN_CHUNKS_Y; + if (fb->curmode.yres % SCREEN_CHUNKS_Y != 0) + state->chunk_height += 1; + state->chunk_ypos = state->chunk_height + * state->chunk_ynum; + totaldim = state->chunk_height * (state->chunk_ynum + 1); + if (totaldim > fb->curmode.yres) { + state->chunk_height -= (totaldim - fb->curmode.yres); + } + + /* Send a header */ + hdr.msgtype = 0; + hdr.nrects = htons(1); + hdr.xpos = htons(state->chunk_xpos); + hdr.ypos = htons(state->chunk_ypos); + hdr.width = htons(state->chunk_width); + hdr.height= htons(state->chunk_height); + hdr.enctype = htonl(0); + state->chunk_lindex = 0; + lines_left = state->chunk_height; + + err = tcp_write(pcb, &hdr, sizeof(hdr), TCP_WRITE_FLAG_COPY); + + if (err != ERR_OK) { + if (err != ERR_MEM) + outputf("RFB: header send error %d", err); + + /* Crap. Reset chunk_height to 0 so that next time around, + * we'll recalculate this chunk (not advance) and try to + * send the header again. + */ + state->chunk_height = 0; + } + } + + do { + outputf("RFB: (%d [%d], %d [%d]), %d x %d, line %d", + state->chunk_xnum, state->chunk_xpos, + state->chunk_ynum, state->chunk_ypos, + state->chunk_width, state->chunk_height, + state->chunk_lindex); + + lptr = fb->fbaddr + + (fb->curmode.xres * fb->curmode.bytestride + * (state->chunk_ypos + state->chunk_lindex)) + + (state->chunk_xpos * fb->curmode.bytestride); + + /* The network card can't DMA from video RAM, + * so use TCP_WRITE_FLAG_COPY. */ + err = tcp_write(pcb, lptr, + fb->curmode.bytestride * state->chunk_width, + TCP_WRITE_FLAG_COPY); + + if (err == ERR_OK) { + state->chunk_lindex += 1; + } + + } while (err == ERR_OK && state->chunk_lindex < state->chunk_height); + + if (err != ERR_OK) { + if (err != ERR_MEM) + outputf("RFB: send error %d", err); + + outputf("RFB: that's all for now"); + break; + } + + if (tcp_sndbuf(pcb) == 0) { + break; + } + } + + break; + } + + if (tcp_output(pcb) != ERR_OK) + outputf("RFB: tcp_output bailed in send_fsm?"); +} + +static err_t rfb_sent(void *arg, struct tcp_pcb *pcb, uint16_t len) { + struct rfb_state *state = arg; + send_fsm(pcb, state); + return ERR_OK; } static void close_conn(struct tcp_pcb *pcb, struct rfb_state *state) { @@ -204,7 +393,7 @@ static enum fsm_result recv_fsm(struct tcp_pcb *pcb, struct rfb_state *state) { state->state = ST_MAIN; outputf("RFB: Sending server info", state->version); - tcp_write(pcb, &server_info, sizeof(server_info), 0); + tcp_write(pcb, &server_info, sizeof(server_info), TCP_WRITE_FLAG_COPY); tcp_output(pcb); return OK; @@ -242,7 +431,6 @@ static enum fsm_result recv_fsm(struct tcp_pcb *pcb, struct rfb_state *state) { for (i = 0; i < ntohs(req->num); i++) { outputf("RFB: Encoding: %d", ntohl(req->encodings[i])); /* XXX ... */ - } state->readpos += pktsize; @@ -253,7 +441,7 @@ static enum fsm_result recv_fsm(struct tcp_pcb *pcb, struct rfb_state *state) { return NEEDMORE; outputf("RFB: UpdateRequest"); - state->update_allowed = 1; + state->update_requested = 1; memcpy(&state->client_interest_area, state->data, sizeof(struct fb_update_req)); @@ -263,9 +451,11 @@ static enum fsm_result recv_fsm(struct tcp_pcb *pcb, struct rfb_state *state) { case KEY_EVENT: if (state->writepos < sizeof(struct key_event_pkt)) return NEEDMORE; - outputf("RFB: Key"); - /* XXX stub */ + struct key_event_pkt * p = (struct key_event_pkt *)state->data; + + outputf("RFB: Key: %d (%c)", htonl(p->keysym), (htonl(p->keysym) & 0xFF)); + kbd_inject_keysym(htonl(p->keysym), p->downflag); state->readpos += sizeof(struct key_event_pkt); return OK; @@ -349,9 +539,10 @@ static err_t rfb_recv(void *arg, struct tcp_pcb *pcb, case OK: outputf("RFB FSM: ok"); - /* Might as well send now... */ - if (state->update_allowed && !state->sending) { - start_send(pcb, state); + /* Kick off a send. */ + if (state->send_state == SST_IDLE + && state->update_requested) { + send_fsm(pcb, state); } if (state->readpos == state->writepos) { @@ -384,8 +575,8 @@ static err_t rfb_accept(void *arg, struct tcp_pcb *pcb, err_t err) { state->state = ST_BEGIN; state->readpos = 0; state->writepos = 0; - state->update_allowed = 0; - state->sending = 0; + state->update_requested = 0; + state->send_state = SST_IDLE; /* XXX: update_server_info() should be called from the 64ms timer, and deal * with screen resizes appropriately. */ @@ -399,6 +590,7 @@ static err_t rfb_accept(void *arg, struct tcp_pcb *pcb, err_t err) { tcp_arg(pcb, state); tcp_recv(pcb, rfb_recv); + tcp_sent(pcb, rfb_sent); /* tcp_err(pcb, rfb_err); tcp_poll(pcb, rfb_poll, 2);