From e45cb3e5b5760ffd4a894ff4c988decc903c470d Mon Sep 17 00:00:00 2001 From: tema5002 Date: Thu, 28 May 2026 15:43:19 +0300 Subject: [PATCH] update stuffs --- Makefile | 3 + chatclient-cli.c | 307 ++++++++++++++++++++++++----------------------- chatserver.c | 71 +++++------ shared.h | 120 ++++++++++++++++++ 4 files changed, 311 insertions(+), 190 deletions(-) create mode 100644 shared.h diff --git a/Makefile b/Makefile index e005d30..a0cc64f 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,9 @@ chatserver: chatserver.c cc chatserver.c -o chatserver -O3 +#chatclient: chatclient.c +# gcc -o chatclient chatclient.c `pkg-config --cflags --libs gtk+-3.0` -lpthread + chatclient-cli: chatclient-cli.c cc chatclient-cli.c -o chatclient-cli -lpthread -lncurses diff --git a/chatclient-cli.c b/chatclient-cli.c index d600900..f19c484 100755 --- a/chatclient-cli.c +++ b/chatclient-cli.c @@ -1,30 +1,33 @@ +#include "shared.h" #include #include -#include #include #include #include #include -#include -#include -#include #include -#include #include struct { - bool title_header; - bool message_field; - bool input_field; + struct { + bool title_header; + bool message_field; + bool input_field; + } needs_redraw; struct { int x, y; } cursor; int scroll_offset; int unread; -} render_state = {0}; +} render_state = { + .needs_redraw = {1, 1, 1}, + .cursor = {0, 0}, + .scroll_offset = 0, + .unread = 0 +}; struct input_state_t { - char buffer[1024]; + char buffer[MAX_MESSAGE_LENGTH]; int buffer_size; int content_length; int cursor_pos; @@ -35,7 +38,7 @@ int server_fd; FILE *reader; FILE *writer; -struct input_state_t input_state = (struct input_state_t){{0}, sizeof(input_state.buffer) - 1, 0, 0, 0}; +struct input_state_t input_state = (struct input_state_t){{0}, MAX_MESSAGE_LENGTH, 0, 0, 0}; static int input_state_insert_char(const char ch) { if (input_state.content_length >= input_state.buffer_size - 1) return 1; @@ -67,8 +70,8 @@ static void input_state_backspace_char() { } } -static int input_state_handle_input_line(const int ch) { - render_state.input_field = 0; +static int input_state_handle_input(const int ch) { + render_state.needs_redraw.input_field = 1; if (ch == '\n' || ch == '\r' || ch == KEY_ENTER) return 1; if (ch == KEY_BACKSPACE || ch == 127) { input_state_backspace_char(); return 0; } if (ch == KEY_DC) { input_state_delete_char(); return 0; } @@ -78,28 +81,30 @@ static int input_state_handle_input_line(const int ch) { if (ch == KEY_END) { input_state.cursor_pos = input_state.content_length; return 0; } if (ch == KEY_PPAGE) { render_state.scroll_offset += LINES - 3 - 1 -2; - render_state.message_field = false; - render_state.title_header = false; + render_state.needs_redraw.message_field = 1; + render_state.unread = 0; + render_state.needs_redraw.title_header = 1; return 0; } if (ch == KEY_NPAGE) { render_state.scroll_offset -= LINES - 3 - 1 -2; if (render_state.scroll_offset < 0) render_state.scroll_offset = 0; - render_state.message_field = false; - render_state.title_header = false; + render_state.needs_redraw.message_field = 1; + render_state.needs_redraw.title_header = 1; return 0; } if (ch == KEY_UP) { render_state.scroll_offset++; - render_state.message_field = false; - render_state.title_header = false; + render_state.needs_redraw.message_field = 1; + render_state.needs_redraw.title_header = 1; return 0; } if (ch == KEY_DOWN) { if (render_state.scroll_offset > 0) { render_state.scroll_offset--; - render_state.message_field = false; - render_state.title_header = false; + render_state.needs_redraw.message_field = 1; + if (render_state.scroll_offset == 0) render_state.unread = 0; + render_state.needs_redraw.title_header = 1; } return 0; } @@ -132,9 +137,9 @@ static void add_new_message(const char* str) { if (render_state.scroll_offset > 0) { render_state.scroll_offset++; render_state.unread++; - render_state.title_header = 0; + render_state.needs_redraw.title_header = 1; } - render_state.message_field = 0; + render_state.needs_redraw.message_field = 1; pthread_mutex_unlock(&messages.lock); } @@ -142,47 +147,131 @@ static void add_new_message(const char* str) { #define PROGRAM_NAME "chatclient-cli" #define CC_COLOR_PAIR_TITLE_HEADER 1 +#define CC_COLOR_PAIR_TITLE_HEADER2 3 #define CC_COLOR_PAIR_INPUT 2 +char server_str[CC_ADDRLEN]; +int server_str_len; + static void cc_render_title_header( const int x, const int y, const int w ) { - char system_time[20]; - const int time_length = strftime(system_time, 20, "%Y-%m-%d %H:%M:%S", localtime(&(time_t){time(NULL)})); + char system_time[21]; + const time_t now = time(NULL); + int time_length = strftime(system_time, sizeof(system_time), "%Y-%m-%d %H:%M:%S", localtime(&now)); - char indicator[32] = ""; + char indicator[32] = {0}; int indicator_length = 0; if (render_state.unread > 0) { - indicator_length = snprintf(indicator, sizeof(indicator), " [%d unread]", render_state.unread); + indicator_length = snprintf(indicator, sizeof(indicator), " %c%d unread%c", + now % 2 ? ' ' : '[', + render_state.unread, + now % 2 ? ' ' : ']' + ); } - move(y, x); - attron(COLOR_PAIR(CC_COLOR_PAIR_TITLE_HEADER)); + const int name_length = strlen(PROGRAM_NAME); + const int total_length = name_length + indicator_length + server_str_len + time_length; + char header_str[1024]; + int n; - const int name_length = sizeof(PROGRAM_NAME) - 1; - const int total_length = name_length + time_length + indicator_length; + int left_right_padding = w / 16; + if (left_right_padding > 2) left_right_padding = 2; + if (total_length + left_right_padding * 2 + 2 * 2 <= w) { + const int total_padding = w - left_right_padding * 2 - total_length; + const int padding2 = total_padding / 2; + const int padding1 = total_padding - padding2; - if (total_length + 2 > w) { - const int padding = (w - name_length - indicator_length) / 2; - PRINT_SPACES(padding); - printw("%-*s", w - padding, PROGRAM_NAME); - printw("%s", indicator); + n = snprintf(header_str, sizeof(header_str), + "%*c" + "%s" + "%s" + "%*c" + "%s" + "%*c" + "%s" + "%*c", + left_right_padding, ' ', + PROGRAM_NAME, + indicator, + padding1, ' ', + server_str, + padding2, ' ', + system_time, + left_right_padding, ' ' + ); + } + else if (name_length + indicator_length + server_str_len + left_right_padding * 2 + 2 <= w) { + const int padding = w - left_right_padding * 2 - name_length - indicator_length - server_str_len; + + n = snprintf(header_str, sizeof(header_str), + "%*c" + "%s" + "%s" + "%*c" + "%s" + "%*c", + left_right_padding, ' ', + PROGRAM_NAME, + indicator, + padding, ' ', + server_str, + left_right_padding, ' ' + ); + } + else if (name_length + indicator_length <= w) { + const int total_left_right_padding = w - name_length - indicator_length; + int right_padding = total_left_right_padding / 2; + int left_padding = total_left_right_padding - right_padding; + + n = snprintf(header_str, sizeof(header_str), + "%*c" + "%s" + "%s" + "%*c", + left_padding, ' ', + PROGRAM_NAME, + indicator, + right_padding, ' ' + ); + } + else if (name_length <= w) { + const int total_left_right_padding = w - name_length; + int right_padding = total_left_right_padding / 2; + int left_padding = total_left_right_padding - right_padding; + + n = snprintf(header_str, sizeof(header_str), + "%*c" + "%s" + "%*c", + left_padding, ' ', + PROGRAM_NAME, + right_padding, ' ' + ); } else { - const int left_right_padding = 2; - const int padding = w - left_right_padding * 2 - total_length; - - PRINT_SPACES(left_right_padding); - printw("%s", PROGRAM_NAME); - printw("%s", indicator); - PRINT_SPACES(padding); - printw("%s", system_time); - PRINT_SPACES(left_right_padding); + n = snprintf(header_str, sizeof(header_str), + "%*c", + w, ' ' + ); } - attroff(COLOR_PAIR(CC_COLOR_PAIR_TITLE_HEADER)); + int begin_color = render_state.scroll_offset % (w * 2) > w ? CC_COLOR_PAIR_TITLE_HEADER : CC_COLOR_PAIR_TITLE_HEADER2; + int end_color = render_state.scroll_offset % (w * 2) < w ? CC_COLOR_PAIR_TITLE_HEADER : CC_COLOR_PAIR_TITLE_HEADER2; + int current_color = begin_color; + move(y, x); + attron(COLOR_PAIR(current_color)); + for (int i = 0; i < n; i++) { + if (i == render_state.scroll_offset % w) { + attroff(COLOR_PAIR(current_color)); + current_color = end_color; + attron(COLOR_PAIR(current_color)); + } + addch(header_str[i]); + } + attroff(COLOR_PAIR(current_color)); } static void cc_render_message_field( @@ -270,22 +359,22 @@ static void cc_render_ui( pthread_mutex_lock(&messages.lock); bool do_refresh = false; - if (!render_state.title_header) { - render_state.title_header = true; + if (render_state.needs_redraw.title_header) { + render_state.needs_redraw.title_header = 0; cc_render_title_header(x, y, w); - do_refresh = true; + do_refresh = 1; } - if (!render_state.message_field) { - render_state.message_field = true; + if (render_state.needs_redraw.message_field) { + render_state.needs_redraw.message_field = 0; cc_render_message_field(x, y + 1, w, h - 3); - do_refresh = true; + do_refresh = 1; } - if (!render_state.input_field) { - render_state.input_field = true; + if (render_state.needs_redraw.input_field) { + render_state.needs_redraw.input_field = 0; cc_render_input_field(x, y + h - 3, w); - do_refresh = true; + do_refresh = 1; } if (do_refresh) { @@ -300,9 +389,9 @@ static void update_terminal_size() { refresh(); clear(); initscr(); - render_state.title_header = false; - render_state.message_field = false; - render_state.input_field = false; + render_state.needs_redraw.title_header = 1; + render_state.needs_redraw.message_field = 1; + render_state.needs_redraw.input_field = 1; } static int handle_input() { @@ -320,40 +409,6 @@ static int handle_input() { return ch; } -#define exit_error(...) do { \ - fprintf(stderr, __VA_ARGS__); \ - endwin(); exit(EXIT_FAILURE); \ -} while (0) - -#define exit_perror(s) do { \ - perror(s); \ - endwin(); exit(EXIT_FAILURE); \ -} while (0) - -static int connect_to_server(struct in6_addr addr, int port) { - struct sockaddr_in6 server_addr; - - server_fd = socket(AF_INET6, SOCK_STREAM, 0); - if (server_fd < 0) exit_perror("Socket creation failed"); - const int server_fd2 = dup(server_fd); - if (server_fd2 < 0) exit_perror("dup"); - - server_addr.sin6_family = AF_INET6; - server_addr.sin6_addr = addr; - server_addr.sin6_port = htons(port); - - int opt = 0; - if (setsockopt(server_fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)) < 0) { - // i guess so bro - } - - if (connect(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) exit_perror("connect"); - - if ((reader = fdopen(server_fd, "r")) == NULL) exit_perror("fdopen reader"); - if ((writer = fdopen(server_fd2, "w")) == NULL) exit_perror("fdopen writer"); - return 0; -} - void *reader_thread(void *data) { char buffer[1078]; while (fgets(buffer, sizeof(buffer), reader) != NULL) { @@ -364,74 +419,24 @@ void *reader_thread(void *data) { return NULL; } -static int resolve_domain_to_ipv6(const char *hostname, struct in6_addr *server_addr) { - struct addrinfo hints, *res; - int found = 0; - - memset(&hints, 0, sizeof hints); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - - if (getaddrinfo(hostname, NULL, &hints, &res) != 0) return 0; - - for (struct addrinfo *p = res; p != NULL; p = p->ai_next) { - if (p->ai_family == AF_INET6) { - struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr; - memcpy(server_addr, &ipv6->sin6_addr, sizeof(struct in6_addr)); - found = 1; - break; - } - if (p->ai_family == AF_INET && !found) { - struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr; - memset(server_addr, 0, sizeof(struct in6_addr)); - server_addr->s6_addr[10] = 0xff; - server_addr->s6_addr[11] = 0xff; - memcpy(&server_addr->s6_addr[12], &(ipv4->sin_addr), 4); - found = 1; - } - } - - freeaddrinfo(res); - return found; -} - int main(int argc, char* argv[]) { - struct in6_addr server_addr = in6addr_any; - int port = 5000; - if (argc >= 2) { - if (inet_pton(AF_INET6, argv[1], &server_addr) != 1) { - struct in_addr v4; - if (inet_pton(AF_INET, argv[1], &v4) != 1) { - if (!resolve_domain_to_ipv6(argv[1], &server_addr)) { - exit_error("invalid address or domain: %s\n", argv[1]); - } - } - else { - memset(&server_addr, 0, sizeof(server_addr)); - server_addr.s6_addr[10] = 0xff; - server_addr.s6_addr[11] = 0xff; - memcpy(&server_addr.s6_addr[12], &v4, 4); - } - } - } - if (argc >= 3) { - port = atoi(argv[2]); - if (port <= 0) exit_error("unknown argument: %s\n", argv[2]); - } setlocale(LC_ALL, ""); initscr(); start_color(); use_default_colors(); init_pair(CC_COLOR_PAIR_TITLE_HEADER, COLOR_BLACK, COLOR_CYAN); + init_pair(CC_COLOR_PAIR_TITLE_HEADER2, COLOR_BLACK, COLOR_YELLOW); init_pair(CC_COLOR_PAIR_INPUT, COLOR_GREEN, -1); cbreak(); noecho(); curs_set(1); keypad(stdscr, TRUE); + struct sockaddr_in6 server_addr; + cc_client_parse_args_and_connect(argc, argv, &server_addr, &server_fd, &reader, &writer); + server_str_len = sockaddr2str((struct sockaddr*)&server_addr, server_str, sizeof(server_str)); init_messages(); - connect_to_server(server_addr, port); pthread_t reader_thread_id; pthread_create(&reader_thread_id, NULL, reader_thread, NULL); @@ -442,20 +447,20 @@ int main(int argc, char* argv[]) { const int input = handle_input(); - if (input_state_handle_input_line(input)) { + if (input_state_handle_input(input)) { if (input_state.content_length > 0) { fprintf(writer, "%s\n", input_state.buffer); fflush(writer); - input_state = (struct input_state_t){{0}, sizeof(input_state.buffer) - 1, 0, 0, 0}; - render_state.message_field = false; - render_state.unread = 0; + input_state = (struct input_state_t){{0}, MAX_MESSAGE_LENGTH, 0, 0, 0}; + render_state.needs_redraw.message_field = 1; + render_state.unread = -1; } } const time_t now = time(NULL); if (now > last_second) { last_second = now; - render_state.title_header = false; + render_state.needs_redraw.title_header = 1; } } diff --git a/chatserver.c b/chatserver.c index a91133f..ed99300 100644 --- a/chatserver.c +++ b/chatserver.c @@ -7,26 +7,7 @@ #include #include -static void sockaddr2str(const struct sockaddr *sa, char *s, const size_t maxlen) { - if (sa->sa_family == AF_INET) { - const struct sockaddr_in *sin = (struct sockaddr_in *)sa; - inet_ntop(AF_INET, &sin->sin_addr, s, maxlen); - const size_t len = strnlen(s, maxlen); - snprintf(s + len, maxlen - len, ":%u", ntohs(sin->sin_port)); - return; - } - - if (sa->sa_family == AF_INET6) { - const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; - s[0] = '['; - inet_ntop(AF_INET6, &sin6->sin6_addr, s + 1, maxlen - 1); - const size_t len = strnlen(s, maxlen); - snprintf(s + len, maxlen - len, "]:%u", ntohs(sin6->sin6_port)); - return; - } - - strncpy(s, "unknown", maxlen); -} +#include "shared.h" #define MAX_CLIENTS 100 #define BUFFER_SIZE 1024 @@ -56,6 +37,29 @@ typedef struct { fflush(stdout); \ } while (0) +/* +const char* ansi_colors[] = { + "\033[30m", "\033[31m", "\033[32m", "\033[33m", "\033[34m", "\033[35m", "\033[36m", "\033[37m", + "\033[90m", "\033[91m", "\033[92m", "\033[93m", "\033[94m", "\033[95m", "\033[96m", "\033[97m", + "\033[0m" +}; + +static unsigned long djb2(const unsigned char *str, size_t size) { + unsigned long hash = 5381; + while (size--) hash = ((hash << 5) + hash) + *str++; + return hash; +} + +const char* sockaddr_color(const struct sockaddr *addr) { + if (addr->sa_family == AF_INET) { + return ansi_colors[djb2((const unsigned char*)&((const struct sockaddr_in*)&addr)->sin_addr, 4) % 16]; + } + if (addr->sa_family == AF_INET6) { + return ansi_colors[djb2((const unsigned char*)&((const struct sockaddr_in6*)&addr)->sin6_addr, 16) % 16]; + } + return ansi_colors[16]; +}*/ + void* handle_client(void* _arg) { const pthread_input_t* arg = _arg; const int client_fd = arg->sock; @@ -63,20 +67,20 @@ void* handle_client(void* _arg) { free(_arg); char message[BUFFER_SIZE]; - + FILE* reader = fdopen(client_fd, "r"); - + if (reader == NULL) { perror("fdopen"); close(client_fd); return NULL; } - + while (fgets(message, BUFFER_SIZE, reader) != NULL) { message[strcspn(message, "\n")] = 0; if (message[0] == '\0') continue; - - char client_ip[INET6_ADDRSTRLEN + 8]; + + char client_ip[CC_ADDRLEN]; sockaddr2str((struct sockaddr*)&addr, client_ip, sizeof(client_ip)); tell_everyone("%s: %s\n", client_ip, message); } @@ -89,23 +93,12 @@ void* handle_client(void* _arg) { break; } pthread_mutex_unlock(&clients.lock); - char client_ip[INET6_ADDRSTRLEN + 8]; + char client_ip[CC_ADDRLEN]; sockaddr2str((struct sockaddr*)&addr, client_ip, sizeof(client_ip)); tell_everyone("- %s. total clients: %d\n", client_ip, clients.count); return NULL; } -#define streq(a, b) (strcmp((a), (b)) == 0) -#define exit_error(...) do { \ - fprintf(stderr, __VA_ARGS__); \ - exit(EXIT_FAILURE); \ -} while (0) - -#define exit_perror(s) do { \ - perror(s); \ - exit(EXIT_FAILURE); \ -} while (0) - int main(int argc, char* argv[]) { struct in6_addr bind_addr = in6addr_any; int port = 5000; @@ -165,8 +158,8 @@ int main(int argc, char* argv[]) { pthread_create(&thread, NULL, handle_client, pi); pthread_detach(thread); pthread_mutex_unlock(&clients.lock); - char ip_buf[INET6_ADDRSTRLEN + 8]; + char ip_buf[CC_ADDRLEN]; sockaddr2str((struct sockaddr*)&client_addr, ip_buf, sizeof(ip_buf)); tell_everyone("+ %s. total clients: %d\n", ip_buf, clients.count); } -} \ No newline at end of file +} diff --git a/shared.h b/shared.h new file mode 100644 index 0000000..584dba1 --- /dev/null +++ b/shared.h @@ -0,0 +1,120 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +#define CC_ADDRLEN (INET6_ADDRSTRLEN + 8) // [...:12345] + +static int sockaddr2str(const struct sockaddr *sa, char *s, const size_t maxlen) { + if (sa->sa_family == AF_INET) { + const struct sockaddr_in *sin = (struct sockaddr_in *)sa; + inet_ntop(AF_INET, &sin->sin_addr, s, maxlen); + const size_t len = strnlen(s, maxlen); + return len + snprintf(s + len, maxlen - len, ":%u", ntohs(sin->sin_port)); + } + + if (sa->sa_family == AF_INET6) { + const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; + s[0] = '['; + inet_ntop(AF_INET6, &sin6->sin6_addr, s + 1, maxlen - 1); + const size_t len = strnlen(s, maxlen); + return len + snprintf(s + len, maxlen - len, "]:%u", ntohs(sin6->sin6_port)); + } + + strncpy(s, "unknown", maxlen); + return 7; +} + +#define MAX_MESSAGE_LENGTH 1024 + +#define streq(a, b) (strcmp((a), (b)) == 0) + +#define exit_error(...) do { \ + fprintf(stderr, __VA_ARGS__); \ + exit(EXIT_FAILURE); \ +} while (0) + +#define exit_perror(s) do { \ + perror(s); \ + exit(EXIT_FAILURE); \ +} while (0) + +static int resolve_domain_to_ipv6(const char *hostname, struct in6_addr *server_addr) { + struct addrinfo hints, *res; + int found = 0; + + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + if (getaddrinfo(hostname, NULL, &hints, &res) != 0) return 0; + + for (struct addrinfo *p = res; p != NULL; p = p->ai_next) { + if (p->ai_family == AF_INET6) { + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr; + memcpy(server_addr, &ipv6->sin6_addr, sizeof(struct in6_addr)); + found = 1; + break; + } + if (p->ai_family == AF_INET && !found) { + struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr; + memset(server_addr, 0, sizeof(struct in6_addr)); + server_addr->s6_addr[10] = 0xff; + server_addr->s6_addr[11] = 0xff; + memcpy(&server_addr->s6_addr[12], &(ipv4->sin_addr), 4); + found = 1; + } + } + + freeaddrinfo(res); + return found; +} + +static int cc_client_parse_args_and_connect(int argc, char* argv[], struct sockaddr_in6* server_addr, int* server_fd, FILE** reader, FILE** writer) { + struct in6_addr bind_addr = in6addr_any; + int port = 5000; + if (argc >= 2) { + if (inet_pton(AF_INET6, argv[1], &bind_addr) != 1) { + struct in_addr v4; + if (inet_pton(AF_INET, argv[1], &v4) != 1) { + if (!resolve_domain_to_ipv6(argv[1], &bind_addr)) { + exit_error("invalid address or domain: %s\n", argv[1]); + } + } + else { + memset(&bind_addr, 0, sizeof(bind_addr)); + bind_addr.s6_addr[10] = 0xff; + bind_addr.s6_addr[11] = 0xff; + memcpy(&bind_addr.s6_addr[12], &v4, 4); + } + } + } + if (argc >= 3) { + port = atoi(argv[2]); + if (port <= 0) exit_error("unknown argument: %s\n", argv[2]); + } + + *server_fd = socket(AF_INET6, SOCK_STREAM, 0); + if (*server_fd < 0) exit_perror("socket"); + const int server_fd2 = dup(*server_fd); + if (server_fd2 < 0) exit_perror("dup"); + + server_addr->sin6_family = AF_INET6; + server_addr->sin6_addr = bind_addr; + server_addr->sin6_port = htons(port); + + int opt = 0; + if (setsockopt(*server_fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)) < 0) { + // i guess so bro + } + + if (connect(*server_fd, (struct sockaddr *)server_addr, sizeof(*server_addr)) < 0) exit_perror("connect"); + + if ((*reader = fdopen(*server_fd, "r")) == NULL) exit_perror("fdopen reader"); + if ((*writer = fdopen(server_fd2, "w")) == NULL) exit_perror("fdopen writer"); + return 0; +}