X-Git-Url: http://git.joshuawise.com/netwatch.git/blobdiff_plain/d0dc32550bab5d545b91dd76003f04b827968f41..779b8ca85ab0f4cc686f9bb3ffe622fef404b99e:/net/rfb.c diff --git a/net/rfb.c b/net/rfb.c index eff9c41..e86f03d 100644 --- a/net/rfb.c +++ b/net/rfb.c @@ -2,8 +2,10 @@ #include #include #include +#include #include "lwip/tcp.h" +#include "lwip/stats.h" #include "rfb.h" @@ -14,7 +16,10 @@ #define POINTER_EVENT 5 #define CLIENT_CUT_TEXT 6 -#define RFB_BUF_SIZE 64 +#define RFB_BUF_SIZE 512 + +#define SCREEN_CHUNKS_X 8 +#define SCREEN_CHUNKS_Y 8 struct pixel_format { uint8_t bpp; @@ -88,7 +93,7 @@ struct update_header { struct rfb_state { enum { - ST_BEGIN, + ST_BEGIN = 0, ST_CLIENTINIT, ST_MAIN } state; @@ -105,13 +110,26 @@ struct rfb_state { struct fb_update_req client_interest_area; enum { - SST_IDLE, - SST_NEEDS_UPDATE, - SST_SENDING + SST_IDLE = 0, + SST_HEADER, + SST_DATA } send_state; - uint32_t update_pos; - uint32_t frame_bytes; + uint32_t checksums[SCREEN_CHUNKS_X][SCREEN_CHUNKS_Y]; + + 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; + + uint32_t chunk_checksum; + + int chunk_actually_sent; + int try_in_a_bit; }; static struct server_init_message server_info; @@ -148,88 +166,157 @@ static void update_server_info() { } } +static int advance_chunk(struct rfb_state *state) { + + 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->chunk_ynum = 0; + state->send_state = SST_IDLE; + if (!(state->chunk_actually_sent)) + state->try_in_a_bit = 2; + return 1; + } + + return 0; +} + static void send_fsm(struct tcp_pcb *pcb, struct rfb_state *state) { struct update_header hdr; - int left, sndlength; + int lines_left; + unsigned 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; - } - - /* potential FALL THROUGH */ - - case SST_NEEDS_UPDATE: - outputf("RFB send: sending header"); - /* Send a header */ - state->frame_bytes = fb->curmode.xres * fb->curmode.yres * fb->curmode.bytestride; - hdr.msgtype = 0; - hdr.nrects = htons(1); - hdr.xpos = htons(0); - hdr.ypos = htons(0); - hdr.width = htons(fb->curmode.xres); - hdr.height = htons(fb->curmode.yres); - hdr.enctype = htonl(0); - tcp_write(pcb, &hdr, sizeof(hdr), 0); - tcp_output(pcb); + while(1) { - state->update_pos = 0; - state->send_state = SST_SENDING; + switch (state->send_state) { - /* FALL THROUGH */ + case SST_IDLE: + /* Nothing to do */ - case SST_SENDING: - - while (1) { - left = state->frame_bytes - state->update_pos; - - if (left == 0) { - state->send_state = SST_IDLE; - break; + if (state->update_requested) { + outputf("RFB send: update requested"); + state->update_requested = 0; + state->chunk_actually_sent = 0; + state->send_state = SST_HEADER; + } else { + return; + } + + /* FALL THROUGH to SST_HEADER */ + + case SST_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); } - if (left > tcp_mss(pcb)) { - sndlength = tcp_mss(pcb); - } else { - sndlength = left; + 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); } - do { - err = tcp_write(pcb, fb->fbaddr + state->update_pos, sndlength, 0); - if (err == ERR_MEM) { - outputf("RFB: ERR_MEM sending %d", sndlength); - sndlength /= 2; + /* Do we _actually_ need to send this chunk? */ + if (fb->checksum_rect) { + state->chunk_checksum = fb->checksum_rect(state->chunk_xpos, state->chunk_ypos, + state->chunk_width, state->chunk_height); + + if (state->chunk_checksum == state->checksums[state->chunk_xnum][state->chunk_ynum]) { + if (advance_chunk(state)) + return; + continue; } - } while (err == ERR_MEM && sndlength > 1); + /* Checksum gets set in data block, AFTER the data has been sent. */ + } - if (err == ERR_OK) { - outputf("RFB: attempting send %d", sndlength); - } else { - outputf("RFB: send error %d", err); - break; + outputf("actually sent"); + state->chunk_actually_sent = 1; + + /* Send a header */ + hdr.msgtype = 0; + state->chunk_lindex = 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); + 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); + + /* Try again later. */ + return; } - if (tcp_output(pcb) != ERR_OK) { - outputf("RFB: tcp_output has had enough"); + state->send_state = SST_DATA; + + /* FALL THROUGH to SST_DATA */ + + case SST_DATA: + + lines_left = state->chunk_height - state->chunk_lindex; + + if (lines_left == 0) { + state->send_state = SST_HEADER; + state->checksums[state->chunk_xnum][state->chunk_ynum] = state->chunk_checksum; + if (advance_chunk(state)) + return; break; } - state->update_pos += sndlength; + 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; + } else { + if (err != ERR_MEM) + outputf("RFB: send error %d", err); + + return; + } + if (tcp_sndbuf(pcb) == 0) { - break; + return; } } - - 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) { @@ -238,12 +325,29 @@ static err_t rfb_sent(void *arg, struct tcp_pcb *pcb, uint16_t len) { return ERR_OK; } +static err_t rfb_poll(void *arg, struct tcp_pcb *pcb) { + struct rfb_state *state = arg; + send_fsm(pcb, state); + if (state->try_in_a_bit) { + state->try_in_a_bit--; + if (!(state->try_in_a_bit)) { + state->update_requested = 1; + } + } +/* + stats_display(); +*/ + return ERR_OK; +} + static void close_conn(struct tcp_pcb *pcb, struct rfb_state *state) { + outputf("close_conn: bailing"); tcp_arg(pcb, NULL); tcp_sent(pcb, NULL); tcp_recv(pcb, NULL); mem_free(state); tcp_close(pcb); + outputf("close_conn: done"); } enum fsm_result { @@ -309,7 +413,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; @@ -347,7 +451,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; @@ -368,9 +471,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; @@ -394,17 +499,18 @@ static enum fsm_result recv_fsm(struct tcp_pcb *pcb, struct rfb_state *state) { (struct text_event_pkt *)state->data; if (state->writepos < sizeof(struct text_event_pkt) - + pkt->length) + + pkt->length) return NEEDMORE; /* XXX stub */ state->readpos += sizeof(struct text_event_pkt) - + pkt->length; + + pkt->length; return OK; default: outputf("RFB: Bad command: %d", state->data[0]); + return FAIL; } default: outputf("RFB: Bad state"); @@ -413,8 +519,9 @@ static enum fsm_result recv_fsm(struct tcp_pcb *pcb, struct rfb_state *state) { } static err_t rfb_recv(void *arg, struct tcp_pcb *pcb, - struct pbuf *p, err_t err) { + struct pbuf *p, err_t err) { struct rfb_state *state = arg; + uint16_t copylen; if (state == NULL) @@ -437,8 +544,8 @@ static err_t rfb_recv(void *arg, struct tcp_pcb *pcb, return ERR_OK; } - outputf("RFB: Processing %d", p->tot_len); - pbuf_copy_partial(p, state->data + state->writepos, p->tot_len, 0); + copylen = pbuf_copy_partial(p, state->data + state->writepos, p->tot_len, 0); + outputf("RFB: Processing %d, wp %d, cp %d", p->tot_len, state->writepos, copylen); state->writepos += p->tot_len; tcp_recved(pcb, p->tot_len); @@ -448,26 +555,21 @@ static err_t rfb_recv(void *arg, struct tcp_pcb *pcb, switch (recv_fsm(pcb, state)) { case NEEDMORE: outputf("RFB FSM: blocking"); - /* Need more data */ - return ERR_OK; + goto doneprocessing; case OK: outputf("RFB FSM: ok"); - /* Might as well send now... */ - if (state->send_state == SST_IDLE - && state->update_requested) { - send_fsm(pcb, state); - } - if (state->readpos == state->writepos) { state->readpos = 0; state->writepos = 0; - return ERR_OK; + goto doneprocessing; } else { memmove(state->data, - state->data + state->readpos, - state->writepos - state->readpos); + state->data + state->readpos, + state->writepos - state->readpos); + state->writepos -= state->readpos; + state->readpos = 0; } break; case FAIL: @@ -477,6 +579,15 @@ static err_t rfb_recv(void *arg, struct tcp_pcb *pcb, return ERR_OK; } } + +doneprocessing: + + /* Kick off a send. */ + if (state->send_state == SST_IDLE && state->update_requested) { + send_fsm(pcb, state); + } + + return ERR_OK; } static err_t rfb_accept(void *arg, struct tcp_pcb *pcb, err_t err) { @@ -487,10 +598,9 @@ static err_t rfb_accept(void *arg, struct tcp_pcb *pcb, err_t err) { state = (struct rfb_state *)mem_malloc(sizeof(struct rfb_state)); + memset(state, 0, sizeof(struct rfb_state)); + state->state = ST_BEGIN; - state->readpos = 0; - state->writepos = 0; - state->update_requested = 0; state->send_state = SST_IDLE; /* XXX: update_server_info() should be called from the 64ms timer, and deal @@ -506,9 +616,9 @@ 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_poll(pcb, rfb_poll, 1); /* tcp_err(pcb, rfb_err); - tcp_poll(pcb, rfb_poll, 2); */ tcp_write(pcb, "RFB 003.008\n", 12, 0); tcp_output(pcb);