render_remote.c
changeset 2 69f8c0acaac7
child 3 675be0a45157
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/render_remote.c	Sat May 31 19:35:21 2008 +0300
@@ -0,0 +1,203 @@
+#include <stdlib.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "render_remote.h"
+#include "common.h"
+
+struct remote_render_ctx {
+    struct event ev_conn;
+    struct bufferevent *data_bev;
+
+    #pragma pack(push)
+    #pragma pack(1)
+
+    struct {
+        u_int8_t    mode;
+
+        u_int32_t   img_w;
+        u_int32_t   img_h;
+
+        double      x1;
+        double      y1;
+        double      x2;
+        double      y2;
+    } render_cmd;
+
+    #pragma pack(pop)
+
+    void (*cb_sent)(void *arg);
+    void (*cb_data)(struct evbuffer *buf, void *arg);
+    void (*cb_done)(void *arg);
+    void (*cb_fail)(void *arg);
+
+    void *cb_arg;
+};
+
+void _remote_render_ctx_free (struct remote_render_ctx **ctx) {
+    // free the data_bev
+    if ((*ctx)->data_bev) {
+        bufferevent_free((*ctx)->data_bev);
+        (*ctx)->data_bev = NULL;
+    }
+    
+    // free the context structure
+    free(*ctx);
+    
+    *ctx = NULL;
+}
+
+#define RENDER_FAILED(ctx, desc) \
+    do {                                        \
+        perror(desc);                           \
+        ctx->cb_fail(ctx->cb_arg);              \
+        _remote_render_ctx_free(&ctx);          \
+    } while (0)
+
+void _remote_write (struct bufferevent *bev, void *arg) {
+    struct remote_render_ctx *ctx = arg;
+
+    // the write buffer was drained, so the render command was sent
+    ctx->cb_sent(ctx->cb_arg);
+    
+    // we don't care about EV_WRITE anymore
+    if (bufferevent_disable(ctx->data_bev, EV_WRITE))
+        RENDER_FAILED(ctx, "render_remote: bufferevent_disable");
+
+    // start receiving data
+    if (bufferevent_enable(ctx->data_bev, EV_READ))
+        RENDER_FAILED(ctx, "render_remote: bufferevent_enable");
+}
+
+void _remote_read (struct bufferevent *bev, void *arg) {
+    struct remote_render_ctx *ctx = arg;
+    
+    // pass the bufferevent's input buffer to our callback - libevent doesn't provide any function to access this, but hopefully this works correctly
+    ctx->cb_data(bev->input, ctx->cb_arg);
+}
+
+void _remote_error (struct bufferevent *bev, short what, void *arg) {
+    struct remote_render_ctx *ctx = arg;
+
+    // OH NOES; WHAT DO WE DO!?
+    
+    if (what & EVBUFFER_EOF) {
+        // great!
+        ctx->cb_done(ctx->cb_arg);
+
+    } else if (what & EVBUFFER_ERROR) {
+        // crap.
+        perr("render_remote");
+
+        ctx->cb_fail(ctx->cb_arg);
+
+    } else if (what & EVBUFFER_TIMEOUT) {
+        // ah well
+        error("render_remote: timeout");
+
+        ctx->cb_fail(ctx->cb_arg);
+
+    } else {
+        err_exit("weird bufferevent error code: 0x%02X", what);
+    }
+
+    // free resources
+    _remote_render_ctx_free(&ctx);
+}
+
+void _remote_connected (int fd, short event, void *arg) {
+    struct remote_render_ctx *ctx = arg;
+
+    // set up the read/write bufferevent
+    if ((ctx->data_bev = bufferevent_new(fd, &_remote_read, &_remote_write, &_remote_error, ctx)) == NULL)
+        RENDER_FAILED(ctx, "render_remote: bufferevent_new");
+
+    // write the render command
+    if (bufferevent_write(ctx->data_bev, &ctx->render_cmd, sizeof(ctx->render_cmd)))
+        RENDER_FAILED(ctx, "render_remote: bufferevent_write");
+
+    // wait for it to be written out
+    if (bufferevent_enable(ctx->data_bev, EV_WRITE))
+        RENDER_FAILED(ctx, "render_remote: bufferevent_enable");
+}
+
+void render_cmd_build (render_t *rctx, struct remote_render_ctx *rrctx) {
+    // just copy over the render params to the render_cmd
+    rrctx->render_cmd.mode = rctx->mode;
+    rrctx->render_cmd.img_w = htonl(rctx->img_w);
+    rrctx->render_cmd.img_h = htonl(rctx->img_h);
+    rrctx->render_cmd.x1 = rctx->x1;
+    rrctx->render_cmd.y1 = rctx->y1;
+    rrctx->render_cmd.x2 = rctx->x2;
+    rrctx->render_cmd.y2 = rctx->y2;
+}
+
+int render_remote (
+        render_t *render_ctx,
+        struct sockaddr_storage *remote,
+        void (*cb_sent)(void *arg),
+        void (*cb_data)(struct evbuffer *buf, void *arg),
+        void (*cb_done)(void *arg),
+        void (*cb_fail)(void *arg),
+        void *cb_arg
+) {    
+    // alloc the remote render ctx
+    struct remote_render_ctx *ctx = malloc(sizeof(struct remote_render_ctx));
+
+    if (!ctx) {
+        error("render_remote: malloc");
+        return -1;
+    }
+    
+    // store the provided callback functions
+    ctx->cb_sent = cb_sent;
+    ctx->cb_data = cb_data;
+    ctx->cb_done = cb_done;
+    ctx->cb_fail = cb_fail;
+    ctx->cb_arg = cb_arg;
+    
+    // copy the relevant stuff from the render_ctx
+    render_cmd_build(render_ctx, ctx);
+    
+    // create the socket
+    int sock = socket(remote->ss_family, SOCK_STREAM, 0);
+
+    if (sock < 0) {
+        free(ctx);
+        perror("render_remote: socket");
+        return -1;
+    }
+
+    // mark it as nonblocking
+    if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
+        free(ctx);
+        close(sock);
+        perror("render_remote: fcntl");
+        return -1;
+    }
+    
+    // initiate the connect
+    int err = connect(sock, (struct sockaddr *) remote, sizeof(*remote));
+
+    if (err != -1 || errno != EINPROGRESS) {
+        free(ctx);
+        close(sock);
+        perror("render_remote: connect");
+        return -1;
+    }
+
+    // do the libevent dance
+    event_set(&ctx->ev_conn, sock, EV_WRITE, &_remote_connected, ctx);
+
+    if (event_add(&ctx->ev_conn, NULL)) {
+        free(ctx);
+        close(sock);
+        error("render_remote: event_add");
+        return -1;
+    }
+    
+    // success
+    return 0;
+}