--- /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;
+}