1
0
mirror of https://github.com/Tha14/toxic.git synced 2025-06-26 22:46:44 +02:00

Compare commits

...

161 Commits

Author SHA1 Message Date
7f38c3c6e7 add prompt logging support 2014-03-01 18:06:35 -05:00
7109b8fa18 refactor logging functions to only handle chatlog pointers 2014-03-01 07:10:44 -05:00
1e503b1080 fix 2014-02-27 23:38:15 -05:00
4fb82cceaa save logging preference for friend chats and improve log command message 2014-02-27 23:33:00 -05:00
46b046a209 make C-e and C-aa work like they do in bash and fix/format help messages 2014-02-27 18:55:18 -05:00
6ee1f1ed0f fix 2014-02-26 21:30:27 -05:00
044b731089 Fix bug 2014-02-26 21:00:35 -05:00
d83ef1d8be update help messages 2014-02-26 19:45:11 -05:00
459eb64dc4 Merge branch 'master' of https://github.com/Tox/toxic 2014-02-26 19:03:15 -05:00
af5627b050 update to 0.2.7 2014-02-26 19:02:22 -05:00
9b57c05648 add command to turn logs on/off 2014-02-26 19:00:13 -05:00
817f763589 give groupchat logs unique names 2014-02-26 17:15:34 -05:00
8e4db369bc log events 2014-02-26 06:35:19 -05:00
a61f5f6a6d properly close windows on exit 2014-02-26 05:23:11 -05:00
5ff7065744 basic logging for groupchats 2014-02-26 03:51:26 -05:00
831d8e5f24 implement chat logging 2014-02-26 01:51:06 -05:00
6896292c49 Update README.md 2014-02-25 03:07:03 -05:00
b6613a015f add license info to source files 2014-02-25 02:28:24 -05:00
2d9f4facf7 connect to limited number of nodes on init instead of all of them 2014-02-24 20:08:51 -05:00
e7920d1da7 fix connection error codes 2014-02-24 19:41:02 -05:00
eb09fceed7 fix bug and some cleanup 2014-02-24 19:18:43 -05:00
b308e19e6b Merge pull request #80 from viric/dhtservers
Fallback to loading /usr/share/toxic/DHTservers.
2014-02-24 02:17:14 -05:00
5867f1a672 Merge pull request #82 from kl4ng/master
down arrow returns empty string if at end of history
2014-02-23 12:49:20 -08:00
5187861b69 down arrow returns empty string if at end of history 2014-02-23 14:22:45 -05:00
0013dae552 Merge branch 'master' of https://github.com/Tox/toxic 2014-02-23 05:40:21 -05:00
b018aa384e small fix and don't show typing alert for /commands 2014-02-23 05:38:44 -05:00
ad8f99dae4 Revert that 2014-02-23 02:19:22 -08:00
5b9d3f6f62 fix segfault 2014-02-23 05:15:48 -05:00
93bcecde70 Update README.md 2014-02-23 02:11:51 -08:00
6be1c907d9 Fallback to loading /usr/share/toxic/DHTservers.
First try ~/.config/tox/DHTservers, and fallback to the /usr/share
one if the former fails.

