|
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 } |