diff --git a/README.md b/README.md index 3318693..4f2b528 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ Toxic is an ncurses based instant messaging client for [Tox](http://tox.im) whic * Execute the configure script with ```./configure``` (you may need to pass it the location of your dependency libraries, i.e.): ```./configure --prefix=/where/to/install --with-libtoxcore-headers=/path/to/ProjectTox-Core/toxcore --with-libtoxcore-libs=/path/to/ProjectTox-Core/build/toxcore --with-libsodium-headers=/path/to/libsodium/include/ --with-libsodium-libs=/path/to/sodiumtest/lib/ ``` +* Compile with --disable-av to build without audio call support * Compile and install the program with ```make && sudo make install``` #### Notes diff --git a/build/Makefile.am b/build/Makefile.am index 55359f8..d3940e1 100644 --- a/build/Makefile.am +++ b/build/Makefile.am @@ -29,7 +29,9 @@ toxic_SOURCES = $(top_srcdir)/src/main.c \ $(top_srcdir)/src/log.c \ $(top_srcdir)/src/log.h \ $(top_srcdir)/src/file_senders.c \ - $(top_srcdir)/src/file_senders.h + $(top_srcdir)/src/file_senders.h \ + $(top_srcdir)/src/line_info.c \ + $(top_srcdir)/src/line_info.h toxic_CFLAGS = -I$(top_srcdir) \ $(NCURSES_CFLAGS) \ diff --git a/configure.ac b/configure.ac index df0d35e..0becaaf 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.65]) -AC_INIT([toxic], [0.3.0], [https://tox.im/]) +AC_INIT([toxic], [0.3.2], [https://tox.im/]) AC_CONFIG_AUX_DIR(configure_aux) AC_CONFIG_SRCDIR([src/main.c]) AC_CONFIG_HEADERS([config.h]) diff --git a/misc/DHTnodes b/misc/DHTnodes index 5e27942..d11d4ab 100644 --- a/misc/DHTnodes +++ b/misc/DHTnodes @@ -1,2 +1,8 @@ 192.254.75.98 33445 FE3914F4616E227F29B2103450D6B55A836AD4BD23F97144E2C4ABE8D504FE1B 2607:5600:284::2 33445 FE3914F4616E227F29B2103450D6B55A836AD4BD23F97144E2C4ABE8D504FE1B +23.226.230.47 33445 A09162D68618E742FFBCA1C2C70385E6679604B2D80EA6E84AD0996A1AC8A074 +173.255.223.85 33445 92E0CE88651FC89D54D6A3110FD08854ECD487613E69BFB1002380D35FD4F947 +144.76.60.215 33445 04119E835DF3E78BACF0F84235B300546AF8B936F035185E2A8E9E0A67C8924F +109.169.46.133 33445 4086B340BF2C2C3CC5412BCF673080EF10CF5E75A06AC960497BD85B9DEA2E64 +54.199.139.199 33445 7F9C31FE850E97CEFD4C4591DF93FC757C7C12549DDD55F8EEAECC34FE76C029 +66.175.223.88 33445 B24E2FB924AE66D023FE1E42A2EE3B432010206F751A2FFD3E297383ACF1572E diff --git a/src/audio_call.c b/src/audio_call.c index 9fbe0b1..5850151 100644 --- a/src/audio_call.c +++ b/src/audio_call.c @@ -11,6 +11,7 @@ #include "chat_commands.h" #include "global_commands.h" #include "toxic_windows.h" +#include "line_info.h" #include #include @@ -51,7 +52,6 @@ struct _ASettings { int ttas; /* Transmission thread active status (0 - stopped, 1- running) */ } ASettins; - void callback_recv_invite ( void *arg ); void callback_recv_ringing ( void *arg ); void callback_recv_starting ( void *arg ); @@ -65,16 +65,24 @@ void callback_requ_timeout ( void *arg ); void callback_peer_timeout ( void* arg ); +static void print_err (ToxWindow *self, uint8_t *error_str) +{ + line_info_add(self, NULL, NULL, NULL, error_str, SYS_MSG, 0, 0); +} + /* Opens device under current index */ -int device_open (WINDOW *window, _Devices type) +int device_open (ToxWindow *self, _Devices type) { + WINDOW *window = self->chatwin->history; + /* Do not error if no device */ if ( !ASettins.device[type].size ) return 0; ALCdevice* prev_device = ASettins.device[type].dhndl; - const char* error = NULL; + uint8_t msg[MAX_STR_SIZE]; + uint8_t* error = NULL; if ( type == input ) { ASettins.device[type].dhndl = alcCaptureOpenDevice( @@ -101,7 +109,10 @@ int device_open (WINDOW *window, _Devices type) if ( prev_device ) alcCaptureCloseDevice(prev_device); - if ( window ) wprintw(window, "Input device: %s\n", ASettins.device[type].devices[ASettins.device[type].index]); + if ( window ) { + snprintf(msg, sizeof(msg), "Input device: %s", ASettins.device[type].devices[ASettins.device[type].index]); + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); + } } ASettins.device[type].ctx = NULL; @@ -136,20 +147,27 @@ int device_open (WINDOW *window, _Devices type) ASettins.device[type].ctx = alcCreateContext(ASettins.device[type].dhndl, NULL); - if ( window ) wprintw(window, "Output device: %s\n", ASettins.device[type].devices[ASettins.device[type].index]); + if ( window ) { + snprintf(msg, sizeof(msg), "Output device: %s", ASettins.device[type].devices[ASettins.device[type].index]); + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); + } } } if ( error ) { - if ( window ) wprintw(window, "Error: %s\n", error); + if ( window ) { + snprintf(msg, sizeof(msg), "Error: %s", error); + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); + } + return -1; } else return 0; } -int device_close (WINDOW *window, _Devices type) +int device_close (ToxWindow *self, _Devices type) { - const char* device = NULL; + uint8_t* device = NULL; if ( ASettins.device[type].dhndl ) { if (type == input) { @@ -169,11 +187,14 @@ int device_close (WINDOW *window, _Devices type) ASettins.device[type].index = ASettins.device[type].dix; } - if ( window && device ) wprintw(window, "Closed %s device\n", device); + if ( self && device ) { + uint8_t msg[MAX_STR_SIZE]; + snprintf(msg, sizeof(msg), "Closed %s device", device); + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); + } } - -ToxAv* init_audio(ToxWindow* window, Tox* tox) +ToxAv* init_audio(ToxWindow* self, Tox* tox) { ASettins.errors = NoError; ASettins.ttas = 0; /* Not running */ @@ -214,7 +235,8 @@ ToxAv* init_audio(ToxWindow* window, Tox* tox) } if (!ASettins.device[input].size && !ASettins.device[output].size) { - wprintw(window->window, "No devices: disabling audio!\n"); + uint8_t *msg = "No devices: disabling audio!"; + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); ASettins.av = NULL; } else { @@ -230,19 +252,19 @@ ToxAv* init_audio(ToxWindow* window, Tox* tox) return NULL; } - toxav_register_callstate_callback(callback_call_started, av_OnStart, window); - toxav_register_callstate_callback(callback_call_canceled, av_OnCancel, window); - toxav_register_callstate_callback(callback_call_rejected, av_OnReject, window); - toxav_register_callstate_callback(callback_call_ended, av_OnEnd, window); - toxav_register_callstate_callback(callback_recv_invite, av_OnInvite, window); + toxav_register_callstate_callback(callback_call_started, av_OnStart, self); + toxav_register_callstate_callback(callback_call_canceled, av_OnCancel, self); + toxav_register_callstate_callback(callback_call_rejected, av_OnReject, self); + toxav_register_callstate_callback(callback_call_ended, av_OnEnd, self); + toxav_register_callstate_callback(callback_recv_invite, av_OnInvite, self); - toxav_register_callstate_callback(callback_recv_ringing, av_OnRinging, window); - toxav_register_callstate_callback(callback_recv_starting, av_OnStarting, window); - toxav_register_callstate_callback(callback_recv_ending, av_OnEnding, window); + toxav_register_callstate_callback(callback_recv_ringing, av_OnRinging, self); + toxav_register_callstate_callback(callback_recv_starting, av_OnStarting, self); + toxav_register_callstate_callback(callback_recv_ending, av_OnEnding, self); - toxav_register_callstate_callback(callback_recv_error, av_OnError, window); - toxav_register_callstate_callback(callback_requ_timeout, av_OnRequestTimeout, window); - toxav_register_callstate_callback(callback_peer_timeout, av_OnPeerTimeout, window); + toxav_register_callstate_callback(callback_recv_error, av_OnError, self); + toxav_register_callstate_callback(callback_requ_timeout, av_OnRequestTimeout, self); + toxav_register_callstate_callback(callback_peer_timeout, av_OnPeerTimeout, self); } return ASettins.av; @@ -256,7 +278,6 @@ void terminate_audio() toxav_kill(ASettins.av); } - int errors() { return ASettins.errors; @@ -376,7 +397,7 @@ cleanup: _cbend; } -int start_transmission() +int start_transmission(ToxWindow *self) { if ( !ASettins.av ) return -1; @@ -385,9 +406,9 @@ int start_transmission() return -1; /* Now open our devices */ - if ( -1 == device_open(NULL, input) ) + if ( -1 == device_open(self, input) ) return -1; - if ( -1 == device_open(NULL, output)) + if ( -1 == device_open(self, output)) return -1; /* Don't provide support for video */ @@ -483,7 +504,8 @@ void callback_peer_timeout ( void* arg ) */ void cmd_call(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) { - const char* error_str; + uint8_t msg[MAX_STR_SIZE]; + uint8_t* error_str; if (argc != 0) { error_str = "Invalid syntax!"; goto on_error; } @@ -497,17 +519,19 @@ void cmd_call(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA goto on_error; } - - wprintw(window, "Calling...\n"); - + + error_str = "Calling..."; + line_info_add(self, NULL, NULL, NULL, error_str, SYS_MSG, 0, 0); + return; -on_error: - wprintw(window, "%s\n", error_str); +on_error: + snprintf(msg, sizeof(msg), "%s", error_str); + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); } void cmd_answer(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) { - const char* error_str; + uint8_t* error_str; if (argc != 0) { error_str = "Invalid syntax!"; goto on_error; } @@ -527,12 +551,12 @@ void cmd_answer(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[ return; on_error: - wprintw(window, "%s\n", error_str); + print_err (self, error_str); } void cmd_reject(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) { - const char* error_str; + uint8_t* error_str; if (argc != 0) { error_str = "Invalid syntax!"; goto on_error; } @@ -552,12 +576,12 @@ void cmd_reject(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[ return; on_error: - wprintw(window, "%s\n", error_str); + print_err (self, error_str); } void cmd_hangup(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) { - const char* error_str; + uint8_t* error_str; if (argc != 0) { error_str = "Invalid syntax!"; goto on_error; } @@ -575,12 +599,12 @@ void cmd_hangup(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[ return; on_error: - wprintw(window, "%s\n", error_str); + print_err (self, error_str); } void cmd_cancel(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) { - const char* error_str; + uint8_t* error_str; if (argc != 0) { error_str = "Invalid syntax!"; goto on_error; } @@ -599,13 +623,14 @@ void cmd_cancel(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[ return; on_error: - wprintw(window, "%s\n", error_str); + print_err (self, error_str); } void cmd_list_devices(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) { - const char* error_str; + uint8_t msg[MAX_STR_SIZE]; + uint8_t* error_str; if ( argc != 1 ) { if ( argc < 1 ) error_str = "Type must be specified!"; @@ -623,22 +648,26 @@ void cmd_list_devices(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (* type = output; else { - wprintw(window, "Invalid type: %s\n", argv[1]); + snprintf(msg, sizeof(msg), "Invalid type: %s", argv[1]); + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); return; } int i = 0; - for ( ; i < ASettins.device[type].size; i ++) - wprintw(window, "%d: %s\n", i, ASettins.device[type].devices[i]); - + for ( ; i < ASettins.device[type].size; i ++) { + snprintf(msg, sizeof(msg), "%d: %s", i, ASettins.device[type].devices[i]); + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); + } + return; on_error: - wprintw(window, "%s\n", error_str); + print_err (self, error_str); } void cmd_change_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) { - const char* error_str; + uint8_t msg[MAX_STR_SIZE]; + uint8_t* error_str; if ( argc != 2 ) { if ( argc < 1 ) error_str = "Type must be specified!"; @@ -662,7 +691,8 @@ void cmd_change_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char ( type = output; else { - wprintw(window, "Invalid type: %s\n", argv[1]); + snprintf(msg, sizeof(msg), "Invalid type: %s", argv[1]); + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); return; } @@ -681,9 +711,10 @@ void cmd_change_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char ( } ASettins.device[type].index = selection; - wprintw(window, "Selected: %s\n", ASettins.device[type].devices[selection]); + snprintf(msg, sizeof(msg), "Selected: %s", ASettins.device[type].devices[selection]); + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); return; on_error: - wprintw(window, "%s\n", error_str); -} \ No newline at end of file + print_err (self, error_str); +} diff --git a/src/audio_call.h b/src/audio_call.h index 1b8801d..49cdd68 100644 --- a/src/audio_call.h +++ b/src/audio_call.h @@ -20,11 +20,11 @@ typedef enum _AudioError /* You will have to pass pointer to first member of 'windows' * declared in windows.c otherwise undefined behaviour will */ -ToxAv* init_audio(ToxWindow* window, Tox* tox); +ToxAv* init_audio(ToxWindow* self, Tox* tox); void terminate_audio(); int errors(); -int start_transmission(); +int start_transmission(ToxWindow *self); #endif /* _audio_h */ \ No newline at end of file diff --git a/src/chat.c b/src/chat.c index ca1666a..9111423 100644 --- a/src/chat.c +++ b/src/chat.c @@ -34,6 +34,7 @@ #include "friendlist.h" #include "toxic_strings.h" #include "log.h" +#include "line_info.h" #ifdef _SUPPORT_AUDIO #include "audio_call.h" @@ -84,7 +85,7 @@ static const uint8_t chat_cmd_list[AC_NUM_CHAT_COMMANDS][MAX_CMDNAME_SIZE] = { #endif /* _SUPPORT_AUDIO */ }; -static void set_typingstatus(ToxWindow *self, Tox *m, bool is_typing) +static void set_typingstatus(ToxWindow *self, Tox *m, uint8_t is_typing) { ChatContext *ctx = self->chatwin; @@ -99,6 +100,7 @@ void kill_chat_window(ToxWindow *self) StatusBar *statusbar = self->stb; log_disable(ctx->log); + line_info_cleanup(ctx->hst); int f_num = self->num; delwin(ctx->linewin); @@ -107,38 +109,36 @@ void kill_chat_window(ToxWindow *self) disable_chatwin(f_num); free(ctx->log); + free(ctx->hst); free(ctx); free(statusbar); } -static void chat_onMessage(ToxWindow *self, Tox *m, int num, uint8_t *msg, uint16_t len) +static void chat_onMessage(ToxWindow *self, Tox *m, int32_t num, uint8_t *msg, uint16_t len) { if (self->num != num) return; + msg[len] = '\0'; + ChatContext *ctx = self->chatwin; - uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'}; - tox_get_name(m, num, nick); - nick[TOXIC_MAX_NAME_LENGTH] = '\0'; + uint8_t nick[TOX_MAX_NAME_LENGTH]; + int n_len = tox_get_name(m, num, nick); - print_time(ctx->history); - wattron(ctx->history, COLOR_PAIR(CYAN)); - wprintw(ctx->history, "%s: ", nick); - wattroff(ctx->history, COLOR_PAIR(CYAN)); + n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH-1); + nick[n_len] = '\0'; - if (msg[0] == '>') { - wattron(ctx->history, COLOR_PAIR(GREEN)); - wprintw(ctx->history, "%s\n", msg); - wattroff(ctx->history, COLOR_PAIR(GREEN)); - } else - wprintw(ctx->history, "%s\n", msg); + uint8_t timefrmt[TIME_STR_SIZE]; + get_time_str(timefrmt); + + line_info_add(self, timefrmt, nick, NULL, msg, IN_MSG, 0, 0); write_to_log(msg, nick, ctx->log, false); alert_window(self, WINDOW_ALERT_1, true); } -static void chat_onConnectionChange(ToxWindow *self, Tox *m, int num, uint8_t status) +static void chat_onConnectionChange(ToxWindow *self, Tox *m, int32_t num, uint8_t status) { if (self->num != num) return; @@ -150,11 +150,11 @@ static void chat_onConnectionChange(ToxWindow *self, Tox *m, int num, uint8_t st friends[num].is_typing = tox_get_is_typing(m, num); } else { statusbar->is_online = false; - friends[num].is_typing = false; + friends[num].is_typing = 0; } } -static void chat_onTypingChange(ToxWindow *self, Tox *m, int num, int is_typing) +static void chat_onTypingChange(ToxWindow *self, Tox *m, int32_t num, uint8_t is_typing) { if (self->num != num) return; @@ -162,37 +162,41 @@ static void chat_onTypingChange(ToxWindow *self, Tox *m, int num, int is_typing) friends[num].is_typing = is_typing; } -static void chat_onAction(ToxWindow *self, Tox *m, int num, uint8_t *action, uint16_t len) +static void chat_onAction(ToxWindow *self, Tox *m, int32_t num, uint8_t *action, uint16_t len) { if (self->num != num) return; + + action[len] = '\0'; + ChatContext *ctx = self->chatwin; - uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'}; - tox_get_name(m, num, nick); - nick[TOXIC_MAX_NAME_LENGTH] = '\0'; + uint8_t nick[TOX_MAX_NAME_LENGTH]; + int n_len = tox_get_name(m, num, nick); - print_time(ctx->history); - wattron(ctx->history, COLOR_PAIR(YELLOW)); - wprintw(ctx->history, "* %s %s\n", nick, action); - wattroff(ctx->history, COLOR_PAIR(YELLOW)); + n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH-1);; + nick[n_len] = '\0'; + uint8_t timefrmt[TIME_STR_SIZE]; + get_time_str(timefrmt); + + line_info_add(self, timefrmt, nick, NULL, action, ACTION, 0, 0); write_to_log(action, nick, ctx->log, true); alert_window(self, WINDOW_ALERT_1, true); } -static void chat_onNickChange(ToxWindow *self, Tox *m, int num, uint8_t *nick, uint16_t len) +static void chat_onNickChange(ToxWindow *self, Tox *m, int32_t num, uint8_t *nick, uint16_t len) { if (self->num != num) return; - nick[TOXIC_MAX_NAME_LENGTH] = '\0'; - len = strlen(nick) + 1; - memcpy(self->name, nick, len); + len = MIN(len, TOXIC_MAX_NAME_LENGTH-1); + nick[len] = '\0'; + strcpy(self->name, nick); } -static void chat_onStatusChange(ToxWindow *self, Tox *m, int num, TOX_USERSTATUS status) +static void chat_onStatusChange(ToxWindow *self, Tox *m, int32_t num, uint8_t status) { if (self->num != num) return; @@ -201,32 +205,36 @@ static void chat_onStatusChange(ToxWindow *self, Tox *m, int num, TOX_USERSTATUS statusbar->status = status; } -static void chat_onStatusMessageChange(ToxWindow *self, int num, uint8_t *status, uint16_t len) +static void chat_onStatusMessageChange(ToxWindow *self, int32_t num, uint8_t *status, uint16_t len) { if (self->num != num) return; StatusBar *statusbar = self->stb; statusbar->statusmsg_len = len; - memcpy(statusbar->statusmsg, status, len); + strcpy(statusbar->statusmsg, status); + statusbar->statusmsg[len] = '\0'; } -static void chat_onFileSendRequest(ToxWindow *self, Tox *m, int num, uint8_t filenum, +static void chat_onFileSendRequest(ToxWindow *self, Tox *m, int32_t num, uint8_t filenum, uint64_t filesize, uint8_t *pathname, uint16_t path_len) { if (self->num != num) return; - ChatContext *ctx = self->chatwin; + uint8_t msg[MAX_STR_SIZE]; + uint8_t *errmsg; uint8_t filename[MAX_STR_SIZE]; get_file_name(pathname, filename); - wprintw(ctx->history, "File transfer request for '%s' (%llu bytes).\n", filename, - (long long unsigned int)filesize); + snprintf(msg, sizeof(msg), "File transfer request for '%s' (%llu bytes).", filename, + (long long unsigned int)filesize); + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); if (filenum >= MAX_FILES) { - wprintw(ctx->history, "Too many pending file requests; discarding.\n"); + errmsg = "Too many pending file requests; discarding."; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); return; } @@ -243,14 +251,17 @@ static void chat_onFileSendRequest(ToxWindow *self, Tox *m, int num, uint8_t fil filename[len + strlen(d)] = '\0'; if (count > 999) { - wprintw(ctx->history, "Error saving file to disk.\n"); + errmsg = "Error saving file to disk."; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); return; } } - wprintw(ctx->history, "Type '/savefile %d' to accept the file transfer.\n", filenum); + snprintf(msg, sizeof(msg), "Type '/savefile %d' to accept the file transfer.", filenum); + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); friends[num].file_receiver.pending[filenum] = true; + friends[num].file_receiver.size[filenum] = filesize; strcpy(friends[num].file_receiver.filenames[filenum], filename); alert_window(self, WINDOW_ALERT_2, true); @@ -259,20 +270,21 @@ static void chat_onFileSendRequest(ToxWindow *self, Tox *m, int num, uint8_t fil static void chat_close_file_receiver(int num, uint8_t filenum) { friends[num].file_receiver.pending[filenum] = false; + friends[num].file_receiver.size[filenum] = 0; FILE *file = friends[num].file_receiver.files[filenum]; if (file != NULL) fclose(file); } -static void chat_onFileControl(ToxWindow *self, Tox *m, int num, uint8_t receive_send, +static void chat_onFileControl(ToxWindow *self, Tox *m, int32_t num, uint8_t receive_send, uint8_t filenum, uint8_t control_type, uint8_t *data, uint16_t length) { if (self->num != num) return; - ChatContext *ctx = self->chatwin; - uint8_t *filename; + const uint8_t *filename; + uint8_t msg[MAX_STR_SIZE] = {0}; if (receive_send == 0) filename = friends[num].file_receiver.filenames[filenum]; @@ -281,61 +293,73 @@ static void chat_onFileControl(ToxWindow *self, Tox *m, int num, uint8_t receive switch (control_type) { case TOX_FILECONTROL_ACCEPT: - wprintw(ctx->history, "File transfer for '%s' accepted.\n", filename); + snprintf(msg, sizeof(msg), "File transfer for '%s' accepted (%.1f%%)", filename, 0.0); + file_senders[filenum].line_id = self->chatwin->hst->line_end->id + 1; break; /*case TOX_FILECONTROL_PAUSE: wprintw(ctx->history, "File transfer for '%s' paused.\n", filename); break; */ case TOX_FILECONTROL_KILL: - wprintw(ctx->history, "File transfer for '%s' failed.\n", filename); - + snprintf(msg, sizeof(msg), "File transfer for '%s' failed.", filename); if (receive_send == 0) chat_close_file_receiver(num, filenum); - else - chat_close_file_receiver(num, filenum); break; case TOX_FILECONTROL_FINISHED: - wprintw(ctx->history, "File transfer for '%s' complete.\n", filename); + snprintf(msg, sizeof(msg), "File transfer for '%s' complete.", filename); chat_close_file_receiver(num, filenum); break; } + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); alert_window(self, WINDOW_ALERT_2, true); } -static void chat_onFileData(ToxWindow *self, Tox *m, int num, uint8_t filenum, uint8_t *data, +static void chat_onFileData(ToxWindow *self, Tox *m, int32_t num, uint8_t filenum, uint8_t *data, uint16_t length) { if (self->num != num) return; - ChatContext *ctx = self->chatwin; - if (fwrite(data, length, 1, friends[num].file_receiver.files[filenum]) != 1) { - wattron(ctx->history, COLOR_PAIR(RED)); - wprintw(ctx->history, "* Error writing to file.\n"); - wattroff(ctx->history, COLOR_PAIR(RED)); + uint8_t *msg = " * Error writing to file."; + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, RED); tox_file_send_control(m, num, 1, filenum, TOX_FILECONTROL_KILL, 0, 0); chat_close_file_receiver(num, filenum); } + + /* refresh line with percentage complete */ + uint8_t msg[MAX_STR_SIZE]; + uint64_t size = friends[num].file_receiver.size[filenum]; + long double remain = (long double) tox_file_data_remaining(m, num, filenum, 1); + long double pct_remain = 100; + + if (remain) + pct_remain = (1 - (remain / size)) * 100; + + const uint8_t *name = friends[num].file_receiver.filenames[filenum]; + snprintf(msg, sizeof(msg), "Saving file as: '%s' (%.1Lf%%)", name, pct_remain); + line_info_set(self, friends[num].file_receiver.line_id, msg); + } -static void chat_onGroupInvite(ToxWindow *self, Tox *m, int friendnumber, uint8_t *group_pub_key) +static void chat_onGroupInvite(ToxWindow *self, Tox *m, int32_t friendnumber, uint8_t *group_pub_key) { if (self->num != friendnumber) return; - ChatContext *ctx = self->chatwin; - uint8_t name[TOX_MAX_NAME_LENGTH] = {'\0'}; + uint8_t name[TOX_MAX_NAME_LENGTH]; + uint8_t msg[MAX_STR_SIZE + TOX_MAX_NAME_LENGTH]; + int n_len = tox_get_name(m, friendnumber, name); - if (tox_get_name(m, friendnumber, name) == -1) - return; + n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH-1); + name[n_len] = '\0'; - wprintw(ctx->history, "%s has invited you to a group chat.\n", name); + snprintf(msg, sizeof(msg), "%s has invited you to a group chat.\n" + "Type \"/join\" to join the chat.", name); + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); memcpy(friends[friendnumber].pending_groupchat, group_pub_key, TOX_CLIENT_ID_SIZE); - wprintw(ctx->history, "Type \"/join\" to join the chat.\n"); alert_window(self, WINDOW_ALERT_2, true); } @@ -346,150 +370,147 @@ void chat_onInvite (ToxWindow *self, ToxAv *av) { if (self->num != toxav_get_peer_id(av, 0)) return; - - ChatContext *ctx = self->chatwin; - - wprintw(ctx->history, "Incoming audio call!\n" - "Answer: \"/answer\" \"/reject\"\n"); + + uint8_t *msg = "Incoming audio call!\nType: \"/answer\" or \"/reject\""; + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); alert_window(self, WINDOW_ALERT_0, true); } + void chat_onRinging (ToxWindow *self, ToxAv *av) { if (self->num != toxav_get_peer_id(av, 0)) return; - - ChatContext *ctx = self->chatwin; - - wprintw(ctx->history, "Ringing...\n" - "\"/cancel\" ?\n"); + + uint8_t *msg = "Ringing...\n\"cancel\" ?"; + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); } + void chat_onStarting (ToxWindow *self, ToxAv *av) { if (self->num != toxav_get_peer_id(av, 0)) return; - - ChatContext *ctx = self->chatwin; - - if ( 0 != start_transmission() ) {/* YEAH! */ - wprintw(ctx->history, "Error starting transmission!\n"); + + uint8_t *msg; + + if ( 0 != start_transmission(self) ) {/* YEAH! */ + msg = "Error starting transmission!"; + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); return; } - - wprintw(ctx->history, "Call started! \n" - "Type: \"/hangup\" to end it.\n"); - + + msg = "Call started!\nType: \"/hangup\" to end it."; + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); } + void chat_onEnding (ToxWindow *self, ToxAv *av) { if (self->num != toxav_get_peer_id(av, 0)) return; - - ChatContext *ctx = self->chatwin; - + + uint8_t *msg = "Call ended!"; + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); + toxav_kill_transmission(av); - - wprintw(ctx->history, "Call ended! \n"); } + void chat_onError (ToxWindow *self, ToxAv *av) { if (self->num != toxav_get_peer_id(av, 0)) return; - - ChatContext *ctx = self->chatwin; - - wprintw(ctx->history, "Error! \n"); + + uint8_t *msg = "Error!"; + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); } + void chat_onStart (ToxWindow *self, ToxAv *av) { if (self->num != toxav_get_peer_id(av, 0)) return; - - ChatContext *ctx = self->chatwin; - - if ( 0 != start_transmission() ) {/* YEAH! */ - wprintw(ctx->history, "Error starting transmission!\n"); + + uint8_t *msg; + + if ( 0 != start_transmission(self) ) {/* YEAH! */ + msg = "Error starting transmission!"; + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); return; } - - wprintw(ctx->history, "Call started! \n" - "Type: \"/hangup\" to end it.\n"); + + msg = "Call started!\nType: \"/hangup\" to end it."; + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); } + void chat_onCancel (ToxWindow *self, ToxAv *av) { if (self->num != toxav_get_peer_id(av, 0)) return; - - ChatContext *ctx = self->chatwin; - - wprintw(ctx->history, "Call canceled! \n"); + + uint8_t *msg = "Call canceled!"; + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); } + void chat_onReject (ToxWindow *self, ToxAv *av) { if (self->num != toxav_get_peer_id(av, 0)) return; - - ChatContext *ctx = self->chatwin; - - wprintw(ctx->history, "Rejected! \n"); + + uint8_t *msg = "Rejected!"; + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); } + void chat_onEnd (ToxWindow *self, ToxAv *av) { if (self->num != toxav_get_peer_id(av, 0)) return; - - ChatContext *ctx = self->chatwin; - + toxav_kill_transmission(av); - - wprintw(ctx->history, "Call ended! \n"); + + uint8_t *msg = "Call ended!"; + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); } + void chat_onRequestTimeout (ToxWindow *self, ToxAv *av) { if (self->num != toxav_get_peer_id(av, 0)) return; - - ChatContext *ctx = self->chatwin; - - wprintw(ctx->history, "No answer! \n"); + + uint8_t *msg = "No answer!"; + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); } void chat_onPeerTimeout (ToxWindow *self, ToxAv *av) { if (self->num != toxav_get_peer_id(av, 0)) return; - - ChatContext *ctx = self->chatwin; - - wprintw(ctx->history, "Peer disconnected; call ended! \n"); + + uint8_t *msg = "Peer disconnected; call ended!"; + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); } #endif /* _SUPPORT_AUDIO */ static void send_action(ToxWindow *self, ChatContext *ctx, Tox *m, uint8_t *action) { - if (action == NULL) { - wprintw(ctx->history, "Invalid syntax.\n"); + if (action == NULL) return; - } uint8_t selfname[TOX_MAX_NAME_LENGTH]; - tox_get_self_name(m, selfname, TOX_MAX_NAME_LENGTH); + uint16_t len = tox_get_self_name(m, selfname); + selfname[len] = '\0'; - print_time(ctx->history); - wattron(ctx->history, COLOR_PAIR(YELLOW)); - wprintw(ctx->history, "* %s %s\n", selfname, action); - wattroff(ctx->history, COLOR_PAIR(YELLOW)); + uint8_t timefrmt[TIME_STR_SIZE]; + get_time_str(timefrmt); - if (tox_send_action(m, self->num, action, strlen(action) + 1) == 0) { - wattron(ctx->history, COLOR_PAIR(RED)); - wprintw(ctx->history, " * Failed to send action\n"); - wattroff(ctx->history, COLOR_PAIR(RED)); + line_info_add(self, timefrmt, selfname, NULL, action, ACTION, 0, 0); + + if (tox_send_action(m, self->num, action, strlen(action)) == 0) { + uint8_t *errmsg = " * Failed to send action."; + line_info_add(self, NULL, selfname, NULL, errmsg, SYS_MSG, 0, RED); } else { write_to_log(action, selfname, ctx->log, true); } } -static void chat_onKey(ToxWindow *self, Tox *m, wint_t key) +static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) { ChatContext *ctx = self->chatwin; StatusBar *statusbar = self->stb; @@ -499,124 +520,19 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key) getmaxyx(self->window, y2, x2); int cur_len = 0; - if (key == 0x107 || key == 0x8 || key == 0x7f) { /* BACKSPACE key: Remove character behind pos */ - if (ctx->pos > 0) { - cur_len = MAX(1, wcwidth(ctx->line[ctx->pos - 1])); - del_char_buf_bck(ctx->line, &ctx->pos, &ctx->len); - - if (x == 0) - wmove(self->window, y-1, x2 - cur_len); - else - wmove(self->window, y, x - cur_len); - } else { - beep(); - } + if (!ltr && (key == T_KEY_ESC)) { /* ESC key: Toggle history scroll mode */ + bool scroll = ctx->hst->scroll_mode ? false : true; + line_info_toggle_scroll(self, scroll); } - else if (key == KEY_DC) { /* DEL key: Remove character at pos */ - if (ctx->pos != ctx->len) - del_char_buf_frnt(ctx->line, &ctx->pos, &ctx->len); - else - beep(); + /* If we're in scroll mode ignore rest of function */ + if (ctx->hst->scroll_mode) { + line_info_onKey(self, key); + return; } - else if (key == T_KEY_DISCARD) { /* CTRL-U: Delete entire line behind pos */ - if (ctx->pos > 0) { - discard_buf(ctx->line, &ctx->pos, &ctx->len); - wmove(self->window, y2 - CURS_Y_OFFSET, 0); - } else { - beep(); - } - } - - else if (key == T_KEY_KILL) { /* CTRL-K: Delete entire line in front of pos */ - if (ctx->pos != ctx->len) - kill_buf(ctx->line, &ctx->pos, &ctx->len); - else - beep(); - } - - else if (key == KEY_HOME || key == T_KEY_C_A) { /* HOME/C-a key: Move cursor to start of line */ - if (ctx->pos > 0) { - ctx->pos = 0; - wmove(self->window, y2 - CURS_Y_OFFSET, 0); - } - } - - else if (key == KEY_END || key == T_KEY_C_E) { /* END/C-e key: move cursor to end of line */ - if (ctx->pos != ctx->len) { - ctx->pos = ctx->len; - mv_curs_end(self->window, MAX(0, wcswidth(ctx->line, (CHATBOX_HEIGHT-1)*x2)), y2, x2); - } - } - - else if (key == KEY_LEFT) { - if (ctx->pos > 0) { - --ctx->pos; - cur_len = MAX(1, wcwidth(ctx->line[ctx->pos])); - - if (x == 0) - wmove(self->window, y-1, x2 - cur_len); - else - wmove(self->window, y, x - cur_len); - } else { - beep(); - } - } - - else if (key == KEY_RIGHT) { - if (ctx->pos < ctx->len) { - cur_len = MAX(1, wcwidth(ctx->line[ctx->pos])); - ++ctx->pos; - - if (x == x2-1) - wmove(self->window, y+1, 0); - else - wmove(self->window, y, x + cur_len); - } else { - beep(); - } - } - - else if (key == KEY_UP) { /* fetches previous item in history */ - fetch_hist_item(ctx->line, &ctx->pos, &ctx->len, ctx->ln_history, ctx->hst_tot, - &ctx->hst_pos, LN_HIST_MV_UP); - mv_curs_end(self->window, ctx->len, y2, x2); - } - - else if (key == KEY_DOWN) { /* fetches next item in history */ - fetch_hist_item(ctx->line, &ctx->pos, &ctx->len, ctx->ln_history, ctx->hst_tot, - &ctx->hst_pos, LN_HIST_MV_DWN); - mv_curs_end(self->window, ctx->len, y2, x2); - } - - else if (key == '\t') { /* TAB key: completes command */ - if (ctx->len > 1 && ctx->line[0] == '/') { - int diff = complete_line(ctx->line, &ctx->pos, &ctx->len, chat_cmd_list, AC_NUM_CHAT_COMMANDS, - MAX_CMDNAME_SIZE); - - if (diff != -1) { - if (x + diff > x2 - 1) { - int ofst = (x + diff - 1) - (x2 - 1); - wmove(self->window, y+1, ofst); - } else { - wmove(self->window, y, x+diff); - } - } else { - beep(); - } - } else { - beep(); - } - } - - else -#if HAVE_WIDECHAR - if (iswprint(key)) -#else - if (isprint(key)) -#endif - { /* prevents buffer overflows and strange behaviour when cursor goes past the window */ + if (ltr) { + /* prevents buffer overflows and strange behaviour when cursor goes past the window */ if ( (ctx->len < MAX_STR_SIZE-1) && (ctx->len < (x2 * (CHATBOX_HEIGHT - 1)-1)) ) { add_char_to_buf(ctx->line, &ctx->pos, &ctx->len, key); @@ -627,84 +543,197 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key) } if (!ctx->self_is_typing && ctx->line[0] != '/') - set_typingstatus(self, m, true); - } - /* RETURN key: Execute command or print line */ - else if (key == '\n') { - uint8_t line[MAX_STR_SIZE]; + set_typingstatus(self, m, 1); - if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) - memset(&line, 0, sizeof(line)); + } else { /* if (!ltr) */ - wclear(ctx->linewin); - wmove(self->window, y2 - CURS_Y_OFFSET, 0); - wclrtobot(self->window); + if (key == 0x107 || key == 0x8 || key == 0x7f) { /* BACKSPACE key */ + if (ctx->pos > 0) { + cur_len = MAX(1, wcwidth(ctx->line[ctx->pos - 1])); + del_char_buf_bck(ctx->line, &ctx->pos, &ctx->len); - if (!string_is_empty(line)) - add_line_to_hist(ctx->line, ctx->len, ctx->ln_history, &ctx->hst_tot, &ctx->hst_pos); - - if (line[0] == '/') { - if (strcmp(line, "/close") == 0) { - if (ctx->self_is_typing) - set_typingstatus(self, m, false); - - kill_chat_window(self); - return; - } else if (strncmp(line, "/me ", strlen("/me ")) == 0) { - send_action(self, ctx, m, line + strlen("/me ")); + if (x == 0) + wmove(self->window, y-1, x2 - cur_len); + else + wmove(self->window, y, x - cur_len); } else { - execute(ctx->history, self, m, line, CHAT_COMMAND_MODE); - } - } else if (!string_is_empty(line)) { - uint8_t selfname[TOX_MAX_NAME_LENGTH]; - tox_get_self_name(m, selfname, TOX_MAX_NAME_LENGTH); - - print_time(ctx->history); - wattron(ctx->history, COLOR_PAIR(GREEN)); - wprintw(ctx->history, "%s: ", selfname); - wattroff(ctx->history, COLOR_PAIR(GREEN)); - - if (line[0] == '>') { - wattron(ctx->history, COLOR_PAIR(GREEN)); - wprintw(ctx->history, "%s\n", line); - wattroff(ctx->history, COLOR_PAIR(GREEN)); - } else - wprintw(ctx->history, "%s\n", line); - - if (!statusbar->is_online || tox_send_message(m, self->num, line, strlen(line) + 1) == 0) { - wattron(ctx->history, COLOR_PAIR(RED)); - wprintw(ctx->history, " * Failed to send message.\n"); - wattroff(ctx->history, COLOR_PAIR(RED)); - } else { - write_to_log(line, selfname, ctx->log, false); + beep(); } } - reset_buf(ctx->line, &ctx->pos, &ctx->len); + else if (key == KEY_DC) { /* DEL key: Remove character at pos */ + if (ctx->pos != ctx->len) + del_char_buf_frnt(ctx->line, &ctx->pos, &ctx->len); + else + beep(); + } + + else if (key == T_KEY_DISCARD) { /* CTRL-U: Delete entire line behind pos */ + if (ctx->pos > 0) { + discard_buf(ctx->line, &ctx->pos, &ctx->len); + wmove(self->window, y2 - CURS_Y_OFFSET, 0); + } else { + beep(); + } + } + + else if (key == T_KEY_KILL) { /* CTRL-K: Delete entire line in front of pos */ + if (ctx->pos != ctx->len) + kill_buf(ctx->line, &ctx->pos, &ctx->len); + else + beep(); + } + + else if (key == KEY_HOME || key == T_KEY_C_A) { /* HOME/C-a key: Move cursor to start of line */ + if (ctx->pos > 0) { + ctx->pos = 0; + wmove(self->window, y2 - CURS_Y_OFFSET, 0); + } + } + + else if (key == KEY_END || key == T_KEY_C_E) { /* END/C-e key: move cursor to end of line */ + if (ctx->pos != ctx->len) { + ctx->pos = ctx->len; + mv_curs_end(self->window, MAX(0, wcswidth(ctx->line, (CHATBOX_HEIGHT-1)*x2)), y2, x2); + } + } + + else if (key == KEY_LEFT) { + if (ctx->pos > 0) { + --ctx->pos; + cur_len = MAX(1, wcwidth(ctx->line[ctx->pos])); + + if (x == 0) + wmove(self->window, y-1, x2 - cur_len); + else + wmove(self->window, y, x - cur_len); + } else { + beep(); + } + } + + else if (key == KEY_RIGHT) { + if (ctx->pos < ctx->len) { + cur_len = MAX(1, wcwidth(ctx->line[ctx->pos])); + ++ctx->pos; + + if (x == x2-1) + wmove(self->window, y+1, 0); + else + wmove(self->window, y, x + cur_len); + } else { + beep(); + } + } + + else if (key == KEY_UP) { /* fetches previous item in history */ + fetch_hist_item(ctx->line, &ctx->pos, &ctx->len, ctx->ln_history, ctx->hst_tot, + &ctx->hst_pos, MOVE_UP); + mv_curs_end(self->window, ctx->len, y2, x2); + } + + else if (key == KEY_DOWN) { /* fetches next item in history */ + fetch_hist_item(ctx->line, &ctx->pos, &ctx->len, ctx->ln_history, ctx->hst_tot, + &ctx->hst_pos, MOVE_DOWN); + mv_curs_end(self->window, ctx->len, y2, x2); + } + + else if (key == '\t') { /* TAB key: completes command */ + if (ctx->len > 1 && ctx->line[0] == '/') { + int diff = complete_line(ctx->line, &ctx->pos, &ctx->len, chat_cmd_list, AC_NUM_CHAT_COMMANDS, + MAX_CMDNAME_SIZE); + + if (diff != -1) { + if (x + diff > x2 - 1) { + int ofst = (x + diff - 1) - (x2 - 1); + wmove(self->window, y+1, ofst); + } else { + wmove(self->window, y, x+diff); + } + } else { + beep(); + } + } else { + beep(); + } + } + + /* RETURN key: Execute command or print line */ + else if (key == '\n') { + uint8_t line[MAX_STR_SIZE]; + + if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) + memset(&line, 0, sizeof(line)); + + wclear(ctx->linewin); + wmove(self->window, y2 - CURS_Y_OFFSET, 0); + + if (!string_is_empty(line)) + add_line_to_hist(ctx->line, ctx->len, ctx->ln_history, &ctx->hst_tot, &ctx->hst_pos); + + if (line[0] == '/') { + if (strcmp(line, "/close") == 0) { + if (ctx->self_is_typing) + set_typingstatus(self, m, 0); + + kill_chat_window(self); + return; + } else if (strncmp(line, "/me ", strlen("/me ")) == 0) { + send_action(self, ctx, m, line + strlen("/me ")); + } else { + execute(ctx->history, self, m, line, CHAT_COMMAND_MODE); + } + } else if (!string_is_empty(line)) { + uint8_t selfname[TOX_MAX_NAME_LENGTH]; + uint16_t len = tox_get_self_name(m, selfname); + selfname[len] = '\0'; + + uint8_t timefrmt[TIME_STR_SIZE]; + get_time_str(timefrmt); + + line_info_add(self, timefrmt, selfname, NULL, line, OUT_MSG, 0, 0); + + if (!statusbar->is_online || tox_send_message(m, self->num, line, strlen(line)) == 0) { + uint8_t *errmsg = " * Failed to send message."; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, RED); + } else { + write_to_log(line, selfname, ctx->log, false); + } + } + + reset_buf(ctx->line, &ctx->pos, &ctx->len); + } } if (ctx->len <= 0 && ctx->self_is_typing) - set_typingstatus(self, m, false); + set_typingstatus(self, m, 0); } static void chat_onDraw(ToxWindow *self, Tox *m) { - curs_set(1); int x2, y2; getmaxyx(self->window, y2, x2); ChatContext *ctx = self->chatwin; + line_info_print(self); wclear(ctx->linewin); - if (ctx->len > 0) { - uint8_t line[MAX_STR_SIZE]; - - if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) { - reset_buf(ctx->line, &ctx->pos, &ctx->len); - wmove(self->window, y2 - CURS_Y_OFFSET, 0); - } else { - mvwprintw(ctx->linewin, 1, 0, "%s", line); + if (ctx->hst->scroll_mode) { + line_info_onDraw(self); + } else { + curs_set(1); + scrollok(ctx->history, 1); + + if (ctx->len > 0 && !ctx->hst->scroll_mode) { + uint8_t line[MAX_STR_SIZE]; + + if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) { + reset_buf(ctx->line, &ctx->pos, &ctx->len); + wmove(self->window, y2 - CURS_Y_OFFSET, 0); + } else { + mvwprintw(ctx->linewin, 1, 0, "%s", line); + } } } @@ -715,10 +744,10 @@ static void chat_onDraw(ToxWindow *self, Tox *m) /* Draw name, status and note in statusbar */ if (statusbar->is_online) { - char *status_text = "Unknown"; + const uint8_t *status_text = "Unknown"; int colour = WHITE; - TOX_USERSTATUS status = statusbar->status; + uint8_t status = statusbar->status; switch (status) { case TOX_USERSTATUS_NONE: @@ -733,8 +762,16 @@ static void chat_onDraw(ToxWindow *self, Tox *m) status_text = "Busy"; colour = RED; break; + case TOX_USERSTATUS_INVALID: + status_text = "ERROR"; + colour = MAGENTA; + break; } + wattron(statusbar->topline, COLOR_PAIR(colour) | A_BOLD); + wprintw(statusbar->topline, " [%s]", status_text); + wattroff(statusbar->topline, COLOR_PAIR(colour) | A_BOLD); + if (friends[self->num].is_typing) wattron(statusbar->topline, COLOR_PAIR(YELLOW)); @@ -744,15 +781,11 @@ static void chat_onDraw(ToxWindow *self, Tox *m) if (friends[self->num].is_typing) wattroff(statusbar->topline, COLOR_PAIR(YELLOW)); - - wattron(statusbar->topline, COLOR_PAIR(colour) | A_BOLD); - wprintw(statusbar->topline, "[%s]", status_text); - wattroff(statusbar->topline, COLOR_PAIR(colour) | A_BOLD); } else { + wprintw(statusbar->topline, " [Offline]"); wattron(statusbar->topline, A_BOLD); wprintw(statusbar->topline, " %s ", self->name); wattroff(statusbar->topline, A_BOLD); - wprintw(statusbar->topline, "[Offline]"); } /* Reset statusbar->statusmsg on window resize */ @@ -760,10 +793,12 @@ static void chat_onDraw(ToxWindow *self, Tox *m) uint8_t statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH] = {'\0'}; pthread_mutex_lock(&Winthread.lock); - tox_get_status_message(m, self->num, statusmsg, TOX_MAX_STATUSMESSAGE_LENGTH); + uint16_t s_len = tox_get_status_message(m, self->num, statusmsg, TOX_MAX_STATUSMESSAGE_LENGTH); pthread_mutex_unlock(&Winthread.lock); + statusmsg[s_len] = '\0'; + snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg); - statusbar->statusmsg_len = tox_get_status_message_size(m, self->num); + statusbar->statusmsg_len = s_len; } self->x = x2; @@ -775,11 +810,8 @@ static void chat_onDraw(ToxWindow *self, Tox *m) statusbar->statusmsg_len = maxlen; } - if (statusbar->statusmsg[0]) { - wattron(statusbar->topline, A_BOLD); - wprintw(statusbar->topline, " - %s ", statusbar->statusmsg); - wattroff(statusbar->topline, A_BOLD); - } + if (statusbar->statusmsg[0]) + wprintw(statusbar->topline, "- %s ", statusbar->statusmsg); wclrtoeol(statusbar->topline); wmove(statusbar->topline, 0, x2 - (KEY_IDENT_DIGITS * 2) - 3); @@ -796,48 +828,55 @@ static void chat_onDraw(ToxWindow *self, Tox *m) static void chat_onInit(ToxWindow *self, Tox *m) { + curs_set(1); int x2, y2; getmaxyx(self->window, y2, x2); self->x = x2; /* Init statusbar info */ StatusBar *statusbar = self->stb; + statusbar->status = tox_get_user_status(m, self->num); statusbar->is_online = tox_get_friend_connection_status(m, self->num) == 1; uint8_t statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH] = {'\0'}; - tox_get_status_message(m, self->num, statusmsg, TOX_MAX_STATUSMESSAGE_LENGTH); + uint16_t s_len = tox_get_status_message(m, self->num, statusmsg, TOX_MAX_STATUSMESSAGE_LENGTH); + statusmsg[s_len] = '\0'; snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg); - statusbar->statusmsg_len = tox_get_status_message_size(m, self->num); + statusbar->statusmsg_len = s_len; /* Init subwindows */ ChatContext *ctx = self->chatwin; + statusbar->topline = subwin(self->window, 2, x2, 0, 0); ctx->history = subwin(self->window, y2-CHATBOX_HEIGHT+1, x2, 0, 0); scrollok(ctx->history, 1); ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2-CHATBOX_HEIGHT, 0); + ctx->hst = malloc(sizeof(struct history)); ctx->log = malloc(sizeof(struct chatlog)); - if (ctx->log == NULL) { + if (ctx->log == NULL || ctx->hst == NULL) { endwin(); fprintf(stderr, "malloc() failed. Aborting...\n"); exit(EXIT_FAILURE); } + memset(ctx->hst, 0, sizeof(struct history)); memset(ctx->log, 0, sizeof(struct chatlog)); + line_info_init(ctx->hst); + if (friends[self->num].logging_on) log_enable(self->name, friends[self->num].pub_key, ctx->log); - wprintw(ctx->history, "\n\n"); execute(ctx->history, self, m, "/help", CHAT_COMMAND_MODE); execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE); wmove(self->window, y2 - CURS_Y_OFFSET, 0); } -ToxWindow new_chat(Tox *m, int friendnum) +ToxWindow new_chat(Tox *m, int32_t friendnum) { ToxWindow ret; memset(&ret, 0, sizeof(ret)); @@ -875,12 +914,18 @@ ToxWindow new_chat(Tox *m, int friendnum) #endif /* _SUPPORT_AUDIO */ uint8_t name[TOX_MAX_NAME_LENGTH] = {'\0'}; - uint16_t len = tox_get_name(m, friendnum, name); - memcpy(ret.name, name, len); - ret.name[TOXIC_MAX_NAME_LENGTH] = '\0'; + int len = tox_get_name(m, friendnum, name); + + len = MIN(len, TOXIC_MAX_NAME_LENGTH-1); + + name[len] = '\0'; + strcpy(ret.name, name); ChatContext *chatwin = calloc(1, sizeof(ChatContext)); + memset(chatwin, 0, sizeof(ChatContext)); + StatusBar *stb = calloc(1, sizeof(StatusBar)); + memset(stb, 0, sizeof(StatusBar)); if (stb != NULL && chatwin != NULL) { ret.chatwin = chatwin; diff --git a/src/chat.h b/src/chat.h index fac132a..d3aeb3e 100644 --- a/src/chat.h +++ b/src/chat.h @@ -26,6 +26,6 @@ #include "toxic_windows.h" void kill_chat_window(ToxWindow *self); -ToxWindow new_chat(Tox *m, int friendnum); +ToxWindow new_chat(Tox *m, int32_t friendnum); #endif /* end of include guard: CHAT_H_6489PZ13 */ diff --git a/src/chat_commands.c b/src/chat_commands.c index fc13c4f..cd8f106 100644 --- a/src/chat_commands.c +++ b/src/chat_commands.c @@ -31,6 +31,7 @@ #include "misc_tools.h" #include "friendlist.h" #include "execute.h" +#include "line_info.h" extern ToxWindow *prompt; @@ -41,6 +42,10 @@ extern uint8_t max_file_senders_index; void cmd_chat_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) { + struct history *hst = self->chatwin->hst; + line_info_clear(hst); + struct line_info *start = hst->line_start; + if (argc == 1) { if (!strcmp(argv[1], "global")) { execute(window, self, m, "/help", GLOBAL_COMMAND_MODE); @@ -48,81 +53,103 @@ void cmd_chat_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*arg } } - wattron(window, COLOR_PAIR(CYAN) | A_BOLD); - wprintw(window, "Chat commands:\n"); - wattroff(window, COLOR_PAIR(CYAN) | A_BOLD); + uint8_t *msg = "Chat commands:"; + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 1, CYAN); #ifdef _SUPPORT_AUDIO + #define NUMLINES 13 +#else + #define NUMLINES 9 +#endif - wprintw(window, " /call : Audio call\n"); - wprintw(window, " /cancel : Cancel call\n"); - wprintw(window, " /answer : Answer incomming call\n"); - wprintw(window, " /reject : Reject incomming call\n"); - wprintw(window, " /hangup : Hangup active call\n"); + uint8_t lines[NUMLINES][MAX_STR_SIZE] = { +#ifdef _SUPPORT_AUDIO + { " /call : Audio call" }, + { " /cancel : Cancel call" }, + { " /answer : Answer incomming call" }, + { " /reject : Reject incoming call" }, + { " /hangup : Hangup active call" }, #endif /* _SUPPORT_AUDIO */ - - wprintw(window, " /invite : Invite friend to a group chat\n"); - wprintw(window, " /join : Join a pending group chat\n"); - wprintw(window, " /log or : Enable/disable logging\n"); - wprintw(window, " /sendfile : Send a file\n"); - wprintw(window, " /savefile : Receive a file\n"); - wprintw(window, " /close : Close the current chat window\n"); - wprintw(window, " /help : Print this message again\n"); - wprintw(window, " /help global : Show a list of global commands\n"); - - wattron(window, COLOR_PAIR(CYAN) | A_BOLD); - wprintw(window, " * Argument messages must be enclosed in quotation marks.\n\n"); - wattroff(window, COLOR_PAIR(CYAN) | A_BOLD); + { " /invite : Invite friend to a group chat" }, + { " /join : Join a pending group chat" }, + { " /log or : Enable/disable logging" }, + { " /sendfile : Send a file" }, + { " /savefile : Receive a file" }, + { " /close : Close the current chat window" }, + { " /help : Print this message again" }, + { " /help global : Show a list of global commands" }, +}; + + int i; + + for (i = 0; i < NUMLINES; ++i) + line_info_add(self, NULL, NULL, NULL, lines[i], SYS_MSG, 0, 0); + + msg = " * Use ESC key to toggle history scroll mode\n"; + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 1, CYAN); + + hst->line_start = start; } void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) { + uint8_t *errmsg; + if (argc < 1) { - wprintw(window, "Invalid syntax.\n"); - return; + errmsg = "Invalid syntax"; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); + return; } int groupnum = atoi(argv[1]); if (groupnum == 0 && strcmp(argv[1], "0")) { /* atoi returns 0 value on invalid input */ - wprintw(window, "Invalid syntax.\n"); + errmsg = "Invalid syntax."; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); return; } if (tox_invite_friend(m, self->num, groupnum) == -1) { - wprintw(window, "Failed to invite friend.\n"); + errmsg = "Failed to invite friend."; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); return; } - wprintw(window, "Invited friend to group chat %d.\n", groupnum); + uint8_t msg[MAX_STR_SIZE]; + snprintf(msg, sizeof(msg), "Invited friend to Room #%d.", groupnum); + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); } void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) { + uint8_t *errmsg; + if (get_num_active_windows() >= MAX_WINDOWS_NUM) { - wattron(window, COLOR_PAIR(RED)); - wprintw(window, " * Warning: Too many windows are open.\n"); - wattron(window, COLOR_PAIR(RED)); + errmsg = " * Warning: Too many windows are open."; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, RED); return; } uint8_t *groupkey = friends[self->num].pending_groupchat; if (groupkey[0] == '\0') { - wprintw(window, "No pending group chat invite.\n"); + errmsg = "No pending group chat invite."; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); return; } int groupnum = tox_join_groupchat(m, self->num, groupkey); if (groupnum == -1) { - wprintw(window, "Group chat instance failed to initialize.\n"); + errmsg = "Group chat instance failed to initialize."; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); return; } if (init_groupchat_win(prompt, m, groupnum) == -1) { - wprintw(window, "Group chat window failed to initialize.\n"); + errmsg = "Group chat window failed to initialize."; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); tox_del_groupchat(m, groupnum); return; } @@ -130,36 +157,44 @@ void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) { + uint8_t *errmsg; + if (argc != 1) { - wprintw(window, "Invalid syntax.\n"); + errmsg = "Invalid syntax."; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); return; } uint8_t filenum = atoi(argv[1]); if ((filenum == 0 && strcmp(argv[1], "0")) || filenum >= MAX_FILES) { - wprintw(window, "No pending file transfers with that number.\n"); + errmsg = "No pending file transfers with that number."; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); return; } if (!friends[self->num].file_receiver.pending[filenum]) { - wprintw(window, "No pending file transfers with that number.\n"); + errmsg = "No pending file transfers with that number."; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); return; } uint8_t *filename = friends[self->num].file_receiver.filenames[filenum]; if (tox_file_send_control(m, self->num, 1, filenum, TOX_FILECONTROL_ACCEPT, 0, 0) == 0) { - wprintw(window, "Accepted file transfer %u. Saving file as: '%s'\n", filenum, filename); + uint8_t msg[MAX_STR_SIZE]; + snprintf(msg, sizeof(msg), "Saving file as: '%s' (%.1f%%)", filename, 0.0); + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); + friends[self->num].file_receiver.line_id = self->chatwin->hst->line_end->id; if ((friends[self->num].file_receiver.files[filenum] = fopen(filename, "a")) == NULL) { - wattron(window, COLOR_PAIR(RED)); - wprintw(window, "* Error writing to file.\n"); - wattroff(window, COLOR_PAIR(RED)); + errmsg = "* Error writing to file."; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, RED); tox_file_send_control(m, self->num, 1, filenum, TOX_FILECONTROL_KILL, 0, 0); } } else { - wprintw(window, "File transfer failed.\n"); + errmsg = "File transfer failed."; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); } friends[self->num].file_receiver.pending[filenum] = false; @@ -167,20 +202,25 @@ void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) { + uint8_t *errmsg; + if (max_file_senders_index >= (MAX_FILES-1)) { - wprintw(window,"Please wait for some of your outgoing file transfers to complete.\n"); + errmsg = "Please wait for some of your outgoing file transfers to complete."; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); return; } if (argc < 1) { - wprintw(window, "Invalid syntax.\n"); + errmsg = "Invalid syntax."; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); return; } uint8_t *path = argv[1]; if (path[0] != '\"') { - wprintw(window, "File path must be enclosed in quotes.\n"); + errmsg = "File path must be enclosed in quotes."; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); return; } @@ -188,14 +228,16 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv int path_len = strlen(path); if (path_len > MAX_STR_SIZE) { - wprintw(window, "File path exceeds character limit.\n"); + errmsg = "File path exceeds character limit."; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); return; } FILE *file_to_send = fopen(path, "r"); if (file_to_send == NULL) { - wprintw(window, "File '%s' not found.\n", path); + errmsg = "File not found."; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); return; } @@ -208,7 +250,8 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv int filenum = tox_new_file_sender(m, self->num, filesize, filename, strlen(filename) + 1); if (filenum == -1) { - wprintw(window, "Error sending file.\n"); + errmsg = "Error sending file."; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); return; } @@ -220,13 +263,16 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv file_senders[i].active = true; file_senders[i].toxwin = self; file_senders[i].file = file_to_send; - file_senders[i].filenum = (uint8_t) filenum; + file_senders[i].filenum = filenum; file_senders[i].friendnum = self->num; - file_senders[i].timestamp = (uint64_t) time(NULL); + file_senders[i].timestamp = get_unix_time(); + file_senders[i].size = filesize; file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1, tox_file_data_size(m, self->num), file_to_send); - wprintw(window, "Sending file: '%s'\n", path); + uint8_t msg[MAX_STR_SIZE]; + snprintf(msg, sizeof(msg), "Sending file: '%s'", path); + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); if (i == max_file_senders_index) ++max_file_senders_index; diff --git a/src/execute.c b/src/execute.c index 18b1302..4d088a2 100644 --- a/src/execute.c +++ b/src/execute.c @@ -32,6 +32,7 @@ #include "execute.h" #include "chat_commands.h" #include "global_commands.h" +#include "line_info.h" struct cmd_func { const char *name; @@ -77,7 +78,7 @@ static struct cmd_func chat_commands[] = { /* Parses input command and puts args into arg array. Returns number of arguments on success, -1 on failure. */ -static int parse_command(WINDOW *w, char *cmd, char (*args)[MAX_STR_SIZE]) +static int parse_command(WINDOW *w, ToxWindow *self, char *cmd, char (*args)[MAX_STR_SIZE]) { int num_args = 0; bool cmd_end = false; /* flags when we get to the end of cmd */ @@ -89,7 +90,8 @@ static int parse_command(WINDOW *w, char *cmd, char (*args)[MAX_STR_SIZE]) end = strchr(cmd+1, '\"'); if (end++ == NULL) { /* Increment past the end quote */ - wprintw(w, "Invalid argument. Did you forget a closing \"?\n"); + uint8_t *errmsg = "Invalid argument. Did you forget a closing \"?"; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); return -1; } @@ -132,7 +134,7 @@ void execute(WINDOW* w, ToxWindow *self, Tox *m, char *cmd, int mode) return; char args[MAX_NUM_ARGS][MAX_STR_SIZE] = {0}; - int num_args = parse_command(w, cmd, args); + int num_args = parse_command(w, self, cmd, args); if (num_args == -1) return; @@ -154,5 +156,6 @@ void execute(WINDOW* w, ToxWindow *self, Tox *m, char *cmd, int mode) if (do_command(w, self, m, num_args, GLOBAL_NUM_COMMANDS, global_commands, args) == 0) return; - wprintw(w, "Invalid command.\n"); + uint8_t *errmsg = "Invalid command."; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); } diff --git a/src/execute.h b/src/execute.h index 1f22a84..1e91f17 100644 --- a/src/execute.h +++ b/src/execute.h @@ -24,7 +24,7 @@ #ifdef _SUPPORT_AUDIO #define GLOBAL_NUM_COMMANDS 16 - #define CHAT_NUM_COMMANDS 9 + #define CHAT_NUM_COMMANDS 10 #else #define GLOBAL_NUM_COMMANDS 14 #define CHAT_NUM_COMMANDS 5 diff --git a/src/file_senders.c b/src/file_senders.c index b9d81fe..b3b2338 100644 --- a/src/file_senders.c +++ b/src/file_senders.c @@ -29,6 +29,7 @@ #include #include "toxic_windows.h" +#include "line_info.h" FileSender file_senders[MAX_FILES]; uint8_t max_file_senders_index; @@ -61,24 +62,25 @@ void close_all_file_senders(void) void do_file_senders(Tox *m) { + uint8_t msg[MAX_STR_SIZE]; int i; for (i = 0; i < max_file_senders_index; ++i) { if (!file_senders[i].active) continue; + ToxWindow *self = file_senders[i].toxwin; uint8_t *pathname = file_senders[i].pathname; - uint8_t filenum = file_senders[i].filenum; - int friendnum = file_senders[i].friendnum; + int filenum = file_senders[i].filenum; + int32_t friendnum = file_senders[i].friendnum; FILE *fp = file_senders[i].file; - uint64_t current_time = (uint64_t) time(NULL); + uint64_t current_time = get_unix_time(); /* If file transfer has timed out kill transfer and send kill control */ if (timed_out(file_senders[i].timestamp, current_time, TIMEOUT_FILESENDER)) { - ChatContext *ctx = file_senders[i].toxwin->chatwin; - - if (ctx != NULL) { - wprintw(ctx->history, "File transfer for '%s' timed out.\n", pathname); + if (self->chatwin != NULL) { + snprintf(msg, sizeof(msg), "File transfer for '%s' timed out.", pathname); + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); alert_window(file_senders[i].toxwin, WINDOW_ALERT_2, true); } @@ -96,11 +98,24 @@ void do_file_senders(Tox *m) file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1, tox_file_data_size(m, friendnum), fp); - if (file_senders[i].piecelen == 0) { - ChatContext *ctx = file_senders[i].toxwin->chatwin; + /* refresh line with percentage complete */ + if (self->chatwin != NULL) { + uint64_t size = file_senders[i].size; + long double remain = (long double) tox_file_data_remaining(m, friendnum, filenum, 0); + long double pct_remain = 100; - if (ctx != NULL) { - wprintw(ctx->history, "File '%s' successfuly sent.\n", pathname); + if (remain) + pct_remain = (1 - (remain / size)) * 100; + + const uint8_t *name = file_senders[filenum].pathname; + snprintf(msg, sizeof(msg), "File transfer for '%s' accepted (%.1Lf%%)", name, pct_remain); + line_info_set(self, file_senders[filenum].line_id, msg); + } + + if (file_senders[i].piecelen == 0) { + if (self->chatwin != NULL) { + snprintf(msg, sizeof(msg), "File '%s' successfuly sent.", pathname); + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); alert_window(file_senders[i].toxwin, WINDOW_ALERT_2, true); } diff --git a/src/friendlist.c b/src/friendlist.c index 430bfeb..ecb9054 100644 --- a/src/friendlist.c +++ b/src/friendlist.c @@ -27,12 +27,14 @@ #include #include #include +#include #include #include "chat.h" #include "friendlist.h" #include "misc_tools.h" +#include "line_info.h" #ifdef _SUPPORT_AUDIO #include "audio_call.h" @@ -49,7 +51,10 @@ extern struct _Winthread Winthread; ToxicFriend friends[MAX_FRIENDS_NUM]; static int friendlist_index[MAX_FRIENDS_NUM] = {0}; -static PendingDel pendingdelete; +struct _pendingDel { + int num; + bool active; +} pendingdelete; #define S_WEIGHT 100 @@ -65,7 +70,7 @@ static int index_name_cmp(const void *n1, const void *n2) } /* sorts friendlist_index first by connection status then alphabetically */ -void sort_friendlist_index(Tox *m) +void sort_friendlist_index(void) { int i; int n = 0; @@ -78,7 +83,17 @@ void sort_friendlist_index(Tox *m) qsort(friendlist_index, num_friends, sizeof(int), index_name_cmp); } -static void friendlist_onMessage(ToxWindow *self, Tox *m, int num, uint8_t *str, uint16_t len) +static void update_friend_last_online(int32_t num, uint64_t timestamp) +{ + friends[num].last_online.last_on = timestamp; + friends[num].last_online.tm = *localtime(×tamp); + + /* if the format changes make sure TIME_STR_SIZE is the correct size */ + strftime(friends[num].last_online.hour_min_str, TIME_STR_SIZE, "%I:%M %p", + &friends[num].last_online.tm); +} + +static void friendlist_onMessage(ToxWindow *self, Tox *m, int32_t num, uint8_t *str, uint16_t len) { if (num >= max_friends_index) return; @@ -87,43 +102,50 @@ static void friendlist_onMessage(ToxWindow *self, Tox *m, int num, uint8_t *str, if (get_num_active_windows() < MAX_WINDOWS_NUM) { friends[num].chatwin = add_window(m, new_chat(m, friends[num].num)); } else { + str[len] = '\0'; + uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'}; - tox_get_name(m, num, nick); - nick[TOXIC_MAX_NAME_LENGTH] = '\0'; - wprintw(prompt->window, "%s: %s\n", nick, str); + int n_len = tox_get_name(m, num, nick); + n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH-1); + nick[n_len] = '\0'; - prep_prompt_win(); - wattron(prompt->window, COLOR_PAIR(RED)); - wprintw(prompt->window, "* Warning: Too many windows are open.\n"); - wattron(prompt->window, COLOR_PAIR(RED)); + uint8_t timefrmt[TIME_STR_SIZE]; + get_time_str(timefrmt); + line_info_add(prompt, timefrmt, nick, NULL, str, IN_MSG, 0, 0); + + uint8_t *msg = "* Warning: Too many windows are open."; + line_info_add(prompt, NULL, NULL, NULL, msg, SYS_MSG, 0, RED); alert_window(prompt, WINDOW_ALERT_1, true); } } } -static void friendlist_onConnectionChange(ToxWindow *self, Tox *m, int num, uint8_t status) +static void friendlist_onConnectionChange(ToxWindow *self, Tox *m, int32_t num, uint8_t status) { if (num >= max_friends_index) return; - friends[num].online = status == 1 ? true : false; - sort_friendlist_index(m); + friends[num].online = status; + update_friend_last_online(num, get_unix_time()); + store_data(m, DATA_FILE); + sort_friendlist_index(); } -static void friendlist_onNickChange(ToxWindow *self, Tox *m, int num, uint8_t *str, uint16_t len) +static void friendlist_onNickChange(ToxWindow *self, Tox *m, int32_t num, uint8_t *str, uint16_t len) { if (len > TOX_MAX_NAME_LENGTH || num >= max_friends_index) return; - str[TOXIC_MAX_NAME_LENGTH] = '\0'; - len = strlen(str) + 1; - memcpy(friends[num].name, str, len); + len = MIN(len, TOXIC_MAX_NAME_LENGTH-1); + + str[len] = '\0'; + strcpy(friends[num].name, str); friends[num].namelength = len; - sort_friendlist_index(m); + sort_friendlist_index(); } -static void friendlist_onStatusChange(ToxWindow *self, Tox *m, int num, TOX_USERSTATUS status) +static void friendlist_onStatusChange(ToxWindow *self, Tox *m, int32_t num, uint8_t status) { if (num >= max_friends_index) return; @@ -131,16 +153,17 @@ static void friendlist_onStatusChange(ToxWindow *self, Tox *m, int num, TOX_USER friends[num].status = status; } -static void friendlist_onStatusMessageChange(ToxWindow *self, int num, uint8_t *str, uint16_t len) +static void friendlist_onStatusMessageChange(ToxWindow *self, int32_t num, uint8_t *str, uint16_t len) { if (len > TOX_MAX_STATUSMESSAGE_LENGTH || num >= max_friends_index) return; - memcpy(friends[num].statusmsg, str, len); + strcpy(friends[num].statusmsg, str); + friends[num].statusmsg[len] = '\0'; friends[num].statusmsg_len = len; } -void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int num, bool sort) +void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int32_t num, bool sort) { if (max_friends_index < 0 || max_friends_index >= MAX_FRIENDS_NUM) return; @@ -156,13 +179,14 @@ void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int num, bool sort) friends[i].status = TOX_USERSTATUS_NONE; friends[i].namelength = tox_get_name(m, num, friends[i].name); tox_get_client_id(m, num, friends[i].pub_key); + update_friend_last_online(i, tox_get_last_online(m, i)); if (friends[i].namelength == -1 || friends[i].name[0] == '\0') { strcpy(friends[i].name, (uint8_t *) UNKNOWN_NAME); - friends[i].namelength = strlen(UNKNOWN_NAME) + 1; + friends[i].namelength = strlen(UNKNOWN_NAME); } else { /* Enforce toxic's maximum name length */ - friends[i].name[TOXIC_MAX_NAME_LENGTH] = '\0'; - friends[i].namelength = strlen(friends[i].name) + 1; + friends[i].namelength = MIN(friends[i].namelength, TOXIC_MAX_NAME_LENGTH); + friends[i].name[friends[i].namelength] = '\0'; } num_friends = tox_count_friendlist(m); @@ -171,14 +195,14 @@ void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int num, bool sort) ++max_friends_index; if (sort) - sort_friendlist_index(m); + sort_friendlist_index(); return; } } } -static void friendlist_onFileSendRequest(ToxWindow *self, Tox *m, int num, uint8_t filenum, +static void friendlist_onFileSendRequest(ToxWindow *self, Tox *m, int32_t num, uint8_t filenum, uint64_t filesize, uint8_t *filename, uint16_t filename_len) { if (num >= max_friends_index) @@ -190,21 +214,21 @@ static void friendlist_onFileSendRequest(ToxWindow *self, Tox *m, int num, uint8 } else { tox_file_send_control(m, num, 1, filenum, TOX_FILECONTROL_KILL, 0, 0); - uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'}; - tox_get_name(m, num, nick); - nick[TOXIC_MAX_NAME_LENGTH] = '\0'; + uint8_t nick[TOX_MAX_NAME_LENGTH]; + int n_len = tox_get_name(m, num, nick); + n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH-1); + nick[n_len] = '\0'; - prep_prompt_win(); - wattron(prompt->window, COLOR_PAIR(RED)); - wprintw(prompt->window, "* File transfer from %s failed: too many windows are open.\n", nick); - wattron(prompt->window, COLOR_PAIR(RED)); + uint8_t msg[MAX_STR_SIZE]; + snprintf(msg, sizeof(msg), "* File transfer from %s failed: too many windows are open.", nick); + line_info_add(prompt, NULL, NULL, NULL, msg, SYS_MSG, 0, RED); alert_window(prompt, WINDOW_ALERT_1, true); } } } -static void friendlist_onGroupInvite(ToxWindow *self, Tox *m, int num, uint8_t *group_pub_key) +static void friendlist_onGroupInvite(ToxWindow *self, Tox *m, int32_t num, uint8_t *group_pub_key) { if (num >= max_friends_index) return; @@ -213,14 +237,14 @@ static void friendlist_onGroupInvite(ToxWindow *self, Tox *m, int num, uint8_t * if (get_num_active_windows() < MAX_WINDOWS_NUM) { friends[num].chatwin = add_window(m, new_chat(m, friends[num].num)); } else { - uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'}; - tox_get_name(m, num, nick); - nick[TOXIC_MAX_NAME_LENGTH] = '\0'; + uint8_t nick[TOX_MAX_NAME_LENGTH]; + int n_len = tox_get_name(m, num, nick); + n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH-1); + nick[n_len] = '\0'; - prep_prompt_win(); - wattron(prompt->window, COLOR_PAIR(RED)); - wprintw(prompt->window, "* Group chat invite from %s failed: too many windows are open.\n", nick); - wattron(prompt->window, COLOR_PAIR(RED)); + uint8_t msg[MAX_STR_SIZE]; + snprintf(msg, sizeof(msg), "* Group chat invite from %s failed: too many windows are open.", nick); + line_info_add(prompt, NULL, NULL, NULL, msg, SYS_MSG, 0, RED); alert_window(prompt, WINDOW_ALERT_1, true); } @@ -237,7 +261,7 @@ static void select_friend(ToxWindow *self, Tox *m, wint_t key) } } -static void delete_friend(Tox *m, int f_num) +static void delete_friend(Tox *m, int32_t f_num) { tox_del_friend(m, f_num); memset(&friends[f_num], 0, sizeof(ToxicFriend)); @@ -256,12 +280,12 @@ static void delete_friend(Tox *m, int f_num) if (num_friends && num_selected == num_friends) --num_selected; - sort_friendlist_index(m); + sort_friendlist_index(); store_data(m, DATA_FILE); } /* activates delete friend popup */ -static void del_friend_activate(ToxWindow *self, Tox *m, int f_num) +static void del_friend_activate(ToxWindow *self, Tox *m, int32_t f_num) { int x2, y2; getmaxyx(self->window, y2, x2); @@ -277,7 +301,7 @@ static void del_friend_deactivate(ToxWindow *self, Tox *m, wint_t key) if (key == 'y') delete_friend(m, pendingdelete.num); - memset(&pendingdelete, 0, sizeof(PendingDel)); + memset(&pendingdelete, 0, sizeof(pendingdelete)); delwin(self->popup); self->popup = NULL; clear(); @@ -303,7 +327,7 @@ static void draw_popup(ToxWindow *self, Tox *m) wrefresh(self->popup); } -static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key) +static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) { if (num_friends == 0) return; @@ -318,25 +342,25 @@ static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key) return; } - if (key == '\n') { - /* Jump to chat window if already open */ - if (friends[f].chatwin != -1) { - set_active_window(friends[f].chatwin); - } else if (get_num_active_windows() < MAX_WINDOWS_NUM) { - friends[f].chatwin = add_window(m, new_chat(m, friends[f].num)); - set_active_window(friends[f].chatwin); - } else { - prep_prompt_win(); - wattron(prompt->window, COLOR_PAIR(RED)); - wprintw(prompt->window, "* Warning: Too many windows are open.\n"); - wattron(prompt->window, COLOR_PAIR(RED)); + if (key != ltr) { + if (key == '\n') { + /* Jump to chat window if already open */ + if (friends[f].chatwin != -1) { + set_active_window(friends[f].chatwin); + } else if (get_num_active_windows() < MAX_WINDOWS_NUM) { + friends[f].chatwin = add_window(m, new_chat(m, friends[f].num)); + set_active_window(friends[f].chatwin); + } else { + uint8_t *msg = "* Warning: Too many windows are open."; + line_info_add(prompt, NULL, NULL, NULL, msg, SYS_MSG, 0, RED); - alert_window(prompt, WINDOW_ALERT_1, true); + alert_window(prompt, WINDOW_ALERT_1, true); + } + } else if (key == KEY_DC) { + del_friend_activate(self, m, f); + } else { + select_friend(self, m, key); } - } else if (key == KEY_DC) { - del_friend_activate(self, m, f); - } else { - select_friend(self, m, key); } } @@ -349,6 +373,9 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m) int x2, y2; getmaxyx(self->window, y2, x2); + uint64_t cur_time = get_unix_time(); + struct tm cur_loc_tm = *localtime(&cur_time); + bool fix_statuses = x2 != self->x; /* true if window x axis has changed */ wattron(self->window, COLOR_PAIR(CYAN)); @@ -368,8 +395,9 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m) pthread_mutex_unlock(&Winthread.lock); wattron(self->window, A_BOLD); - wprintw(self->window, " Online: %d/%d \n\n", nf, num_friends); + wprintw(self->window, " Online: "); wattroff(self->window, A_BOLD); + wprintw(self->window, "%d/%d \n\n", nf, num_friends); if ((y2 - FLIST_OFST) <= 0) /* don't allow division by zero */ return; @@ -396,7 +424,7 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m) } if (friends[f].online) { - TOX_USERSTATUS status = friends[f].status; + uint8_t status = friends[f].status; int colour = WHITE; switch (status) { @@ -409,34 +437,41 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m) case TOX_USERSTATUS_BUSY: colour = RED; break; + case TOX_USERSTATUS_INVALID: + colour = MAGENTA; + break; } - wprintw(self->window, "["); wattron(self->window, COLOR_PAIR(colour) | A_BOLD); - wprintw(self->window, "O"); + wprintw(self->window, "O "); wattroff(self->window, COLOR_PAIR(colour) | A_BOLD); - wprintw(self->window, "]"); if (f_selected) - wattron(self->window, A_BOLD); + wattron(self->window, COLOR_PAIR(BLUE)); + wattron(self->window, A_BOLD); wprintw(self->window, "%s", friends[f].name); + wattroff(self->window, A_BOLD); if (f_selected) - wattroff(self->window, A_BOLD); + wattroff(self->window, COLOR_PAIR(BLUE)); /* Reset friends[f].statusmsg on window resize */ if (fix_statuses) { uint8_t statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH] = {'\0'}; + pthread_mutex_lock(&Winthread.lock); - tox_get_status_message(m, friends[f].num, statusmsg, TOX_MAX_STATUSMESSAGE_LENGTH); - friends[f].statusmsg_len = tox_get_status_message_size(m, f); + uint16_t s_len = tox_get_status_message(m, friends[f].num, statusmsg, + TOX_MAX_STATUSMESSAGE_LENGTH); pthread_mutex_unlock(&Winthread.lock); + + friends[f].statusmsg_len = s_len; + friends[f].statusmsg[s_len] = '\0'; snprintf(friends[f].statusmsg, sizeof(friends[f].statusmsg), "%s", statusmsg); } /* Truncate note if it doesn't fit on one line */ - uint16_t maxlen = x2 - getcurx(self->window) - 4; + uint16_t maxlen = x2 - getcurx(self->window) - 2; if (friends[f].statusmsg_len > maxlen) { friends[f].statusmsg[maxlen-3] = '\0'; strcat(friends[f].statusmsg, "..."); @@ -444,21 +479,43 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m) friends[f].statusmsg_len = maxlen; } - wprintw(self->window, " (%s)\n", friends[f].statusmsg); + if (friends[f].statusmsg[0]) + wprintw(self->window, " %s", friends[f].statusmsg); + + wprintw(self->window, "\n"); } else { - wprintw(self->window, "["); + wprintw(self->window, "o "); + + if (f_selected) + wattron(self->window, COLOR_PAIR(BLUE)); + wattron(self->window, A_BOLD); - wprintw(self->window, "O"); + wprintw(self->window, "%s", friends[f].name); wattroff(self->window, A_BOLD); - wprintw(self->window, "]"); if (f_selected) - wattron(self->window, A_BOLD); + wattroff(self->window, COLOR_PAIR(YELLOW)); + + uint64_t last_seen = friends[f].last_online.last_on; - wprintw(self->window, "%s\n", friends[f].name); + if (last_seen != 0) { + int day_dist = (cur_loc_tm.tm_yday - friends[f].last_online.tm.tm_yday) % 365; + const uint8_t *hourmin = friends[f].last_online.hour_min_str; - if (f_selected) - wattroff(self->window, A_BOLD); + switch (day_dist) { + case 0: + wprintw(self->window, " Last seen: Today %s\n", hourmin); + break; + case 1: + wprintw(self->window, " Last seen: Yesterday %s\n", hourmin); + break; + default: + wprintw(self->window, " Last seen: %d days ago\n", day_dist); + break; + } + } else { + wprintw(self->window, " Last seen: Never\n"); + } } } } @@ -468,16 +525,11 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m) draw_popup(self, m); } -void disable_chatwin(int f_num) +void disable_chatwin(int32_t f_num) { friends[f_num].chatwin = -1; } -static void friendlist_onInit(ToxWindow *self, Tox *m) -{ - -} - #ifdef _SUPPORT_AUDIO static void friendlist_onAv(ToxWindow *self, ToxAv *av) { @@ -493,14 +545,17 @@ static void friendlist_onAv(ToxWindow *self, ToxAv *av) friends[id].chatwin = add_window(m, new_chat(m, friends[id].num)); } else { uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'}; - tox_get_name(m, id, nick); - nick[TOXIC_MAX_NAME_LENGTH] = '\0'; - wprintw(prompt->window, "Audio action from: %s!\n", nick); - - prep_prompt_win(); - wattron(prompt->window, COLOR_PAIR(RED)); - wprintw(prompt->window, "* Warning: Too many windows are open.\n"); - wattron(prompt->window, COLOR_PAIR(RED)); + int n_len = tox_get_name(m, id, nick); + + n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH-1); + nick[n_len] = '\0'; + + uint8_t msg[MAX_STR_SIZE]; + snprintf(msg, sizeof(msg), "Audio action from: %s!", nick); + line_info_add(prompt, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); + + uint8_t *errmsg = "* Warning: Too many windows are open."; + line_info_add(prompt, NULL, NULL, NULL, errmsg, SYS_MSG, 0, RED); alert_window(prompt, WINDOW_ALERT_0, true); } @@ -517,7 +572,6 @@ ToxWindow new_friendlist(void) ret.onKey = &friendlist_onKey; ret.onDraw = &friendlist_onDraw; - ret.onInit = &friendlist_onInit; ret.onFriendAdded = &friendlist_onFriendAdded; ret.onMessage = &friendlist_onMessage; ret.onConnectionChange = &friendlist_onConnectionChange; diff --git a/src/friendlist.h b/src/friendlist.h index 75af609..363cbea 100644 --- a/src/friendlist.h +++ b/src/friendlist.h @@ -23,8 +23,17 @@ #ifndef FRIENDLIST_H_53I41IM #define FRIENDLIST_H_53I41IM +#include #include "toxic_windows.h" +#define TIME_STR_SIZE 16 + +struct LastOnline { + uint64_t last_on; + struct tm tm; + uint8_t hour_min_str[TIME_STR_SIZE]; /* holds 12-hour time string e.g. "10:43 PM" */ +}; + typedef struct { uint8_t name[TOX_MAX_NAME_LENGTH]; uint16_t namelength; @@ -32,28 +41,24 @@ typedef struct { uint16_t statusmsg_len; uint8_t pending_groupchat[TOX_CLIENT_ID_SIZE]; uint8_t pub_key[TOX_CLIENT_ID_SIZE]; - int num; + int32_t num; int chatwin; bool active; bool online; - bool is_typing; + uint8_t is_typing; bool logging_on; /* saves preference for friend irrespective of chat windows */ - TOX_USERSTATUS status; + uint8_t status; + struct LastOnline last_online; struct FileReceiver file_receiver; } ToxicFriend; -typedef struct { - int num; - bool active; -} PendingDel; - ToxWindow new_friendlist(void); -void disable_chatwin(int f_num); +void disable_chatwin(int32_t f_num); int get_friendnum(uint8_t *name); -void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int num, bool sort); +void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int32_t num, bool sort); /* sorts friendlist_index first by connection status then alphabetically */ -void sort_friendlist_index(Tox *m); +void sort_friendlist_index(void); #endif /* end of include guard: FRIENDLIST_H_53I41IM */ diff --git a/src/global_commands.c b/src/global_commands.c index 0eb3392..deb1b93 100644 --- a/src/global_commands.c +++ b/src/global_commands.c @@ -30,6 +30,8 @@ #include "toxic_windows.h" #include "misc_tools.h" #include "friendlist.h" +#include "log.h" +#include "line_info.h" extern char *DATA_FILE; extern ToxWindow *prompt; @@ -42,30 +44,34 @@ extern uint8_t num_frnd_requests; /* command functions */ void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) { - /* check arguments */ + uint8_t *msg; + if (argc != 1) { - wprintw(window, "Invalid syntax.\n"); - return; + msg = "Invalid syntax."; + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); + return; } int req = atoi(argv[1]); if ((req == 0 && strcmp(argv[1], "0"))|| req >= MAX_FRIENDS_NUM) { - wprintw(window, "No pending friend request with that number.\n"); + msg = "No pending friend request with that number."; + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); return; } if (!strlen(pending_frnd_requests[req])) { - wprintw(window, "No pending friend request with that number.\n"); + msg = "No pending friend request with that number."; + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); return; } - int friendnum = tox_add_friend_norequest(m, pending_frnd_requests[req]); + int32_t friendnum = tox_add_friend_norequest(m, pending_frnd_requests[req]); if (friendnum == -1) - wprintw(window, "Failed to add friend.\n"); + msg = "Failed to add friend."; else { - wprintw(window, "Friend request accepted.\n"); + msg = "Friend request accepted."; on_friendadded(m, friendnum, true); } @@ -79,12 +85,16 @@ void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[ } num_frnd_requests = i; + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); } void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) { + uint8_t *errmsg; + if (argc < 1) { - wprintw(window, "Invalid syntax.\n"); + errmsg = "Invalid syntax."; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); return; } @@ -95,7 +105,8 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX uint8_t *temp = argv[2]; if (temp[0] != '\"') { - wprintw(window, "Message must be enclosed in quotes.\n"); + errmsg = "Message must be enclosed in quotes."; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); return; } @@ -103,12 +114,14 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX snprintf(msg, sizeof(msg), "%s", temp); } else { uint8_t selfname[TOX_MAX_NAME_LENGTH]; - tox_get_self_name(m, selfname, TOX_MAX_NAME_LENGTH); + uint16_t n_len = tox_get_self_name(m, selfname); + selfname[n_len] = '\0'; snprintf(msg, sizeof(msg), "Hello, my name is %s. Care to Tox?", selfname); } if (strlen(id) != 2 * TOX_FRIEND_ADDRESS_SIZE) { - wprintw(window, "Invalid ID length.\n"); + errmsg = "Invalid ID length."; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); return; } @@ -123,7 +136,8 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX xx[2] = '\0'; if (sscanf(xx, "%02x", &x) != 1) { - wprintw(window, "Invalid ID.\n"); + errmsg = "Invalid ID."; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); return; } @@ -134,48 +148,52 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX id[i] = toupper(id[i]); } - int f_num = tox_add_friend(m, id_bin, msg, strlen(msg) + 1); + int32_t f_num = tox_add_friend(m, id_bin, msg, strlen(msg)); switch (f_num) { case TOX_FAERR_TOOLONG: - wprintw(window, "Message is too long.\n"); + errmsg = "Message is too long."; break; case TOX_FAERR_NOMESSAGE: - wprintw(window, "Please add a message to your request.\n"); + errmsg = "Please add a message to your request."; break; case TOX_FAERR_OWNKEY: - wprintw(window, "That appears to be your own ID.\n"); + errmsg = "That appears to be your own ID."; break; case TOX_FAERR_ALREADYSENT: - wprintw(window, "Friend request has already been sent.\n"); + errmsg = "Friend request has already been sent."; break; case TOX_FAERR_UNKNOWN: - wprintw(window, "Undefined error when adding friend.\n"); + errmsg = "Undefined error when adding friend."; break; case TOX_FAERR_BADCHECKSUM: - wprintw(window, "Bad checksum in address.\n"); + errmsg = "Bad checksum in address."; break; case TOX_FAERR_SETNEWNOSPAM: - wprintw(window, "Nospam was different (is this contact already added?)\n"); + errmsg = "Nospam was different (is this contact already added?"; break; default: - wprintw(window, "Friend request sent.\n"); + errmsg = "Friend request sent."; on_friendadded(m, f_num, true); break; } + + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); } void cmd_clear(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) { - wclear(window); - wprintw(window, "\n\n"); + line_info_clear(self->chatwin->hst); } void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) { + uint8_t *errmsg; + /* check arguments */ if (argc != 3) { - wprintw(window, "Invalid syntax.\n"); + errmsg = "Invalid syntax."; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); return; } @@ -185,7 +203,8 @@ void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv) char *key = argv[3]; if (atoi(port) == 0) { - wprintw(window, "Invalid syntax.\n"); + errmsg = "Invalid syntax."; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); return; } @@ -197,53 +216,46 @@ void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv) void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) { + uint8_t *errmsg; + if (get_num_active_windows() >= MAX_WINDOWS_NUM) { - wattron(window, COLOR_PAIR(RED)); - wprintw(window, " * Warning: Too many windows are open.\n"); - wattron(window, COLOR_PAIR(RED)); + errmsg = " * Warning: Too many windows are open."; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, RED); return; } int groupnum = tox_add_groupchat(m); if (groupnum == -1) { - wprintw(window, "Group chat instance failed to initialize.\n"); + errmsg = "Group chat instance failed to initialize."; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); return; } if (init_groupchat_win(prompt, m, groupnum) == -1) { - wprintw(window, "Group chat window failed to initialize.\n"); + errmsg = "Group chat window failed to initialize."; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); tox_del_groupchat(m, groupnum); return; } - wprintw(window, "Group chat created as %d.\n", groupnum); + uint8_t msg[MAX_STR_SIZE]; + snprintf(msg, sizeof(msg), "Group chat created as %d.", groupnum); + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); } void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) { + uint8_t *msg; + struct chatlog *log = self->chatwin->log; + if (argc == 0) { - bool on; - - if (self->is_chat || self->is_groupchat) - on = self->chatwin->log->log_on; - else if (self->is_prompt) - on = self->promptbuf->log->log_on; - - if (on) { - wprintw(window, "Logging for this window is "); - wattron(window, COLOR_PAIR(GREEN) | A_BOLD); - wprintw(window, "[on]"); - wattroff(window, COLOR_PAIR(GREEN) | A_BOLD); - wprintw(window, ". Type \"/log off\" to disable.\n"); - } else { - wprintw(window, "Logging for this window is "); - wattron(window, COLOR_PAIR(RED) | A_BOLD); - wprintw(window, "[off]"); - wattroff(window, COLOR_PAIR(RED) | A_BOLD); - wprintw(window, ". Type \"/log on\" to enable.\n"); - } + if (log->log_on) + msg = "Logging for this window is ON. Type \"/log off\" to disable."; + else + msg = "Logging for this window is OFF. Type \"/log on\" to enable."; + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); return; } @@ -253,38 +265,31 @@ void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX if (self->is_chat) { friends[self->num].logging_on = true; - log_enable(self->name, friends[self->num].pub_key, self->chatwin->log); + log_enable(self->name, friends[self->num].pub_key, log); } else if (self->is_prompt) { uint8_t myid[TOX_FRIEND_ADDRESS_SIZE]; tox_get_address(m, myid); - log_enable(self->name, &myid, self->promptbuf->log); + log_enable(self->name, myid, log); } else if (self->is_groupchat) { - log_enable(self->name, NULL, self->chatwin->log); + log_enable(self->name, NULL, log); } - wprintw(window, "Logging "); - wattron(window, COLOR_PAIR(GREEN) | A_BOLD); - wprintw(window, "[on]\n"); - wattroff(window, COLOR_PAIR(GREEN) | A_BOLD); + msg = "Logging enabled"; + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); return; } else if (!strcmp(swch, "0") || !strcmp(swch, "off")) { - if (self->is_chat) { + if (self->is_chat) friends[self->num].logging_on = false; - log_disable(self->chatwin->log); - } else if (self->is_prompt) { - log_disable(self->promptbuf->log); - } else if (self->is_groupchat) { - log_disable(self->chatwin->log); - } - wprintw(window, "Logging "); - wattron(window, COLOR_PAIR(RED) | A_BOLD); - wprintw(window, "[off]\n"); - wattroff(window, COLOR_PAIR(RED) | A_BOLD); + log_disable(log); + + msg = "Logging disabled"; + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); return; } - wprintw(window, "Invalid option. Use \"/log on\" and \"/log off\" to toggle logging.\n"); + msg = "Invalid option. Use \"/log on\" and \"/log off\" to toggle logging."; + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); } void cmd_myid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) @@ -301,14 +306,17 @@ void cmd_myid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA strcat(id, xx); } - wprintw(window, "%s\n", id); + line_info_add(self, NULL, NULL, NULL, id, SYS_MSG, 0, 0); } void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) { + uint8_t *errmsg; + /* check arguments */ if (argc < 1) { - wprintw(window, "Invalid name.\n"); + errmsg = "Invalid name."; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); return; } @@ -322,71 +330,90 @@ void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA } if (!valid_nick(nick)) { - wprintw(window, "Invalid name.\n"); + errmsg = "Invalid name."; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); return; } - if (len > TOXIC_MAX_NAME_LENGTH) { - nick[TOXIC_MAX_NAME_LENGTH] = L'\0'; - len = TOXIC_MAX_NAME_LENGTH; - } + len = MIN(len, TOXIC_MAX_NAME_LENGTH-1); - tox_set_name(m, nick, len+1); - prompt_update_nick(prompt, nick, len+1); + nick[len] = L'\0'; + + tox_set_name(m, nick, len); + prompt_update_nick(prompt, nick, len); store_data(m, DATA_FILE); } void cmd_note(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) { + uint8_t *errmsg; + if (argc < 1) { - wprintw(window, "Wrong number of arguments.\n"); + errmsg = "Wrong number of arguments."; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); return; } uint8_t *msg = argv[1]; if (msg[0] != '\"') { - wprintw(window, "Note must be enclosed in quotes.\n"); + errmsg = "Note must be enclosed in quotes."; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); return; } msg[strlen(++msg)-1] = L'\0'; - uint16_t len = strlen(msg) + 1; + uint16_t len = strlen(msg); tox_set_status_message(m, msg, len); - prompt_update_statusmessage(prompt, msg, len); } void cmd_prompt_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) { - wclear(window); - wattron(window, COLOR_PAIR(CYAN) | A_BOLD); - wprintw(window, "\n\nGlobal commands:\n"); - wattroff(window, COLOR_PAIR(CYAN) | A_BOLD); + struct history *hst = self->chatwin->hst; + line_info_clear(hst); + struct line_info *start = hst->line_start; + + uint8_t *msg = "Global commands:"; + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 1, CYAN); - wprintw(window, " /add : Add friend with optional message\n"); - wprintw(window, " /accept : Accept friend request\n"); - wprintw(window, " /connect : Manually connect to a DHT node\n"); - wprintw(window, " /status : Set status with optional note\n"); - wprintw(window, " /note : Set a personal note\n"); - wprintw(window, " /nick : Set your nickname\n"); - wprintw(window, " /log or : Enable/disable logging\n"); - wprintw(window, " /groupchat : Create a group chat\n"); - wprintw(window, " /myid : Print your ID\n"); - wprintw(window, " /help : Print this message again\n"); - wprintw(window, " /clear : Clear the window\n"); - wprintw(window, " /quit or /exit : Exit Toxic\n"); - #ifdef _SUPPORT_AUDIO - wprintw(window, " /lsdev : List devices where type: in|out\n"); - wprintw(window, " /sdev : Set active device\n"); + #define NUMLINES 14 +#else + #define NUMLINES 12 +#endif + + uint8_t lines[NUMLINES][MAX_STR_SIZE] = { + + { " /add : Add friend with optional message" }, + { " /accept : Accept friend request" }, + { " /connect : Manually connect to a DHT node" }, + { " /status : Set status with optional note" }, + { " /note : Set a personal note" }, + { " /nick : Set your nickname" }, + { " /log or : Enable/disable logging" }, + { " /groupchat : Create a group chat" }, + { " /myid : Print your ID" }, + { " /help : Print this message again" }, + { " /clear : Clear window history" }, + { " /quit or /exit : Exit Toxic" }, +#ifdef _SUPPORT_AUDIO + { " /lsdev : List devices where type: in|out" }, + { " /sdev : Set active device" }, #endif /* _SUPPORT_AUDIO */ - - wattron(window, COLOR_PAIR(CYAN) | A_BOLD); - wprintw(window, " * Argument messages must be enclosed in quotation marks.\n"); - wprintw(window, " * Use ctrl-o and ctrl-p to navigate through the tabs.\n\n"); - wattroff(window, COLOR_PAIR(CYAN) | A_BOLD); + +}; + int i; + + for (i = 0; i < NUMLINES; ++i) + line_info_add(self, NULL, NULL, NULL, lines[i], SYS_MSG, 0, 0); + + msg = " * Argument messages must be enclosed in quotation marks.\n" + " * Use ctrl-o and ctrl-p to navigate through the tabs.\n"; + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 1, CYAN); + + hst->line_start = start; } void cmd_quit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) @@ -397,16 +424,19 @@ void cmd_quit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) { uint8_t *msg = NULL; + uint8_t *errmsg; if (argc >= 2) { msg = argv[2]; if (msg[0] != '\"') { - wprintw(window, "Note must be enclosed in quotes.\n"); + errmsg = "Note must be enclosed in quotes."; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); return; } } else if (argc != 1) { - wprintw(window, "Wrong number of arguments.\n"); + errmsg = "Wrong number of arguments."; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); return; } @@ -427,7 +457,8 @@ void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[ else if (!strcmp(l_status, "busy")) status_kind = TOX_USERSTATUS_BUSY; else { - wprintw(window, "Invalid status. Valid statuses are: online, busy and away.\n"); + errmsg = "Invalid status. Valid statuses are: online, busy and away."; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); return; } @@ -436,7 +467,7 @@ void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[ if (msg != NULL) { msg[strlen(++msg)-1] = L'\0'; /* remove opening and closing quotes */ - uint16_t len = strlen(msg) + 1; + uint16_t len = strlen(msg); tox_set_status_message(m, msg, len); prompt_update_statusmessage(prompt, msg, len); } diff --git a/src/groupchat.c b/src/groupchat.c index 539438d..4d24182 100644 --- a/src/groupchat.c +++ b/src/groupchat.c @@ -35,6 +35,7 @@ #include "prompt.h" #include "toxic_strings.h" #include "log.h" +#include "line_info.h" extern char *DATA_FILE; extern int store_data(Tox *m, char *path); @@ -56,9 +57,14 @@ int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum) groupchats[i].num_peers = 0; groupchats[i].peer_names = malloc(sizeof(uint8_t) * TOX_MAX_NAME_LENGTH); groupchats[i].oldpeer_names = malloc(sizeof(uint8_t) * TOX_MAX_NAME_LENGTH); + groupchats[i].peer_name_lengths = malloc(sizeof(uint16_t)); + groupchats[i].oldpeer_name_lengths = malloc(sizeof(uint16_t)); + + memset(groupchats[i].peer_names, 0, sizeof(uint8_t) * TOX_MAX_NAME_LENGTH); + memset(groupchats[i].peer_name_lengths, 0, sizeof(uint16_t)); - /* temp fix */ memcpy(&groupchats[i].oldpeer_names[0], UNKNOWN_NAME, sizeof(UNKNOWN_NAME)); + groupchats[i].oldpeer_name_lengths[0] = (uint16_t) strlen(UNKNOWN_NAME); set_active_window(groupchats[i].chatwin); @@ -77,9 +83,11 @@ void kill_groupchat_window(ToxWindow *self) ChatContext *ctx = self->chatwin; log_disable(ctx->log); + line_info_cleanup(ctx->hst); delwin(ctx->linewin); del_window(self); free(ctx->log); + free(ctx->hst); free(ctx); } @@ -90,6 +98,8 @@ static void close_groupchat(ToxWindow *self, Tox *m, int groupnum) free(groupchats[groupnum].peer_names); free(groupchats[groupnum].oldpeer_names); + free(groupchats[groupnum].peer_name_lengths); + free(groupchats[groupnum].oldpeer_name_lengths); memset(&groupchats[groupnum], 0, sizeof(GroupChat)); int i; @@ -103,29 +113,44 @@ static void close_groupchat(ToxWindow *self, Tox *m, int groupnum) kill_groupchat_window(self); } -static void print_groupchat_help(ChatContext *ctx) +static void print_groupchat_help(ToxWindow *self) { - wattron(ctx->history, COLOR_PAIR(CYAN) | A_BOLD); - wprintw(ctx->history, "Group chat commands:\n"); - wattroff(ctx->history, COLOR_PAIR(CYAN) | A_BOLD); + struct history *hst = self->chatwin->hst; + line_info_clear(hst); + struct line_info *start = hst->line_start; - wprintw(ctx->history, " /add : Add friend with optional message\n"); - wprintw(ctx->history, " /status : Set your status with optional note\n"); - wprintw(ctx->history, " /note : Set a personal note\n"); - wprintw(ctx->history, " /nick : Set your nickname\n"); - wprintw(ctx->history, " /groupchat : Create a group chat\n"); - wprintw(ctx->history, " /log or : Enable/disable logging\n"); - wprintw(ctx->history, " /close : Close the current group chat\n"); - wprintw(ctx->history, " /help : Print this message again\n"); - wprintw(ctx->history, " /help global : Show a list of global commands\n"); - - wattron(ctx->history, COLOR_PAIR(CYAN) | A_BOLD); - wprintw(ctx->history, " * Argument messages must be enclosed in quotation marks.\n"); - wprintw(ctx->history, " * Scroll peer list with the Page Up/Page Down keys.\n\n"); - wattroff(ctx->history, COLOR_PAIR(CYAN) | A_BOLD); - wattron(ctx->history, COLOR_PAIR(WHITE) | A_BOLD); - wprintw(ctx->history, " Notice, some friends will be missing names while finding peers\n\n"); - wattroff(ctx->history, COLOR_PAIR(WHITE) | A_BOLD); + uint8_t *msg = "Group chat commands:"; + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 1, CYAN); + + #define NUMLINES 9 + + uint8_t lines[NUMLINES][MAX_STR_SIZE] = { + + { " /add : Add friend with optional message" }, + { " /status : Set your status with optional note" }, + { " /note : Set a personal note" }, + { " /nick : Set your nickname" }, + { " /groupchat : Create a group chat" }, + { " /log or : Enable/disable logging" }, + { " /close : Close the current group chat" }, + { " /help : Print this message again" }, + { " /help global : Show a list of global commands" }, + +}; + + int i; + + for (i = 0; i < NUMLINES; ++i) + line_info_add(self, NULL, NULL, NULL, lines[i], SYS_MSG, 0, 0); + + msg = " * Use ESC key to toggle history scroll mode"; + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 1, CYAN); + msg = " * Scroll peer list with the Page Up/Page Down keys.\n"; + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 1, CYAN); + msg = " * Notice, some friends will be missing names while finding peers\n"; + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 1, 0); + + hst->line_start = start; } static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, int groupnum, int peernum, @@ -134,18 +159,24 @@ static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, int groupnum, int if (self->num != groupnum) return; + msg[len] = '\0'; + ChatContext *ctx = self->chatwin; uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'}; - tox_group_peername(m, groupnum, peernum, nick); - nick[TOXIC_MAX_NAME_LENGTH] = '\0'; /* enforce client max name length */ + int n_len = tox_group_peername(m, groupnum, peernum, nick); + + n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH-1); /* enforce client max name length */ + nick[n_len] = '\0'; /* check if message contains own name and alert appropriately */ int alert_type = WINDOW_ALERT_1; bool beep = false; - uint8_t selfnick[TOX_MAX_NAME_LENGTH] = {'\0'}; - tox_get_self_name(m, selfnick, TOX_MAX_NAME_LENGTH); + uint8_t selfnick[TOX_MAX_NAME_LENGTH]; + uint16_t sn_len = tox_get_self_name(m, selfnick); + selfnick[sn_len] = '\0'; + int nick_clr = strcmp(nick, selfnick) == 0 ? GREEN : CYAN; bool nick_match = strcasestr(msg, selfnick) && strncmp(selfnick, nick, TOXIC_MAX_NAME_LENGTH); @@ -158,19 +189,10 @@ static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, int groupnum, int alert_window(self, alert_type, beep); - print_time(ctx->history); - wattron(ctx->history, COLOR_PAIR(nick_clr)); - wprintw(ctx->history, "%s: ", nick); - wattroff(ctx->history, COLOR_PAIR(nick_clr)); - - if (msg[0] == '>') { - wattron(ctx->history, COLOR_PAIR(GREEN)); - wprintw(ctx->history, "%s\n", msg); - wattroff(ctx->history, COLOR_PAIR(GREEN)); - } else { - wprintw(ctx->history, "%s\n", msg); - } + uint8_t timefrmt[TIME_STR_SIZE]; + get_time_str(timefrmt); + line_info_add(self, timefrmt, nick, NULL, msg, IN_MSG, 0, nick_clr); write_to_log(msg, nick, ctx->log, false); } @@ -180,14 +202,17 @@ static void groupchat_onGroupAction(ToxWindow *self, Tox *m, int groupnum, int p if (self->num != groupnum) return; + action[len] = '\0'; + ChatContext *ctx = self->chatwin; /* check if message contains own name and alert appropriately */ int alert_type = WINDOW_ALERT_1; bool beep = false; - uint8_t selfnick[TOX_MAX_NAME_LENGTH] = {'\0'}; - tox_get_self_name(m, selfnick, TOX_MAX_NAME_LENGTH); + uint8_t selfnick[TOX_MAX_NAME_LENGTH]; + uint16_t n_len = tox_get_self_name(m, selfnick); + selfnick[n_len] = '\0'; bool nick_match = strcasestr(action, selfnick); @@ -199,47 +224,65 @@ static void groupchat_onGroupAction(ToxWindow *self, Tox *m, int groupnum, int p alert_window(self, alert_type, beep); uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'}; - tox_group_peername(m, groupnum, peernum, nick); - nick[TOXIC_MAX_NAME_LENGTH] = '\0'; /* enforce client max name length */ + n_len = tox_group_peername(m, groupnum, peernum, nick); - print_time(ctx->history); - wattron(ctx->history, COLOR_PAIR(YELLOW)); - wprintw(ctx->history, "* %s %s\n", nick, action); - wattroff(ctx->history, COLOR_PAIR(YELLOW)); + n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH-1); + nick[n_len] = '\0'; + uint8_t timefrmt[TIME_STR_SIZE]; + get_time_str(timefrmt); + + line_info_add(self, timefrmt, nick, NULL, action, ACTION, 0, 0); write_to_log(action, nick, ctx->log, true); } -/* Puts two copies of peerlist in chat instance */ -static void copy_peernames(int gnum, int npeers, uint8_t tmp_peerlist[][TOX_MAX_NAME_LENGTH]) +/* Puts two copies of peerlist/lengths in chat instance */ +static void copy_peernames(int gnum, uint8_t peerlist[][TOX_MAX_NAME_LENGTH], uint16_t lengths[], int npeers) { /* Assumes these are initiated in init_groupchat_win */ free(groupchats[gnum].peer_names); free(groupchats[gnum].oldpeer_names); + free(groupchats[gnum].peer_name_lengths); + free(groupchats[gnum].oldpeer_name_lengths); int N = TOX_MAX_NAME_LENGTH; groupchats[gnum].peer_names = malloc(sizeof(uint8_t) * npeers * N); groupchats[gnum].oldpeer_names = malloc(sizeof(uint8_t) * npeers * N); + groupchats[gnum].peer_name_lengths = malloc(sizeof(uint16_t) * npeers); + groupchats[gnum].oldpeer_name_lengths = malloc(sizeof(uint16_t) * npeers); - if (groupchats[gnum].peer_names == NULL || groupchats[gnum].oldpeer_names == NULL) { + memset(groupchats[gnum].peer_names, 0, sizeof(uint8_t) * npeers * N); + memset(groupchats[gnum].peer_name_lengths, 0, sizeof(uint16_t) * npeers); + + if (groupchats[gnum].peer_names == NULL || groupchats[gnum].oldpeer_names == NULL + || groupchats[gnum].peer_name_lengths == NULL || groupchats[gnum].oldpeer_name_lengths == NULL) { endwin(); fprintf(stderr, "malloc() failed. Aborting...\n"); exit(EXIT_FAILURE); } + uint16_t unknown_len = strlen(UNKNOWN_NAME); int i; for (i = 0; i < npeers; ++i) { - if (string_is_empty(tmp_peerlist[i])) { + if (string_is_empty(peerlist[i])) { memcpy(&groupchats[gnum].peer_names[i*N], UNKNOWN_NAME, sizeof(UNKNOWN_NAME)); + groupchats[gnum].peer_name_lengths[i] = unknown_len; } else { - memcpy(&groupchats[gnum].peer_names[i*N], tmp_peerlist[i], N); - groupchats[gnum].peer_names[i*N+TOXIC_MAX_NAME_LENGTH] = '\0'; + memcpy(&groupchats[gnum].peer_names[i*N], peerlist[i], N); + uint16_t n_len = lengths[i]; + + n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH-1); + + groupchats[gnum].peer_names[i*N+n_len] = '\0'; + groupchats[gnum].peer_name_lengths[i] = n_len; } } memcpy(groupchats[gnum].oldpeer_names, groupchats[gnum].peer_names, N*npeers); + memcpy(groupchats[gnum].oldpeer_name_lengths, groupchats[gnum].peer_name_lengths, + sizeof(uint16_t) * npeers); } static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnum, int peernum, @@ -252,49 +295,48 @@ static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnu int num_peers = groupchats[groupnum].num_peers; /* get old peer name before updating name list */ - uint8_t oldpeername[TOX_MAX_NAME_LENGTH] = {0}; + uint8_t oldpeername[TOX_MAX_NAME_LENGTH]; - if (change != TOX_CHAT_CHANGE_PEER_ADD) + if (change != TOX_CHAT_CHANGE_PEER_ADD) { memcpy(oldpeername, &groupchats[groupnum].oldpeer_names[peernum*TOX_MAX_NAME_LENGTH], sizeof(oldpeername)); + uint16_t old_n_len = groupchats[groupnum].oldpeer_name_lengths[peernum]; + oldpeername[old_n_len] = '\0'; + } - /* Update name lists */ + /* Update name/len lists */ uint8_t tmp_peerlist[num_peers][TOX_MAX_NAME_LENGTH]; - tox_group_get_names(m, groupnum, tmp_peerlist, num_peers); - copy_peernames(groupnum, num_peers, tmp_peerlist); + uint16_t tmp_peerlens[num_peers]; + tox_group_get_names(m, groupnum, tmp_peerlist, tmp_peerlens, num_peers); + copy_peernames(groupnum, tmp_peerlist, tmp_peerlens, num_peers); /* get current peername then sort namelist */ - uint8_t peername[TOX_MAX_NAME_LENGTH] = {0}; - memcpy(peername, &groupchats[groupnum].peer_names[peernum*TOX_MAX_NAME_LENGTH], sizeof(peername)); + uint8_t peername[TOX_MAX_NAME_LENGTH]; + + if (change != TOX_CHAT_CHANGE_PEER_DEL) { + uint16_t n_len = groupchats[groupnum].peer_name_lengths[peernum]; + memcpy(peername, &groupchats[groupnum].peer_names[peernum*TOX_MAX_NAME_LENGTH], sizeof(peername)); + peername[n_len] = '\0'; + } qsort(groupchats[groupnum].peer_names, groupchats[groupnum].num_peers, TOX_MAX_NAME_LENGTH, qsort_strcasecmp_hlpr); ChatContext *ctx = self->chatwin; - print_time(ctx->history); uint8_t *event; + uint8_t timefrmt[TIME_STR_SIZE]; + get_time_str(timefrmt); switch (change) { case TOX_CHAT_CHANGE_PEER_ADD: event = "has joined the room"; - - wattron(ctx->history, COLOR_PAIR(GREEN)); - wattron(ctx->history, A_BOLD); - wprintw(ctx->history, "* %s", peername); - wattroff(ctx->history, A_BOLD); - wprintw(ctx->history, " %s\n", event); - wattroff(ctx->history, COLOR_PAIR(GREEN)); - + line_info_add(self, timefrmt, peername, NULL, event, CONNECTION, 0, GREEN); write_to_log(event, peername, ctx->log, true); break; case TOX_CHAT_CHANGE_PEER_DEL: event = "has left the room"; - - wattron(ctx->history, A_BOLD); - wprintw(ctx->history, "* %s", oldpeername); - wattroff(ctx->history, A_BOLD); - wprintw(ctx->history, " %s\n", event); + line_info_add(self, timefrmt, oldpeername, NULL, event, CONNECTION, 0, 0); if (groupchats[self->num].side_pos > 0) --groupchats[self->num].side_pos; @@ -303,19 +345,10 @@ static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnu break; case TOX_CHAT_CHANGE_PEER_NAME: - wattron(ctx->history, COLOR_PAIR(MAGENTA)); - wattron(ctx->history, A_BOLD); - wprintw(ctx->history, "* %s", oldpeername); - wattroff(ctx->history, A_BOLD); + event = " is now known as "; + line_info_add(self, timefrmt, oldpeername, peername, event, NAME_CHANGE, 0, 0); - wprintw(ctx->history, " is now known as "); - - wattron(ctx->history, A_BOLD); - wprintw(ctx->history, "%s\n", peername); - wattroff(ctx->history, A_BOLD); - wattroff(ctx->history, COLOR_PAIR(MAGENTA)); - - uint8_t tmp_event[TOXIC_MAX_NAME_LENGTH + 32]; + uint8_t tmp_event[TOXIC_MAX_NAME_LENGTH * 2 + 32]; snprintf(tmp_event, sizeof(tmp_event), "is now known as %s", peername); write_to_log(tmp_event, oldpeername, ctx->log, true); break; @@ -330,22 +363,13 @@ static void send_group_action(ToxWindow *self, ChatContext *ctx, Tox *m, uint8_t return; } - /* uint8_t selfname[TOX_MAX_NAME_LENGTH]; - tox_get_self_name(m, selfname, TOX_MAX_NAME_LENGTH); - - print_time(ctx->history); - wattron(ctx->history, COLOR_PAIR(YELLOW)); - wprintw(ctx->history, "* %s %s\n", selfname, action); - wattroff(ctx->history, COLOR_PAIR(YELLOW)); */ - - if (tox_group_action_send(m, self->num, action, strlen(action) + 1) == -1) { - wattron(ctx->history, COLOR_PAIR(RED)); - wprintw(ctx->history, " * Failed to send action\n"); - wattroff(ctx->history, COLOR_PAIR(RED)); + if (tox_group_action_send(m, self->num, action, strlen(action)) == -1) { + uint8_t *errmsg = " * Failed to send action."; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, RED); } } -static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key) +static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) { ChatContext *ctx = self->chatwin; @@ -354,143 +378,18 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key) getmaxyx(self->window, y2, x2); int cur_len = 0; - if (key == 0x107 || key == 0x8 || key == 0x7f) { /* BACKSPACE key: Remove character behind pos */ - if (ctx->pos > 0) { - cur_len = MAX(1, wcwidth(ctx->line[ctx->pos - 1])); - del_char_buf_bck(ctx->line, &ctx->pos, &ctx->len); - - if (x == 0) - wmove(self->window, y-1, x2 - cur_len); - else - wmove(self->window, y, x - cur_len); - } else { - beep(); - } - } - - else if (key == KEY_DC) { /* DEL key: Remove character at pos */ - if (ctx->pos != ctx->len) - del_char_buf_frnt(ctx->line, &ctx->pos, &ctx->len); - else - beep(); + if (!ltr && (key == T_KEY_ESC) ) { /* ESC key: Toggle history scroll mode */ + bool scroll = ctx->hst->scroll_mode ? false : true; + line_info_toggle_scroll(self, scroll); } - else if (key == T_KEY_DISCARD) { /* CTRL-U: Delete entire line behind pos */ - if (ctx->pos > 0) { - discard_buf(ctx->line, &ctx->pos, &ctx->len); - wmove(self->window, y2 - CURS_Y_OFFSET, 0); - } else { - beep(); - } + /* If we're in scroll mode ignore rest of function */ + if (ctx->hst->scroll_mode) { + line_info_onKey(self, key); + return; } - else if (key == T_KEY_KILL) { /* CTRL-K: Delete entire line in front of pos */ - if (ctx->pos != ctx->len) - kill_buf(ctx->line, &ctx->pos, &ctx->len); - else - beep(); - } - - else if (key == KEY_HOME || key == T_KEY_C_A) { /* HOME/C-a key: Move cursor to start of line */ - if (ctx->pos > 0) { - ctx->pos = 0; - wmove(self->window, y2 - CURS_Y_OFFSET, 0); - } - } - - else if (key == KEY_END || key == T_KEY_C_E) { /* END/C-e key: move cursor to end of line */ - if (ctx->pos != ctx->len) { - ctx->pos = ctx->len; - mv_curs_end(self->window, MAX(0, wcswidth(ctx->line, (CHATBOX_HEIGHT-1)*x2)), y2, x2); - } - } - - else if (key == KEY_LEFT) { - if (ctx->pos > 0) { - --ctx->pos; - cur_len = MAX(1, wcwidth(ctx->line[ctx->pos])); - - if (x == 0) - wmove(self->window, y-1, x2 - cur_len); - else - wmove(self->window, y, x - cur_len); - } else { - beep(); - } - } - - else if (key == KEY_RIGHT) { - if (ctx->pos < ctx->len) { - cur_len = MAX(1, wcwidth(ctx->line[ctx->pos])); - ++ctx->pos; - - if (x == x2-1) - wmove(self->window, y+1, 0); - else - wmove(self->window, y, x + cur_len); - } else { - beep(); - } - } - - else if (key == KEY_UP) { /* fetches previous item in history */ - fetch_hist_item(ctx->line, &ctx->pos, &ctx->len, ctx->ln_history, ctx->hst_tot, - &ctx->hst_pos, LN_HIST_MV_UP); - mv_curs_end(self->window, ctx->len, y2, x2); - } - - else if (key == KEY_DOWN) { /* fetches next item in history */ - fetch_hist_item(ctx->line, &ctx->pos, &ctx->len, ctx->ln_history, ctx->hst_tot, - &ctx->hst_pos, LN_HIST_MV_DWN); - mv_curs_end(self->window, ctx->len, y2, x2); - } - - else if (key == '\t') { /* TAB key: completes peer name */ - if (ctx->len > 0) { - int diff; - - if ((ctx->line[0] != '/') || (ctx->line[1] == 'm' && ctx->line[2] == 'e')) - diff = complete_line(ctx->line, &ctx->pos, &ctx->len, groupchats[self->num].peer_names, - groupchats[self->num].num_peers, TOX_MAX_NAME_LENGTH); - else - diff = complete_line(ctx->line, &ctx->pos, &ctx->len, glob_cmd_list, AC_NUM_GLOB_COMMANDS, - MAX_CMDNAME_SIZE); - - if (diff != -1) { - if (x + diff > x2 - 1) { - int ofst = (x + diff - 1) - (x2 - 1); - wmove(self->window, y+1, ofst); - } else { - wmove(self->window, y, x+diff); - } - } else { - beep(); - } - } else { - beep(); - } - } - - /* Scroll peerlist up and down one position if list overflows window */ - else if (key == KEY_NPAGE) { - int L = y2 - CHATBOX_HEIGHT - SDBAR_OFST; - - if (groupchats[self->num].side_pos < groupchats[self->num].num_peers - L) - ++groupchats[self->num].side_pos; - } - - else if (key == KEY_PPAGE) { - if (groupchats[self->num].side_pos > 0) - --groupchats[self->num].side_pos; - } - - else -#if HAVE_WIDECHAR - if (iswprint(key)) -#else - if (isprint(key)) -#endif - { /* prevents buffer overflows and strange behaviour when cursor goes past the window */ + if (ltr) { if ( (ctx->len < MAX_STR_SIZE-1) && (ctx->len < (x2 * (CHATBOX_HEIGHT - 1)-1)) ) { add_char_to_buf(ctx->line, &ctx->pos, &ctx->len, key); @@ -499,67 +398,206 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key) else wmove(self->window, y, x + MAX(1, wcwidth(key))); } - } - /* RETURN key: Execute command or print line */ - else if (key == '\n') { - uint8_t line[MAX_STR_SIZE]; + } else { /* if (!ltr) */ - if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) - memset(&line, 0, sizeof(line)); + if (key == 0x107 || key == 0x8 || key == 0x7f) { /* BACKSPACE key: Remove character behind pos */ + if (ctx->pos > 0) { + cur_len = MAX(1, wcwidth(ctx->line[ctx->pos - 1])); + del_char_buf_bck(ctx->line, &ctx->pos, &ctx->len); - wclear(ctx->linewin); - wmove(self->window, y2 - CURS_Y_OFFSET, 0); - wclrtobot(self->window); - - if (!string_is_empty(line)) - add_line_to_hist(ctx->line, ctx->len, ctx->ln_history, &ctx->hst_tot, &ctx->hst_pos); - - if (line[0] == '/') { - if (strcmp(line, "/close") == 0) { - close_groupchat(self, m, self->num); - return; - } else if (strcmp(line, "/help") == 0) { - if (strcmp(line, "help global") == 0) - execute(ctx->history, self, m, "/help", GLOBAL_COMMAND_MODE); + if (x == 0) + wmove(self->window, y-1, x2 - cur_len); else - print_groupchat_help(ctx); - - } else if (strncmp(line, "/me ", strlen("/me ")) == 0) { - send_group_action(self, ctx, m, line + strlen("/me ")); + wmove(self->window, y, x - cur_len); } else { - execute(ctx->history, self, m, line, GROUPCHAT_COMMAND_MODE); - } - } else if (!string_is_empty(line)) { - if (tox_group_message_send(m, self->num, line, strlen(line) + 1) == -1) { - wattron(ctx->history, COLOR_PAIR(RED)); - wprintw(ctx->history, " * Failed to send message.\n"); - wattroff(ctx->history, COLOR_PAIR(RED)); + beep(); } } - reset_buf(ctx->line, &ctx->pos, &ctx->len); + else if (key == KEY_DC) { /* DEL key: Remove character at pos */ + if (ctx->pos != ctx->len) + del_char_buf_frnt(ctx->line, &ctx->pos, &ctx->len); + else + beep(); + } + + else if (key == T_KEY_DISCARD) { /* CTRL-U: Delete entire line behind pos */ + if (ctx->pos > 0) { + discard_buf(ctx->line, &ctx->pos, &ctx->len); + wmove(self->window, y2 - CURS_Y_OFFSET, 0); + } else { + beep(); + } + } + + else if (key == T_KEY_KILL) { /* CTRL-K: Delete entire line in front of pos */ + if (ctx->pos != ctx->len) + kill_buf(ctx->line, &ctx->pos, &ctx->len); + else + beep(); + } + + else if (key == KEY_HOME || key == T_KEY_C_A) { /* HOME/C-a key: Move cursor to start of line */ + if (ctx->pos > 0) { + ctx->pos = 0; + wmove(self->window, y2 - CURS_Y_OFFSET, 0); + } + } + + else if (key == KEY_END || key == T_KEY_C_E) { /* END/C-e key: move cursor to end of line */ + if (ctx->pos != ctx->len) { + ctx->pos = ctx->len; + mv_curs_end(self->window, MAX(0, wcswidth(ctx->line, (CHATBOX_HEIGHT-1)*x2)), y2, x2); + } + } + + else if (key == KEY_LEFT) { + if (ctx->pos > 0) { + --ctx->pos; + cur_len = MAX(1, wcwidth(ctx->line[ctx->pos])); + + if (x == 0) + wmove(self->window, y-1, x2 - cur_len); + else + wmove(self->window, y, x - cur_len); + } else { + beep(); + } + } + + else if (key == KEY_RIGHT) { + if (ctx->pos < ctx->len) { + cur_len = MAX(1, wcwidth(ctx->line[ctx->pos])); + ++ctx->pos; + + if (x == x2-1) + wmove(self->window, y+1, 0); + else + wmove(self->window, y, x + cur_len); + } else { + beep(); + } + } + + else if (key == KEY_UP) { /* fetches previous item in history */ + fetch_hist_item(ctx->line, &ctx->pos, &ctx->len, ctx->ln_history, ctx->hst_tot, + &ctx->hst_pos, MOVE_UP); + mv_curs_end(self->window, ctx->len, y2, x2); + } + + else if (key == KEY_DOWN) { /* fetches next item in history */ + fetch_hist_item(ctx->line, &ctx->pos, &ctx->len, ctx->ln_history, ctx->hst_tot, + &ctx->hst_pos, MOVE_DOWN); + mv_curs_end(self->window, ctx->len, y2, x2); + } + + else if (key == '\t') { /* TAB key: completes peer name */ + if (ctx->len > 0) { + int diff; + + if ((ctx->line[0] != '/') || (ctx->line[1] == 'm' && ctx->line[2] == 'e')) + diff = complete_line(ctx->line, &ctx->pos, &ctx->len, groupchats[self->num].peer_names, + groupchats[self->num].num_peers, TOX_MAX_NAME_LENGTH); + else + diff = complete_line(ctx->line, &ctx->pos, &ctx->len, glob_cmd_list, AC_NUM_GLOB_COMMANDS, + MAX_CMDNAME_SIZE); + + if (diff != -1) { + if (x + diff > x2 - 1) { + int ofst = (x + diff - 1) - (x2 - 1); + wmove(self->window, y+1, ofst); + } else { + wmove(self->window, y, x+diff); + } + } else { + beep(); + } + } else { + beep(); + } + } + + /* Scroll peerlist up and down one position if list overflows window */ + else if (key == KEY_NPAGE) { + int L = y2 - CHATBOX_HEIGHT - SDBAR_OFST; + + if (groupchats[self->num].side_pos < groupchats[self->num].num_peers - L) + ++groupchats[self->num].side_pos; + } + + else if (key == KEY_PPAGE) { + if (groupchats[self->num].side_pos > 0) + --groupchats[self->num].side_pos; + } + + /* RETURN key: Execute command or print line */ + else if (key == '\n') { + uint8_t line[MAX_STR_SIZE]; + + if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) + memset(&line, 0, sizeof(line)); + + wclear(ctx->linewin); + wmove(self->window, y2 - CURS_Y_OFFSET, 0); + + + if (!string_is_empty(line)) + add_line_to_hist(ctx->line, ctx->len, ctx->ln_history, &ctx->hst_tot, &ctx->hst_pos); + + if (line[0] == '/') { + if (strcmp(line, "/close") == 0) { + close_groupchat(self, m, self->num); + return; + } else if (strcmp(line, "/help") == 0) { + if (strcmp(line, "help global") == 0) + execute(ctx->history, self, m, "/help", GLOBAL_COMMAND_MODE); + else + print_groupchat_help(self); + + } else if (strncmp(line, "/me ", strlen("/me ")) == 0) { + send_group_action(self, ctx, m, line + strlen("/me ")); + } else { + execute(ctx->history, self, m, line, GROUPCHAT_COMMAND_MODE); + } + } else if (!string_is_empty(line)) { + if (tox_group_message_send(m, self->num, line, strlen(line)) == -1) { + uint8_t *errmsg = " * Failed to send message."; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, RED); + } + } + + reset_buf(ctx->line, &ctx->pos, &ctx->len); + } } } static void groupchat_onDraw(ToxWindow *self, Tox *m) { - curs_set(1); + int x2, y2; getmaxyx(self->window, y2, x2); ChatContext *ctx = self->chatwin; + line_info_print(self); wclear(ctx->linewin); - if (ctx->len > 0) { - uint8_t line[MAX_STR_SIZE]; + if (ctx->hst->scroll_mode) { + line_info_onDraw(self); + } else { + scrollok(ctx->history, 1); + curs_set(1); - if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) { - reset_buf(ctx->line, &ctx->pos, &ctx->len); - wmove(self->window, y2 - CURS_Y_OFFSET, 0); - } else { - mvwprintw(ctx->linewin, 1, 0, "%s", line); + if (ctx->len > 0) { + uint8_t line[MAX_STR_SIZE]; + + if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) { + reset_buf(ctx->line, &ctx->pos, &ctx->len); + wmove(self->window, y2 - CURS_Y_OFFSET, 0); + } else { + mvwprintw(ctx->linewin, 1, 0, "%s", line); + } } } @@ -601,22 +639,26 @@ static void groupchat_onInit(ToxWindow *self, Tox *m) getmaxyx(self->window, y, x); ChatContext *ctx = self->chatwin; + ctx->history = subwin(self->window, y-CHATBOX_HEIGHT+1, x-SIDEBAR_WIDTH-1, 0, 0); - scrollok(ctx->history, 1); ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x, y-CHATBOX_HEIGHT, 0); ctx->sidebar = subwin(self->window, y-CHATBOX_HEIGHT+1, SIDEBAR_WIDTH, 0, x-SIDEBAR_WIDTH); + ctx->hst = malloc(sizeof(struct history)); ctx->log = malloc(sizeof(struct chatlog)); - if (ctx->log == NULL) { + if (ctx->log == NULL || ctx->hst == NULL) { endwin(); fprintf(stderr, "malloc() failed. Aborting...\n"); exit(EXIT_FAILURE); } + memset(ctx->hst, 0, sizeof(struct history)); memset(ctx->log, 0, sizeof(struct chatlog)); - print_groupchat_help(ctx); + line_info_init(ctx->hst); + + print_groupchat_help(self); execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE); wmove(self->window, y-CURS_Y_OFFSET, 0); diff --git a/src/groupchat.h b/src/groupchat.h index 5b079d5..afaa311 100644 --- a/src/groupchat.h +++ b/src/groupchat.h @@ -28,8 +28,10 @@ typedef struct { bool active; int num_peers; int side_pos; /* current position of the sidebar - used for scrolling up and down */ - uint8_t *peer_names; - uint8_t *oldpeer_names; + uint8_t *peer_names; + uint8_t *oldpeer_names; + uint16_t *peer_name_lengths; + uint16_t *oldpeer_name_lengths; } GroupChat; void kill_groupchat_window(ToxWindow *self); diff --git a/src/line_info.c b/src/line_info.c new file mode 100644 index 0000000..a95501c --- /dev/null +++ b/src/line_info.c @@ -0,0 +1,434 @@ +/* line_info.c + * + * + * Copyright (C) 2014 Toxic All Rights Reserved. + * + * This file is part of Toxic. + * + * Toxic is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Toxic is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Toxic. If not, see . + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "toxic_windows.h" +#include "line_info.h" +#include "groupchat.h" + +void line_info_init(struct history *hst) +{ + hst->line_root = malloc(sizeof(struct line_info)); + + if (hst->line_root == NULL) { + endwin(); + fprintf(stderr, "malloc() failed. Aborting...\n"); + exit(EXIT_FAILURE); + } + + memset(hst->line_root, 0, sizeof(struct line_info)); + hst->line_start = hst->line_root; + hst->line_end = hst->line_start; +} + +/* resets line_start when scroll mode is disabled */ +static void line_info_reset_start(struct history *hst) +{ + struct line_info *line = hst->line_end; + uint32_t start_id = hst->start_id; + + while (line) { + if (line->id == start_id) { + hst->line_start = line; + break; + } + + line = line->prev; + } +} + +void line_info_toggle_scroll(ToxWindow *self, bool scroll) +{ + WINDOW *win = self->chatwin->history; + struct history *hst = self->chatwin->hst; + + if (scroll) { + hst->scroll_mode = true; + scrollok(win, 0); + curs_set(0); + } else { + hst->scroll_mode = false; + scrollok(win, 1); + curs_set(1); + line_info_reset_start(hst); + } +} + +void line_info_cleanup(struct history *hst) +{ + struct line_info *tmp1 = hst->line_root; + + while (tmp1) { + struct line_info *tmp2 = tmp1->next; + free(tmp1); + tmp1 = tmp2; + } +} + +/* moves root forward and frees previous root */ +static void line_info_root_fwd(struct history *hst) +{ + struct line_info *tmp = hst->line_root->next; + tmp->prev = NULL; + + if (hst->line_start->prev == NULL) { /* if line_start is root move it forward as well */ + hst->line_start = hst->line_start->next; + hst->line_start->prev = NULL; + ++hst->start_id; + } + + free(hst->line_root); + hst->line_root = tmp; +} + +void line_info_add(ToxWindow *self, uint8_t *tmstmp, uint8_t *name1, uint8_t *name2, uint8_t *msg, + uint8_t type, uint8_t bold, uint8_t colour) +{ + struct history *hst = self->chatwin->hst; + struct line_info *new_line = malloc(sizeof(struct line_info)); + + if (new_line == NULL) { + endwin(); + fprintf(stderr, "malloc() failed. Aborting...\n"); + exit(EXIT_FAILURE); + } + + memset(new_line, 0, sizeof(struct line_info)); + + int len = 1; /* there will always be a newline */ + + /* for type-specific formatting in print function */ + switch (type) { + case ACTION: + len += 3; + break; + default: + len += 2; + break; + } + + if (msg) { + memcpy(new_line->msg, msg, sizeof(new_line->msg)); + len += strlen(msg); + } if (tmstmp) { + memcpy(new_line->timestamp, tmstmp, sizeof(new_line->timestamp)); + len += strlen(tmstmp); + } if (name1) { + memcpy(new_line->name1, name1, sizeof(new_line->name1)); + len += strlen(name1); + } if (name2) { + memcpy(new_line->name2, name2, sizeof(new_line->name2)); + len += strlen(name2); + } + + new_line->len = len; + new_line->type = type; + new_line->bold = bold; + new_line->colour = colour; + new_line->id = hst->line_end->id + 1; + + new_line->prev = hst->line_end; + hst->line_end->next = new_line; + hst->line_end = new_line; + + if (++hst->line_items > MAX_HISTORY) { + --hst->line_items; + line_info_root_fwd(hst); + } + + int newlines = 0; + int i; + + for (i = 0; msg[i]; ++i) { + if (msg[i] == '\n') + ++newlines; + } + + int y, y2, x, x2; + getmaxyx(self->window, y2, x2); + getyx(self->chatwin->history, y, x); + + if (x2 <= SIDEBAR_WIDTH) + return; + + int offst = self->is_groupchat ? SIDEBAR_WIDTH : 0; /* offset width of groupchat sidebar */ + int lines = (1 + newlines + (len / (x2 - offst))); + hst->queue_lns += lines; + ++hst->queue; + + int max_y = self->is_prompt ? y2 : y2 - CHATBOX_HEIGHT; + + /* move line_start forward proportionate to the number of new lines */ + if (y + hst->queue_lns - hst->queue >= max_y) { + while (lines > 0) { + ++hst->start_id; + lines -= (1 + hst->line_start->len / (x2 - offst)); + + if (!hst->scroll_mode && hst->line_start->next) + hst->line_start = hst->line_start->next; + } + } +} + +void line_info_print(ToxWindow *self) +{ + ChatContext *ctx = self->chatwin; + WINDOW *win = ctx->history; + + ctx->hst->queue = 0; + ctx->hst->queue_lns = 0; + + wclear(win); + int y2, x2; + getmaxyx(self->window, y2, x2); + + if (self->is_prompt) + y2 = MAX_HISTORY; /* temporary fix to make prompt scroll */ + + if (x2 <= SIDEBAR_WIDTH) + return; + + if (self->is_groupchat) + wmove(win, 0, 0); + else + wmove(win, 2, 0); + + struct line_info *line = ctx->hst->line_start->next; + int offst = self->is_groupchat ? SIDEBAR_WIDTH : 0; + int numlines = 0; + + while(line && numlines++ <= y2) { + uint8_t type = line->type; + + switch (type) { + case OUT_MSG: + case IN_MSG: + wattron(win, COLOR_PAIR(BLUE)); + wprintw(win, "%s", line->timestamp); + wattroff(win, COLOR_PAIR(BLUE)); + + int nameclr = GREEN; + + if (line->colour) + nameclr = line->colour; + else if (type == IN_MSG) + nameclr = CYAN; + + wattron(win, COLOR_PAIR(nameclr)); + wprintw(win, "%s: ", line->name1); + wattroff(win, COLOR_PAIR(nameclr)); + + if (line->msg[0] == '>') + wattron(win, COLOR_PAIR(GREEN)); + + wprintw(win, "%s\n", line->msg); + + if (line->msg[0] == '>') + wattroff(win, COLOR_PAIR(GREEN)); + + break; + + case ACTION: + wattron(win, COLOR_PAIR(BLUE)); + wprintw(win, "%s", line->timestamp); + wattroff(win, COLOR_PAIR(BLUE)); + + wattron(win, COLOR_PAIR(YELLOW)); + wprintw(win, "* %s %s\n", line->name1, line->msg); + wattroff(win, COLOR_PAIR(YELLOW)); + + break; + + case SYS_MSG: + if (line->timestamp[0]) { + wattron(win, COLOR_PAIR(BLUE)); + wprintw(win, "%s", line->timestamp); + wattroff(win, COLOR_PAIR(BLUE)); + } + + if (line->bold) + wattron(win, A_BOLD); + if (line->colour) + wattron(win, COLOR_PAIR(line->colour)); + + wprintw(win, "%s\n", line->msg); + + if (line->bold) + wattroff(win, A_BOLD); + if (line->colour) + wattroff(win, COLOR_PAIR(line->colour)); + + break; + + case PROMPT: + wattron(win, COLOR_PAIR(GREEN)); + wprintw(win, "$ "); + wattroff(win, COLOR_PAIR(GREEN)); + + if (line->msg[0]) + wprintw(win, "%s", line->msg); + + wprintw(win, "\n"); + break; + + case CONNECTION: + wattron(win, COLOR_PAIR(BLUE)); + wprintw(win, "%s", line->timestamp); + wattroff(win, COLOR_PAIR(BLUE)); + + wattron(win, COLOR_PAIR(line->colour)); + wattron(win, A_BOLD); + wprintw(win, "* %s ", line->name1); + wattroff(win, A_BOLD); + wprintw(win, "%s\n", line->msg); + wattroff(win, COLOR_PAIR(line->colour)); + + break; + + case NAME_CHANGE: + wattron(win, COLOR_PAIR(BLUE)); + wprintw(win, "%s", line->timestamp); + wattroff(win, COLOR_PAIR(BLUE)); + + wattron(win, COLOR_PAIR(MAGENTA)); + wattron(win, A_BOLD); + wprintw(win, "* %s", line->name1); + wattroff(win, A_BOLD); + + wprintw(win, "%s", line->msg); + + wattron(win, A_BOLD); + wprintw(win, "%s\n", line->name2); + wattroff(win, A_BOLD); + wattroff(win, COLOR_PAIR(MAGENTA)); + + break; + } + + line = line->next; + } +} + +void line_info_set(ToxWindow *self, uint32_t id, uint8_t *msg) +{ + struct line_info *line = self->chatwin->hst->line_end; + + while (line) { + if (line->id == id) { + snprintf(line->msg, sizeof(line->msg), "%s", msg); + return; + } + + line = line->prev; + } +} + +static void line_info_goto_root(struct history *hst) +{ + hst->line_start = hst->line_root; +} + +static void line_info_scroll_up(struct history *hst) +{ + if (hst->line_start->prev) + hst->line_start = hst->line_start->prev; + else beep(); +} + +static void line_info_scroll_down(struct history *hst) +{ + if (hst->line_start->next) + hst->line_start = hst->line_start->next; + else beep(); +} + +static void line_info_page_up(ToxWindow *self, struct history *hst) +{ + int x2, y2; + getmaxyx(self->window, y2, x2); + int jump_dist = y2 / 2; + int i; + + for (i = 0; i < jump_dist && hst->line_start->prev; ++i) + hst->line_start = hst->line_start->prev; +} + +static void line_info_page_down(ToxWindow *self, struct history *hst) +{ + int x2, y2; + getmaxyx(self->window, y2, x2); + int jump_dist = y2 / 2; + int i; + + for (i = 0; i < jump_dist && hst->line_start->next; ++i) + hst->line_start = hst->line_start->next; +} + +void line_info_onKey(ToxWindow *self, wint_t key) +{ + struct history *hst = self->chatwin->hst; + + switch (key) { + case KEY_PPAGE: + line_info_page_up(self, hst); + break; + case KEY_NPAGE: + line_info_page_down(self, hst); + break; + case KEY_UP: + line_info_scroll_up(hst); + break; + case KEY_DOWN: + line_info_scroll_down(hst); + break; + case KEY_HOME: + line_info_goto_root(hst); + break; + case KEY_END: + line_info_reset_start(hst); + break; + } +} + +void line_info_onDraw(ToxWindow *self) +{ + ChatContext *ctx = self->chatwin; + + wattron(ctx->linewin, A_BOLD | COLOR_PAIR(BLUE)); + mvwprintw(ctx->linewin, 1, 0, "Scroll mode:\n"); + wattroff(ctx->linewin, A_BOLD | COLOR_PAIR(BLUE)); + mvwprintw(ctx->linewin, 1, 13, "Use up/down arrows, page up/page down, and home/end to navigate.\n" + " ESC to exit.\n"); +} + +void line_info_clear(struct history *hst) +{ + hst->line_start = hst->line_end; + hst->start_id = hst->line_start->id; +} diff --git a/src/line_info.h b/src/line_info.h new file mode 100644 index 0000000..7357476 --- /dev/null +++ b/src/line_info.h @@ -0,0 +1,85 @@ +/* line_info.h + * + * + * Copyright (C) 2014 Toxic All Rights Reserved. + * + * This file is part of Toxic. + * + * Toxic is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Toxic is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Toxic. If not, see . + * + */ + +#define MAX_HISTORY 700 + +enum { + SYS_MSG, + IN_MSG, + OUT_MSG, + PROMPT, + ACTION, + CONNECTION, + NAME_CHANGE, +} LINE_TYPE; + +struct line_info { + uint8_t timestamp[TIME_STR_SIZE]; + uint8_t name1[TOXIC_MAX_NAME_LENGTH]; + uint8_t name2[TOXIC_MAX_NAME_LENGTH]; + uint8_t msg[MAX_STR_SIZE]; + uint8_t type; + uint8_t bold; + uint8_t colour; + uint32_t id; + uint16_t len; /* combined len of all strings */ + + struct line_info *prev; + struct line_info *next; +}; + +/* Linked list containing chat history lines */ +struct history { + struct line_info *line_root; + struct line_info *line_start; /* the first line we want to start printing at */ + struct line_info *line_end; + uint32_t start_id; /* keeps track of where line_start should be when at bottom of history */ + uint32_t line_items; + bool scroll_mode; + + /* keeps track of lines added between window refreshes */ + uint32_t queue; + uint32_t queue_lns; +}; + +/* adds a line to history (also moves line_start and/or line_root forward if necessary) */ +void line_info_add(ToxWindow *self, uint8_t *tmstmp, uint8_t *name1, uint8_t *name2, uint8_t *msg, + uint8_t type, uint8_t bold, uint8_t colour); + +/* Prints a section of history starting at line_start */ +void line_info_print(ToxWindow *self); + +/* frees all history lines */ +void line_info_cleanup(struct history *hst); + +/* Toggles scroll mode for current window */ +void line_info_toggle_scroll(ToxWindow *self, bool scroll); + +/* clears the screen (does not delete anything) */ +void line_info_clear(struct history *hst); + +/* puts msg in specified line_info msg buffer */ +void line_info_set(ToxWindow *self, uint32_t id, uint8_t *msg); + +void line_info_init(struct history *hst); +void line_info_onKey(ToxWindow *self, wint_t key); +void line_info_onDraw(ToxWindow *self); diff --git a/src/log.c b/src/log.c index 6d93aa6..4a42a03 100644 --- a/src/log.c +++ b/src/log.c @@ -31,6 +31,7 @@ #include "configdir.h" #include "toxic_windows.h" #include "misc_tools.h" +#include "log.h" /* Creates/fetches log file by appending to the config dir the name and a pseudo-unique identity */ void init_logging_session(uint8_t *name, uint8_t *key, struct chatlog *log) @@ -48,17 +49,16 @@ void init_logging_session(uint8_t *name, uint8_t *key, struct chatlog *log) path_len += (KEY_IDENT_DIGITS * 2 + 5); sprintf(&ident[0], "%02X", key[0] & 0xff); - sprintf(&ident[2], "%02X", key[2] & 0xff); + sprintf(&ident[2], "%02X", key[1] & 0xff); ident[KEY_IDENT_DIGITS*2+1] = '\0'; } else { - uint8_t s[MAX_STR_SIZE]; - strftime(s, MAX_STR_SIZE, "%Y-%m-%d[%H:%M:%S]", get_time()); - snprintf(ident, sizeof(ident), "%s", s); + strftime(ident, sizeof(ident), "%Y-%m-%d[%H:%M:%S]", get_time()); path_len += strlen(ident) + 1; } if (path_len > MAX_STR_SIZE) { log->log_on = false; + free(user_config_dir); return; } @@ -67,6 +67,8 @@ void init_logging_session(uint8_t *name, uint8_t *key, struct chatlog *log) snprintf(log_path, MAX_STR_SIZE, "%s%s%s-%s.log", user_config_dir, CONFIGDIR, name, ident); + free(user_config_dir); + log->file = fopen(log_path, "a"); if (log->file == NULL) { @@ -75,10 +77,9 @@ void init_logging_session(uint8_t *name, uint8_t *key, struct chatlog *log) } fprintf(log->file, "\n*** NEW SESSION ***\n\n"); - free(user_config_dir); } -void write_to_log(uint8_t *msg, uint8_t *name, struct chatlog *log, bool event) +void write_to_log(const uint8_t *msg, uint8_t *name, struct chatlog *log, bool event) { if (!log->log_on) return; @@ -99,7 +100,7 @@ void write_to_log(uint8_t *msg, uint8_t *name, struct chatlog *log, bool event) strftime(s, MAX_STR_SIZE, "%Y/%m/%d [%H:%M:%S]", get_time()); fprintf(log->file,"%s %s %s\n", s, name_frmt, msg); - uint64_t curtime = (uint64_t) time(NULL); + uint64_t curtime = get_unix_time(); if (timed_out(log->lastwrite, curtime, LOG_FLUSH_LIMIT)) { fflush(log->file); diff --git a/src/log.h b/src/log.h index 71c9748..9473eeb 100644 --- a/src/log.h +++ b/src/log.h @@ -20,11 +20,20 @@ * */ +#define LOG_FLUSH_LIMIT 2 /* limits calls to fflush(logfile) to a max of one per LOG_FLUSH_LIMIT seconds */ + +struct chatlog { + FILE *file; + uint64_t lastwrite; + int pos; + bool log_on; /* specific to current chat window */ +}; + /* Creates/fetches log file by appending to the config dir the name and a pseudo-unique identity */ void init_logging_session(uint8_t *name, uint8_t *key, struct chatlog *log); /* formats/writes line to log file */ -void write_to_log(uint8_t *msg, uint8_t *name, struct chatlog *log, bool event); +void write_to_log(const uint8_t *msg, uint8_t *name, struct chatlog *log, bool event); /* enables logging for specified log and creates/fetches file if necessary */ void log_enable(uint8_t *name, uint8_t *key, struct chatlog *log); diff --git a/src/main.c b/src/main.c index 02175ea..9cc4796 100644 --- a/src/main.c +++ b/src/main.c @@ -60,6 +60,7 @@ #include "prompt.h" #include "misc_tools.h" #include "file_senders.h" +#include "line_info.h" #ifdef _SUPPORT_AUDIO #include "audio_call.h" @@ -83,7 +84,6 @@ struct _Winthread Winthread; void on_window_resize(int sig) { - endwin(); refresh(); clear(); } @@ -157,20 +157,20 @@ static Tox *init_tox(int ipv4) tox_callback_file_data(m, on_file_data, NULL); #ifdef __linux__ - tox_set_name(m, (uint8_t *) "Cool guy", sizeof("Cool guy")); + tox_set_name(m, (uint8_t *) "Cool guy", strlen("Cool guy")); #elif defined(_WIN32) - tox_set_name(m, (uint8_t *) "I should install GNU/Linux", sizeof("I should install GNU/Linux")); + tox_set_name(m, (uint8_t *) "I should install GNU/Linux", strlen("I should install GNU/Linux")); #elif defined(__APPLE__) - tox_set_name(m, (uint8_t *) "Hipster", sizeof("Hipster")); /* This used to users of other Unixes are hipsters */ + tox_set_name(m, (uint8_t *) "Hipster", strlen("Hipster")); /* This used to users of other Unixes are hipsters */ #else - tox_set_name(m, (uint8_t *) "Registered Minix user #4", sizeof("Registered Minix user #4")); + tox_set_name(m, (uint8_t *) "Registered Minix user #4", strlen("Registered Minix user #4")); #endif return m; } -#define MINLINE 50 /* IP: 7 + port: 5 + key: 38 + spaces: 2 = 70. ! (& e.g. tox.im = 6) */ -#define MAXLINE 256 /* Approx max number of chars in a sever line (name + port + key) */ +#define MINLINE 50 /* IP: 7 + port: 5 + key: 38 + spaces: 2 = 70. ! (& e.g. tox.im = 6) */ +#define MAXLINE 256 /* Approx max number of chars in a sever line (name + port + key) */ #define MAXNODES 50 #define NODELEN (MAXLINE - TOX_CLIENT_ID_SIZE - 7) @@ -272,6 +272,8 @@ int init_connection(Tox *m) static void do_connection(Tox *m, ToxWindow *prompt) { + uint8_t msg[MAX_STR_SIZE] = {0}; + static int conn_try = 0; static int conn_err = 0; static bool dht_on = false; @@ -281,26 +283,26 @@ static void do_connection(Tox *m, ToxWindow *prompt) if (!dht_on && !is_connected && !(conn_try++ % 100)) { if (!conn_err) { if ((conn_err = init_connection(m))) { - prep_prompt_win(); - wprintw(prompt->window, "\nAuto-connect failed with error code %d\n", conn_err); + snprintf(msg, sizeof(msg), "\nAuto-connect failed with error code %d", conn_err); } } } else if (!dht_on && is_connected) { dht_on = true; prompt_update_connectionstatus(prompt, dht_on); - prep_prompt_win(); - wprintw(prompt->window, "DHT connected.\n"); + snprintf(msg, sizeof(msg), "DHT connected."); } else if (dht_on && !is_connected) { dht_on = false; prompt_update_connectionstatus(prompt, dht_on); - prep_prompt_win(); - wprintw(prompt->window, "\nDHT disconnected. Attempting to reconnect.\n"); + snprintf(msg, sizeof(msg), "\nDHT disconnected. Attempting to reconnect."); } + + if (msg[0]) + line_info_add(prompt, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); } static void load_friendlist(Tox *m) { - int i; + int32_t i; uint32_t numfriends = tox_count_friendlist(m); for (i = 0; i < numfriends; ++i) @@ -405,11 +407,13 @@ void exit_toxic(Tox *m) store_data(m, DATA_FILE); close_all_file_senders(); kill_all_windows(); - log_disable(prompt->promptbuf->log); + log_disable(prompt->chatwin->log); + line_info_cleanup(prompt->chatwin->hst); free(DATA_FILE); free(prompt->stb); - free(prompt->promptbuf->log); - free(prompt->promptbuf); + free(prompt->chatwin->log); + free(prompt->chatwin->hst); + free(prompt->chatwin); tox_kill(m); #ifdef _SUPPORT_AUDIO terminate_audio(); @@ -501,8 +505,7 @@ int main(int argc, char *argv[]) prompt = init_windows(m); /* create new thread for ncurses stuff */ - if (pthread_mutex_init(&Winthread.lock, NULL) != 0) - { + if (pthread_mutex_init(&Winthread.lock, NULL) != 0) { endwin(); fprintf(stderr, "Mutex init failed. Aborting...\n"); exit(EXIT_FAILURE); @@ -514,43 +517,39 @@ int main(int argc, char *argv[]) exit(EXIT_FAILURE); } -#ifdef _SUPPORT_AUDIO - - attron(COLOR_PAIR(RED) | A_BOLD); - wprintw(prompt->window, "Starting audio...\n"); - attroff(COLOR_PAIR(RED) | A_BOLD); - + uint8_t *msg; + +#ifdef _SUPPORT_AUDIO + av = init_audio(prompt, m); - + if ( errors() == NoError ) - wprintw(prompt->window, "Audio started with no problems.\n"); + msg = "Audio started with no problems."; else /* Get error code and stuff */ - wprintw(prompt->window, "Error starting audio!\n"); - - + msg = "Error starting audio!"; + + line_info_add(prompt, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); + #endif /* _SUPPORT_AUDIO */ - + if (f_loadfromfile) load_data(m, DATA_FILE); if (f_flag == -1) { - attron(COLOR_PAIR(RED) | A_BOLD); - wprintw(prompt->window, "You passed '-f' without giving an argument.\n" - "defaulting to 'data' for a keyfile...\n"); - attroff(COLOR_PAIR(RED) | A_BOLD); + msg = "You passed '-f' without giving an argument. Defaulting to 'data' for a keyfile..."; + line_info_add(prompt, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); } if (config_err) { - attron(COLOR_PAIR(RED) | A_BOLD); - wprintw(prompt->window, "Unable to determine configuration directory.\n" - "defaulting to 'data' for a keyfile...\n"); - attroff(COLOR_PAIR(RED) | A_BOLD); + msg = "Unable to determine configuration directory. Defaulting to 'data' for a keyfile..."; + line_info_add(prompt, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); } - sort_friendlist_index(m); + sort_friendlist_index(); prompt_init_statusbar(prompt, m); while (true) { + update_unix_time(); do_toxic(m, prompt); usleep(10000); } diff --git a/src/misc_tools.c b/src/misc_tools.c index 3b070fc..0363f1d 100644 --- a/src/misc_tools.c +++ b/src/misc_tools.c @@ -30,9 +30,36 @@ #include #include "toxic_windows.h" +#include "misc_tools.h" extern ToxWindow *prompt; +static uint64_t current_unix_time; + +void update_unix_time(void) +{ + current_unix_time = (uint64_t) time(NULL); +} + +uint64_t get_unix_time(void) +{ + return current_unix_time; +} + +/* Get the current local time */ +struct tm *get_time(void) +{ + struct tm *timeinfo; + uint64_t t = get_unix_time(); + timeinfo = localtime(&t); + return timeinfo; +} + +void get_time_str(uint8_t *buf) +{ + strftime(buf, TIME_STR_SIZE, "[%H:%M:%S] ", get_time()); +} + /* XXX: FIX */ unsigned char *hex_string_to_bin(char hex_string[]) { @@ -54,27 +81,6 @@ unsigned char *hex_string_to_bin(char hex_string[]) return val; } -/* Get the current local time */ -struct tm *get_time(void) -{ - struct tm *timeinfo; - time_t now; - time(&now); - timeinfo = localtime(&now); - return timeinfo; -} - -/* Prints the time to given window */ -void print_time(WINDOW *window) -{ - uint8_t s[MAX_STR_SIZE]; - strftime(s, MAX_STR_SIZE, "[%H:%M:%S] ", get_time()); - - wattron(window, COLOR_PAIR(BLUE)); - wprintw(window, "%s", s); - wattroff(window,COLOR_PAIR(BLUE)); -} - /* Returns 1 if the string is empty, 0 otherwise */ int string_is_empty(char *string) { diff --git a/src/misc_tools.h b/src/misc_tools.h index 41d31a1..b5b2a93 100644 --- a/src/misc_tools.h +++ b/src/misc_tools.h @@ -26,11 +26,17 @@ /* convert a hex string to binary */ unsigned char *hex_string_to_bin(char hex_string[]); +/* get the current unix time */ +uint64_t get_unix_time(void); + +/*Puts the current time in buf in the format of [Hour:Min:Sec] */ +void get_time_str(uint8_t *buf); + /* get the current local time */ struct tm *get_time(void); -/* Prints the time to given window */ -void print_time(WINDOW *window); +/* updates current unix time (should be run once per do_toxic loop) */ +void update_unix_time(void); /* Returns 1 if the string is empty, 0 otherwise */ int string_is_empty(char *string); @@ -61,7 +67,7 @@ int qsort_strcasecmp_hlpr(const void *nick1, const void *nick2); - cannot be empty - cannot start with a space - must not contain contiguous spaces */ -bool valid_nick(uint8_t *nick); +int valid_nick(uint8_t *nick); /* Moves the cursor to the end of the line in given window */ void mv_curs_end(WINDOW *w, size_t len, int max_y, int max_x); diff --git a/src/prompt.c b/src/prompt.c index 2c797c7..943cbf7 100644 --- a/src/prompt.c +++ b/src/prompt.c @@ -32,6 +32,8 @@ #include "execute.h" #include "misc_tools.h" #include "toxic_strings.h" +#include "log.h" +#include "line_info.h" uint8_t pending_frnd_requests[MAX_FRIENDS_NUM][TOX_CLIENT_ID_SIZE] = {0}; uint8_t num_frnd_requests = 0; @@ -64,25 +66,6 @@ const uint8_t glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = { #endif /* _SUPPORT_AUDIO */ }; -/* prevents input string from eating system messages: call this prior to printing a prompt message - TODO: This is only a partial fix */ -void prep_prompt_win(void) -{ - PromptBuf *prt = prompt->promptbuf; - - if (prt->len <= 0) - return; - - wprintw(prompt->window, "\n"); - - if (!prt->at_bottom) { - wmove(prompt->window, prt->orig_y - 1, X_OFST); - ++prt->orig_y; - } else { - wmove(prompt->window, prt->orig_y - 2, X_OFST); - } -} - /* Updates own nick in prompt statusbar */ void prompt_update_nick(ToxWindow *prompt, uint8_t *nick, uint16_t len) { @@ -100,7 +83,7 @@ void prompt_update_statusmessage(ToxWindow *prompt, uint8_t *statusmsg, uint16_t } /* Updates own status in prompt statusbar */ -void prompt_update_status(ToxWindow *prompt, TOX_USERSTATUS status) +void prompt_update_status(ToxWindow *prompt, uint8_t status) { StatusBar *statusbar = prompt->stb; statusbar->status = status; @@ -136,153 +119,166 @@ static int add_friend_request(uint8_t *public_key) return -1; } -static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key) +static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) { - PromptBuf *prt = self->promptbuf; + ChatContext *ctx = self->chatwin; int x, y, y2, x2; - getyx(self->window, y, x); - getmaxyx(self->window, y2, x2); + getyx(ctx->history, y, x); + getmaxyx(ctx->history, y2, x2); - /* BACKSPACE key: Remove one character from line */ - if (key == 0x107 || key == 0x8 || key == 0x7f) { - if (prt->pos > 0) { - del_char_buf_bck(prt->line, &prt->pos, &prt->len); - wmove(self->window, y, x-1); /* not necessary but fixes a display glitch */ - prt->scroll = false; - } else { - beep(); + /* TODO this is buggy */ + /* ESC key: Toggle history scroll mode */ + /* + if (key == T_KEY_ESC) { + bool scroll = ctx->hst->scroll_mode ? false : true; + line_info_toggle_scroll(self, scroll); + } + */ + + /* If we're in scroll mode ignore rest of function */ + if (ctx->hst->scroll_mode) { + line_info_onKey(self, key); + return; + } + + if (ltr) { + if (ctx->len < (MAX_STR_SIZE-1)) { + add_char_to_buf(ctx->line, &ctx->pos, &ctx->len, key); } - } + } else { /* if (!ltr) */ - else if (key == KEY_DC) { /* DEL key: Remove character at pos */ - if (prt->pos != prt->len) { - del_char_buf_frnt(prt->line, &prt->pos, &prt->len); - prt->scroll = false; - } else { - beep(); - } - } - - else if (key == T_KEY_DISCARD) { /* CTRL-U: Delete entire line behind pos */ - if (prt->pos > 0) { - wmove(self->window, prt->orig_y, X_OFST); - wclrtobot(self->window); - discard_buf(prt->line, &prt->pos, &prt->len); - } else { - beep(); - } - } - - else if (key == T_KEY_KILL) { /* CTRL-K: Delete entire line in front of pos */ - if (prt->len != prt->pos) - kill_buf(prt->line, &prt->pos, &prt->len); - else - beep(); - } - - else if (key == KEY_HOME || key == T_KEY_C_A) { /* HOME/C-a key: Move cursor to start of line */ - if (prt->pos != 0) - prt->pos = 0; - } - - else if (key == KEY_END || key == T_KEY_C_E) { /* END/C-e key: move cursor to end of line */ - if (prt->pos != prt->len) - prt->pos = prt->len; - } - - else if (key == KEY_LEFT) { - if (prt->pos > 0) - --prt->pos; - else - beep(); - } - - else if (key == KEY_RIGHT) { - if (prt->pos < prt->len) - ++prt->pos; - else - beep(); - } - - else if (key == KEY_UP) { /* fetches previous item in history */ - wmove(self->window, prt->orig_y, X_OFST); - fetch_hist_item(prt->line, &prt->pos, &prt->len, prt->ln_history, prt->hst_tot, - &prt->hst_pos, LN_HIST_MV_UP); - - /* adjust line y origin appropriately when window scrolls down */ - if (prt->at_bottom && prt->len >= x2 - X_OFST) { - int px2 = prt->len >= x2 ? x2 : x2 - X_OFST; - int p_ofst = px2 != x2 ? 0 : X_OFST; - - if (px2 <= 0) - return; - - int k = prt->orig_y + ((prt->len + p_ofst) / px2); - - if (k >= y2) { - wprintw(self->window, "\n"); - --prt->orig_y; + /* BACKSPACE key: Remove one character from line */ + if (key == 0x107 || key == 0x8 || key == 0x7f) { + if (ctx->pos > 0) { + del_char_buf_bck(ctx->line, &ctx->pos, &ctx->len); + wmove(ctx->history, y, x-1); /* not necessary but fixes a display glitch */ + } else { + beep(); } } - } - else if (key == KEY_DOWN) { /* fetches next item in history */ - wmove(self->window, prt->orig_y, X_OFST); - fetch_hist_item(prt->line, &prt->pos, &prt->len, prt->ln_history, prt->hst_tot, - &prt->hst_pos, LN_HIST_MV_DWN); - } - - else if (key == '\t') { /* TAB key: completes command */ - if (prt->len > 1 && prt->line[0] == '/') { - if (complete_line(prt->line, &prt->pos, &prt->len, glob_cmd_list, AC_NUM_GLOB_COMMANDS, - MAX_CMDNAME_SIZE) == -1) + else if (key == KEY_DC) { /* DEL key: Remove character at pos */ + if (ctx->pos != ctx->len) { + del_char_buf_frnt(ctx->line, &ctx->pos, &ctx->len); + } else { beep(); - } else { - beep(); + } } - } - else -#if HAVE_WIDECHAR - if (iswprint(key)) -#else - if (isprint(key)) -#endif - { - if (prt->len < (MAX_STR_SIZE-1)) { - add_char_to_buf(prt->line, &prt->pos, &prt->len, key); - prt->scroll = true; + else if (key == T_KEY_DISCARD) { /* CTRL-U: Delete entire line behind pos */ + if (ctx->pos > 0) { + wmove(ctx->history, ctx->orig_y, X_OFST); + wclrtobot(ctx->history); + discard_buf(ctx->line, &ctx->pos, &ctx->len); + } else { + beep(); + } } - } - /* RETURN key: execute command */ - else if (key == '\n') { - wprintw(self->window, "\n"); - uint8_t line[MAX_STR_SIZE]; - if (wcs_to_mbs_buf(line, prt->line, MAX_STR_SIZE) == -1) - memset(&line, 0, sizeof(line)); + else if (key == T_KEY_KILL) { /* CTRL-K: Delete entire line in front of pos */ + if (ctx->len != ctx->pos) + kill_buf(ctx->line, &ctx->pos, &ctx->len); + else + beep(); + } - if (!string_is_empty(line)) - add_line_to_hist(prt->line, prt->len, prt->ln_history, &prt->hst_tot, &prt->hst_pos); + else if (key == KEY_HOME || key == T_KEY_C_A) { /* HOME/C-a key: Move cursor to start of line */ + if (ctx->pos != 0) + ctx->pos = 0; + } - execute(self->window, self, m, line, GLOBAL_COMMAND_MODE); - reset_buf(prt->line, &prt->pos, &prt->len); + else if (key == KEY_END || key == T_KEY_C_E) { /* END/C-e key: move cursor to end of line */ + if (ctx->pos != ctx->len) + ctx->pos = ctx->len; + } + + else if (key == KEY_LEFT) { + if (ctx->pos > 0) + --ctx->pos; + else + beep(); + } + + else if (key == KEY_RIGHT) { + if (ctx->pos < ctx->len) + ++ctx->pos; + else + beep(); + } + + else if (key == KEY_UP) { /* fetches previous item in history */ + wmove(ctx->history, ctx->orig_y, X_OFST); + fetch_hist_item(ctx->line, &ctx->pos, &ctx->len, ctx->ln_history, ctx->hst_tot, + &ctx->hst_pos, MOVE_UP); + + /* adjust line y origin appropriately when window scrolls down */ + if (ctx->at_bottom && ctx->len >= x2 - X_OFST) { + int px2 = ctx->len >= x2 ? x2 : x2 - X_OFST; + int p_ofst = px2 != x2 ? 0 : X_OFST; + + if (px2 <= 0) + return; + + int k = ctx->orig_y + ((ctx->len + p_ofst) / px2); + + if (k >= y2) { + --ctx->orig_y; + } + } + } + + else if (key == KEY_DOWN) { /* fetches next item in history */ + wmove(ctx->history, ctx->orig_y, X_OFST); + fetch_hist_item(ctx->line, &ctx->pos, &ctx->len, ctx->ln_history, ctx->hst_tot, + &ctx->hst_pos, MOVE_DOWN); + } + + else if (key == '\t') { /* TAB key: completes command */ + if (ctx->len > 1 && ctx->line[0] == '/') { + if (complete_line(ctx->line, &ctx->pos, &ctx->len, glob_cmd_list, AC_NUM_GLOB_COMMANDS, + MAX_CMDNAME_SIZE) == -1) + beep(); + } else { + beep(); + } + } + + /* RETURN key: execute command */ + else if (key == '\n') { + wprintw(ctx->history, "\n"); + uint8_t line[MAX_STR_SIZE] = {0}; + + if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) + memset(&line, 0, sizeof(line)); + + if (!string_is_empty(line)) + add_line_to_hist(ctx->line, ctx->len, ctx->ln_history, &ctx->hst_tot, &ctx->hst_pos); + + line_info_add(self, NULL, NULL, NULL, line, PROMPT, 0, 0); + execute(ctx->history, self, m, line, GLOBAL_COMMAND_MODE); + reset_buf(ctx->line, &ctx->pos, &ctx->len); + } } } static void prompt_onDraw(ToxWindow *self, Tox *m) { - PromptBuf *prt = self->promptbuf; + ChatContext *ctx = self->chatwin; - curs_set(1); int x, y, x2, y2; - getyx(self->window, y, x); - getmaxyx(self->window, y2, x2); - wclrtobot(self->window); + getyx(ctx->history, y, x); + getmaxyx(ctx->history, y2, x2); + + if (!ctx->hst->scroll_mode) { + curs_set(1); + scrollok(ctx->history, 1); + } + + line_info_print(self); /* if len is >= screen width offset max x by X_OFST to account for prompt char */ - int px2 = prt->len >= x2 ? x2 : x2 - X_OFST; + int px2 = ctx->len >= x2 ? x2 : x2 - X_OFST; if (px2 <= 0) return; @@ -290,33 +286,31 @@ static void prompt_onDraw(ToxWindow *self, Tox *m) /* len offset to account for prompt char (0 if len is < width of screen) */ int p_ofst = px2 != x2 ? 0 : X_OFST; - if (prt->len > 0) { + if (ctx->len > 0) { uint8_t line[MAX_STR_SIZE]; - if (wcs_to_mbs_buf(line, prt->line, MAX_STR_SIZE) == -1) - reset_buf(prt->line, &prt->pos, &prt->len); + if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) + reset_buf(ctx->line, &ctx->pos, &ctx->len); else - mvwprintw(self->window, prt->orig_y, X_OFST, line); + mvwprintw(ctx->history, ctx->orig_y, X_OFST, line); - int k = prt->orig_y + ((prt->len + p_ofst) / px2); + int k = ctx->orig_y + ((ctx->len + p_ofst) / px2); - prt->at_bottom = k == y2 - 1; + ctx->at_bottom = k == y2 - 1; bool botm = k == y2; - bool edge = (prt->len + p_ofst) % px2 == 0; + bool edge = (ctx->len + p_ofst) % px2 == 0; /* move point of line origin up when input scrolls screen down */ - if (prt->scroll && edge && botm) { - --prt->orig_y; - prt->scroll = false; - } + if (edge && botm) + --ctx->orig_y; } else { /* Mark point of origin for new line */ - prt->orig_y = y; + ctx->orig_y = y; } - wattron(self->window, COLOR_PAIR(GREEN)); - mvwprintw(self->window, prt->orig_y, 0, "$ "); - wattroff(self->window, COLOR_PAIR(GREEN)); + wattron(ctx->history, COLOR_PAIR(GREEN)); + mvwprintw(ctx->history, ctx->orig_y, 0, "$ "); + wattroff(ctx->history, COLOR_PAIR(GREEN)); StatusBar *statusbar = self->stb; werase(statusbar->topline); @@ -325,7 +319,7 @@ static void prompt_onDraw(ToxWindow *self, Tox *m) if (statusbar->is_online) { int colour = WHITE; - char *status_text = "Unknown"; + const uint8_t *status_text = "Unknown"; switch (statusbar->status) { case TOX_USERSTATUS_NONE: @@ -340,159 +334,164 @@ static void prompt_onDraw(ToxWindow *self, Tox *m) status_text = "Busy"; colour = RED; break; + case TOX_USERSTATUS_INVALID: + status_text = "ERROR"; + colour = MAGENTA; + break; } + wattron(statusbar->topline, COLOR_PAIR(colour) | A_BOLD); + wprintw(statusbar->topline, " [%s]", status_text); + wattroff(statusbar->topline, COLOR_PAIR(colour) | A_BOLD); wattron(statusbar->topline, A_BOLD); - wprintw(statusbar->topline, " %s ", statusbar->nick); - wattron(statusbar->topline, A_BOLD); - wattron(statusbar->topline, COLOR_PAIR(colour) | A_BOLD); - wprintw(statusbar->topline, "[%s]", status_text); - wattroff(statusbar->topline, COLOR_PAIR(colour) | A_BOLD); + wprintw(statusbar->topline, " %s", statusbar->nick); + wattroff(statusbar->topline, A_BOLD); } else { + wprintw(statusbar->topline, "[Offline]"); wattron(statusbar->topline, A_BOLD); wprintw(statusbar->topline, " %s ", statusbar->nick); wattroff(statusbar->topline, A_BOLD); - wprintw(statusbar->topline, "[Offline]"); } - wattron(statusbar->topline, A_BOLD); - wprintw(statusbar->topline, " - %s", statusbar->statusmsg); - wattroff(statusbar->topline, A_BOLD); + if (statusbar->statusmsg[0]) + wprintw(statusbar->topline, " - %s", statusbar->statusmsg); /* put cursor back in correct spot */ - int y_m = prt->orig_y + ((prt->pos + p_ofst) / px2); - int x_m = (prt->pos + X_OFST) % x2; + int y_m = ctx->orig_y + ((ctx->pos + p_ofst) / px2); + int x_m = (ctx->pos + X_OFST) % x2; wmove(self->window, y_m, x_m); } -static void prompt_onInit(ToxWindow *self, Tox *m) -{ - scrollok(self->window, true); - PromptBuf *prt = self->promptbuf; - - prt->log = malloc(sizeof(struct chatlog)); - - if (prt->log == NULL) { - endwin(); - fprintf(stderr, "malloc() failed. Aborting...\n"); - exit(EXIT_FAILURE); - } - - memset(prt->log, 0, sizeof(struct chatlog)); - - execute(self->window, self, m, "/help", GLOBAL_COMMAND_MODE); - wclrtoeol(self->window); -} - -static void prompt_onConnectionChange(ToxWindow *self, Tox *m, int friendnum , uint8_t status) +static void prompt_onConnectionChange(ToxWindow *self, Tox *m, int32_t friendnum , uint8_t status) { if (friendnum < 0) return; - PromptBuf *prt = self->promptbuf; - prep_prompt_win(); + ChatContext *ctx = self->chatwin; - uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'}; + uint8_t nick[TOX_MAX_NAME_LENGTH] = {0}; + int n_len = tox_get_name(m, friendnum, nick); + n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH-1); - if (tox_get_name(m, friendnum, nick) == -1) - return; - - if (!nick[0]) + if (!nick[0]) { snprintf(nick, sizeof(nick), "%s", UNKNOWN_NAME); + n_len = strlen(UNKNOWN_NAME); + } - wprintw(self->window, "\n"); - print_time(self->window); + nick[n_len] = '\0'; + uint8_t timefrmt[TIME_STR_SIZE]; + get_time_str(timefrmt); uint8_t *msg; if (status == 1) { msg = "has come online"; - wattron(self->window, COLOR_PAIR(GREEN)); - wattron(self->window, A_BOLD); - wprintw(self->window, "* %s ", nick); - wattroff(self->window, A_BOLD); - wprintw(self->window, "%s\n", msg); - wattroff(self->window, COLOR_PAIR(GREEN)); - - write_to_log(msg, nick, prt->log, true); + line_info_add(self, timefrmt, nick, NULL, msg, CONNECTION, 0, GREEN); + write_to_log(msg, nick, ctx->log, true); alert_window(self, WINDOW_ALERT_2, false); } else { msg = "has gone offline"; - wattron(self->window, COLOR_PAIR(RED)); - wattron(self->window, A_BOLD); - wprintw(self->window, "* %s ", nick); - wattroff(self->window, A_BOLD); - wprintw(self->window, "%s\n", msg); - wattroff(self->window, COLOR_PAIR(RED)); - - write_to_log(msg, nick, prt->log, true); + line_info_add(self, timefrmt, nick, NULL, msg, CONNECTION, 0, RED); + write_to_log(msg, nick, ctx->log, true); } } -static void prompt_onFriendRequest(ToxWindow *self, uint8_t *key, uint8_t *data, uint16_t length) +static void prompt_onFriendRequest(ToxWindow *self, Tox *m, uint8_t *key, uint8_t *data, uint16_t length) { - /* make sure message data is null-terminated */ - data[length - 1] = 0; - PromptBuf *prt = self->promptbuf; - prep_prompt_win(); + data[length] = '\0'; - wprintw(self->window, "\n"); - print_time(self->window); + ChatContext *ctx = self->chatwin; + + uint8_t timefrmt[TIME_STR_SIZE]; + get_time_str(timefrmt); uint8_t msg[MAX_STR_SIZE]; - snprintf(msg, sizeof(msg), "Friend request with the message '%s'\n", data); - wprintw(self->window, "%s", msg); - write_to_log(msg, "", prt->log, true); + snprintf(msg, sizeof(msg), "Friend request with the message '%s'", data); + line_info_add(self, timefrmt, NULL, NULL, msg, SYS_MSG, 0, 0); + write_to_log(msg, "", ctx->log, true); int n = add_friend_request(key); if (n == -1) { - uint8_t *errmsg = "Friend request queue is full. Discarding request.\n"; - wprintw(self->window, "%s", errmsg); - write_to_log(errmsg, "", prt->log, true); + uint8_t *errmsg = "Friend request queue is full. Discarding request."; + line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); + write_to_log(errmsg, "", ctx->log, true); return; } - wprintw(self->window, "Type \"/accept %d\" to accept it.\n", n); + snprintf(msg, sizeof(msg), "Type \"/accept %d\" to accept it.", n); + line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); alert_window(self, WINDOW_ALERT_1, true); } void prompt_init_statusbar(ToxWindow *self, Tox *m) { - int x, y; - getmaxyx(self->window, y, x); + int x2, y2; + getmaxyx(self->window, y2, x2); /* Init statusbar info */ StatusBar *statusbar = self->stb; statusbar->status = TOX_USERSTATUS_NONE; statusbar->is_online = false; - uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'}; + uint8_t nick[TOX_MAX_NAME_LENGTH]; uint8_t statusmsg[MAX_STR_SIZE]; pthread_mutex_lock(&Winthread.lock); - tox_get_self_name(m, nick, TOX_MAX_NAME_LENGTH); - tox_get_self_status_message(m, statusmsg, MAX_STR_SIZE); + uint16_t n_len = tox_get_self_name(m, nick); + uint16_t s_len = tox_get_self_status_message(m, statusmsg, MAX_STR_SIZE); + uint8_t status = tox_get_self_user_status(m); pthread_mutex_unlock(&Winthread.lock); - snprintf(statusbar->nick, sizeof(statusbar->nick), "%s", nick); + nick[n_len] = '\0'; + statusmsg[s_len] = '\0'; - /* temporary until statusmessage saving works */ + /* load prev status message or show toxic version if it has never been set */ uint8_t ver[strlen(TOXICVER) + 1]; strcpy(ver, TOXICVER); - uint8_t *toxic_ver = strtok(ver, "_"); + const uint8_t *toxic_ver = strtok(ver, "_"); - if (!strcmp("Online", statusmsg)) + if ( (!strcmp("Online", statusmsg) || !strncmp("Toxing on Toxic", statusmsg, 15)) && toxic_ver != NULL) { snprintf(statusmsg, MAX_STR_SIZE, "Toxing on Toxic v.%s", toxic_ver); + s_len = strlen(statusmsg); + statusmsg[s_len] = '\0'; + } - pthread_mutex_lock(&Winthread.lock); - tox_set_status_message(m, statusmsg, strlen(statusmsg) + 1); - pthread_mutex_unlock(&Winthread.lock); - - snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg); + prompt_update_statusmessage(prompt, statusmsg, s_len); + prompt_update_status(prompt, status); + prompt_update_nick(prompt, nick, n_len); /* Init statusbar subwindow */ - statusbar->topline = subwin(self->window, 2, x, 0, 0); + statusbar->topline = subwin(self->window, 2, x2, 0, 0); +} + +static void prompt_onInit(ToxWindow *self, Tox *m) +{ + ChatContext *ctx = self->chatwin; + + curs_set(1); + int y2, x2; + getmaxyx(self->window, y2, x2); + + ctx->history = subwin(self->window, y2, x2, 0, 0); + scrollok(ctx->history, 1); + + ctx->log = malloc(sizeof(struct chatlog)); + ctx->hst = malloc(sizeof(struct history)); + + if (ctx->log == NULL || ctx->hst == NULL) { + endwin(); + fprintf(stderr, "malloc() failed. Aborting...\n"); + exit(EXIT_FAILURE); + } + + memset(ctx->log, 0, sizeof(struct chatlog)); + memset(ctx->hst, 0, sizeof(struct history)); + + line_info_init(ctx->hst); + execute(ctx->history, self, m, "/help", GLOBAL_COMMAND_MODE); + + wmove(ctx->history, y2-1, 2); } ToxWindow new_prompt(void) @@ -511,11 +510,11 @@ ToxWindow new_prompt(void) strcpy(ret.name, "prompt"); - PromptBuf *promptbuf = calloc(1, sizeof(PromptBuf)); + ChatContext *chatwin = calloc(1, sizeof(ChatContext)); StatusBar *stb = calloc(1, sizeof(StatusBar)); - if (stb != NULL && promptbuf != NULL) { - ret.promptbuf = promptbuf; + if (stb != NULL && chatwin != NULL) { + ret.chatwin = chatwin; ret.stb = stb; } else { endwin(); diff --git a/src/prompt.h b/src/prompt.h index aca8844..af032a3 100644 --- a/src/prompt.h +++ b/src/prompt.h @@ -37,7 +37,7 @@ void prep_prompt_win(void); void prompt_init_statusbar(ToxWindow *self, Tox *m); void prompt_update_nick(ToxWindow *prompt, uint8_t *nick, uint16_t len); void prompt_update_statusmessage(ToxWindow *prompt, uint8_t *statusmsg, uint16_t len); -void prompt_update_status(ToxWindow *prompt, TOX_USERSTATUS status); +void prompt_update_status(ToxWindow *prompt, uint8_t status); void prompt_update_connectionstatus(ToxWindow *prompt, bool is_connected); #endif /* end of include guard: PROMPT_H_UZYGWFFL */ diff --git a/src/toxic_strings.c b/src/toxic_strings.c index 8935954..e535a18 100644 --- a/src/toxic_strings.c +++ b/src/toxic_strings.c @@ -148,7 +148,7 @@ void add_line_to_hist(const wchar_t *buf, size_t len, wchar_t (*hst)[MAX_STR_SIZ void fetch_hist_item(wchar_t *buf, size_t *pos, size_t *len, wchar_t (*hst)[MAX_STR_SIZE], int hst_tot, int *hst_pos, int key_dir) { - if (key_dir == LN_HIST_MV_UP) { + if (key_dir == MOVE_UP) { if (--(*hst_pos) < 0) { *hst_pos = 0; beep(); diff --git a/src/toxic_strings.h b/src/toxic_strings.h index 3e28c0f..cb288e1 100644 --- a/src/toxic_strings.h +++ b/src/toxic_strings.h @@ -48,11 +48,6 @@ void reset_buf(wchar_t *buf, size_t *pos, size_t *len); Returns the difference between the old len and new len of buf on success, -1 if error */ int complete_line(wchar_t *buf, size_t *pos, size_t *len, const void *list, int n_items, int size); -enum { - LN_HIST_MV_UP, - LN_HIST_MV_DWN, -}; - /* adds a line to the ln_history buffer at hst_pos and sets hst_pos to last history item. */ void add_line_to_hist(const wchar_t *buf, size_t len, wchar_t (*hst)[MAX_STR_SIZE], int *hst_tot, int *hst_pos); diff --git a/src/toxic_windows.h b/src/toxic_windows.h index 08c4807..dffa46d 100644 --- a/src/toxic_windows.h +++ b/src/toxic_windows.h @@ -49,6 +49,7 @@ #define CURS_Y_OFFSET 3 /* y-axis cursor offset for chat contexts */ #define CHATBOX_HEIGHT 4 #define KEY_IDENT_DIGITS 2 /* number of hex digits to display for the pub-key based identifier */ +#define TIME_STR_SIZE 16 #define EXIT_SUCCESS 0 #define EXIT_FAILURE 1 @@ -60,6 +61,7 @@ #define T_KEY_PREV 0x0F /* ctrl-o */ #define T_KEY_C_E 0x05 /* ctrl-e */ #define T_KEY_C_A 0x01 /* ctrl-a */ +#define T_KEY_ESC 0x1B /* ESC key */ /* Curses foreground colours (background is black) */ enum { @@ -80,7 +82,12 @@ enum { WINDOW_ALERT_2, }; -/* Fixes text color problem on some terminals. +enum { + MOVE_UP, + MOVE_DOWN, +}; + +/* Fixes text color problem on some terminals. Uncomment if necessary */ /* #define URXVT_FIX */ @@ -90,25 +97,25 @@ typedef struct PromptBuf PromptBuf; typedef struct ChatContext ChatContext; struct ToxWindow { - void(*onKey)(ToxWindow *, Tox *, wint_t); + void(*onKey)(ToxWindow *, Tox *, wint_t, bool); void(*onDraw)(ToxWindow *, Tox *); void(*onInit)(ToxWindow *, Tox *); - void(*onFriendRequest)(ToxWindow *, uint8_t *, uint8_t *, uint16_t); - void(*onFriendAdded)(ToxWindow *, Tox *, int, bool); - void(*onConnectionChange)(ToxWindow *, Tox *, int, uint8_t); - void(*onMessage)(ToxWindow *, Tox *, int, uint8_t *, uint16_t); - void(*onNickChange)(ToxWindow *, Tox *, int, uint8_t *, uint16_t); - void(*onStatusChange)(ToxWindow *, Tox *, int, TOX_USERSTATUS); - void(*onStatusMessageChange)(ToxWindow *, int, uint8_t *, uint16_t); - void(*onAction)(ToxWindow *, Tox *, int, uint8_t *, uint16_t); + void(*onFriendRequest)(ToxWindow *, Tox *, uint8_t *, uint8_t *, uint16_t); + void(*onFriendAdded)(ToxWindow *, Tox *, int32_t, bool); + void(*onConnectionChange)(ToxWindow *, Tox *, int32_t, uint8_t); + void(*onMessage)(ToxWindow *, Tox *, int32_t, uint8_t *, uint16_t); + void(*onNickChange)(ToxWindow *, Tox *, int32_t, uint8_t *, uint16_t); + void(*onStatusChange)(ToxWindow *, Tox *, int32_t, uint8_t); + void(*onStatusMessageChange)(ToxWindow *, int32_t, uint8_t *, uint16_t); + void(*onAction)(ToxWindow *, Tox *, int32_t, uint8_t *, uint16_t); void(*onGroupMessage)(ToxWindow *, Tox *, int, int, uint8_t *, uint16_t); void(*onGroupAction)(ToxWindow *, Tox *, int, int, uint8_t *, uint16_t); - void(*onGroupInvite)(ToxWindow *, Tox *, int, uint8_t *); + void(*onGroupInvite)(ToxWindow *, Tox *, int32_t, uint8_t *); void(*onGroupNamelistChange)(ToxWindow *, Tox*, int, int, uint8_t); - void(*onFileSendRequest)(ToxWindow *, Tox *, int, uint8_t, uint64_t, uint8_t *, uint16_t); - void(*onFileControl)(ToxWindow *, Tox *, int, uint8_t, uint8_t, uint8_t, uint8_t *, uint16_t); - void(*onFileData)(ToxWindow *, Tox *, int, uint8_t, uint8_t *, uint16_t); - void(*onTypingChange)(ToxWindow *, Tox *, int, int); + void(*onFileSendRequest)(ToxWindow *, Tox *, int32_t, uint8_t, uint64_t, uint8_t *, uint16_t); + void(*onFileControl)(ToxWindow *, Tox *, int32_t, uint8_t, uint8_t, uint8_t, uint8_t *, uint16_t); + void(*onFileData)(ToxWindow *, Tox *, int32_t, uint8_t, uint8_t *, uint16_t); + void(*onTypingChange)(ToxWindow *, Tox *, int32_t, uint8_t); #ifdef _SUPPORT_AUDIO @@ -123,11 +130,11 @@ struct ToxWindow { void(*onEnd)(ToxWindow *, ToxAv *); void(*onRequestTimeout)(ToxWindow *, ToxAv *); void(*onPeerTimeout)(ToxWindow *, ToxAv *); - + #endif /* _SUPPORT_AUDIO */ - + char name[TOX_MAX_NAME_LENGTH]; - int num; + int32_t num; /* corresponds to friendnumber in chat windows */ bool active; int x; @@ -141,7 +148,6 @@ struct ToxWindow { bool alert2; ChatContext *chatwin; - PromptBuf *promptbuf; StatusBar *stb; WINDOW *popup; @@ -150,24 +156,15 @@ struct ToxWindow { /* statusbar info holder */ struct StatusBar { - WINDOW *topline; + WINDOW *topline; uint8_t statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH]; uint16_t statusmsg_len; uint8_t nick[TOX_MAX_NAME_LENGTH]; uint16_t nick_len; - TOX_USERSTATUS status; + uint8_t status; bool is_online; }; -#define LOG_FLUSH_LIMIT 2 /* limits calls to fflush(logfile) to a max of one per LOG_FLUSH_LIMIT seconds */ - -struct chatlog { - FILE *file; - uint64_t lastwrite; - int pos; - bool log_on; /* specific to current chat window */ -}; - #define MAX_LINE_HIST 128 /* chat and groupchat window/buffer holder */ @@ -176,35 +173,22 @@ struct ChatContext { size_t pos; size_t len; - wchar_t ln_history[MAX_LINE_HIST][MAX_STR_SIZE]; + wchar_t ln_history[MAX_LINE_HIST][MAX_STR_SIZE]; /* history for input lines/commands */ int hst_pos; int hst_tot; - bool self_is_typing; - + struct history *hst; struct chatlog *log; + uint8_t self_is_typing; + WINDOW *history; WINDOW *linewin; WINDOW *sidebar; -}; - -/* prompt window/buffer holder */ -struct PromptBuf { - wchar_t line[MAX_STR_SIZE]; - size_t pos; - size_t len; + /* specific for prompt */ bool at_bottom; /* true if line end is at bottom of window */ int orig_y; /* y axis point of line origin */ - bool scroll; /* used for prompt window hack to determine when to scroll down */ - - wchar_t ln_history[MAX_LINE_HIST][MAX_STR_SIZE]; - int hst_pos; - int hst_tot; - - struct chatlog *log; - WINDOW *linewin; }; /* Start file transfer code */ @@ -216,19 +200,23 @@ struct PromptBuf { typedef struct { FILE *file; ToxWindow *toxwin; - int friendnum; + int32_t friendnum; bool active; - uint8_t filenum; + int filenum; uint8_t nextpiece[FILE_PIECE_SIZE]; uint16_t piecelen; uint8_t pathname[MAX_STR_SIZE]; uint64_t timestamp; + uint64_t size; + uint32_t line_id; } FileSender; struct FileReceiver { uint8_t filenames[MAX_FILES][MAX_STR_SIZE]; FILE *files[MAX_FILES]; bool pending[MAX_FILES]; + uint64_t size[MAX_FILES]; + uint32_t line_id; }; /* End file transfer code */ @@ -238,22 +226,22 @@ struct _Winthread { pthread_mutex_t lock; }; -void on_request(uint8_t *public_key, uint8_t *data, uint16_t length, void *userdata); -void on_connectionchange(Tox *m, int friendnumber, uint8_t status, void *userdata); -void on_message(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void *userdata); -void on_action(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void *userdata); -void on_nickchange(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void *userdata); -void on_statuschange(Tox *m, int friendnumber, TOX_USERSTATUS status, void *userdata); -void on_statusmessagechange(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void *userdata); -void on_friendadded(Tox *m, int friendnumber, bool sort); +void on_request(Tox *m, uint8_t *public_key, uint8_t *data, uint16_t length, void *userdata); +void on_connectionchange(Tox *m, int32_t friendnumber, uint8_t status, void *userdata); +void on_message(Tox *m, int32_t friendnumber, uint8_t *string, uint16_t length, void *userdata); +void on_action(Tox *m, int32_t friendnumber, uint8_t *string, uint16_t length, void *userdata); +void on_nickchange(Tox *m, int32_t friendnumber, uint8_t *string, uint16_t length, void *userdata); +void on_statuschange(Tox *m, int32_t friendnumber, uint8_t status, void *userdata); +void on_statusmessagechange(Tox *m, int32_t friendnumber, uint8_t *string, uint16_t length, void *userdata); +void on_friendadded(Tox *m, int32_t friendnumber, bool sort); void on_groupmessage(Tox *m, int groupnumber, int peernumber, uint8_t *message, uint16_t length, void *userdata); void on_groupaction(Tox *m, int groupnumber, int peernumber, uint8_t *action, uint16_t length, void *userdata); -void on_groupinvite(Tox *m, int friendnumber, uint8_t *group_pub_key, void *userdata); +void on_groupinvite(Tox *m, int32_t friendnumber, uint8_t *group_pub_key, void *userdata); void on_group_namelistchange(Tox *m, int groupnumber, int peernumber, uint8_t change, void *userdata); -void on_file_sendrequest(Tox *m, int friendnumber, uint8_t filenumber, uint64_t filesize, uint8_t *pathname, uint16_t pathname_length, void *userdata); -void on_file_control(Tox *m, int friendnumber, uint8_t receive_send, uint8_t filenumber, uint8_t control_type, uint8_t *data, uint16_t length, void *userdata); -void on_file_data(Tox *m, int friendnumber, uint8_t filenumber, uint8_t *data, uint16_t length, void *userdata); -void on_typing_change(Tox *m, int friendnumber, int is_typing, void *userdata); +void on_file_sendrequest(Tox *m, int32_t friendnumber, uint8_t filenumber, uint64_t filesize, uint8_t *pathname, uint16_t pathname_length, void *userdata); +void on_file_control(Tox *m, int32_t friendnumber, uint8_t receive_send, uint8_t filenumber, uint8_t control_type, uint8_t *data, uint16_t length, void *userdata); +void on_file_data(Tox *m, int32_t friendnumber, uint8_t filenumber, uint8_t *data, uint16_t length, void *userdata); +void on_typing_change(Tox *m, int32_t friendnumber, uint8_t is_typing, void *userdata); ToxWindow *init_windows(Tox *m); void draw_active_window(Tox *m); diff --git a/src/windows.c b/src/windows.c index 2c3024d..ce29fb6 100644 --- a/src/windows.c +++ b/src/windows.c @@ -43,64 +43,64 @@ extern ToxWindow *prompt; static int num_active_windows; /* CALLBACKS START */ -void on_request(uint8_t *public_key, uint8_t *data, uint16_t length, void *userdata) +void on_request(Tox *m, uint8_t *public_key, uint8_t *data, uint16_t length, void *userdata) { int i; - - for (i = 0; i < num_active_windows; ++i) { + + for (i = 0; i < MAX_WINDOWS_NUM; ++i) { if (windows[i].onFriendRequest != NULL) - windows[i].onFriendRequest(&windows[i], public_key, data, length); + windows[i].onFriendRequest(&windows[i], m, public_key, data, length); } } -void on_connectionchange(Tox *m, int friendnumber, uint8_t status, void *userdata) +void on_connectionchange(Tox *m, int32_t friendnumber, uint8_t status, void *userdata) { int i; - for (i = 0; i < num_active_windows; ++i) { + for (i = 0; i < MAX_WINDOWS_NUM; ++i) { if (windows[i].onConnectionChange != NULL) windows[i].onConnectionChange(&windows[i], m, friendnumber, status); } } -void on_typing_change(Tox *m, int friendnumber, int is_typing, void *userdata) +void on_typing_change(Tox *m, int32_t friendnumber, uint8_t is_typing, void *userdata) { int i; - for (i = 0; i < num_active_windows; ++i) { + for (i = 0; i < MAX_WINDOWS_NUM; ++i) { if (windows[i].onTypingChange != NULL) windows[i].onTypingChange(&windows[i], m, friendnumber, is_typing); } } -void on_message(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void *userdata) +void on_message(Tox *m, int32_t friendnumber, uint8_t *string, uint16_t length, void *userdata) { int i; - for (i = 0; i < num_active_windows; ++i) { + for (i = 0; i < MAX_WINDOWS_NUM; ++i) { if (windows[i].onMessage != NULL) windows[i].onMessage(&windows[i], m, friendnumber, string, length); } } -void on_action(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void *userdata) +void on_action(Tox *m, int32_t friendnumber, uint8_t *string, uint16_t length, void *userdata) { int i; - for (i = 0; i < num_active_windows; ++i) { + for (i = 0; i < MAX_WINDOWS_NUM; ++i) { if (windows[i].onAction != NULL) windows[i].onAction(&windows[i], m, friendnumber, string, length); } } -void on_nickchange(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void *userdata) +void on_nickchange(Tox *m, int32_t friendnumber, uint8_t *string, uint16_t length, void *userdata) { if (friendnumber < 0 || friendnumber > MAX_FRIENDS_NUM) return; int i; - for (i = 0; i < num_active_windows; ++i) { + for (i = 0; i < MAX_WINDOWS_NUM; ++i) { if (windows[i].onNickChange != NULL) windows[i].onNickChange(&windows[i], m, friendnumber, string, length); } @@ -109,31 +109,31 @@ void on_nickchange(Tox *m, int friendnumber, uint8_t *string, uint16_t length, v wprintw(prompt->window, "\nCould not store Tox data\n"); } -void on_statusmessagechange(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void *userdata) +void on_statusmessagechange(Tox *m, int32_t friendnumber, uint8_t *string, uint16_t length, void *userdata) { int i; - for (i = 0; i < num_active_windows; ++i) { + for (i = 0; i < MAX_WINDOWS_NUM; ++i) { if (windows[i].onStatusMessageChange != NULL) windows[i].onStatusMessageChange(&windows[i], friendnumber, string, length); } } -void on_statuschange(Tox *m, int friendnumber, TOX_USERSTATUS status, void *userdata) +void on_statuschange(Tox *m, int32_t friendnumber, uint8_t status, void *userdata) { int i; - for (i = 0; i < num_active_windows; ++i) { + for (i = 0; i < MAX_WINDOWS_NUM; ++i) { if (windows[i].onStatusChange != NULL) windows[i].onStatusChange(&windows[i], m, friendnumber, status); } } -void on_friendadded(Tox *m, int friendnumber, bool sort) +void on_friendadded(Tox *m, int32_t friendnumber, bool sort) { int i; - for (i = 0; i < num_active_windows; ++i) { + for (i = 0; i < MAX_WINDOWS_NUM; ++i) { if (windows[i].onFriendAdded != NULL) windows[i].onFriendAdded(&windows[i], m, friendnumber, sort); } @@ -147,7 +147,7 @@ void on_groupmessage(Tox *m, int groupnumber, int peernumber, uint8_t *message, { int i; - for (i = 0; i < num_active_windows; ++i) { + for (i = 0; i < MAX_WINDOWS_NUM; ++i) { if (windows[i].onGroupMessage != NULL) windows[i].onGroupMessage(&windows[i], m, groupnumber, peernumber, message, length); } @@ -158,17 +158,17 @@ void on_groupaction(Tox *m, int groupnumber, int peernumber, uint8_t *action, ui { int i; - for (i = 0; i < num_active_windows; ++i) { + for (i = 0; i < MAX_WINDOWS_NUM; ++i) { if (windows[i].onGroupAction != NULL) windows[i].onGroupAction(&windows[i], m, groupnumber, peernumber, action, length); } } -void on_groupinvite(Tox *m, int friendnumber, uint8_t *group_pub_key, void *userdata) +void on_groupinvite(Tox *m, int32_t friendnumber, uint8_t *group_pub_key, void *userdata) { int i; - for (i = 0; i < num_active_windows; ++i) { + for (i = 0; i < MAX_WINDOWS_NUM; ++i) { if (windows[i].onGroupInvite != NULL) windows[i].onGroupInvite(&windows[i], m, friendnumber, group_pub_key); } @@ -178,42 +178,42 @@ void on_group_namelistchange(Tox *m, int groupnumber, int peernumber, uint8_t ch { int i; - for (i = 0; i < num_active_windows; ++i) { + for (i = 0; i < MAX_WINDOWS_NUM; ++i) { if (windows[i].onGroupNamelistChange != NULL) windows[i].onGroupNamelistChange(&windows[i], m, groupnumber, peernumber, change); } } -void on_file_sendrequest(Tox *m, int friendnumber, uint8_t filenumber, uint64_t filesize, +void on_file_sendrequest(Tox *m, int32_t friendnumber, uint8_t filenumber, uint64_t filesize, uint8_t *filename, uint16_t filename_length, void *userdata) { int i; - for (i = 0; i < num_active_windows; ++i) { + for (i = 0; i < MAX_WINDOWS_NUM; ++i) { if (windows[i].onFileSendRequest != NULL) windows[i].onFileSendRequest(&windows[i], m, friendnumber, filenumber, filesize, filename, filename_length); } } -void on_file_control (Tox *m, int friendnumber, uint8_t receive_send, uint8_t filenumber, +void on_file_control (Tox *m, int32_t friendnumber, uint8_t receive_send, uint8_t filenumber, uint8_t control_type, uint8_t *data, uint16_t length, void *userdata) { int i; - for (i = 0; i < num_active_windows; ++i) { + for (i = 0; i < MAX_WINDOWS_NUM; ++i) { if (windows[i].onFileControl != NULL) windows[i].onFileControl(&windows[i], m, friendnumber, receive_send, filenumber, control_type, data, length); } } -void on_file_data(Tox *m, int friendnumber, uint8_t filenumber, uint8_t *data, uint16_t length, +void on_file_data(Tox *m, int32_t friendnumber, uint8_t filenumber, uint8_t *data, uint16_t length, void *userdata) { int i; - for (i = 0; i < num_active_windows; ++i) { + for (i = 0; i < MAX_WINDOWS_NUM; ++i) { if (windows[i].onFileData != NULL) windows[i].onFileData(&windows[i], m, friendnumber, filenumber, data, length); } @@ -241,7 +241,9 @@ int add_window(Tox *m, ToxWindow w) wbkgd(w.window, COLOR_PAIR(6)); #endif windows[i] = w; - w.onInit(&w, m); + + if (w.onInit) + w.onInit(&w, m); ++num_active_windows; @@ -392,18 +394,32 @@ void draw_active_window(Tox *m) wrefresh(a->window); /* Handle input */ + bool ltr; #ifdef HAVE_WIDECHAR - if (wget_wch(stdscr, &ch) == ERR) -#else - if ((ch = getch()) == ERR) -#endif + int status = wget_wch(stdscr, &ch); + + if (status == ERR) return; - if (ch == T_KEY_NEXT || ch == T_KEY_PREV) { + if (status == OK) + ltr = iswprint(ch); + else /* if (status == KEY_CODE_YES) */ + ltr = false; +#else + ch = getch(); + + if (ch == ERR) + return; + + /* TODO verify if this works */ + ltr = isprint(ch); +#endif + + if (!ltr && (ch == T_KEY_NEXT || ch == T_KEY_PREV) ) { set_next_window((int) ch); } else { pthread_mutex_lock(&Winthread.lock); - a->onKey(a, m, ch); + a->onKey(a, m, ch, ltr); pthread_mutex_unlock(&Winthread.lock); } }