diff --git a/build/Makefile b/build/Makefile index 8129d70..3a8dce5 100644 --- a/build/Makefile +++ b/build/Makefile @@ -1,4 +1,4 @@ -TOXIC_VERSION = 0.4.4 +TOXIC_VERSION = 0.4.5 REV = $(shell git rev-list HEAD --count) VERSION = $(TOXIC_VERSION)_r$(REV) @@ -15,14 +15,14 @@ MANFILES = toxic.1 toxic.conf.5 LIBS = libtoxcore ncursesw -CFLAGS = -std=gnu99 -pthread -Wall +CFLAGS = -std=gnu99 -pthread -Wall -g CFLAGS += -DTOXICVER="\"$(VERSION)\"" -DHAVE_WIDECHAR -D_XOPEN_SOURCE_EXTENDED CFLAGS += -DPACKAGE_DATADIR="\"$(abspath $(DATADIR))\"" CFLAGS += $(USER_CFLAGS) LDFLAGS = $(USER_LDFLAGS) OBJ = chat.o chat_commands.o configdir.o dns.o execute.o file_senders.o -OBJ += friendlist.o global_commands.o groupchat.o line_info.o input.o help.o +OBJ += friendlist.o global_commands.o groupchat.o line_info.o input.o help.o autocomplete.o OBJ += log.o misc_tools.o prompt.o settings.o toxic.o toxic_strings.o windows.o # Variables for audio support diff --git a/misc/toxic.conf.example b/misc/toxic.conf.example index af99157..9b6acde 100644 --- a/misc/toxic.conf.example +++ b/misc/toxic.conf.example @@ -1,26 +1,51 @@ -# 24 or 12 hour time -time:24; +// SAMPLE TOXIC CONFIGURATION +// USES LIBCONFIG-ACCEPTED SYNTAX -# 1 to enable timestamps, 0 to disable -timestamps:1; +ui = { + // true to enable timestamps, false to disable + timestamps:true; -# 1 to enable autologging, 0 to disable -autolog:0; + // true to disabale terminal alerts on messages, false to enable + alerts:true; -# 1 to disbale terminal alerts on messages, 0 to enable -alerts:1; + // true to use native terminal colours, false to use toxic default colour theme + native_colors:true; -# maximum lines for chat window history -history_size:700; + // true to enable autologging, false to disable + autolog:false; + + // 24 or 12 hour time + time_format=24; -# 1 to use native terminal colours, 0 to use toxic default colour theme -colour_theme:0; + // maximum lines for chat window history + history_size=700; +}; -# preferred audio input device; numbers correspond to /lsdev in -audio_in_dev:0; +audio = { + // preferred audio input device; numbers correspond to /lsdev in + input_device=2; -# preferred audio output device; numbers correspond to /lsdev out -audio_out_dev:0; + // preferred audio output device; numbers correspond to /lsdev out + output_device=0; + + // default VAD treshold; float (recommended values are around 40) + VAD_treshold=40.0; +}; -# preferred path for downloads -download_path:/home/USERNAME/Downloads/; \ No newline at end of file +tox = { + // where to store received files + download_path="/home/USERNAME/Downloads/"; +}; + +sounds = { + error="/usr/local/toxic/sounds/Error.wav"; + self_log_in="/usr/local/toxic/sounds/Log In.wav"; + self_log_out="/usr/local/toxic/sounds/Log Out.wav"; + user_log_in="/usr/local/toxic/sounds/Contact Logs In.wav"; + user_log_out="/usr/local/toxic/sounds/Contact Logs Out.wav"; + call_incoming="/usr/local/toxic/sounds/Incoming Call.wav"; + call_outgoing="/usr/local/toxic/sounds/Outgoing Call.wav"; + generic_message="/usr/local/toxic/sounds/New Message.wav"; + transfer_pending="/usr/local/toxic/sounds/Transfer Pending.wav"; + transfer_completed="/usr/local/toxic/sounds/Transfer Complete.wav"; +}; \ No newline at end of file diff --git a/sounds/Contact Logs In.wav b/sounds/Contact Logs In.wav new file mode 100755 index 0000000..168fca8 Binary files /dev/null and b/sounds/Contact Logs In.wav differ diff --git a/sounds/Contact Logs Out.wav b/sounds/Contact Logs Out.wav new file mode 100755 index 0000000..53c2726 Binary files /dev/null and b/sounds/Contact Logs Out.wav differ diff --git a/sounds/Error.wav b/sounds/Error.wav new file mode 100755 index 0000000..cc35d7c Binary files /dev/null and b/sounds/Error.wav differ diff --git a/sounds/Incoming Call.wav b/sounds/Incoming Call.wav new file mode 100755 index 0000000..f761237 Binary files /dev/null and b/sounds/Incoming Call.wav differ diff --git a/sounds/Log In.wav b/sounds/Log In.wav new file mode 100755 index 0000000..971c21e Binary files /dev/null and b/sounds/Log In.wav differ diff --git a/sounds/Log Out.wav b/sounds/Log Out.wav new file mode 100755 index 0000000..a3c1ccf Binary files /dev/null and b/sounds/Log Out.wav differ diff --git a/sounds/New Message.wav b/sounds/New Message.wav new file mode 100755 index 0000000..f510000 Binary files /dev/null and b/sounds/New Message.wav differ diff --git a/sounds/Outgoing Call.wav b/sounds/Outgoing Call.wav new file mode 100755 index 0000000..de1f7e7 Binary files /dev/null and b/sounds/Outgoing Call.wav differ diff --git a/sounds/Transfer Complete.wav b/sounds/Transfer Complete.wav new file mode 100755 index 0000000..9c35ed5 Binary files /dev/null and b/sounds/Transfer Complete.wav differ diff --git a/sounds/Transfer Pending.wav b/sounds/Transfer Pending.wav new file mode 100755 index 0000000..7b2e49a Binary files /dev/null and b/sounds/Transfer Pending.wav differ diff --git a/sounds/license b/sounds/license new file mode 100644 index 0000000..530d3d7 --- /dev/null +++ b/sounds/license @@ -0,0 +1 @@ +Tox's sounds are licensed under the "Creative Commons Attribution 3.0 Unported", all credit attributed to Adam Reid. diff --git a/src/audio_call.c b/src/audio_call.c index 3007ed6..86209db 100644 --- a/src/audio_call.c +++ b/src/audio_call.c @@ -313,7 +313,7 @@ void callback_peer_timeout ( void* av, int32_t call_index, void* arg ) } void callback_media_change(void* av, int32_t call_index, void* arg) { - /*... TODO cance all media change requests */ + /*... TODO cancel all media change requests */ } /* * End of Callbacks @@ -477,9 +477,10 @@ void cmd_cancel(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[ goto on_error; } +#ifdef _SOUND_NOTIFY stop_sound(self->active_sound); self->active_sound = -1; - +#endif /* _SOUND_NOTIFY */ /* Callback will print status... */ return; diff --git a/src/autocomplete.c b/src/autocomplete.c new file mode 100644 index 0000000..e986e97 --- /dev/null +++ b/src/autocomplete.c @@ -0,0 +1,253 @@ +/* autocomplete.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 . + * + */ + +#include +#include + +#ifdef __APPLE__ + #include + #include +#else + #include +#endif /* ifdef __APPLE__ */ + +#include "windows.h" +#include "toxic.h" +#include "misc_tools.h" +#include "line_info.h" +#include "execute.h" + +static void print_matches(ToxWindow *self, Tox *m, const void *list, int n_items, int size) +{ + if (m) + execute(self->chatwin->history, self, m, "/clear", GLOBAL_COMMAND_MODE); + + const char *L = (char *) list; + int i; + + for (i = 0; i < n_items; ++i) + line_info_add(self, NULL, NULL, NULL, &L[i * size], SYS_MSG, 0, 0); + + line_info_add(self, NULL, NULL, NULL, "", SYS_MSG, 0, 0); /* formatting */ +} + +/* puts match in match buffer. if more than one match, add first n chars that are identical. + e.g. if matches contains: [foo, foobar, foe] we put fo in matches. */ +static void get_str_match(ToxWindow *self, char *match, char (*matches)[MAX_STR_SIZE], int n) +{ + if (n == 1) { + strcpy(match, matches[0]); + return; + } + + int i; + + for (i = 0; i < MAX_STR_SIZE; ++i) { + char ch = matches[0][i]; + int j; + + for (j = 0; j < n; ++j) { + if (matches[j][i] != ch) { + strcpy(match, matches[0]); + match[i] = '\0'; + return; + } + } + } + + strcpy(match, matches[0]); +} + +/* looks for all instances in list that begin with the last entered word in line according to pos, + then fills line with the complete word. e.g. "Hello jo" would complete the line + with "Hello john". If multiple matches, prints out all the matches and semi-completes line. + + list is a pointer to the list of strings being compared, n_items is the number of items + in the list, and size is the size of each item in the list. + + Returns the difference between the old len and new len of line on success, -1 if error */ +int complete_line(ToxWindow *self, const void *list, int n_items, int size) +{ + ChatContext *ctx = self->chatwin; + + if (ctx->pos <= 0 || ctx->len <= 0 || ctx->len >= MAX_STR_SIZE || size > MAX_STR_SIZE) + return -1; + + const char *L = (char *) list; + const char *endchrs = " "; + char ubuf[MAX_STR_SIZE]; + + /* work with multibyte string copy of buf for simplicity */ + if (wcs_to_mbs_buf(ubuf, ctx->line, sizeof(ubuf)) == -1) + return -1; + + bool dir_search = strncmp(ubuf, "/sendfile", strlen("/sendfile")) == 0; + + /* isolate substring from space behind pos to pos */ + char tmp[MAX_STR_SIZE]; + snprintf(tmp, sizeof(tmp), "%s", ubuf); + tmp[ctx->pos] = '\0'; + + const char *s = dir_search ? strchr(tmp, '\"') : strrchr(tmp, ' '); + char *sub = malloc(strlen(ubuf) + 1); + + if (sub == NULL) + exit_toxic_err("failed in complete_line", FATALERR_MEMORY); + + if (!s && !dir_search) { + strcpy(sub, tmp); + + if (sub[0] != '/') + endchrs = ": "; + } else if (s) { + strcpy(sub, &s[1]); + + if (dir_search) { + int sub_len = strlen(sub); + int si = char_rfind(sub, '/', sub_len); + + if (si || *sub == '/') + memmove(sub, &sub[si + 1], sub_len - si); + } + } + + if (string_is_empty(sub)) { + free(sub); + return -1; + } + + int s_len = strlen(sub); + const char *str; + int n_matches = 0; + char matches[n_items][MAX_STR_SIZE]; + int i = 0; + + /* put all list matches in matches array */ + for (i = 0; i < n_items; ++i) { + str = &L[i * size]; + + if (strncasecmp(str, sub, s_len) == 0) + strcpy(matches[n_matches++], str); + } + + free(sub); + + if (!n_matches) + return -1; + + if (!dir_search && n_matches > 1) + print_matches(self, NULL, matches, n_matches, MAX_STR_SIZE); + + char match[MAX_STR_SIZE]; + get_str_match(self, match, matches, n_matches); + + if (dir_search) { + if (n_matches == 1) + endchrs = char_rfind(match, '.', strlen(match)) ? "\"" : "/"; + else + endchrs = ""; + } else if (n_matches > 1) { + endchrs = ""; + } + + /* put match in correct spot in buf and append endchars */ + int n_endchrs = strlen(endchrs); + int m_len = strlen(match); + int strt = ctx->pos - s_len; + int diff = m_len - s_len + n_endchrs; + + if (ctx->len + diff > MAX_STR_SIZE) + return -1; + + char tmpend[MAX_STR_SIZE]; + strcpy(tmpend, &ubuf[ctx->pos]); + strcpy(&ubuf[strt], match); + strcpy(&ubuf[strt + m_len], endchrs); + strcpy(&ubuf[strt + m_len + n_endchrs], tmpend); + + /* convert to widechar and copy back to original buf */ + wchar_t newbuf[MAX_STR_SIZE]; + + if (mbs_to_wcs_buf(newbuf, ubuf, MAX_STR_SIZE) == -1) + return -1; + + wcscpy(ctx->line, newbuf); + + ctx->len += diff; + ctx->pos += diff; + + return diff; +} + +/* matches /sendfile "" line to matching directories. + + if only one match, auto-complete line. + return diff between old len and new len of ctx->line, or -1 if no matches +*/ +#define MAX_DIRS 256 + +int dir_match(ToxWindow *self, Tox *m, const wchar_t *line) +{ + char b_path[MAX_STR_SIZE]; + char b_name[MAX_STR_SIZE]; + + if (wcs_to_mbs_buf(b_path, line, sizeof(b_path)) == -1) + return -1; + + int si = char_rfind(b_path, '/', strlen(b_path)); + + if (!b_path[0]) { /* list everything in pwd */ + strcpy(b_path, "."); + } else if (!si && b_path[0] != '/') { /* look for matches in pwd */ + char tmp[MAX_STR_SIZE]; + snprintf(tmp, sizeof(tmp), ".%s", b_path); + strcpy(b_path, tmp); + } + + strcpy(b_name, &b_path[si + 1]); + b_path[si + 1] = '\0'; + int b_name_len = strlen(b_name); + + DIR *dp = opendir(b_path); + + if (dp == NULL) + return -1; + + char dirnames[MAX_DIRS][NAME_MAX]; + struct dirent *entry; + int dircount = 0; + + while ((entry = readdir(dp)) && dircount < MAX_DIRS) { + if (strncmp(entry->d_name, b_name, b_name_len) == 0) { + snprintf(dirnames[dircount], sizeof(dirnames[dircount]), "%s", entry->d_name); + ++dircount; + } + } + + if (dircount == 0) + return -1; + + if (dircount > 1) + print_matches(self, m, dirnames, dircount, NAME_MAX); + + return complete_line(self, dirnames, dircount, NAME_MAX); +} diff --git a/src/autocomplete.h b/src/autocomplete.h new file mode 100644 index 0000000..a9f6f18 --- /dev/null +++ b/src/autocomplete.h @@ -0,0 +1,43 @@ +/* autocomplete.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 . + * + */ + +#ifndef _autocomplete_h +#define _autocomplete_h + +/* looks for all instances in list that begin with the last entered word in line according to pos, + then fills line with the complete word. e.g. "Hello jo" would complete the line + with "Hello john". If multiple matches, prints out all the matches and semi-completes line. + + list is a pointer to the list of strings being compared, n_items is the number of items + in the list, and size is the size of each item in the list. + + Returns the difference between the old len and new len of line on success, -1 if error */ +int complete_line(ToxWindow *self, const void *list, int n_items, int size); + +/* matches /sendfile "" line to matching directories. + + if only one match, auto-complete line. + return diff between old len and new len of ctx->line, or -1 if no matches +*/ +int dir_match(ToxWindow *self, Tox *m, const wchar_t *line); + +#endif /* #define _autocomplete_h */ \ No newline at end of file diff --git a/src/chat.c b/src/chat.c index d2f52f7..b540036 100644 --- a/src/chat.c +++ b/src/chat.c @@ -20,6 +20,10 @@ * */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE /* needed for wcswidth() */ +#endif + #include #include #include @@ -37,11 +41,13 @@ #include "settings.h" #include "input.h" #include "help.h" - -#ifdef _SUPPORT_AUDIO -#include "audio_call.h" +#include "autocomplete.h" #include "notify.h" -#endif /* _SUPPORT_AUDIO */ + +#ifdef _AUDIO + #include "audio_call.h" +#endif /* _AUDIO */ + extern char *DATA_FILE; @@ -51,16 +57,16 @@ extern ToxicFriend friends[MAX_FRIENDS_NUM]; extern struct _Winthread Winthread; extern struct user_settings *user_settings_; -#ifdef _SUPPORT_AUDIO +#ifdef _AUDIO static void init_infobox(ToxWindow *self); static void kill_infobox(ToxWindow *self); -#endif /* _SUPPORT_AUDIO */ +#endif /* _AUDIO */ -#ifdef _SUPPORT_AUDIO +#ifdef _AUDIO #define AC_NUM_CHAT_COMMANDS 26 #else #define AC_NUM_CHAT_COMMANDS 18 -#endif /* _SUPPORT_AUDIO */ +#endif /* _AUDIO */ /* Array of chat command names used for tab completion. */ static const char chat_cmd_list[AC_NUM_CHAT_COMMANDS][MAX_CMDNAME_SIZE] = { @@ -83,7 +89,7 @@ static const char chat_cmd_list[AC_NUM_CHAT_COMMANDS][MAX_CMDNAME_SIZE] = { { "/sendfile" }, { "/status" }, -#ifdef _SUPPORT_AUDIO +#ifdef _AUDIO { "/call" }, { "/cancel" }, @@ -94,7 +100,7 @@ static const char chat_cmd_list[AC_NUM_CHAT_COMMANDS][MAX_CMDNAME_SIZE] = { { "/mute" }, { "/sense" }, -#endif /* _SUPPORT_AUDIO */ +#endif /* _AUDIO */ }; static void set_typingstatus(ToxWindow *self, Tox *m, uint8_t is_typing) @@ -123,7 +129,7 @@ void kill_chat_window(ToxWindow *self) log_disable(ctx->log); line_info_cleanup(ctx->hst); -#ifdef _SUPPORT_AUDIO +#ifdef _AUDIO stop_current_call(self); #endif @@ -360,7 +366,7 @@ static void chat_onFileControl(ToxWindow *self, Tox *m, int32_t num, uint8_t rec if (receive_send == 1) { snprintf(msg, sizeof(msg), "File transfer for '%s' accepted (%.1f%%)", filename, 0.0); file_senders[i].line_id = self->chatwin->hst->line_end->id + 1; - notify(self, unknown, NT_NOFOCUS | NT_BEEP | NT_WNDALERT_2); + notify(self, silent, NT_NOFOCUS | NT_BEEP | NT_WNDALERT_2); } break; @@ -442,7 +448,7 @@ static void chat_onGroupInvite(ToxWindow *self, Tox *m, int32_t friendnumber, co } /* Av Stuff */ -#ifdef _SUPPORT_AUDIO +#ifdef _AUDIO void chat_onInvite (ToxWindow *self, ToxAv *av, int call_index) { @@ -454,8 +460,10 @@ void chat_onInvite (ToxWindow *self, ToxAv *av, int call_index) self->call_idx = call_index; line_info_add(self, NULL, NULL, NULL, "Incoming audio call! Type: \"/answer\" or \"/reject\"", SYS_MSG, 0, 0); +#ifdef _SOUND_NOTIFY if (self->active_sound == -1) self->active_sound = notify(self, call_incoming, NT_LOOP | NT_WNDALERT_0); +#endif /* _SOUND_NOTIFY */ } void chat_onRinging (ToxWindow *self, ToxAv *av, int call_index) @@ -465,8 +473,10 @@ void chat_onRinging (ToxWindow *self, ToxAv *av, int call_index) line_info_add(self, NULL, NULL, NULL, "Ringing...\"cancel\" ?", SYS_MSG, 0, 0); +#ifdef _SOUND_NOTIFY if (self->active_sound == -1) self->active_sound = notify(self, call_outgoing, NT_LOOP); +#endif /* _SOUND_NOTIFY */ } void chat_onStarting (ToxWindow *self, ToxAv *av, int call_index) @@ -477,8 +487,11 @@ void chat_onStarting (ToxWindow *self, ToxAv *av, int call_index) init_infobox(self); line_info_add(self, NULL, NULL, NULL, "Call started! Type: \"/hangup\" to end it.", SYS_MSG, 0, 0); + +#ifdef _SOUND_NOTIFY stop_sound(self->active_sound); self->active_sound = -1; +#endif /* _SOUND_NOTIFY */ } void chat_onEnding (ToxWindow *self, ToxAv *av, int call_index) @@ -489,8 +502,11 @@ void chat_onEnding (ToxWindow *self, ToxAv *av, int call_index) kill_infobox(self); self->call_idx = -1; line_info_add(self, NULL, NULL, NULL, "Call ended!", SYS_MSG, 0, 0); + +#ifdef _SOUND_NOTIFY stop_sound(self->active_sound); self->active_sound = -1; +#endif /* _SOUND_NOTIFY */ } void chat_onError (ToxWindow *self, ToxAv *av, int call_index) @@ -500,8 +516,11 @@ void chat_onError (ToxWindow *self, ToxAv *av, int call_index) self->call_idx = -1; line_info_add(self, NULL, NULL, NULL, "Error!", SYS_MSG, 0, 0); + +#ifdef _SOUND_NOTIFY stop_sound(self->active_sound); self->active_sound = -1; +#endif /* _SOUND_NOTIFY */ } void chat_onStart (ToxWindow *self, ToxAv *av, int call_index) @@ -512,8 +531,11 @@ void chat_onStart (ToxWindow *self, ToxAv *av, int call_index) init_infobox(self); line_info_add(self, NULL, NULL, NULL, "Call started! Type: \"/hangup\" to end it.", SYS_MSG, 0, 0); + +#ifdef _SOUND_NOTIFY stop_sound(self->active_sound); self->active_sound = -1; +#endif /* _SOUND_NOTIFY */ } void chat_onCancel (ToxWindow *self, ToxAv *av, int call_index) @@ -524,8 +546,11 @@ void chat_onCancel (ToxWindow *self, ToxAv *av, int call_index) kill_infobox(self); self->call_idx = -1; line_info_add(self, NULL, NULL, NULL, "Call canceled!", SYS_MSG, 0, 0); + +#ifdef _SOUND_NOTIFY stop_sound(self->active_sound); self->active_sound = -1; +#endif /* _SOUND_NOTIFY */ } void chat_onReject (ToxWindow *self, ToxAv *av, int call_index) @@ -535,8 +560,11 @@ void chat_onReject (ToxWindow *self, ToxAv *av, int call_index) self->call_idx = -1; line_info_add(self, NULL, NULL, NULL, "Rejected!", SYS_MSG, 0, 0); + +#ifdef _SOUND_NOTIFY stop_sound(self->active_sound); self->active_sound = -1; +#endif /* _SOUND_NOTIFY */ } void chat_onEnd (ToxWindow *self, ToxAv *av, int call_index) @@ -547,8 +575,11 @@ void chat_onEnd (ToxWindow *self, ToxAv *av, int call_index) kill_infobox(self); self->call_idx = -1; line_info_add(self, NULL, NULL, NULL, "Call ended!", SYS_MSG, 0, 0); + +#ifdef _SOUND_NOTIFY stop_sound(self->active_sound); self->active_sound = -1; +#endif /* _SOUND_NOTIFY */ } void chat_onRequestTimeout (ToxWindow *self, ToxAv *av, int call_index) @@ -558,8 +589,11 @@ void chat_onRequestTimeout (ToxWindow *self, ToxAv *av, int call_index) self->call_idx = -1; line_info_add(self, NULL, NULL, NULL, "No answer!", SYS_MSG, 0, 0); + +#ifdef _SOUND_NOTIFY stop_sound(self->active_sound); self->active_sound = -1; +#endif /* _SOUND_NOTIFY */ } void chat_onPeerTimeout (ToxWindow *self, ToxAv *av, int call_index) @@ -570,8 +604,11 @@ void chat_onPeerTimeout (ToxWindow *self, ToxAv *av, int call_index) kill_infobox(self); self->call_idx = -1; line_info_add(self, NULL, NULL, NULL, "Peer disconnected; call ended!", SYS_MSG, 0, 0); + +#ifdef _SOUND_NOTIFY stop_sound(self->active_sound); self->active_sound = -1; +#endif /* _SOUND_NOTIFY */ } static void init_infobox(ToxWindow *self) @@ -656,7 +693,7 @@ static void draw_infobox(ToxWindow *self) wrefresh(infobox->win); } -#endif /* _SUPPORT_AUDIO */ +#endif /* _AUDIO */ static void send_action(ToxWindow *self, ChatContext *ctx, Tox *m, char *action) { @@ -711,20 +748,24 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) input_handle(self, key, x, y, x2, y2); - if (key == '\t') { /* TAB key: auto-completes command */ - if (ctx->len > 1 && ctx->line[0] == '/') { - int diff = complete_line(ctx, chat_cmd_list, AC_NUM_CHAT_COMMANDS, MAX_CMDNAME_SIZE); + if (key == '\t' && ctx->len > 1 && ctx->line[0] == '/') { /* TAB key: auto-complete */ + int diff = -1; + int sf_len = 11; - if (diff != -1) { - if (x + diff > x2 - 1) { - wmove(self->window, y, x + diff); - ctx->start += diff; - } else { - wmove(self->window, y, x + diff); - } - } else notify(self, error, 0); - - } else notify(self, error, 0); + if (wcsncmp(ctx->line, L"/sendfile \"", sf_len) == 0) { + diff = dir_match(self, m, &ctx->line[sf_len]); + } else { + diff = complete_line(self, chat_cmd_list, AC_NUM_CHAT_COMMANDS, MAX_CMDNAME_SIZE); + } + + if (diff != -1) { + if (x + diff > x2 - 1) { + int wlen = wcswidth(ctx->line, sizeof(ctx->line)); + ctx->start = wlen < x2 ? 0 : wlen - x2 + 1; + } + } else { + notify(self, error, 0); + } } else if (key == '\n') { rm_trailing_spaces_buf(ctx); @@ -757,8 +798,7 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) line_info_add(self, timefrmt, selfname, NULL, line, OUT_MSG, 0, 0); if (!statusbar->is_online || tox_send_message(m, self->num, (uint8_t *) line, strlen(line)) == 0) { - char *errmsg = " * Failed to send message."; - line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, RED); + line_info_add(self, NULL, NULL, NULL, " * Failed to send message.", SYS_MSG, 0, RED); } else { write_to_log(line, selfname, ctx->log, false); } @@ -877,12 +917,12 @@ static void chat_onDraw(ToxWindow *self, Tox *m) int y, x; getyx(self->window, y, x); (void) x; - int new_x = ctx->start ? x2 - 1 : ctx->pos; + int new_x = ctx->start ? x2 - 1 : wcswidth(ctx->line, ctx->pos); wmove(self->window, y + 1, new_x); wrefresh(self->window); -#ifdef _SUPPORT_AUDIO +#ifdef _AUDIO if (ctx->infobox.active) { draw_infobox(self); wrefresh(self->window); @@ -933,7 +973,7 @@ static void chat_onInit(ToxWindow *self, Tox *m) line_info_init(ctx->hst); if (friends[self->num].logging_on) - log_enable(self->name, friends[self->num].pub_key, ctx->log); + log_enable(nick, friends[self->num].pub_key, ctx->log); execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE); @@ -964,7 +1004,7 @@ ToxWindow new_chat(Tox *m, int32_t friendnum) ret.onFileControl = &chat_onFileControl; ret.onFileData = &chat_onFileData; -#ifdef _SUPPORT_AUDIO +#ifdef _AUDIO ret.onInvite = &chat_onInvite; ret.onRinging = &chat_onRinging; ret.onStarting = &chat_onStarting; @@ -979,9 +1019,11 @@ ToxWindow new_chat(Tox *m, int32_t friendnum) ret.call_idx = -1; ret.device_selection[0] = ret.device_selection[1] = -1; -#endif /* _SUPPORT_AUDIO */ - +#endif /* _AUDIO */ + +#ifdef _SOUND_NOTIFY ret.active_sound = -1; +#endif /* _SOUND_NOTIFY */ char nick[TOX_MAX_NAME_LENGTH]; int n_len = get_nick_truncate(m, nick, friendnum); diff --git a/src/chat_commands.c b/src/chat_commands.c index 8ea9c79..daf2afd 100644 --- a/src/chat_commands.c +++ b/src/chat_commands.c @@ -57,13 +57,13 @@ void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*a } if (tox_invite_friend(m, self->num, groupnum) == -1) { - errmsg = "Failed to invite friend."; + errmsg = "Failed to invite contact to group."; line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); return; } char msg[MAX_STR_SIZE]; - snprintf(msg, sizeof(msg), "Invited friend to Room #%d.", groupnum); + snprintf(msg, sizeof(msg), "Invited contact to Group %d.", groupnum); line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); } diff --git a/src/chat_commands.h b/src/chat_commands.h index 4b8ffc2..3220545 100644 --- a/src/chat_commands.h +++ b/src/chat_commands.h @@ -31,7 +31,7 @@ void cmd_join_group(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR void cmd_savefile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_sendfile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); -#ifdef _SUPPORT_AUDIO +#ifdef _AUDIO void cmd_call(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_answer(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_reject(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); @@ -40,6 +40,6 @@ void cmd_cancel(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZ void cmd_ccur_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_mute(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_sense(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); -#endif /* _SUPPORT_AUDIO */ +#endif /* _AUDIO */ #endif /* #define _chat_commands_h */ diff --git a/src/device.c b/src/device.c index 9f466b1..54b4174 100644 --- a/src/device.c +++ b/src/device.c @@ -66,7 +66,9 @@ static int size[2]; /* Size of above containers */ Device *running[2][MAX_DEVICES]; /* Running devices */ uint32_t primary_device[2]; /* Primary device */ +#ifdef _AUDIO static ToxAv* av = NULL; +#endif /* _AUDIO */ /* q_mutex */ #define lock pthread_mutex_lock(&mutex) @@ -79,7 +81,11 @@ _Bool thread_running = _True, void* thread_poll(void*); /* Meet devices */ +#ifdef _AUDIO DeviceError init_devices(ToxAv* av_) +#else +DeviceError init_devices() +#endif /* _AUDIO */ { const char *stringed_device_list; @@ -115,7 +121,9 @@ DeviceError init_devices(ToxAv* av_) if ( pthread_create(&thread_id, NULL, thread_poll, NULL) != 0 || pthread_detach(thread_id) != 0) return de_InternalError; +#ifdef _AUDIO av = av_; +#endif /* _AUDIO */ return (DeviceError) ae_None; } @@ -391,8 +399,11 @@ void* thread_poll (void* arg) // TODO: maybe use thread for every input source int16_t frame[4096]; alcCaptureSamples(device->dhndl, frame, f_size); - if ( device->muted || - (device->enable_VAD && !toxav_has_activity(av, device->call_idx, frame, f_size, device->VAD_treshold))) + if ( device->muted + #ifdef _AUDIO + || (device->enable_VAD && !toxav_has_activity(av, device->call_idx, frame, f_size, device->VAD_treshold)) + #endif /* _AUDIO */ + ) { unlock; continue; } /* Skip if no voice activity */ if ( device->cb ) device->cb(frame, f_size, device->cb_data); diff --git a/src/device.h b/src/device.h index 6b368a5..dfda7e3 100644 --- a/src/device.h +++ b/src/device.h @@ -56,7 +56,12 @@ typedef enum DeviceError { typedef void (*DataHandleCallback) (const int16_t*, uint32_t size, void* data); +#ifdef _AUDIO DeviceError init_devices(ToxAv* av); +#else +DeviceError init_devices(); +#endif /* _AUDIO */ + DeviceError terminate_devices(); /* Callback handles ready data from INPUT device */ diff --git a/src/dns.c b/src/dns.c index 09476f8..4331541 100644 --- a/src/dns.c +++ b/src/dns.c @@ -26,9 +26,9 @@ #include #ifdef __APPLE__ -#include + #include #else -#include + #include #endif /* ifdef __APPLE__ */ #include @@ -79,6 +79,7 @@ static struct _thread_data { static struct _dns_thread { pthread_t tid; + pthread_attr_t attr; } dns_thread; @@ -100,7 +101,8 @@ static void kill_dns_thread(void *dns_obj) tox_dns3_kill(dns_obj); memset(&t_data, 0, sizeof(struct _thread_data)); - pthread_exit(0); + pthread_attr_destroy(&dns_thread.attr); + pthread_exit(NULL); } /* puts TXT from dns response in buf. Returns length of TXT on success, -1 on fail.*/ @@ -303,10 +305,12 @@ void dns3_lookup(ToxWindow *self, Tox *m, char *id_bin, char *addr, char *msg) t_data.m = m; t_data.busy = 1; - pthread_mutex_unlock(&Winthread.lock); + if (pthread_attr_init(&dns_thread.attr) != 0) + exit_toxic_err("failed in dns3_lookup", FATALERR_THREAD_ATTR); - if (pthread_create(&dns_thread.tid, NULL, dns3_lookup_thread, NULL) != 0) + if (pthread_attr_setdetachstate(&dns_thread.attr, PTHREAD_CREATE_DETACHED) != 0) + exit_toxic_err("failed in dns3_lookup", FATALERR_THREAD_ATTR); + + if (pthread_create(&dns_thread.tid, &dns_thread.attr, dns3_lookup_thread, NULL) != 0) exit_toxic_err("failed in dns3_lookup", FATALERR_THREAD_CREATE); - - pthread_mutex_lock(&Winthread.lock); } diff --git a/src/execute.c b/src/execute.c index 6c30bed..c35dd24 100644 --- a/src/execute.c +++ b/src/execute.c @@ -54,10 +54,10 @@ static struct cmd_func global_commands[] = { { "/quit", cmd_quit }, { "/status", cmd_status }, -#ifdef _SUPPORT_AUDIO +#ifdef _AUDIO { "/lsdev", cmd_list_devices }, { "/sdev", cmd_change_device }, -#endif /* _SUPPORT_AUDIO */ +#endif /* _AUDIO */ }; static struct cmd_func chat_commands[] = { @@ -66,7 +66,7 @@ static struct cmd_func chat_commands[] = { { "/savefile", cmd_savefile }, { "/sendfile", cmd_sendfile }, -#ifdef _SUPPORT_AUDIO +#ifdef _AUDIO { "/call", cmd_call }, { "/cancel", cmd_cancel }, { "/answer", cmd_answer }, @@ -75,42 +75,49 @@ static struct cmd_func chat_commands[] = { { "/sdev", cmd_ccur_device }, { "/mute", cmd_mute }, { "/sense", cmd_sense }, -#endif /* _SUPPORT_AUDIO */ +#endif /* _AUDIO */ }; /* Parses input command and puts args into arg array. Returns number of arguments on success, -1 on failure. */ -static int parse_command(WINDOW *w, ToxWindow *self, char *cmd, char (*args)[MAX_STR_SIZE]) +static int parse_command(WINDOW *w, ToxWindow *self, const char *input, char (*args)[MAX_STR_SIZE]) { + char *cmd = strdup(input); + + if (cmd == NULL) + exit_toxic_err("failed in parse_command", FATALERR_MEMORY); + int num_args = 0; - bool cmd_end = false; /* flags when we get to the end of cmd */ - char *end; /* points to the end of the current arg */ + int i = 0; /* index of last char in an argument */ /* characters wrapped in double quotes count as one arg */ - while (!cmd_end && num_args < MAX_NUM_ARGS) { - if (*cmd == '\"') { - end = strchr(cmd + 1, '\"'); + while (num_args < MAX_NUM_ARGS) { + int qt_ofst = 0; /* set to 1 to offset index for quote char at end of arg */ - if (end++ == NULL) { /* Increment past the end quote */ + if (*cmd == '\"') { + qt_ofst = 1; + i = char_find(1, cmd, '\"'); + + if (cmd[i] == '\0') { char *errmsg = "Invalid argument. Did you forget a closing \"?"; line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0); + free(cmd); return -1; } - - cmd_end = *end == '\0'; } else { - end = strchr(cmd, ' '); - cmd_end = end == NULL; + i = char_find(0, cmd, ' '); } - if (!cmd_end) - *end++ = '\0'; /* mark end of current argument */ + memcpy(args[num_args], cmd, i + qt_ofst); + args[num_args++][i + qt_ofst] = '\0'; - /* Copy from start of current arg to where we just inserted the null byte */ - strcpy(args[num_args++], cmd); - cmd = end; + if (cmd[i] == '\0') /* no more args */ + break; + + strcpy(cmd, &cmd[i + 1]); } + free(cmd); return num_args; } @@ -130,13 +137,13 @@ static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, int num_ return 1; } -void execute(WINDOW *w, ToxWindow *self, Tox *m, char *cmd, int mode) +void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode) { - if (string_is_empty(cmd)) + if (string_is_empty(input)) return; char args[MAX_NUM_ARGS][MAX_STR_SIZE]; - int num_args = parse_command(w, self, cmd, args); + int num_args = parse_command(w, self, input, args); if (num_args == -1) return; diff --git a/src/execute.h b/src/execute.h index 0e78ef0..9b704bc 100644 --- a/src/execute.h +++ b/src/execute.h @@ -28,13 +28,13 @@ #define MAX_NUM_ARGS 4 /* Includes command */ -#ifdef _SUPPORT_AUDIO +#ifdef _AUDIO #define GLOBAL_NUM_COMMANDS 16 #define CHAT_NUM_COMMANDS 12 #else #define GLOBAL_NUM_COMMANDS 14 #define CHAT_NUM_COMMANDS 4 -#endif /* _SUPPORT_AUDIO */ +#endif /* _AUDIO */ enum { GLOBAL_COMMAND_MODE, @@ -42,6 +42,6 @@ enum { GROUPCHAT_COMMAND_MODE, }; -void execute(WINDOW *w, ToxWindow *self, Tox *m, char *cmd, int mode); +void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode); #endif /* #define _execute_h */ diff --git a/src/friendlist.c b/src/friendlist.c index bcf757d..0e0fe58 100644 --- a/src/friendlist.c +++ b/src/friendlist.c @@ -34,12 +34,13 @@ #include "misc_tools.h" #include "line_info.h" #include "settings.h" - -#ifdef _SUPPORT_AUDIO -#include "audio_call.h" #include "notify.h" + +#ifdef _AUDIO +#include "audio_call.h" #endif + extern char *DATA_FILE; extern ToxWindow *prompt; @@ -383,7 +384,7 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m) wattron(self->window, A_BOLD); wprintw(self->window, " Enter "); wattroff(self->window, A_BOLD); - wprintw(self->window, "key. Delete a friend with the"); + wprintw(self->window, "key. Delete a contact with the"); wattron(self->window, A_BOLD); wprintw(self->window, " Delete "); wattroff(self->window, A_BOLD); @@ -551,7 +552,7 @@ void disable_chatwin(int32_t f_num) friends[f_num].chatwin = -1; } -#ifdef _SUPPORT_AUDIO +#ifdef _AUDIO static void friendlist_onAv(ToxWindow *self, ToxAv *av, int call_index) { int id = toxav_get_peer_id(av, call_index, 0); @@ -582,7 +583,7 @@ static void friendlist_onAv(ToxWindow *self, ToxAv *av, int call_index) } } } -#endif /* _SUPPORT_AUDIO */ +#endif /* _AUDIO */ ToxWindow new_friendlist(void) { @@ -604,7 +605,7 @@ ToxWindow new_friendlist(void) ret.onFileSendRequest = &friendlist_onFileSendRequest; ret.onGroupInvite = &friendlist_onGroupInvite; -#ifdef _SUPPORT_AUDIO +#ifdef _AUDIO ret.onInvite = &friendlist_onAv; ret.onRinging = &friendlist_onAv; ret.onStarting = &friendlist_onAv; @@ -619,10 +620,12 @@ ToxWindow new_friendlist(void) ret.call_idx = -1; ret.device_selection[0] = ret.device_selection[1] = -1; -#endif /* _SUPPORT_AUDIO */ +#endif /* _AUDIO */ +#ifdef _SOUND_NOTIFY ret.active_sound = -1; +#endif /* _SOUND_NOTIFY */ - strcpy(ret.name, "friends"); + strcpy(ret.name, "contacts"); return ret; } diff --git a/src/global_commands.h b/src/global_commands.h index e0e88ba..89f10bc 100644 --- a/src/global_commands.h +++ b/src/global_commands.h @@ -41,9 +41,9 @@ void cmd_status(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZ void cmd_add_helper(ToxWindow *self, Tox *m, char *id_bin, char *msg); -#ifdef _SUPPORT_AUDIO +#ifdef _AUDIO void cmd_list_devices(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_change_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); -#endif /* _SUPPORT_AUDIO */ +#endif /* _AUDIO */ #endif /* #define _global_commands_h */ diff --git a/src/groupchat.c b/src/groupchat.c index 09ef0d2..a7b11db 100644 --- a/src/groupchat.c +++ b/src/groupchat.c @@ -21,7 +21,7 @@ */ #ifndef _GNU_SOURCE -#define _GNU_SOURCE /* needed for strcasestr() */ +#define _GNU_SOURCE /* needed for strcasestr() and wcswidth() */ #endif #include @@ -42,6 +42,7 @@ #include "input.h" #include "help.h" #include "notify.h" +#include "autocomplete.h" extern char *DATA_FILE; @@ -146,7 +147,7 @@ static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, int groupnum, int notify(self, generic_message, NT_WNDALERT_0); nick_clr = RED; } - else notify(self, unknown, NT_WNDALERT_1); + else notify(self, silent, NT_WNDALERT_1); char timefrmt[TIME_STR_SIZE]; get_time_str(timefrmt, sizeof(timefrmt)); @@ -170,7 +171,7 @@ static void groupchat_onGroupAction(ToxWindow *self, Tox *m, int groupnum, int p if (strcasestr(action, selfnick)) { notify(self, generic_message, NT_WNDALERT_0); } - else notify(self, unknown, NT_WNDALERT_1); + else notify(self, silent, NT_WNDALERT_1); char nick[TOX_MAX_NAME_LENGTH]; n_len = tox_group_peername(m, groupnum, peernum, (uint8_t *) nick); @@ -303,7 +304,7 @@ static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnu break; } - notify(self, unknown, NT_WNDALERT_2); + notify(self, silent, NT_WNDALERT_2); } static void send_group_action(ToxWindow *self, ChatContext *ctx, Tox *m, char *action) @@ -351,17 +352,15 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) int diff; if ((ctx->line[0] != '/') || (ctx->line[1] == 'm' && ctx->line[2] == 'e')) - diff = complete_line(ctx, groupchats[self->num].peer_names, + diff = complete_line(self, groupchats[self->num].peer_names, groupchats[self->num].num_peers, TOX_MAX_NAME_LENGTH); else - diff = complete_line(ctx, glob_cmd_list, AC_NUM_GLOB_COMMANDS, MAX_CMDNAME_SIZE); + diff = complete_line(self, glob_cmd_list, AC_NUM_GLOB_COMMANDS, MAX_CMDNAME_SIZE); if (diff != -1) { if (x + diff > x2 - 1) { - wmove(self->window, y, x + diff); - ctx->start += diff; - } else { - wmove(self->window, y, x + diff); + int wlen = wcswidth(ctx->line, sizeof(ctx->line)); + ctx->start = wlen < x2 ? 0 : wlen - x2 + 1; } } else { beep(); @@ -460,7 +459,7 @@ static void groupchat_onDraw(ToxWindow *self, Tox *m) int y, x; getyx(self->window, y, x); (void) x; - int new_x = ctx->start ? x2 - 1 : ctx->pos; + int new_x = ctx->start ? x2 - 1 : wcswidth(ctx->line, ctx->pos); wmove(self->window, y + 1, new_x); wrefresh(self->window); diff --git a/src/help.c b/src/help.c index 75cceb8..cbfdae3 100644 --- a/src/help.c +++ b/src/help.c @@ -131,7 +131,7 @@ static void help_draw_global(ToxWindow *self) wprintw(win, "Global Commands:\n"); wattroff(win, A_BOLD | COLOR_PAIR(RED)); - wprintw(win, " /add : Add friend with optional message\n"); + wprintw(win, " /add : Add contact with optional message\n"); wprintw(win, " /accept : Accept friend request\n"); wprintw(win, " /connect : Manually connect to a DHT node\n"); wprintw(win, " /status : Set status with optional note\n"); @@ -144,14 +144,14 @@ static void help_draw_global(ToxWindow *self) wprintw(win, " /close : Close the current chat window\n"); wprintw(win, " /quit or /exit : Exit Toxic\n"); -#ifdef _SUPPORT_AUDIO +#ifdef _AUDIO wattron(win, A_BOLD); wprintw(win, "\n Audio:\n"); wattroff(win, A_BOLD); wprintw(win, " /lsdev : List devices where type: in|out\n"); wprintw(win, " /sdev : Set active device\n"); -#endif /* _SUPPORT_AUDIO */ +#endif /* _AUDIO */ help_draw_bottom_menu(win); @@ -169,12 +169,12 @@ static void help_draw_chat(ToxWindow *self) wprintw(win, "Chat Commands:\n"); wattroff(win, A_BOLD | COLOR_PAIR(RED)); - wprintw(win, " /invite : Invite friend to a group chat\n"); + wprintw(win, " /invite : Invite contact to a group chat\n"); wprintw(win, " /join : Join a pending group chat\n"); wprintw(win, " /sendfile : Send a file\n"); wprintw(win, " /savefile : Receive a file\n"); -#ifdef _SUPPORT_AUDIO +#ifdef _AUDIO wattron(win, A_BOLD); wprintw(win, "\n Audio:\n"); wattroff(win, A_BOLD); @@ -187,7 +187,7 @@ static void help_draw_chat(ToxWindow *self) wprintw(win, " /sdev : Change active device\n"); wprintw(win, " /mute : Mute active device if in call\n"); wprintw(win, " /sense : VAD sensitivity treshold\n"); -#endif /* _SUPPORT_AUDIO */ +#endif /* _AUDIO */ help_draw_bottom_menu(win); @@ -226,7 +226,7 @@ void help_onKey(ToxWindow *self, wint_t key) break; case 'c': -#ifdef _SUPPORT_AUDIO +#ifdef _AUDIO help_init_window(self, 19, 80); #else help_init_window(self, 9, 80); @@ -235,7 +235,7 @@ void help_onKey(ToxWindow *self, wint_t key) break; case 'g': -#ifdef _SUPPORT_AUDIO +#ifdef _AUDIO help_init_window(self, 21, 80); #else help_init_window(self, 17, 80); diff --git a/src/input.c b/src/input.c index d0b4022..f78685b 100644 --- a/src/input.c +++ b/src/input.c @@ -37,99 +37,87 @@ void input_new_char(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_ { ChatContext *ctx = self->chatwin; - if (ctx->len >= MAX_STR_SIZE - 1) { + int cur_len = wcwidth(key); + + /* this is the only place we need to do this check */ + if (cur_len == -1) { beep(); return; } - int cur_len = wcwidth(key); - - /* this is the only place we need to do this check */ - if (cur_len == -1) + if (add_char_to_buf(ctx, key) == -1) { + beep(); return; - - add_char_to_buf(ctx, key); + } if (x + cur_len >= mx_x) { int s_len = wcwidth(ctx->line[ctx->start]); - int cdiff = cur_len - s_len; - ctx->start += 1 + MAX(0, cdiff); - wmove(self->window, y, wcswidth(&ctx->line[ctx->start], mx_x)); - } else { - wmove(self->window, y, x + cur_len); + ctx->start += 1 + MAX(0, cur_len - s_len); } } /* delete a char via backspace key from input field and buffer */ -static void input_backspace(ToxWindow *self, int x, int y, int mx_x, int mx_y) +static void input_backspace(ToxWindow *self, int x, int mx_x) { ChatContext *ctx = self->chatwin; - if (ctx->pos <= 0) { + if (del_char_buf_bck(ctx) == -1) { beep(); return; } int cur_len = wcwidth(ctx->line[ctx->pos - 1]); - - del_char_buf_bck(ctx); - int s_len = wcwidth(ctx->line[ctx->start - 1]); - int cdiff = s_len - cur_len; - if (ctx->start && (x >= mx_x - cur_len)) { - ctx->start = MAX(0, ctx->start - 1 + cdiff); - wmove(self->window, y, wcswidth(&ctx->line[ctx->start], mx_x)); - } else if (ctx->start && (ctx->pos == ctx->len)) { + if (ctx->start && (x >= mx_x - cur_len)) + ctx->start = MAX(0, ctx->start - 1 + (s_len - cur_len)); + else if (ctx->start && (ctx->pos == ctx->len)) ctx->start = MAX(0, ctx->start - cur_len); - wmove(self->window, y, wcswidth(&ctx->line[ctx->start], mx_x)); - } else if (ctx->start) { + else if (ctx->start) ctx->start = MAX(0, ctx->start - cur_len); - } else { - wmove(self->window, y, x - cur_len); - } } /* delete a char via delete key from input field and buffer */ static void input_delete(ToxWindow *self) { - ChatContext *ctx = self->chatwin; - - if (ctx->pos >= ctx->len) { + if (del_char_buf_frnt(self->chatwin) == -1) beep(); - return; - } - - del_char_buf_frnt(ctx); } /* deletes entire line before cursor from input field and buffer */ -static void input_discard(ToxWindow *self, int mx_y) +static void input_discard(ToxWindow *self) { - ChatContext *ctx = self->chatwin; - - if (ctx->pos <= 0) { + if (discard_buf(self->chatwin) == -1) beep(); - return; - } - - discard_buf(ctx); - wmove(self->window, mx_y - CURS_Y_OFFSET, 0); } /* deletes entire line after cursor from input field and buffer */ static void input_kill(ChatContext *ctx) { - if (ctx->pos != ctx->len) { - kill_buf(ctx); + if (kill_buf(ctx) == -1) + beep(); +} + +static void input_yank(ToxWindow *self, int x, int mx_x) +{ + ChatContext *ctx = self->chatwin; + + if (yank_buf(ctx) == -1) { + beep(); return; } - beep(); + int yank_cols = MAX(0, wcswidth(ctx->yank, ctx->yank_len)); + + if (x + yank_cols >= mx_x) { + int rmdr = MAX(0, (x + yank_cols) - mx_x); + int s_len = wcswidth(&ctx->line[ctx->start], rmdr); + ctx->start += s_len + 1; + } } /* moves cursor/line position to end of line in input field and buffer */ -static void input_mv_end(ToxWindow *self, int x, int y, int mx_x, int mx_y) +static void input_mv_end(ToxWindow *self, int y, int mx_x) { ChatContext *ctx = self->chatwin; @@ -137,90 +125,68 @@ static void input_mv_end(ToxWindow *self, int x, int y, int mx_x, int mx_y) int wlen = wcswidth(ctx->line, sizeof(ctx->line)); ctx->start = MAX(0, 1 + (mx_x * (wlen / mx_x) - mx_x) + (wlen % mx_x)); - - int llen = wcswidth(&ctx->line[ctx->start], mx_x); - int new_x = wlen >= mx_x ? mx_x - 1 : llen; - - wmove(self->window, y, new_x); } /* moves cursor/line position to start of line in input field and buffer */ -static void input_mv_home(ToxWindow *self, int mx_y) +static void input_mv_home(ToxWindow *self) { ChatContext *ctx = self->chatwin; - if (ctx->pos <= 0) { - beep(); + if (ctx->pos <= 0) return; - } ctx->pos = 0; ctx->start = 0; - wmove(self->window, mx_y - CURS_Y_OFFSET, 0); } /* moves cursor/line position left in input field and buffer */ -static void input_mv_left(ToxWindow *self, int x, int y, int mx_x, int mx_y) +static void input_mv_left(ToxWindow *self, int x, int mx_x) { ChatContext *ctx = self->chatwin; - if (ctx->pos <= 0) { - beep(); + if (ctx->pos <= 0) return; - } int cur_len = wcwidth(ctx->line[ctx->pos - 1]); --ctx->pos; int s_len = wcwidth(ctx->line[ctx->start - 1]); - int cdiff = s_len - cur_len; - if (ctx->start && (x >= mx_x - cur_len)) { - ctx->start = MAX(0, ctx->start - 1 + cdiff); - wmove(self->window, y, wcswidth(&ctx->line[ctx->start], mx_x)); - } else if (ctx->start && (ctx->pos == ctx->len)) { + if (ctx->start && (x >= mx_x - cur_len)) + ctx->start = MAX(0, ctx->start - 1 + (s_len - cur_len)); + else if (ctx->start && (ctx->pos == ctx->len)) ctx->start = MAX(0, ctx->start - cur_len); - wmove(self->window, y, wcswidth(&ctx->line[ctx->start], mx_x)); - } else if (ctx->start) { + else if (ctx->start) ctx->start = MAX(0, ctx->start - cur_len); - } else { - wmove(self->window, y, x - cur_len); - } } /* moves cursor/line position right in input field and buffer */ -static void input_mv_right(ToxWindow *self, int x, int y, int mx_x, int mx_y) +static void input_mv_right(ToxWindow *self, int x, int mx_x) { ChatContext *ctx = self->chatwin; - if (ctx->pos >= ctx->len) { - beep(); + if (ctx->pos >= ctx->len) return; - } ++ctx->pos; - int cur_len = MAX(1, wcwidth(ctx->line[ctx->pos - 1])); + int cur_len = wcwidth(ctx->line[ctx->pos - 1]); if (x + cur_len >= mx_x) { int s_len = wcwidth(ctx->line[ctx->start]); - int cdiff = cur_len - s_len; - ctx->start += 1 + MAX(0, cdiff); - wmove(self->window, y, wcswidth(&ctx->line[ctx->start], mx_x)); - } else { - wmove(self->window, y, x + cur_len); + ctx->start += 1 + MAX(0, cur_len - s_len); } } /* puts a line history item in input field and buffer */ -static void input_history(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y) +static void input_history(ToxWindow *self, wint_t key, int mx_x) { ChatContext *ctx = self->chatwin; fetch_hist_item(ctx, key); - ctx->start = mx_x * (ctx->len / mx_x); - input_mv_end(self, x, y, mx_x, mx_y); + int wlen = wcswidth(ctx->line, sizeof(ctx->line)); + ctx->start = wlen < mx_x ? 0 : wlen - mx_x + 1; } /* Handles non-printable input keys that behave the same for all types of chat windows. @@ -232,7 +198,7 @@ bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y) switch (key) { case 0x7f: case KEY_BACKSPACE: - input_backspace(self, x, y, mx_x, mx_y); + input_backspace(self, x, mx_x); break; case KEY_DC: @@ -240,34 +206,38 @@ bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y) break; case T_KEY_DISCARD: - input_discard(self, mx_y); + input_discard(self); break; case T_KEY_KILL: input_kill(self->chatwin); break; + case T_KEY_C_Y: + input_yank(self, x, mx_x); + break; + case KEY_HOME: case T_KEY_C_A: - input_mv_home(self, mx_y); + input_mv_home(self); break; case KEY_END: case T_KEY_C_E: - input_mv_end(self, x, y, mx_x, mx_y); + input_mv_end(self, y, mx_x); break; case KEY_LEFT: - input_mv_left(self, x, y, mx_x, mx_y); + input_mv_left(self, x, mx_x); break; case KEY_RIGHT: - input_mv_right(self, x, y, mx_x, mx_y); + input_mv_right(self, x, mx_x); break; case KEY_UP: case KEY_DOWN: - input_history(self, key, x, y, mx_x, mx_y); + input_history(self, key, mx_x); break; default: diff --git a/src/misc_tools.c b/src/misc_tools.c index 3c530ae..9d31c61 100644 --- a/src/misc_tools.c +++ b/src/misc_tools.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "toxic.h" #include "windows.h" @@ -108,7 +109,7 @@ char *hex_string_to_bin(const char *hex_string) } /* Returns 1 if the string is empty, 0 otherwise */ -int string_is_empty(char *string) +int string_is_empty(const char *string) { return string[0] == '\0'; } @@ -174,23 +175,29 @@ int valid_nick(char *nick) void get_file_name(char *namebuf, int bufsize, const char *pathname) { int idx = strlen(pathname) - 1; + char *path = strdup(pathname); - char tmpname[MAX_STR_SIZE]; - snprintf(tmpname, sizeof(tmpname), "%s", pathname); + if (path == NULL) + exit_toxic_err("failed in get_file_name", FATALERR_MEMORY); while (idx >= 0 && pathname[idx] == '/') - tmpname[idx--] = '\0'; + path[idx--] = '\0'; - char *filename = strrchr(tmpname, '/'); + char *finalname = strdup(path); - if (filename != NULL) { - if (!strlen(++filename)) - filename = tmpname; - } else { - filename = tmpname; + if (finalname == NULL) + exit_toxic_err("failed in get_file_name", FATALERR_MEMORY); + + const char *basenm = strrchr(path, '/'); + + if (basenm != NULL) { + if (basenm[1]) + strcpy(finalname, &basenm[1]); } - snprintf(namebuf, bufsize, "%s", filename); + snprintf(namebuf, bufsize, "%s", finalname); + free(finalname); + free(path); } /* converts str to all lowercase */ @@ -210,4 +217,32 @@ int get_nick_truncate(Tox *m, char *buf, int friendnum) len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1); buf[len] = '\0'; return len; +} + +/* returns index of the first instance of ch in s starting at idx. + returns length of s if char not found */ +int char_find(int idx, const char *s, char ch) +{ + int i = idx; + + for (i = idx; s[i]; ++i) { + if (s[i] == ch) + break; + } + + return i; +} + +/* returns index of the last instance of ch in s starting at len + returns 0 if char not found (skips 0th index) */ +int char_rfind(const char *s, char ch, int len) +{ + int i = 0; + + for (i = len; i > 0; --i) { + if (s[i] == ch) + break; + } + + return i; } \ No newline at end of file diff --git a/src/misc_tools.h b/src/misc_tools.h index 72a39d4..37894de 100644 --- a/src/misc_tools.h +++ b/src/misc_tools.h @@ -52,7 +52,7 @@ struct tm *get_time(void); void update_unix_time(void); /* Returns 1 if the string is empty, 0 otherwise */ -int string_is_empty(char *string); +int string_is_empty(const char *string); /* convert a multibyte string to a wide character string (must provide buffer) */ int char_to_wcs_buf(wchar_t *buf, const char *string, size_t n); @@ -89,4 +89,12 @@ void str_to_lower(char *str); Returns nick len on success, -1 on failure */ int get_nick_truncate(Tox *m, char *buf, int friendnum); +/* returns index of the first instance of ch in s starting at idx. + returns length of s if char not found */ +int char_find(int idx, const char *s, char ch); + +/* returns index of the last instance of ch in s + returns 0 if char not found */ +int char_rfind(const char *s, char ch, int len); + #endif /* #define _misc_tools_h */ diff --git a/src/notify.c b/src/notify.c new file mode 100644 index 0000000..0f98fe1 --- /dev/null +++ b/src/notify.c @@ -0,0 +1,333 @@ +/* notify.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 . + * + */ + +#include "notify.h" +#include "device.h" +#include "settings.h" + +#include +#include +#include +#include +#include + +#ifdef __APPLE__ + #include + #include + #ifdef _SOUND_NOTIFY + #include /* Is this good? */ + #endif +#else + #include + #include + #ifdef _SOUND_NOTIFY + #include /* freealut packet */ + #endif +#endif + +#ifdef _X11 + #include +#endif /* _X11 */ + +#define SOUNDS_SIZE 10 +#define ACTIVE_SOUNDS_MAX 50 + +extern struct user_settings *user_settings_; + +struct _Control { + time_t cooldown; + unsigned long this_window; +#ifdef _X11 + Display *display; +#endif /* _X11 */ + +#ifdef _SOUND_NOTIFY + pthread_mutex_t poll_mutex[1]; + uint32_t device_idx; /* index of output device */ + _Bool poll_active; + char* sounds[SOUNDS_SIZE]; +#endif /* _SOUND_NOTIFY */ +} Control = {0}; + +#ifdef _SOUND_NOTIFY +struct _ActiveSounds { + uint32_t source; + uint32_t buffer; + _Bool active; + _Bool looping; +} actives[ACTIVE_SOUNDS_MAX] = {0}; +#endif +/**********************************************************************************/ +/**********************************************************************************/ +/**********************************************************************************/ +/**********************************************************************************/ +/**********************************************************************************/ + +long unsigned int get_focused_window_id() +{ +#ifdef _X11 + if (!Control.display) return 0; + + Window focus; + int revert; + XGetInputFocus(Control.display, &focus, &revert); + return focus; +#else + return 0; +#endif /* _X11 */ +} + +#ifdef _SOUND_NOTIFY + +_Bool is_playing(int source) +{ + int ready; + alGetSourcei(source, AL_SOURCE_STATE, &ready); + return ready == AL_PLAYING; +} + +/* Terminate all sounds but wait them to finish first */ +void graceful_clear() +{ + int i; + pthread_mutex_lock(Control.poll_mutex); + while (1) { + for (i = 0; i < ACTIVE_SOUNDS_MAX; i ++) { + if (actives[i].active) { + if ( actives[i].looping ) { + stop_sound(i); + } else { + if (!is_playing(actives[i].source)) + memset(&actives[i], 0, sizeof(struct _ActiveSounds)); + else break; + } + } + + } + + if (i == ACTIVE_SOUNDS_MAX) { + pthread_mutex_unlock(Control.poll_mutex); + return; + } + + usleep(1000); + } +} + +void* do_playing(void* _p) +{ + (void)_p; + int i; + while(Control.poll_active) { + pthread_mutex_lock(Control.poll_mutex); + for (i = 0; i < ACTIVE_SOUNDS_MAX; i ++) { + if (actives[i].active && !actives[i].looping) { + if (!is_playing(actives[i].source)) { + /* Close */ + + alSourceStop(actives[i].source); + alDeleteSources(1, &actives[i].source); + alDeleteBuffers(1,&actives[i].buffer); + memset(&actives[i], 0, sizeof(struct _ActiveSounds)); + } + } + } + pthread_mutex_unlock(Control.poll_mutex); + usleep(10000); + } +} + + +int play_source(uint32_t source, uint32_t buffer, _Bool looping) +{ + pthread_mutex_lock(Control.poll_mutex); + int i = 0; + for (; i < ACTIVE_SOUNDS_MAX && actives[i].active; i ++); + if ( i == ACTIVE_SOUNDS_MAX ) { + pthread_mutex_unlock(Control.poll_mutex); + return -1; /* Full */ + } + + alSourcePlay(source); + + actives[i].active = 1; + actives[i].source = source; + actives[i].buffer = buffer; + actives[i].looping = looping; + + pthread_mutex_unlock(Control.poll_mutex); + return i; +} +#endif +/**********************************************************************************/ +/**********************************************************************************/ +/**********************************************************************************/ +/**********************************************************************************/ +/**********************************************************************************/ + + + +/* Opens primary device */ +int init_notify(int login_cooldown) +{ +#ifdef _SOUND_NOTIFY + if (open_primary_device(output, &Control.device_idx) != de_None) + return -1; + + pthread_mutex_init(Control.poll_mutex, NULL); + pthread_t thread; + if (pthread_create(&thread, NULL, do_playing, NULL) != 0 || pthread_detach(thread) != 0 ) { + pthread_mutex_destroy(Control.poll_mutex); + return -1; + } + Control.poll_active = 1; +#endif /* _SOUND_NOTIFY */ + + Control.cooldown = time(NULL) + login_cooldown; +#ifdef _X11 + Control.display = XOpenDisplay(NULL); + Control.this_window = get_focused_window_id(); +#else + Control.this_window = 1; +#endif /* _X11 */ + + + return 1; +} + +void terminate_notify() +{ +#ifdef _SOUND_NOTIFY + if ( !Control.poll_active ) return; + Control.poll_active = 0; + + int i = 0; + for (; i < SOUNDS_SIZE; i ++) free(Control.sounds[i]); + + graceful_clear(); + close_device(output, Control.device_idx); +#endif /* _SOUND_NOTIFY */ +} + +#ifdef _SOUND_NOTIFY +void set_sound(Notification sound, const char* value) +{ + free(Control.sounds[sound]); + + size_t len = strlen(value) + 1; + Control.sounds[sound] = calloc(1, len); + memcpy(Control.sounds[sound], value, len); +} + +int play_sound_internal(Notification what, _Bool loop) +{ + char* data; + int format; + int clockrate; + int buffer_size; + char loop_; + uint32_t source; + uint32_t buffer; + + alutLoadWAVFile(Control.sounds[what], &format, (void**)&data, &buffer_size, &clockrate, &loop_); + alGenSources(1, &source); + alGenBuffers(1, &buffer); + alBufferData(buffer, format, data, buffer_size, clockrate); + alSourcei(source, AL_BUFFER, buffer); + alSourcei(source, AL_LOOPING, loop); + alutUnloadWAV(format, data, buffer_size, clockrate); + + int rc = play_source(source, buffer, loop); + if (rc < 0) { + alSourceStop(source); + alDeleteSources(1, &source); + alDeleteBuffers(1,&buffer); + return -1; + } + + return rc; +} + +int play_notify_sound(Notification notif, uint64_t flags) +{ + int rc = 0; + + if (flags & NT_BEEP) beep(); + else if (notif != silent) { + if ( !Control.poll_active || (flags & NT_RESTOL && Control.cooldown > time(NULL)) || !Control.sounds[notif] ) + return -1; + + rc = play_sound_internal(notif, flags & NT_LOOP ? 1 : 0); + } + + return rc; +} + + +void stop_sound(int sound) +{ + if (sound >= 0 && sound < ACTIVE_SOUNDS_MAX && actives[sound].looping && actives[sound].active) { + alSourcei(actives[sound].source, AL_LOOPING, false); + alSourceStop(actives[sound].source); + alDeleteSources(1, &actives[sound].source); + alDeleteBuffers(1,&actives[sound].buffer); + memset(&actives[sound], 0, sizeof(struct _ActiveSounds)); + } +} +#endif + +static int m_play_sound(Notification notif, uint64_t flags) +{ +#ifdef _SOUND_NOTIFY + return play_notify_sound(notif, flags); + #else + beep(); + return -1; +#endif /* _SOUND_NOTIFY */ + +} + +int notify(ToxWindow* self, Notification notif, uint64_t flags) +{ + if (flags & NT_NOFOCUS && Control.this_window == get_focused_window_id()) + return -1; + + int rc = -1; + + if (self && (!self->stb || self->stb->status != TOX_USERSTATUS_BUSY) && user_settings_->alerts == ALERTS_ENABLED) + rc = m_play_sound(notif, flags); + + else if (flags & NT_ALWAYS) + rc = m_play_sound(notif, flags); + + if (flags & NT_NOTIFWND) { + /* TODO: pop notify window */ + } + + if (self && self->alert == WINDOW_ALERT_NONE) { + if (flags & NT_WNDALERT_0) self->alert = WINDOW_ALERT_0; + else if (flags & NT_WNDALERT_1) self->alert = WINDOW_ALERT_1; + else if (flags & NT_WNDALERT_2) self->alert = WINDOW_ALERT_2; + } + + return rc; +} \ No newline at end of file diff --git a/src/notify.h b/src/notify.h new file mode 100644 index 0000000..1a3089c --- /dev/null +++ b/src/notify.h @@ -0,0 +1,73 @@ +/* notify.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 . + * + */ + +#ifndef _notify_h +#define _notify_h + +#include +#include "windows.h" + +typedef enum _Notification +{ + silent = -1, + error, + self_log_in, + self_log_out, + user_log_in, + user_log_out, + call_incoming, + call_outgoing, + generic_message, + transfer_pending, + transfer_completed, +} Notification; + +typedef enum _Flags { + NT_NOFOCUS = 1 << 0, /* Notify when focus is not on this terminal. NOTE: only works with x11, + * if no x11 present this flag is ignored + */ + NT_BEEP = 1 << 1, /* Play native sound instead: \a */ + NT_LOOP = 1 << 2, /* Loop sound. If this setting active, notify() will return id of the sound + * so it could be stopped. It will return 0 if error or NT_NATIVE flag is set and play \a instead + */ + NT_RESTOL = 1 << 3, /* Respect tolerance. Usually used to stop flood at toxic startup + * Only works if login_cooldown is true when calling init_notify() + */ + NT_NOTIFWND = 1 << 4, /* Pop notify window. NOTE: only works(/WILL WORK) if libnotify is present */ + NT_WNDALERT_0 = 1 << 5, /* Alert toxic */ + NT_WNDALERT_1 = 1 << 6, /* Alert toxic */ + NT_WNDALERT_2 = 1 << 7, /* Alert toxic */ + + NT_ALWAYS = 1 << 8, /* Force sound to play */ +} Flags; + +int init_notify(int login_cooldown); +void terminate_notify(); + +int notify(ToxWindow* self, Notification notif, uint64_t flags); + +#ifdef _SOUND_NOTIFY +void set_sound(Notification sound, const char* value); +void stop_sound(int sound); +#endif /* _SOUND_NOTIFY */ + +#endif /* _notify_h */ \ No newline at end of file diff --git a/src/prompt.c b/src/prompt.c index 0d799cb..0fdca1b 100644 --- a/src/prompt.c +++ b/src/prompt.c @@ -20,6 +20,10 @@ * */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE /* needed for wcswidth() */ +#endif + #include #include #include @@ -36,6 +40,7 @@ #include "input.h" #include "help.h" #include "notify.h" +#include "autocomplete.h" char pending_frnd_requests[MAX_FRIENDS_NUM][TOX_CLIENT_ID_SIZE]; uint16_t num_frnd_requests = 0; @@ -62,15 +67,16 @@ const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = { { "/quit" }, { "/status" }, -#ifdef _SUPPORT_AUDIO +#ifdef _AUDIO { "/lsdev" }, { "/sdev" }, -#endif /* _SUPPORT_AUDIO */ +#endif /* _AUDIO */ }; -void kill_prompt_window(ToxWindow *self) { +void kill_prompt_window(ToxWindow *self) +{ ChatContext *ctx = self->chatwin; StatusBar *statusbar = self->stb; @@ -172,14 +178,12 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) if (key == '\t') { /* TAB key: auto-completes command */ if (ctx->len > 1 && ctx->line[0] == '/') { - int diff = complete_line(ctx, glob_cmd_list, AC_NUM_GLOB_COMMANDS, MAX_CMDNAME_SIZE); + int diff = complete_line(self, glob_cmd_list, AC_NUM_GLOB_COMMANDS, MAX_CMDNAME_SIZE); if (diff != -1) { if (x + diff > x2 - 1) { - wmove(self->window, y, x + diff); - ctx->start += diff; - } else { - wmove(self->window, y, x + diff); + int wlen = wcswidth(ctx->line, sizeof(ctx->line)); + ctx->start = wlen < x2 ? 0 : wlen - x2 + 1; } } else { beep(); @@ -275,7 +279,7 @@ static void prompt_onDraw(ToxWindow *self, Tox *m) getyx(self->window, y, x); (void) x; - int new_x = ctx->start ? x2 - 1 : ctx->pos; + int new_x = ctx->start ? x2 - 1 : wcswidth(ctx->line, ctx->pos); wmove(self->window, y + 1, new_x); wrefresh(self->window); @@ -444,7 +448,7 @@ ToxWindow new_prompt(void) ret.onConnectionChange = &prompt_onConnectionChange; ret.onFriendRequest = &prompt_onFriendRequest; - strcpy(ret.name, "prompt"); + strcpy(ret.name, "home"); ChatContext *chatwin = calloc(1, sizeof(ChatContext)); StatusBar *stb = calloc(1, sizeof(StatusBar)); diff --git a/src/prompt.h b/src/prompt.h index 93de1e5..b2b3620 100644 --- a/src/prompt.h +++ b/src/prompt.h @@ -26,11 +26,11 @@ #include "toxic.h" #include "windows.h" -#ifdef _SUPPORT_AUDIO +#ifdef _AUDIO #define AC_NUM_GLOB_COMMANDS 17 #else #define AC_NUM_GLOB_COMMANDS 15 -#endif /* _SUPPORT_AUDIO */ +#endif /* _AUDIO */ ToxWindow new_prompt(void); void prep_prompt_win(void); diff --git a/src/settings.c b/src/settings.c index 84a0a6d..11e7897 100644 --- a/src/settings.c +++ b/src/settings.c @@ -29,9 +29,9 @@ #include "configdir.h" #include "notify.h" -#ifdef _SUPPORT_AUDIO +#ifdef _AUDIO #include "device.h" -#endif /* _SUPPORT_AUDIO */ +#endif /* _AUDIO */ #include "settings.h" #include "line_info.h" @@ -76,7 +76,7 @@ static void tox_defaults(struct user_settings* settings) /*settings->download_path;*/ /* TODO: Set this? */ } -#ifdef _SUPPORT_AUDIO +#ifdef _AUDIO const struct _audio_strings { const char* self; const char* input_device; @@ -96,7 +96,7 @@ static void audio_defaults(struct user_settings* settings) } #endif -#ifdef _ENABLE_SOUND_NOTIFY +#ifdef _SOUND_NOTIFY const struct _sound_strings { const char* self; const char* error; @@ -133,7 +133,7 @@ int settings_load(struct user_settings *s, char *path) /* Load default settings */ ui_defaults(s); tox_defaults(s); -#ifdef _SUPPORT_AUDIO +#ifdef _AUDIO audio_defaults(s); #endif @@ -164,7 +164,7 @@ int settings_load(struct user_settings *s, char *path) } } -#ifdef _SUPPORT_AUDIO +#ifdef _AUDIO if ((setting = config_lookup(cfg, audio_strings.self)) != NULL) { config_setting_lookup_int(setting, audio_strings.input_device, &s->audio_in_dev); s->audio_in_dev = s->audio_in_dev < 0 || s->audio_in_dev > MAX_DEVICES ? 0 : s->audio_in_dev; @@ -176,7 +176,7 @@ int settings_load(struct user_settings *s, char *path) } #endif -#ifdef _ENABLE_SOUND_NOTIFY +#ifdef _SOUND_NOTIFY if ((setting = config_lookup(cfg, sound_strings.self)) != NULL) { if ( config_setting_lookup_string(setting, sound_strings.error, &str) == CONFIG_TRUE ) set_sound(error, str); diff --git a/src/settings.h b/src/settings.h index 25b4e3d..cd5243e 100644 --- a/src/settings.h +++ b/src/settings.h @@ -33,7 +33,7 @@ struct user_settings { int history_size; /* int between MIN_HISTORY and MAX_HISTORY */ char* download_path; -#ifdef _SUPPORT_AUDIO +#ifdef _AUDIO int audio_in_dev; int audio_out_dev; double VAD_treshold; diff --git a/src/toxic.c b/src/toxic.c index 675248e..269e68f 100644 --- a/src/toxic.c +++ b/src/toxic.c @@ -51,18 +51,19 @@ #include "settings.h" #include "log.h" #include "notify.h" +#include "device.h" -#ifdef _SUPPORT_AUDIO +#ifdef _AUDIO #include "audio_call.h" -#endif /* _SUPPORT_AUDIO */ +#endif /* _AUDIO */ #ifndef PACKAGE_DATADIR #define PACKAGE_DATADIR "." #endif -#ifdef _SUPPORT_AUDIO +#ifdef _AUDIO ToxAv *av; -#endif /* _SUPPORT_AUDIO */ +#endif /* _AUDIO */ /* Export for use in Callbacks */ char *DATA_FILE = NULL; @@ -102,9 +103,9 @@ void exit_toxic_success(Tox *m) notify(NULL, self_log_out, NT_ALWAYS); terminate_notify(); -#ifdef _SUPPORT_AUDIO +#ifdef _AUDIO terminate_audio(); -#endif /* _SUPPORT_AUDIO */ +#endif /* _AUDIO */ tox_kill(m); endwin(); exit(EXIT_SUCCESS); @@ -324,7 +325,7 @@ int init_connection(Tox *m) return 4; } -#define TRY_CONNECT 10 +#define TRY_CONNECT 10 /* Seconds between connection attempts when DHT is not connected */ static void do_connection(Tox *m, ToxWindow *prompt) { @@ -372,7 +373,7 @@ static void load_friendlist(Tox *m) /* * Store Messenger to given location - * Return 0 stored successfully + * Return 0 stored successfully or ignoring data file * Return 1 file path is NULL * Return 2 malloc failed * Return 3 opening path failed @@ -386,19 +387,15 @@ int store_data(Tox *m, char *path) if (path == NULL) return 1; - FILE *fd; - int len; - char *buf; - - len = tox_size(m); - buf = malloc(len); + int len = tox_size(m); + char *buf = malloc(len); if (buf == NULL) return 2; tox_save(m, (uint8_t *) buf); - fd = fopen(path, "wb"); + FILE *fd = fopen(path, "wb"); if (fd == NULL) { free(buf); @@ -422,15 +419,13 @@ static void load_data(Tox *m, char *path) return; FILE *fd; - int len; - char *buf; if ((fd = fopen(path, "rb")) != NULL) { fseek(fd, 0, SEEK_END); - len = ftell(fd); + int len = ftell(fd); fseek(fd, 0, SEEK_SET); - buf = malloc(len); + char *buf = malloc(len); if (buf == NULL) { fclose(fd); @@ -449,9 +444,7 @@ static void load_data(Tox *m, char *path) free(buf); fclose(fd); } else { - int st; - - if ((st = store_data(m, path)) != 0) + if (store_data(m, path) != 0) exit_toxic_err("failed in load_data", FATALERR_STORE_DATA); } } @@ -531,6 +524,10 @@ static void parse_args(int argc, char *argv[]) switch (opt) { case 'f': DATA_FILE = strdup(optarg); + + if (DATA_FILE == NULL) + exit_toxic_err("failed in parse_args", FATALERR_MEMORY); + break; case 'x': @@ -578,6 +575,9 @@ int main(int argc, char *argv[]) if (DATA_FILE == NULL ) { if (config_err) { DATA_FILE = strdup("data"); + + if (DATA_FILE == NULL) + exit_toxic_err("failed in main", FATALERR_MEMORY); } else { DATA_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen("data") + 1); @@ -621,15 +621,17 @@ int main(int argc, char *argv[]) char *msg; -#ifdef _SUPPORT_AUDIO +#ifdef _AUDIO av = init_audio(prompt, m); set_primary_device(input, user_settings_->audio_in_dev); set_primary_device(output, user_settings_->audio_out_dev); - -#endif /* _SUPPORT_AUDIO */ +#elif _SOUND_NOTIFY + if ( init_devices() == de_InternalError ) + line_info_add(prompt, NULL, NULL, NULL, "Failed to init devices", SYS_MSG, 0, 0); +#endif /* _AUDIO */ init_notify(60); notify(prompt, self_log_in, 0); @@ -647,7 +649,7 @@ int main(int argc, char *argv[]) sort_friendlist_index(); prompt_init_statusbar(prompt, m); - uint64_t last_save = get_unix_time(); + uint64_t last_save = (uint64_t) time(NULL); while (true) { update_unix_time(); diff --git a/src/toxic.h b/src/toxic.h index efb6cd4..fa5dc4a 100644 --- a/src/toxic.h +++ b/src/toxic.h @@ -42,7 +42,7 @@ #define UNKNOWN_NAME "Anonymous" -#define MAX_FRIENDS_NUM 500 +#define MAX_FRIENDS_NUM 999 #define MAX_STR_SIZE TOX_MAX_MESSAGE_LENGTH #define MAX_CMDNAME_SIZE 64 #define TOXIC_MAX_NAME_LENGTH 32 /* Must be <= TOX_MAX_NAME_LENGTH */ @@ -62,17 +62,19 @@ #define T_KEY_C_V 0x16 /* ctrl-v */ #define T_KEY_C_F 0x06 /* ctrl-f */ #define T_KEY_C_H 0x08 /* ctrl-h */ +#define T_KEY_C_Y 0x19 /* ctrl-y */ typedef enum _FATAL_ERRS { - FATALERR_MEMORY = -1, /* malloc() or calloc() failed */ - FATALERR_FREAD = -2, /* fread() failed on critical read */ - FATALERR_THREAD_CREATE = -3, - FATALERR_MUTEX_INIT = -4, - FATALERR_LOCALE_SET = -5, - FATALERR_STORE_DATA = -6, - FATALERR_NETWORKINIT = -7, /* Tox network failed to init */ - FATALERR_INFLOOP = -8, /* infinite loop detected */ - FATALERR_WININIT = -9, /* window init failed */ + FATALERR_MEMORY = -1, /* malloc() or calloc() failed */ + FATALERR_FREAD = -2, /* fread() failed on critical read */ + FATALERR_THREAD_CREATE = -3, /* thread creation failed */ + FATALERR_MUTEX_INIT = -4, /* mutex init failed */ + FATALERR_THREAD_ATTR = -5, /* thread attr object init failed */ + FATALERR_LOCALE_SET = -6, /* system locale not set */ + FATALERR_STORE_DATA = -7, /* store_data failed in critical section */ + FATALERR_NETWORKINIT = -8, /* Tox network failed to init */ + FATALERR_INFLOOP = -9, /* infinite loop detected */ + FATALERR_WININIT = -10, /* window init failed */ } FATAL_ERRS; /* Fixes text color problem on some terminals. diff --git a/src/toxic_strings.c b/src/toxic_strings.c index fb6d28a..932a0c8 100644 --- a/src/toxic_strings.c +++ b/src/toxic_strings.c @@ -29,59 +29,98 @@ #include "misc_tools.h" #include "toxic_strings.h" -/* Adds char to line at pos */ -void add_char_to_buf(ChatContext *ctx, wint_t ch) +/* Adds char to line at pos. Return 0 on success, -1 if line buffer is full */ +int add_char_to_buf(ChatContext *ctx, wint_t ch) { - if (ctx->len >= MAX_STR_SIZE) - return; + if (ctx->len >= MAX_STR_SIZE - 1) + return -1; wmemmove(&ctx->line[ctx->pos + 1], &ctx->line[ctx->pos], ctx->len - ctx->pos); ctx->line[ctx->pos++] = ch; ctx->line[++ctx->len] = L'\0'; + + return 0; } -/* Deletes the character before pos */ -void del_char_buf_bck(ChatContext *ctx) +/* Deletes the character before pos. Return 0 on success, -1 if nothing to delete */ +int del_char_buf_bck(ChatContext *ctx) { - if (ctx->pos == 0) - return; + if (ctx->pos <= 0) + return -1; wmemmove(&ctx->line[ctx->pos - 1], &ctx->line[ctx->pos], ctx->len - ctx->pos); --ctx->pos; ctx->line[--ctx->len] = L'\0'; + + return 0; } -/* Deletes the character at pos */ -void del_char_buf_frnt(ChatContext *ctx) +/* Deletes the character at pos. Return 0 on success, -1 if nothing to delete. */ +int del_char_buf_frnt(ChatContext *ctx) { if (ctx->pos >= ctx->len) - return; + return -1; wmemmove(&ctx->line[ctx->pos], &ctx->line[ctx->pos + 1], ctx->len - ctx->pos - 1); ctx->line[--ctx->len] = L'\0'; + + return 0; } -/* Deletes the line from beginning to pos */ -void discard_buf(ChatContext *ctx) +/* Deletes the line from beginning to pos and puts discarded portion in yank buffer. + Return 0 on success, -1 if noting to discard. */ +int discard_buf(ChatContext *ctx) { if (ctx->pos <= 0) - return; + return -1; + + ctx->yank_len = ctx->pos; + wmemcpy(ctx->yank, ctx->line, ctx->yank_len); + ctx->yank[ctx->yank_len] = L'\0'; wmemmove(ctx->line, &ctx->line[ctx->pos], ctx->len - ctx->pos); ctx->len -= ctx->pos; ctx->pos = 0; ctx->start = 0; ctx->line[ctx->len] = L'\0'; + + return 0; } -/* Deletes the line from pos to len */ -void kill_buf(ChatContext *ctx) +/* Deletes the line from pos to len and puts killed portion in yank buffer. + Return 0 on success, -1 if nothing to kill. */ +int kill_buf(ChatContext *ctx) { - if (ctx->len == ctx->pos) - return; + if (ctx->len <= ctx->pos) + return -1; + + ctx->yank_len = ctx->len - ctx->pos; + wmemcpy(ctx->yank, &ctx->line[ctx->pos], ctx->yank_len); + ctx->yank[ctx->yank_len] = L'\0'; ctx->line[ctx->pos] = L'\0'; ctx->len = ctx->pos; + + return 0; +} + +/* Inserts string in ctx->yank into line at pos. + Return 0 on success, -1 if yank buffer is empty or too long */ +int yank_buf(ChatContext *ctx) +{ + if (!ctx->yank[0]) + return -1; + + if (ctx->yank_len + ctx->len >= MAX_STR_SIZE - 1) + return -1; + + wmemmove(&ctx->line[ctx->pos + ctx->yank_len], &ctx->line[ctx->pos], ctx->len - ctx->pos); + wmemcpy(&ctx->line[ctx->pos], ctx->yank, ctx->yank_len); + + ctx->pos += ctx->yank_len; + ctx->len += ctx->yank_len; + ctx->line[ctx->len] = L'\0'; + return 0; } /* nulls line and sets pos, len and start to 0 */ @@ -169,86 +208,3 @@ void fetch_hist_item(ChatContext *ctx, int key_dir) ctx->pos = h_len; ctx->len = h_len; } - -/* looks for the first instance in list that begins with the last entered word in line according to pos, - then fills line with the complete word. e.g. "Hello jo" would complete the line - with "Hello john". - - list is a pointer to the list of strings being compared, n_items is the number of items - in the list, and size is the size of each item in the list. - - Returns the difference between the old len and new len of line on success, -1 if error */ -int complete_line(ChatContext *ctx, const void *list, int n_items, int size) -{ - if (ctx->pos <= 0 || ctx->len <= 0 || ctx->len >= MAX_STR_SIZE) - return -1; - - const char *L = (char *) list; - - char ubuf[MAX_STR_SIZE]; - - /* work with multibyte string copy of buf for simplicity */ - if (wcs_to_mbs_buf(ubuf, ctx->line, sizeof(ubuf)) == -1) - return -1; - - /* isolate substring from space behind pos to pos */ - char tmp[MAX_STR_SIZE]; - snprintf(tmp, sizeof(tmp), "%s", ubuf); - tmp[ctx->pos] = '\0'; - char *sub = strrchr(tmp, ' '); - int n_endchrs = 1; /* 1 = append space to end of match, 2 = append ": " */ - - if (!sub++) { - sub = tmp; - - if (sub[0] != '/') /* make sure it's not a command */ - n_endchrs = 2; - } - - if (string_is_empty(sub)) - return -1; - - int s_len = strlen(sub); - const char *match; - bool is_match = false; - int i; - - /* look for a match in list */ - for (i = 0; i < n_items; ++i) { - match = &L[i * size]; - - if ((is_match = strncasecmp(match, sub, s_len) == 0)) - break; - } - - if (!is_match) - return -1; - - /* put match in correct spot in buf and append endchars (space or ": ") */ - const char *endchrs = n_endchrs == 1 ? " " : ": "; - int m_len = strlen(match); - int strt = ctx->pos - s_len; - int diff = m_len - s_len + n_endchrs; - - if (ctx->len + diff > MAX_STR_SIZE) - return -1; - - char tmpend[MAX_STR_SIZE]; - strcpy(tmpend, &ubuf[ctx->pos]); - strcpy(&ubuf[strt], match); - strcpy(&ubuf[strt + m_len], endchrs); - strcpy(&ubuf[strt + m_len + n_endchrs], tmpend); - - /* convert to widechar and copy back to original buf */ - wchar_t newbuf[MAX_STR_SIZE]; - - if (mbs_to_wcs_buf(newbuf, ubuf, MAX_STR_SIZE) == -1) - return -1; - - wcscpy(ctx->line, newbuf); - - ctx->len += diff; - ctx->pos += diff; - - return diff; -} diff --git a/src/toxic_strings.h b/src/toxic_strings.h index 8c7e12a..fde51d7 100644 --- a/src/toxic_strings.h +++ b/src/toxic_strings.h @@ -25,37 +25,33 @@ #include "windows.h" -/* Adds char to line at pos */ -void add_char_to_buf(ChatContext *ctx, wint_t ch); +/* Adds char to line at pos. Return 0 on success, -1 if line buffer is full */ +int add_char_to_buf(ChatContext *ctx, wint_t ch); -/* Deletes the character before pos */ -void del_char_buf_bck(ChatContext *ctx); +/* Deletes the character before pos. Return 0 on success, -1 if nothing to delete */ +int del_char_buf_bck(ChatContext *ctx); -/* Deletes the character at pos */ -void del_char_buf_frnt(ChatContext *ctx); +/* Deletes the character at pos. Return 0 on success, -1 if nothing to delete. */ +int del_char_buf_frnt(ChatContext *ctx); -/* Deletes the line from beginning to pos */ -void discard_buf(ChatContext *ctx); +/* Deletes the line from beginning to pos and puts discarded portion in yank buffer. + Return 0 on success, -1 if noting to discard */ +int discard_buf(ChatContext *ctx); -/* Deletes the line from pos to len */ -void kill_buf(ChatContext *ctx); +/* Deletes the line from pos to len and puts killed portion in yank buffer. + Return 0 on success, -1 if nothing to kill. */ +int kill_buf(ChatContext *ctx); /* nulls line and sets pos, len and start to 0 */ void reset_buf(ChatContext *ctx); +/* Inserts string in ctx->yank into line at pos. + Return 0 on success, -1 if yank buffer is empty or too long */ +int yank_buf(ChatContext *ctx); + /* Removes trailing spaces from line. */ void rm_trailing_spaces_buf(ChatContext *ctx); -/* looks for the first instance in list that begins with the last entered word in line according to pos, - then fills line with the complete word. e.g. "Hello jo" would complete the line - with "Hello john". - - list is a pointer to the list of strings being compared, n_items is the number of items - in the list, and size is the size of each item in the list. - - Returns the difference between the old len and new len of line on success, -1 if error */ -int complete_line(ChatContext *ctx, const void *list, int n_items, int size); - /* adds a line to the ln_history buffer at hst_pos and sets hst_pos to last history item. */ void add_line_to_hist(ChatContext *ctx); diff --git a/src/windows.c b/src/windows.c index c249d87..37c3e3d 100644 --- a/src/windows.c +++ b/src/windows.c @@ -326,11 +326,11 @@ void on_window_resize(void) ToxWindow *w = &windows[i]; - delwin(w->window); - w->window = newwin(y2, x2, 0, 0); - - if (windows[i].is_friendlist) + if (windows[i].is_friendlist) { + delwin(w->window); + w->window = newwin(y2, x2, 0, 0); continue; + } if (w->help->active) wclear(w->help->win); @@ -342,7 +342,9 @@ void on_window_resize(void) delwin(w->chatwin->linewin); delwin(w->chatwin->history); + delwin(w->window); + w->window = newwin(y2, x2, 0, 0); w->chatwin->linewin = subwin(w->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0); if (w->is_groupchat) { @@ -353,12 +355,12 @@ void on_window_resize(void) w->stb->topline = subwin(w->window, 2, x2, 0, 0); } -#ifdef _SUPPORT_AUDIO +#ifdef _AUDIO if (w->chatwin->infobox.active) { delwin(w->chatwin->infobox.win); w->chatwin->infobox.win = newwin(INFOBOX_HEIGHT, INFOBOX_WIDTH + 1, 1, x2 - INFOBOX_WIDTH); } -#endif /* _SUPPORT_AUDIO */ +#endif /* _AUDIO */ scrollok(w->chatwin->history, 0); } @@ -491,4 +493,4 @@ void kill_all_windows(void) else if (windows[i].is_groupchat) kill_groupchat_window(&windows[i]); } -} \ No newline at end of file +} diff --git a/src/windows.h b/src/windows.h index 30cb2d9..d56fa43 100644 --- a/src/windows.h +++ b/src/windows.h @@ -29,9 +29,9 @@ #include -#ifdef _SUPPORT_AUDIO +#ifdef _AUDIO #include -#endif /* _SUPPORT_AUDIO */ +#endif /* _AUDIO */ #include "toxic.h" @@ -98,7 +98,7 @@ struct ToxWindow { void(*onFileData)(ToxWindow *, Tox *, int32_t, uint8_t, const char *, uint16_t); void(*onTypingChange)(ToxWindow *, Tox *, int32_t, uint8_t); -#ifdef _SUPPORT_AUDIO +#ifdef _AUDIO void(*onInvite)(ToxWindow *, ToxAv *, int); void(*onRinging)(ToxWindow *, ToxAv *, int); @@ -116,9 +116,9 @@ struct ToxWindow { * Don't modify outside av callbacks. */ int device_selection[2]; /* -1 if not set, if set uses these selections instead of primary device */ -#endif /* _SUPPORT_AUDIO */ +#endif /* _AUDIO */ -#ifdef _ENABLE_SOUND_NOTIFY +#ifdef _SOUND_NOTIFY int active_sound; #endif @@ -152,7 +152,7 @@ struct StatusBar { bool is_online; }; -#ifdef _SUPPORT_AUDIO +#ifdef _AUDIO #define INFOBOX_HEIGHT 7 @@ -171,7 +171,7 @@ struct infobox { WINDOW *win; }; -#endif /* _SUPPORT_AUDIO */ +#endif /* _AUDIO */ #define MAX_LINE_HIST 128 @@ -186,10 +186,13 @@ struct ChatContext { int hst_pos; int hst_tot; + wchar_t yank[MAX_STR_SIZE]; /* contains last killed/discarded line */ + int yank_len; + struct history *hst; struct chatlog *log; -#ifdef _SUPPORT_AUDIO +#ifdef _AUDIO struct infobox infobox; #endif