This should allow people just starting toxic for the first time to
connect to the DHT.
2014-02-23 09:52:07 +00:00
9f309ecb96 Move to 0.2.6 2014-02-23 01:37:43 -08:00
2b707f1d80 implemented typing status 2014-02-23 04:28:33 -05:00
6269eafeaa Merge branch 'master' of https://github.com/Tox/toxic 2014-02-22 04:52:08 -05:00
18b0abe25d merge 2014-02-22 04:51:54 -05:00
352656230c HOW DO YOU FORGET ABOUT THIS FOR 6 MONTHS??? 2014-02-22 01:51:30 -08:00
da6ef159e6 null check just incase 2014-02-22 04:50:47 -05:00
9e2fde8d84 make status message version not hard-coded 2014-02-22 04:46:35 -05:00
aec16a22a4 Update prompt.c 2014-02-22 01:39:32 -08:00
3960208fd6 Update configure.ac 2014-02-22 01:25:10 -08:00
198f9af71d Fine 2014-02-22 01:21:37 -08:00
61b2a7f0c9 Use ssl 2014-02-22 00:59:33 -08:00
137b8c2412 Merge pull request #78 from kl4ng/master
ncurses libraries README note
2014-02-22 01:13:46 -05:00
fd3604be44 small fix 2014-02-22 01:04:40 -05:00
c7a2643ee0 added other required package 2014-02-22 00:26:46 -05:00
77875d0ca6 typo 2014-02-22 00:20:29 -05:00
e8a72146b0 added note about ncurses libraries 2014-02-21 23:59:11 -05:00
a4cb568558 small fix 2014-02-21 22:02:16 -05:00
9353c2078b Merge branch 'master' of https://github.com/Tox/toxic 2014-02-21 21:30:29 -05:00
7e23afb57e make friendlist offline statuses bold 2014-02-21 21:24:33 -05:00
2982dc6ddd show pseudo-unique identifier in friend chat windows 2014-02-21 21:21:12 -05:00
23d47e9539 Merge pull request #77 from alevy/umask
umask such that stored files are u+rw only
2014-02-21 18:31:15 -05:00
a68b733d58 umask such that stored files are u+rw only 2014-02-21 01:25:49 -08:00
4294e39b49 check connection only once per call 2014-02-21 01:57:13 -05:00
d9142eb355 restructure main toxic loop 2014-02-21 00:45:29 -05:00
69c467fa5f rm unnecessary file transfer limit 2014-02-20 07:47:19 -05:00
eb2d6afa00 make own nick green in groupchats 2014-02-16 23:15:27 -05:00
5f1ddcf5f6 make error msg more clear for normal users 2014-02-14 05:10:53 -05:00
07089271cc prompt tab alealert when friend comes online 2014-02-13 06:24:58 -05:00
c3d2ff6bfb couple fixes 2014-02-11 19:12:26 -05:00
16b8f85d95 send name instead of full path with file send request 2014-02-11 18:52:04 -05:00
1f06606c5b enforce toxic max name length in groupchats 2014-02-10 20:13:22 -05:00
0dd588182b don't alert when you type your own nick in groupchat 2014-02-09 22:55:42 -05:00
3cd15f3846 show number of online friends 2014-02-07 23:31:35 -05:00
bd4b430fbb count_friendlist is too inefficient to use that way 2014-02-07 23:28:17 -05:00
42cd80efe9 use API function for friend count instead of global var 2014-02-07 18:44:10 -05:00
da99d776df fix 2014-01-30 22:03:26 -05:00
ebf7d4517f don't modify name list when truncating nicks to fit in groupchat side panel 2014-01-30 22:01:08 -05:00
a028de17cd how did that get there 2014-01-25 01:04:05 -05:00
e360d2aa5a Merge branch 'master' of https://github.com/Tox/toxic 2014-01-22 23:30:27 -05:00
866cafbf1b timestamp for prompt connection and friend request alerts 2014-01-22 23:29:28 -05:00
d4e8b7de5d Moved to onion routing servers, removed dead ones 2014-01-21 06:53:05 -08:00
06b09cd981 correct value 2013-12-18 20:18:38 -05:00
feefecb2ac fix a couple bugs 2013-12-18 20:18:02 -05:00
d04f5fa102 beep when it should beep 2013-12-15 20:52:10 -05:00
dfb5b16e7d show friend count and change friend delete key to delete 2013-12-14 19:29:45 -05:00
14c9599a30 couple fixes 2013-12-14 15:38:21 -05:00
c371c37bce had the names confused 2013-12-14 01:10:22 -05:00
6f034d434a this makes more sense 2013-12-14 00:57:46 -05:00
eb6d832e3e correct usage of wcwidth() 2013-12-14 00:36:58 -05:00
10ae3865ca improve line history functionality 2013-12-14 00:36:06 -05:00
34cc4314a5 added groupchat actions 2013-12-13 20:57:32 -05:00
8b1dbd44ba Merge pull request #63 from aitjcize/groupchat-wchar
Fix groupchat cursor movement.
2013-12-11 21:57:27 -08:00
f3cdb3cd62 Fix groupchat cursor movement. 2013-12-12 13:55:42 +08:00
4dfdfa33a1 Merge pull request #62 from aitjcize/wchar-cursor
Fix wchar cursor movement.
2013-12-11 21:42:27 -08:00
bc8e737514 Fix wchar cursor movement. 2013-12-12 13:43:29 +08:00
1dad3711c4 better error handling for failed string conversions 2013-12-12 00:37:29 -05:00
12e1a60ca3 ugly partial fix for bug 2013-12-11 05:41:02 -05:00
ff30a29df1 small improvement 2013-12-11 04:49:21 -05:00
629041d465 simplify code a bit 2013-12-11 03:29:31 -05:00
83a81f6db6 Merge branch 'master' of https://github.com/Tox/toxic 2013-12-11 00:21:42 -05:00
9a5a598c5a add line history with up/down keys 2013-12-11 00:10:09 -05:00
d9d4329ccf Merge pull request #61 from naxuroqa/api-update
api update
2013-12-10 17:55:43 -08:00
ab2be21942 api update to irungentoo/ProjectTox-Core@2d841fb791 2013-12-11 01:51:40 +01:00
ccc0640dab put string helpers in own file 2013-12-10 03:03:45 -05:00
44d9f7fe61 busy status mutes beeping 2013-12-09 19:25:09 -05:00
a833be730f Merge branch 'master' of https://github.com/Tox/toxic 2013-12-09 18:00:35 -05:00
4065715b78 split lists up so only commands that work in given window autocomplete 2013-12-09 17:56:20 -05:00
f76b672d47 Update README.md 2013-12-09 03:28:20 -05:00
674aa682e7 implement tab completion for commands 2013-12-08 18:14:57 -05:00
9c2551b3b9 small fix 2013-12-08 16:10:52 -05:00
f67725f636 improve tab completion 2013-12-08 15:49:47 -05:00
5fc14a48db a few aesthetic changes 2013-12-08 05:45:23 -05:00
df57adcc6d fix memory leaks and safer way to convert strings from wc to mb 2013-12-08 04:16:49 -05:00
e834821348 replace name_compare with lib function 2013-12-08 01:18:10 -05:00
b8b032e441 a few fixes, and make tab completion case insensitive 2013-12-08 01:07:22 -05:00
4c27df32b0 add tab auto-complete function for groupchat names 2013-12-07 22:10:32 -05:00
3804233c21 didnt mean to change that 2013-12-06 22:07:37 -05:00
eb9d4361f0 Merge branch 'master' of https://github.com/Tox/toxic 2013-12-06 22:04:28 -05:00
e224f92210 change keys toctrl-o and p due to weird bug with ctrl-[ 2013-12-06 22:03:35 -05:00
6089f02d57 If IPv6 fails, print something and try IPv4. 2013-12-06 21:43:57 -05:00
d232538317 make ctrl-] [ keys switch pages 2013-12-06 20:56:30 -05:00
f942982f4e alert user when name is mentioned in groupchat 2013-12-06 19:41:53 -05:00
ce45580c83 partially fix bug where input line eats prompt messages 2013-12-06 05:07:35 -05:00
18a7bbea3d fix 2013-12-05 23:05:16 -05:00
7a14845790 add line kill and discard ability 2013-12-05 22:55:14 -05:00
2ad238d69f cchange prompt to $ char and make timestamps blue 2013-12-05 03:29:39 -05:00
70e8bdb409 fix line scrolling bug 2013-12-05 01:22:02 -05:00
3740cb6763 fix prompt bug, improve comments, and give prompt its own window struct 2013-12-04 22:33:04 -05:00
a57f94306a put back prompt char 2013-12-04 18:09:51 -05:00
e3400e095b a few fixes and update version 2013-12-04 16:21:32 -05:00
1517cbb6cb allow line editing in prompt window 2013-12-04 16:14:33 -05:00
424ab7cd10 fix prompt user input mechanics 2013-12-04 03:57:03 -05:00
a2af0bc047 don't sort after every friend add on statup 2013-12-04 01:08:26 -05:00
2fde13530b fix bug 2013-12-04 00:44:37 -05:00
db10c66922 API change 2013-12-03 18:06:28 -05:00
88ebb06ba5 colour theme changes 2013-12-03 18:01:17 -05:00
03ef257fdc small fix 2013-12-03 05:18:49 -05:00
1a86327f9f allow groupchat sidebar to scroll 2013-12-03 03:44:02 -05:00
ba750753a5 fix for possible divsion by zero 2013-12-02 18:34:14 -05:00
7a89229375 allow friendlist to scroll up and down if friends overflow the window 2013-12-02 18:23:04 -05:00
81c48f7b2b Merge pull request #60 from aitjcize/ipv4
Add option to switch off ipv6.
2013-12-02 14:37:42 -08:00
b0c60238f7 Add option to switch off ipv6. 2013-12-02 22:02:34 +08:00
ca1644c23b fix 2013-12-01 17:08:57 -05:00
f67cc479ec format consistency 2013-12-01 16:59:46 -05:00
b1b2cc44df minor improvements 2013-12-01 16:57:05 -05:00
192a06c4f0 fixed arrow printing bug 2013-12-01 13:25:03 -05:00
49655e13a0 fix 2013-12-01 03:29:12 -05:00
5850e1c333 line edit support for home, del and end keys 2013-12-01 02:58:21 -05:00
37dd2bee2d Merge branch 'master' of https://github.com/Tox/toxic 2013-11-30 22:15:16 -05:00
bb6b28b7c3 allow line editing via arrow keys in chat/groupchat windows 2013-11-30 22:12:43 -05:00
8f1da153a7 Merge pull request #59 from FullName/path-idx-real-fix
Fix partial fix: A slash in pos 0 still led to read access to pathname[-1].
2013-11-30 16:26:30 -08:00
4f4a379a01 Fix partial fix: A slash in pos 0 still led to read access to pathname[-1]. 2013-11-30 22:14:09 +01:00
c25296e65a fixes 2013-11-30 16:09:45 -05:00
f6a6aecaf5 fix window related bugs and warn user when max window limit has been reached 2013-11-30 05:35:25 -05:00
8ff907d719 wrong signal 2013-11-29 19:31:47 -05:00
a02bbfa643 kill file transfer if writing fails 2013-11-29 19:26:59 -05:00
57c2872b75 try to limit scope of globals 2013-11-29 18:52:21 -05:00
7ad520f128 reduce syntactic complexity - these don't need to be void 2013-11-29 17:48:08 -05:00
e4e7ed7e4e Merge pull request #58 from aitjcize/api-name
Fix corresponding API name changes in toxcore.
2013-11-29 08:26:53 -08:00
7ee84ce5e1 Fix typo. 2013-11-29 23:56:42 +08:00
8c0ad1ef80 Fix corresponding API name changes in toxcore. 2013-11-29 23:14:59 +08:00
4babd53be6 Merge pull request #57 from aitjcize/ret-code-inconsistency
Fix API ret code changes of ToxCore
2013-11-29 07:05:48 -08:00
e29ce6ab6f Fix API ret code changes of ToxCore 2013-11-29 18:30:40 +08:00
b909ab37b7 add peer count to sidepanel 2013-11-29 01:30:10 -05:00
04b394d6dd close file transfers on exit & fixes 2013-11-28 22:28:40 -05:00
8cf3043dd3 Merge branch 'master' of https://github.com/Tox/toxic 2013-11-28 20:24:58 -05:00
8584667ec9 don't need that 2013-11-28 20:24:44 -05:00
d29e5dbe48 fixes 2013-11-28 20:23:37 -05:00
e21e5c18ff Fix possible crash. 2013-11-28 19:52:30 -05:00
a98ec22fd6 improve window alert functionality 2013-11-28 19:45:28 -05:00
2057e7bc4f couple fixes 2013-11-28 18:56:56 -05:00
30 changed files with 2636 additions and 689 deletions

View File

@ -1,10 +1,10 @@
## Toxic - console client for [Tox](http://tox.im) ## Toxic
The client formerly resided in the [Tox core repository](https://github.com/irungentoo/ProjectTox-Core) and is now available as a standalone version. Toxic is an ncurses based instant messaging client for [Tox](http://tox.im) which 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://i.imgur.com/hL7WhVl.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 may need to install the ncurses libraries. For Debian based systems:
```
sudo apt-get install libncurses5-dev libncursesw5-dev
```

View File

@ -23,7 +23,11 @@ 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 \
$(top_srcdir)/src/log.c \
$(top_srcdir)/src/log.h
toxic_CFLAGS = -I$(top_srcdir) \ toxic_CFLAGS = -I$(top_srcdir) \
$(NCURSES_CFLAGS) \ $(NCURSES_CFLAGS) \

View File

@ -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.7], [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])

