aboutsummaryrefslogtreecommitdiff
path: root/src/client/session.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/session.c')
-rw-r--r--src/client/session.c315
1 files changed, 315 insertions, 0 deletions
diff --git a/src/client/session.c b/src/client/session.c
new file mode 100644
index 0000000..e26736c
--- /dev/null
+++ b/src/client/session.c
@@ -0,0 +1,315 @@
+#include <basic/string32.h>
+#include <os/os.h>
+#include <client/fscord.h>
+#include <client/session.h>
+#include <client/draw.h>
+#include <client/font.h>
+#include <messages/messages.h>
+
+internal_var Fscord *s_fscord;
+
+internal_fn void
+session_draw_chat_message(ChatMessage *message, V2F32 pos)
+{
+ OSOffscreenBuffer *screen = s_fscord->offscreen_buffer;
+ Font *font = &s_fscord->font;
+ Arena *frame_arena = &s_fscord->frame_arena;
+
+ f32 str_width;
+ f32 dx = font->y_advance / 2.f;
+
+
+ pos.x += font->y_advance;
+
+
+ // draw local time
+ time_t local_time_src = message->creation_time.seconds;
+ struct tm *local_time = localtime(&local_time_src);
+
+ char time_cstr_len = 5;
+ char time_cstr[time_cstr_len+1];
+ sprintf(time_cstr, "%.2d:%.2d", local_time->tm_hour, local_time->tm_min);
+
+ String32 *time_str = string32_create_from_ascii(frame_arena, time_cstr);
+ draw_string32(screen, pos, time_str, font);
+ str_width = font_get_string32_width(font, time_str);
+ pos.x += str_width + dx;
+
+
+ // draw sender_name
+ String32Buffer *sender_name_buff = string32_buffer_create(frame_arena, message->sender_name->len + 2);
+ string32_buffer_append_ascii_cstr(sender_name_buff, "<");
+ string32_buffer_append_string32_buffer(sender_name_buff, message->sender_name);
+ string32_buffer_append_ascii_cstr(sender_name_buff, ">");
+
+ String32 *sender_name = string32_buffer_to_string32(frame_arena, sender_name_buff);
+ draw_string32(screen, pos, sender_name, font);
+ str_width = font_get_string32_width(font, sender_name);
+ pos.x += str_width + dx;
+
+
+ // draw content
+ String32 *content = string32_buffer_to_string32(frame_arena, message->content);
+ draw_string32(screen, pos, content, font);
+}
+
+internal_fn void
+session_draw_chat(Session *session, RectF32 rect)
+{
+ Font *font = &s_fscord->font;
+ OSOffscreenBuffer *screen = s_fscord->offscreen_buffer;
+
+
+ // draw background
+ V3F32 bg_color = v3f32(0.2, 0.2, 0.3);
+ draw_rectf32(screen, rect, bg_color);
+
+
+ // draw border
+ RectF32 border_rect = rect;
+ f32 border_size = font->y_advance / 8.f;
+ V3F32 border_color = v3f32(0, 0, 0);
+ draw_border(screen, border_rect, border_size, border_color);
+
+
+ // draw messages
+ V2F32 pos = v2f32(rect.x0 + border_size*4, rect.y0 + border_size*4);
+ f32 dy = font->y_advance + border_size * 4;
+
+ if (session->cur_message_count == 0) {
+ return;
+ }
+
+ // Todo: Can we make this cleaner?
+ // We're only using indices and no counts within loops, which is good.
+ // But, we're also using size_t for indices which can wrap around 0
+ // and make conditions less clear.
+ size_t l = (session->message0 + session->cur_message_count - 1) % session->max_message_count;
+ size_t r = MIN(session->message0 + session->cur_message_count - 1, session->max_message_count-1);
+ while (l < session->message0) {
+ if (pos.y >= pos.y + dy) {
+ break;
+ }
+ ChatMessage *message = &session->messages[l];
+ session_draw_chat_message(message, pos);
+ pos.y += dy;
+
+ l--;
+ }
+ r++;
+ do {
+ r--;
+
+ if (pos.y >= pos.y + dy) {
+ break;
+ }
+ ChatMessage *message = &session->messages[r];
+ session_draw_chat_message(message, pos);
+ pos.y += dy;
+ } while (r != session->message0);
+}
+
+internal_fn void
+session_draw_prompt(Session *session, RectF32 rect)
+{
+ OSOffscreenBuffer *screen = s_fscord->offscreen_buffer;
+ Arena *frame_arena = &s_fscord->frame_arena;
+ Font *font = &s_fscord->font;
+
+
+ // draw background
+ V3F32 bg_color = v3f32(0.2, 0.3, 0.2);
+ draw_rectf32(screen, rect, bg_color);
+
+
+ // draw border
+ RectF32 border_rect = rect;
+ f32 border_size = font->y_advance / 8.f;
+ V3F32 border_color = v3f32(0.16, 0.32, 0.08);
+ draw_border(screen, border_rect, border_size, border_color);
+
+
+ // draw text
+ f32 xmargin = border_size * 4;
+ f32 ymargin = border_size * 4;
+ V2F32 pos = v2f32(rect.x0 + xmargin, rect.y0 + ymargin);
+ String32 *prompt_str = string32_buffer_to_string32(frame_arena, session->prompt);
+ draw_string32(screen, pos, prompt_str, font);
+
+
+ draw_cursor(screen, pos, font, prompt_str, session->prompt->cursor);
+}
+
+internal_fn void
+session_draw_users(Session *session, RectF32 rect)
+{
+ OSOffscreenBuffer *screen = s_fscord->offscreen_buffer;
+ Font *font = &s_fscord->font;
+ Arena *frame_arena = &s_fscord->frame_arena;
+
+
+ // draw background
+ V3F32 bg_color = v3f32(0.3, 0.2, 0.2);
+ draw_rectf32(screen, rect, bg_color);
+
+
+ // draw border
+ f32 border_size = font->y_advance / 8.f;
+ V3F32 border_color = v3f32(0, 0, 0);
+ draw_border(screen, rect, border_size, border_color);
+
+
+ // draw users
+ f32 xmargin = border_size * 2;
+ f32 ymargin = border_size * 2;
+ f32 dy = font->y_advance;
+ V2F32 pos = v2f32(rect.x0 + xmargin, rect.y1 - ymargin - dy);
+
+ printf("drawing users...\n");
+ for (size_t i = 0; i < session->cur_user_count; i++) {
+ if (pos.y < ymargin - font->y_advance) {
+ break;
+ }
+
+ f32 width_remain = rect.x1 - pos.x;
+ User *user = &session->users[i];
+
+ // Todo: we want to cut characters on rectangle boundaries
+ size_t name_len_desired = user->name->len;
+ size_t name_len_avail = font->scale * 1000; // Todo: find better value
+ size_t name_len = name_len_desired <= name_len_avail ? name_len_desired : name_len_avail;
+ String32 *username = string32_buffer_to_string32_with_len(frame_arena, user->name, name_len);
+ draw_string32(screen, pos, username, font);
+ pos.y -= dy;
+ }
+}
+
+void
+session_draw(Session *session)
+{
+ OSOffscreenBuffer *screen = s_fscord->offscreen_buffer;
+
+ f32 left_width = screen->width * 0.3;
+ f32 prompt_height = screen->height * 0.1;
+
+ RectF32 users_rect = rectf32(0, 0, left_width, screen->height);
+ RectF32 prompt_rect = rectf32(left_width, 0, screen->width, prompt_height);
+ RectF32 chat_rect = rectf32(left_width, prompt_height, screen->width, screen->height);
+
+ session_draw_users(session, users_rect);
+ session_draw_prompt(session, prompt_rect);
+ session_draw_chat(session, chat_rect);
+}
+
+void
+session_add_chat_message(Session *session, Time creation_time, String32 *sender_name, String32 *content)
+{
+ size_t i = (session->message0 + session->cur_message_count) % session->max_message_count;
+
+ ChatMessage *message = &session->messages[i];
+ message->creation_time = creation_time;
+ string32_buffer_copy_string32(message->sender_name, sender_name);
+ string32_buffer_copy_string32(message->content, content);
+
+ if (session->cur_message_count < session->max_message_count) {
+ session->cur_message_count++;
+ }
+}
+
+void
+session_rm_user(Session *session, String32 *username)
+{
+ // Todo: use a hashmap
+ size_t rm_idx = SIZE_MAX;
+ for (size_t i = 0; i < session->cur_user_count; i++) {
+ if (string32_buffer_equal_string32(session->users[i].name, username)) {
+ rm_idx = i;
+ break;
+ }
+ }
+ assert(rm_idx != SIZE_MAX);
+
+ // swap users
+ size_t last_idx = session->cur_user_count - 1;
+ for (size_t i = rm_idx + 1; i <= last_idx; i++) {
+ string32_buffer_copy_string32_buffer(session->users[i-1].name, session->users[i].name);
+ }
+
+ session->cur_user_count -= 1;
+}
+
+void
+session_add_user(Session *session, String32 *username)
+{
+ assert(session->cur_user_count < session->max_user_count);
+
+ User *user = &session->users[session->cur_user_count++];
+ string32_buffer_copy_string32(user->name, username);
+}
+
+void
+session_process_event(Session *session, OSEvent *event)
+{
+ Arena *frame_arena = &s_fscord->frame_arena;
+
+ switch (event->type) {
+ case OS_EVENT_KEY_PRESS: {
+ u32 codepoint = event->ev.key_press.code;
+ if (codepoint == '\r') {
+ String32 *trans_prompt = string32_buffer_to_string32(frame_arena, session->prompt);
+ send_c2s_chat_message(trans_prompt);
+ string32_buffer_reset(session->prompt);
+ }
+ else {
+ string32_buffer_edit(session->prompt, event->ev.key_press);
+ }
+ }
+ break;
+
+ default: {
+ printf("ui_update_session did not process an event\n");
+ }
+ }
+}
+
+void
+session_reset(Session *session)
+{
+ session->cur_user_count = 0;
+
+ session->message0 = 0;
+ session->cur_message_count = 0;
+
+ string32_buffer_reset(session->prompt);
+}
+
+Session *
+session_create(Arena *arena, struct Fscord *fscord)
+{
+ Session *session = arena_push(arena, sizeof(Session));
+
+ size_t max_user_count = MESSAGES_MAX_USER_COUNT;
+ session->cur_user_count = 0;
+ session->max_user_count = max_user_count;
+ session->users = arena_push(arena, max_user_count * sizeof(User));
+ for (size_t i = 0; i < max_user_count; i++) {
+ session->users[i].name = string32_buffer_create(arena, MESSAGES_MAX_MESSAGE_LEN);
+ }
+
+ size_t max_message_count = 256;
+ session->message0 = 0;
+ session->cur_message_count = 0;
+ session->max_message_count = max_message_count;
+ session->messages = arena_push(arena, max_message_count * sizeof(ChatMessage));
+ for (size_t i = 0; i < max_message_count; i++) {
+ session->messages[i].sender_name = string32_buffer_create(arena, MESSAGES_MAX_USERNAME_LEN);
+ session->messages[i].content = string32_buffer_create(arena, MESSAGES_MAX_MESSAGE_LEN);
+ }
+
+ session->prompt = string32_buffer_create(arena, MESSAGES_MAX_MESSAGE_LEN);
+
+ s_fscord = fscord;
+
+ return session;
+}
+