diff options
Diffstat (limited to 'src/lib/libkogata/gip.c')
-rw-r--r-- | src/lib/libkogata/gip.c | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/src/lib/libkogata/gip.c b/src/lib/libkogata/gip.c new file mode 100644 index 0000000..3b6084c --- /dev/null +++ b/src/lib/libkogata/gip.c @@ -0,0 +1,207 @@ +#include <string.h> +#include <malloc.h> + +#include <gip.h> + +typedef struct { + gip_reply_callback_t cb; + void* data; +} gip_cmd_t; + +void giph_msg_header(mainloop_fd_t *fd); +void giph_buffer_info(mainloop_fd_t *fd); +void giph_mode_info(mainloop_fd_t *fd); +void giph_buffer_damage(mainloop_fd_t *fd); + +void gip_error(mainloop_fd_t *fd); + +gip_handler_t *new_gip_handler(gip_handler_callbacks_t *cb, void* data) { + gip_handler_t *h = (gip_handler_t*)malloc(sizeof(gip_handler_t)); + if (h == 0) return 0; + + memset(h, 0, sizeof(gip_handler_t)); + + h->cb = cb; + h->data = data; + + h->requests_in_progress = create_hashtbl(id_key_eq_fun, id_hash_fun, free_val); + if (h->requests_in_progress == 0) { + free(h); + return 0; + } + + h->mainloop_item.data = h; + h->mainloop_item.on_error = &gip_error; + h->next_req_id = 1; + + mainloop_expect(&h->mainloop_item, &h->msg_buf, sizeof(gip_msg_header), giph_msg_header); + + return h; +} + +void delete_gip_handler(gip_handler_t *h) { + delete_hashtbl(h->requests_in_progress); + free(h); +} + +bool gip_send_msg(gip_handler_t *h, gip_msg_header *msg, void* msg_data) { + // ---- Write message + size_t extra_size = 0; + if (msg->code == GIPN_BUFFER_INFO) extra_size = sizeof(gip_buffer_info_msg); + if (msg->code == GIPR_MODE_INFO) extra_size = sizeof(gip_mode_info_msg); + if (msg->code == GIPN_BUFFER_DAMAGE) extra_size = sizeof(gip_buffer_damage_msg); + + bool ok = false; + + char* buf = (char*)malloc(sizeof(gip_msg_header) + extra_size); + + if (buf != 0) { + memcpy(buf, msg, sizeof(gip_msg_header)); + if (extra_size) memcpy(buf + sizeof(gip_msg_header), msg_data, extra_size); + ok = mainloop_nonblocking_write(&h->mainloop_item, buf, sizeof(gip_msg_header) + extra_size, true); + } + + if (!ok) { + dbg_printf("GIP warning: failed to send message (type %d)\n", msg->code); + if (buf) free(buf); + } + + return ok; +} + +bool gip_cmd(gip_handler_t *h, gip_msg_header *msg, void* msg_data, gip_reply_callback_t cb, void* cb_data) { + msg->req_id = 0; + + // ---- Add callback handler + if (cb != 0) { + gip_cmd_t *c = (gip_cmd_t*)malloc(sizeof(gip_cmd_t)); + if (c == 0) return false; + + c->cb = cb; + c->data = cb_data; + + while(msg->req_id == 0) msg->req_id = (h->next_req_id++); + + bool add_ok = hashtbl_add(h->requests_in_progress, (void*)msg->req_id, c); + if (!add_ok) { + free(c); + return false; + } + } + + bool ok = gip_send_msg(h, msg, msg_data); + if (!ok && msg->req_id != 0) hashtbl_remove(h->requests_in_progress, (void*)msg->req_id); + + return ok; +} + +bool gip_reply(gip_handler_t *h, gip_msg_header *orig_request, gip_msg_header *msg, void* msg_data) { + msg->req_id = orig_request->req_id; + return gip_send_msg(h, msg, msg_data); +} + +bool gip_reply_fail(gip_handler_t *h, gip_msg_header *o) { + gip_msg_header m = { + .code = GIPR_FAILURE, + .arg = 0, + }; + return gip_reply(h, o, &m, 0); +} + +bool gip_reply_ok(gip_handler_t *h, gip_msg_header *o) { + gip_msg_header m = { + .code = GIPR_OK, + .arg = 0, + }; + return gip_reply(h, o, &m, 0); +} + +bool gip_notify(gip_handler_t *h, gip_msg_header *msg, void* msg_data) { + msg->req_id = 0; + return gip_send_msg(h, msg, msg_data); +} + +// ---- Message handlers + +void giph_got_reply(gip_handler_t *h, gip_msg_header *msg) { + gip_cmd_t *c = (gip_cmd_t*)hashtbl_find(h->requests_in_progress, (void*)msg->req_id);; + if (c != 0) { + c->cb(h, msg, c->data); + hashtbl_remove(h->requests_in_progress, (void*)msg->req_id); + } +} + +void giph_msg_header(mainloop_fd_t *fd) { + gip_handler_t *h = (gip_handler_t*)fd->data; + + int code = h->msg_buf.code; + noarg_gip_callback_t use_cb = 0; + if (code == GIPC_RESET) { + use_cb = h->cb->reset; + } else if (code == GIPR_INITIATE) { + use_cb = h->cb->initiate; + giph_got_reply(h, &h->msg_buf); + } else if (code == GIPR_OK) { + use_cb = h->cb->ok; + giph_got_reply(h, &h->msg_buf); + } else if (code == GIPR_FAILURE) { + use_cb = h->cb->failure; + giph_got_reply(h, &h->msg_buf); + } else if (code == GIPC_ENABLE_FEATURES) { + use_cb = h->cb->enable_features; + } else if (code == GIPC_DISABLE_FEATURES) { + use_cb = h->cb->disable_features; + } else if (code == GIPC_QUERY_MODE) { + use_cb = h->cb->query_mode; + } else if (code == GIPC_SET_MODE) { + use_cb = h->cb->set_mode; + } else if (code == GIPC_SWITCH_BUFFER) { + use_cb = h->cb->switch_buffer; + } else if (code == GIPN_BUFFER_INFO) { + mainloop_expect(fd, &h->buffer_info_msg_buf, sizeof(gip_buffer_info_msg), giph_buffer_info); + } else if (code == GIPR_MODE_INFO) { + mainloop_expect(fd, &h->mode_info_msg_buf, sizeof(gip_mode_info_msg), giph_mode_info); + // this is a reply but we cannot call giph_got_reply immediately since more data is needed + // giph_got_reply(h, id); + } else if (code == GIPN_BUFFER_DAMAGE) { + mainloop_expect(fd, &h->buffer_damage_msg_buf, sizeof(gip_buffer_damage_msg), giph_buffer_damage); + } else { + use_cb = h->cb->unknown_msg; + } + + if (use_cb) use_cb(h, &h->msg_buf); +} + +void giph_buffer_info(mainloop_fd_t *fd) { + gip_handler_t *h = (gip_handler_t*)fd->data; + + if (h->cb->buffer_info) h->cb->buffer_info(h, &h->msg_buf, &h->buffer_info_msg_buf); + + mainloop_expect(&h->mainloop_item, &h->msg_buf, sizeof(gip_msg_header), giph_msg_header); +} + +void giph_mode_info(mainloop_fd_t *fd) { + gip_handler_t *h = (gip_handler_t*)fd->data; + + if (h->cb->mode_info) h->cb->mode_info(h, &h->msg_buf, &h->mode_info_msg_buf); + + // call giph_got_reply with more data ? + + mainloop_expect(&h->mainloop_item, &h->msg_buf, sizeof(gip_msg_header), giph_msg_header); +} + +void giph_buffer_damage(mainloop_fd_t *fd) { + gip_handler_t *h = (gip_handler_t*)fd->data; + + if (h->cb->buffer_damage) h->cb->buffer_damage(h, &h->msg_buf, &h->buffer_damage_msg_buf); + + mainloop_expect(&h->mainloop_item, &h->msg_buf, sizeof(gip_msg_header), giph_msg_header); +} + +void gip_error(mainloop_fd_t *fd) { + gip_handler_t *h = (gip_handler_t*)fd->data; + + if (h->cb->fd_error) h->cb->fd_error(h); +} + +/* vim: set ts=4 sw=4 tw=0 noet :*/ |