View File

@ -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

View File

@ -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})

View File

@ -1,5 +1,23 @@
/* /* chat.c
* Toxic -- Tox Curses Client *
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/ */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
@ -13,26 +31,82 @@
#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"
#include "log.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 18
/* 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" },
{ "/log" },
{ "/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;
}
void kill_chat_window(ToxWindow *self)
{
set_active_window(0);
ChatContext *ctx = self->chatwin;
StatusBar *statusbar = self->stb;
log_disable(ctx->log);
int f_num = self->num;
delwin(ctx->linewin);
delwin(statusbar->topline);
del_window(self);
disable_chatwin(f_num);
free(ctx->log);
free(ctx);
free(statusbar);
}
static void chat_onMessage(ToxWindow *self, Tox *m, int num, uint8_t *msg, uint16_t len) static void chat_onMessage(ToxWindow *self, Tox *m, 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 +115,8 @@ 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); add_to_log_buf(msg, nick, ctx->log, false);
alert_window(self, WINDOW_ALERT_1, true);
} }
static void chat_onConnectionChange(ToxWindow *self, Tox *m, int num, uint8_t status) static void chat_onConnectionChange(ToxWindow *self, Tox *m, int num, uint8_t status)
@ -49,8 +124,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 +148,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 +159,11 @@ 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); add_to_log_buf(action, nick, ctx->log, true);
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 +178,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 +187,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 +198,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 +234,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 +243,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 +256,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 +281,16 @@ 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
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 +298,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 +309,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,121 +329,244 @@ 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));
} else {
add_to_log_buf(action, selfname, ctx->log, true);
} }
} }
static void chat_onKey(ToxWindow *self, Tox *m, wint_t key) static void chat_onKey(ToxWindow *self, Tox *m, wint_t key)
{ {
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;
/* 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 || key == T_KEY_C_A) { /* HOME/C-a key: Move cursor to start of line */
if (ctx->pos > 0) {
ctx->pos = 0;
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
}
}
else if (key == KEY_END || key == T_KEY_C_E) { /* END/C-e key: move cursor to end of line */
if (ctx->pos != ctx->len) {
ctx->pos = ctx->len;
mv_curs_end(self->window, MAX(0, wcswidth(ctx->line, (CHATBOX_HEIGHT-1)*x2)), y2, x2);
}
}
else if (key == KEY_LEFT) {
if (ctx->pos > 0) {
--ctx->pos;
cur_len = MAX(1, wcwidth(ctx->line[ctx->pos]));
if (x == 0)
wmove(self->window, y-1, x2 - cur_len);
else
wmove(self->window, y, x - cur_len);
} else {
beep();
}
}
else if (key == KEY_RIGHT) {
if (ctx->pos < ctx->len) {
cur_len = MAX(1, wcwidth(ctx->line[ctx->pos]));
++ctx->pos;
if (x == x2-1)
wmove(self->window, y+1, 0);
else
wmove(self->window, y, x + cur_len);
} else {
beep();
}
}
else if (key == KEY_UP) { /* fetches previous item in history */
fetch_hist_item(ctx->line, &ctx->pos, &ctx->len, ctx->ln_history, ctx->hst_tot,
&ctx->hst_pos, LN_HIST_MV_UP);
mv_curs_end(self->window, ctx->len, y2, x2);
}
else if (key == KEY_DOWN) { /* fetches next item in history */
fetch_hist_item(ctx->line, &ctx->pos, &ctx->len, ctx->ln_history, ctx->hst_tot,
&ctx->hst_pos, LN_HIST_MV_DWN);
mv_curs_end(self->window, ctx->len, y2, x2);
}
else if (key == '\t') { /* TAB key: completes command */
if (ctx->len > 1 && ctx->line[0] == '/') {
int diff = complete_line(ctx->line, &ctx->pos, &ctx->len, chat_cmd_list, AC_NUM_CHAT_COMMANDS,
MAX_CMDNAME_SIZE);
if (diff != -1) {
if (x + diff > x2 - 1) {
int ofst = (x + diff - 1) - (x2 - 1);
wmove(self->window, y+1, ofst);
} else {
wmove(self->window, y, x+diff);
}
} else {
beep();
}
} else {
beep();
}
}
else
#if HAVE_WIDECHAR #if 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 (strcmp(line, "/close") == 0) {
int f_num = self->num; if (ctx->self_is_typing)
delwin(ctx->linewin); set_typingstatus(self, m, false);
delwin(statusbar->topline);
del_window(self); kill_chat_window(self);
disable_chatwin(f_num); return;
} 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 { }
/* make sure the string has at least non-space character */ } else if (!string_is_empty(line)) {
if (!string_is_empty(line)) { uint8_t selfname[TOX_MAX_NAME_LENGTH];
uint8_t selfname[TOX_MAX_NAME_LENGTH]; tox_get_self_name(m, 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 } else {
wprintw(ctx->history, "%s\n", line); add_to_log_buf(line, selfname, ctx->log, false);
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));
}
} }
} }
if (close_win) { reset_buf(ctx->line, &ctx->pos, &ctx->len);
free(ctx);
free(statusbar);
} else {
ctx->line[0] = L'\0';
ctx->pos = 0;
}
free(line);
} }
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 +591,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 +612,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 +634,60 @@ 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);
ctx->log = malloc(sizeof(struct chatlog));
if (ctx->log == NULL) {
endwin();
fprintf(stderr, "malloc() failed. Aborting...\n");
exit(EXIT_FAILURE);
}
memset(ctx->log, 0, sizeof(struct chatlog));
if (friends[self->num].logging_on)
log_enable(self->name, friends[self->num].pub_key, ctx->log);
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); execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE);
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
} }
ToxWindow new_chat(Tox *m, int friendnum) ToxWindow new_chat(Tox *m, int friendnum)
@ -462,11 +695,15 @@ 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.is_chat = 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 +714,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';

