diff options
Diffstat (limited to 'source/archived/v1/client.c')
-rw-r--r-- | source/archived/v1/client.c | 259 |
1 files changed, 259 insertions, 0 deletions
diff --git a/source/archived/v1/client.c b/source/archived/v1/client.c new file mode 100644 index 0000000..878b678 --- /dev/null +++ b/source/archived/v1/client.c @@ -0,0 +1,259 @@ +// Client for chatty + +// initial size for the messages array +#define MESSAGES_SIZE 5 + +// clang-format off +#define TB_IMPL +#include "termbox2.h" +// clang-format on +#include "common.h" + +#include <arpa/inet.h> +#include <errno.h> +#include <poll.h> +#include <stdarg.h> +#include <sys/socket.h> +#include <time.h> +#include <unistd.h> + +enum { FD_SERVER = 0, + FD_TTY, + FD_RESIZE, + FD_MAX }; + +// offset of the input prompt +int curs_offs_x = 2; +int prompt_offs_y = 3; + +// filedescriptor for server +static int serverfd; +// Input message to be send +Message input = { + .author = USERNAME, + .timestamp = {0}, + .text_len = 0, +}; +// current amount of messages +int nmessages = 0; +// length of messages array +int messages_size = MESSAGES_SIZE; +// All messages sent and received in order +Message messages[MESSAGES_SIZE] = {0}; +// incremented each time a new message is printed +int msg_y = 0; + +// Cleans up resources, should called before exiting. +void cleanup(void); +// Displays an error message msg, followed by the errno variable and exits exeuction. +void err_exit(const char *msg); +// Display the welcome ui screen containing the prompt and messages array. +void scren_welcome(void); +// Append msg to the messages array. Returns -1 if there was no space in the messages array +// otherwise returns 0 on success. +u8 messages_add(Message *msg); + +void cleanup(void) +{ + tb_shutdown(); + if (serverfd) + if (close(serverfd)) + writef("Error while closing server socket. errno: %d\n", errno); +} + +// panic +void err_exit(const char *msg) +{ + cleanup(); + writef("%s errno: %d\n", msg, errno); + _exit(1); +} + +void screen_welcome(void) +{ + tb_set_cursor(curs_offs_x, global.height - prompt_offs_y); + tb_print(0, global.height - prompt_offs_y, 0, 0, ">"); + + // if there is not enough space to fit all messages, skip the n first messages of the array. + int skip = 0; + int lines_available = global.height - prompt_offs_y - 1; // pad by 1 from prompt + if (lines_available - nmessages < 0) + skip = nmessages - lines_available; + for (msg_y = skip; msg_y < nmessages; msg_y++) { + tb_printf(0, msg_y - skip, 0, 0, "%s [%s]: %s", messages[msg_y].timestamp, messages[msg_y].author, messages[msg_y].text); + } +} + +u8 messages_add(Message *msg) +{ + if (nmessages == messages_size) { + return -1; + } + + memcpy(messages[nmessages].author, msg->author, MESSAGE_AUTHOR_LEN); + memcpy(messages[nmessages].timestamp, msg->timestamp, MESSAGE_TIMESTAMP_LEN); + messages[nmessages].text_len = msg->text_len; + messages[nmessages].text = msg->text; + + nmessages++; + msg_y++; + + return 0; +} + +int main(void) +{ + // current event + struct tb_event ev; + // time for a new entered message + time_t now; + // localtime of new sent message + struct tm *ltime; + char buf[MESSAGE_MAX]; + input.text = buf; + + int serverfd, ttyfd, resizefd; + Message msg_recv = {0}; + const struct sockaddr_in address = { + AF_INET, + htons(PORT), + {0}, + }; + + tb_init(); + bytebuf_puts(&global.out, global.caps[TB_CAP_SHOW_CURSOR]); + + screen_welcome(); + tb_present(); + + tb_get_fds(&ttyfd, &resizefd); + serverfd = socket(AF_INET, SOCK_STREAM, 0); + + struct pollfd fds[FD_MAX] = { + {serverfd, POLLIN, 0}, // FD_SERVER + { ttyfd, POLLIN, 0}, // FD_TTY + {resizefd, POLLIN, 0}, // FD_RESIZE + }; + + if (connect(serverfd, (struct sockaddr *)&address, sizeof(address))) + err_exit("Error while connecting."); + + for (;;) { + if (poll(fds, FD_MAX, 50000) == -1) { + // check if it was a resize event that interrupted the system call + if (errno == EINTR) { + tb_peek_event(&ev, 80); + if (ev.type != TB_EVENT_RESIZE) + err_exit("Error while polling."); + else { + tb_clear(); + screen_welcome(); + } + } + } + + if (fds[FD_TTY].revents & POLLIN) { + tb_poll_event(&ev); + switch (ev.key) { + // exit + case TB_KEY_CTRL_C: + case TB_KEY_CTRL_D: + case TB_KEY_ESC: + goto exit_loop; + // remove line till cursor + case TB_KEY_CTRL_U: + while (global.cursor_x > curs_offs_x) { + global.cursor_x--; + tb_print(global.cursor_x, global.cursor_y, 0, 0, " "); + } + tb_set_cursor(curs_offs_x, global.cursor_y); + input.text_len = 0; + break; + // send message + case TB_KEY_CTRL_M: + if (input.text_len <= 0) + break; + while (global.cursor_x > curs_offs_x) { + global.cursor_x--; + tb_print(global.cursor_x, global.cursor_y, 0, 0, " "); + } + tb_set_cursor(curs_offs_x, global.cursor_y); + + // zero terminate + input.text[input.text_len] = 0; + + // print new message + time(&now); + ltime = localtime(&now); + strftime((char*)input.timestamp, sizeof(input.timestamp), "%H:%M:%S", ltime); + + messages_add(&input); + + if (message_send(&input, serverfd) == -1) + err_exit("Error while sending message."); + + // reset buffer + input.text_len = 0; + + // update the screen + // NOTE: kind of wasteful cause we should only display new message + tb_clear(); + screen_welcome(); + + break; + // remove word + case TB_KEY_CTRL_W: + // Delete consecutive space + while (input.text[input.text_len - 1] == ' ' && global.cursor_x > curs_offs_x) { + global.cursor_x--; + input.text_len--; + tb_print(global.cursor_x, global.cursor_y, 0, 0, " "); + } + // Delete until next non-space + while (input.text[input.text_len - 1] != ' ' && global.cursor_x > curs_offs_x) { + global.cursor_x--; + input.text_len--; + tb_print(global.cursor_x, global.cursor_y, 0, 0, " "); + } + input.text[input.text_len] = 0; + break; + } + + // append pressed character to input.text + // TODO: wrap instead, allocate more ram for the message instead + if (ev.ch > 0 && input.text_len < MESSAGE_MAX && input.text_len < global.width - 3 - 1) { + tb_printf(global.cursor_x, global.cursor_y, 0, 0, "%c", ev.ch); + global.cursor_x++; + + input.text[input.text_len++] = ev.ch; + } + + } else if (fds[FD_SERVER].revents & POLLIN) { + int nrecv = message_receive(&msg_recv, serverfd); + + if (nrecv == 0) { + // Server closed + // TODO: error message like (disconnected) + break; + } else if (nrecv == -1) { + err_exit("Error while receiveiving from server."); + } + messages_add(&msg_recv); + tb_clear(); + screen_welcome(); + + } else if (fds[FD_RESIZE].revents & POLLIN) { + tb_poll_event(&ev); + if (ev.type == TB_EVENT_RESIZE) { + tb_clear(); + screen_welcome(); + } + } + + tb_present(); + } +exit_loop:; + + cleanup(); + return 0; +} |