--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Makefile Fri May 30 14:24:23 2008 +0300
@@ -0,0 +1,9 @@
+LDFLAGS = -L/usr/local/lib -levent -lpng
+CFLAGS = -Wall -g
+
+render_file: mandelbrot.o common.o
+
+web_main: mandelbrot.o common.o
+
+render_node: mandelbrot.o common.o
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/common.c Fri May 30 14:24:23 2008 +0300
@@ -0,0 +1,44 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <string.h>
+
+#include "common.h"
+
+void die (const char *msg) {
+ perror(msg);
+ exit(EXIT_FAILURE);
+}
+
+void error (const char *fmt, ...) {
+ va_list va;
+
+ va_start(va, fmt);
+ vfprintf(stderr, fmt, va);
+ va_end(va);
+
+ fprintf(stderr, "\n");
+}
+
+void perr_exit (const char *fmt, ...) {
+ va_list va;
+
+ va_start(va, fmt);
+ vfprintf(stderr, fmt, va);
+ va_end(va);
+
+ fprintf(stderr, ": %s\n", strerror(errno));
+ exit(EXIT_FAILURE);
+}
+
+void err_exit (const char *fmt, ...) {
+ va_list va;
+
+ va_start(va, fmt);
+ vfprintf(stderr, fmt, va);
+ va_end(va);
+
+ fprintf(stderr, "\n");
+ exit(EXIT_FAILURE);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/common.h Fri May 30 14:24:23 2008 +0300
@@ -0,0 +1,6 @@
+void die (const char *msg);
+void error (const char *fmt, ...);
+void perr_exit (const char *fmt, ...);
+void err_exit (const char *fmt, ...);
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mandelbrot.c Fri May 30 14:24:23 2008 +0300
@@ -0,0 +1,224 @@
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include <arpa/inet.h>
+
+#include <png.h>
+
+#include "mandelbrot.h"
+#include "common.h"
+
+
+#define DETAIL 255
+
+#define absdelta(a, b) (a>b ? a-b : b-a)
+
+void render_ctx_set (struct render_ctx *ctx, render_ctx_write_cb write_fn, render_ctx_flush_cb flush_fn, void *arg) {
+ memset(ctx, 0, sizeof(*ctx));
+
+ ctx->write_fn = write_fn;
+ ctx->flush_fn = flush_fn;
+ ctx->cb_arg = arg;
+}
+
+void render_ctx_stream (struct render_ctx *ctx, FILE *fh) {
+ memset(ctx, 0, sizeof(*ctx));
+
+ ctx->stream = fh;
+}
+
+void user_write_data(png_structp png_ptr, png_bytep data, png_size_t length) {
+ struct render_ctx *ctx = (struct render_ctx *) png_get_io_ptr(png_ptr);
+
+ if (ctx->write_fn)
+ switch (ctx->write_fn(data, length, ctx->cb_arg)) {
+ case RENDER_CB_ERR :
+ ctx->_error = 1;
+ break;
+
+ case RENDER_CB_OK :
+ // great!
+ break;
+ }
+}
+
+void user_flush_data(png_structp png_ptr) {
+ struct render_ctx *ctx = (struct render_ctx *) png_get_io_ptr(png_ptr);
+
+ if (ctx->flush_fn)
+ switch (ctx->flush_fn(ctx->cb_arg)) {
+ case RENDER_CB_ERR :
+ ctx->_error = 1;
+ break;
+
+ case RENDER_CB_OK :
+ // great!
+ break;
+ }
+}
+
+int _validate_render (
+ u_int32_t img_w, u_int32_t img_h,
+ double x1, double y1, double x2, double y2
+) {
+ if (
+ (img_w == 0 || img_h == 0) // zero-size image
+ || (x1 >= x2 || y1 >= y2) // invalid region
+ )
+ return MANDELBROT_ERR_INVALID;
+ else
+ return MANDELBROT_OK;
+}
+
+
+int mandelbrot_render_region (
+ struct render_ctx *ctx,
+ u_int32_t img_w, u_int32_t img_h,
+ double x1, double y1, double x2, double y2
+) {
+ u_int32_t img_x, img_y;
+ double x0, y0, x, y, _x, _y, w_scale, h_scale;
+ u_int8_t iter;
+ u_int8_t *row;
+
+ // validate!
+ int valid = _validate_render(img_w, img_h, x1, y1, x2, y2);
+
+ if (valid != MANDELBROT_OK)
+ return valid;
+
+ // clear out any potential error in ctx
+ ctx->_error = 0;
+
+ // calcluate the scale factors
+ w_scale = img_w/absdelta(x1, x2);
+ h_scale = img_h/absdelta(y1, y2);
+
+ // malloc the memory used to render each row
+ row = (u_int8_t *) malloc(img_w);
+
+ if (!row)
+ return MANDELBROT_ERR_MALLOC;
+
+ // libpng initialization
+ png_structp png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+
+ if (!png_ptr) {
+ free(row);
+ return MANDELBROT_ERR_PNG_INIT;
+ }
+
+ png_infop info_ptr = png_create_info_struct(png_ptr);
+
+ if (!info_ptr) {
+ png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
+ free(row);
+ return MANDELBROT_ERR_PNG_INIT;
+ }
+
+ if (ctx->stream) {
+ // use normal libpng I/O
+ png_init_io(png_ptr, ctx->stream);
+ } else {
+ // setup our custom I/O callbacks
+ png_set_write_fn(png_ptr, ctx, &user_write_data, &user_flush_data);
+ }
+
+ // some PNG metadata
+ png_set_IHDR(png_ptr, info_ptr, img_w, img_h, 8, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+
+ // write out the PNG header
+ png_write_info(png_ptr, info_ptr);
+
+ // possible error return
+ if (ctx->_error) {
+ free(row);
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ return MANDELBROT_ERR_CB;
+ }
+
+ // start rendering!
+ for (img_y=0; img_y < img_h; img_y++) {
+ // render the current row
+ for (img_x=0; img_x < img_w; img_x++) {
+ x = 0;
+ y = 0;
+ x0 = img_x/w_scale + x1;
+ y0 = img_y/h_scale + y1;
+ iter = DETAIL;
+
+ while (x*x + y*y < (2*2) && iter > 0) {
+ _x = x*x - y*y + x0;
+ _y = 2*x*y + y0;
+
+ x = _x;
+ y = _y;
+
+ iter--;
+ }
+
+ row[img_x] = iter;
+ }
+
+ // write the raw pixels to libpng
+ png_write_row(png_ptr, row);
+
+ // check for user errors return
+ if (ctx->_error) {
+ free(row);
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ return MANDELBROT_ERR_CB;
+ }
+ }
+
+ // finished writing
+ png_write_end(png_ptr, info_ptr);
+
+ // clean up
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ free(row);
+
+ // check for user errors return
+ if (ctx->_error)
+ return MANDELBROT_ERR_CB;
+
+ // return succesfully
+ return MANDELBROT_OK;
+}
+
+int mandelbrot_render_region_timed (
+ struct render_ctx *ctx,
+ u_int32_t img_w, u_int32_t img_h,
+ double x1, double y1, double x2, double y2,
+ double *duration
+) {
+ clock_t t1 = clock();
+ int ret = mandelbrot_render_region(ctx, img_w, img_h, x1, y1, x2, y2);
+ clock_t t2 = clock();
+
+ *duration = ((double)(t2 - t1))/CLOCKS_PER_SEC;
+
+ return ret;
+}
+
+int render_cmd_set (
+ struct render_cmd *cmd,
+ u_int32_t img_w, u_int32_t img_h,
+ double x1, double y1, double x2, double y2
+) {
+ // validate!
+ int valid = _validate_render(img_w, img_h, x1, y1, x2, y2);
+
+ if (valid != MANDELBROT_OK)
+ return valid;
+
+ cmd->img_w = htonl(img_w);
+ cmd->img_h = htonl(img_h);
+ cmd->x1 = x1;
+ cmd->y1 = y1;
+ cmd->x2 = x2;
+ cmd->y2 = y2;
+
+ return MANDELBROT_OK;
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mandelbrot.h Fri May 30 14:24:23 2008 +0300
@@ -0,0 +1,117 @@
+/*
+ * code to render a mandelbrot
+ */
+
+#include <sys/types.h>
+
+typedef int (*render_ctx_write_cb)(const unsigned char *data, size_t length, void *arg);
+typedef int (*render_ctx_flush_cb)(void *arg);
+
+#define RENDER_CB_OK 0
+#define RENDER_CB_ERR 1
+
+struct render_ctx {
+ /*
+ * If we just use normal FILE* stream operations
+ */
+ FILE *stream;
+
+ /*
+ * These callback functions are used to handle the PNG data as it's rendered.
+ *
+ * If they return RENDER_CB_OK, all is fine and rendering will continue normally.
+ * If they return RENDER_CB_ERR, rendering will abort.
+ */
+
+ // called to handle the output data
+ render_ctx_write_cb write_fn;
+
+ // called when the output data should be flushed - can be safely ignored if not needed
+ render_ctx_flush_cb flush_fn;
+
+ // the callback argument
+ void *cb_arg;
+
+ // error status
+ int _error;
+};
+
+/*
+ * initialize the given struct render_ctx
+ */
+void render_ctx_set (struct render_ctx *ctx, render_ctx_write_cb write_fn, render_ctx_flush_cb flush_fn, void *arg);
+void render_ctx_stream (struct render_ctx *ctx, FILE *fh);
+
+/*
+ * return codes for mandelbrot_render
+ */
+#define MANDELBROT_OK 0
+#define MANDELBROT_ERR_MALLOC 1
+#define MANDELBROT_ERR_PNG_INIT 2
+#define MANDELBROT_ERR_CB 3
+#define MANDELBROT_ERR_INVALID 4
+
+/*
+ * render an <img_w>x<img_h> PNG image of the mandelbrot region bounded by (x1, y1) -> (x2, y2)
+ */
+int mandelbrot_render_region (
+ struct render_ctx *ctx,
+ u_int32_t img_w, u_int32_t img_h,
+ double x1, double y1, double x2, double y2
+);
+
+int mandelbrot_render_region_timed (
+ struct render_ctx *ctx,
+ u_int32_t img_w, u_int32_t img_h,
+ double x1, double y1, double x2, double y2,
+ double *duration
+);
+
+/*
+ * remote rendering
+ */
+
+#pragma pack(push)
+#pragma pack(1)
+
+struct render_cmd {
+ u_int32_t img_w;
+ u_int32_t img_h;
+
+ double x1;
+ double y1;
+ double x2;
+ double y2;
+};
+
+#pragma pack(pop)
+
+#define RENDER_PORT 6159
+#define RENDER_CMD_SIZE sizeof(struct render_cmd)
+
+/*
+ * build the command to send for remote rendering
+ */
+int render_cmd_set (
+ struct render_cmd *cmd,
+ u_int32_t img_w, u_int32_t img_h,
+ double x1, double y1, double x2, double y2
+);
+
+/*
+ * The "full" mandelbrot region
+ */
+#define REGION_X1 -2.0
+#define REGION_Y1 -1.5
+#define REGION_X2 1.0
+#define REGION_Y2 1.5
+
+#define mandelbrot_render_full(ctx, img_w, img_h) \
+ mandelbrot_render_region(ctx, img_w, img_h, REGION_X1, REGION_Y1, REGION_X2, REGION_Y2)
+
+#define mandelbrot_render_full_timed(ctx, img_w, img_h, duration) \
+ mandelbrot_render_region_timed(ctx, img_w, img_h, REGION_X1, REGION_Y1, REGION_X2, REGION_Y2, duration)
+
+#define render_cmd_set_full(cmd, img_w, img_h) \
+ render_cmd_set(cmd, img_w, img_h, REGION_X1, REGION_Y1, REGION_X2, REGION_Y2)
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/render_file.c Fri May 30 14:24:23 2008 +0300
@@ -0,0 +1,173 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include "mandelbrot.h"
+#include "common.h"
+
+static int verbose;
+
+int file_write (const unsigned char *data, size_t length, void *arg) {
+ size_t ret = fwrite(data, length, 1, (FILE *) arg);
+
+ return (ret == 1) ? RENDER_CB_OK : RENDER_CB_ERR;
+}
+
+void render_local (int img_w, int img_h, FILE *output) {
+ struct render_ctx ctx;
+
+ render_ctx_set(&ctx, &file_write, NULL, output);
+
+ double duration;
+
+ if (mandelbrot_render_full_timed(
+ &ctx,
+ img_w, img_h,
+ &duration
+ ) != MANDELBROT_OK)
+ err_exit("mandelbrot_render_region failed");
+
+ if (verbose)
+ fprintf(stdout, "rendered %dx%d mandelbrot in %f seconds\n", img_w, img_h, duration);
+}
+
+void render_remote (int img_w, int img_h, FILE *output, FILE *remote) {
+ struct render_cmd cmd;
+
+ if (render_cmd_set_full(&cmd, img_w, img_h) != MANDELBROT_OK) {
+ err_exit("mandelbrot_render_remote failed");
+ }
+
+ int ret = fwrite((void *) &cmd, sizeof(cmd), 1, remote);
+
+ if (ret != 1)
+ perr_exit("fwrite(remote)");
+
+ if (verbose)
+ printf("sent render command of %zu bytes\n", sizeof(cmd));
+
+ // shuffle data around
+ while (!feof(remote)) {
+ char buf[1024];
+
+ int bytes = fread(buf, 1, sizeof(buf), remote);
+
+ if (bytes)
+ if (fwrite(buf, bytes, 1, output) != 1)
+ perr_exit("fwrite(output)");
+ }
+
+ if (verbose)
+ printf("got PNG data\n");
+
+ if (ferror(remote))
+ perr_exit("fread(remote)");
+}
+
+FILE *open_remote (char *remote) {
+ char *addr_str, *port_str = NULL;
+
+ addr_str = strsep(&remote, ":");
+
+ if (remote)
+ port_str = remote;
+
+ struct sockaddr_in addr;
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port_str ? atoi(port_str) : RENDER_PORT);
+
+ if (inet_aton(addr_str, &addr.sin_addr) == 0)
+ err_exit("invalid address: %s", addr_str);
+
+ int sock;
+
+ if ((sock = socket(PF_INET, SOCK_STREAM, 0)) == -1)
+ perr_exit("socket");
+
+ if (verbose)
+ printf("connect(%s, %d)\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
+
+ if (connect(sock, (const struct sockaddr *) &addr, sizeof(addr)) == -1)
+ perr_exit("connect(%s, %d)", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
+
+ FILE *fh = fdopen(sock, "w+");
+
+ if (!fh || ferror(fh))
+ perr_exit("fdopen");
+
+ setbuffer(fh, NULL, 0);
+
+ return fh;
+}
+
+int main (int argc, char **argv) {
+ int opt;
+
+ FILE *output = NULL, *remote = NULL;
+ int img_w = 256, img_h = 256;
+
+
+ while ((opt = getopt(argc, argv, "w:h:o:vr:")) != -1) {
+ switch(opt) {
+ case 'w' :
+ img_w = atoi(optarg);
+ break;
+
+ case 'h' :
+ img_h = atoi(optarg);
+ break;
+
+ case 'o' :
+ if (output)
+ err_exit("Only use -o once");
+
+ output = fopen(optarg, "w");
+
+ if (!output)
+ die(optarg);
+
+ break;
+
+ case 'v' :
+ verbose = 1;
+ break;
+
+ case 'r' :
+ if (remote)
+ err_exit("Only use -r once");
+
+ remote = open_remote(optarg);
+
+ break;
+
+ default :
+ err_exit("Usage: %s [-w img_w] [-h img_h] [-o output_file] [-v] [-r host[:port]]", argv[0]);
+ }
+ }
+
+ if (!output)
+ output = stdout;
+
+ if (remote) {
+ if (verbose)
+ fprintf(stderr, "Render [%dx%d] mandelbrot remotely\n", img_w, img_h);
+
+ render_remote(img_w, img_h, output, remote);
+
+ fclose(remote);
+ } else {
+ if (verbose)
+ fprintf(stderr, "Render [%dx%d] mandelbrot locally\n", img_w, img_h);
+
+ render_local(img_w, img_h, output);
+ }
+
+ fclose(output);
+
+ return 0;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/render_node.c Fri May 30 14:24:23 2008 +0300
@@ -0,0 +1,127 @@
+#include <stdio.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <stdlib.h>
+
+#include "mandelbrot.h"
+#include "common.h"
+
+int read_int (FILE *fh, u_int32_t *i) {
+ printf("read_int: fread(%p, %zu, 1, %p)... ", i, sizeof(*i), fh);
+ fflush(stdout);
+ int ret = fread(i, sizeof(*i), 1, fh);
+ printf("ok: %d\n", ret);
+
+ if (ret == 0) {
+ error("EOF");
+ return 0;
+ } else if (ret != 1) {
+ perror("read_int");
+ return 0;
+ }
+
+ *i = ntohl(*i);
+
+ printf("read_int: %i\n", *i);
+
+ return 1;
+}
+
+int read_double (FILE *fh, double *dbl) {
+ int ret = fread(dbl, sizeof(*dbl), 1, fh);
+
+ if (ret != 1) {
+ perror("read_double");
+ return 0;
+ }
+
+ printf("read_double: %f\n", *dbl);
+
+ return 1;
+}
+
+void handle_client (int sock) {
+ // open it as a FILE*
+ FILE *fh = fdopen(sock, "r+");
+
+ // read the parameters
+ u_int32_t img_w, img_h;
+ double x1, y1, x2, y2;
+
+ if (
+ !read_int(fh, &img_w) ||
+ !read_int(fh, &img_h) ||
+ !read_double(fh, &x1) ||
+ !read_double(fh, &y1) ||
+ !read_double(fh, &x2) ||
+ !read_double(fh, &y2)
+ ) {
+ error("ERR: invalid arguments");
+ fclose(fh);
+ return;
+ }
+
+ printf("RENDER: [%ux%u] (%f, %f) -> (%f, %f): ", img_w, img_h, x1, y1, x2, y2);
+
+ double duration;
+
+ // set up the render_ctx
+ struct render_ctx ctx;
+ render_ctx_stream(&ctx, fh);
+
+ // render!
+ mandelbrot_render_region_timed(
+ &ctx,
+ img_w, img_h,
+ x1, y1, x2, y2,
+ &duration
+ );
+
+ printf("time=%fs\n", duration);
+
+ // close the FILE* and socket
+ fclose(fh);
+
+ return;
+}
+
+int main (int argc, char** argv) {
+ int ssock, sock;
+ struct sockaddr_in addr;
+ socklen_t addr_len;
+
+ // create the socket
+ if ((ssock = socket(PF_INET, SOCK_STREAM, 0)) == -1)
+ die("socket");
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(RENDER_PORT);
+ addr.sin_addr.s_addr = INADDR_ANY;
+
+ if (argc > 1)
+ addr.sin_port = htons(atoi(argv[1]));
+
+ if (bind(ssock, (struct sockaddr *) &addr, sizeof(struct sockaddr_in)) == -1)
+ die("bind");
+
+ if (listen(ssock, 1) == -1)
+ die("listen");
+
+ printf("RUN: %s:%hu\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
+
+ // main accept loop
+ while (1) {
+ addr_len = sizeof(struct sockaddr_in);
+
+ // accept a new client
+ if ((sock = accept(ssock, (struct sockaddr *) &addr, &addr_len)) == -1)
+ die("accept");
+
+ printf("ACCEPT: %s:%hu\n", inet_ntoa(addr.sin_addr), addr.sin_port);
+
+ // handle their resquest
+ handle_client(sock);
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web_main.c Fri May 30 14:24:23 2008 +0300
@@ -0,0 +1,123 @@
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <event.h>
+#include <evhttp.h>
+
+#include "mandelbrot.h"
+#include "common.h"
+
+struct http_render_state {
+ struct evbuffer *buf;
+
+} _http_render_state = { NULL };
+
+int _http_render_cb (const unsigned char *data, size_t length, void *arg) {
+
+ // create a buffer
+ struct evbuffer *buf = evbuffer_new();
+
+ if (!buf) {
+ error("_http_render_cb: evbuffer_new failed");
+ return RENDER_CB_ERR;
+ }
+
+ // add the data to it
+ if (evbuffer_add(buf, data, length)) {
+ error("_http_render_cb: evbuffer_add(%d) failed", length);
+ return RENDER_CB_ERR;
+ }
+
+ // send it
+ evhttp_send_reply_chunk((struct evhttp_request *) arg, buf);
+
+ printf("_http_render_cb: sent chunk: %zu\n", length);
+
+ // free the buffer (!!!)
+ evbuffer_free(buf);
+
+ // return ok
+ return RENDER_CB_OK;
+}
+
+void http_render (struct evhttp_request *request, void *arg) {
+ // gather some info about the request
+ const char *uri = evhttp_request_uri(request);
+ char *peer_address;
+ u_short peer_port;
+
+ evhttp_connection_get_peer(request->evcon, &peer_address, &peer_port);
+
+ // request arguments
+ u_int32_t img_w = 256, img_h = 256;
+ struct evkeyval *qarg;
+ struct evkeyvalq qargs;
+
+ evhttp_parse_query(uri, &qargs);
+
+ TAILQ_FOREACH(qarg, &qargs, next) {
+ if (strcmp(qarg->key, "w") == 0)
+ img_w = strtol(qarg->value, NULL, 10);
+ else if (strcmp(qarg->key, "h") == 0)
+ img_h = strtol(qarg->value, NULL, 10);
+ }
+
+ // clean up the qargs (badly named functions :< )
+ evhttp_clear_headers(&qargs);
+
+ // request log
+ printf("REQ: [%s:%d] uri=%s, img_w=%d, img_h=%d\n", peer_address, peer_port, uri, img_w, img_h);
+
+ // set headers
+ evhttp_add_header(request->output_headers, "Content-Type", "image/png");
+ evhttp_send_reply_start(request, HTTP_OK, "OK");
+
+ // render
+ struct render_ctx ctx;
+ render_ctx_set(&ctx, &_http_render_cb, NULL, request);
+
+ int err = mandelbrot_render_full(&ctx, img_w, img_h);
+
+ if (err) {
+ fprintf(stderr, "ERR: mandelbrot_render_full(%d, %d): %d\n", img_w, img_h, err);
+ }
+
+ // reply end
+ evhttp_send_reply_end(request);
+}
+
+int main (void) {
+ // libevent/http init
+ struct event_base *ev_base = event_init();
+
+ if (!ev_base)
+ die("event_init");
+
+ struct evhttp *http_server = evhttp_new(ev_base);
+
+ if (!http_server)
+ die("evhttp_new");
+
+ // bind to the correct interface/port
+ if (evhttp_bind_socket(http_server, "0.0.0.0", 8117))
+ die("evhttp_bind_socket");
+
+ // add our http request handler
+ evhttp_set_cb(http_server, "/render", &http_render, NULL);
+
+ // we shall now run
+ printf("RUN 0.0.0.0:8117\n");
+
+ // run the libevent mainloop
+ if (event_dispatch())
+ die("event_dispatch");
+
+ // clean up
+ evhttp_free(http_server);
+
+ // successfull exit
+ return 0;
+}