View File

@ -1,8 +1,31 @@
/* chat.h
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef CHAT_H_6489PZ13 #ifndef CHAT_H_6489PZ13
#define CHAT_H_6489PZ13 #define CHAT_H_6489PZ13
#include "toxic_windows.h" #include "toxic_windows.h"
void kill_chat_window(ToxWindow *self);
ToxWindow new_chat(Tox *m, int friendnum); ToxWindow new_chat(Tox *m, int friendnum);
#endif /* end of include guard: CHAT_H_6489PZ13 */ #endif /* end of include guard: CHAT_H_6489PZ13 */

View File

@ -1,5 +1,23 @@
/* /* chat_commands.c
* Toxic -- Tox Curses Client *
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/ */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
@ -11,35 +29,41 @@
#include "toxic_windows.h" #include "toxic_windows.h"
#include "misc_tools.h" #include "misc_tools.h"
#include "friendlist.h"
#include "execute.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])
{ {
if (argc == 1) {
if (!strcmp(argv[1], "global")) {
execute(window, self, m, "/help", GLOBAL_COMMAND_MODE);
return;
}
}
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, " /invite <n> : Invite friend to a group chat\n");
wprintw(window, " /note <msg> : Set a personal note\n"); wprintw(window, " /join : Join a pending group chat\n");
wprintw(window, " /nick <nick> : Set your nickname\n"); wprintw(window, " /log <on> or <off> : Enable/disable logging\n");
wprintw(window, " /invite <n> : Invite friend to a group chat\n"); wprintw(window, " /sendfile <filepath> : Send a file\n");
wprintw(window, " /me <action> : Do an action\n"); wprintw(window, " /savefile <n> : Receive a file\n");
wprintw(window, " /myid : Print your ID\n"); wprintw(window, " /close : Close the current chat window\n");
wprintw(window, " /join : Join a pending group chat\n"); wprintw(window, " /help : Print this message again\n");
wprintw(window, " /clear : Clear the screen\n"); wprintw(window, " /help global : Show a list of global commands\n");
wprintw(window, " /close : Close the current chat window\n");
wprintw(window, " /sendfile <filepath> : Send a file\n");
wprintw(window, " /savefile <n> : Receive a file\n");
wprintw(window, " /quit or /exit : Exit Toxic\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 +90,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 +139,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 +149,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 +185,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 +204,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;
} }

View File

@ -1,5 +1,23 @@
/* /* chat_commands.h
* Toxic -- Tox Curses Client *
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/ */
void cmd_chat_help(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_chat_help(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);

View File

@ -1,20 +1,22 @@
/* /* configdir.c
* Copyright (C) 2013 Tox project All Rights Reserved.
* *
* This file is part of Tox.
* *
* Tox is free software: you can redistribute it and/or modify * Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* Tox is distributed in the hope that it will be useful, * Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Tox. If not, see <http://www.gnu.org/licenses/>. * along with Toxic. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */

View File

@ -1,20 +1,22 @@
/* /* configdir.h
* Copyright (C) 2013 Tox project All Rights Reserved.
* *
* This file is part of Tox.
* *
* Tox is free software: you can redistribute it and/or modify * Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* Tox is distributed in the hope that it will be useful, * Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Tox. If not, see <http://www.gnu.org/licenses/>. * along with Toxic. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */

View File

@ -1,5 +1,23 @@
/* /* execute.c
* Toxic -- Tox Curses Client *
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/ */
#include <stdlib.h> #include <stdlib.h>
@ -15,8 +33,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 },
@ -25,6 +41,7 @@ static struct cmd_func global_commands[] = {
{ "/exit", cmd_quit }, { "/exit", cmd_quit },
{ "/groupchat", cmd_groupchat }, { "/groupchat", cmd_groupchat },
{ "/help", cmd_prompt_help }, { "/help", cmd_prompt_help },
{ "/log", cmd_log },
{ "/myid", cmd_myid }, { "/myid", cmd_myid },
{ "/nick", cmd_nick }, { "/nick", cmd_nick },
{ "/note", cmd_note }, { "/note", cmd_note },
@ -33,8 +50,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 },

View File

@ -1,8 +1,28 @@
/* /* execute.h
* Toxic -- Tox Curses Client *
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/ */
#define MAX_NUM_ARGS 4 /* Includes command */ #define MAX_NUM_ARGS 4 /* Includes command */
#define GLOBAL_NUM_COMMANDS 14
#define CHAT_NUM_COMMANDS 5
enum { enum {
GLOBAL_COMMAND_MODE, GLOBAL_COMMAND_MODE,

View File

@ -1,5 +1,23 @@
/* /* friendlist.c
* Toxic -- Tox Curses Client *
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/ */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
@ -20,25 +38,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 +73,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 +126,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 +147,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 +158,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 +174,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 +232,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 +243,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 +264,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 +366,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 +382,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 +399,7 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
} }
} }
self->x = x; self->x = x2;
wrefresh(self->window); wrefresh(self->window);
} }
@ -323,6 +418,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;

View File

@ -1,13 +1,52 @@
/* friendlist.h
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef FRIENDLIST_H_53I41IM #ifndef FRIENDLIST_H_53I41IM
#define FRIENDLIST_H_53I41IM #define FRIENDLIST_H_53I41IM
#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;
bool logging_on; /* saves preference for friend irrespective of chat windows */
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 */

View File

