X-Git-Url: http://git.joshuawise.com/netwatch.git/blobdiff_plain/b80e06bab89f413f40553f85490e0b8ce3225df7..075bbc718253e94387bd857b22c7bedea259651b:/net/rfb.c?ds=sidebyside diff --git a/net/rfb.c b/net/rfb.c index fbd14b1..ff0f089 100644 --- a/net/rfb.c +++ b/net/rfb.c @@ -75,6 +75,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 +100,18 @@ 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 update_pos; + uint32_t frame_bytes; }; static struct server_init_message server_info; @@ -129,8 +148,100 @@ 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 left, sndlength; + 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), TCP_WRITE_FLAG_COPY); + + state->update_pos = 0; + state->send_state = SST_SENDING; + + /* FALL THROUGH */ + + case SST_SENDING: + + while (1) { + unsigned char mbuf[8192 /* XXX magic */]; + + left = state->frame_bytes - state->update_pos; + + if (left == 0) { + state->send_state = SST_IDLE; + break; + } + + if (left > 8192) + left = 8192; + + if (left > tcp_mss(pcb)) { + sndlength = tcp_mss(pcb); + } else { + sndlength = left; + } + + memcpy(mbuf, fb->fbaddr + state->update_pos, sndlength); /* It's OK if it becomes smaller later. */ + + do { + err = tcp_write(pcb, mbuf, sndlength, TCP_WRITE_FLAG_COPY /* This is my memory on the stack, thank you very much. */); + if (err == ERR_MEM) { + outputf("RFB: ERR_MEM sending %d", sndlength); + sndlength /= 2; + } + } while (err == ERR_MEM && sndlength > 1); + + if (err == ERR_OK) { + outputf("RFB: attempting send %d", sndlength); + } else { + outputf("RFB: send error %d", err); + break; + } + + state->update_pos += sndlength; + + 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 +315,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; @@ -253,7 +364,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)); @@ -350,8 +461,9 @@ static err_t rfb_recv(void *arg, struct tcp_pcb *pcb, outputf("RFB FSM: ok"); /* Might as well send now... */ - if (state->update_allowed && !state->sending) { - start_send(pcb, state); + if (state->send_state == SST_IDLE + && state->update_requested) { + send_fsm(pcb, state); } if (state->readpos == state->writepos) { @@ -384,8 +496,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 +511,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);