render_remote.c
changeset 2 69f8c0acaac7
child 3 675be0a45157
equal deleted inserted replaced
1:6aa1a0d1f88d 2:69f8c0acaac7
       
     1 #include <stdlib.h>
       
     2 #include <arpa/inet.h>
       
     3 #include <unistd.h>
       
     4 #include <fcntl.h>
       
     5 #include <errno.h>
       
     6 
       
     7 #include "render_remote.h"
       
     8 #include "common.h"
       
     9 
       
    10 struct remote_render_ctx {
       
    11     struct event ev_conn;
       
    12     struct bufferevent *data_bev;
       
    13 
       
    14     #pragma pack(push)
       
    15     #pragma pack(1)
       
    16 
       
    17     struct {
       
    18         u_int8_t    mode;
       
    19 
       
    20         u_int32_t   img_w;
       
    21         u_int32_t   img_h;
       
    22 
       
    23         double      x1;
       
    24         double      y1;
       
    25         double      x2;
       
    26         double      y2;
       
    27     } render_cmd;
       
    28 
       
    29     #pragma pack(pop)
       
    30 
       
    31     void (*cb_sent)(void *arg);
       
    32     void (*cb_data)(struct evbuffer *buf, void *arg);
       
    33     void (*cb_done)(void *arg);
       
    34     void (*cb_fail)(void *arg);
       
    35 
       
    36     void *cb_arg;
       
    37 };
       
    38 
       
    39 void _remote_render_ctx_free (struct remote_render_ctx **ctx) {
       
    40     // free the data_bev
       
    41     if ((*ctx)->data_bev) {
       
    42         bufferevent_free((*ctx)->data_bev);
       
    43         (*ctx)->data_bev = NULL;
       
    44     }
       
    45     
       
    46     // free the context structure
       
    47     free(*ctx);
       
    48     
       
    49     *ctx = NULL;
       
    50 }
       
    51 
       
    52 #define RENDER_FAILED(ctx, desc) \
       
    53     do {                                        \
       
    54         perror(desc);                           \
       
    55         ctx->cb_fail(ctx->cb_arg);              \
       
    56         _remote_render_ctx_free(&ctx);          \
       
    57     } while (0)
       
    58 
       
    59 void _remote_write (struct bufferevent *bev, void *arg) {
       
    60     struct remote_render_ctx *ctx = arg;
       
    61 
       
    62     // the write buffer was drained, so the render command was sent
       
    63     ctx->cb_sent(ctx->cb_arg);
       
    64     
       
    65     // we don't care about EV_WRITE anymore
       
    66     if (bufferevent_disable(ctx->data_bev, EV_WRITE))
       
    67         RENDER_FAILED(ctx, "render_remote: bufferevent_disable");
       
    68 
       
    69     // start receiving data
       
    70     if (bufferevent_enable(ctx->data_bev, EV_READ))
       
    71         RENDER_FAILED(ctx, "render_remote: bufferevent_enable");
       
    72 }
       
    73 
       
    74 void _remote_read (struct bufferevent *bev, void *arg) {
       
    75     struct remote_render_ctx *ctx = arg;
       
    76     
       
    77     // pass the bufferevent's input buffer to our callback - libevent doesn't provide any function to access this, but hopefully this works correctly
       
    78     ctx->cb_data(bev->input, ctx->cb_arg);
       
    79 }
       
    80 
       
    81 void _remote_error (struct bufferevent *bev, short what, void *arg) {
       
    82     struct remote_render_ctx *ctx = arg;
       
    83 
       
    84     // OH NOES; WHAT DO WE DO!?
       
    85     
       
    86     if (what & EVBUFFER_EOF) {
       
    87         // great!
       
    88         ctx->cb_done(ctx->cb_arg);
       
    89 
       
    90     } else if (what & EVBUFFER_ERROR) {
       
    91         // crap.
       
    92         perr("render_remote");
       
    93 
       
    94         ctx->cb_fail(ctx->cb_arg);
       
    95 
       
    96     } else if (what & EVBUFFER_TIMEOUT) {
       
    97         // ah well
       
    98         error("render_remote: timeout");
       
    99 
       
   100         ctx->cb_fail(ctx->cb_arg);
       
   101 
       
   102     } else {
       
   103         err_exit("weird bufferevent error code: 0x%02X", what);
       
   104     }
       
   105 
       
   106     // free resources
       
   107     _remote_render_ctx_free(&ctx);
       
   108 }
       
   109 
       
   110 void _remote_connected (int fd, short event, void *arg) {
       
   111     struct remote_render_ctx *ctx = arg;
       
   112 
       
   113     // set up the read/write bufferevent
       
   114     if ((ctx->data_bev = bufferevent_new(fd, &_remote_read, &_remote_write, &_remote_error, ctx)) == NULL)
       
   115         RENDER_FAILED(ctx, "render_remote: bufferevent_new");
       
   116 
       
   117     // write the render command
       
   118     if (bufferevent_write(ctx->data_bev, &ctx->render_cmd, sizeof(ctx->render_cmd)))
       
   119         RENDER_FAILED(ctx, "render_remote: bufferevent_write");
       
   120 
       
   121     // wait for it to be written out
       
   122     if (bufferevent_enable(ctx->data_bev, EV_WRITE))
       
   123         RENDER_FAILED(ctx, "render_remote: bufferevent_enable");
       
   124 }
       
   125 
       
   126 void render_cmd_build (render_t *rctx, struct remote_render_ctx *rrctx) {
       
   127     // just copy over the render params to the render_cmd
       
   128     rrctx->render_cmd.mode = rctx->mode;
       
   129     rrctx->render_cmd.img_w = htonl(rctx->img_w);
       
   130     rrctx->render_cmd.img_h = htonl(rctx->img_h);
       
   131     rrctx->render_cmd.x1 = rctx->x1;
       
   132     rrctx->render_cmd.y1 = rctx->y1;
       
   133     rrctx->render_cmd.x2 = rctx->x2;
       
   134     rrctx->render_cmd.y2 = rctx->y2;
       
   135 }
       
   136 
       
   137 int render_remote (
       
   138         render_t *render_ctx,
       
   139         struct sockaddr_storage *remote,
       
   140         void (*cb_sent)(void *arg),
       
   141         void (*cb_data)(struct evbuffer *buf, void *arg),
       
   142         void (*cb_done)(void *arg),
       
   143         void (*cb_fail)(void *arg),
       
   144         void *cb_arg
       
   145 ) {    
       
   146     // alloc the remote render ctx
       
   147     struct remote_render_ctx *ctx = malloc(sizeof(struct remote_render_ctx));
       
   148 
       
   149     if (!ctx) {
       
   150         error("render_remote: malloc");
       
   151         return -1;
       
   152     }
       
   153     
       
   154     // store the provided callback functions
       
   155     ctx->cb_sent = cb_sent;
       
   156     ctx->cb_data = cb_data;
       
   157     ctx->cb_done = cb_done;
       
   158     ctx->cb_fail = cb_fail;
       
   159     ctx->cb_arg = cb_arg;
       
   160     
       
   161     // copy the relevant stuff from the render_ctx
       
   162     render_cmd_build(render_ctx, ctx);
       
   163     
       
   164     // create the socket
       
   165     int sock = socket(remote->ss_family, SOCK_STREAM, 0);
       
   166 
       
   167     if (sock < 0) {
       
   168         free(ctx);
       
   169         perror("render_remote: socket");
       
   170         return -1;
       
   171     }
       
   172 
       
   173     // mark it as nonblocking
       
   174     if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
       
   175         free(ctx);
       
   176         close(sock);
       
   177         perror("render_remote: fcntl");
       
   178         return -1;
       
   179     }
       
   180     
       
   181     // initiate the connect
       
   182     int err = connect(sock, (struct sockaddr *) remote, sizeof(*remote));
       
   183 
       
   184     if (err != -1 || errno != EINPROGRESS) {
       
   185         free(ctx);
       
   186         close(sock);
       
   187         perror("render_remote: connect");
       
   188         return -1;
       
   189     }
       
   190 
       
   191     // do the libevent dance
       
   192     event_set(&ctx->ev_conn, sock, EV_WRITE, &_remote_connected, ctx);
       
   193 
       
   194     if (event_add(&ctx->ev_conn, NULL)) {
       
   195         free(ctx);
       
   196         close(sock);
       
   197         error("render_remote: event_add");
       
   198         return -1;
       
   199     }
       
   200     
       
   201     // success
       
   202     return 0;
       
   203 }