@ -1,5 +1,23 @@
/* /* global_commands.c
* Toxic -- Tox Curses Client *
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/ */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
@ -11,13 +29,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 +60,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 +103,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 +134,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 +156,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 +197,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;
} }
@ -198,11 +220,78 @@ void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*arg
wprintw(window, "Group chat created as %d.\n", groupnum); wprintw(window, "Group chat created as %d.\n", groupnum);
} }
void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc == 0) {
bool on;
if (self->is_chat || self->is_groupchat)
on = self->chatwin->log->log_on;
else if (self->is_prompt)
on = self->promptbuf->log->log_on;
if (on) {
wprintw(window, "Logging for this window is ");
wattron(window, COLOR_PAIR(GREEN) | A_BOLD);
wprintw(window, "[on]");
wattroff(window, COLOR_PAIR(GREEN) | A_BOLD);
wprintw(window, ". Type \"/log off\" to disable.\n");
} else {
wprintw(window, "Logging for this window is ");
wattron(window, COLOR_PAIR(RED) | A_BOLD);
wprintw(window, "[off]");
wattroff(window, COLOR_PAIR(RED) | A_BOLD);
wprintw(window, ". Type \"/log on\" to enable.\n");
}
return;
}
uint8_t *swch = argv[1];
if (!strcmp(swch, "1") || !strcmp(swch, "on")) {
if (self->is_chat) {
friends[self->num].logging_on = true;
log_enable(self->name, friends[self->num].pub_key, self->chatwin->log);
} else if (self->is_prompt) {
uint8_t myid[TOX_FRIEND_ADDRESS_SIZE];
tox_get_address(m, myid);
log_enable(self->name, &myid, self->promptbuf->log);
} else if (self->is_groupchat) {
log_enable(self->name, NULL, self->chatwin->log);
}
wprintw(window, "Logging ");
wattron(window, COLOR_PAIR(GREEN) | A_BOLD);
wprintw(window, "[on]\n");
wattroff(window, COLOR_PAIR(GREEN) | A_BOLD);
return;
} else if (!strcmp(swch, "0") || !strcmp(swch, "off")) {
if (self->is_chat) {
friends[self->num].logging_on = false;
log_disable(self->chatwin->log);
} else if (self->is_prompt) {
log_disable(self->promptbuf->log);
} else if (self->is_groupchat) {
log_disable(self->chatwin->log);
}
wprintw(window, "Logging ");
wattron(window, COLOR_PAIR(RED) | A_BOLD);
wprintw(window, "[off]\n");
wattroff(window, COLOR_PAIR(RED) | A_BOLD);
return;
}
wprintw(window, "Invalid option. Use \"/log on\" and \"/log off\" to toggle logging.\n");
}
void cmd_myid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_myid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{ {
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 +331,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 +353,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,26 +363,25 @@ 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");
wprintw(window, " /connect <ip> <port> <key> : Manually connect to a DHT server\n"); wprintw(window, " /connect <ip> <port> <key> : Manually connect to a DHT server\n");
wprintw(window, " /status <type> <msg> : Set your status with optional note\n"); wprintw(window, " /status <type> <msg> : Set status with optional note\n");
wprintw(window, " /note <msg> : Set a personal note\n"); wprintw(window, " /note <msg> : Set a personal note\n");
wprintw(window, " /nick <nick> : Set your nickname\n"); wprintw(window, " /nick <nick> : Set your nickname\n");
wprintw(window, " /groupchat : Create a group chat\n"); wprintw(window, " /log <on> or <off> : Enable/disable logging\n");
wprintw(window, " /myid : Print your ID\n"); wprintw(window, " /groupchat : Create a group chat\n");
wprintw(window, " /quit or /exit : Exit Toxic\n"); wprintw(window, " /myid : Print your ID\n");
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");
wprintw(window, " /quit or /exit : Exit Toxic\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 +426,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);
} }
} }

View File

@ -1,5 +1,23 @@
/* /* global_commands.h
* Toxic -- Tox Curses Client *
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/ */
void cmd_accept(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_accept(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
@ -7,6 +25,7 @@ void cmd_add(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE])
void cmd_clear(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_clear(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_connect(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_connect(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_groupchat(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_groupchat(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_log(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_myid(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_myid(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_nick(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_nick(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_note(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_note(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);

View File

@ -1,5 +1,23 @@
/* /* groupchat.c
* Toxic -- Tox Curses Client *
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/ */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
@ -14,16 +32,19 @@
#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; #include "log.h"
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 +65,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;
} }
} }
@ -53,8 +72,20 @@ int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum)
return -1; return -1;
} }
static void close_groupchatwin(Tox *m, int groupnum) void kill_groupchat_window(ToxWindow *self)
{ {
ChatContext *ctx = self->chatwin;
log_disable(ctx->log);
delwin(ctx->linewin);
del_window(self);
free(ctx->log);
free(ctx);
}
static void close_groupchat(ToxWindow *self, Tox *m, int groupnum)
{
set_active_window(0);
tox_del_groupchat(m, groupnum); tox_del_groupchat(m, groupnum);
free(groupchats[groupnum].peer_names); free(groupchats[groupnum].peer_names);
@ -68,32 +99,30 @@ static void close_groupchatwin(Tox *m, int groupnum)
break; break;
} }
--num_groupchats;
max_groupchat_index = i; max_groupchat_index = i;
kill_groupchat_window(self);
} }
static void print_groupchat_help(ChatContext *ctx) 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, " /log <on> or <off> : Enable/disable logging\n");
wprintw(ctx->history, " /clear : Clear the screen\n"); wprintw(ctx->history, " /close : Close the current group chat\n");
wprintw(ctx->history, " /close : Close the current group chat\n"); wprintw(ctx->history, " /help : Print this message again\n");
wprintw(ctx->history, " /quit or /exit : Exit Toxic\n"); wprintw(ctx->history, " /help global : Show a list of global commands\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 +131,80 @@ 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);
}
add_to_log_buf(msg, nick, ctx->log, false);
}
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; add_to_log_buf(action, nick, ctx->log, true);
} }
/* Puts two copies of peerlist in chat instance */ /* Puts two copies of peerlist in chat instance */
@ -144,10 +228,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,35 +257,48 @@ 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);
uint8_t *event;
switch (change) { switch (change) {
case TOX_CHAT_CHANGE_PEER_ADD: case TOX_CHAT_CHANGE_PEER_ADD:
event = "has joined the room";
wattron(ctx->history, COLOR_PAIR(GREEN)); wattron(ctx->history, COLOR_PAIR(GREEN));
wattron(ctx->history, A_BOLD); wattron(ctx->history, A_BOLD);
wprintw(ctx->history, "* %s", peername); wprintw(ctx->history, "* %s", peername);
wattroff(ctx->history, A_BOLD); wattroff(ctx->history, A_BOLD);
wprintw(ctx->history, " has joined the room\n"); wprintw(ctx->history, " %s\n", event);
wattroff(ctx->history, COLOR_PAIR(GREEN)); wattroff(ctx->history, COLOR_PAIR(GREEN));
add_to_log_buf(event, peername, ctx->log, true);
break; break;
case TOX_CHAT_CHANGE_PEER_DEL: case TOX_CHAT_CHANGE_PEER_DEL:
wattron(ctx->history, COLOR_PAIR(RED)); event = "has left the room";
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, " %s\n", event);
wattroff(ctx->history, COLOR_PAIR(RED));
if (groupchats[self->num].side_pos > 0)
--groupchats[self->num].side_pos;
add_to_log_buf(event, oldpeername, ctx->log, true);
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));
wattron(ctx->history, A_BOLD); wattron(ctx->history, A_BOLD);
@ -212,116 +311,285 @@ static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnu
wprintw(ctx->history, "%s\n", peername); wprintw(ctx->history, "%s\n", peername);
wattroff(ctx->history, A_BOLD); wattroff(ctx->history, A_BOLD);
wattroff(ctx->history, COLOR_PAIR(MAGENTA)); wattroff(ctx->history, COLOR_PAIR(MAGENTA));
uint8_t tmp_event[TOXIC_MAX_NAME_LENGTH + 32];
snprintf(tmp_event, sizeof(tmp_event), "is now known as %s", peername);
add_to_log_buf(tmp_event, oldpeername, ctx->log, true);
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 || key == T_KEY_C_A) { /* HOME/C-a key: Move cursor to start of line */
if (ctx->pos > 0) {
ctx->pos = 0;
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
}
}
else if (key == KEY_END || key == T_KEY_C_E) { /* END/C-e key: move cursor to end of line */
if (ctx->pos != ctx->len) {
ctx->pos = ctx->len;
mv_curs_end(self->window, MAX(0, wcswidth(ctx->line, (CHATBOX_HEIGHT-1)*x2)), y2, x2);
}
}
else if (key == KEY_LEFT) {
if (ctx->pos > 0) {
--ctx->pos;
cur_len = MAX(1, wcwidth(ctx->line[ctx->pos]));
if (x == 0)
wmove(self->window, y-1, x2 - cur_len);
else
wmove(self->window, y, x - cur_len);
} else {
beep();
}
}
else if (key == KEY_RIGHT) {
if (ctx->pos < ctx->len) {
cur_len = MAX(1, wcwidth(ctx->line[ctx->pos]));
++ctx->pos;
if (x == x2-1)
wmove(self->window, y+1, 0);
else
wmove(self->window, y, x + cur_len);
} else {
beep();
}
}
else if (key == KEY_UP) { /* fetches previous item in history */
fetch_hist_item(ctx->line, &ctx->pos, &ctx->len, ctx->ln_history, ctx->hst_tot,
&ctx->hst_pos, LN_HIST_MV_UP);
mv_curs_end(self->window, ctx->len, y2, x2);
}
else if (key == KEY_DOWN) { /* fetches next item in history */
fetch_hist_item(ctx->line, &ctx->pos, &ctx->len, ctx->ln_history, ctx->hst_tot,
&ctx->hst_pos, LN_HIST_MV_DWN);
mv_curs_end(self->window, ctx->len, y2, x2);
}
else if (key == '\t') { /* TAB key: completes peer name */
if (ctx->len > 0) {
int diff;
if ((ctx->line[0] != '/') || (ctx->line[1] == 'm' && ctx->line[2] == 'e'))
diff = complete_line(ctx->line, &ctx->pos, &ctx->len, groupchats[self->num].peer_names,
groupchats[self->num].num_peers, TOX_MAX_NAME_LENGTH);
else
diff = complete_line(ctx->line, &ctx->pos, &ctx->len, glob_cmd_list, AC_NUM_GLOB_COMMANDS,
MAX_CMDNAME_SIZE);
if (diff != -1) {
if (x + diff > x2 - 1) {
int ofst = (x + diff - 1) - (x2 - 1);
wmove(self->window, y+1, ofst);
} else {
wmove(self->window, y, x+diff);
}
} else {
beep();
}
} else {
beep();
}
}
/* Scroll peerlist up and down one position if list overflows window */
else if (key == KEY_NPAGE) {
int L = y2 - CHATBOX_HEIGHT - SDBAR_OFST;
if (groupchats[self->num].side_pos < groupchats[self->num].num_peers - L)
++groupchats[self->num].side_pos;
}
else if (key == KEY_PPAGE) {
if (groupchats[self->num].side_pos > 0)
--groupchats[self->num].side_pos;
}
else
#if HAVE_WIDECHAR #if 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;
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 (strcmp(line, "/close") == 0) {
set_active_window(0); close_groupchat(self, m, self->num);
int groupnum = self->num; return;
delwin(ctx->linewin); } else if (strcmp(line, "/help") == 0) {
del_window(self); if (strcmp(line, "help global") == 0)
close_groupchatwin(m, groupnum); execute(ctx->history, self, m, "/help", GLOBAL_COMMAND_MODE);
} else if (strncmp(line, "/help", strlen("/help")) == 0) else
print_groupchat_help(ctx); print_groupchat_help(ctx);
else
} else if (strncmp(line, "/me ", strlen("/me ")) == 0) {
send_group_action(self, ctx, m, line + strlen("/me "));
} else {
execute(ctx->history, self, m, line, GROUPCHAT_COMMAND_MODE); execute(ctx->history, self, m, line, GROUPCHAT_COMMAND_MODE);
} else { }
/* make sure the string has at least non-space character */ } else if (!string_is_empty(line)) {
if (!string_is_empty(line)) { if (tox_group_message_send(m, self->num, line, strlen(line) + 1) == -1) {
// uint8_t selfname[TOX_MAX_NAME_LENGTH]; wattron(ctx->history, COLOR_PAIR(RED));
// tox_getselfname(m, selfname, TOX_MAX_NAME_LENGTH); wprintw(ctx->history, " * Failed to send message.\n");
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) reset_buf(ctx->line, &ctx->pos, &ctx->len);
free(ctx);
else {
ctx->line[0] = L'\0';
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,13 +597,25 @@ 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);
ctx->log = malloc(sizeof(struct chatlog));
if (ctx->log == NULL) {
endwin();
fprintf(stderr, "malloc() failed. Aborting...\n");
exit(EXIT_FAILURE);
}
memset(ctx->log, 0, sizeof(struct chatlog));
print_groupchat_help(ctx); print_groupchat_help(ctx);
execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE);
wmove(self->window, y-CURS_Y_OFFSET, 0); wmove(self->window, y-CURS_Y_OFFSET, 0);
} }
@ -344,12 +624,15 @@ 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.is_groupchat = 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);

