mirror of
https://github.com/Tha14/toxic.git
synced 2025-06-27 17:56:46 +02:00
Compare commits
136 Commits
prealpha_w
...
0.2.6.1
Author | SHA1 | Date | |
---|---|---|---|
0013dae552 | |||
b018aa384e | |||
ad8f99dae4 | |||
5b9d3f6f62 | |||
93bcecde70 | |||
9f309ecb96 | |||
2b707f1d80 | |||
6269eafeaa | |||
18b0abe25d | |||
352656230c | |||
da6ef159e6 | |||
9e2fde8d84 | |||
aec16a22a4 | |||
3960208fd6 | |||
198f9af71d | |||
61b2a7f0c9 | |||
137b8c2412 | |||
fd3604be44 | |||
c7a2643ee0 | |||
77875d0ca6 | |||
e8a72146b0 | |||
a4cb568558 | |||
9353c2078b | |||
7e23afb57e | |||
2982dc6ddd | |||
23d47e9539 | |||
a68b733d58 | |||
4294e39b49 | |||
d9142eb355 | |||
69c467fa5f | |||
eb2d6afa00 | |||
5f1ddcf5f6 | |||
07089271cc | |||
c3d2ff6bfb | |||
16b8f85d95 | |||
1f06606c5b | |||
0dd588182b | |||
3cd15f3846 | |||
bd4b430fbb | |||
42cd80efe9 | |||
da99d776df | |||
ebf7d4517f | |||
a028de17cd | |||
e360d2aa5a | |||
866cafbf1b | |||
d4e8b7de5d | |||
06b09cd981 | |||
feefecb2ac | |||
d04f5fa102 | |||
dfb5b16e7d | |||
14c9599a30 | |||
c371c37bce | |||
6f034d434a | |||
eb6d832e3e | |||
10ae3865ca | |||
34cc4314a5 | |||
8b1dbd44ba | |||
f3cdb3cd62 | |||
4dfdfa33a1 | |||
bc8e737514 | |||
1dad3711c4 | |||
12e1a60ca3 | |||
ff30a29df1 | |||
629041d465 | |||
83a81f6db6 | |||
9a5a598c5a | |||
d9d4329ccf | |||
ab2be21942 | |||
ccc0640dab | |||
44d9f7fe61 | |||
a833be730f | |||
4065715b78 | |||
f76b672d47 | |||
674aa682e7 | |||
9c2551b3b9 | |||
f67725f636 | |||
5fc14a48db | |||
df57adcc6d | |||
e834821348 | |||
b8b032e441 | |||
4c27df32b0 | |||
3804233c21 | |||
eb9d4361f0 | |||
e224f92210 | |||
6089f02d57 | |||
d232538317 | |||
f942982f4e | |||
ce45580c83 | |||
18a7bbea3d | |||
7a14845790 | |||
2ad238d69f | |||
70e8bdb409 | |||
3740cb6763 | |||
a57f94306a | |||
e3400e095b | |||
1517cbb6cb | |||
424ab7cd10 | |||
a2af0bc047 | |||
2fde13530b | |||
db10c66922 | |||
88ebb06ba5 | |||
03ef257fdc | |||
1a86327f9f | |||
ba750753a5 | |||
7a89229375 | |||
81c48f7b2b | |||
b0c60238f7 | |||
ca1644c23b | |||
f67cc479ec | |||
b1b2cc44df | |||
192a06c4f0 | |||
49655e13a0 | |||
5850e1c333 | |||
37dd2bee2d | |||
bb6b28b7c3 | |||
8f1da153a7 | |||
4f4a379a01 | |||
c25296e65a | |||
f6a6aecaf5 | |||
8ff907d719 | |||
a02bbfa643 | |||
57c2872b75 | |||
7ad520f128 | |||
e4e7ed7e4e | |||
7ee84ce5e1 | |||
8c0ad1ef80 | |||
4babd53be6 | |||
e29ce6ab6f | |||
b909ab37b7 | |||
04b394d6dd | |||
8cf3043dd3 | |||
8584667ec9 | |||
d29e5dbe48 | |||
e21e5c18ff | |||
a98ec22fd6 | |||
2057e7bc4f |
@ -1,10 +1,10 @@
|
|||||||
## Toxic - console client for [Tox](http://tox.im)
|
## Toxic - console client for [Tox](http://tox.im)
|
||||||
|
|
||||||
The client formerly resided in the [Tox core repository](https://github.com/irungentoo/ProjectTox-Core) and is now available as a standalone version.
|
The client formerly resided in the [Tox core repository](https://github.com/irungentoo/ProjectTox-Core) and is now available as a standalone program. It looks like [this](http://wiki.tox.im/images/b/b6/Ncursesclient1.png).
|
||||||
|
|
||||||
To compile, first generate the configure script by running the ```autoreconf -i``` command.
|
To compile, first generate the configure script by running the ```autoreconf -i``` command.
|
||||||
|
|
||||||
Then execute the configure script (you'll probably have to pass it the location of your dependency libraries, i.e.):
|
Then 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/core --with-libtoxcore-libs=/path/to/ProjectTox-Core/build/core --with-libsodium-headers=/path/to/libsodium/include/ --with-libsodium-libs=/path/to/sodiumtest/lib/
|
./configure --prefix=/where/to/install --with-libtoxcore-headers=/path/to/ProjectTox-Core/core --with-libtoxcore-libs=/path/to/ProjectTox-Core/build/core --with-libsodium-headers=/path/to/libsodium/include/ --with-libsodium-libs=/path/to/sodiumtest/lib/
|
||||||
|
|
||||||
@ -14,3 +14,7 @@ Then execute the configure script (you'll probably have to pass it the location
|
|||||||
echo '/usr/local/lib/' | sudo tee -a /etc/ld.so.conf.d/locallib.conf
|
echo '/usr/local/lib/' | sudo tee -a /etc/ld.so.conf.d/locallib.conf
|
||||||
sudo ldconfig
|
sudo ldconfig
|
||||||
```
|
```
|
||||||
|
If you dont already have them, you might want to install the ncurses libraries, on Debian:
|
||||||
|
```
|
||||||
|
sudo apt-get install libncurses5-dev libncursesw5-dev
|
||||||
|
```
|
||||||
|
@ -23,7 +23,9 @@ toxic_SOURCES = $(top_srcdir)/src/main.c \
|
|||||||
$(top_srcdir)/src/execute.c \
|
$(top_srcdir)/src/execute.c \
|
||||||
$(top_srcdir)/src/execute.h \
|
$(top_srcdir)/src/execute.h \
|
||||||
$(top_srcdir)/src/misc_tools.c \
|
$(top_srcdir)/src/misc_tools.c \
|
||||||
$(top_srcdir)/src/misc_tools.h
|
$(top_srcdir)/src/misc_tools.h \
|
||||||
|
$(top_srcdir)/src/toxic_strings.c \
|
||||||
|
$(top_srcdir)/src/toxic_strings.h
|
||||||
|
|
||||||
toxic_CFLAGS = -I$(top_srcdir) \
|
toxic_CFLAGS = -I$(top_srcdir) \
|
||||||
$(NCURSES_CFLAGS) \
|
$(NCURSES_CFLAGS) \
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
# Process this file with autoconf to produce a configure script.
|
# Process this file with autoconf to produce a configure script.
|
||||||
|
|
||||||
AC_PREREQ([2.65])
|
AC_PREREQ([2.65])
|
||||||
AC_INIT([toxic], [0.2.3], [http://tox.im/])
|
AC_INIT([toxic], [0.2.6], [https://tox.im/])
|
||||||
AC_CONFIG_AUX_DIR(configure_aux)
|
AC_CONFIG_AUX_DIR(configure_aux)
|
||||||
AC_CONFIG_SRCDIR([src/main.c])
|
AC_CONFIG_SRCDIR([src/main.c])
|
||||||
AC_CONFIG_HEADERS([config.h])
|
AC_CONFIG_HEADERS([config.h])
|
||||||
|
@ -1,6 +1,2 @@
|
|||||||
54.215.145.71 33445 6EDDEE2188EF579303C0766B4796DCBA89C93058B6032FEA51593DCD42FB746C
|
192.254.75.98 33445 FE3914F4616E227F29B2103450D6B55A836AD4BD23F97144E2C4ABE8D504FE1B
|
||||||
66.175.223.88 33445 B24E2FB924AE66D023FE1E42A2EE3B432010206F751A2FFD3E297383ACF1572E
|
2607:5600:284::2 33445 FE3914F4616E227F29B2103450D6B55A836AD4BD23F97144E2C4ABE8D504FE1B
|
||||||
192.184.81.118 33445 5CD7EB176C19A2FD840406CD56177BB8E75587BB366F7BB3004B19E3EDC04143
|
|
||||||
192.210.149.121 33445 F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67
|
|
||||||
198.46.136.167 33445 728925473812C7AAC482BE7250BCCAD0B8CB9F737BF3D42ABD34459C1768F854
|
|
||||||
95.47.140.214 33445 F4BF7C5A9D0EF4CB684090C38DE937FAE1612021F21FEA4DCBFAC6AAFEF58E68
|
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
cmake_minimum_required(VERSION 2.6.0)
|
|
||||||
project(toxic C)
|
|
||||||
|
|
||||||
execute_process(COMMAND git rev-list HEAD --count OUTPUT_VARIABLE COMMIT)
|
|
||||||
SET(GCC_COVERAGE_COMPILE_FLAGS '-DTOXICVER="0.1.1_r${COMMIT}"')
|
|
||||||
add_definitions(${GCC_COVERAGE_COMPILE_FLAGS})
|
|
||||||
set(exe_name toxic)
|
|
||||||
|
|
||||||
add_executable(${exe_name}
|
|
||||||
main.c
|
|
||||||
windows.c
|
|
||||||
prompt.c
|
|
||||||
friendlist.c
|
|
||||||
dhtstatus.c
|
|
||||||
chat.c
|
|
||||||
configdir.c)
|
|
||||||
|
|
||||||
include_directories(${CURSES_INCLUDE_DIR})
|
|
||||||
|
|
||||||
target_link_libraries(${exe_name}
|
|
||||||
${CURSES_LIBRARIES})
|
|
||||||
|
|
||||||
linkCoreLibraries(${exe_name})
|
|
417
src/chat.c
417
src/chat.c
@ -13,26 +13,61 @@
|
|||||||
#include "toxic_windows.h"
|
#include "toxic_windows.h"
|
||||||
#include "execute.h"
|
#include "execute.h"
|
||||||
#include "misc_tools.h"
|
#include "misc_tools.h"
|
||||||
|
#include "friendlist.h"
|
||||||
|
#include "toxic_strings.h"
|
||||||
|
|
||||||
extern char *DATA_FILE;
|
extern char *DATA_FILE;
|
||||||
extern int store_data(Tox *m, char *path);
|
extern int store_data(Tox *m, char *path);
|
||||||
extern int num_groupchats;
|
|
||||||
|
extern FileSender file_senders[MAX_FILES];
|
||||||
|
extern ToxicFriend friends[MAX_FRIENDS_NUM];
|
||||||
|
|
||||||
|
#define AC_NUM_CHAT_COMMANDS 17
|
||||||
|
|
||||||
|
/* Array of chat command names used for tab completion. */
|
||||||
|
static const uint8_t chat_cmd_list[AC_NUM_CHAT_COMMANDS][MAX_CMDNAME_SIZE] = {
|
||||||
|
{ "/accept" },
|
||||||
|
{ "/add" },
|
||||||
|
{ "/clear" },
|
||||||
|
{ "/close" },
|
||||||
|
{ "/connect" },
|
||||||
|
{ "/exit" },
|
||||||
|
{ "/groupchat" },
|
||||||
|
{ "/help" },
|
||||||
|
{ "/invite" },
|
||||||
|
{ "/join" },
|
||||||
|
{ "/myid" },
|
||||||
|
{ "/nick" },
|
||||||
|
{ "/note" },
|
||||||
|
{ "/quit" },
|
||||||
|
{ "/savefile" },
|
||||||
|
{ "/sendfile" },
|
||||||
|
{ "/status" },
|
||||||
|
};
|
||||||
|
|
||||||
|
static void set_typingstatus(ToxWindow *self, Tox *m, bool is_typing)
|
||||||
|
{
|
||||||
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
|
tox_set_user_is_typing(m, self->num, is_typing);
|
||||||
|
ctx->self_is_typing = is_typing;
|
||||||
|
}
|
||||||
|
|
||||||
static void chat_onMessage(ToxWindow *self, Tox *m, int num, uint8_t *msg, uint16_t len)
|
static void chat_onMessage(ToxWindow *self, Tox *m, int num, uint8_t *msg, uint16_t len)
|
||||||
{
|
{
|
||||||
if (self->num != num)
|
if (self->num != num)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ChatContext *ctx = (ChatContext *) self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
|
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
|
||||||
tox_getname(m, num, nick);
|
tox_get_name(m, num, nick);
|
||||||
nick[TOXIC_MAX_NAME_LENGTH] = '\0';
|
nick[TOXIC_MAX_NAME_LENGTH] = '\0';
|
||||||
|
|
||||||
print_time(ctx->history);
|
print_time(ctx->history);
|
||||||
wattron(ctx->history, COLOR_PAIR(4));
|
wattron(ctx->history, COLOR_PAIR(CYAN));
|
||||||
wprintw(ctx->history, "%s: ", nick);
|
wprintw(ctx->history, "%s: ", nick);
|
||||||
wattroff(ctx->history, COLOR_PAIR(4));
|
wattroff(ctx->history, COLOR_PAIR(CYAN));
|
||||||
|
|
||||||
if (msg[0] == '>') {
|
if (msg[0] == '>') {
|
||||||
wattron(ctx->history, COLOR_PAIR(GREEN));
|
wattron(ctx->history, COLOR_PAIR(GREEN));
|
||||||
@ -41,7 +76,7 @@ static void chat_onMessage(ToxWindow *self, Tox *m, int num, uint8_t *msg, uint1
|
|||||||
} else
|
} else
|
||||||
wprintw(ctx->history, "%s\n", msg);
|
wprintw(ctx->history, "%s\n", msg);
|
||||||
|
|
||||||
alert_window(self);
|
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, int num, uint8_t status)
|
||||||
@ -49,8 +84,23 @@ static void chat_onConnectionChange(ToxWindow *self, Tox *m, int num, uint8_t st
|
|||||||
if (self->num != num)
|
if (self->num != num)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
StatusBar *statusbar = (StatusBar *) self->stb;
|
StatusBar *statusbar = self->stb;
|
||||||
statusbar->is_online = status == 1 ? true : false;
|
|
||||||
|
if (status == 1) {
|
||||||
|
statusbar->is_online = true;
|
||||||
|
friends[num].is_typing = tox_get_is_typing(m, num);
|
||||||
|
} else {
|
||||||
|
statusbar->is_online = false;
|
||||||
|
friends[num].is_typing = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void chat_onTypingChange(ToxWindow *self, Tox *m, int num, int is_typing)
|
||||||
|
{
|
||||||
|
if (self->num != num)
|
||||||
|
return;
|
||||||
|
|
||||||
|
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, int num, uint8_t *action, uint16_t len)
|
||||||
@ -58,10 +108,10 @@ static void chat_onAction(ToxWindow *self, Tox *m, int num, uint8_t *action, uin
|
|||||||
if (self->num != num)
|
if (self->num != num)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ChatContext *ctx = (ChatContext *) self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
|
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
|
||||||
tox_getname(m, num, nick);
|
tox_get_name(m, num, nick);
|
||||||
nick[TOXIC_MAX_NAME_LENGTH] = '\0';
|
nick[TOXIC_MAX_NAME_LENGTH] = '\0';
|
||||||
|
|
||||||
print_time(ctx->history);
|
print_time(ctx->history);
|
||||||
@ -69,10 +119,10 @@ static void chat_onAction(ToxWindow *self, Tox *m, int num, uint8_t *action, uin
|
|||||||
wprintw(ctx->history, "* %s %s\n", nick, action);
|
wprintw(ctx->history, "* %s %s\n", nick, action);
|
||||||
wattroff(ctx->history, COLOR_PAIR(YELLOW));
|
wattroff(ctx->history, COLOR_PAIR(YELLOW));
|
||||||
|
|
||||||
alert_window(self);
|
alert_window(self, WINDOW_ALERT_1, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void chat_onNickChange(ToxWindow *self, int num, uint8_t *nick, uint16_t len)
|
static void chat_onNickChange(ToxWindow *self, Tox *m, int num, uint8_t *nick, uint16_t len)
|
||||||
{
|
{
|
||||||
if (self->num != num)
|
if (self->num != num)
|
||||||
return;
|
return;
|
||||||
@ -87,7 +137,7 @@ static void chat_onStatusChange(ToxWindow *self, Tox *m, int num, TOX_USERSTATUS
|
|||||||
if (self->num != num)
|
if (self->num != num)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
StatusBar *statusbar = (StatusBar *) self->stb;
|
StatusBar *statusbar = self->stb;
|
||||||
statusbar->status = status;
|
statusbar->status = status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,7 +146,7 @@ static void chat_onStatusMessageChange(ToxWindow *self, int num, uint8_t *status
|
|||||||
if (self->num != num)
|
if (self->num != num)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
StatusBar *statusbar = (StatusBar *) self->stb;
|
StatusBar *statusbar = self->stb;
|
||||||
statusbar->statusmsg_len = len;
|
statusbar->statusmsg_len = len;
|
||||||
memcpy(statusbar->statusmsg, status, len);
|
memcpy(statusbar->statusmsg, status, len);
|
||||||
}
|
}
|
||||||
@ -107,25 +157,10 @@ static void chat_onFileSendRequest(ToxWindow *self, Tox *m, int num, uint8_t fil
|
|||||||
if (self->num != num)
|
if (self->num != num)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ChatContext *ctx = (ChatContext *) self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
int idx = strlen(pathname) - 1;
|
uint8_t filename[MAX_STR_SIZE];
|
||||||
while (pathname[idx] == '/') {
|
get_file_name(pathname, filename);
|
||||||
pathname[idx--] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* try to get file name from path */
|
|
||||||
uint8_t *filename = strrchr(pathname, '/'); // Try unix style paths
|
|
||||||
|
|
||||||
if (filename != NULL) {
|
|
||||||
if (!strlen(++filename))
|
|
||||||
filename = pathname;
|
|
||||||
} else {
|
|
||||||
filename = strrchr(pathname, '\\'); // Try windows style paths
|
|
||||||
|
|
||||||
if (filename == NULL)
|
|
||||||
filename = pathname;
|
|
||||||
}
|
|
||||||
|
|
||||||
wprintw(ctx->history, "File transfer request for '%s' (%llu bytes).\n", filename,
|
wprintw(ctx->history, "File transfer request for '%s' (%llu bytes).\n", filename,
|
||||||
(long long unsigned int)filesize);
|
(long long unsigned int)filesize);
|
||||||
@ -158,7 +193,7 @@ static void chat_onFileSendRequest(ToxWindow *self, Tox *m, int num, uint8_t fil
|
|||||||
friends[num].file_receiver.pending[filenum] = true;
|
friends[num].file_receiver.pending[filenum] = true;
|
||||||
strcpy(friends[num].file_receiver.filenames[filenum], filename);
|
strcpy(friends[num].file_receiver.filenames[filenum], filename);
|
||||||
|
|
||||||
alert_window(self);
|
alert_window(self, WINDOW_ALERT_2, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void chat_onFileControl(ToxWindow *self, Tox *m, int num, uint8_t receive_send,
|
static void chat_onFileControl(ToxWindow *self, Tox *m, int num, uint8_t receive_send,
|
||||||
@ -167,7 +202,7 @@ static void chat_onFileControl(ToxWindow *self, Tox *m, int num, uint8_t receive
|
|||||||
if (self->num != num)
|
if (self->num != num)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ChatContext *ctx = (ChatContext *) self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
uint8_t *filename;
|
uint8_t *filename;
|
||||||
|
|
||||||
if (receive_send == 0)
|
if (receive_send == 0)
|
||||||
@ -180,18 +215,23 @@ static void chat_onFileControl(ToxWindow *self, Tox *m, int num, uint8_t receive
|
|||||||
wprintw(ctx->history, "File transfer for '%s' accepted.\n", filename);
|
wprintw(ctx->history, "File transfer for '%s' accepted.\n", filename);
|
||||||
break;
|
break;
|
||||||
case TOX_FILECONTROL_PAUSE:
|
case TOX_FILECONTROL_PAUSE:
|
||||||
wprintw(ctx->history, "File transfer for '%s' paused.\n", filename);
|
// wprintw(ctx->history, "File transfer for '%s' paused.\n", filename);
|
||||||
break;
|
break;
|
||||||
case TOX_FILECONTROL_KILL:
|
case TOX_FILECONTROL_KILL:
|
||||||
wprintw(ctx->history, "File transfer for '%s' failed.\n", filename);
|
wprintw(ctx->history, "File transfer for '%s' failed.\n", filename);
|
||||||
friends[num].file_receiver.pending[filenum] = false;
|
|
||||||
|
if (receive_send == 0)
|
||||||
|
friends[num].file_receiver.pending[filenum] = false;
|
||||||
|
else
|
||||||
|
close_file_sender(filenum);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case TOX_FILECONTROL_FINISHED:
|
case TOX_FILECONTROL_FINISHED:
|
||||||
wprintw(ctx->history, "File transfer for '%s' complete.\n", filename);
|
wprintw(ctx->history, "File transfer for '%s' complete.\n", filename);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
alert_window(self);
|
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, int num, uint8_t filenum, uint8_t *data,
|
||||||
@ -200,13 +240,17 @@ static void chat_onFileData(ToxWindow *self, Tox *m, int num, uint8_t filenum, u
|
|||||||
if (self->num != num)
|
if (self->num != num)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ChatContext *ctx = (ChatContext *) self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
uint8_t *filename = friends[num].file_receiver.filenames[filenum];
|
uint8_t *filename = friends[num].file_receiver.filenames[filenum];
|
||||||
FILE *file_to_save = fopen(filename, "a");
|
FILE *file_to_save = fopen(filename, "a");
|
||||||
|
|
||||||
// we have a problem here, but don't let it segfault
|
// we have a problem here, but don't let it segfault
|
||||||
if (file_to_save == NULL) {
|
if (file_to_save == NULL) {
|
||||||
|
wattron(ctx->history, COLOR_PAIR(RED));
|
||||||
|
wprintw(ctx->history, "* Error writing to file.\n");
|
||||||
|
wattroff(ctx->history, COLOR_PAIR(RED));
|
||||||
|
tox_file_send_control(m, num, 1, filenum, TOX_FILECONTROL_KILL, 0, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,6 +258,7 @@ static void chat_onFileData(ToxWindow *self, Tox *m, int num, uint8_t filenum, u
|
|||||||
wattron(ctx->history, COLOR_PAIR(RED));
|
wattron(ctx->history, COLOR_PAIR(RED));
|
||||||
wprintw(ctx->history, "* Error writing to file.\n");
|
wprintw(ctx->history, "* Error writing to file.\n");
|
||||||
wattroff(ctx->history, COLOR_PAIR(RED));
|
wattroff(ctx->history, COLOR_PAIR(RED));
|
||||||
|
tox_file_send_control(m, num, 1, filenum, TOX_FILECONTROL_KILL, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(file_to_save);
|
fclose(file_to_save);
|
||||||
@ -224,22 +269,17 @@ static void chat_onGroupInvite(ToxWindow *self, Tox *m, int friendnumber, uint8_
|
|||||||
if (self->num != friendnumber)
|
if (self->num != friendnumber)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ChatContext *ctx = (ChatContext *) self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
uint8_t name[TOX_MAX_NAME_LENGTH] = {'\0'};
|
uint8_t name[TOX_MAX_NAME_LENGTH] = {'\0'};
|
||||||
|
|
||||||
if (tox_getname(m, friendnumber, name) == -1)
|
if (tox_get_name(m, friendnumber, name) == -1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
wprintw(ctx->history, "%s has invited you to a group chat.\n", name);
|
wprintw(ctx->history, "%s has invited you to a group chat.\n", name);
|
||||||
|
|
||||||
if (num_groupchats >= MAX_GROUPCHAT_NUM) {
|
|
||||||
wprintw(ctx->history, "Maximum number of group chats has been reached. Discarding invite.\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(friends[friendnumber].pending_groupchat, group_pub_key, TOX_CLIENT_ID_SIZE);
|
memcpy(friends[friendnumber].pending_groupchat, group_pub_key, TOX_CLIENT_ID_SIZE);
|
||||||
wprintw(ctx->history, "Type \"/join\" to join the chat.\n");
|
wprintw(ctx->history, "Type \"/join\" to join the chat.\n");
|
||||||
alert_window(self);
|
alert_window(self, WINDOW_ALERT_2, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void send_action(ToxWindow *self, ChatContext *ctx, Tox *m, uint8_t *action) {
|
static void send_action(ToxWindow *self, ChatContext *ctx, Tox *m, uint8_t *action) {
|
||||||
@ -249,14 +289,14 @@ static void send_action(ToxWindow *self, ChatContext *ctx, Tox *m, uint8_t *acti
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint8_t selfname[TOX_MAX_NAME_LENGTH];
|
uint8_t selfname[TOX_MAX_NAME_LENGTH];
|
||||||
tox_getselfname(m, selfname, TOX_MAX_NAME_LENGTH);
|
tox_get_self_name(m, selfname, TOX_MAX_NAME_LENGTH);
|
||||||
|
|
||||||
print_time(ctx->history);
|
print_time(ctx->history);
|
||||||
wattron(ctx->history, COLOR_PAIR(YELLOW));
|
wattron(ctx->history, COLOR_PAIR(YELLOW));
|
||||||
wprintw(ctx->history, "* %s %s\n", selfname, action);
|
wprintw(ctx->history, "* %s %s\n", selfname, action);
|
||||||
wattroff(ctx->history, COLOR_PAIR(YELLOW));
|
wattroff(ctx->history, COLOR_PAIR(YELLOW));
|
||||||
|
|
||||||
if (tox_sendaction(m, self->num, action, strlen(action) + 1) == 0) {
|
if (tox_send_action(m, self->num, action, strlen(action) + 1) == 0) {
|
||||||
wattron(ctx->history, COLOR_PAIR(RED));
|
wattron(ctx->history, COLOR_PAIR(RED));
|
||||||
wprintw(ctx->history, " * Failed to send action\n");
|
wprintw(ctx->history, " * Failed to send action\n");
|
||||||
wattroff(ctx->history, COLOR_PAIR(RED));
|
wattroff(ctx->history, COLOR_PAIR(RED));
|
||||||
@ -265,43 +305,158 @@ static void send_action(ToxWindow *self, ChatContext *ctx, Tox *m, uint8_t *acti
|
|||||||
|
|
||||||
static void chat_onKey(ToxWindow *self, Tox *m, wint_t key)
|
static void chat_onKey(ToxWindow *self, Tox *m, wint_t key)
|
||||||
{
|
{
|
||||||
ChatContext *ctx = (ChatContext *) self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
StatusBar *statusbar = (StatusBar *) self->stb;
|
StatusBar *statusbar = self->stb;
|
||||||
|
|
||||||
int x, y, y2, x2;
|
int x, y, y2, x2;
|
||||||
getyx(self->window, y, x);
|
getyx(self->window, y, x);
|
||||||
getmaxyx(self->window, y2, x2);
|
getmaxyx(self->window, y2, x2);
|
||||||
|
int cur_len = 0;
|
||||||
|
bool close_win = false;
|
||||||
|
|
||||||
/* BACKSPACE key: Remove one character from line */
|
if (key == 0x107 || key == 0x8 || key == 0x7f) { /* BACKSPACE key: Remove character behind pos */
|
||||||
if (key == 0x107 || key == 0x8 || key == 0x7f) {
|
|
||||||
if (ctx->pos > 0) {
|
if (ctx->pos > 0) {
|
||||||
ctx->line[--ctx->pos] = L'\0';
|
cur_len = MAX(1, wcwidth(ctx->line[ctx->pos - 1]));
|
||||||
|
del_char_buf_bck(ctx->line, &ctx->pos, &ctx->len);
|
||||||
|
|
||||||
if (x == 0)
|
if (x == 0)
|
||||||
mvwdelch(self->window, y - 1, x2 - 1);
|
wmove(self->window, y-1, x2 - cur_len);
|
||||||
else
|
else
|
||||||
mvwdelch(self->window, y, x - 1);
|
wmove(self->window, y, x - cur_len);
|
||||||
|
} else {
|
||||||
|
beep();
|
||||||
}
|
}
|
||||||
} else
|
}
|
||||||
/* Add printable chars to buffer and print on input space */
|
|
||||||
|
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) { /* HOME key: Move cursor to beginning of line */
|
||||||
|
if (ctx->pos > 0) {
|
||||||
|
ctx->pos = 0;
|
||||||
|
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (key == KEY_END) { /* END 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 HAVE_WIDECHAR
|
||||||
if (iswprint(key)) {
|
if (iswprint(key))
|
||||||
#else
|
#else
|
||||||
if (isprint(key)) {
|
if (isprint(key))
|
||||||
#endif
|
#endif
|
||||||
if (ctx->pos < (MAX_STR_SIZE-1)) {
|
{ /* prevents buffer overflows and strange behaviour when cursor goes past the window */
|
||||||
mvwaddstr(self->window, y, x, wc_to_char(key));
|
if ( (ctx->len < MAX_STR_SIZE-1) && (ctx->len < (x2 * (CHATBOX_HEIGHT - 1)-1)) ) {
|
||||||
ctx->line[ctx->pos++] = key;
|
add_char_to_buf(ctx->line, &ctx->pos, &ctx->len, key);
|
||||||
ctx->line[ctx->pos] = L'\0';
|
|
||||||
|
if (x == x2-1)
|
||||||
|
wmove(self->window, y+1, 0);
|
||||||
|
else
|
||||||
|
wmove(self->window, y, x + MAX(1, wcwidth(key)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!ctx->self_is_typing && ctx->line[0] != '/')
|
||||||
|
set_typingstatus(self, m, true);
|
||||||
}
|
}
|
||||||
/* RETURN key: Execute command or print line */
|
/* RETURN key: Execute command or print line */
|
||||||
else if (key == '\n') {
|
else if (key == '\n') {
|
||||||
uint8_t *line = wcs_to_char(ctx->line);
|
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);
|
wclear(ctx->linewin);
|
||||||
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
|
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
|
||||||
wclrtobot(self->window);
|
wclrtobot(self->window);
|
||||||
bool close_win = false;
|
|
||||||
|
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 (line[0] == '/') {
|
||||||
if (close_win = !strcmp(line, "/close")) {
|
if (close_win = !strcmp(line, "/close")) {
|
||||||
@ -310,34 +465,30 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key)
|
|||||||
delwin(statusbar->topline);
|
delwin(statusbar->topline);
|
||||||
del_window(self);
|
del_window(self);
|
||||||
disable_chatwin(f_num);
|
disable_chatwin(f_num);
|
||||||
} else if (!strncmp(line, "/me ", strlen("/me ")))
|
} else if (strncmp(line, "/me ", strlen("/me ")) == 0)
|
||||||
send_action(self, ctx, m, line + strlen("/me "));
|
send_action(self, ctx, m, line + strlen("/me "));
|
||||||
else
|
else
|
||||||
execute(ctx->history, self, m, line, CHAT_COMMAND_MODE);
|
execute(ctx->history, self, m, line, CHAT_COMMAND_MODE);
|
||||||
} else {
|
} else if (!string_is_empty(line)) {
|
||||||
/* make sure the string has at least non-space character */
|
uint8_t selfname[TOX_MAX_NAME_LENGTH];
|
||||||
if (!string_is_empty(line)) {
|
tox_get_self_name(m, selfname, TOX_MAX_NAME_LENGTH);
|
||||||
uint8_t selfname[TOX_MAX_NAME_LENGTH];
|
|
||||||
tox_getselfname(m, selfname, TOX_MAX_NAME_LENGTH);
|
|
||||||
|
|
||||||
print_time(ctx->history);
|
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));
|
wattron(ctx->history, COLOR_PAIR(GREEN));
|
||||||
wprintw(ctx->history, "%s: ", selfname);
|
wprintw(ctx->history, "%s\n", line);
|
||||||
wattroff(ctx->history, COLOR_PAIR(GREEN));
|
wattroff(ctx->history, COLOR_PAIR(GREEN));
|
||||||
|
} else
|
||||||
|
wprintw(ctx->history, "%s\n", line);
|
||||||
|
|
||||||
if (line[0] == '>') {
|
if (!statusbar->is_online || tox_send_message(m, self->num, line, strlen(line) + 1) == 0) {
|
||||||
wattron(ctx->history, COLOR_PAIR(GREEN));
|
wattron(ctx->history, COLOR_PAIR(RED));
|
||||||
wprintw(ctx->history, "%s\n", line);
|
wprintw(ctx->history, " * Failed to send message.\n");
|
||||||
wattroff(ctx->history, COLOR_PAIR(GREEN));
|
wattroff(ctx->history, COLOR_PAIR(RED));
|
||||||
} else
|
|
||||||
wprintw(ctx->history, "%s\n", line);
|
|
||||||
|
|
||||||
if (!statusbar->is_online
|
|
||||||
|| tox_sendmessage(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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -345,25 +496,40 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key)
|
|||||||
free(ctx);
|
free(ctx);
|
||||||
free(statusbar);
|
free(statusbar);
|
||||||
} else {
|
} else {
|
||||||
ctx->line[0] = L'\0';
|
reset_buf(ctx->line, &ctx->pos, &ctx->len);
|
||||||
ctx->pos = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
free(line);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!close_win) {
|
||||||
|
if (ctx->len <= 0 && ctx->self_is_typing)
|
||||||
|
set_typingstatus(self, m, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void chat_onDraw(ToxWindow *self, Tox *m)
|
static void chat_onDraw(ToxWindow *self, Tox *m)
|
||||||
{
|
{
|
||||||
curs_set(1);
|
curs_set(1);
|
||||||
int x, y;
|
int x2, y2;
|
||||||
getmaxyx(self->window, y, x);
|
getmaxyx(self->window, y2, x2);
|
||||||
|
|
||||||
ChatContext *ctx = (ChatContext *) self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Draw status bar */
|
/* Draw status bar */
|
||||||
StatusBar *statusbar = (StatusBar *) self->stb;
|
StatusBar *statusbar = self->stb;
|
||||||
mvwhline(statusbar->topline, 1, 0, ACS_HLINE, x);
|
mvwhline(statusbar->topline, 1, 0, ACS_HLINE, x2);
|
||||||
wmove(statusbar->topline, 0, 0);
|
wmove(statusbar->topline, 0, 0);
|
||||||
|
|
||||||
/* Draw name, status and note in statusbar */
|
/* Draw name, status and note in statusbar */
|
||||||
@ -388,9 +554,16 @@ static void chat_onDraw(ToxWindow *self, Tox *m)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (friends[self->num].is_typing)
|
||||||
|
wattron(statusbar->topline, COLOR_PAIR(YELLOW));
|
||||||
|
|
||||||
wattron(statusbar->topline, A_BOLD);
|
wattron(statusbar->topline, A_BOLD);
|
||||||
wprintw(statusbar->topline, " %s ", self->name);
|
wprintw(statusbar->topline, " %s ", self->name);
|
||||||
wattroff(statusbar->topline, A_BOLD);
|
wattroff(statusbar->topline, A_BOLD);
|
||||||
|
|
||||||
|
if (friends[self->num].is_typing)
|
||||||
|
wattroff(statusbar->topline, COLOR_PAIR(YELLOW));
|
||||||
|
|
||||||
wattron(statusbar->topline, COLOR_PAIR(colour) | A_BOLD);
|
wattron(statusbar->topline, COLOR_PAIR(colour) | A_BOLD);
|
||||||
wprintw(statusbar->topline, "[%s]", status_text);
|
wprintw(statusbar->topline, "[%s]", status_text);
|
||||||
wattroff(statusbar->topline, COLOR_PAIR(colour) | A_BOLD);
|
wattroff(statusbar->topline, COLOR_PAIR(colour) | A_BOLD);
|
||||||
@ -402,17 +575,17 @@ static void chat_onDraw(ToxWindow *self, Tox *m)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Reset statusbar->statusmsg on window resize */
|
/* Reset statusbar->statusmsg on window resize */
|
||||||
if (x != self->x) {
|
if (x2 != self->x) {
|
||||||
uint8_t statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH] = {'\0'};
|
uint8_t statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH] = {'\0'};
|
||||||
tox_copy_statusmessage(m, self->num, statusmsg, TOX_MAX_STATUSMESSAGE_LENGTH);
|
tox_get_status_message(m, self->num, statusmsg, TOX_MAX_STATUSMESSAGE_LENGTH);
|
||||||
snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg);
|
snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg);
|
||||||
statusbar->statusmsg_len = tox_get_statusmessage_size(m, self->num);
|
statusbar->statusmsg_len = tox_get_status_message_size(m, self->num);
|
||||||
}
|
}
|
||||||
|
|
||||||
self->x = x;
|
self->x = x2;
|
||||||
|
|
||||||
/* Truncate note if it doesn't fit in statusbar */
|
/* Truncate note if it doesn't fit in statusbar */
|
||||||
uint16_t maxlen = x - getcurx(statusbar->topline) - 4;
|
uint16_t maxlen = x2 - getcurx(statusbar->topline) - (KEY_IDENT_DIGITS * 2) - 7;
|
||||||
if (statusbar->statusmsg_len > maxlen) {
|
if (statusbar->statusmsg_len > maxlen) {
|
||||||
statusbar->statusmsg[maxlen] = '\0';
|
statusbar->statusmsg[maxlen] = '\0';
|
||||||
statusbar->statusmsg_len = maxlen;
|
statusbar->statusmsg_len = maxlen;
|
||||||
@ -424,37 +597,44 @@ static void chat_onDraw(ToxWindow *self, Tox *m)
|
|||||||
wattroff(statusbar->topline, A_BOLD);
|
wattroff(statusbar->topline, A_BOLD);
|
||||||
}
|
}
|
||||||
|
|
||||||
wprintw(statusbar->topline, "\n");
|
wclrtoeol(statusbar->topline);
|
||||||
|
wmove(statusbar->topline, 0, x2 - (KEY_IDENT_DIGITS * 2) - 3);
|
||||||
|
wprintw(statusbar->topline, "{");
|
||||||
|
|
||||||
mvwhline(ctx->linewin, 0, 0, ACS_HLINE, x);
|
int i;
|
||||||
wrefresh(self->window);
|
|
||||||
|
for (i = 0; i < KEY_IDENT_DIGITS; ++i)
|
||||||
|
wprintw(statusbar->topline, "%02X", friends[self->num].pub_key[i] & 0xff);
|
||||||
|
|
||||||
|
wprintw(statusbar->topline, "}\n");
|
||||||
|
mvwhline(ctx->linewin, 0, 0, ACS_HLINE, x2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void chat_onInit(ToxWindow *self, Tox *m)
|
static void chat_onInit(ToxWindow *self, Tox *m)
|
||||||
{
|
{
|
||||||
int x, y;
|
int x2, y2;
|
||||||
getmaxyx(self->window, y, x);
|
getmaxyx(self->window, y2, x2);
|
||||||
self->x = x;
|
self->x = x2;
|
||||||
|
|
||||||
/* Init statusbar info */
|
/* Init statusbar info */
|
||||||
StatusBar *statusbar = (StatusBar *) self->stb;
|
StatusBar *statusbar = self->stb;
|
||||||
statusbar->status = tox_get_userstatus(m, self->num);
|
statusbar->status = tox_get_user_status(m, self->num);
|
||||||
statusbar->is_online = tox_get_friend_connectionstatus(m, self->num) == 1;
|
statusbar->is_online = tox_get_friend_connection_status(m, self->num) == 1;
|
||||||
|
|
||||||
uint8_t statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH] = {'\0'};
|
uint8_t statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH] = {'\0'};
|
||||||
tox_copy_statusmessage(m, self->num, statusmsg, TOX_MAX_STATUSMESSAGE_LENGTH);
|
tox_get_status_message(m, self->num, statusmsg, TOX_MAX_STATUSMESSAGE_LENGTH);
|
||||||
snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg);
|
snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg);
|
||||||
statusbar->statusmsg_len = tox_get_statusmessage_size(m, self->num);
|
statusbar->statusmsg_len = tox_get_status_message_size(m, self->num);
|
||||||
|
|
||||||
/* Init subwindows */
|
/* Init subwindows */
|
||||||
ChatContext *ctx = (ChatContext *) self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
statusbar->topline = subwin(self->window, 2, x, 0, 0);
|
statusbar->topline = subwin(self->window, 2, x2, 0, 0);
|
||||||
ctx->history = subwin(self->window, y-CHATBOX_HEIGHT+1, x, 0, 0);
|
ctx->history = subwin(self->window, y2-CHATBOX_HEIGHT+1, x2, 0, 0);
|
||||||
scrollok(ctx->history, 1);
|
scrollok(ctx->history, 1);
|
||||||
ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x, y-CHATBOX_HEIGHT, 0);
|
ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2-CHATBOX_HEIGHT, 0);
|
||||||
wprintw(ctx->history, "\n\n");
|
wprintw(ctx->history, "\n\n");
|
||||||
execute(ctx->history, self, m, "/help", CHAT_COMMAND_MODE);
|
execute(ctx->history, self, m, "/help", CHAT_COMMAND_MODE);
|
||||||
wmove(self->window, y - CURS_Y_OFFSET, 0);
|
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
ToxWindow new_chat(Tox *m, int friendnum)
|
ToxWindow new_chat(Tox *m, int friendnum)
|
||||||
@ -462,11 +642,14 @@ ToxWindow new_chat(Tox *m, int friendnum)
|
|||||||
ToxWindow ret;
|
ToxWindow ret;
|
||||||
memset(&ret, 0, sizeof(ret));
|
memset(&ret, 0, sizeof(ret));
|
||||||
|
|
||||||
|
ret.active = true;
|
||||||
|
|
||||||
ret.onKey = &chat_onKey;
|
ret.onKey = &chat_onKey;
|
||||||
ret.onDraw = &chat_onDraw;
|
ret.onDraw = &chat_onDraw;
|
||||||
ret.onInit = &chat_onInit;
|
ret.onInit = &chat_onInit;
|
||||||
ret.onMessage = &chat_onMessage;
|
ret.onMessage = &chat_onMessage;
|
||||||
ret.onConnectionChange = &chat_onConnectionChange;
|
ret.onConnectionChange = &chat_onConnectionChange;
|
||||||
|
ret.onTypingChange = & chat_onTypingChange;
|
||||||
ret.onGroupInvite = &chat_onGroupInvite;
|
ret.onGroupInvite = &chat_onGroupInvite;
|
||||||
ret.onNickChange = &chat_onNickChange;
|
ret.onNickChange = &chat_onNickChange;
|
||||||
ret.onStatusChange = &chat_onStatusChange;
|
ret.onStatusChange = &chat_onStatusChange;
|
||||||
@ -477,7 +660,7 @@ ToxWindow new_chat(Tox *m, int friendnum)
|
|||||||
ret.onFileData = &chat_onFileData;
|
ret.onFileData = &chat_onFileData;
|
||||||
|
|
||||||
uint8_t name[TOX_MAX_NAME_LENGTH] = {'\0'};
|
uint8_t name[TOX_MAX_NAME_LENGTH] = {'\0'};
|
||||||
uint16_t len = tox_getname(m, friendnum, name);
|
uint16_t len = tox_get_name(m, friendnum, name);
|
||||||
memcpy(ret.name, name, len);
|
memcpy(ret.name, name, len);
|
||||||
ret.name[TOXIC_MAX_NAME_LENGTH] = '\0';
|
ret.name[TOXIC_MAX_NAME_LENGTH] = '\0';
|
||||||
|
|
||||||
|
@ -11,15 +11,20 @@
|
|||||||
|
|
||||||
#include "toxic_windows.h"
|
#include "toxic_windows.h"
|
||||||
#include "misc_tools.h"
|
#include "misc_tools.h"
|
||||||
|
#include "friendlist.h"
|
||||||
|
|
||||||
extern ToxWindow *prompt;
|
extern ToxWindow *prompt;
|
||||||
extern int num_groupchats;
|
|
||||||
|
extern ToxicFriend friends[MAX_FRIENDS_NUM];
|
||||||
|
|
||||||
|
extern FileSender file_senders[MAX_FILES];
|
||||||
|
extern uint8_t max_file_senders_index;
|
||||||
|
|
||||||
void cmd_chat_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_chat_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
wattron(window, COLOR_PAIR(CYAN) | A_BOLD);
|
wattron(window, COLOR_PAIR(CYAN) | A_BOLD);
|
||||||
wprintw(window, "Chat commands:\n");
|
wprintw(window, "Chat commands:\n");
|
||||||
wattroff(window, A_BOLD);
|
wattroff(window, COLOR_PAIR(CYAN) | A_BOLD);
|
||||||
|
|
||||||
wprintw(window, " /status <type> <msg> : Set your status with optional note\n");
|
wprintw(window, " /status <type> <msg> : Set your status with optional note\n");
|
||||||
wprintw(window, " /note <msg> : Set a personal note\n");
|
wprintw(window, " /note <msg> : Set a personal note\n");
|
||||||
@ -35,11 +40,9 @@ void cmd_chat_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*arg
|
|||||||
wprintw(window, " /quit or /exit : Exit Toxic\n");
|
wprintw(window, " /quit or /exit : Exit Toxic\n");
|
||||||
wprintw(window, " /help : Print this message again\n");
|
wprintw(window, " /help : Print this message again\n");
|
||||||
|
|
||||||
wattron(window, A_BOLD);
|
wattron(window, COLOR_PAIR(CYAN) | A_BOLD);
|
||||||
wprintw(window, "\n * Argument messages must be enclosed in quotation marks.\n");
|
wprintw(window, " * Argument messages must be enclosed in quotation marks.\n\n");
|
||||||
wattroff(window, A_BOLD);
|
wattroff(window, COLOR_PAIR(CYAN) | A_BOLD);
|
||||||
|
|
||||||
wattroff(window, COLOR_PAIR(CYAN));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
@ -66,8 +69,10 @@ void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*a
|
|||||||
|
|
||||||
void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
if (num_groupchats >= MAX_GROUPCHAT_NUM) {
|
if (num_active_windows() >= MAX_WINDOWS_NUM) {
|
||||||
wprintw(window, "Maximum number of group chats has been reached.\n");
|
wattron(window, COLOR_PAIR(RED));
|
||||||
|
wprintw(window, " * Warning: Too many windows are open.\n");
|
||||||
|
wattron(window, COLOR_PAIR(RED));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,7 +118,7 @@ void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
|
|||||||
|
|
||||||
uint8_t *filename = friends[self->num].file_receiver.filenames[filenum];
|
uint8_t *filename = friends[self->num].file_receiver.filenames[filenum];
|
||||||
|
|
||||||
if (tox_file_sendcontrol(m, self->num, 1, filenum, TOX_FILECONTROL_ACCEPT, 0, 0))
|
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);
|
wprintw(window, "Accepted file transfer %u. Saving file as: '%s'\n", filenum, filename);
|
||||||
else
|
else
|
||||||
wprintw(window, "File transfer failed.\n");
|
wprintw(window, "File transfer failed.\n");
|
||||||
@ -123,7 +128,7 @@ 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])
|
void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
if (num_file_senders >= MAX_FILES) {
|
if (max_file_senders_index >= (MAX_FILES-1)) {
|
||||||
wprintw(window,"Please wait for some of your outgoing file transfers to complete.\n");
|
wprintw(window,"Please wait for some of your outgoing file transfers to complete.\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -159,7 +164,9 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
|
|||||||
uint64_t filesize = ftell(file_to_send);
|
uint64_t filesize = ftell(file_to_send);
|
||||||
fseek(file_to_send, 0, SEEK_SET);
|
fseek(file_to_send, 0, SEEK_SET);
|
||||||
|
|
||||||
int filenum = tox_new_filesender(m, self->num, filesize, path, path_len + 1);
|
uint8_t filename[MAX_STR_SIZE];
|
||||||
|
get_file_name(path, filename);
|
||||||
|
int filenum = tox_new_file_sender(m, self->num, filesize, filename, strlen(filename) + 1);
|
||||||
|
|
||||||
if (filenum == -1) {
|
if (filenum == -1) {
|
||||||
wprintw(window, "Error sending file.\n");
|
wprintw(window, "Error sending file.\n");
|
||||||
@ -176,14 +183,14 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
|
|||||||
file_senders[i].file = file_to_send;
|
file_senders[i].file = file_to_send;
|
||||||
file_senders[i].filenum = (uint8_t) filenum;
|
file_senders[i].filenum = (uint8_t) filenum;
|
||||||
file_senders[i].friendnum = self->num;
|
file_senders[i].friendnum = self->num;
|
||||||
file_senders[i].timestamp = (uint64_t)time(NULL);
|
file_senders[i].timestamp = (uint64_t) time(NULL);
|
||||||
file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1,
|
file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1,
|
||||||
tox_filedata_size(m, self->num), file_to_send);
|
tox_file_data_size(m, self->num), file_to_send);
|
||||||
|
|
||||||
wprintw(window, "Sending file: '%s'\n", path);
|
wprintw(window, "Sending file: '%s'\n", path);
|
||||||
|
|
||||||
if (i == num_file_senders)
|
if (i == max_file_senders_index)
|
||||||
++num_file_senders;
|
++max_file_senders_index;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -15,8 +15,6 @@ struct cmd_func {
|
|||||||
void (*func)(WINDOW *w, ToxWindow *, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
void (*func)(WINDOW *w, ToxWindow *, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
};
|
};
|
||||||
|
|
||||||
#define GLOBAL_NUM_COMMANDS 13
|
|
||||||
|
|
||||||
static struct cmd_func global_commands[] = {
|
static struct cmd_func global_commands[] = {
|
||||||
{ "/accept", cmd_accept },
|
{ "/accept", cmd_accept },
|
||||||
{ "/add", cmd_add },
|
{ "/add", cmd_add },
|
||||||
@ -33,8 +31,6 @@ static struct cmd_func global_commands[] = {
|
|||||||
{ "/status", cmd_status },
|
{ "/status", cmd_status },
|
||||||
};
|
};
|
||||||
|
|
||||||
#define CHAT_NUM_COMMANDS 5
|
|
||||||
|
|
||||||
static struct cmd_func chat_commands[] = {
|
static struct cmd_func chat_commands[] = {
|
||||||
{ "/help", cmd_chat_help },
|
{ "/help", cmd_chat_help },
|
||||||
{ "/invite", cmd_groupinvite },
|
{ "/invite", cmd_groupinvite },
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#define MAX_NUM_ARGS 4 /* Includes command */
|
#define MAX_NUM_ARGS 4 /* Includes command */
|
||||||
|
#define GLOBAL_NUM_COMMANDS 13
|
||||||
|
#define CHAT_NUM_COMMANDS 5
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
GLOBAL_COMMAND_MODE,
|
GLOBAL_COMMAND_MODE,
|
||||||
|
179
src/friendlist.c
179
src/friendlist.c
@ -20,25 +20,27 @@ extern char *DATA_FILE;
|
|||||||
extern ToxWindow *prompt;
|
extern ToxWindow *prompt;
|
||||||
|
|
||||||
static int max_friends_index = 0; /* marks the index of the last friend in friends array */
|
static int max_friends_index = 0; /* marks the index of the last friend in friends array */
|
||||||
static int num_friends = 0;
|
|
||||||
static int num_selected = 0;
|
static int num_selected = 0;
|
||||||
|
static int num_friends = 0;
|
||||||
|
|
||||||
|
ToxicFriend friends[MAX_FRIENDS_NUM];
|
||||||
static int friendlist_index[MAX_FRIENDS_NUM] = {0};
|
static int friendlist_index[MAX_FRIENDS_NUM] = {0};
|
||||||
|
|
||||||
|
#define S_WEIGHT 100
|
||||||
|
|
||||||
static int index_name_cmp(const void *n1, const void *n2)
|
static int index_name_cmp(const void *n1, const void *n2)
|
||||||
{
|
{
|
||||||
int res = name_compare(friends[*(int *) n1].name, friends[*(int *) n2].name);
|
int res = qsort_strcasecmp_hlpr(friends[*(int *) n1].name, friends[*(int *) n2].name);
|
||||||
|
|
||||||
int k = 100;
|
|
||||||
/* Use weight to make qsort always put online friends before offline */
|
/* Use weight to make qsort always put online friends before offline */
|
||||||
res = friends[*(int *) n1].online ? (res - k) : (res + k);
|
res = friends[*(int *) n1].online ? (res - S_WEIGHT) : (res + S_WEIGHT);
|
||||||
res = friends[*(int *) n2].online ? (res + k) : (res - k);
|
res = friends[*(int *) n2].online ? (res + S_WEIGHT) : (res - S_WEIGHT);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* sorts friendlist_index first by connection status then alphabetically */
|
/* sorts friendlist_index first by connection status then alphabetically */
|
||||||
void sort_friendlist_index(void)
|
void sort_friendlist_index(Tox *m)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int n = 0;
|
int n = 0;
|
||||||
@ -53,37 +55,52 @@ void sort_friendlist_index(void)
|
|||||||
|
|
||||||
static void friendlist_onMessage(ToxWindow *self, Tox *m, int num, uint8_t *str, uint16_t len)
|
static void friendlist_onMessage(ToxWindow *self, Tox *m, int num, uint8_t *str, uint16_t len)
|
||||||
{
|
{
|
||||||
if (num < 0 || num >= max_friends_index)
|
if (num >= max_friends_index)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (friends[num].chatwin == -1)
|
if (friends[num].chatwin == -1) {
|
||||||
friends[num].chatwin = add_window(m, new_chat(m, friends[num].num));
|
if (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';
|
||||||
|
wprintw(prompt->window, "%s: %s\n", nick, str);
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
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, int num, uint8_t status)
|
||||||
{
|
{
|
||||||
if (num < 0 || num >= max_friends_index)
|
if (num >= max_friends_index)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
friends[num].online = status == 1 ? true : false;
|
friends[num].online = status == 1 ? true : false;
|
||||||
sort_friendlist_index();
|
sort_friendlist_index(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void friendlist_onNickChange(ToxWindow *self, int num, uint8_t *str, uint16_t len)
|
static void friendlist_onNickChange(ToxWindow *self, Tox *m, int num, uint8_t *str, uint16_t len)
|
||||||
{
|
{
|
||||||
if (len >= TOX_MAX_NAME_LENGTH || num < 0 || num >= max_friends_index)
|
if (len > TOX_MAX_NAME_LENGTH || num >= max_friends_index)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
str[TOXIC_MAX_NAME_LENGTH] = '\0';
|
str[TOXIC_MAX_NAME_LENGTH] = '\0';
|
||||||
len = strlen(str) + 1;
|
len = strlen(str) + 1;
|
||||||
memcpy(friends[num].name, str, len);
|
memcpy(friends[num].name, str, len);
|
||||||
friends[num].namelength = len;
|
friends[num].namelength = len;
|
||||||
sort_friendlist_index();
|
sort_friendlist_index(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void friendlist_onStatusChange(ToxWindow *self, Tox *m, int num, TOX_USERSTATUS status)
|
static void friendlist_onStatusChange(ToxWindow *self, Tox *m, int num, TOX_USERSTATUS status)
|
||||||
{
|
{
|
||||||
if (num < 0 || num >= max_friends_index)
|
if (num >= max_friends_index)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
friends[num].status = status;
|
friends[num].status = status;
|
||||||
@ -91,14 +108,14 @@ static void friendlist_onStatusChange(ToxWindow *self, Tox *m, int num, TOX_USER
|
|||||||
|
|
||||||
static void friendlist_onStatusMessageChange(ToxWindow *self, int num, uint8_t *str, uint16_t len)
|
static void friendlist_onStatusMessageChange(ToxWindow *self, int num, uint8_t *str, uint16_t len)
|
||||||
{
|
{
|
||||||
if (len >= TOX_MAX_STATUSMESSAGE_LENGTH || num < 0 || num >= max_friends_index)
|
if (len > TOX_MAX_STATUSMESSAGE_LENGTH || num >= max_friends_index)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
memcpy(friends[num].statusmsg, str, len);
|
memcpy(friends[num].statusmsg, str, len);
|
||||||
friends[num].statusmsg_len = len;
|
friends[num].statusmsg_len = len;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int num)
|
static void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int num, bool sort)
|
||||||
{
|
{
|
||||||
if (max_friends_index < 0 || max_friends_index >= MAX_FRIENDS_NUM)
|
if (max_friends_index < 0 || max_friends_index >= MAX_FRIENDS_NUM)
|
||||||
return;
|
return;
|
||||||
@ -112,8 +129,8 @@ static void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int num)
|
|||||||
friends[i].chatwin = -1;
|
friends[i].chatwin = -1;
|
||||||
friends[i].online = false;
|
friends[i].online = false;
|
||||||
friends[i].status = TOX_USERSTATUS_NONE;
|
friends[i].status = TOX_USERSTATUS_NONE;
|
||||||
friends[i].namelength = tox_getname(m, num, friends[i].name);
|
friends[i].namelength = tox_get_name(m, num, friends[i].name);
|
||||||
memset(friends[i].pending_groupchat, 0, TOX_CLIENT_ID_SIZE);
|
tox_get_client_id(m, num, friends[i].pub_key);
|
||||||
|
|
||||||
if (friends[i].namelength == -1 || friends[i].name[0] == '\0') {
|
if (friends[i].namelength == -1 || friends[i].name[0] == '\0') {
|
||||||
strcpy(friends[i].name, (uint8_t *) UNKNOWN_NAME);
|
strcpy(friends[i].name, (uint8_t *) UNKNOWN_NAME);
|
||||||
@ -123,12 +140,14 @@ static void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int num)
|
|||||||
friends[i].namelength = strlen(friends[i].name) + 1;
|
friends[i].namelength = strlen(friends[i].name) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
++num_friends;
|
num_friends = tox_count_friendlist(m);
|
||||||
|
|
||||||
if (i == max_friends_index)
|
if (i == max_friends_index)
|
||||||
++max_friends_index;
|
++max_friends_index;
|
||||||
|
|
||||||
sort_friendlist_index();
|
if (sort)
|
||||||
|
sort_friendlist_index(m);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -137,20 +156,50 @@ static void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int num)
|
|||||||
static void friendlist_onFileSendRequest(ToxWindow *self, Tox *m, int num, uint8_t filenum,
|
static void friendlist_onFileSendRequest(ToxWindow *self, Tox *m, int num, uint8_t filenum,
|
||||||
uint64_t filesize, uint8_t *filename, uint16_t filename_len)
|
uint64_t filesize, uint8_t *filename, uint16_t filename_len)
|
||||||
{
|
{
|
||||||
if (num < 0 || num >= max_friends_index)
|
if (num >= max_friends_index)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (friends[num].chatwin == -1)
|
if (friends[num].chatwin == -1) {
|
||||||
friends[num].chatwin = add_window(m, new_chat(m, friends[num].num));
|
if (num_active_windows() < MAX_WINDOWS_NUM) {
|
||||||
|
friends[num].chatwin = add_window(m, new_chat(m, friends[num].num));
|
||||||
|
} 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';
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
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, int num, uint8_t *group_pub_key)
|
||||||
{
|
{
|
||||||
if (num < 0 || num >= max_friends_index)
|
if (num >= max_friends_index)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (friends[num].chatwin == -1)
|
if (friends[num].chatwin == -1) {
|
||||||
friends[num].chatwin = add_window(m, new_chat(m, friends[num].num));
|
if (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';
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
alert_window(prompt, WINDOW_ALERT_1, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void select_friend(ToxWindow *self, Tox *m, wint_t key)
|
static void select_friend(ToxWindow *self, Tox *m, wint_t key)
|
||||||
@ -165,8 +214,8 @@ static void select_friend(ToxWindow *self, Tox *m, wint_t key)
|
|||||||
|
|
||||||
static void delete_friend(Tox *m, ToxWindow *self, int f_num, wint_t key)
|
static void delete_friend(Tox *m, ToxWindow *self, int f_num, wint_t key)
|
||||||
{
|
{
|
||||||
tox_delfriend(m, f_num);
|
tox_del_friend(m, f_num);
|
||||||
memset(&friends[f_num], 0, sizeof(friend_t));
|
memset(&friends[f_num], 0, sizeof(ToxicFriend));
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@ -176,13 +225,13 @@ static void delete_friend(Tox *m, ToxWindow *self, int f_num, wint_t key)
|
|||||||
}
|
}
|
||||||
|
|
||||||
max_friends_index = i;
|
max_friends_index = i;
|
||||||
--num_friends;
|
num_friends = tox_count_friendlist(m);
|
||||||
|
|
||||||
/* make sure num_selected stays within num_friends range */
|
/* make sure num_selected stays within num_friends range */
|
||||||
if (num_friends && num_selected == num_friends)
|
if (num_friends && num_selected == num_friends)
|
||||||
--num_selected;
|
--num_selected;
|
||||||
|
|
||||||
sort_friendlist_index();
|
sort_friendlist_index(m);
|
||||||
store_data(m, DATA_FILE);
|
store_data(m, DATA_FILE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,38 +246,62 @@ static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key)
|
|||||||
/* Jump to chat window if already open */
|
/* Jump to chat window if already open */
|
||||||
if (friends[f].chatwin != -1) {
|
if (friends[f].chatwin != -1) {
|
||||||
set_active_window(friends[f].chatwin);
|
set_active_window(friends[f].chatwin);
|
||||||
} else {
|
} else if (num_active_windows() < MAX_WINDOWS_NUM) {
|
||||||
friends[f].chatwin = add_window(m, new_chat(m, friends[f].num));
|
friends[f].chatwin = add_window(m, new_chat(m, friends[f].num));
|
||||||
set_active_window(friends[f].chatwin);
|
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));
|
||||||
|
|
||||||
|
alert_window(prompt, WINDOW_ALERT_1, true);
|
||||||
}
|
}
|
||||||
} else if (key == 0x107 || key == 0x8 || key == 0x7f) {
|
} else if (key == KEY_DC) {
|
||||||
delete_friend(m, self, f, key);
|
delete_friend(m, self, f, key);
|
||||||
} else {
|
} else {
|
||||||
select_friend(self, m, key);
|
select_friend(self, m, key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define FLIST_OFST 4 /* Accounts for the lines at top */
|
||||||
|
|
||||||
static void friendlist_onDraw(ToxWindow *self, Tox *m)
|
static void friendlist_onDraw(ToxWindow *self, Tox *m)
|
||||||
{
|
{
|
||||||
curs_set(0);
|
curs_set(0);
|
||||||
werase(self->window);
|
werase(self->window);
|
||||||
int x, y;
|
int x2, y2;
|
||||||
getmaxyx(self->window, y, x);
|
getmaxyx(self->window, y2, x2);
|
||||||
|
|
||||||
bool fix_statuses = x != self->x; /* true if window x axis has changed */
|
bool fix_statuses = x2 != self->x; /* true if window x axis has changed */
|
||||||
|
|
||||||
if (num_friends == 0) {
|
wattron(self->window, COLOR_PAIR(CYAN));
|
||||||
wprintw(self->window, "Empty. Add some friends! :-)\n");
|
wprintw(self->window, " Open a chat window with the");
|
||||||
} else {
|
wattron(self->window, A_BOLD);
|
||||||
wattron(self->window, COLOR_PAIR(CYAN) | A_BOLD);
|
wprintw(self->window, " Enter ");
|
||||||
wprintw(self->window, " Open chat with up/down keys and enter.\n");
|
wattroff(self->window, A_BOLD);
|
||||||
wprintw(self->window, " Delete friends with the backspace key.\n\n");
|
wprintw(self->window, "key. Delete a friend with the");
|
||||||
wattroff(self->window, COLOR_PAIR(CYAN) | A_BOLD);
|
wattron(self->window, A_BOLD);
|
||||||
}
|
wprintw(self->window, " Delete ");
|
||||||
|
wattroff(self->window, A_BOLD);
|
||||||
|
wprintw(self->window, "key.\n\n");
|
||||||
|
wattroff(self->window, COLOR_PAIR(CYAN));
|
||||||
|
|
||||||
|
wattron(self->window, A_BOLD);
|
||||||
|
wprintw(self->window, " Friends: %d/%d \n\n", tox_get_num_online_friends(m), num_friends);
|
||||||
|
wattroff(self->window, A_BOLD);
|
||||||
|
|
||||||
|
if ((y2 - FLIST_OFST) <= 0) /* don't allow division by zero */
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Determine which portion of friendlist to draw based on current position */
|
||||||
|
int page = num_selected / (y2 - FLIST_OFST);
|
||||||
|
int start = (y2 - FLIST_OFST) * page;
|
||||||
|
int end = y2 - FLIST_OFST + start;
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < num_friends && i < y-3; ++i) {
|
for (i = start; i < num_friends && i < end; ++i) {
|
||||||
int f = friendlist_index[i];
|
int f = friendlist_index[i];
|
||||||
bool f_selected = false;
|
bool f_selected = false;
|
||||||
|
|
||||||
@ -275,13 +348,13 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
|
|||||||
/* Reset friends[f].statusmsg on window resize */
|
/* Reset friends[f].statusmsg on window resize */
|
||||||
if (fix_statuses) {
|
if (fix_statuses) {
|
||||||
uint8_t statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH] = {'\0'};
|
uint8_t statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH] = {'\0'};
|
||||||
tox_copy_statusmessage(m, friends[f].num, statusmsg, TOX_MAX_STATUSMESSAGE_LENGTH);
|
tox_get_status_message(m, friends[f].num, statusmsg, TOX_MAX_STATUSMESSAGE_LENGTH);
|
||||||
snprintf(friends[f].statusmsg, sizeof(friends[f].statusmsg), "%s", statusmsg);
|
snprintf(friends[f].statusmsg, sizeof(friends[f].statusmsg), "%s", statusmsg);
|
||||||
friends[f].statusmsg_len = tox_get_statusmessage_size(m, f);
|
friends[f].statusmsg_len = tox_get_status_message_size(m, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Truncate note if it doesn't fit on one line */
|
/* Truncate note if it doesn't fit on one line */
|
||||||
uint16_t maxlen = x - getcurx(self->window) - 4;
|
uint16_t maxlen = x2 - getcurx(self->window) - 4;
|
||||||
if (friends[f].statusmsg_len > maxlen) {
|
if (friends[f].statusmsg_len > maxlen) {
|
||||||
friends[f].statusmsg[maxlen-3] = '\0';
|
friends[f].statusmsg[maxlen-3] = '\0';
|
||||||
strcat(friends[f].statusmsg, "...");
|
strcat(friends[f].statusmsg, "...");
|
||||||
@ -291,7 +364,11 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
|
|||||||
|
|
||||||
wprintw(self->window, " (%s)\n", friends[f].statusmsg);
|
wprintw(self->window, " (%s)\n", friends[f].statusmsg);
|
||||||
} else {
|
} else {
|
||||||
wprintw(self->window, "[O]");
|
wprintw(self->window, "[");
|
||||||
|
wattron(self->window, A_BOLD);
|
||||||
|
wprintw(self->window, "O");
|
||||||
|
wattroff(self->window, A_BOLD);
|
||||||
|
wprintw(self->window, "]");
|
||||||
|
|
||||||
if (f_selected)
|
if (f_selected)
|
||||||
wattron(self->window, A_BOLD);
|
wattron(self->window, A_BOLD);
|
||||||
@ -304,7 +381,7 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self->x = x;
|
self->x = x2;
|
||||||
wrefresh(self->window);
|
wrefresh(self->window);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -323,6 +400,8 @@ ToxWindow new_friendlist(void)
|
|||||||
ToxWindow ret;
|
ToxWindow ret;
|
||||||
memset(&ret, 0, sizeof(ret));
|
memset(&ret, 0, sizeof(ret));
|
||||||
|
|
||||||
|
ret.active = true;
|
||||||
|
|
||||||
ret.onKey = &friendlist_onKey;
|
ret.onKey = &friendlist_onKey;
|
||||||
ret.onDraw = &friendlist_onDraw;
|
ret.onDraw = &friendlist_onDraw;
|
||||||
ret.onInit = &friendlist_onInit;
|
ret.onInit = &friendlist_onInit;
|
||||||
|
@ -3,11 +3,27 @@
|
|||||||
|
|
||||||
#include "toxic_windows.h"
|
#include "toxic_windows.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t name[TOX_MAX_NAME_LENGTH];
|
||||||
|
uint16_t namelength;
|
||||||
|
uint8_t statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH];
|
||||||
|
uint16_t statusmsg_len;
|
||||||
|
uint8_t pending_groupchat[TOX_CLIENT_ID_SIZE];
|
||||||
|
uint8_t pub_key[TOX_CLIENT_ID_SIZE];
|
||||||
|
int num;
|
||||||
|
int chatwin;
|
||||||
|
bool active;
|
||||||
|
bool online;
|
||||||
|
bool is_typing;
|
||||||
|
TOX_USERSTATUS status;
|
||||||
|
struct FileReceiver file_receiver;
|
||||||
|
} ToxicFriend;
|
||||||
|
|
||||||
ToxWindow new_friendlist(void);
|
ToxWindow new_friendlist(void);
|
||||||
void disable_chatwin(int f_num);
|
void disable_chatwin(int f_num);
|
||||||
int get_friendnum(uint8_t *name);
|
int get_friendnum(uint8_t *name);
|
||||||
|
|
||||||
/* sorts friendlist_index first by connection status then alphabetically */
|
/* sorts friendlist_index first by connection status then alphabetically */
|
||||||
void sort_friendlist_index(void);
|
void sort_friendlist_index(Tox *m);
|
||||||
|
|
||||||
#endif /* end of include guard: FRIENDLIST_H_53I41IM */
|
#endif /* end of include guard: FRIENDLIST_H_53I41IM */
|
||||||
|
@ -11,13 +11,15 @@
|
|||||||
|
|
||||||
#include "toxic_windows.h"
|
#include "toxic_windows.h"
|
||||||
#include "misc_tools.h"
|
#include "misc_tools.h"
|
||||||
|
#include "friendlist.h"
|
||||||
|
|
||||||
extern char *DATA_FILE;
|
extern char *DATA_FILE;
|
||||||
extern ToxWindow *prompt;
|
extern ToxWindow *prompt;
|
||||||
|
|
||||||
|
extern ToxicFriend friends[MAX_FRIENDS_NUM];
|
||||||
|
|
||||||
extern uint8_t pending_frnd_requests[MAX_FRIENDS_NUM][TOX_CLIENT_ID_SIZE];
|
extern uint8_t pending_frnd_requests[MAX_FRIENDS_NUM][TOX_CLIENT_ID_SIZE];
|
||||||
extern uint8_t num_frnd_requests;
|
extern uint8_t num_frnd_requests;
|
||||||
extern int num_groupchats;
|
|
||||||
|
|
||||||
/* command functions */
|
/* command functions */
|
||||||
void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
@ -40,13 +42,13 @@ void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int friendnum = tox_addfriend_norequest(m, pending_frnd_requests[req]);
|
int friendnum = tox_add_friend_norequest(m, pending_frnd_requests[req]);
|
||||||
|
|
||||||
if (friendnum == -1)
|
if (friendnum == -1)
|
||||||
wprintw(window, "Failed to add friend.\n");
|
wprintw(window, "Failed to add friend.\n");
|
||||||
else {
|
else {
|
||||||
wprintw(window, "Friend request accepted.\n");
|
wprintw(window, "Friend request accepted.\n");
|
||||||
on_friendadded(m, friendnum);
|
on_friendadded(m, friendnum, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(&pending_frnd_requests[req], 0, TOX_CLIENT_ID_SIZE);
|
memset(&pending_frnd_requests[req], 0, TOX_CLIENT_ID_SIZE);
|
||||||
@ -83,7 +85,7 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
|
|||||||
snprintf(msg, sizeof(msg), "%s", temp);
|
snprintf(msg, sizeof(msg), "%s", temp);
|
||||||
} else {
|
} else {
|
||||||
uint8_t selfname[TOX_MAX_NAME_LENGTH];
|
uint8_t selfname[TOX_MAX_NAME_LENGTH];
|
||||||
tox_getselfname(m, selfname, TOX_MAX_NAME_LENGTH);
|
tox_get_self_name(m, selfname, TOX_MAX_NAME_LENGTH);
|
||||||
snprintf(msg, sizeof(msg), "Hello, my name is %s. Care to Tox?", selfname);
|
snprintf(msg, sizeof(msg), "Hello, my name is %s. Care to Tox?", selfname);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,7 +116,7 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
|
|||||||
id[i] = toupper(id[i]);
|
id[i] = toupper(id[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
int f_num = tox_addfriend(m, id_bin, msg, strlen(msg) + 1);
|
int f_num = tox_add_friend(m, id_bin, msg, strlen(msg) + 1);
|
||||||
|
|
||||||
switch (f_num) {
|
switch (f_num) {
|
||||||
case TOX_FAERR_TOOLONG:
|
case TOX_FAERR_TOOLONG:
|
||||||
@ -136,11 +138,11 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
|
|||||||
wprintw(window, "Bad checksum in address.\n");
|
wprintw(window, "Bad checksum in address.\n");
|
||||||
break;
|
break;
|
||||||
case TOX_FAERR_SETNEWNOSPAM:
|
case TOX_FAERR_SETNEWNOSPAM:
|
||||||
wprintw(window, "Nospam was different.\n");
|
wprintw(window, "Nospam was different (is this contact already added?)\n");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
wprintw(window, "Friend request sent.\n");
|
wprintw(window, "Friend request sent.\n");
|
||||||
on_friendadded(m, f_num);
|
on_friendadded(m, f_num, true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -177,8 +179,10 @@ 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])
|
void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
if (num_groupchats >= MAX_GROUPCHAT_NUM) {
|
if (num_active_windows() >= MAX_WINDOWS_NUM) {
|
||||||
wprintw(window, "Maximum number of group chats has been reached.\n");
|
wattron(window, COLOR_PAIR(RED));
|
||||||
|
wprintw(window, " * Warning: Too many windows are open.\n");
|
||||||
|
wattron(window, COLOR_PAIR(RED));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,7 +206,7 @@ void cmd_myid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
|
|||||||
{
|
{
|
||||||
char id[TOX_FRIEND_ADDRESS_SIZE * 2 + 1] = {0};
|
char id[TOX_FRIEND_ADDRESS_SIZE * 2 + 1] = {0};
|
||||||
uint8_t address[TOX_FRIEND_ADDRESS_SIZE];
|
uint8_t address[TOX_FRIEND_ADDRESS_SIZE];
|
||||||
tox_getaddress(m, address);
|
tox_get_address(m, address);
|
||||||
|
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
@ -242,7 +246,7 @@ void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
|
|||||||
len = TOXIC_MAX_NAME_LENGTH;
|
len = TOXIC_MAX_NAME_LENGTH;
|
||||||
}
|
}
|
||||||
|
|
||||||
tox_setname(m, nick, len+1);
|
tox_set_name(m, nick, len+1);
|
||||||
prompt_update_nick(prompt, nick, len+1);
|
prompt_update_nick(prompt, nick, len+1);
|
||||||
|
|
||||||
store_data(m, DATA_FILE);
|
store_data(m, DATA_FILE);
|
||||||
@ -264,7 +268,7 @@ void cmd_note(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
|
|||||||
|
|
||||||
msg[strlen(++msg)-1] = L'\0';
|
msg[strlen(++msg)-1] = L'\0';
|
||||||
uint16_t len = strlen(msg) + 1;
|
uint16_t len = strlen(msg) + 1;
|
||||||
tox_set_statusmessage(m, msg, len);
|
tox_set_status_message(m, msg, len);
|
||||||
|
|
||||||
prompt_update_statusmessage(prompt, msg, len);
|
prompt_update_statusmessage(prompt, msg, len);
|
||||||
}
|
}
|
||||||
@ -274,7 +278,7 @@ void cmd_prompt_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*a
|
|||||||
wclear(window);
|
wclear(window);
|
||||||
wattron(window, COLOR_PAIR(CYAN) | A_BOLD);
|
wattron(window, COLOR_PAIR(CYAN) | A_BOLD);
|
||||||
wprintw(window, "\n\nGlobal commands:\n");
|
wprintw(window, "\n\nGlobal commands:\n");
|
||||||
wattroff(window, A_BOLD);
|
wattroff(window, COLOR_PAIR(CYAN) | A_BOLD);
|
||||||
|
|
||||||
wprintw(window, " /add <id> <msg> : Add friend with optional message\n");
|
wprintw(window, " /add <id> <msg> : Add friend with optional message\n");
|
||||||
wprintw(window, " /accept <n> : Accept friend request\n");
|
wprintw(window, " /accept <n> : Accept friend request\n");
|
||||||
@ -288,12 +292,10 @@ void cmd_prompt_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*a
|
|||||||
wprintw(window, " /help : Print this message again\n");
|
wprintw(window, " /help : Print this message again\n");
|
||||||
wprintw(window, " /clear : Clear the window\n");
|
wprintw(window, " /clear : Clear the window\n");
|
||||||
|
|
||||||
wattron(window, A_BOLD);
|
wattron(window, COLOR_PAIR(CYAN) | A_BOLD);
|
||||||
wprintw(window, " * Argument messages must be enclosed in quotation marks.\n");
|
wprintw(window, " * Argument messages must be enclosed in quotation marks.\n");
|
||||||
wprintw(window, " * Use TAB and Shift-TAB to navigate through the tabs.\n");
|
wprintw(window, " * Use ctrl-o and ctrl-p to navigate through the tabs.\n\n");
|
||||||
wattroff(window, A_BOLD);
|
wattroff(window, COLOR_PAIR(CYAN) | A_BOLD);
|
||||||
|
|
||||||
wattroff(window, COLOR_PAIR(CYAN));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmd_quit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_quit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
@ -338,13 +340,13 @@ void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
tox_set_userstatus(m, status_kind);
|
tox_set_user_status(m, status_kind);
|
||||||
prompt_update_status(prompt, status_kind);
|
prompt_update_status(prompt, status_kind);
|
||||||
|
|
||||||
if (msg != NULL) {
|
if (msg != NULL) {
|
||||||
msg[strlen(++msg)-1] = L'\0'; /* remove opening and closing quotes */
|
msg[strlen(++msg)-1] = L'\0'; /* remove opening and closing quotes */
|
||||||
uint16_t len = strlen(msg) + 1;
|
uint16_t len = strlen(msg) + 1;
|
||||||
tox_set_statusmessage(m, msg, len);
|
tox_set_status_message(m, msg, len);
|
||||||
prompt_update_statusmessage(prompt, msg, len);
|
prompt_update_statusmessage(prompt, msg, len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
404
src/groupchat.c
404
src/groupchat.c
@ -14,16 +14,18 @@
|
|||||||
#include "execute.h"
|
#include "execute.h"
|
||||||
#include "misc_tools.h"
|
#include "misc_tools.h"
|
||||||
#include "groupchat.h"
|
#include "groupchat.h"
|
||||||
|
#include "prompt.h"
|
||||||
static GroupChat groupchats[MAX_GROUPCHAT_NUM];
|
#include "toxic_strings.h"
|
||||||
static int max_groupchat_index = 0;
|
|
||||||
int num_groupchats = 0;
|
|
||||||
|
|
||||||
ToxWindow new_group_chat(Tox *m, int groupnum);
|
|
||||||
|
|
||||||
extern char *DATA_FILE;
|
extern char *DATA_FILE;
|
||||||
extern int store_data(Tox *m, char *path);
|
extern int store_data(Tox *m, char *path);
|
||||||
|
|
||||||
|
static GroupChat groupchats[MAX_WINDOWS_NUM];
|
||||||
|
static int max_groupchat_index = 0;
|
||||||
|
|
||||||
|
/* temporary until group chats have unique commands */
|
||||||
|
extern glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE];
|
||||||
|
|
||||||
int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum)
|
int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@ -44,8 +46,6 @@ int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum)
|
|||||||
if (i == max_groupchat_index)
|
if (i == max_groupchat_index)
|
||||||
++max_groupchat_index;
|
++max_groupchat_index;
|
||||||
|
|
||||||
++num_groupchats;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -68,7 +68,6 @@ static void close_groupchatwin(Tox *m, int groupnum)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
--num_groupchats;
|
|
||||||
max_groupchat_index = i;
|
max_groupchat_index = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,11 +75,11 @@ static void print_groupchat_help(ChatContext *ctx)
|
|||||||
{
|
{
|
||||||
wattron(ctx->history, COLOR_PAIR(CYAN) | A_BOLD);
|
wattron(ctx->history, COLOR_PAIR(CYAN) | A_BOLD);
|
||||||
wprintw(ctx->history, "Group chat commands:\n");
|
wprintw(ctx->history, "Group chat commands:\n");
|
||||||
wattroff(ctx->history, A_BOLD);
|
wattroff(ctx->history, COLOR_PAIR(CYAN) | A_BOLD);
|
||||||
|
|
||||||
wprintw(ctx->history, " /add <id> <msg> : Add friend with optional message\n");
|
wprintw(ctx->history, " /add <id> <msg> : Add friend with optional message\n");
|
||||||
wprintw(ctx->history, " /status <type> <msg>: Set your status with optional note\n");
|
wprintw(ctx->history, " /status <type> <msg>: Set your status with optional note\n");
|
||||||
wprintw(ctx->history, " /note <msg> : Set a personal note\n");
|
wprintw(ctx->history, " /note <msg> : Set a personal note\n");
|
||||||
wprintw(ctx->history, " /nick <nick> : Set your nickname\n");
|
wprintw(ctx->history, " /nick <nick> : Set your nickname\n");
|
||||||
wprintw(ctx->history, " /groupchat : Create a group chat\n");
|
wprintw(ctx->history, " /groupchat : Create a group chat\n");
|
||||||
wprintw(ctx->history, " /myid : Print your ID\n");
|
wprintw(ctx->history, " /myid : Print your ID\n");
|
||||||
@ -89,11 +88,10 @@ static void print_groupchat_help(ChatContext *ctx)
|
|||||||
wprintw(ctx->history, " /quit or /exit : Exit Toxic\n");
|
wprintw(ctx->history, " /quit or /exit : Exit Toxic\n");
|
||||||
wprintw(ctx->history, " /help : Print this message again\n");
|
wprintw(ctx->history, " /help : Print this message again\n");
|
||||||
|
|
||||||
wattron(ctx->history, A_BOLD);
|
wattron(ctx->history, COLOR_PAIR(CYAN) | A_BOLD);
|
||||||
wprintw(ctx->history, "\n * Argument messages must be enclosed in quotation marks.\n");
|
wprintw(ctx->history, " * Argument messages must be enclosed in quotation marks.\n");
|
||||||
wattroff(ctx->history, A_BOLD);
|
wprintw(ctx->history, " * Scroll peer list with the Page Up/Page Down keys.\n\n");
|
||||||
|
wattroff(ctx->history, COLOR_PAIR(CYAN) | A_BOLD);
|
||||||
wattroff(ctx->history, COLOR_PAIR(CYAN));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, int groupnum, int peernum,
|
static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, int groupnum, int peernum,
|
||||||
@ -102,25 +100,76 @@ static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, int groupnum, int
|
|||||||
if (self->num != groupnum)
|
if (self->num != groupnum)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ChatContext *ctx = (ChatContext *) self->chatwin;
|
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 */
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
int nick_clr = strcmp(nick, selfnick) == 0 ? GREEN : CYAN;
|
||||||
|
|
||||||
|
bool nick_match = strcasestr(msg, selfnick) && strncmp(selfnick, nick, TOXIC_MAX_NAME_LENGTH);
|
||||||
|
|
||||||
|
if (nick_match) {
|
||||||
|
alert_type = WINDOW_ALERT_0;
|
||||||
|
beep = true;
|
||||||
|
nick_clr = RED;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void groupchat_onGroupAction(ToxWindow *self, Tox *m, int groupnum, int peernum, uint8_t *action,
|
||||||
|
uint16_t len)
|
||||||
|
{
|
||||||
|
if (self->num != groupnum)
|
||||||
|
return;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
bool nick_match = strcasestr(action, selfnick);
|
||||||
|
|
||||||
|
if (nick_match) {
|
||||||
|
alert_type = WINDOW_ALERT_0;
|
||||||
|
beep = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
alert_window(self, alert_type, beep);
|
||||||
|
|
||||||
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
|
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
|
||||||
tox_group_peername(m, groupnum, peernum, nick);
|
tox_group_peername(m, groupnum, peernum, nick);
|
||||||
nick[TOXIC_MAX_NAME_LENGTH] = '\0'; /* enforce client max name length */
|
nick[TOXIC_MAX_NAME_LENGTH] = '\0'; /* enforce client max name length */
|
||||||
|
|
||||||
print_time(ctx->history);
|
print_time(ctx->history);
|
||||||
wattron(ctx->history, COLOR_PAIR(BLUE));
|
wattron(ctx->history, COLOR_PAIR(YELLOW));
|
||||||
wprintw(ctx->history, "%s: ", nick);
|
wprintw(ctx->history, "* %s %s\n", nick, action);
|
||||||
wattroff(ctx->history, COLOR_PAIR(BLUE));
|
wattroff(ctx->history, COLOR_PAIR(YELLOW));
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
self->blink = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Puts two copies of peerlist in chat instance */
|
/* Puts two copies of peerlist in chat instance */
|
||||||
@ -144,10 +193,12 @@ static void copy_peernames(int gnum, int npeers, uint8_t tmp_peerlist[][TOX_MAX_
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < npeers; ++i) {
|
for (i = 0; i < npeers; ++i) {
|
||||||
if (string_is_empty(tmp_peerlist[i]))
|
if (string_is_empty(tmp_peerlist[i])) {
|
||||||
memcpy(&groupchats[gnum].peer_names[i*N], UNKNOWN_NAME, sizeof(UNKNOWN_NAME));
|
memcpy(&groupchats[gnum].peer_names[i*N], UNKNOWN_NAME, sizeof(UNKNOWN_NAME));
|
||||||
else
|
} else {
|
||||||
memcpy(&groupchats[gnum].peer_names[i*N], tmp_peerlist[i], N);
|
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].oldpeer_names, groupchats[gnum].peer_names, N*npeers);
|
memcpy(groupchats[gnum].oldpeer_names, groupchats[gnum].peer_names, N*npeers);
|
||||||
@ -171,16 +222,16 @@ static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnu
|
|||||||
|
|
||||||
/* Update name lists */
|
/* Update name lists */
|
||||||
uint8_t tmp_peerlist[num_peers][TOX_MAX_NAME_LENGTH];
|
uint8_t tmp_peerlist[num_peers][TOX_MAX_NAME_LENGTH];
|
||||||
tox_group_copy_names(m, groupnum, tmp_peerlist, num_peers);
|
tox_group_get_names(m, groupnum, tmp_peerlist, num_peers);
|
||||||
copy_peernames(groupnum, num_peers, tmp_peerlist);
|
copy_peernames(groupnum, num_peers, tmp_peerlist);
|
||||||
|
|
||||||
/* get current peername then sort namelist */
|
/* get current peername then sort namelist */
|
||||||
uint8_t peername[TOX_MAX_NAME_LENGTH] = {0};
|
uint8_t peername[TOX_MAX_NAME_LENGTH] = {0};
|
||||||
memcpy(peername, &groupchats[groupnum].peer_names[peernum*TOX_MAX_NAME_LENGTH], sizeof(peername));
|
memcpy(peername, &groupchats[groupnum].peer_names[peernum*TOX_MAX_NAME_LENGTH], sizeof(peername));
|
||||||
|
|
||||||
qsort(groupchats[groupnum].peer_names, groupchats[groupnum].num_peers, TOX_MAX_NAME_LENGTH, name_compare);
|
qsort(groupchats[groupnum].peer_names, groupchats[groupnum].num_peers, TOX_MAX_NAME_LENGTH, qsort_strcasecmp_hlpr);
|
||||||
|
|
||||||
ChatContext *ctx = (ChatContext *) self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
print_time(ctx->history);
|
print_time(ctx->history);
|
||||||
|
|
||||||
switch (change) {
|
switch (change) {
|
||||||
@ -193,12 +244,14 @@ static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnu
|
|||||||
wattroff(ctx->history, COLOR_PAIR(GREEN));
|
wattroff(ctx->history, COLOR_PAIR(GREEN));
|
||||||
break;
|
break;
|
||||||
case TOX_CHAT_CHANGE_PEER_DEL:
|
case TOX_CHAT_CHANGE_PEER_DEL:
|
||||||
wattron(ctx->history, COLOR_PAIR(RED));
|
|
||||||
wattron(ctx->history, A_BOLD);
|
wattron(ctx->history, A_BOLD);
|
||||||
wprintw(ctx->history, "* %s", oldpeername);
|
wprintw(ctx->history, "* %s", oldpeername);
|
||||||
wattroff(ctx->history, A_BOLD);
|
wattroff(ctx->history, A_BOLD);
|
||||||
wprintw(ctx->history, " has left the room\n");
|
wprintw(ctx->history, " has left the room\n");
|
||||||
wattroff(ctx->history, COLOR_PAIR(RED));
|
|
||||||
|
if (groupchats[self->num].side_pos > 0)
|
||||||
|
--groupchats[self->num].side_pos;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case TOX_CHAT_CHANGE_PEER_NAME:
|
case TOX_CHAT_CHANGE_PEER_NAME:
|
||||||
wattron(ctx->history, COLOR_PAIR(MAGENTA));
|
wattron(ctx->history, COLOR_PAIR(MAGENTA));
|
||||||
@ -214,48 +267,202 @@ static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnu
|
|||||||
wattroff(ctx->history, COLOR_PAIR(MAGENTA));
|
wattroff(ctx->history, COLOR_PAIR(MAGENTA));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
alert_window(self, WINDOW_ALERT_2, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void send_group_action(ToxWindow *self, ChatContext *ctx, Tox *m, uint8_t *action) {
|
||||||
|
if (action == NULL) {
|
||||||
|
wprintw(ctx->history, "Invalid syntax.\n");
|
||||||
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key)
|
static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key)
|
||||||
{
|
{
|
||||||
ChatContext *ctx = (ChatContext *) self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
int x, y, y2, x2;
|
int x, y, y2, x2;
|
||||||
getyx(self->window, y, x);
|
getyx(self->window, y, x);
|
||||||
getmaxyx(self->window, y2, x2);
|
getmaxyx(self->window, y2, x2);
|
||||||
|
int cur_len = 0;
|
||||||
|
|
||||||
/* BACKSPACE key: Remove one character from line */
|
if (key == 0x107 || key == 0x8 || key == 0x7f) { /* BACKSPACE key: Remove character behind pos */
|
||||||
if (key == 0x107 || key == 0x8 || key == 0x7f) {
|
|
||||||
if (ctx->pos > 0) {
|
if (ctx->pos > 0) {
|
||||||
ctx->line[--ctx->pos] = L'\0';
|
cur_len = MAX(1, wcwidth(ctx->line[ctx->pos - 1]));
|
||||||
|
del_char_buf_bck(ctx->line, &ctx->pos, &ctx->len);
|
||||||
|
|
||||||
if (x == 0)
|
if (x == 0)
|
||||||
mvwdelch(self->window, y - 1, x2 - 1);
|
wmove(self->window, y-1, x2 - cur_len);
|
||||||
else
|
else
|
||||||
mvwdelch(self->window, y, x - 1);
|
wmove(self->window, y, x - cur_len);
|
||||||
|
} else {
|
||||||
|
beep();
|
||||||
}
|
}
|
||||||
} else
|
}
|
||||||
/* Add printable chars to buffer and print on input space */
|
|
||||||
|
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) { /* HOME key: Move cursor to beginning of line */
|
||||||
|
if (ctx->pos > 0) {
|
||||||
|
ctx->pos = 0;
|
||||||
|
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (key == KEY_END) { /* END 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 HAVE_WIDECHAR
|
||||||
if (iswprint(key)) {
|
if (iswprint(key))
|
||||||
#else
|
#else
|
||||||
if (isprint(key)) {
|
if (isprint(key))
|
||||||
#endif
|
#endif
|
||||||
if (ctx->pos < (MAX_STR_SIZE-1)) {
|
{ /* prevents buffer overflows and strange behaviour when cursor goes past the window */
|
||||||
mvwaddstr(self->window, y, x, wc_to_char(key));
|
if ( (ctx->len < MAX_STR_SIZE-1) && (ctx->len < (x2 * (CHATBOX_HEIGHT - 1)-1)) ) {
|
||||||
ctx->line[ctx->pos++] = key;
|
add_char_to_buf(ctx->line, &ctx->pos, &ctx->len, key);
|
||||||
ctx->line[ctx->pos] = L'\0';
|
|
||||||
|
if (x == x2-1)
|
||||||
|
wmove(self->window, y+1, 0);
|
||||||
|
else
|
||||||
|
wmove(self->window, y, x + MAX(1, wcwidth(key)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* RETURN key: Execute command or print line */
|
/* RETURN key: Execute command or print line */
|
||||||
else if (key == '\n') {
|
else if (key == '\n') {
|
||||||
uint8_t *line = wcs_to_char(ctx->line);
|
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);
|
wclear(ctx->linewin);
|
||||||
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
|
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
|
||||||
wclrtobot(self->window);
|
wclrtobot(self->window);
|
||||||
bool close_win = false;
|
bool close_win = false;
|
||||||
|
|
||||||
|
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 (line[0] == '/') {
|
||||||
if (close_win = strcmp(line, "/close") == 0) {
|
if (close_win = strcmp(line, "/close") == 0) {
|
||||||
set_active_window(0);
|
set_active_window(0);
|
||||||
@ -263,65 +470,78 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key)
|
|||||||
delwin(ctx->linewin);
|
delwin(ctx->linewin);
|
||||||
del_window(self);
|
del_window(self);
|
||||||
close_groupchatwin(m, groupnum);
|
close_groupchatwin(m, groupnum);
|
||||||
} else if (strncmp(line, "/help", strlen("/help")) == 0)
|
} else if (strcmp(line, "/help") == 0)
|
||||||
print_groupchat_help(ctx);
|
print_groupchat_help(ctx);
|
||||||
|
else if (strncmp(line, "/me ", strlen("/me ")) == 0)
|
||||||
|
send_group_action(self, ctx, m, line + strlen("/me "));
|
||||||
else
|
else
|
||||||
execute(ctx->history, self, m, line, GROUPCHAT_COMMAND_MODE);
|
execute(ctx->history, self, m, line, GROUPCHAT_COMMAND_MODE);
|
||||||
} else {
|
} else if (!string_is_empty(line)) {
|
||||||
/* make sure the string has at least non-space character */
|
if (tox_group_message_send(m, self->num, line, strlen(line) + 1) == -1) {
|
||||||
if (!string_is_empty(line)) {
|
wattron(ctx->history, COLOR_PAIR(RED));
|
||||||
// uint8_t selfname[TOX_MAX_NAME_LENGTH];
|
wprintw(ctx->history, " * Failed to send message.\n");
|
||||||
// tox_getselfname(m, selfname, TOX_MAX_NAME_LENGTH);
|
wattroff(ctx->history, COLOR_PAIR(RED));
|
||||||
|
|
||||||
// print_time(ctx->history);
|
|
||||||
// wattron(ctx->history, COLOR_PAIR(GREEN));
|
|
||||||
// wprintw(ctx->history, "%s: ", selfname);
|
|
||||||
// wattroff(ctx->history, COLOR_PAIR(GREEN));
|
|
||||||
// wprintw(ctx->history, "%s\n", 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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (close_win)
|
if (close_win)
|
||||||
free(ctx);
|
free(ctx);
|
||||||
else {
|
else
|
||||||
ctx->line[0] = L'\0';
|
reset_buf(ctx->line, &ctx->pos, &ctx->len);
|
||||||
ctx->pos = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(line);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void groupchat_onDraw(ToxWindow *self, Tox *m)
|
static void groupchat_onDraw(ToxWindow *self, Tox *m)
|
||||||
{
|
{
|
||||||
curs_set(1);
|
curs_set(1);
|
||||||
int x, y;
|
int x2, y2;
|
||||||
getmaxyx(self->window, y, x);
|
getmaxyx(self->window, y2, x2);
|
||||||
|
|
||||||
ChatContext *ctx = (ChatContext *) self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
wclrtobot(ctx->sidebar);
|
|
||||||
mvwhline(ctx->linewin, 0, 0, ACS_HLINE, x);
|
wclear(ctx->linewin);
|
||||||
mvwvline(ctx->sidebar, 0, 0, ACS_VLINE, y-CHATBOX_HEIGHT);
|
|
||||||
mvwaddch(ctx->sidebar, y-CHATBOX_HEIGHT, 0, ACS_BTEE);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wclear(ctx->sidebar);
|
||||||
|
mvwhline(ctx->linewin, 0, 0, ACS_HLINE, x2);
|
||||||
|
mvwvline(ctx->sidebar, 0, 0, ACS_VLINE, y2-CHATBOX_HEIGHT);
|
||||||
|
mvwaddch(ctx->sidebar, y2-CHATBOX_HEIGHT, 0, ACS_BTEE);
|
||||||
|
|
||||||
int num_peers = groupchats[self->num].num_peers;
|
int num_peers = groupchats[self->num].num_peers;
|
||||||
|
|
||||||
|
wmove(ctx->sidebar, 0, 1);
|
||||||
|
wattron(ctx->sidebar, A_BOLD);
|
||||||
|
wprintw(ctx->sidebar, "Peers: %d\n", num_peers);
|
||||||
|
wattroff(ctx->sidebar, A_BOLD);
|
||||||
|
|
||||||
|
mvwaddch(ctx->sidebar, 1, 0, ACS_LTEE);
|
||||||
|
mvwhline(ctx->sidebar, 1, 1, ACS_HLINE, SIDEBAR_WIDTH-1);
|
||||||
|
|
||||||
int N = TOX_MAX_NAME_LENGTH;
|
int N = TOX_MAX_NAME_LENGTH;
|
||||||
int maxlines = y - CHATBOX_HEIGHT;
|
int maxlines = y2 - SDBAR_OFST - CHATBOX_HEIGHT;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < num_peers && i < maxlines; ++i) {
|
for (i = 0; i < num_peers && i < maxlines; ++i) {
|
||||||
wmove(ctx->sidebar, i, 1);
|
wmove(ctx->sidebar, i+2, 1);
|
||||||
groupchats[self->num].peer_names[i*N+SIDEBAR_WIDTH-2] = '\0';
|
int peer = i + groupchats[self->num].side_pos;
|
||||||
wprintw(ctx->sidebar, "%s\n", &groupchats[self->num].peer_names[i*N]);
|
|
||||||
}
|
|
||||||
|
|
||||||
wrefresh(self->window);
|
/* truncate nick to fit in side panel without modifying list */
|
||||||
|
uint8_t tmpnck[TOX_MAX_NAME_LENGTH];
|
||||||
|
memcpy(tmpnck, &groupchats[self->num].peer_names[peer*N], SIDEBAR_WIDTH-2);
|
||||||
|
tmpnck[SIDEBAR_WIDTH-2] = '\0';
|
||||||
|
|
||||||
|
wprintw(ctx->sidebar, "%s\n", tmpnck);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void groupchat_onInit(ToxWindow *self, Tox *m)
|
static void groupchat_onInit(ToxWindow *self, Tox *m)
|
||||||
@ -329,10 +549,10 @@ static void groupchat_onInit(ToxWindow *self, Tox *m)
|
|||||||
int x, y;
|
int x, y;
|
||||||
getmaxyx(self->window, y, x);
|
getmaxyx(self->window, y, x);
|
||||||
|
|
||||||
ChatContext *ctx = (ChatContext *) self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
ctx->history = subwin(self->window, y-CHATBOX_HEIGHT+1, x-SIDEBAR_WIDTH-1, 0, 0);
|
ctx->history = subwin(self->window, y-CHATBOX_HEIGHT+1, x-SIDEBAR_WIDTH-1, 0, 0);
|
||||||
scrollok(ctx->history, 1);
|
scrollok(ctx->history, 1);
|
||||||
ctx->linewin = subwin(self->window, 2, x, y-CHATBOX_HEIGHT, 0);
|
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->sidebar = subwin(self->window, y-CHATBOX_HEIGHT+1, SIDEBAR_WIDTH, 0, x-SIDEBAR_WIDTH);
|
||||||
|
|
||||||
print_groupchat_help(ctx);
|
print_groupchat_help(ctx);
|
||||||
@ -344,12 +564,14 @@ ToxWindow new_group_chat(Tox *m, int groupnum)
|
|||||||
ToxWindow ret;
|
ToxWindow ret;
|
||||||
memset(&ret, 0, sizeof(ret));
|
memset(&ret, 0, sizeof(ret));
|
||||||
|
|
||||||
|
ret.active = true;
|
||||||
|
|
||||||
ret.onKey = &groupchat_onKey;
|
ret.onKey = &groupchat_onKey;
|
||||||
ret.onDraw = &groupchat_onDraw;
|
ret.onDraw = &groupchat_onDraw;
|
||||||
ret.onInit = &groupchat_onInit;
|
ret.onInit = &groupchat_onInit;
|
||||||
ret.onGroupMessage = &groupchat_onGroupMessage;
|
ret.onGroupMessage = &groupchat_onGroupMessage;
|
||||||
ret.onGroupNamelistChange = &groupchat_onGroupNamelistChange;
|
ret.onGroupNamelistChange = &groupchat_onGroupNamelistChange;
|
||||||
// ret.onAction = &groupchat_onAction;
|
ret.onGroupAction = &groupchat_onGroupAction;
|
||||||
|
|
||||||
snprintf(ret.name, sizeof(ret.name), "Room #%d", groupnum);
|
snprintf(ret.name, sizeof(ret.name), "Room #%d", groupnum);
|
||||||
|
|
||||||
|
@ -3,13 +3,16 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#define SIDEBAR_WIDTH 16
|
#define SIDEBAR_WIDTH 16
|
||||||
|
#define SDBAR_OFST 2 /* Offset for the peer number box at the top of the statusbar */
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int chatwin;
|
int chatwin;
|
||||||
bool active;
|
bool active;
|
||||||
int num_peers;
|
int num_peers;
|
||||||
|
int side_pos; /* current position of the sidebar - used for scrolling up and down */
|
||||||
uint8_t *peer_names;
|
uint8_t *peer_names;
|
||||||
uint8_t *oldpeer_names;
|
uint8_t *oldpeer_names;
|
||||||
} GroupChat;
|
} GroupChat;
|
||||||
|
|
||||||
int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum);
|
int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum);
|
||||||
|
ToxWindow new_group_chat(Tox *m, int groupnum);
|
||||||
|
135
src/main.c
135
src/main.c
@ -49,6 +49,9 @@ char *DATA_FILE = NULL;
|
|||||||
char *SRVLIST_FILE = NULL;
|
char *SRVLIST_FILE = NULL;
|
||||||
ToxWindow *prompt = NULL;
|
ToxWindow *prompt = NULL;
|
||||||
|
|
||||||
|
FileSender file_senders[MAX_FILES];
|
||||||
|
uint8_t max_file_senders_index;
|
||||||
|
|
||||||
void on_window_resize(int sig)
|
void on_window_resize(int sig)
|
||||||
{
|
{
|
||||||
endwin();
|
endwin();
|
||||||
@ -89,37 +92,45 @@ static void init_term(void)
|
|||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
static Tox *init_tox()
|
static Tox *init_tox(int ipv4)
|
||||||
{
|
{
|
||||||
/* Init core */
|
/* Init core */
|
||||||
Tox *m = tox_new(TOX_ENABLE_IPV6_DEFAULT);
|
int ipv6 = !ipv4;
|
||||||
|
Tox *m = tox_new(ipv6);
|
||||||
|
|
||||||
|
if (TOX_ENABLE_IPV6_DEFAULT && m == NULL) {
|
||||||
|
fprintf(stderr, "IPv6 didn't initialize, trying IPv4 only\n");
|
||||||
|
m = tox_new(0);
|
||||||
|
}
|
||||||
|
|
||||||
if (m == NULL)
|
if (m == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/* Callbacks */
|
/* Callbacks */
|
||||||
tox_callback_connectionstatus(m, on_connectionchange, NULL);
|
tox_callback_connection_status(m, on_connectionchange, NULL);
|
||||||
tox_callback_friendrequest(m, on_request, NULL);
|
tox_callback_typing_change(m, on_typing_change, NULL);
|
||||||
tox_callback_friendmessage(m, on_message, NULL);
|
tox_callback_friend_request(m, on_request, NULL);
|
||||||
tox_callback_namechange(m, on_nickchange, NULL);
|
tox_callback_friend_message(m, on_message, NULL);
|
||||||
tox_callback_userstatus(m, on_statuschange, NULL);
|
tox_callback_name_change(m, on_nickchange, NULL);
|
||||||
tox_callback_statusmessage(m, on_statusmessagechange, NULL);
|
tox_callback_user_status(m, on_statuschange, NULL);
|
||||||
tox_callback_action(m, on_action, NULL);
|
tox_callback_status_message(m, on_statusmessagechange, NULL);
|
||||||
|
tox_callback_friend_action(m, on_action, NULL);
|
||||||
tox_callback_group_invite(m, on_groupinvite, NULL);
|
tox_callback_group_invite(m, on_groupinvite, NULL);
|
||||||
tox_callback_group_message(m, on_groupmessage, NULL);
|
tox_callback_group_message(m, on_groupmessage, NULL);
|
||||||
tox_callback_group_namelistchange(m, on_group_namelistchange, NULL);
|
tox_callback_group_action(m, on_groupaction, NULL);
|
||||||
tox_callback_file_sendrequest(m, on_file_sendrequest, NULL);
|
tox_callback_group_namelist_change(m, on_group_namelistchange, NULL);
|
||||||
|
tox_callback_file_send_request(m, on_file_sendrequest, NULL);
|
||||||
tox_callback_file_control(m, on_file_control, NULL);
|
tox_callback_file_control(m, on_file_control, NULL);
|
||||||
tox_callback_file_data(m, on_file_data, NULL);
|
tox_callback_file_data(m, on_file_data, NULL);
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
tox_setname(m, (uint8_t *) "Cool guy", sizeof("Cool guy"));
|
tox_set_name(m, (uint8_t *) "Cool guy", sizeof("Cool guy"));
|
||||||
#elif defined(_WIN32)
|
#elif defined(_WIN32)
|
||||||
tox_setname(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", sizeof("I should install GNU/Linux"));
|
||||||
#elif defined(__APPLE__)
|
#elif defined(__APPLE__)
|
||||||
tox_setname(m, (uint8_t *) "Hipster", sizeof("Hipster")); //This used to users of other Unixes are hipsters
|
tox_set_name(m, (uint8_t *) "Hipster", sizeof("Hipster")); //This used to users of other Unixes are hipsters
|
||||||
#else
|
#else
|
||||||
tox_setname(m, (uint8_t *) "Registered Minix user #4", sizeof("Registered Minix user #4"));
|
tox_set_name(m, (uint8_t *) "Registered Minix user #4", sizeof("Registered Minix user #4"));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return m;
|
return m;
|
||||||
@ -190,7 +201,7 @@ int init_connection_helper(Tox *m, int linenumber)
|
|||||||
* 4: failed to resolve name to IP
|
* 4: failed to resolve name to IP
|
||||||
* 5: serverlist file contains no acceptable line
|
* 5: serverlist file contains no acceptable line
|
||||||
*/
|
*/
|
||||||
static uint8_t init_connection_serverlist_loaded = 0;
|
static int init_connection_serverlist_loaded = 0;
|
||||||
int init_connection(Tox *m)
|
int init_connection(Tox *m)
|
||||||
{
|
{
|
||||||
if (linecnt > 0) /* already loaded serverlist */
|
if (linecnt > 0) /* already loaded serverlist */
|
||||||
@ -222,29 +233,34 @@ int init_connection(Tox *m)
|
|||||||
return 5;
|
return 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void do_tox(Tox *m, ToxWindow *prompt)
|
static void do_connection(Tox *m, ToxWindow *prompt)
|
||||||
{
|
{
|
||||||
static int conn_try = 0;
|
static int conn_try = 0;
|
||||||
static int conn_err = 0;
|
static int conn_err = 0;
|
||||||
static bool dht_on = false;
|
static bool dht_on = false;
|
||||||
|
|
||||||
if (!dht_on && !tox_isconnected(m) && !(conn_try++ % 100)) {
|
bool is_connected = tox_isconnected(m);
|
||||||
|
|
||||||
|
if (!dht_on && !is_connected && !(conn_try++ % 100)) {
|
||||||
|
prep_prompt_win();
|
||||||
if (!conn_err) {
|
if (!conn_err) {
|
||||||
wprintw(prompt->window, "Establishing connection...\n");
|
wprintw(prompt->window, "Establishing connection...\n");
|
||||||
if ((conn_err = init_connection(m)))
|
if ((conn_err = init_connection(m)))
|
||||||
wprintw(prompt->window, "\nAuto-connect failed with error code %d\n", conn_err);
|
wprintw(prompt->window, "\nAuto-connect failed with error code %d\n", conn_err);
|
||||||
}
|
}
|
||||||
} else if (!dht_on && tox_isconnected(m)) {
|
} else if (!dht_on && is_connected) {
|
||||||
dht_on = true;
|
dht_on = true;
|
||||||
prompt_update_connectionstatus(prompt, dht_on);
|
prompt_update_connectionstatus(prompt, dht_on);
|
||||||
|
|
||||||
|
prep_prompt_win();
|
||||||
wprintw(prompt->window, "\nDHT connected.\n");
|
wprintw(prompt->window, "\nDHT connected.\n");
|
||||||
} else if (dht_on && !tox_isconnected(m)) {
|
} else if (dht_on && !is_connected) {
|
||||||
dht_on = false;
|
dht_on = false;
|
||||||
prompt_update_connectionstatus(prompt, dht_on);
|
prompt_update_connectionstatus(prompt, dht_on);
|
||||||
|
|
||||||
|
prep_prompt_win();
|
||||||
wprintw(prompt->window, "\nDHT disconnected. Attempting to reconnect.\n");
|
wprintw(prompt->window, "\nDHT disconnected. Attempting to reconnect.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
tox_do(m);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int f_loadfromfile;
|
int f_loadfromfile;
|
||||||
@ -329,12 +345,10 @@ static void load_data(Tox *m, char *path)
|
|||||||
tox_load(m, buf, len);
|
tox_load(m, buf, len);
|
||||||
|
|
||||||
uint32_t i = 0;
|
uint32_t i = 0;
|
||||||
|
|
||||||
uint8_t name[TOX_MAX_NAME_LENGTH];
|
uint8_t name[TOX_MAX_NAME_LENGTH];
|
||||||
while (tox_getname(m, i, name) != -1) {
|
|
||||||
on_friendadded(m, i);
|
while (tox_get_name(m, i, name) != -1)
|
||||||
i++;
|
on_friendadded(m, i++, false);
|
||||||
}
|
|
||||||
|
|
||||||
free(buf);
|
free(buf);
|
||||||
fclose(fd);
|
fclose(fd);
|
||||||
@ -349,26 +363,26 @@ static void load_data(Tox *m, char *path)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void close_file_sender(int i)
|
void close_file_sender(int i)
|
||||||
{
|
{
|
||||||
fclose(file_senders[i].file);
|
fclose(file_senders[i].file);
|
||||||
memset(&file_senders[i], 0, sizeof(FileSender));
|
memset(&file_senders[i], 0, sizeof(FileSender));
|
||||||
|
|
||||||
int j;
|
int j;
|
||||||
|
|
||||||
for (j = num_file_senders; j > 0; --j) {
|
for (j = max_file_senders_index; j > 0; --j) {
|
||||||
if (file_senders[j-1].active)
|
if (file_senders[j-1].active)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
num_file_senders = j;
|
max_file_senders_index = j;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void do_file_senders(Tox *m)
|
static void do_file_senders(Tox *m)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < num_file_senders; ++i) {
|
for (i = 0; i < max_file_senders_index; ++i) {
|
||||||
if (!file_senders[i].active)
|
if (!file_senders[i].active)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -380,38 +394,36 @@ static void do_file_senders(Tox *m)
|
|||||||
|
|
||||||
/* If file transfer has timed out kill transfer and send kill control */
|
/* If file transfer has timed out kill transfer and send kill control */
|
||||||
if (timed_out(file_senders[i].timestamp, current_time, TIMEOUT_FILESENDER)) {
|
if (timed_out(file_senders[i].timestamp, current_time, TIMEOUT_FILESENDER)) {
|
||||||
ChatContext *ctx = (ChatContext *) file_senders[i].toxwin->chatwin;
|
ChatContext *ctx = file_senders[i].toxwin->chatwin;
|
||||||
|
|
||||||
if (ctx != NULL) {
|
if (ctx != NULL) {
|
||||||
wprintw(ctx->history, "File transfer for '%s' timed out.\n", pathname);
|
wprintw(ctx->history, "File transfer for '%s' timed out.\n", pathname);
|
||||||
alert_window(file_senders[i].toxwin);
|
alert_window(file_senders[i].toxwin, WINDOW_ALERT_2, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
tox_file_sendcontrol(m, friendnum, 0, filenum, TOX_FILECONTROL_KILL, 0, 0);
|
tox_file_send_control(m, friendnum, 0, filenum, TOX_FILECONTROL_KILL, 0, 0);
|
||||||
close_file_sender(i);
|
close_file_sender(i);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int pieces = 0;
|
while (true) {
|
||||||
|
if (tox_file_send_data(m, friendnum, filenum, file_senders[i].nextpiece,
|
||||||
while (pieces++ < MAX_PIECES_SEND) {
|
file_senders[i].piecelen) == -1)
|
||||||
if (!tox_file_senddata(m, friendnum, filenum, file_senders[i].nextpiece,
|
|
||||||
file_senders[i].piecelen))
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
file_senders[i].timestamp = current_time;
|
file_senders[i].timestamp = current_time;
|
||||||
file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1,
|
file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1,
|
||||||
tox_filedata_size(m, friendnum), fp);
|
tox_file_data_size(m, friendnum), fp);
|
||||||
|
|
||||||
if (file_senders[i].piecelen == 0) {
|
if (file_senders[i].piecelen == 0) {
|
||||||
ChatContext *ctx = (ChatContext *) file_senders[i].toxwin->chatwin;
|
ChatContext *ctx = file_senders[i].toxwin->chatwin;
|
||||||
|
|
||||||
if (ctx != NULL) {
|
if (ctx != NULL) {
|
||||||
wprintw(ctx->history, "File '%s' successfuly sent.\n", pathname);
|
wprintw(ctx->history, "File '%s' successfuly sent.\n", pathname);
|
||||||
alert_window(file_senders[i].toxwin);
|
alert_window(file_senders[i].toxwin, WINDOW_ALERT_2, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
tox_file_sendcontrol(m, friendnum, 0, filenum, TOX_FILECONTROL_FINISHED, 0, 0);
|
tox_file_send_control(m, friendnum, 0, filenum, TOX_FILECONTROL_FINISHED, 0, 0);
|
||||||
close_file_sender(i);
|
close_file_sender(i);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -422,14 +434,33 @@ static void do_file_senders(Tox *m)
|
|||||||
void exit_toxic(Tox *m)
|
void exit_toxic(Tox *m)
|
||||||
{
|
{
|
||||||
store_data(m, DATA_FILE);
|
store_data(m, DATA_FILE);
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < max_file_senders_index; ++i) {
|
||||||
|
if (file_senders[i].active)
|
||||||
|
fclose(file_senders[i].file);
|
||||||
|
}
|
||||||
|
|
||||||
free(DATA_FILE);
|
free(DATA_FILE);
|
||||||
free(SRVLIST_FILE);
|
free(SRVLIST_FILE);
|
||||||
free(prompt->stb);
|
free(prompt->stb);
|
||||||
|
free(prompt->promptbuf);
|
||||||
tox_kill(m);
|
tox_kill(m);
|
||||||
endwin();
|
endwin();
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void do_toxic(Tox *m, ToxWindow *prompt)
|
||||||
|
{
|
||||||
|
do_connection(m, prompt);
|
||||||
|
draw_active_window(m);
|
||||||
|
do_file_senders(m);
|
||||||
|
|
||||||
|
/* main tox-core loop */
|
||||||
|
tox_do(m);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
char *user_config_dir = get_user_config_dir();
|
char *user_config_dir = get_user_config_dir();
|
||||||
@ -438,6 +469,10 @@ int main(int argc, char *argv[])
|
|||||||
f_loadfromfile = 1;
|
f_loadfromfile = 1;
|
||||||
int f_flag = 0;
|
int f_flag = 0;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
int f_use_ipv4 = 0;
|
||||||
|
|
||||||
|
// Make sure all written files are read/writeable only by the current user.
|
||||||
|
umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
|
||||||
|
|
||||||
for (i = 0; i < argc; ++i) {
|
for (i = 0; i < argc; ++i) {
|
||||||
if (argv[i] == NULL)
|
if (argv[i] == NULL)
|
||||||
@ -450,6 +485,8 @@ int main(int argc, char *argv[])
|
|||||||
f_flag = -1;
|
f_flag = -1;
|
||||||
} else if (argv[i][1] == 'n') {
|
} else if (argv[i][1] == 'n') {
|
||||||
f_loadfromfile = 0;
|
f_loadfromfile = 0;
|
||||||
|
} else if (argv[i][1] == '4') {
|
||||||
|
f_use_ipv4 = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -490,7 +527,7 @@ int main(int argc, char *argv[])
|
|||||||
free(user_config_dir);
|
free(user_config_dir);
|
||||||
|
|
||||||
init_term();
|
init_term();
|
||||||
Tox *m = init_tox();
|
Tox *m = init_tox(f_use_ipv4);
|
||||||
|
|
||||||
if (m == NULL) {
|
if (m == NULL) {
|
||||||
endwin();
|
endwin();
|
||||||
@ -518,14 +555,10 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
prompt_init_statusbar(prompt, m);
|
prompt_init_statusbar(prompt, m);
|
||||||
sort_friendlist_index();
|
sort_friendlist_index(m);
|
||||||
|
|
||||||
while (true) {
|
while (true)
|
||||||
do_tox(m, prompt);
|
do_toxic(m, prompt);
|
||||||
do_file_senders(m);
|
|
||||||
draw_active_window(m);
|
|
||||||
}
|
|
||||||
|
|
||||||
exit_toxic(m);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
125
src/misc_tools.c
125
src/misc_tools.c
@ -9,6 +9,8 @@
|
|||||||
|
|
||||||
#include "toxic_windows.h"
|
#include "toxic_windows.h"
|
||||||
|
|
||||||
|
extern ToxWindow *prompt;
|
||||||
|
|
||||||
// XXX: FIX
|
// XXX: FIX
|
||||||
unsigned char *hex_string_to_bin(char hex_string[])
|
unsigned char *hex_string_to_bin(char hex_string[])
|
||||||
{
|
{
|
||||||
@ -45,9 +47,9 @@ void print_time(WINDOW *window)
|
|||||||
{
|
{
|
||||||
struct tm *timeinfo = get_time();
|
struct tm *timeinfo = get_time();
|
||||||
|
|
||||||
wattron(window, COLOR_PAIR(CYAN));
|
wattron(window, COLOR_PAIR(BLUE));
|
||||||
wprintw(window, "[%02d:%02d:%02d] ", timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec);
|
wprintw(window, "[%02d:%02d:%02d] ", timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec);
|
||||||
wattroff(window, COLOR_PAIR(CYAN));
|
wattroff(window,COLOR_PAIR(BLUE));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns 1 if the string is empty, 0 otherwise */
|
/* Returns 1 if the string is empty, 0 otherwise */
|
||||||
@ -56,8 +58,37 @@ int string_is_empty(char *string)
|
|||||||
return string[0] == '\0';
|
return string[0] == '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
/* convert wide characters to null terminated string */
|
/* convert a multibyte string to a wide character string (must provide buffer) */
|
||||||
uint8_t *wcs_to_char(wchar_t *string)
|
int mbs_to_wcs_buf(wchar_t *buf, const uint8_t *string, size_t n)
|
||||||
|
{
|
||||||
|
size_t len = mbstowcs(NULL, string, 0) + 1;
|
||||||
|
|
||||||
|
if (n < len)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if ((len = mbstowcs(buf, string, n)) == (size_t) -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* converts wide character string into a multibyte string.
|
||||||
|
Same thing as wcs_to_mbs() but caller must provide its own buffer */
|
||||||
|
int wcs_to_mbs_buf(uint8_t *buf, const wchar_t *string, size_t n)
|
||||||
|
{
|
||||||
|
size_t len = wcstombs(NULL, string, 0) + 1;
|
||||||
|
|
||||||
|
if (n < len)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if ((len = wcstombs(buf, string, n)) == (size_t) -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* convert wide characters to multibyte string: string returned must be free'd */
|
||||||
|
uint8_t *wcs_to_mbs(wchar_t *string)
|
||||||
{
|
{
|
||||||
uint8_t *ret = NULL;
|
uint8_t *ret = NULL;
|
||||||
size_t len = wcstombs(NULL, string, 0);
|
size_t len = wcstombs(NULL, string, 0);
|
||||||
@ -65,8 +96,10 @@ uint8_t *wcs_to_char(wchar_t *string)
|
|||||||
if (len != (size_t) -1) {
|
if (len != (size_t) -1) {
|
||||||
ret = malloc(++len);
|
ret = malloc(++len);
|
||||||
|
|
||||||
if (ret != NULL)
|
if (ret != NULL) {
|
||||||
wcstombs(ret, string, len);
|
if (wcstombs(ret, string, len) == (size_t) -1)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ret = malloc(2);
|
ret = malloc(2);
|
||||||
|
|
||||||
@ -85,7 +118,7 @@ uint8_t *wcs_to_char(wchar_t *string)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* convert a wide char to null terminated string */
|
/* convert a wide char to multibyte string */
|
||||||
char *wc_to_char(wchar_t ch)
|
char *wc_to_char(wchar_t ch)
|
||||||
{
|
{
|
||||||
static char ret[MB_LEN_MAX + 1];
|
static char ret[MB_LEN_MAX + 1];
|
||||||
@ -107,32 +140,31 @@ bool timed_out(uint64_t timestamp, uint64_t curtime, uint64_t timeout)
|
|||||||
return timestamp + timeout <= curtime;
|
return timestamp + timeout <= curtime;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Beeps and makes window tab blink */
|
/* Colours the window tab according to type. Beeps if is_beep is true */
|
||||||
void alert_window(ToxWindow *self)
|
void alert_window(ToxWindow *self, int type, bool is_beep)
|
||||||
{
|
{
|
||||||
self->blink = true;
|
switch (type) {
|
||||||
beep();
|
case WINDOW_ALERT_0:
|
||||||
}
|
self->alert0 = true;
|
||||||
|
break;
|
||||||
/* case-insensitive string compare function for use with qsort - same return logic as strcmp */
|
case WINDOW_ALERT_1:
|
||||||
int name_compare(const void *nick1, const void *nick2)
|
self->alert1 = true;
|
||||||
{
|
break;
|
||||||
char s[TOX_MAX_NAME_LENGTH];
|
case WINDOW_ALERT_2:
|
||||||
char t[TOX_MAX_NAME_LENGTH];
|
self->alert2 = true;
|
||||||
strcpy(s, (const char *) nick1);
|
break;
|
||||||
strcpy(t, (const char *) nick2);
|
|
||||||
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; s[i] && t[i]; ++i) {
|
|
||||||
s[i] = tolower(s[i]);
|
|
||||||
t[i] = tolower(t[i]);
|
|
||||||
|
|
||||||
if (s[i] != t[i])
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return s[i] - t[i];
|
StatusBar *stb = prompt->stb;
|
||||||
|
|
||||||
|
if (is_beep && stb->status != TOX_USERSTATUS_BUSY)
|
||||||
|
beep();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* case-insensitive string compare function for use with qsort */
|
||||||
|
int qsort_strcasecmp_hlpr(const void *nick1, const void *nick2)
|
||||||
|
{
|
||||||
|
return strcasecmp((const char *) nick1, (const char *) nick2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns true if nick is valid. A valid toxic nick:
|
/* Returns true if nick is valid. A valid toxic nick:
|
||||||
@ -152,4 +184,35 @@ bool valid_nick(uint8_t *nick)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Moves 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)
|
||||||
|
{
|
||||||
|
int end_y = (len / max_x) + (max_y - CURS_Y_OFFSET);
|
||||||
|
int end_x = len % max_x;
|
||||||
|
wmove(w, end_y, end_x);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* gets base file name from path or original file name if no path is supplied */
|
||||||
|
void get_file_name(uint8_t *pathname, uint8_t *namebuf)
|
||||||
|
{
|
||||||
|
int idx = strlen(pathname) - 1;
|
||||||
|
|
||||||
|
while (idx >= 0 && pathname[idx] == '/')
|
||||||
|
pathname[idx--] = '\0';
|
||||||
|
|
||||||
|
uint8_t *filename = strrchr(pathname, '/'); // Try unix style paths
|
||||||
|
|
||||||
|
if (filename != NULL) {
|
||||||
|
if (!strlen(++filename))
|
||||||
|
filename = pathname;
|
||||||
|
} else {
|
||||||
|
filename = strrchr(pathname, '\\'); // Try windows style paths
|
||||||
|
|
||||||
|
if (filename == NULL)
|
||||||
|
filename = pathname;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(namebuf, MAX_STR_SIZE, "%s", filename);
|
||||||
|
}
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
* Toxic -- Tox Curses Client
|
* Toxic -- Tox Curses Client
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
|
// #define MIN(x, y) (((x) < (y)) ? (x) : (y))
|
||||||
|
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
|
||||||
|
|
||||||
/* convert a hex string to binary */
|
/* convert a hex string to binary */
|
||||||
unsigned char *hex_string_to_bin(char hex_string[]);
|
unsigned char *hex_string_to_bin(char hex_string[]);
|
||||||
@ -16,23 +17,36 @@ void print_time(WINDOW *window);
|
|||||||
/* Returns 1 if the string is empty, 0 otherwise */
|
/* Returns 1 if the string is empty, 0 otherwise */
|
||||||
int string_is_empty(char *string);
|
int string_is_empty(char *string);
|
||||||
|
|
||||||
/* convert wide characters to null terminated string */
|
/* convert a multibyte string to a wide character string (must provide buffer) */
|
||||||
uint8_t *wcs_to_char(wchar_t *string);
|
int char_to_wcs_buf(wchar_t *buf, const uint8_t *string, size_t n);
|
||||||
|
|
||||||
/* convert a wide char to null terminated string */
|
/* converts wide character string into a multibyte string.
|
||||||
|
Same thing as wcs_to_mbs() but caller must provide its own buffer */
|
||||||
|
int wcs_to_mbs_buf(uint8_t *buf, const wchar_t *string, size_t n);
|
||||||
|
|
||||||
|
/* convert wide characters to multibyte string: string returned must be free'd */
|
||||||
|
uint8_t *wcs_to_mbs(wchar_t *string);
|
||||||
|
|
||||||
|
/* convert a wide char to multibyte char */
|
||||||
char *wc_to_char(wchar_t ch);
|
char *wc_to_char(wchar_t ch);
|
||||||
|
|
||||||
/* Returns true if connection has timed out, false otherwise */
|
/* Returns true if connection has timed out, false otherwise */
|
||||||
bool timed_out(uint64_t timestamp, uint64_t timeout, uint64_t curtime);
|
bool timed_out(uint64_t timestamp, uint64_t timeout, uint64_t curtime);
|
||||||
|
|
||||||
/* Beeps and makes window tab blink */
|
/* Colours the window tab according to type. Beeps if is_beep is true */
|
||||||
void alert_window(ToxWindow *self);
|
void alert_window(ToxWindow *self, int type, bool is_beep);
|
||||||
|
|
||||||
/* case-insensitive string compare function for use with qsort - same return logic as strcmp */
|
/* case-insensitive string compare function for use with qsort */
|
||||||
int name_compare(const void *nick1, const void *nick2);
|
int qsort_strcasecmp_hlpr(const void *nick1, const void *nick2);
|
||||||
|
|
||||||
/* Returns true if nick is valid. A valid toxic nick:
|
/* Returns true if nick is valid. A valid toxic nick:
|
||||||
- cannot be empty
|
- cannot be empty
|
||||||
- cannot start with a space
|
- cannot start with a space
|
||||||
- must not contain contiguous spaces */
|
- must not contain contiguous spaces */
|
||||||
bool valid_nick(uint8_t *nick);
|
bool 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);
|
||||||
|
|
||||||
|
/* gets base file name from path or original file name if no path is supplied */
|
||||||
|
void get_file_name(uint8_t *pathname, uint8_t *namebuf);
|
||||||
|
301
src/prompt.c
301
src/prompt.c
@ -13,17 +13,53 @@
|
|||||||
#include "prompt.h"
|
#include "prompt.h"
|
||||||
#include "execute.h"
|
#include "execute.h"
|
||||||
#include "misc_tools.h"
|
#include "misc_tools.h"
|
||||||
|
#include "toxic_strings.h"
|
||||||
|
|
||||||
uint8_t pending_frnd_requests[MAX_FRIENDS_NUM][TOX_CLIENT_ID_SIZE] = {0};
|
uint8_t pending_frnd_requests[MAX_FRIENDS_NUM][TOX_CLIENT_ID_SIZE] = {0};
|
||||||
uint8_t num_frnd_requests = 0;
|
uint8_t num_frnd_requests = 0;
|
||||||
|
extern ToxWindow *prompt;
|
||||||
|
|
||||||
static char prompt_buf[MAX_STR_SIZE] = {'\0'};
|
/* Array of global command names used for tab completion. */
|
||||||
static int prompt_buf_pos = 0;
|
const uint8_t glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
|
||||||
|
{ "/accept" },
|
||||||
|
{ "/add" },
|
||||||
|
{ "/clear" },
|
||||||
|
{ "/close" }, /* rm /close when groupchats gets its own list */
|
||||||
|
{ "/connect" },
|
||||||
|
{ "/exit" },
|
||||||
|
{ "/groupchat" },
|
||||||
|
{ "/help" },
|
||||||
|
{ "/join" },
|
||||||
|
{ "/myid" },
|
||||||
|
{ "/nick" },
|
||||||
|
{ "/note" },
|
||||||
|
{ "/quit" },
|
||||||
|
{ "/status" },
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 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 */
|
/* Updates own nick in prompt statusbar */
|
||||||
void prompt_update_nick(ToxWindow *prompt, uint8_t *nick, uint16_t len)
|
void prompt_update_nick(ToxWindow *prompt, uint8_t *nick, uint16_t len)
|
||||||
{
|
{
|
||||||
StatusBar *statusbar = (StatusBar *) prompt->stb;
|
StatusBar *statusbar = prompt->stb;
|
||||||
snprintf(statusbar->nick, sizeof(statusbar->nick), "%s", nick);
|
snprintf(statusbar->nick, sizeof(statusbar->nick), "%s", nick);
|
||||||
statusbar->nick_len = len;
|
statusbar->nick_len = len;
|
||||||
}
|
}
|
||||||
@ -31,7 +67,7 @@ void prompt_update_nick(ToxWindow *prompt, uint8_t *nick, uint16_t len)
|
|||||||
/* Updates own statusmessage in prompt statusbar */
|
/* Updates own statusmessage in prompt statusbar */
|
||||||
void prompt_update_statusmessage(ToxWindow *prompt, uint8_t *statusmsg, uint16_t len)
|
void prompt_update_statusmessage(ToxWindow *prompt, uint8_t *statusmsg, uint16_t len)
|
||||||
{
|
{
|
||||||
StatusBar *statusbar = (StatusBar *) prompt->stb;
|
StatusBar *statusbar = prompt->stb;
|
||||||
snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg);
|
snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg);
|
||||||
statusbar->statusmsg_len = len;
|
statusbar->statusmsg_len = len;
|
||||||
}
|
}
|
||||||
@ -39,14 +75,14 @@ void prompt_update_statusmessage(ToxWindow *prompt, uint8_t *statusmsg, uint16_t
|
|||||||
/* Updates own status in prompt statusbar */
|
/* Updates own status in prompt statusbar */
|
||||||
void prompt_update_status(ToxWindow *prompt, TOX_USERSTATUS status)
|
void prompt_update_status(ToxWindow *prompt, TOX_USERSTATUS status)
|
||||||
{
|
{
|
||||||
StatusBar *statusbar = (StatusBar *) prompt->stb;
|
StatusBar *statusbar = prompt->stb;
|
||||||
statusbar->status = status;
|
statusbar->status = status;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Updates own connection status in prompt statusbar */
|
/* Updates own connection status in prompt statusbar */
|
||||||
void prompt_update_connectionstatus(ToxWindow *prompt, bool is_connected)
|
void prompt_update_connectionstatus(ToxWindow *prompt, bool is_connected)
|
||||||
{
|
{
|
||||||
StatusBar *statusbar = (StatusBar *) prompt->stb;
|
StatusBar *statusbar = prompt->stb;
|
||||||
statusbar->is_online = is_connected;
|
statusbar->is_online = is_connected;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,18 +90,19 @@ void prompt_update_connectionstatus(ToxWindow *prompt, bool is_connected)
|
|||||||
Returns request number on success, -1 if queue is full or other error. */
|
Returns request number on success, -1 if queue is full or other error. */
|
||||||
static int add_friend_request(uint8_t *public_key)
|
static int add_friend_request(uint8_t *public_key)
|
||||||
{
|
{
|
||||||
if (num_frnd_requests < MAX_FRIENDS_NUM) {
|
if (num_frnd_requests >= MAX_FRIENDS_NUM)
|
||||||
int i;
|
return -1;
|
||||||
|
|
||||||
for (i = 0; i <= num_frnd_requests; ++i) {
|
int i;
|
||||||
if (!strlen(pending_frnd_requests[i])) {
|
|
||||||
memcpy(pending_frnd_requests[i], public_key, TOX_CLIENT_ID_SIZE);
|
|
||||||
|
|
||||||
if (i == num_frnd_requests)
|
for (i = 0; i <= num_frnd_requests; ++i) {
|
||||||
++num_frnd_requests;
|
if (!strlen(pending_frnd_requests[i])) {
|
||||||
|
memcpy(pending_frnd_requests[i], public_key, TOX_CLIENT_ID_SIZE);
|
||||||
|
|
||||||
return i;
|
if (i == num_frnd_requests)
|
||||||
}
|
++num_frnd_requests;
|
||||||
|
|
||||||
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,58 +111,187 @@ static int add_friend_request(uint8_t *public_key)
|
|||||||
|
|
||||||
static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key)
|
static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key)
|
||||||
{
|
{
|
||||||
|
PromptBuf *prt = self->promptbuf;
|
||||||
|
|
||||||
int x, y, y2, x2;
|
int x, y, y2, x2;
|
||||||
getyx(self->window, y, x);
|
getyx(self->window, y, x);
|
||||||
getmaxyx(self->window, y2, x2);
|
getmaxyx(self->window, y2, x2);
|
||||||
|
|
||||||
/* BACKSPACE key: Remove one character from line */
|
/* BACKSPACE key: Remove one character from line */
|
||||||
if (key == 0x107 || key == 0x8 || key == 0x7f) {
|
if (key == 0x107 || key == 0x8 || key == 0x7f) {
|
||||||
if (prompt_buf_pos != 0) {
|
if (prt->pos > 0) {
|
||||||
prompt_buf[--prompt_buf_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 */
|
||||||
if (x == 0)
|
prt->scroll = false;
|
||||||
mvwdelch(self->window, y - 1, x2 - 1);
|
} else {
|
||||||
else
|
beep();
|
||||||
mvwdelch(self->window, y, x - 1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Add printable characters to line */
|
else if (key == KEY_DC) { /* DEL key: Remove character at pos */
|
||||||
else if (isprint(key)) {
|
if (prt->pos != prt->len) {
|
||||||
if (prompt_buf_pos < (MAX_STR_SIZE-1)) {
|
del_char_buf_frnt(prt->line, &prt->pos, &prt->len);
|
||||||
mvwaddch(self->window, y, x, key);
|
prt->scroll = false;
|
||||||
prompt_buf[prompt_buf_pos++] = key;
|
} else {
|
||||||
prompt_buf[prompt_buf_pos] = '\0';
|
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) { /* HOME key: Move cursor to beginning of line */
|
||||||
|
if (prt->pos != 0)
|
||||||
|
prt->pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (key == KEY_END) { /* END 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
/* RETURN key: execute command */
|
/* RETURN key: execute command */
|
||||||
else if (key == '\n') {
|
else if (key == '\n') {
|
||||||
wprintw(self->window, "\n");
|
wprintw(self->window, "\n");
|
||||||
execute(self->window, self, m, prompt_buf, GLOBAL_COMMAND_MODE);
|
uint8_t line[MAX_STR_SIZE];
|
||||||
prompt_buf_pos = 0;
|
|
||||||
prompt_buf[0] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (wcs_to_mbs_buf(line, prt->line, MAX_STR_SIZE) == -1)
|
||||||
|
memset(&line, 0, sizeof(line));
|
||||||
|
|
||||||
|
if (!string_is_empty(line))
|
||||||
|
add_line_to_hist(prt->line, prt->len, prt->ln_history, &prt->hst_tot, &prt->hst_pos);
|
||||||
|
|
||||||
|
execute(self->window, self, m, line, GLOBAL_COMMAND_MODE);
|
||||||
|
reset_buf(prt->line, &prt->pos, &prt->len);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void prompt_onDraw(ToxWindow *self, Tox *m)
|
static void prompt_onDraw(ToxWindow *self, Tox *m)
|
||||||
{
|
{
|
||||||
curs_set(1);
|
PromptBuf *prt = self->promptbuf;
|
||||||
|
|
||||||
|
curs_set(1);
|
||||||
int x, y, x2, y2;
|
int x, y, x2, y2;
|
||||||
getyx(self->window, y, x);
|
getyx(self->window, y, x);
|
||||||
getmaxyx(self->window, y2, x2);
|
getmaxyx(self->window, y2, x2);
|
||||||
|
wclrtobot(self->window);
|
||||||
|
|
||||||
/* Someone please fix this disgusting hack */
|
/* if len is >= screen width offset max x by X_OFST to account for prompt char */
|
||||||
size_t i;
|
int px2 = prt->len >= x2 ? x2 : x2 - X_OFST;
|
||||||
|
|
||||||
for (i = 0; i < prompt_buf_pos; ++i) {
|
if (px2 <= 0)
|
||||||
if ((prompt_buf_pos + 3) >= x2)
|
return;
|
||||||
--y;
|
|
||||||
|
/* 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) {
|
||||||
|
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);
|
||||||
|
else
|
||||||
|
mvwprintw(self->window, prt->orig_y, X_OFST, line);
|
||||||
|
|
||||||
|
int k = prt->orig_y + ((prt->len + p_ofst) / px2);
|
||||||
|
|
||||||
|
prt->at_bottom = k == y2 - 1;
|
||||||
|
bool botm = k == y2;
|
||||||
|
bool edge = (prt->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;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else { /* Mark point of origin for new line */
|
||||||
|
prt->orig_y = y;
|
||||||
}
|
}
|
||||||
|
|
||||||
StatusBar *statusbar = (StatusBar *) self->stb;
|
wattron(self->window, COLOR_PAIR(GREEN));
|
||||||
|
mvwprintw(self->window, prt->orig_y, 0, "$ ");
|
||||||
|
wattroff(self->window, COLOR_PAIR(GREEN));
|
||||||
|
|
||||||
|
StatusBar *statusbar = self->stb;
|
||||||
werase(statusbar->topline);
|
werase(statusbar->topline);
|
||||||
mvwhline(statusbar->topline, 1, 0, ACS_HLINE, x2);
|
mvwhline(statusbar->topline, 1, 0, ACS_HLINE, x2);
|
||||||
wmove(statusbar->topline, 0, 0);
|
wmove(statusbar->topline, 0, 0);
|
||||||
@ -166,15 +332,10 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
|
|||||||
wprintw(statusbar->topline, " - %s", statusbar->statusmsg);
|
wprintw(statusbar->topline, " - %s", statusbar->statusmsg);
|
||||||
wattroff(statusbar->topline, A_BOLD);
|
wattroff(statusbar->topline, A_BOLD);
|
||||||
|
|
||||||
wprintw(statusbar->topline, "\n");
|
/* put cursor back in correct spot */
|
||||||
|
int y_m = prt->orig_y + ((prt->pos + p_ofst) / px2);
|
||||||
wattron(self->window, COLOR_PAIR(GREEN));
|
int x_m = (prt->pos + X_OFST) % x2;
|
||||||
mvwprintw(self->window, y, 0, "# ");
|
wmove(self->window, y_m, x_m);
|
||||||
wattroff(self->window, COLOR_PAIR(GREEN));
|
|
||||||
mvwprintw(self->window, y, 2, "%s", prompt_buf);
|
|
||||||
wclrtoeol(self->window);
|
|
||||||
|
|
||||||
wrefresh(self->window);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void prompt_onInit(ToxWindow *self, Tox *m)
|
static void prompt_onInit(ToxWindow *self, Tox *m)
|
||||||
@ -189,30 +350,36 @@ static void prompt_onConnectionChange(ToxWindow *self, Tox *m, int friendnum , u
|
|||||||
if (friendnum < 0)
|
if (friendnum < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
prep_prompt_win();
|
||||||
|
|
||||||
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
|
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
|
||||||
|
|
||||||
if (tox_getname(m, friendnum, nick) == -1)
|
if (tox_get_name(m, friendnum, nick) == -1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!nick[0])
|
if (!nick[0])
|
||||||
snprintf(nick, sizeof(nick), "%s", UNKNOWN_NAME);
|
snprintf(nick, sizeof(nick), "%s", UNKNOWN_NAME);
|
||||||
|
|
||||||
|
wprintw(self->window, "\n");
|
||||||
|
print_time(self->window);
|
||||||
|
|
||||||
if (status == 1) {
|
if (status == 1) {
|
||||||
wattron(self->window, COLOR_PAIR(GREEN));
|
wattron(self->window, COLOR_PAIR(GREEN));
|
||||||
wattron(self->window, A_BOLD);
|
wattron(self->window, A_BOLD);
|
||||||
wprintw(self->window, "\n%s ", nick);
|
wprintw(self->window, "* %s ", nick);
|
||||||
wattroff(self->window, A_BOLD);
|
wattroff(self->window, A_BOLD);
|
||||||
wprintw(self->window, "has come online\n");
|
wprintw(self->window, "has come online\n");
|
||||||
wattroff(self->window, COLOR_PAIR(GREEN));
|
wattroff(self->window, COLOR_PAIR(GREEN));
|
||||||
|
|
||||||
|
alert_window(self, WINDOW_ALERT_2, false);
|
||||||
} else {
|
} else {
|
||||||
wattron(self->window, COLOR_PAIR(RED));
|
wattron(self->window, COLOR_PAIR(RED));
|
||||||
wattron(self->window, A_BOLD);
|
wattron(self->window, A_BOLD);
|
||||||
wprintw(self->window, "\n%s ", nick);
|
wprintw(self->window, "* %s ", nick);
|
||||||
wattroff(self->window, A_BOLD);
|
wattroff(self->window, A_BOLD);
|
||||||
wprintw(self->window, "has gone offline\n");
|
wprintw(self->window, "has gone offline\n");
|
||||||
wattroff(self->window, COLOR_PAIR(RED));
|
wattroff(self->window, COLOR_PAIR(RED));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void prompt_onFriendRequest(ToxWindow *self, uint8_t *key, uint8_t *data, uint16_t length)
|
static void prompt_onFriendRequest(ToxWindow *self, uint8_t *key, uint8_t *data, uint16_t length)
|
||||||
@ -220,7 +387,10 @@ static void prompt_onFriendRequest(ToxWindow *self, uint8_t *key, uint8_t *data,
|
|||||||
// make sure message data is null-terminated
|
// make sure message data is null-terminated
|
||||||
data[length - 1] = 0;
|
data[length - 1] = 0;
|
||||||
|
|
||||||
wprintw(self->window, "\nFriend request with the message: %s\n", data);
|
prep_prompt_win();
|
||||||
|
wprintw(self->window, "\n");
|
||||||
|
print_time(self->window);
|
||||||
|
wprintw(self->window, "Friend request with the message: '%s'\n", data);
|
||||||
|
|
||||||
int n = add_friend_request(key);
|
int n = add_friend_request(key);
|
||||||
|
|
||||||
@ -230,7 +400,7 @@ static void prompt_onFriendRequest(ToxWindow *self, uint8_t *key, uint8_t *data,
|
|||||||
}
|
}
|
||||||
|
|
||||||
wprintw(self->window, "Type \"/accept %d\" to accept it.\n", n);
|
wprintw(self->window, "Type \"/accept %d\" to accept it.\n", n);
|
||||||
alert_window(self);
|
alert_window(self, WINDOW_ALERT_1, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void prompt_init_statusbar(ToxWindow *self, Tox *m)
|
void prompt_init_statusbar(ToxWindow *self, Tox *m)
|
||||||
@ -239,16 +409,25 @@ void prompt_init_statusbar(ToxWindow *self, Tox *m)
|
|||||||
getmaxyx(self->window, y, x);
|
getmaxyx(self->window, y, x);
|
||||||
|
|
||||||
/* Init statusbar info */
|
/* Init statusbar info */
|
||||||
StatusBar *statusbar = (StatusBar *) self->stb;
|
StatusBar *statusbar = self->stb;
|
||||||
statusbar->status = TOX_USERSTATUS_NONE;
|
statusbar->status = TOX_USERSTATUS_NONE;
|
||||||
statusbar->is_online = false;
|
statusbar->is_online = false;
|
||||||
|
|
||||||
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
|
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
|
||||||
tox_getselfname(m, nick, TOX_MAX_NAME_LENGTH);
|
tox_get_self_name(m, nick, TOX_MAX_NAME_LENGTH);
|
||||||
snprintf(statusbar->nick, sizeof(statusbar->nick), "%s", nick);
|
snprintf(statusbar->nick, sizeof(statusbar->nick), "%s", nick);
|
||||||
|
|
||||||
/* temporary until statusmessage saving works */
|
/* temporary until statusmessage saving works */
|
||||||
uint8_t *statusmsg = "Toxing on Toxic v.0.2.3";
|
uint8_t ver[strlen(TOXICVER) + 1];
|
||||||
|
uint8_t statusmsg[MAX_STR_SIZE];
|
||||||
|
strcpy(ver, TOXICVER);
|
||||||
|
uint8_t *toxic_ver = strtok(ver, "_");
|
||||||
|
|
||||||
|
if (toxic_ver != NULL)
|
||||||
|
snprintf(statusmsg, MAX_STR_SIZE, "Toxing on Toxic v.%s", toxic_ver);
|
||||||
|
else
|
||||||
|
snprintf(statusmsg, MAX_STR_SIZE, "Toxing on Toxic hacker edition");
|
||||||
|
|
||||||
m_set_statusmessage(m, statusmsg, strlen(statusmsg) + 1);
|
m_set_statusmessage(m, statusmsg, strlen(statusmsg) + 1);
|
||||||
snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg);
|
snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg);
|
||||||
|
|
||||||
@ -261,6 +440,8 @@ ToxWindow new_prompt(void)
|
|||||||
ToxWindow ret;
|
ToxWindow ret;
|
||||||
memset(&ret, 0, sizeof(ret));
|
memset(&ret, 0, sizeof(ret));
|
||||||
|
|
||||||
|
ret.active = true;
|
||||||
|
|
||||||
ret.onKey = &prompt_onKey;
|
ret.onKey = &prompt_onKey;
|
||||||
ret.onDraw = &prompt_onDraw;
|
ret.onDraw = &prompt_onDraw;
|
||||||
ret.onInit = &prompt_onInit;
|
ret.onInit = &prompt_onInit;
|
||||||
@ -269,11 +450,13 @@ ToxWindow new_prompt(void)
|
|||||||
|
|
||||||
strcpy(ret.name, "prompt");
|
strcpy(ret.name, "prompt");
|
||||||
|
|
||||||
|
PromptBuf *promptbuf = calloc(1, sizeof(PromptBuf));
|
||||||
StatusBar *stb = calloc(1, sizeof(StatusBar));
|
StatusBar *stb = calloc(1, sizeof(StatusBar));
|
||||||
|
|
||||||
if (stb != NULL)
|
if (stb != NULL && promptbuf != NULL) {
|
||||||
|
ret.promptbuf = promptbuf;
|
||||||
ret.stb = stb;
|
ret.stb = stb;
|
||||||
else {
|
} else {
|
||||||
endwin();
|
endwin();
|
||||||
fprintf(stderr, "calloc() failed. Aborting...\n");
|
fprintf(stderr, "calloc() failed. Aborting...\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
|
@ -5,7 +5,12 @@
|
|||||||
#ifndef PROMPT_H_UZYGWFFL
|
#ifndef PROMPT_H_UZYGWFFL
|
||||||
#define PROMPT_H_UZYGWFFL
|
#define PROMPT_H_UZYGWFFL
|
||||||
|
|
||||||
|
#define X_OFST 2 /* offset to account for prompt char */
|
||||||
|
|
||||||
|
#define AC_NUM_GLOB_COMMANDS 14
|
||||||
|
|
||||||
ToxWindow new_prompt(void);
|
ToxWindow new_prompt(void);
|
||||||
|
void prep_prompt_win(void);
|
||||||
void prompt_init_statusbar(ToxWindow *self, Tox *m);
|
void prompt_init_statusbar(ToxWindow *self, Tox *m);
|
||||||
void prompt_update_nick(ToxWindow *prompt, uint8_t *nick, uint16_t len);
|
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_statusmessage(ToxWindow *prompt, uint8_t *statusmsg, uint16_t len);
|
||||||
|
227
src/toxic_strings.c
Normal file
227
src/toxic_strings.c
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
/*
|
||||||
|
* Toxic -- Tox Curses Client
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "toxic_windows.h"
|
||||||
|
#include "misc_tools.h"
|
||||||
|
#include "toxic_strings.h"
|
||||||
|
|
||||||
|
/* Adds char to buffer at pos */
|
||||||
|
void add_char_to_buf(wchar_t *buf, size_t *pos, size_t *len, wint_t ch)
|
||||||
|
{
|
||||||
|
if (*pos < 0 || *len >= MAX_STR_SIZE)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* move all chars including null in front of pos one space forward and insert char in pos */
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = *len; i >= *pos && i >= 0; --i)
|
||||||
|
buf[i+1] = buf[i];
|
||||||
|
|
||||||
|
buf[(*pos)++] = ch;
|
||||||
|
++(*len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Deletes the character before pos */
|
||||||
|
void del_char_buf_bck(wchar_t *buf, size_t *pos, size_t *len)
|
||||||
|
{
|
||||||
|
if (*pos <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* similar to add_char_to_buf but deletes a char */
|
||||||
|
for (i = *pos-1; i <= *len; ++i)
|
||||||
|
buf[i] = buf[i+1];
|
||||||
|
|
||||||
|
--(*pos);
|
||||||
|
--(*len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Deletes the character at pos */
|
||||||
|
void del_char_buf_frnt(wchar_t *buf, size_t *pos, size_t *len)
|
||||||
|
{
|
||||||
|
if (*pos < 0 || *pos >= *len)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = *pos; i < *len; ++i)
|
||||||
|
buf[i] = buf[i+1];
|
||||||
|
|
||||||
|
--(*len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Deletes the line from beginning to pos */
|
||||||
|
void discard_buf(wchar_t *buf, size_t *pos, size_t *len)
|
||||||
|
{
|
||||||
|
if (*pos <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
int c = 0;
|
||||||
|
|
||||||
|
for (i = *pos; i <= *len; ++i)
|
||||||
|
buf[c++] = buf[i];
|
||||||
|
|
||||||
|
*pos = 0;
|
||||||
|
*len = c - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Deletes the line from pos to len */
|
||||||
|
void kill_buf(wchar_t *buf, size_t *pos, size_t *len)
|
||||||
|
{
|
||||||
|
if (*len == *pos)
|
||||||
|
return;
|
||||||
|
|
||||||
|
buf[*pos] = L'\0';
|
||||||
|
*len = *pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* nulls buf and sets pos and len to 0 */
|
||||||
|
void reset_buf(wchar_t *buf, size_t *pos, size_t *len)
|
||||||
|
{
|
||||||
|
buf[0] = L'\0';
|
||||||
|
*pos = 0;
|
||||||
|
*len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define HIST_PURGE MAX_LINE_HIST / 4
|
||||||
|
|
||||||
|
/* shifts hist items back and makes room for HIST_PURGE new entries */
|
||||||
|
static void shift_hist_back(wchar_t (*hst)[MAX_STR_SIZE], int *hst_tot)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int n = MAX_LINE_HIST - HIST_PURGE;
|
||||||
|
|
||||||
|
for (i = 0; i < n; ++i)
|
||||||
|
wmemcpy(hst[i], hst[i+HIST_PURGE], MAX_STR_SIZE);
|
||||||
|
|
||||||
|
*hst_tot = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* adds a line to the ln_history buffer at hst_pos and sets hst_pos to end of history. */
|
||||||
|
void add_line_to_hist(const wchar_t *buf, size_t len, wchar_t (*hst)[MAX_STR_SIZE], int *hst_tot,
|
||||||
|
int *hst_pos)
|
||||||
|
{
|
||||||
|
if (len > MAX_STR_SIZE)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (*hst_tot >= MAX_LINE_HIST)
|
||||||
|
shift_hist_back(hst, hst_tot);
|
||||||
|
|
||||||
|
++(*hst_tot);
|
||||||
|
*hst_pos = *hst_tot;
|
||||||
|
|
||||||
|
wmemcpy(hst[*hst_tot-1], buf, len + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* copies history item at hst_pos to buf. Sets pos and len to the len of the history item.
|
||||||
|
hst_pos is decremented or incremented depending on key_dir. */
|
||||||
|
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 (--(*hst_pos) < 0) {
|
||||||
|
++(*hst_pos);
|
||||||
|
beep();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (++(*hst_pos) >= hst_tot) {
|
||||||
|
--(*hst_pos);
|
||||||
|
beep();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const wchar_t *hst_line = hst[*hst_pos];
|
||||||
|
size_t h_len = wcslen(hst_line);
|
||||||
|
|
||||||
|
wmemcpy(buf, hst_line, h_len + 1);
|
||||||
|
|
||||||
|
*pos = h_len;
|
||||||
|
*len = h_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* looks for the first instance in list that begins with the last entered word in buf according to pos,
|
||||||
|
then fills buf with the complete word. e.g. "Hello jo" would complete the buffer
|
||||||
|
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 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)
|
||||||
|
{
|
||||||
|
if (*pos <= 0 || *len <= 0 || *len >= MAX_STR_SIZE)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
const uint8_t *L = (uint8_t *) list;
|
||||||
|
|
||||||
|
uint8_t ubuf[MAX_STR_SIZE];
|
||||||
|
/* work with multibyte string copy of buf for simplicity */
|
||||||
|
if (wcs_to_mbs_buf(ubuf, buf, MAX_STR_SIZE) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* isolate substring from space behind pos to pos */
|
||||||
|
uint8_t tmp[MAX_STR_SIZE];
|
||||||
|
snprintf(tmp, sizeof(tmp), "%s", ubuf);
|
||||||
|
tmp[*pos] = '\0';
|
||||||
|
uint8_t *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 uint8_t *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 uint8_t *endchrs = n_endchrs == 1 ? " " : ": ";
|
||||||
|
int m_len = strlen(match);
|
||||||
|
int strt = (int) *pos - s_len;
|
||||||
|
int diff = m_len - s_len + n_endchrs;
|
||||||
|
|
||||||
|
if (*len + diff > MAX_STR_SIZE)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
uint8_t tmpend[MAX_STR_SIZE];
|
||||||
|
strcpy(tmpend, &ubuf[*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(buf, newbuf);
|
||||||
|
|
||||||
|
*len += (size_t) diff;
|
||||||
|
*pos += (size_t) diff;
|
||||||
|
|
||||||
|
return diff;
|
||||||
|
}
|
45
src/toxic_strings.h
Normal file
45
src/toxic_strings.h
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Toxic -- Tox Curses Client
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Adds char to buffer at pos */
|
||||||
|
void add_char_to_buf(wchar_t *buf, size_t *pos, size_t *len, wint_t ch);
|
||||||
|
|
||||||
|
/* Deletes the character before pos */
|
||||||
|
void del_char_buf_bck(wchar_t *buf, size_t *pos, size_t *len);
|
||||||
|
|
||||||
|
/* Deletes the character at pos */
|
||||||
|
void del_char_buf_frnt(wchar_t *buf, size_t *pos, size_t *len);
|
||||||
|
|
||||||
|
/* Deletes the line from beginning to pos */
|
||||||
|
void discard_buf(wchar_t *buf, size_t *pos, size_t *len);
|
||||||
|
|
||||||
|
/* Deletes the line from pos to len */
|
||||||
|
void kill_buf(wchar_t *buf, size_t *pos, size_t *len);
|
||||||
|
|
||||||
|
/* nulls buf and sets pos and len to 0 */
|
||||||
|
void reset_buf(wchar_t *buf, size_t *pos, size_t *len);
|
||||||
|
|
||||||
|
/* looks for the first instance in list that begins with the last entered word in buf according to pos,
|
||||||
|
then fills buf with the complete word. e.g. "Hello jo" would complete the buffer
|
||||||
|
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 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);
|
||||||
|
|
||||||
|
/* copies history item at hst_pos to buf. Sets pos and len to the len of the history item.
|
||||||
|
hst_pos is decremented or incremented depending on key_dir. */
|
||||||
|
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);
|
@ -19,17 +19,24 @@
|
|||||||
|
|
||||||
#define MAX_WINDOWS_NUM 32
|
#define MAX_WINDOWS_NUM 32
|
||||||
#define MAX_FRIENDS_NUM 100
|
#define MAX_FRIENDS_NUM 100
|
||||||
#define MAX_GROUPCHAT_NUM MAX_WINDOWS_NUM - N_DEFAULT_WINS
|
|
||||||
#define MAX_STR_SIZE 256
|
#define MAX_STR_SIZE 256
|
||||||
|
#define MAX_CMDNAME_SIZE 64
|
||||||
#define KEY_SIZE_BYTES 32
|
#define KEY_SIZE_BYTES 32
|
||||||
#define TOXIC_MAX_NAME_LENGTH 32 /* Must be <= TOX_MAX_NAME_LENGTH */
|
#define TOXIC_MAX_NAME_LENGTH 32 /* Must be <= TOX_MAX_NAME_LENGTH */
|
||||||
#define N_DEFAULT_WINS 2 /* number of permanent default windows */
|
#define N_DEFAULT_WINS 2 /* number of permanent default windows */
|
||||||
#define CURS_Y_OFFSET 3 /* y-axis cursor offset for chat contexts */
|
#define CURS_Y_OFFSET 3 /* y-axis cursor offset for chat contexts */
|
||||||
#define CHATBOX_HEIGHT 4
|
#define CHATBOX_HEIGHT 4
|
||||||
|
#define KEY_IDENT_DIGITS 2 /* number of hex digits to display for the pub-key based identifier */
|
||||||
|
|
||||||
#define EXIT_SUCCESS 0
|
#define EXIT_SUCCESS 0
|
||||||
#define EXIT_FAILURE 1
|
#define EXIT_FAILURE 1
|
||||||
|
|
||||||
|
/* ASCII key codes */
|
||||||
|
#define T_KEY_KILL 0xB /* ctrl-k */
|
||||||
|
#define T_KEY_DISCARD 0x15 /* ctrl-u */
|
||||||
|
#define T_KEY_NEXT 0x10 /* ctrl-p */
|
||||||
|
#define T_KEY_PREV 0x0F /* ctrl-o */
|
||||||
|
|
||||||
/* Curses foreground colours (background is black) */
|
/* Curses foreground colours (background is black) */
|
||||||
enum {
|
enum {
|
||||||
WHITE,
|
WHITE,
|
||||||
@ -42,43 +49,61 @@ enum {
|
|||||||
BLACK,
|
BLACK,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* tab alert types: lower types take priority */
|
||||||
|
enum {
|
||||||
|
WINDOW_ALERT_0,
|
||||||
|
WINDOW_ALERT_1,
|
||||||
|
WINDOW_ALERT_2,
|
||||||
|
};
|
||||||
|
|
||||||
/* Fixes text color problem on some terminals.
|
/* Fixes text color problem on some terminals.
|
||||||
Uncomment if necessary */
|
Uncomment if necessary */
|
||||||
//#define URXVT_FIX
|
//#define URXVT_FIX
|
||||||
|
|
||||||
typedef struct ToxWindow_ ToxWindow;
|
typedef struct ToxWindow ToxWindow;
|
||||||
|
typedef struct StatusBar StatusBar;
|
||||||
|
typedef struct PromptBuf PromptBuf;
|
||||||
|
typedef struct ChatContext ChatContext;
|
||||||
|
|
||||||
struct ToxWindow_ {
|
struct ToxWindow {
|
||||||
void(*onKey)(ToxWindow *, Tox *, wint_t);
|
void(*onKey)(ToxWindow *, Tox *, wint_t);
|
||||||
void(*onDraw)(ToxWindow *, Tox *);
|
void(*onDraw)(ToxWindow *, Tox *);
|
||||||
void(*onInit)(ToxWindow *, Tox *);
|
void(*onInit)(ToxWindow *, Tox *);
|
||||||
void(*onFriendRequest)(ToxWindow *, uint8_t *, uint8_t *, uint16_t);
|
void(*onFriendRequest)(ToxWindow *, uint8_t *, uint8_t *, uint16_t);
|
||||||
void(*onFriendAdded)(ToxWindow *, Tox *, int);
|
void(*onFriendAdded)(ToxWindow *, Tox *, int, bool);
|
||||||
void(*onConnectionChange)(ToxWindow *, Tox *, int, uint8_t);
|
void(*onConnectionChange)(ToxWindow *, Tox *, int, uint8_t);
|
||||||
void(*onMessage)(ToxWindow *, Tox *, int, uint8_t *, uint16_t);
|
void(*onMessage)(ToxWindow *, Tox *, int, uint8_t *, uint16_t);
|
||||||
void(*onNickChange)(ToxWindow *, int, uint8_t *, uint16_t);
|
void(*onNickChange)(ToxWindow *, Tox *, int, uint8_t *, uint16_t);
|
||||||
void(*onStatusChange)(ToxWindow *, Tox *, int, TOX_USERSTATUS);
|
void(*onStatusChange)(ToxWindow *, Tox *, int, TOX_USERSTATUS);
|
||||||
void(*onStatusMessageChange)(ToxWindow *, int, uint8_t *, uint16_t);
|
void(*onStatusMessageChange)(ToxWindow *, int, uint8_t *, uint16_t);
|
||||||
void(*onAction)(ToxWindow *, Tox *, int, uint8_t *, uint16_t);
|
void(*onAction)(ToxWindow *, Tox *, int, uint8_t *, uint16_t);
|
||||||
void(*onGroupMessage)(ToxWindow *, Tox *, int, int, 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 *, int, uint8_t *);
|
||||||
void(*onGroupNamelistChange)(ToxWindow *, Tox*, int, int, uint8_t);
|
void(*onGroupNamelistChange)(ToxWindow *, Tox*, int, int, uint8_t);
|
||||||
void(*onFileSendRequest)(ToxWindow *, Tox *, int, uint8_t, uint64_t, uint8_t *, uint16_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(*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(*onFileData)(ToxWindow *, Tox *, int, uint8_t, uint8_t *, uint16_t);
|
||||||
|
void(*onTypingChange)(ToxWindow *, Tox *, int, int);
|
||||||
|
|
||||||
char name[TOX_MAX_NAME_LENGTH];
|
char name[TOX_MAX_NAME_LENGTH];
|
||||||
bool blink;
|
|
||||||
int num;
|
int num;
|
||||||
|
bool active;
|
||||||
int x;
|
int x;
|
||||||
|
|
||||||
void *chatwin;
|
bool alert0;
|
||||||
void *stb;
|
bool alert1;
|
||||||
|
bool alert2;
|
||||||
|
|
||||||
|
ChatContext *chatwin;
|
||||||
|
PromptBuf *promptbuf;
|
||||||
|
StatusBar *stb;
|
||||||
|
|
||||||
WINDOW *window;
|
WINDOW *window;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
/* statusbar info holder */
|
||||||
|
struct StatusBar {
|
||||||
WINDOW *topline;
|
WINDOW *topline;
|
||||||
uint8_t statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH];
|
uint8_t statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH];
|
||||||
uint16_t statusmsg_len;
|
uint16_t statusmsg_len;
|
||||||
@ -86,22 +111,48 @@ typedef struct {
|
|||||||
uint16_t nick_len;
|
uint16_t nick_len;
|
||||||
TOX_USERSTATUS status;
|
TOX_USERSTATUS status;
|
||||||
bool is_online;
|
bool is_online;
|
||||||
} StatusBar;
|
};
|
||||||
|
|
||||||
typedef struct {
|
#define MAX_LINE_HIST 128
|
||||||
|
|
||||||
|
/* chat and groupchat window/buffer holder */
|
||||||
|
struct ChatContext {
|
||||||
wchar_t line[MAX_STR_SIZE];
|
wchar_t line[MAX_STR_SIZE];
|
||||||
size_t pos;
|
size_t pos;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
wchar_t ln_history[MAX_LINE_HIST][MAX_STR_SIZE];
|
||||||
|
int hst_pos;
|
||||||
|
int hst_tot;
|
||||||
|
|
||||||
|
bool self_is_typing;
|
||||||
|
|
||||||
WINDOW *history;
|
WINDOW *history;
|
||||||
WINDOW *linewin;
|
WINDOW *linewin;
|
||||||
WINDOW *sidebar;
|
WINDOW *sidebar;
|
||||||
} ChatContext;
|
};
|
||||||
|
|
||||||
|
/* prompt window/buffer holder */
|
||||||
|
struct PromptBuf {
|
||||||
|
wchar_t line[MAX_STR_SIZE];
|
||||||
|
size_t pos;
|
||||||
|
size_t len;
|
||||||
|
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;
|
||||||
|
|
||||||
|
WINDOW *linewin;
|
||||||
|
};
|
||||||
|
|
||||||
/* Start file transfer code */
|
/* Start file transfer code */
|
||||||
|
|
||||||
#define MAX_FILES 256
|
#define MAX_FILES 256
|
||||||
#define FILE_PIECE_SIZE 1024
|
#define FILE_PIECE_SIZE 1024
|
||||||
#define TIMEOUT_FILESENDER 300
|
#define TIMEOUT_FILESENDER 300
|
||||||
#define MAX_PIECES_SEND 100 /* Max number of pieces to send per file per call to do_file_senders() */
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
FILE *file;
|
FILE *file;
|
||||||
@ -115,32 +166,13 @@ typedef struct {
|
|||||||
uint64_t timestamp;
|
uint64_t timestamp;
|
||||||
} FileSender;
|
} FileSender;
|
||||||
|
|
||||||
FileSender file_senders[MAX_FILES];
|
struct FileReceiver {
|
||||||
uint8_t num_file_senders;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint8_t filenames[MAX_FILES][MAX_STR_SIZE];
|
uint8_t filenames[MAX_FILES][MAX_STR_SIZE];
|
||||||
bool pending[MAX_FILES];
|
bool pending[MAX_FILES];
|
||||||
} FileReceiver;
|
};
|
||||||
|
|
||||||
/* End file transfer code */
|
/* End file transfer code */
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint8_t name[TOX_MAX_NAME_LENGTH];
|
|
||||||
uint16_t namelength;
|
|
||||||
uint8_t statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH];
|
|
||||||
uint16_t statusmsg_len;
|
|
||||||
uint8_t pending_groupchat[TOX_CLIENT_ID_SIZE];
|
|
||||||
int num;
|
|
||||||
int chatwin;
|
|
||||||
bool active;
|
|
||||||
bool online;
|
|
||||||
TOX_USERSTATUS status;
|
|
||||||
FileReceiver file_receiver;
|
|
||||||
} friend_t;
|
|
||||||
|
|
||||||
friend_t friends[MAX_FRIENDS_NUM];
|
|
||||||
|
|
||||||
void on_request(uint8_t *public_key, uint8_t *data, uint16_t length, void *userdata);
|
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_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_message(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void *userdata);
|
||||||
@ -148,17 +180,20 @@ void on_action(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void
|
|||||||
void on_nickchange(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_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_statusmessagechange(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void *userdata);
|
||||||
void on_friendadded(Tox *m, int friendnumber);
|
void on_friendadded(Tox *m, int friendnumber, bool sort);
|
||||||
void on_groupmessage(Tox *m, int groupnumber, int peernumber, uint8_t *message, uint16_t length, void *userdata);
|
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, int 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_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_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_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_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);
|
||||||
|
|
||||||
ToxWindow *init_windows(Tox *mToAssign);
|
ToxWindow *init_windows(Tox *m);
|
||||||
void draw_active_window(Tox *m);
|
void draw_active_window(Tox *m);
|
||||||
int add_window(Tox *m, ToxWindow w);
|
int add_window(Tox *m, ToxWindow w);
|
||||||
void del_window(ToxWindow *w);
|
void del_window(ToxWindow *w);
|
||||||
void set_active_window(int ch);
|
void set_active_window(int ch);
|
||||||
|
int num_active_windows(void);
|
||||||
#endif
|
#endif
|
||||||
|
100
src/windows.c
100
src/windows.c
@ -13,8 +13,8 @@ extern char *DATA_FILE;
|
|||||||
|
|
||||||
static ToxWindow windows[MAX_WINDOWS_NUM];
|
static ToxWindow windows[MAX_WINDOWS_NUM];
|
||||||
static ToxWindow *active_window;
|
static ToxWindow *active_window;
|
||||||
static Tox *m;
|
|
||||||
static ToxWindow *prompt;
|
extern ToxWindow *prompt;
|
||||||
|
|
||||||
/* CALLBACKS START */
|
/* CALLBACKS START */
|
||||||
void on_request(uint8_t *public_key, uint8_t *data, uint16_t length, void *userdata)
|
void on_request(uint8_t *public_key, uint8_t *data, uint16_t length, void *userdata)
|
||||||
@ -37,6 +37,16 @@ void on_connectionchange(Tox *m, int friendnumber, uint8_t status, void *userdat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void on_typing_change(Tox *m, int friendnumber, int is_typing, void *userdata)
|
||||||
|
{
|
||||||
|
int 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, int friendnumber, uint8_t *string, uint16_t length, void *userdata)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@ -66,7 +76,7 @@ void on_nickchange(Tox *m, int friendnumber, uint8_t *string, uint16_t length, v
|
|||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||||
if (windows[i].onNickChange != NULL)
|
if (windows[i].onNickChange != NULL)
|
||||||
windows[i].onNickChange(&windows[i], friendnumber, string, length);
|
windows[i].onNickChange(&windows[i], m, friendnumber, string, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (store_data(m, DATA_FILE))
|
if (store_data(m, DATA_FILE))
|
||||||
@ -93,13 +103,13 @@ void on_statuschange(Tox *m, int friendnumber, TOX_USERSTATUS status, void *user
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_friendadded(Tox *m, int friendnumber)
|
void on_friendadded(Tox *m, int friendnumber, bool sort)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||||
if (windows[i].onFriendAdded != NULL)
|
if (windows[i].onFriendAdded != NULL)
|
||||||
windows[i].onFriendAdded(&windows[i], m, friendnumber);
|
windows[i].onFriendAdded(&windows[i], m, friendnumber, sort);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (store_data(m, DATA_FILE))
|
if (store_data(m, DATA_FILE))
|
||||||
@ -117,6 +127,17 @@ void on_groupmessage(Tox *m, int groupnumber, int peernumber, uint8_t *message,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void on_groupaction(Tox *m, int groupnumber, int peernumber, uint8_t *action, uint16_t length,
|
||||||
|
void *userdata)
|
||||||
|
{
|
||||||
|
int 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, int friendnumber, uint8_t *group_pub_key, void *userdata)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@ -182,7 +203,7 @@ int add_window(Tox *m, ToxWindow w)
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; i++) {
|
for (i = 0; i < MAX_WINDOWS_NUM; i++) {
|
||||||
if (windows[i].window)
|
if (windows[i].active)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
w.window = newwin(LINES - 2, COLS, 0, 0);
|
w.window = newwin(LINES - 2, COLS, 0, 0);
|
||||||
@ -221,7 +242,7 @@ void set_next_window(int ch)
|
|||||||
ToxWindow *inf = active_window;
|
ToxWindow *inf = active_window;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (ch == '\t') {
|
if (ch == T_KEY_NEXT) {
|
||||||
if (++active_window > end)
|
if (++active_window > end)
|
||||||
active_window = windows;
|
active_window = windows;
|
||||||
} else if (--active_window < windows)
|
} else if (--active_window < windows)
|
||||||
@ -246,9 +267,8 @@ void set_active_window(int index)
|
|||||||
active_window = windows + index;
|
active_window = windows + index;
|
||||||
}
|
}
|
||||||
|
|
||||||
ToxWindow *init_windows(Tox *mToAssign)
|
ToxWindow *init_windows(Tox *m)
|
||||||
{
|
{
|
||||||
m = mToAssign;
|
|
||||||
int n_prompt = add_window(m, new_prompt());
|
int n_prompt = add_window(m, new_prompt());
|
||||||
|
|
||||||
if (n_prompt == -1 || add_window(m, new_friendlist()) == -1) {
|
if (n_prompt == -1 || add_window(m, new_friendlist()) == -1) {
|
||||||
@ -263,12 +283,29 @@ ToxWindow *init_windows(Tox *mToAssign)
|
|||||||
return prompt;
|
return prompt;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define TAB_BLINKRATE 30
|
static void draw_window_tab(ToxWindow toxwin)
|
||||||
|
{
|
||||||
|
/* alert0 takes priority */
|
||||||
|
if (toxwin.alert0)
|
||||||
|
attron(COLOR_PAIR(GREEN));
|
||||||
|
else if (toxwin.alert1)
|
||||||
|
attron(COLOR_PAIR(RED));
|
||||||
|
else if (toxwin.alert2)
|
||||||
|
attron(COLOR_PAIR(MAGENTA));
|
||||||
|
|
||||||
|
clrtoeol();
|
||||||
|
printw(" [%s]", toxwin.name);
|
||||||
|
|
||||||
|
if (toxwin.alert0)
|
||||||
|
attroff(COLOR_PAIR(GREEN));
|
||||||
|
else if (toxwin.alert1)
|
||||||
|
attroff(COLOR_PAIR(RED));
|
||||||
|
else if (toxwin.alert2)
|
||||||
|
attroff(COLOR_PAIR(MAGENTA));
|
||||||
|
}
|
||||||
|
|
||||||
static void draw_bar(void)
|
static void draw_bar(void)
|
||||||
{
|
{
|
||||||
static int odd = 0;
|
|
||||||
|
|
||||||
attron(COLOR_PAIR(BLUE));
|
attron(COLOR_PAIR(BLUE));
|
||||||
mvhline(LINES - 2, 0, '_', COLS);
|
mvhline(LINES - 2, 0, '_', COLS);
|
||||||
attroff(COLOR_PAIR(BLUE));
|
attroff(COLOR_PAIR(BLUE));
|
||||||
@ -282,7 +319,7 @@ static void draw_bar(void)
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||||
if (windows[i].window) {
|
if (windows[i].active) {
|
||||||
if (windows + i == active_window) {
|
if (windows + i == active_window) {
|
||||||
#ifdef URXVT_FIX
|
#ifdef URXVT_FIX
|
||||||
attron(A_BOLD | COLOR_PAIR(GREEN));
|
attron(A_BOLD | COLOR_PAIR(GREEN));
|
||||||
@ -291,18 +328,9 @@ static void draw_bar(void)
|
|||||||
attron(A_BOLD);
|
attron(A_BOLD);
|
||||||
}
|
}
|
||||||
|
|
||||||
odd = (odd + 1) % TAB_BLINKRATE;
|
draw_window_tab(windows[i]);
|
||||||
|
|
||||||
if (windows[i].blink && (odd < (TAB_BLINKRATE / 2)))
|
if (windows + i == active_window) {
|
||||||
attron(COLOR_PAIR(RED));
|
|
||||||
|
|
||||||
clrtoeol();
|
|
||||||
printw(" [%s]", windows[i].name);
|
|
||||||
|
|
||||||
if (windows[i].blink && (odd < (TAB_BLINKRATE / 2)))
|
|
||||||
attroff(COLOR_PAIR(RED));
|
|
||||||
|
|
||||||
if (windows + i == active_window) {
|
|
||||||
#ifdef URXVT_FIX
|
#ifdef URXVT_FIX
|
||||||
attroff(A_BOLD | COLOR_PAIR(GREEN));
|
attroff(A_BOLD | COLOR_PAIR(GREEN));
|
||||||
} else {
|
} else {
|
||||||
@ -318,16 +346,21 @@ static void draw_bar(void)
|
|||||||
void draw_active_window(Tox *m)
|
void draw_active_window(Tox *m)
|
||||||
{
|
{
|
||||||
ToxWindow *a = active_window;
|
ToxWindow *a = active_window;
|
||||||
|
a->alert0 = false;
|
||||||
|
a->alert1 = false;
|
||||||
|
a->alert2 = false;
|
||||||
|
|
||||||
wint_t ch = 0;
|
wint_t ch = 0;
|
||||||
|
|
||||||
|
draw_bar();
|
||||||
|
|
||||||
touchwin(a->window);
|
touchwin(a->window);
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
wresize(a->window, LINES - 2, COLS);
|
wresize(a->window, LINES - 2, COLS);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
a->blink = false;
|
|
||||||
draw_bar();
|
|
||||||
a->onDraw(a, m);
|
a->onDraw(a, m);
|
||||||
|
wrefresh(a->window);
|
||||||
|
|
||||||
/* Handle input */
|
/* Handle input */
|
||||||
#ifdef HAVE_WIDECHAR
|
#ifdef HAVE_WIDECHAR
|
||||||
@ -336,8 +369,21 @@ void draw_active_window(Tox *m)
|
|||||||
ch = getch();
|
ch = getch();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (ch == '\t' || ch == KEY_BTAB)
|
if (ch == T_KEY_NEXT || ch == T_KEY_PREV)
|
||||||
set_next_window((int) ch);
|
set_next_window((int) ch);
|
||||||
else if (ch != ERR)
|
else if (ch != ERR)
|
||||||
a->onKey(a, m, ch);
|
a->onKey(a, m, ch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int num_active_windows(void)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||||
|
if (windows[i].active)
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user