aboutsummaryrefslogtreecommitdiff
path: root/source/chatty.c
diff options
context:
space:
mode:
Diffstat (limited to 'source/chatty.c')
-rw-r--r--source/chatty.c528
1 files changed, 264 insertions, 264 deletions
diff --git a/source/chatty.c b/source/chatty.c
index 32a4431..01d450b 100644
--- a/source/chatty.c
+++ b/source/chatty.c
@@ -15,7 +15,7 @@
#define TIMEOUT_RECONNECT 1
#define MAX_INPUT_LEN 512
// Filepath where user ID is stored
-#define ID_FILE "_id"
+#define ID_FILE ".chatty_id"
// Filepath where logged
#define LOGFILE "chatty.log"
// enable logging
@@ -26,10 +26,10 @@
#ifndef Assert
#ifdef DEBUG
#define Assert(expr) if (!(expr)) \
- { \
- tb_shutdown(); \
- raise(SIGTRAP); \
- }
+{ \
+tb_shutdown(); \
+raise(SIGTRAP); \
+}
#else
#define Assert(expr) ;
#endif // DEBUG
@@ -49,10 +49,10 @@
#include "ui.h"
enum { FDS_BI = 0, // for one-way communication with the server (eg. TextMessage)
- FDS_UNI, // For two-way communication with the server (eg. IDMessage)
- FDS_TTY,
- FDS_RESIZE,
- FDS_MAX };
+ FDS_UNI, // For two-way communication with the server (eg. IDMessage)
+ FDS_TTY,
+ FDS_RESIZE,
+ FDS_MAX };
typedef struct {
u8 Author[AUTHOR_LEN];
@@ -96,7 +96,7 @@ get_user_by_id(Arena* clientsArena, ID id)
{
// User is not in the clientsArena
if (id == user.ID) return &user;
-
+
User* clients = clientsArena->addr;
for (u64 i = 0; i < (clientsArena->pos / sizeof(*clients)); i++)
{
@@ -117,16 +117,16 @@ add_user_info(Arena* clientsArena, s32 fd, u64 id)
IDMessage message = {id};
s32 nsend = sendAnyMessage(fd, header, &message);
Assert(nsend != -1);
-
+
// Wait for response
IntroductionMessage introduction_message;
recvAnyMessageType(fd, &header, &introduction_message, HEADER_TYPE_INTRODUCTION);
-
+
// Add the information
User* client = ArenaPush(clientsArena, sizeof(*client));
memcpy(client->Author, introduction_message.author, AUTHOR_LEN);
client->ID = id;
-
+
LoggingF("Got " USER_FMT "\n", USER_ARG((*client)));
return client;
}
@@ -137,10 +137,10 @@ get_connection(struct sockaddr_in* address)
{
s32 fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd == -1) return -1;
-
+
s32 err = connect(fd, (struct sockaddr*)address, sizeof(*address));
if (err) return -1;
-
+
return fd;
}
@@ -157,14 +157,14 @@ authenticate(User* user, s32 fd)
IDMessage message = {user->ID};
s32 nsend = sendAnyMessage(fd, header, &message);
Assert(nsend != -1);
-
+
ErrorMessage error_message;
s32 nrecv = recvAnyMessageType(fd, &header, &error_message, HEADER_TYPE_ERROR);
Assert(nrecv != -1);
// TODO: handle not found
if (nrecv == 0)
return 0;
-
+
if (error_message.type == ERROR_TYPE_SUCCESS)
return 1;
else
@@ -178,7 +178,7 @@ authenticate(User* user, s32 fd)
memcpy(message.author, user->Author, AUTHOR_LEN);
s32 nsend = sendAnyMessage(fd, header, &message);
Assert(nsend != -1);
-
+
IDMessage id_message;
s32 nrecv = recvAnyMessageType(fd, &header, &id_message, HEADER_TYPE_ID);
Assert(nrecv != -1);
@@ -205,7 +205,7 @@ thread_reconnect(void* fds_ptr)
{
// timeout
nanosleep(&t, &t);
-
+
bifd = get_connection(&address);
if (bifd == -1)
{
@@ -219,27 +219,27 @@ thread_reconnect(void* fds_ptr)
close(bifd);
continue;
}
-
+
LoggingF("Reconnect succeeded (%d, %d), authenticating\n", unifd, bifd);
-
+
if (authenticate(&user, bifd) &&
authenticate(&user, unifd))
{
break;
}
-
+
close(bifd);
close(unifd);
-
+
LoggingF("Failed, retrying...\n");
}
-
+
fds[FDS_BI].fd = bifd;
fds[FDS_UNI].fd = unifd;
-
+
// Redraw screen
raise(SIGWINCH);
-
+
return 0;
}
@@ -247,31 +247,31 @@ command_output
run_command_get_output(char *Command, char *Argv[], u8 *OutputBuffer, int Len)
{
command_output Result = {0};
-
+
int CommandPipe[2];
int Error = pipe(CommandPipe);
Assert(Error != -1);
-
+
int Pid = fork();
Assert(Pid != -1);
-
+
// Run command in child
if (!Pid)
{
dup2(CommandPipe[1], STDOUT_FILENO); //redirect stdout to Pipe
close(CommandPipe[0]);
close(CommandPipe[1]);
-
+
int fd = open("/dev/null", O_WRONLY);
dup2(fd, STDERR_FILENO);
-
+
execvp(Command, Argv);
}
-
+
// Wait for child
int statval;
waitpid(Pid, &statval, 0);
-
+
if(WIFEXITED(statval))
{
int ExitCode = WEXITSTATUS(statval);
@@ -285,12 +285,12 @@ run_command_get_output(char *Command, char *Argv[], u8 *OutputBuffer, int Len)
Result.Error = 1;
return Result;
}
-
+
close(CommandPipe[1]);
-
+
Result.NumRead = read(CommandPipe[0], OutputBuffer, Len);
Assert(Result.NumRead != -1);
-
+
return Result;
}
@@ -308,49 +308,49 @@ DisplayChat(Arena* ScratchArena,
};
u32 FreeHeight = global.height - TextBox.H;
TextBox.Y = FreeHeight;
-
+
#define MIN_TEXT_WIDTH_FOR_WRAPPING 20
s32 MinBoxWidth = TEXTBOX_MIN_WIDTH;
s32 InputBoxTextWidth = TextBox.W - MinBoxWidth + 2;
bool ShouldIncreaseSize = (
- (s32)InputLen >= InputBoxTextWidth &&
- InputBoxTextWidth > MIN_TEXT_WIDTH_FOR_WRAPPING
- );
+ (s32)InputLen >= InputBoxTextWidth &&
+ InputBoxTextWidth > MIN_TEXT_WIDTH_FOR_WRAPPING
+ );
if (ShouldIncreaseSize)
{
TextBox.H++;
}
#undef MIN_TEXT_WIDTH_FOR_WRAPPING
-
+
rect TextR = {
TextBox.X + 2, TextBox.Y + 1,
TextBox.W - 2*TEXTBOX_PADDING_X - 2*TEXTBOX_BORDER_WIDTH,
TextBox.H - 2*TEXTBOX_BORDER_WIDTH
};
-
+
if (global.height < TextBox.H || global.width < TextBox.W)
{
tb_hide_cursor();
return;
}
-
+
bytebuf_puts(&global.out, global.caps[TB_CAP_SHOW_CURSOR]);
global.cursor_x = TextR.X;
global.cursor_y = TextR.Y;
DrawBox(TextBox, 0);
DrawTextBox(TextR, Input, InputLen);
-
+
// Print vertical bar
s32 VerticalBarOffset = TIMESTAMP_LEN + AUTHOR_LEN + 2;
for (u32 Y = 0; Y < FreeHeight; Y++)
tb_print(VerticalBarOffset, Y, 0, 0, "│");
-
+
// show error popup if server disconnected
if (fds[FDS_UNI].fd == -1 || fds[FDS_BI].fd == -1)
{
popup(TB_RED, TB_BLACK, (u8*)"Server disconnected.");
}
-
+
// Print messages in msgsArena, if there are too many to display, start printing from an offset.
// Looks like this:
// 03:24:29 [1234567890ab] hello homes how are
@@ -358,53 +358,53 @@ DisplayChat(Arena* ScratchArena,
// 03:24:33 [TlasT] │ I am fine
// 03:24:33 [Fin] │ I am too
{
-
+
// If there is not enough space to draw, do not draw
if (FreeHeight <= 0) return;
-
+
// Used to go to the next message in MessagesArena by incrementing with the messages' size.
u8* MessageAddress = MessagesArena->addr;
Assert(MessageAddress != 0);
-
+
// Skip messages if there is not enough space to display them all
u32 MessagesOffset = (MessagesNum > FreeHeight) ? MessagesNum - FreeHeight : 0;
for (u32 MessageIndex = 0; MessageIndex < MessagesOffset; MessageIndex++)
{
HeaderMessage* header = (HeaderMessage*)MessageAddress;
MessageAddress += sizeof(*header);
-
+
switch (header->type)
{
- case HEADER_TYPE_TEXT:
- {
- TextMessage* message = (TextMessage*)MessageAddress;
- MessageAddress += TEXTMESSAGE_SIZE;
- MessageAddress += message->len * sizeof(*message->text);
- break;
- }
- case HEADER_TYPE_PRESENCE:
+ case HEADER_TYPE_TEXT:
+ {
+ TextMessage* message = (TextMessage*)MessageAddress;
+ MessageAddress += TEXTMESSAGE_SIZE;
+ MessageAddress += message->len * sizeof(*message->text);
+ break;
+ }
+ case HEADER_TYPE_PRESENCE:
MessageAddress += sizeof(PresenceMessage);
break;
- case HEADER_TYPE_HISTORY:
+ case HEADER_TYPE_HISTORY:
MessageAddress += sizeof(HistoryMessage);
break;
- default:
+ default:
// unhandled message type
Assert(0);
}
}
-
+
u32 MessageY = 0;
-
+
for (u32 i = MessagesOffset;
- i < MessagesNum;
- i++)
+ i < MessagesNum;
+ i++)
{
if (MessageY >= FreeHeight) break;
-
+
HeaderMessage* header = (HeaderMessage*)MessageAddress;
MessageAddress += sizeof(*header);
-
+
User* client = get_user_by_id(ClientsArena, header->id);
if (!client)
{
@@ -412,84 +412,84 @@ DisplayChat(Arena* ScratchArena,
client = add_user_info(ClientsArena, fds[FDS_BI].fd, header->id);
}
Assert(client);
-
+
switch (header->type)
{
- case HEADER_TYPE_TEXT:
- {
- TextMessage* message = (TextMessage*)MessageAddress;
-
-
- // Color own messages
- u32 fg = 0;
- if (user.ID == header->id)
- {
- fg = TB_CYAN;
- }
- else
- {
- fg = TB_MAGENTA;
- }
-
- // prefix is of format "HH:MM:SS [<author>] ", create it
- u8 timestamp[TIMESTAMP_LEN];
- formatTimestamp(timestamp, message->timestamp);
-
- tb_printf(0, MessageY, TB_WHITE, 0, "%s", timestamp);
- tb_printf(TIMESTAMP_LEN, MessageY, fg, 0, "[%s]", client->Author);
-
- // Only display when there is enough space
- if (global.width > VerticalBarOffset + 2)
+ case HEADER_TYPE_TEXT:
{
- raw_result RawText = markdown_to_raw(ScratchArena, (wchar_t*)&message->text, message->len);
- markdown_formatoptions MDFormat = preprocess_markdown(ScratchArena,
- (wchar_t*)&message->text,
- message->len);
-
- u32 timesWrapped = tb_print_wrapped_with_markdown(VerticalBarOffset + 2, MessageY, fg, 0,
- RawText.Text, RawText.Len,
- global.width, global.height, MDFormat);
-
- // Free the memory
- ScratchArena->pos = 0;
-
- MessageY += timesWrapped;
- }
- else
+ TextMessage* message = (TextMessage*)MessageAddress;
+
+
+ // Color own messages
+ u32 fg = 0;
+ if (user.ID == header->id)
+ {
+ fg = TB_CYAN;
+ }
+ else
+ {
+ fg = TB_MAGENTA;
+ }
+
+ // prefix is of format "HH:MM:SS [<author>] ", create it
+ u8 timestamp[TIMESTAMP_LEN];
+ formatTimestamp(timestamp, message->timestamp);
+
+ tb_printf(0, MessageY, TB_WHITE, 0, "%s", timestamp);
+ tb_printf(TIMESTAMP_LEN, MessageY, fg, 0, "[%s]", client->Author);
+
+ // Only display when there is enough space
+ if (global.width > VerticalBarOffset + 2)
+ {
+ raw_result RawText = markdown_to_raw(ScratchArena, (wchar_t*)&message->text, message->len);
+ markdown_formatoptions MDFormat = preprocess_markdown(ScratchArena,
+ (wchar_t*)&message->text,
+ message->len);
+
+ u32 timesWrapped = tb_print_wrapped_with_markdown(VerticalBarOffset + 2, MessageY, fg, 0,
+ RawText.Text, RawText.Len,
+ global.width, global.height, MDFormat);
+
+ // Free the memory
+ ScratchArena->pos = 0;
+
+ MessageY += timesWrapped;
+ }
+ else
+ {
+ // We still displayed the timestamp so we need to increment the Y.
+ MessageY++;
+ }
+
+ u32 message_size = TEXTMESSAGE_SIZE + message->len * sizeof(*message->text);
+ MessageAddress += message_size;
+ } break;
+ case HEADER_TYPE_PRESENCE:
{
- // We still displayed the timestamp so we need to increment the Y.
+ PresenceMessage* message = (PresenceMessage*)MessageAddress;
+ tb_printf(TIMESTAMP_LEN, MessageY, TB_MAGENTA, 0, "[%s]", client->Author);
+
+ // Wrap Text in '*'
+ u8 *Text = presenceTypeString(message->type);
+ u32 Len = 0;
+ while(Text[Len]) Len++;
+ u32 FormattedText[Len+2];
+ FormattedText[0] = '*';
+ FormattedText[Len+1] = '*';
+ for (u32 i = 1; i < Len + 1; i++) FormattedText[i] = Text[i-1];
+
+ tb_print_markdown(VerticalBarOffset + 2, MessageY, 0, 0, FormattedText, Len + 2);
+
MessageY++;
- }
-
- u32 message_size = TEXTMESSAGE_SIZE + message->len * sizeof(*message->text);
- MessageAddress += message_size;
- } break;
- case HEADER_TYPE_PRESENCE:
- {
- PresenceMessage* message = (PresenceMessage*)MessageAddress;
- tb_printf(TIMESTAMP_LEN, MessageY, TB_MAGENTA, 0, "[%s]", client->Author);
-
- // Wrap Text in '*'
- u8 *Text = presenceTypeString(message->type);
- u32 Len = 0;
- while(Text[Len]) Len++;
- u32 FormattedText[Len+2];
- FormattedText[0] = '*';
- FormattedText[Len+1] = '*';
- for (u32 i = 1; i < Len + 1; i++) FormattedText[i] = Text[i-1];
-
- tb_print_markdown(VerticalBarOffset + 2, MessageY, 0, 0, FormattedText, Len + 2);
-
- MessageY++;
- MessageAddress += sizeof(*message);
- } break;
- case HEADER_TYPE_HISTORY:
- {
- HistoryMessage* message = (HistoryMessage*)MessageAddress;
- MessageAddress += sizeof(*message);
- // TODO: implement
- } break;
- default:
+ MessageAddress += sizeof(*message);
+ } break;
+ case HEADER_TYPE_HISTORY:
+ {
+ HistoryMessage* message = (HistoryMessage*)MessageAddress;
+ MessageAddress += sizeof(*message);
+ // TODO: implement
+ } break;
+ default:
tb_printf(0, MessageY, 0, 0, "%s", headerTypeString(header->type));
MessageY++;
break;
@@ -507,40 +507,40 @@ main(int argc, char** argv)
fprintf(stderr, "usage: chatty <username>\n");
return 1;
}
-
+
u32 arg_len = strlen(argv[1]);
Assert(arg_len <= AUTHOR_LEN - 1);
memcpy(user.Author, argv[1], arg_len);
user.Author[arg_len] = '\0';
-
+
s32 err = 0; // error code for functions
-
+
u32 MessagesNum = 0; // Number of messages in msgsArena
s32 nrecv = 0; // number of bytes received
-
+
wchar_t Input[MAX_INPUT_LEN] = {0}; // input buffer
u32 InputIndex = 0; // number of characters in input
-
+
Arena ScratchArena;
Arena MessagesArena;
Arena ClientsArena;
ArenaAlloc(&MessagesArena, Megabytes(64)); // Messages received & sent
ArenaAlloc(&ClientsArena, Megabytes(1)); // Arena for storing clients
ArenaAlloc(&ScratchArena, Megabytes(1)); // Arena for storing clients
-
+
struct tb_event ev; // event fork keypress & resize
u8 quit = 0; // boolean to indicate if we want to quit the main loop
u8* quitmsg = 0; // this string will be printed before returning from main
-
+
pthread_t thr_rec; // thread for reconnecting to server when disconnected
-
+
#ifdef LOGGING
LogFD = open(LOGFILE, O_RDWR | O_CREAT | O_TRUNC, 0600);
Assert(LogFD != -1);
#else
logfd = 2; // stderr
#endif
-
+
// poopoo C cannot infer type
struct pollfd fds[FDS_MAX] = {
{-1, POLLIN, 0}, // FDS_BI
@@ -548,18 +548,18 @@ main(int argc, char** argv)
{-1, POLLIN, 0}, // FDS_TTY
{-1, POLLIN, 0}, // FDS_RESIZE
};
-
+
address = (struct sockaddr_in){
AF_INET,
htons(PORT),
{0},
{0},
};
-
+
#ifdef IMPORT_ID
// File for storing the user's ID.
u32 idfile = open(ID_FILE, O_RDWR | O_CREAT, 0600);
- s32 nread = read(idfile, &user.id, sizeof(user.id));
+ s32 nread = read(idfile, &user.ID, sizeof(user.ID));
Assert(nread != -1);
#endif
/* Authentication */
@@ -591,43 +591,43 @@ main(int argc, char** argv)
fds[FDS_BI].fd = bifd;
fds[FDS_UNI].fd = unifd;
}
-
+
#ifdef IMPORT_ID
// Save id
- write(idfile, &user.id, sizeof(user.id));
+ write(idfile, &user.ID, sizeof(user.ID));
#endif
-
+
LoggingF("Got ID: %lu\n", user.ID);
-
+
// for wide character printing
Assert(setlocale(LC_ALL, ""));
-
+
// init
tb_init();
tb_get_fds(&fds[FDS_TTY].fd, &fds[FDS_RESIZE].fd);
-
+
DisplayChat(&ScratchArena,
&MessagesArena, MessagesNum,
&ClientsArena, fds,
Input, InputIndex);
tb_present();
-
+
// main loop
while (!quit)
{
err = poll(fds, FDS_MAX, TIMEOUT_POLL);
// ignore resize events and use them to redraw the screen
Assert(err != -1 || errno == EINTR);
-
+
tb_clear();
-
+
if (fds[FDS_UNI].revents & POLLIN)
{
// got data from server
HeaderMessage header;
nrecv = recv(fds[FDS_UNI].fd, &header, sizeof(header), 0);
Assert(nrecv != -1);
-
+
// Server disconnects
if (nrecv == 0)
{
@@ -646,39 +646,39 @@ main(int argc, char** argv)
LoggingF("Header received does not match version\n");
continue;
}
-
+
void* addr = ArenaPush(&MessagesArena, sizeof(header));
memcpy(addr, &header, sizeof(header));
-
+
// Messages handled from server
switch (header.type)
{
- case HEADER_TYPE_TEXT:
+ case HEADER_TYPE_TEXT:
recvTextMessage(&MessagesArena, fds[FDS_UNI].fd);
MessagesNum++;
break;
- case HEADER_TYPE_PRESENCE:;
+ case HEADER_TYPE_PRESENCE:;
PresenceMessage* message = ArenaPush(&MessagesArena, sizeof(*message));
nrecv = recv(fds[FDS_UNI].fd, message, sizeof(*message), 0);
Assert(nrecv != -1);
Assert(nrecv == sizeof(*message));
MessagesNum++;
break;
- default:
+ default:
LoggingF("Got unhandled message: %s\n", headerTypeString(header.type));
break;
}
}
}
-
+
if (fds[FDS_TTY].revents & POLLIN)
{
// got a key event
tb_poll_event(&ev);
-
+
switch (ev.key)
{
- case TB_KEY_CTRL_W:
+ case TB_KEY_CTRL_W:
// delete consecutive whitespace
while (InputIndex)
{
@@ -700,111 +700,111 @@ main(int argc, char** argv)
InputIndex--;
}
break;
- case TB_KEY_CTRL_Z:
- {
- pid_t pid = getpid();
- tb_shutdown();
- kill(pid, SIGSTOP);
- tb_init();
- } break;
- case TB_KEY_CTRL_Y: // Paste clipboard contents to input
- {
- u32 OutputBufferLen = MAX_INPUT_LEN - InputIndex;
- if (OutputBufferLen <= 0) break;
-
- u8 OutputBuffer[OutputBufferLen];
-
- char *PathName = "xclip";
- char *Argv[] = {PathName, "-o", "-sel", "c", 0};
-
- command_output Output = run_command_get_output(PathName, Argv, OutputBuffer, OutputBufferLen - 1);
- if (Output.Error) break;
-
- // Remove trailing whitespace
- int BufferIndex = Output.NumRead - 1;
- while (BufferIndex > 0 &&
- (OutputBuffer[BufferIndex] == '\n' ||
- OutputBuffer[BufferIndex] == '\t'))
+ case TB_KEY_CTRL_Z:
{
- OutputBuffer[BufferIndex] = 0;
- BufferIndex--;
- }
-
- // Append to output
- for (s32 BufferIndex = 0; BufferIndex < Output.NumRead; BufferIndex++)
+ pid_t pid = getpid();
+ tb_shutdown();
+ kill(pid, SIGSTOP);
+ tb_init();
+ } break;
+ case TB_KEY_CTRL_Y: // Paste clipboard contents to input
{
- // convert u8 to u32
- u32 ch = OutputBuffer[BufferIndex];
- Input[InputIndex] = ch;
- InputIndex++;
- }
-
- } break;
- case TB_KEY_CTRL_I:
- {
- for (u32 i = 0;
- i < TAB_WIDTH && InputIndex < MAX_INPUT_LEN - 1;
- i++)
+ u32 OutputBufferLen = MAX_INPUT_LEN - InputIndex;
+ if (OutputBufferLen <= 0) break;
+
+ u8 OutputBuffer[OutputBufferLen];
+
+ char *PathName = "xclip";
+ char *Argv[] = {PathName, "-o", "-sel", "c", 0};
+
+ command_output Output = run_command_get_output(PathName, Argv, OutputBuffer, OutputBufferLen - 1);
+ if (Output.Error) break;
+
+ // Remove trailing whitespace
+ int BufferIndex = Output.NumRead - 1;
+ while (BufferIndex > 0 &&
+ (OutputBuffer[BufferIndex] == '\n' ||
+ OutputBuffer[BufferIndex] == '\t'))
+ {
+ OutputBuffer[BufferIndex] = 0;
+ BufferIndex--;
+ }
+
+ // Append to output
+ for (s32 BufferIndex = 0; BufferIndex < Output.NumRead; BufferIndex++)
+ {
+ // convert u8 to u32
+ u32 ch = OutputBuffer[BufferIndex];
+ Input[InputIndex] = ch;
+ InputIndex++;
+ }
+
+ } break;
+ case TB_KEY_CTRL_I:
{
- Input[InputIndex] = L' ';
- InputIndex++;
- }
- } break;
- case TB_KEY_BACKSPACE2:
+ for (u32 i = 0;
+ i < TAB_WIDTH && InputIndex < MAX_INPUT_LEN - 1;
+ i++)
+ {
+ Input[InputIndex] = L' ';
+ InputIndex++;
+ }
+ } break;
+ case TB_KEY_BACKSPACE2:
if (InputIndex) InputIndex--;
Input[InputIndex] = 0;
break;
- case TB_KEY_CTRL_D:
- case TB_KEY_CTRL_C:
+ case TB_KEY_CTRL_D:
+ case TB_KEY_CTRL_C:
quit = 1;
break;
- case TB_KEY_CTRL_M: // send message
- {
- raw_result RawText = markdown_to_raw(0, Input, InputIndex);
-
- if (RawText.Len == 0)
- // do not send empty message
- break;
- if (fds[FDS_UNI].fd == -1)
- // do not send message to disconnected server
- break;
-
- // null terminate
- Input[InputIndex] = 0;
- InputIndex++;
-
- // Save header
- HeaderMessage* header = ArenaPush(&MessagesArena, sizeof(*header));
- header->version = PROTOCOL_VERSION;
- header->type = HEADER_TYPE_TEXT;
- header->id = user.ID;
-
- // Save message
- TextMessage* sendmsg = ArenaPush(&MessagesArena, TEXTMESSAGE_SIZE);
- sendmsg->timestamp = time(0);
- sendmsg->len = InputIndex;
-
- u32 text_size = InputIndex * sizeof(*Input);
- ArenaPush(&MessagesArena, text_size);
- memcpy(&sendmsg->text, Input, text_size);
-
- sendAnyMessage(fds[FDS_UNI].fd, *header, sendmsg);
-
- MessagesNum++;
- // also clear input
- } // fallthrough
- case TB_KEY_CTRL_U: // clear input
+ case TB_KEY_CTRL_M: // send message
+ {
+ raw_result RawText = markdown_to_raw(0, Input, InputIndex);
+
+ if (RawText.Len == 0)
+ // do not send empty message
+ break;
+ if (fds[FDS_UNI].fd == -1)
+ // do not send message to disconnected server
+ break;
+
+ // null terminate
+ Input[InputIndex] = 0;
+ InputIndex++;
+
+ // Save header
+ HeaderMessage* header = ArenaPush(&MessagesArena, sizeof(*header));
+ header->version = PROTOCOL_VERSION;
+ header->type = HEADER_TYPE_TEXT;
+ header->id = user.ID;
+
+ // Save message
+ TextMessage* sendmsg = ArenaPush(&MessagesArena, TEXTMESSAGE_SIZE);
+ sendmsg->timestamp = time(0);
+ sendmsg->len = InputIndex;
+
+ u32 text_size = InputIndex * sizeof(*Input);
+ ArenaPush(&MessagesArena, text_size);
+ memcpy(&sendmsg->text, Input, text_size);
+
+ sendAnyMessage(fds[FDS_UNI].fd, *header, sendmsg);
+
+ MessagesNum++;
+ // also clear input
+ } // fallthrough
+ case TB_KEY_CTRL_U: // clear input
bzero(Input, InputIndex * sizeof(*Input));
InputIndex = 0;
break;
- default:
+ default:
if (ev.ch == 0)
break;
-
+
// TODO: show error
if (InputIndex == MAX_INPUT_LEN - 1) // last byte reserved for \0
break;
-
+
// append key to input buffer
Input[InputIndex] = ev.ch;
InputIndex++;
@@ -812,23 +812,23 @@ main(int argc, char** argv)
if (quit)
break;
}
-
+
// These are used to redraw the screen from threads
if (fds[FDS_RESIZE].revents & POLLIN)
{
// ignore
tb_poll_event(&ev);
}
-
+
DisplayChat(&ScratchArena, &MessagesArena, MessagesNum, &ClientsArena, fds, Input, InputIndex);
-
+
tb_present();
}
-
+
tb_shutdown();
-
+
if (quitmsg != 0)
printf("%s\n", quitmsg);
-
+
return 0;
}