View File

@ -1,15 +1,37 @@
/* /* groupchat.h
* Toxic -- Tox Curses Client *
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/ */
#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;
void kill_groupchat_window(ToxWindow *self);
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);

138
src/log.c Normal file
View File

@ -0,0 +1,138 @@
/* log.c
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "configdir.h"
#include "toxic_windows.h"
#include "misc_tools.h"
/* gets the log path by appending to the config dir the name and a pseudo-unique identity */
void init_logging_session(uint8_t *name, uint8_t *key, struct chatlog *log)
{
if (!log->log_on)
return;
char *user_config_dir = get_user_config_dir();
int path_len = strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(name);
/* use first 4 digits of key as log ident. If no key use a timestamp */
uint8_t ident[32];
if (key != NULL) {
path_len += (KEY_IDENT_DIGITS * 2 + 5);
sprintf(&ident[0], "%02X", key[0] & 0xff);
sprintf(&ident[2], "%02X", key[2] & 0xff);
ident[KEY_IDENT_DIGITS*2+1] = '\0';
} else {
struct tm *tminfo = get_time();
snprintf(ident, sizeof(ident),
"%04d-%02d-%02d[%02d:%02d:%02d]", tminfo->tm_year+1900,tminfo->tm_mon+1, tminfo->tm_mday,
tminfo->tm_hour, tminfo->tm_min, tminfo->tm_sec);
path_len += strlen(ident) + 1;
}
if (path_len > MAX_STR_SIZE) {
log->log_on = false;
return;
}
snprintf(log->log_path, MAX_STR_SIZE, "%s%s%s-%s.log",
user_config_dir, CONFIGDIR, name, ident);
FILE *logfile = fopen(log->log_path, "a");
if (logfile == NULL) {
log->log_on = false;
return;
}
fprintf(logfile, "\n*** NEW SESSION ***\n\n");
fclose(logfile);
free(user_config_dir);
}
/* writes contents from a chatcontext's log buffer to respective log file and resets log pos.
This is triggered when the log buffer is full, but may be forced. */
void write_to_log(struct chatlog *log)
{
if (!log->log_on)
return;
FILE *logfile = fopen(log->log_path, "a");
if (logfile == NULL) {
log->log_on = false;
return;
}
int i;
for (i = 0; i < log->pos; ++i)
fprintf(logfile, "%s", log->log_buf[i]);
log->pos = 0;
fclose(logfile);
}
/* Adds line/event to log_buf with timestamp and name. If buf is full, triggers write_to_log.
If event is true, formats line as an event, e.g. * name has gone offline */
void add_to_log_buf(uint8_t *msg, uint8_t *name, struct chatlog *log, bool event)
{
if (!log->log_on)
return;
uint8_t name_frmt[TOXIC_MAX_NAME_LENGTH + 3];
if (event)
snprintf(name_frmt, sizeof(name_frmt), "* %s", name);
else
snprintf(name_frmt, sizeof(name_frmt), "%s:", name);
struct tm *tminfo = get_time();
snprintf(log->log_buf[log->pos], MAX_LOG_LINE_SIZE, "%04d/%02d/%02d [%02d:%02d:%02d] %s %s\n",
tminfo->tm_year + 1900, tminfo->tm_mon + 1, tminfo->tm_mday,
tminfo->tm_hour, tminfo->tm_min, tminfo->tm_sec, name_frmt, msg);
if (++(log->pos) >= MAX_LOG_BUF_LINES)
write_to_log(log);
}
void log_enable(uint8_t *name, uint8_t *key, struct chatlog *log)
{
log->log_on = true;
if (!log->log_path[0])
init_logging_session(name, key, log);
}
void log_disable(struct chatlog *log)
{
if (log->log_on) {
write_to_log(log);
log->log_on = false;
}
}

