aboutsummaryrefslogblamecommitdiff
path: root/src/lib/libkogata/gip.c
blob: 7fa1cca461f0ec3c9ef38ab7dc848ba4357477ca (plain) (tree)




























































































































                                                                                                                   
                                                                            

                                                                                              

                                                 





                                                                            
                                        

                                   

                                                      




                                           
                                                  

                                     
                                                  

                                          
                                                  













                                                                                                            




















                                                                                                                  
                                                              


















                                                                                                  
#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, void* msg_data) {
	gip_cmd_t *c = (gip_cmd_t*)hashtbl_find(h->requests_in_progress, (void*)msg->req_id);;
	if (c != 0) {
		ASSERT(c->cb != 0);
		c->cb(h, msg, msg_data, 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;
	ASSERT(fd == &h->mainloop_item);

	int code = h->msg_buf.code;
	dbg_printf("Got GIP header, code %d\n", 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, 0);
	} else if (code == GIPR_OK) {
		use_cb = h->cb->ok;
		giph_got_reply(h, &h->msg_buf, 0);
	} else if (code == GIPR_FAILURE) {
		use_cb = h->cb->failure;
		giph_got_reply(h, &h->msg_buf, 0);
	} 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);
	} 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);
	giph_got_reply(h, &h->msg_buf, &h->mode_info_msg_buf);

	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 :*/