35
src/log.h Normal file
View File

@ -0,0 +1,35 @@
/* log.h
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
/* gets the log path by appending to the config dir the name and a pseudo-unique identity */
void init_logging_session(uint8_t *name, uint8_t *key, struct chatlog *log);
/* Adds line/event to log_buf with timestamp and name. If buf is full, triggers write_to_log.
If event is true, formats line as an event, e.g. * name has gone offline */
void add_to_log_buf(uint8_t *msg, uint8_t *name, struct chatlog *log, bool event);
/* writes contents from a chatcontext's log buffer to respective log file and resets log pos.
This is triggered automatically when the log buffer is full, but may be forced. */
void write_to_log(struct chatlog *log);
void log_enable(uint8_t *name, uint8_t *key, struct chatlog *log);
void log_disable(struct chatlog *log);

View File

@ -1,5 +1,23 @@
/* /* main.c
* Toxic -- Tox Curses Client *
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/ */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
@ -47,8 +65,14 @@
/* Export for use in Callbacks */ /* Export for use in Callbacks */
char *DATA_FILE = NULL; char *DATA_FILE = NULL;
char *SRVLIST_FILE = NULL; char *SRVLIST_FILE = NULL;
ToxWindow *prompt = NULL; ToxWindow *prompt = NULL;
static int f_loadfromfile; /* 1 if we want to load from/save the data file, 0 otherwise */
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 +113,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;
@ -135,11 +167,14 @@ static char servers[MAXSERVERS][SERVERLEN];
static uint16_t ports[MAXSERVERS]; static uint16_t ports[MAXSERVERS];
static uint8_t keys[MAXSERVERS][TOX_CLIENT_ID_SIZE]; static uint8_t keys[MAXSERVERS][TOX_CLIENT_ID_SIZE];
int serverlist_load(void) static int serverlist_load(const char *filename)
{ {
FILE *fp = NULL; FILE *fp = NULL;
fp = fopen(SRVLIST_FILE, "r"); if (!filename)
return 1;
fp = fopen(filename, "r");
if (fp == NULL) if (fp == NULL)
return 1; return 1;
@ -175,10 +210,10 @@ int serverlist_load(void)
return 0; return 0;
} }
int init_connection_helper(Tox *m, int linenumber) int init_connection_helper(Tox *m, int line)
{ {
return tox_bootstrap_from_address(m, servers[linenumber], TOX_ENABLE_IPV6_DEFAULT, return tox_bootstrap_from_address(m, servers[line], TOX_ENABLE_IPV6_DEFAULT,
ports[linenumber], keys[linenumber]); ports[line], keys[line]);
} }
/* Connects to a random DHT server listed in the DHTservers file /* Connects to a random DHT server listed in the DHTservers file
@ -186,69 +221,83 @@ int init_connection_helper(Tox *m, int linenumber)
* return codes: * return codes:
* 1: failed to open server file * 1: failed to open server file
* 2: no line of sufficient length in server file * 2: no line of sufficient length in server file
* 3: (old, removed) failed to split a selected line in the server file * 3: failed to resolve name to IP
* 4: failed to resolve name to IP * 4: serverlist file contains no acceptable line
* 5: serverlist file contains no acceptable line
*/ */
static uint8_t init_connection_serverlist_loaded = 0; static bool srvlist_loaded = false;
#define NUM_INIT_NODES 5
int init_connection(Tox *m) int init_connection(Tox *m)
{ {
if (linecnt > 0) /* already loaded serverlist */ if (linecnt > 0) /* already loaded serverlist */
return init_connection_helper(m, rand() % linecnt) ? 0 : 4; return init_connection_helper(m, rand() % linecnt) ? 0 : 3;
/* only once: /* only once:
* - load the serverlist * - load the serverlist
* - connect to "everyone" inside * - connect to "everyone" inside
*/ */
if (!init_connection_serverlist_loaded) { if (!srvlist_loaded) {
init_connection_serverlist_loaded = 1; srvlist_loaded = true;
int res = serverlist_load(); int res = serverlist_load(SRVLIST_FILE);
if (res)
return res; if (res) {
/* Fallback on the provided DHTServers in /usr/share or /usr/local/share,
so new starts of toxic will connect to the DHT. */
res = serverlist_load(PACKAGE_DATADIR "/DHTservers");
if (res)
return res;
}
if (!linecnt) if (!linecnt)
return 4; return 2;
res = 6; res = 3;
int linenumber; int i;
for(linenumber = 0; linenumber < linecnt; linenumber++) int n = MIN(NUM_INIT_NODES, linecnt);
if (init_connection_helper(m, linenumber))
for(i = 0; i < n; ++i)
if (init_connection_helper(m, rand() % linecnt))
res = 0; res = 0;
return res; return res;
} }
/* empty serverlist file */ /* empty serverlist file */
return 5; return 4;
} }
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;
/* /*
* Store Messenger to given location * Store Messenger to given location
* Return 0 stored successfully * Return 0 stored successfully
@ -329,12 +378,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 +396,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 +427,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 +467,36 @@ 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);
}
kill_all_windows();
free(DATA_FILE); free(DATA_FILE);
free(SRVLIST_FILE); free(SRVLIST_FILE);
free(prompt->stb); free(prompt->stb);
log_disable(prompt->promptbuf->log);
free(prompt->promptbuf->log);
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 +505,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 +521,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;
} }
} }
} }
@ -472,25 +545,21 @@ int main(int argc, char *argv[])
} }
} }
if (config_err) { SRVLIST_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen("DHTservers") + 1);
SRVLIST_FILE = strdup(PACKAGE_DATADIR "/DHTservers"); if (SRVLIST_FILE != NULL) {
strcpy(SRVLIST_FILE, user_config_dir);
strcat(SRVLIST_FILE, CONFIGDIR);
strcat(SRVLIST_FILE, "DHTservers");
} else { } else {
SRVLIST_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen("DHTservers") + 1); endwin();
if (SRVLIST_FILE != NULL) { fprintf(stderr, "malloc() failed. Aborting...\n");
strcpy(SRVLIST_FILE, user_config_dir); exit(EXIT_FAILURE);
strcat(SRVLIST_FILE, CONFIGDIR);
strcat(SRVLIST_FILE, "DHTservers");
} else {
endwin();
fprintf(stderr, "malloc() failed. Aborting...\n");
exit(EXIT_FAILURE);
}
} }
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 +587,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;
} }

View File

@ -1,5 +1,23 @@
/* /* misc_tools.c
* Toxic -- Tox Curses Client *
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/ */
#include <stdlib.h> #include <stdlib.h>
@ -9,6 +27,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,19 +65,48 @@ 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 true if the string is empty, false otherwise */
int string_is_empty(char *string) bool 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 +114,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 +136,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 +158,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 +202,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);
}

View File

@ -1,8 +1,27 @@
/* /* misc_tools.h
* Toxic -- Tox Curses Client *
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/ */
#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[]);
@ -13,26 +32,39 @@ struct tm *get_time(void);
/* Prints the time to given window */ /* Prints the time to given window */
void print_time(WINDOW *window); void print_time(WINDOW *window);
/* Returns 1 if the string is empty, 0 otherwise */ /* Returns true if the string is empty, false otherwise */
int string_is_empty(char *string); bool 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);

View File

@ -1,5 +1,23 @@
/* /* prompt.c
* Toxic -- Tox Curses Client *
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/ */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
@ -13,17 +31,54 @@
#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" },
{ "/log" },
{ "/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 +86,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 +94,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 +109,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 +130,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 || key == T_KEY_C_A) { /* HOME/C-a key: Move cursor to start of line */
if (prt->pos != 0)
prt->pos = 0;
}
else if (key == KEY_END || key == T_KEY_C_E) { /* END/C-e key: move cursor to end of line */
if (prt->pos != prt->len)
prt->pos = prt->len;
}
else if (key == KEY_LEFT) {
if (prt->pos > 0)
--prt->pos;
else
beep();
}
else if (key == KEY_RIGHT) {
if (prt->pos < prt->len)
++prt->pos;
else
beep();
}
else if (key == KEY_UP) { /* fetches previous item in history */
wmove(self->window, prt->orig_y, X_OFST);
fetch_hist_item(prt->line, &prt->pos, &prt->len, prt->ln_history, prt->hst_tot,
&prt->hst_pos, LN_HIST_MV_UP);
/* adjust line y origin appropriately when window scrolls down */
if (prt->at_bottom && prt->len >= x2 - X_OFST) {
int px2 = prt->len >= x2 ? x2 : x2 - X_OFST;
int p_ofst = px2 != x2 ? 0 : X_OFST;
if (px2 <= 0)
return;
int k = prt->orig_y + ((prt->len + p_ofst) / px2);
if (k >= y2) {
wprintw(self->window, "\n");
--prt->orig_y;
}
}
}
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,20 +351,27 @@ 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)
{ {
scrollok(self->window, true); scrollok(self->window, true);
PromptBuf *prt = self->promptbuf;
prt->log = malloc(sizeof(struct chatlog));
if (prt->log == NULL) {
endwin();
fprintf(stderr, "malloc() failed. Aborting...\n");
exit(EXIT_FAILURE);
}
memset(prt->log, 0, sizeof(struct chatlog));
execute(self->window, self, m, "/help", GLOBAL_COMMAND_MODE); execute(self->window, self, m, "/help", GLOBAL_COMMAND_MODE);
wclrtoeol(self->window); wclrtoeol(self->window);
} }
@ -189,48 +381,72 @@ static void prompt_onConnectionChange(ToxWindow *self, Tox *m, int friendnum , u
if (friendnum < 0) if (friendnum < 0)
return; return;
PromptBuf *prt = self->promptbuf;
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);
uint8_t *msg;
if (status == 1) { if (status == 1) {
msg = "has come online\n";
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, "%s", msg);
wattroff(self->window, COLOR_PAIR(GREEN)); wattroff(self->window, COLOR_PAIR(GREEN));
add_to_log_buf(msg, nick, prt->log, true);
alert_window(self, WINDOW_ALERT_2, false);
} else { } else {
msg = "has gone offline\n";
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, "%s", msg);
wattroff(self->window, COLOR_PAIR(RED)); wattroff(self->window, COLOR_PAIR(RED));
}
add_to_log_buf(msg, nick, prt->log, true);
}
} }
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)
{ {
// make sure message data is null-terminated // make sure message data is null-terminated
data[length - 1] = 0; data[length - 1] = 0;
PromptBuf *prt = self->promptbuf;
prep_prompt_win();
wprintw(self->window, "\nFriend request with the message: %s\n", data); wprintw(self->window, "\n");
print_time(self->window);
uint8_t msg[MAX_STR_SIZE];
snprintf(msg, sizeof(msg), "Friend request with the message '%s'\n", data);
wprintw(self->window, "%s", msg);
add_to_log_buf(msg, "", prt->log, true);
int n = add_friend_request(key); int n = add_friend_request(key);
if (n == -1) { if (n == -1) {
wprintw(self->window, "Friend request queue is full. Discarding request.\n"); uint8_t *errmsg = "Friend request queue is full. Discarding request.\n";
wprintw(self->window, "%s", errmsg);
add_to_log_buf(errmsg, "", prt->log, true);
return; return;
} }
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 +455,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 +486,9 @@ ToxWindow new_prompt(void)
ToxWindow ret; ToxWindow ret;
memset(&ret, 0, sizeof(ret)); memset(&ret, 0, sizeof(ret));
ret.active = true;
ret.is_prompt = 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 +497,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);

View File

@ -1,11 +1,34 @@
/* /* prompt.h
* Toxic -- Tox Curses Client *
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/ */
#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 15
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);

247
src/toxic_strings.c Normal file
View File

@ -0,0 +1,247 @@
/* toxic_strings.c
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#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.
resets buffer if at end of history */
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 = 0;
beep();
}
} else {
if (++(*hst_pos) >= hst_tot) {
*hst_pos = hst_tot;
reset_buf(buf, pos, len);
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;
}

63
src/toxic_strings.h Normal file
View File

@ -0,0 +1,63 @@
/* toxic_strings.h
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
/* 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);

View File

@ -1,5 +1,23 @@
/* /* toxic_windows.h
* Toxic -- Tox Curses Client *
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/ */
#ifndef _windows_h #ifndef _windows_h
@ -19,17 +37,26 @@
#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 */
#define T_KEY_C_E 0x05 /* ctrl-e */
#define T_KEY_C_A 0x01 /* ctrl-a */
/* Curses foreground colours (background is black) */ /* Curses foreground colours (background is black) */
enum { enum {
WHITE, WHITE,
@ -42,43 +69,66 @@ 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; /* window type identifiers */
void *stb; bool is_chat;
bool is_groupchat;
bool is_prompt;
bool alert0;
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 +136,62 @@ 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_LOG_BUF_LINES 10 /* write log_buf contents to log file after this many lines */
#define MAX_LOG_LINE_SIZE MAX_STR_SIZE + TOXIC_MAX_NAME_LENGTH + 32 /* extra room for timestamp */
struct chatlog {
uint8_t log_path[MAX_STR_SIZE];
uint8_t log_buf[MAX_LOG_BUF_LINES][MAX_LOG_LINE_SIZE];
int pos;
bool log_on; /* specific to current chat window */
};
#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;
struct chatlog *log;
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;
struct chatlog *log;
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 +205,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 +219,23 @@ 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);
/* closes all chat and groupchat windows (should only be called on shutdown) */
void kill_all_windows(void);
#endif #endif

View File

@ -1,3 +1,25 @@
/* windows.c
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
#include "config.h" #include "config.h"
#endif #endif
@ -8,13 +30,14 @@
#include "friendlist.h" #include "friendlist.h"
#include "prompt.h" #include "prompt.h"
#include "toxic_windows.h" #include "toxic_windows.h"
#include "groupchat.h"
extern char *DATA_FILE; 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 +60,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 +99,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 +126,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 +150,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 +226,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 +265,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 +290,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 +306,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 +342,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 +351,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 +369,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 +392,34 @@ 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;
}
/* destroys all chat and groupchat windows (should only be called on shutdown) */
void kill_all_windows(void)
{
int i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].is_chat)
kill_chat_window(&windows[i]);
else if (windows[i].is_groupchat)
kill_groupchat_window(&windows[i]);
}
}