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

Compare commits

...

215 Commits

Author SHA1 Message Date
4d249c5fe3 Bumped to 0.4.0 2014-06-01 14:53:57 -07:00
7206a9ea73 fix another possible buffer overflow
..
2014-06-01 13:42:42 -04:00
848b4e9a4c fix possible buffer overflow 2014-06-01 12:54:45 -04:00
d65d0a08aa fix chat scroll bug 2014-06-01 03:45:46 -04:00
ab1c97fb2b properly set default user settings 2014-05-29 13:58:37 -04:00
b0a66706bd Update README.md
Update default example path for Tox libs
2014-05-29 15:04:14 +01:00
f0962bd060 add setting to control history size 2014-05-25 19:54:34 -04:00
083ca2f3b7 missing format arg 2014-05-25 13:31:44 -04:00
8481b50f97 Merge pull request #132 from Impyy/master
Update audio_call.c
2014-05-25 18:55:40 +02:00
a04f7ee661 Merge pull request #130 from mannol1/master
Updated toxic to build against new core.
2014-05-25 18:54:19 +02:00
10d47d1ac6 Update audio_call.c
Fixed 2 small typos.
2014-05-24 15:35:10 +02:00
445f5aa1fd fix possible buffer overflow 2014-05-23 23:52:43 -04:00
1b49a89c8e correct inaccuracy 2014-05-20 20:59:43 -04:00
9bf92d1e48 Merge branch 'master' of https://github.com/Tox/toxic 2014-05-20 19:38:39 -04:00
da308b2253 fix file transfer bug 2014-05-20 19:36:53 -04:00
0c834b60f5 Not done yet. 2014-05-16 20:00:01 +02:00
2cf5430b85 Fixed a "bug" 2014-05-13 10:14:19 -07:00
30d2a5514e Yup 2014-05-13 10:11:20 -07:00
1d6a6efb81 Merge FreeBSD fixes upstream 2014-05-08 13:13:33 -07:00
af09961875 Merge pull request #128 from aitjcize/null-term
Fix file sender null terminator.
2014-05-05 15:41:09 -04:00
a46fe25283 Force terminate filename. 2014-05-06 03:39:09 +08:00
dfe3f1c4c2 Fix file sender null terminator. 2014-05-06 01:16:59 +08:00
b969079af5 Merge branch 'master' of https://github.com/Tox/toxic 2014-05-01 04:11:29 -04:00
2837c5697f small fix 2014-05-01 04:00:19 -04:00
820b619847 Update DHTnodes 2014-04-28 00:53:20 -07:00
6b72ef0720 change a few memcpy's to strcpy to fix possible segfaults 2014-04-26 04:09:51 -04:00
81125be971 show selected friend's ID at bottom of friendlist 2014-04-24 23:25:38 -04:00
decc585065 Merge branch 'master' of https://github.com/Tox/toxic 2014-04-22 21:17:28 -04:00
17ad66346e make cleaclear command refresh curses 2014-04-22 21:16:35 -04:00
e0330c984f Update README.md 2014-04-21 16:37:39 -07:00
3a9056745f Merge branch 'master' of https://github.com/Tox/toxic 2014-04-20 16:43:14 -04:00
9b997fbf01 fix bug 2014-04-20 16:42:37 -04:00
4cac797b40 General cleanup
Signed-off-by: Sean Qureshi <sean@tox.im>
2014-04-19 14:58:13 -07:00
7f5dc0a756 Removed useless line. 2014-04-19 17:49:19 -04:00
93d77fdeca load data before prompt init to fix bug with prompt logging init 2014-04-12 03:54:43 -04:00
5b3acf3f6b fix issue where log file cannot be created due to invalid name/path 2014-04-12 03:39:15 -04:00
a29136d6b7 use toxic settings for colour theme instead of config flag 2014-04-12 03:12:45 -04:00
4a8db6f098 Merge branch 'terminal_colors' of https://github.com/ooesili/toxic into ooesili-terminal_colors 2014-04-12 03:04:36 -04:00
766ae685c2 add setting to disable terminal alerts 2014-04-11 21:47:09 -04:00
e384f87a04 increase line message buffer size 2014-04-10 18:16:27 -04:00
688564cfc2 merge 2014-04-08 17:23:19 -04:00
4643996c3a add audio settings for conf file 2014-04-08 17:20:21 -04:00
eef5b4941f Merge pull request #121 from czarkoff/openbsd
Include "pthread.h"
2014-04-08 13:57:07 +02:00
4b83df3652 Merge pull request #122 from czarkoff/redeclarations
Drop typedef redeclarations
2014-04-08 13:56:39 +02:00
f54cd87abc Drop typedef redeclarations
C99 doesn't permit redeclaring typedefs in the same scope.
2014-04-08 12:11:10 +02:00
be8a0de38d Include "pthread.h"
It isn't implicit on OpenBSD.
2014-04-08 12:09:42 +02:00
9fbf7bd1c1 Merge branch 'master' of https://github.com/Tox/toxic 2014-04-08 02:51:22 -04:00
cd0bccfbeb remove leading 0 and am/pm for 12 hour timestamp in chat windows 2014-04-08 02:50:56 -04:00
c1cb367acf Update README.md 2014-04-08 02:17:19 -04:00
de3a28c6e6 create empty config file if none found && make default time 24 hours 2014-04-07 18:16:38 -04:00
5976d33fef settings_load needs to be before init_term 2014-04-07 06:22:51 -04:00
e17b62c98b Merge branch 'master' of https://github.com/Tox/toxic 2014-04-07 06:04:15 -04:00
92948abcf1 implement basic user settings 2014-04-07 06:03:45 -04:00
852c3e89d6 Merge pull request #119 from mannol1/master
Wow
2014-04-07 08:44:53 +02:00
8ce1a3d3e9 Codec settings and MORE! 2014-04-06 23:56:49 +02:00
cb9622136c Merge remote-tracking branch 'upstream/master' 2014-04-06 23:26:02 +02:00
46b57feb2f Codec settings 2014-04-06 23:26:00 +02:00
a9bcab4aee fix possible memory leak 2014-04-06 05:20:46 -04:00
0fdb01ff97 Turn off native colours by default 2014-04-05 16:20:35 -06:00
b9290c8a83 Let user disable native colours with ./configure 2014-04-05 13:31:53 -06:00
246a514e88 fix possible segfault 2014-04-02 18:57:11 -04:00
20f126e1d8 fix possible segfault 2014-04-02 17:27:07 -04:00
82027a5b4f Use default terminal fg/bg colors when we can. 2014-04-02 14:25:12 -06:00
3b2010200d possibly fix scrolling bug 2014-04-02 04:59:51 -04:00
24cd6d772f bigger history 2014-04-01 17:42:27 -04:00
c46676daa5 buffer needs to be empty 2014-04-01 16:32:53 -04:00
411ae8d0f5 off by 1 error 2014-04-01 04:43:52 -04:00
e419299487 use correct types 2014-04-01 03:53:12 -04:00
f3a8ba6ab3 small fix 2014-04-01 03:16:38 -04:00
6d98f38128 forgot a few string nulls 2014-04-01 02:49:35 -04:00
08f57de9e0 fix segfault and memory leak 2014-04-01 02:38:32 -04:00
3b7e161149 don't send null terminated strings, and null terminate all incoming strings 2014-03-31 22:34:05 -04:00
15815bf4bb split big function up 2014-03-31 21:26:09 -04:00
43a5ee2d4f esc isn't printable 2014-03-30 21:31:20 -04:00
bd817e77f0 Merge pull request #113 from graboy/master
Fixed support for wide characters
2014-03-30 17:22:41 -04:00
f8a4312fdd Repaired window switching after widechar fix 2014-03-30 16:42:27 -04:00
cce7892d94 Fixed character support 2014-03-30 16:40:13 -04:00
1420618eb0 small fix 2014-03-30 01:22:40 -04:00
52d6e8431f fix 2014-03-29 21:16:25 -04:00
1b89af9063 null fix 2014-03-29 05:44:20 -04:00
d873181306 fix scrolling bug 2014-03-29 04:40:35 -04:00
26640cda14 prevent screen from moving on input when in scroll mode 2014-03-28 19:52:26 -04:00
21c48bde5c string safety 2014-03-28 03:46:00 -04:00
8c071fb208 fix potential segfaults 2014-03-28 01:46:09 -04:00
b36a8fd8fa show percentage complete for file transfers 2014-03-27 23:05:50 -04:00
94e936575e fix memory leaks 2014-03-27 17:59:31 -04:00
1b3c40b539 put help message in chatwindow box when in scroll mode 2014-03-27 05:08:48 -04:00
bd5453002e a few more fixes 2014-03-26 19:14:28 -04:00
c218e104e7 a few fixes 2014-03-26 05:52:21 -04:00
f6db888808 Merge branch 'master' of https://github.com/stqism/toxic 2014-03-25 19:35:47 -07:00
3a804fefd1 Mention av
Signed-off-by: Sean Qureshi <sean@tox.im>
2014-03-25 19:34:11 -07:00
e0deda27da Merge branch 'master' of https://github.com/JFreegman/toxic 2014-03-25 19:27:14 -07:00
bb97f3e543 merge 2014-03-25 22:10:05 -04:00
92c0f737ac . 2014-03-25 22:02:48 -04:00
29b549e677 few fixes 2014-03-25 21:43:49 -04:00
3baa830afb move log struct to proper place 2014-03-25 08:25:10 -04:00
a5ce17f44e refactor groupchats to allow scrolling 2014-03-25 08:21:50 -04:00
2f981ecb12 make audio messages compatible with new printing method 2014-03-25 04:39:44 -04:00
5e941427d3 refactor prompt to allow scrolling 2014-03-25 03:17:22 -04:00
a40b6b1b1b replace prompt's PromptBuf struct with a ChatContext for compatibility 2014-03-24 07:55:28 -04:00
e5b6e0ad9f refactor chat history to allow scrolling 2014-03-24 07:18:58 -04:00
5956c6acaf Merge branch 'master' of https://github.com/mannol1/toxic 2014-03-23 15:32:51 -07:00
58f33fa1d6 Merge upstream 2014-03-23 22:54:56 +01:00
7384440a3d small fix 2014-03-21 04:03:07 -04:00
c1dfb741c9 typging change callback update 2014-03-20 20:59:54 -04:00
25b5545644 Update DHTnodes 2014-03-20 19:11:47 -04:00
d49e911fe4 type fixes 2014-03-19 03:14:08 -04:00
50a37495f8 API update 2014-03-18 21:48:26 -04:00
8bea44a44c properly handle invalid statuses 2014-03-18 19:34:02 -04:00
3ad82cf3b1 fix bug in issue #104 2014-03-17 22:18:04 -04:00
99e36195f7 make sure default statusmsg shows correct version 2014-03-17 20:38:30 -04:00
d03a661635 Merge branch 'master' of https://github.com/Tox/toxic 2014-03-17 19:58:08 -04:00
c5c12385ef Merge pull request #103 from mannol1/master
Open devices when call starts instead of keeping them opened all the time
2014-03-18 00:51:41 +01:00
705a55d1b5 Open devices when call starts instead of keeping them opened all the time 2014-03-18 00:50:15 +01:00
77db725822 Merge pull request #102 from graboy/master
Incorrectly handled error check for widechars
2014-03-16 20:40:26 -04:00
e6c68143bd Fixed incorrectly handled error check for longchars 2014-03-16 20:18:31 -04:00
b5cbd8e410 Merge pull request #100 from jin-eld/fix-noav
Fix toxic build when toxav is not available
2014-03-16 19:36:47 +00:00
8024757e57 Fix toxic build when toxav is not available 2014-03-16 20:28:46 +01:00
a5a7361370 Merge pull request #99 from jin-eld/issue#98
Add checks for pthreads to the build system
2014-03-16 17:01:24 +01:00
7f70345dae Add checks for pthreads to the build system
pthreads are now required, so add appropriate checks to the build system
and set the compile flags accordingly.

closes #98
2014-03-16 16:55:25 +01:00
33a4e806e2 handle last online stuff properly & update toxic version 2014-03-16 03:31:48 -04:00
1a9cd4cd2c Merge pull request #97 from mannol1/master
Fixes and stuff...
2014-03-15 14:12:02 +01:00
c055af7348 retain last status on load and some changes to statusbars 2014-03-15 07:40:13 -04:00
65eb185a9f attempt to make friendlist more visually appealing 2014-03-15 04:14:46 -04:00
a68fc671e5 show "last seen" time for offline friends 2014-03-14 23:46:24 -04:00
79fbf0a31f Fixes and stuff... 2014-03-14 23:08:08 +01:00
d29836845c get unix time more efficiently 2014-03-13 23:56:46 -04:00
c2d417c78b Merge branch 'master' of https://github.com/Tox/toxic 2014-03-13 23:31:58 -04:00
d8d198c81c rm unused argument for sort func 2014-03-13 23:30:44 -04:00
bc8b1f170e Revert "Remove all traces of autotools"
This reverts commit 514cf8e460.
2014-03-13 09:04:30 -07:00
514cf8e460 Remove all traces of autotools 2014-03-13 09:01:49 -07:00
ac82961bea fix bug and load previous status messages on boot 2014-03-13 07:34:14 -04:00
9d52b6ab5d forgot a lock 2014-03-13 06:43:53 -04:00
aeb70262e0 Merge branch 'master' of https://github.com/Tox/toxic 2014-03-13 06:27:57 -04:00
f6a85518bc implement multi-threading 2014-03-13 06:06:53 -04:00
27c5013697 ... 2014-03-12 23:18:46 -07:00
aa53076e11 Add joke unixtime patch 2014-03-12 22:19:21 -07:00
c37311ae36 an experiment gone horribly wrong 2014-03-12 18:48:25 -04:00
ce76896eb3 easier way to format timestamps 2014-03-12 03:08:13 -04:00
5e377639c8 Merge pull request #94 from lehitoskin/spellcheck
SPELLING IS FOR FOOLS
2014-03-11 22:57:33 -04:00
93fb9611f7 SPELLING IS FOR FOOLS 2014-03-11 19:54:09 -07:00
cc3513968e I don't even 2014-03-11 19:30:23 -07:00
dd697d7af1 Merge branch 'master' of https://github.com/Tox/toxic 2014-03-11 20:00:12 -04:00
a32d76ed16 fix 2014-03-11 20:00:03 -04:00
e6d307f65a Merge pull request #92 from mannol1/master
Fixed segfault
2014-03-12 00:41:43 +01:00
b210068c1d Fixed segfault 2014-03-12 00:25:13 +01:00
0151b9b49f rm 'connecting' message and a few small fixes 2014-03-11 18:57:32 -04:00
ab0da36cb7 Merge pull request #91 from mannol1/master
This should fix segfault and remove one-line comments
2014-03-11 23:26:51 +01:00
ed3e9b476d This should fix segfault and remove one-line comments 2014-03-11 23:22:27 +01:00
7c63bd80d6 Merge pull request #90 from Jman012/master
Fixed another clang issue with bools that broek file sending.
2014-03-11 13:22:07 -07:00
9f06331a0b Fixed another clang issue with bools that broek file sending. 2014-03-11 13:11:22 -07:00
a63cba645f Issue #89 workaround for a toxav_get_peer_id issue 2014-03-11 12:59:12 -07:00
9d50d52216 What a waste of a commit
Note to self, fixup rebase this before g finds it
2014-03-11 10:24:21 -07:00
6cb36e71fe I like using entire commits for tiny things 2014-03-11 10:21:35 -07:00
0b52de3773 Update groupchat.c 2014-03-11 10:19:14 -07:00
22ac65c4a9 Change John Doe to Anonymous 2014-03-11 10:09:20 -07:00
3b90c3495f Forgot al 2014-03-10 23:25:59 -07:00
f4e4fbbef1 Update configure.ac 2014-03-10 21:08:02 -07:00
6aee8c136b Merge pull request #88 from mannol1/master
Toxic audio support
2014-03-10 21:05:46 -07:00
2a0740821c Allow AV in Travis 2014-03-10 21:00:53 -07:00
e6f285adc7 Update with latest core 2014-03-11 01:04:53 +01:00
a80da2b58f Merge remote-tracking branch 'upstream/master' 2014-03-11 00:58:18 +01:00
da924f07a9 Updated to latest core 2014-03-11 00:34:18 +01:00
e8cd1417b7 Fixed clang error, disabling the execute module. 2014-03-08 23:42:37 -08:00
d08feb2cc5 simplify popup drawing 2014-03-09 01:02:54 -05:00
fe0641e981 add popup alert on friend delete 2014-03-08 23:57:21 -05:00
1fd07837ea Fixed build problems 2014-03-08 16:36:42 +01:00
6c2ae4ad24 Don't allow changing device while transmission is active 2014-03-08 02:09:11 +01:00
c678d41709 Now supporting device selection 2014-03-08 01:12:51 +01:00
63745afe09 Toxic now supports audio calls 2014-03-07 03:27:48 +01:00
416419a6e7 Toxic now supports audio calls 2014-03-07 03:14:04 +01:00
33e16fe870 small optimization 2014-03-06 19:39:57 -05:00
d712d6c898 Merge pull request #85 from micrictor/master
Fixing fall-back from IPv6 to IPv4
2014-03-05 18:11:55 +01:00
2ae478d546 Fix fall-back from IPv6 to IPv4
Professionalism edits
2014-03-05 07:17:57 -08:00
4b8de0d16d speed up friendlist loading on startup 2014-03-05 08:01:12 -05:00
2fcfa954ab move file sender stuff to its own files 2014-03-05 05:06:21 -05:00
675c8fa89f fix 2014-03-05 03:37:07 -05:00
d1153f96ca small refactor for incoming file transfers 2014-03-05 03:31:12 -05:00
2f473300cd Fixing fallback from IPv6 to IPv4 2014-03-05 06:01:10 +00:00
92d5b2fefc Merge branch 'master' of https://github.com/Tox/toxic 2014-03-04 03:18:59 -05:00
70f8b103de Merge branch 'master' of https://github.com/JFreegman/toxic 2014-03-04 03:18:01 -05:00
41292c1ded old server list wasn't being installed; default to DHTnodes in /usr/local/share/toxic 2014-03-04 03:17:48 -05:00
90393f1dba Update README.md 2014-03-04 03:14:25 -05:00
87bd0f9b34 old server list wasn't being installed; t in DHTnodes in /usr/local/share/toxic 2014-03-04 03:10:07 -05:00
13e67f4ce3 Update README.md 2014-03-04 03:08:04 -05:00
6c9dbfe3bc Merge branch 'master' of https://github.com/Tox/toxic 2014-03-03 19:23:17 -05:00
24b763bce6 simplify logging 2014-03-03 19:21:52 -05:00
e41008bd4e Minor fixup 2014-03-03 07:55:10 -08:00
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
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
fd86f01fd0 Started with audio 2014-02-23 00:00:34 +01:00
e775c51a06 Merge upsteam/master 2014-02-22 23:58:36 +01:00
43 changed files with 5378 additions and 1554 deletions

View File

@ -4,23 +4,52 @@ compiler:
- clang - clang
before_script: before_script:
# installing libsodium, needed for Core #installing libsodium, needed for Core
- git clone git://github.com/jedisct1/libsodium.git - git clone git://github.com/jedisct1/libsodium.git > /dev/null
- cd libsodium - cd libsodium
- git checkout tags/0.4.2 - git checkout tags/0.4.2 > /dev/null
- ./autogen.sh - ./autogen.sh > /dev/null
- ./configure && make -j3 - ./configure > /dev/null
- sudo make install - make check -j3 > /dev/null
- sudo make install >/dev/null
- cd .. - cd ..
#installing yasm, needed for compiling vpx
- sudo apt-get install yasm > /dev/null
#installing libconfig, needed for DHT_bootstrap_daemon
- wget http://www.hyperrealm.com/libconfig/libconfig-1.4.9.tar.gz > /dev/null
- tar -xvzf libconfig-1.4.9.tar.gz > /dev/null
- cd libconfig-1.4.9
- ./configure > /dev/null
- make -j3 > /dev/null
- sudo make install > /dev/null
- cd ..
#installing libopus, needed for audio encoding/decoding
- wget http://downloads.xiph.org/releases/opus/opus-1.0.3.tar.gz > /dev/null
- tar xzf opus-1.0.3.tar.gz > /dev/null
- cd opus-1.0.3
- ./configure > /dev/null
- make -j3 > /dev/null
- sudo make install > /dev/null
- cd ..
#installing vpx
- git clone http://git.chromium.org/webm/libvpx.git > /dev/null
- cd libvpx
- ./configure --enable-shared > /dev/null
- make -j3 >/dev/null
- sudo make install > /dev/null
- cd ..
#creating libraries links and updating cache
- sudo ldconfig > /dev/null
# creating librarys' links and updating cache # creating librarys' links and updating cache
- sudo ldconfig - sudo ldconfig
- git clone https://github.com/irungentoo/ProjectTox-Core.git toxcore - git clone https://github.com/irungentoo/ProjectTox-Core.git toxcore
- cd toxcore - cd toxcore
- autoreconf -i - autoreconf -i
- ./configure --disable-tests --disable-ntox --disable-dht-bootstrap-daemon - ./configure --disable-tests --disable-ntox --disable-daemon --enable-av
- make -j2 - make -j2
- sudo make install - sudo make install
- cd .. - cd ..
- sudo apt-get install libopenal-dev -yq
script: script:
- autoreconf -i - autoreconf -i
- ./configure - ./configure

View File

@ -1,20 +1,25 @@
## 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 program. It looks like [this](http://wiki.tox.im/images/b/b6/Ncursesclient1.png). 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).
## Installation
* Generate the configure script by running the ```autoreconf -i``` command.
To compile, first generate the configure script by running the ```autoreconf -i``` command. * Execute the configure script with ```./configure``` (you may need to pass it the location of your dependency libraries, i.e.):
```./configure --prefix=/where/to/install --with-libtoxcore-headers=/path/to/ProjectTox-Core/toxcore --with-libtoxcore-libs=/path/to/ProjectTox-Core/build/.libs --with-libsodium-headers=/path/to/libsodium/include/ --with-libsodium-libs=/path/to/sodiumtest/lib/ ```
Then execute the configure script with ./configure (you may need to pass it the location of your dependency libraries, i.e.): * Audio calling support requires openal installed
``` * Compile with --disable-av to build without audio call support
./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/ * Compile and install the program with ```make && sudo make install```
``` #### Notes
*Note:* If your default prefix is /usr/local and you happen to get an error that says "error while loading shared libraries: libtoxcore.so.0: cannot open shared object file: No such file or directory", then you can try running ```sudo ldconfig```. If that doesn't fix it, run: If your default prefix is /usr/local and you get the error: "error while loading shared libraries: libtoxcore.so.0: cannot open shared object file: No such file or directory", then you can try running ```sudo ldconfig```. If that doesn't fix it, run:
``` ```
echo '/usr/local/lib/' | sudo tee -a /etc/ld.so.conf.d/locallib.conf echo '/usr/local/lib/' | sudo tee -a /etc/ld.so.conf.d/locallib.conf
sudo ldconfig sudo ldconfig
``` ```
If you dont already have them, you might want to install the ncurses libraries, on Debian: 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 sudo apt-get install libncurses5-dev libncursesw5-dev
``` ```
## Settings
After running Toxic for the first time an empty file called toxic.conf should reside in your home configuration directory (~/.config/tox for Linux users). For an example on how to use this config file to save settings such as auto-logging and time format see: toxic/misc/toxic.conf

View File

@ -25,12 +25,21 @@ toxic_SOURCES = $(top_srcdir)/src/main.c \
$(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.c \
$(top_srcdir)/src/toxic_strings.h $(top_srcdir)/src/toxic_strings.h \
$(top_srcdir)/src/log.c \
$(top_srcdir)/src/log.h \
$(top_srcdir)/src/file_senders.c \
$(top_srcdir)/src/file_senders.h \
$(top_srcdir)/src/line_info.c \
$(top_srcdir)/src/line_info.h \
$(top_srcdir)/src/settings.c \
$(top_srcdir)/src/settings.h
toxic_CFLAGS = -I$(top_srcdir) \ toxic_CFLAGS = -I$(top_srcdir) \
$(NCURSES_CFLAGS) \ $(NCURSES_CFLAGS) \
$(LIBSODIUM_CFLAGS) \ $(LIBSODIUM_CFLAGS) \
$(LIBTOXCORE_CFLAGS) $(LIBTOXCORE_CFLAGS) \
$(PTHREAD_CFLAGS)
toxic_CPPFLAGS = '-DTOXICVER="$(TOXIC_VERSION)"' toxic_CPPFLAGS = '-DTOXICVER="$(TOXIC_VERSION)"'
@ -39,5 +48,19 @@ toxic_LDADD = $(LIBTOXCORE_LDFLAGS) \
$(NCURSES_LIBS) \ $(NCURSES_LIBS) \
$(LIBTOXCORE_LIBS) \ $(LIBTOXCORE_LIBS) \
$(LIBSODIUM_LIBS) \ $(LIBSODIUM_LIBS) \
$(WINSOCK2_LIBS) $(WINSOCK2_LIBS) \
$(PTHREAD_LIBS)
# For audio support
if BUILD_AV
toxic_SOURCES += $(top_srcdir)/src/audio_call.c \
$(top_srcdir)/src/audio_call.h
toxic_CFLAGS += $(LIBTOXAV_CFLAGS) \
$(OPENAL_CFLAGS)
toxic_LDADD += $(LIBTOXAV_LIBS) \
$(OPENAL_LIBS)
endif

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.6], [https://tox.im/]) AC_INIT([toxic], [0.4.0], [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])
@ -113,6 +113,8 @@ AC_CHECK_FUNCS(
[], [],
[ AC_MSG_ERROR([required library function is missing on your system])]) [ AC_MSG_ERROR([required library function is missing on your system])])
AX_PTHREAD( [], [ AC_MSG_ERROR([pthreads not found on your system]) ])
# pkg-config based tests # pkg-config based tests
PKG_PROG_PKG_CONFIG PKG_PROG_PKG_CONFIG
@ -381,6 +383,74 @@ if test "x$LIBTOXCORE_FOUND" = "xno"; then
AC_SUBST(LIBTOXCORE_LDFLAGS) AC_SUBST(LIBTOXCORE_LDFLAGS)
fi fi
####
#### A/V Stuff
AV_SEARCH_DIR=
BUILD_AV="yes"
AC_ARG_WITH(libtoxav-prefix,
AC_HELP_STRING([--with-libtoxav-prefix=DIR],
[search for libtoxav in DIR, i.e. look for libraries in
DIR/lib and for headers in DIR/include]),
[
AV_SEARCH_DIR="$withval"
]
)
if test -n "$AV_SEARCH_DIR"; then
CFLAGS="$CFLAGS -I$AV_SEARCH_DIR/include"
CPPFLAGS="$CPPFLAGS -I$AV_SEARCH_DIR/include"
LDFLAGS="$LDFLAGS -L$AV_SEARCH_DIR/lib"
export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$AV_SEARCH_DIR/lib/pkgconfig
fi
# Check if specified enable
AC_ARG_ENABLE([av],
[AC_HELP_STRING([--disable-av], [build AV support libraries (default: auto)]) ],
[
if test "x$enableval" = "xno"; then
BUILD_AV="no"
elif test "x$enableval" = "xyes"; then
BUILD_AV="yes"
fi
]
)
# Check for A/V library
if test "x$BUILD_AV" = "xyes"; then
PKG_CHECK_MODULES([OPENAL], [openal],
[
export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig
PKG_CHECK_MODULES([LIBTOXAV], [libtoxav],
[
AC_CHECK_HEADER([tox/toxav.h],
[
# Place define for audio support
AC_DEFINE([_SUPPORT_AUDIO], [], [Is audio supported])
AC_MSG_NOTICE([Building with audio support])
],
[
AC_MSG_NOTICE([No A/V headers; disabling A/V support])
BUILD_AV="no"
],)
],
[
AC_MSG_NOTICE([No A/V library; disabling A/V support])
BUILD_AV="no"
])
],
[
AC_MSG_NOTICE([No openal library; disabling A/V support])
BUILD_AV="no"
])
fi
AM_CONDITIONAL(BUILD_AV, test "x$BUILD_AV" = "xyes")
TOXIC_VERSION="$PACKAGE_VERSION" TOXIC_VERSION="$PACKAGE_VERSION"
AC_PATH_PROG([GIT], [git], [no]) AC_PATH_PROG([GIT], [git], [no])
if test "x$GIT" != "xno"; then if test "x$GIT" != "xno"; then

52
fun/unixtime.patch Normal file
View File

@ -0,0 +1,52 @@
--- /src/misc_tools.c
+++ /src/misc_tools.c
@@ -54,24 +54,11 @@
return val;
}
-/* Get the current local time */
-struct tm *get_time(void)
-{
- struct tm *timeinfo;
- time_t now;
- time(&now);
- timeinfo = localtime(&now);
- return timeinfo;
-}
-
/* Prints the time to given window */
void print_time(WINDOW *window)
{
- uint8_t s[MAX_STR_SIZE];
- strftime(s, MAX_STR_SIZE, "[%H:%M:%S] ", get_time());
-
wattron(window, COLOR_PAIR(BLUE));
- wprintw(window, "%s", s);
+ wprintw(window, "[%d] ", (int)time(NULL));
wattroff(window,COLOR_PAIR(BLUE));
}
--- /src/log.c
+++ /src/log.c
@@ -51,9 +51,7 @@
sprintf(&ident[2], "%02X", key[2] & 0xff);
ident[KEY_IDENT_DIGITS*2+1] = '\0';
} else {
- uint8_t s[MAX_STR_SIZE];
- strftime(s, MAX_STR_SIZE, "%Y-%m-%d[%H:%M:%S]", get_time());
- snprintf(ident, sizeof(ident), "%s", s);
+ snprintf(ident, sizeof(ident), "[%d]", (int)time(NULL));
path_len += strlen(ident) + 1;
}
@@ -95,9 +93,7 @@
else
snprintf(name_frmt, sizeof(name_frmt), "%s:", name);
- uint8_t s[MAX_STR_SIZE];
- strftime(s, MAX_STR_SIZE, "%Y/%m/%d [%H:%M:%S]", get_time());
- fprintf(log->file,"%s %s %s\n", s, name_frmt, msg);
+ fprintf(log->file,"[%d]\n", (int)time(NULL), name_frmt, msg);
uint64_t curtime = (uint64_t) time(NULL);

317
m4/ax_pthread.m4 Normal file
View File

@ -0,0 +1,317 @@
# ===========================================================================
# http://www.gnu.org/software/autoconf-archive/ax_pthread.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
#
# DESCRIPTION
#
# This macro figures out how to build C programs using POSIX threads. It
# sets the PTHREAD_LIBS output variable to the threads library and linker
# flags, and the PTHREAD_CFLAGS output variable to any special C compiler
# flags that are needed. (The user can also force certain compiler
# flags/libs to be tested by setting these environment variables.)
#
# Also sets PTHREAD_CC to any special C compiler that is needed for
# multi-threaded programs (defaults to the value of CC otherwise). (This
# is necessary on AIX to use the special cc_r compiler alias.)
#
# NOTE: You are assumed to not only compile your program with these flags,
# but also link it with them as well. e.g. you should link with
# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
#
# If you are only building threads programs, you may wish to use these
# variables in your default LIBS, CFLAGS, and CC:
#
# LIBS="$PTHREAD_LIBS $LIBS"
# CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
# CC="$PTHREAD_CC"
#
# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
# has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name
# (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
#
# Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the
# PTHREAD_PRIO_INHERIT symbol is defined when compiling with
# PTHREAD_CFLAGS.
#
# ACTION-IF-FOUND is a list of shell commands to run if a threads library
# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it
# is not found. If ACTION-IF-FOUND is not specified, the default action
# will define HAVE_PTHREAD.
#
# Please let the authors know if this macro fails on any platform, or if
# you have any other suggestions or comments. This macro was based on work
# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help
# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by
# Alejandro Forero Cuervo to the autoconf macro repository. We are also
# grateful for the helpful feedback of numerous users.
#
# Updated for Autoconf 2.68 by Daniel Richard G.
#
# LICENSE
#
# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
# Copyright (c) 2011 Daniel Richard G. <skunk@iSKUNK.ORG>
#
# This program 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.
#
# This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
#
# As a special exception, the respective Autoconf Macro's copyright owner
# gives unlimited permission to copy, distribute and modify the configure
# scripts that are the output of Autoconf when processing the Macro. You
# need not follow the terms of the GNU General Public License when using
# or distributing such scripts, even though portions of the text of the
# Macro appear in them. The GNU General Public License (GPL) does govern
# all other use of the material that constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the Autoconf
# Macro released by the Autoconf Archive. When you make and distribute a
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 20
AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
AC_DEFUN([AX_PTHREAD], [
AC_REQUIRE([AC_CANONICAL_HOST])
AC_LANG_PUSH([C])
ax_pthread_ok=no
# We used to check for pthread.h first, but this fails if pthread.h
# requires special compiler flags (e.g. on True64 or Sequent).
# It gets checked for in the link test anyway.
# First of all, check if the user has set any of the PTHREAD_LIBS,
# etcetera environment variables, and if threads linking works using
# them:
if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
save_LIBS="$LIBS"
LIBS="$PTHREAD_LIBS $LIBS"
AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes)
AC_MSG_RESULT($ax_pthread_ok)
if test x"$ax_pthread_ok" = xno; then
PTHREAD_LIBS=""
PTHREAD_CFLAGS=""
fi
LIBS="$save_LIBS"
CFLAGS="$save_CFLAGS"
fi
# We must check for the threads library under a number of different
# names; the ordering is very important because some systems
# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
# libraries is broken (non-POSIX).
# Create a list of thread flags to try. Items starting with a "-" are
# C compiler flags, and other items are library names, except for "none"
# which indicates that we try without any flags at all, and "pthread-config"
# which is a program returning the flags for the Pth emulation library.
ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
# The ordering *is* (sometimes) important. Some notes on the
# individual items follow:
# pthreads: AIX (must check this before -lpthread)
# none: in case threads are in libc; should be tried before -Kthread and
# other compiler flags to prevent continual compiler warnings
# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
# -pthreads: Solaris/gcc
# -mthreads: Mingw32/gcc, Lynx/gcc
# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
# doesn't hurt to check since this sometimes defines pthreads too;
# also defines -D_REENTRANT)
# ... -mt is also the pthreads flag for HP/aCC
# pthread: Linux, etcetera
# --thread-safe: KAI C++
# pthread-config: use pthread-config program (for GNU Pth library)
case ${host_os} in
solaris*)
# On Solaris (at least, for some versions), libc contains stubbed
# (non-functional) versions of the pthreads routines, so link-based
# tests will erroneously succeed. (We need to link with -pthreads/-mt/
# -lpthread.) (The stubs are missing pthread_cleanup_push, or rather
# a function called by this macro, so we could check for that, but
# who knows whether they'll stub that too in a future libc.) So,
# we'll just look for -pthreads and -lpthread first:
ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags"
;;
darwin*)
ax_pthread_flags="-pthread $ax_pthread_flags"
;;
esac
if test x"$ax_pthread_ok" = xno; then
for flag in $ax_pthread_flags; do
case $flag in
none)
AC_MSG_CHECKING([whether pthreads work without any flags])
;;
-*)
AC_MSG_CHECKING([whether pthreads work with $flag])
PTHREAD_CFLAGS="$flag"
;;
pthread-config)
AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no)
if test x"$ax_pthread_config" = xno; then continue; fi
PTHREAD_CFLAGS="`pthread-config --cflags`"
PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
;;
*)
AC_MSG_CHECKING([for the pthreads library -l$flag])
PTHREAD_LIBS="-l$flag"
;;
esac
save_LIBS="$LIBS"
save_CFLAGS="$CFLAGS"
LIBS="$PTHREAD_LIBS $LIBS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
# Check for various functions. We must include pthread.h,
# since some functions may be macros. (On the Sequent, we
# need a special flag -Kthread to make this header compile.)
# We check for pthread_join because it is in -lpthread on IRIX
# while pthread_create is in libc. We check for pthread_attr_init
# due to DEC craziness with -lpthreads. We check for
# pthread_cleanup_push because it is one of the few pthread
# functions on Solaris that doesn't have a non-functional libc stub.
# We try pthread_create on general principles.
AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>
static void routine(void *a) { a = 0; }
static void *start_routine(void *a) { return a; }],
[pthread_t th; pthread_attr_t attr;
pthread_create(&th, 0, start_routine, 0);
pthread_join(th, 0);
pthread_attr_init(&attr);
pthread_cleanup_push(routine, 0);
pthread_cleanup_pop(0) /* ; */])],
[ax_pthread_ok=yes],
[])
LIBS="$save_LIBS"
CFLAGS="$save_CFLAGS"
AC_MSG_RESULT($ax_pthread_ok)
if test "x$ax_pthread_ok" = xyes; then
break;
fi
PTHREAD_LIBS=""
PTHREAD_CFLAGS=""
done
fi
# Various other checks:
if test "x$ax_pthread_ok" = xyes; then
save_LIBS="$LIBS"
LIBS="$PTHREAD_LIBS $LIBS"
save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
# Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
AC_MSG_CHECKING([for joinable pthread attribute])
attr_name=unknown
for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>],
[int attr = $attr; return attr /* ; */])],
[attr_name=$attr; break],
[])
done
AC_MSG_RESULT($attr_name)
if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name,
[Define to necessary symbol if this constant
uses a non-standard name on your system.])
fi
AC_MSG_CHECKING([if more special flags are required for pthreads])
flag=no
case ${host_os} in
aix* | freebsd* | darwin*) flag="-D_THREAD_SAFE";;
osf* | hpux*) flag="-D_REENTRANT";;
solaris*)
if test "$GCC" = "yes"; then
flag="-D_REENTRANT"
else
flag="-mt -D_REENTRANT"
fi
;;
esac
AC_MSG_RESULT(${flag})
if test "x$flag" != xno; then
PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
fi
AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT],
ax_cv_PTHREAD_PRIO_INHERIT, [
AC_LINK_IFELSE([
AC_LANG_PROGRAM([[#include <pthread.h>]], [[int i = PTHREAD_PRIO_INHERIT;]])],
[ax_cv_PTHREAD_PRIO_INHERIT=yes],
[ax_cv_PTHREAD_PRIO_INHERIT=no])
])
AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"],
AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], 1, [Have PTHREAD_PRIO_INHERIT.]))
LIBS="$save_LIBS"
CFLAGS="$save_CFLAGS"
# More AIX lossage: compile with *_r variant
if test "x$GCC" != xyes; then
case $host_os in
aix*)
AS_CASE(["x/$CC"],
[x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6],
[#handle absolute path differently from PATH based program lookup
AS_CASE(["x$CC"],
[x/*],
[AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])],
[AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])])
;;
esac
fi
fi
test -n "$PTHREAD_CC" || PTHREAD_CC="$CC"
AC_SUBST(PTHREAD_LIBS)
AC_SUBST(PTHREAD_CFLAGS)
AC_SUBST(PTHREAD_CC)
# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
if test x"$ax_pthread_ok" = xyes; then
ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
:
else
ax_pthread_ok=no
$2
fi
AC_LANG_POP
])dnl AX_PTHREAD

8
misc/DHTnodes Normal file
View File

@ -0,0 +1,8 @@
192.254.75.98 33445 951C88B7E75C867418ACDB5D273821372BB5BD652740BCDF623A4FA293E75D2F
2607:5600:284::2 33445 951C88B7E75C867418ACDB5D273821372BB5BD652740BCDF623A4FA293E75D2F
23.226.230.47 33445 A09162D68618E742FFBCA1C2C70385E6679604B2D80EA6E84AD0996A1AC8A074
173.255.223.85 33445 92E0CE88651FC89D54D6A3110FD08854ECD487613E69BFB1002380D35FD4F947
144.76.60.215 33445 04119E835DF3E78BACF0F84235B300546AF8B936F035185E2A8E9E0A67C8924F
109.169.46.133 33445 4086B340BF2C2C3CC5412BCF673080EF10CF5E75A06AC960497BD85B9DEA2E64
54.199.139.199 33445 7F9C31FE850E97CEFD4C4591DF93FC757C7C12549DDD55F8EEAECC34FE76C029
66.175.223.88 33445 B24E2FB924AE66D023FE1E42A2EE3B432010206F751A2FFD3E297383ACF1572E

View File

@ -1,2 +0,0 @@
192.254.75.98 33445 FE3914F4616E227F29B2103450D6B55A836AD4BD23F97144E2C4ABE8D504FE1B
2607:5600:284::2 33445 FE3914F4616E227F29B2103450D6B55A836AD4BD23F97144E2C4ABE8D504FE1B

View File

@ -1 +1 @@
dist_pkgdata_DATA = DHTservers dist_pkgdata_DATA = DHTnodes

20
misc/toxic.conf Normal file
View File

@ -0,0 +1,20 @@
# 24 or 12 hour time
time:24;
# 1 to enable autologging, 0 to disable
autolog:0;
# 1 to disbale terminal alerts on messages, 0 to enable
disable_alerts:0;
# maximum lines for chat window history
history_size:700;
# 1 to use native terminal colours, 0 to use toxic default colour theme
colour_theme:0;
# preferred audio input device; numbers correspond to /lsdev in
audio_in_dev:0;
# preferred audio output device; numbers correspond to /lsdev out
audio_out_dev:0;

818
src/audio_call.c Normal file
View File

@ -0,0 +1,818 @@
/*
* Toxic -- Tox Curses Client
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "toxic_windows.h"
#include "audio_call.h"
#include "chat_commands.h"
#include "global_commands.h"
#include "toxic_windows.h"
#include "line_info.h"
#include <curses.h>
#include <AL/al.h>
#include <AL/alc.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#define _cbend pthread_exit(NULL)
#define AUDIO_FRAME_SIZE (av_DefaultSettings.audio_sample_rate * av_DefaultSettings.audio_frame_duration / 1000)
#define MAX_CALLS 10
#define _True 1
#define _False 0
typedef struct _DeviceIx {
ALCdevice *dhndl; /* Handle of device selected/opened */
ALCcontext *ctx; /* Device context */
const char *devices[MAX_DEVICES]; /* Container of available devices */
int size; /* Size of above container */
int dix; /* Index of default device */
int index; /* Current index */
} DeviceIx;
typedef struct _Call {
pthread_t ttid; /* Transmission thread id */
_Bool ttas; /* Transmission thread active status (0 - stopped, 1- running) */
} Call;
struct _ASettings {
DeviceIx device[2];
AudioError errors;
ToxAv *av;
ToxAvCodecSettings cs;
Call calls[MAX_CALLS];
} ASettins;
void callback_recv_invite ( int32_t call_index, void *arg );
void callback_recv_ringing ( int32_t call_index, void *arg );
void callback_recv_starting ( int32_t call_index, void *arg );
void callback_recv_ending ( int32_t call_index, void *arg );
void callback_recv_error ( int32_t call_index, void *arg );
void callback_call_started ( int32_t call_index, void *arg );
void callback_call_canceled ( int32_t call_index, void *arg );
void callback_call_rejected ( int32_t call_index, void *arg );
void callback_call_ended ( int32_t call_index, void *arg );
void callback_requ_timeout ( int32_t call_index, void *arg );
void callback_peer_timeout ( int32_t call_index, void *arg );
static void print_err (ToxWindow *self, uint8_t *error_str)
{
line_info_add(self, NULL, NULL, NULL, error_str, SYS_MSG, 0, 0);
}
/* Opens device under current index
*/
int device_open (ToxWindow *self, _Devices type)
{
WINDOW *window = self->chatwin->history;
/* Do not error if no device */
if ( !ASettins.device[type].size ) return 0;
ALCdevice *prev_device = ASettins.device[type].dhndl;
uint8_t msg[MAX_STR_SIZE];
uint8_t *error = NULL;
if ( type == input ) {
ASettins.device[type].dhndl = alcCaptureOpenDevice(
ASettins.device[type].devices[ASettins.device[type].index],
av_DefaultSettings.audio_sample_rate,
AL_FORMAT_MONO16,
AUDIO_FRAME_SIZE * 4);
if (alcGetError(ASettins.device[type].dhndl) != AL_NO_ERROR) {
/* Now check if we have previous device and act acording to it */
if ( !prev_device ) {
error = "Error starting input device!";
ASettins.errors |= ErrorStartingCaptureDevice;
} else {
error = "Could not start input device, falling back to previous";
/* NOTE: What if device is opened? */
ASettins.device[type].dhndl = prev_device;
}
} else {
/* Close previous */
if ( prev_device )
alcCaptureCloseDevice(prev_device);
if ( window ) {
snprintf(msg, sizeof(msg), "Input device: %s", ASettins.device[type].devices[ASettins.device[type].index]);
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
}
}
ASettins.device[type].ctx = NULL;
} else {
ASettins.device[type].dhndl = alcOpenDevice(ASettins.device[type].devices[ASettins.device[type].index]);
if (alcGetError(ASettins.device[type].dhndl) != AL_NO_ERROR) {
/* Now check if we have previous device and act acording to it */
if ( !prev_device ) {
error = "Error starting output device!";
ASettins.errors |= ErrorStartingOutputDevice;
ASettins.device[type].ctx = NULL;
} else {
error = "Could not start output device, falling back to previous";
/* NOTE: What if device is opened? */
ASettins.device[type].dhndl = prev_device;
}
} else {
/* Close previous */
if ( prev_device ) {
alcCaptureCloseDevice(prev_device);
alcMakeContextCurrent(NULL);
alcDestroyContext(ASettins.device[type].ctx);
}
ASettins.device[type].ctx = alcCreateContext(ASettins.device[type].dhndl, NULL);
if ( window ) {
snprintf(msg, sizeof(msg), "Output device: %s", ASettins.device[type].devices[ASettins.device[type].index]);
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
}
}
}
if ( error ) {
if ( window ) {
snprintf(msg, sizeof(msg), "Error: %s", error);
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
}
return -1;
} else return 0;
}
int device_close (ToxWindow *self, _Devices type)
{
/* Only close if device opened */
if ( ASettins.device[type].dhndl ) {
uint8_t *device = NULL;
if (type == input) {
alcCaptureCloseDevice(ASettins.device[type].dhndl);
device = "input";
} else {
alcCloseDevice(ASettins.device[type].dhndl);
alcMakeContextCurrent(NULL);
if ( ASettins.device[type].ctx )
alcDestroyContext(ASettins.device[type].ctx);
device = "output";
}
ASettins.device[type].index = ASettins.device[type].dix;
if ( device == NULL ) {
return -1;
}
if ( self ) {
uint8_t msg[MAX_STR_SIZE];
snprintf(msg, sizeof(msg), "Closed %s device", device);
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
}
}
return 0;
}
int device_set(ToxWindow *self, _Devices type, long int selection)
{
uint8_t str[MAX_STR_SIZE];
uint8_t *s_type = type == input ? "input" : "output";
if ( selection < 0 || selection >= ASettins.device[type].size ) {
snprintf(str, sizeof(str), "Cannot set audio %s device: Invalid index: %ld", s_type, selection);
line_info_add(self, NULL, NULL, NULL, str, SYS_MSG, 0, 0);
return -1;
}
ASettins.device[type].index = selection;
if ( device_open(self, type) != 0 ) {
snprintf(str, sizeof(str), "Cannot open audio %s device index: %ld", s_type, selection);
line_info_add(self, NULL, NULL, NULL, str, SYS_MSG, 0, 0);
return -1;
}
return 0;
}
ToxAv *init_audio(ToxWindow *self, Tox *tox)
{
ASettins.cs = av_DefaultSettings;
ASettins.cs.video_height = ASettins.cs.video_width = 0;
ASettins.errors = NoError;
memset(ASettins.calls, 0, sizeof(Call) * 10);
/* Capture devices */
const char *stringed_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);
ASettins.device[input].size = 0;
if ( stringed_device_list ) {
const char *default_device = alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
for ( ; *stringed_device_list; ++ASettins.device[input].size ) {
ASettins.device[input].devices[ASettins.device[input].size] = stringed_device_list;
if ( strcmp( default_device , ASettins.device[input].devices[ASettins.device[input].size] ) == 0 )
ASettins.device[input].index = ASettins.device[input].dix = ASettins.device[input].size;
stringed_device_list += strlen( stringed_device_list ) + 1;
}
}
/* Output devices */
stringed_device_list = alcGetString(NULL, ALC_DEVICE_SPECIFIER);
ASettins.device[output].size = 0;
if ( stringed_device_list ) {
const char *default_device = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
for ( ; *stringed_device_list; ++ASettins.device[output].size ) {
ASettins.device[output].devices[ASettins.device[output].size] = stringed_device_list;
if ( strcmp( default_device , ASettins.device[output].devices[ASettins.device[output].size] ) == 0 )
ASettins.device[output].index = ASettins.device[output].dix = ASettins.device[output].size;
stringed_device_list += strlen( stringed_device_list ) + 1;
}
}
if (!ASettins.device[input].size && !ASettins.device[output].size) {
uint8_t *msg = "No devices: disabling audio!";
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
ASettins.av = NULL;
} else {
/* Streaming stuff from core */
ASettins.av = toxav_new(tox, MAX_CALLS);
if ( !ASettins.av ) {
ASettins.errors |= ErrorStartingCoreAudio;
return NULL;
}
toxav_register_callstate_callback(callback_call_started, av_OnStart, self);
toxav_register_callstate_callback(callback_call_canceled, av_OnCancel, self);
toxav_register_callstate_callback(callback_call_rejected, av_OnReject, self);
toxav_register_callstate_callback(callback_call_ended, av_OnEnd, self);
toxav_register_callstate_callback(callback_recv_invite, av_OnInvite, self);
toxav_register_callstate_callback(callback_recv_ringing, av_OnRinging, self);
toxav_register_callstate_callback(callback_recv_starting, av_OnStarting, self);
toxav_register_callstate_callback(callback_recv_ending, av_OnEnding, self);
toxav_register_callstate_callback(callback_recv_error, av_OnError, self);
toxav_register_callstate_callback(callback_requ_timeout, av_OnRequestTimeout, self);
toxav_register_callstate_callback(callback_peer_timeout, av_OnPeerTimeout, self);
}
return ASettins.av;
}
void terminate_audio()
{
int i;
for (i = 0; i < MAX_CALLS; i ++)
stop_transmission(i);
if ( ASettins.av )
toxav_kill(ASettins.av);
device_close(NULL, input);
device_close(NULL, output);
}
int errors()
{
return ASettins.errors;
}
/*
* Transmission
*/
void *transmission(void *arg)
{
ToxWindow* self = arg;
int32_t call_index = self->call_index;
/* Missing audio support */
if ( !ASettins.av ) _cbend;
Call* this_call = &ASettins.calls[call_index];
this_call->ttas = _True;
/* Prepare devices */
alcCaptureStart(ASettins.device[input].dhndl);
alcMakeContextCurrent(ASettins.device[output].ctx);
int32_t dec_frame_len;
int16_t frame[4096];
int32_t sample = 0;
uint32_t buffer;
int32_t ready;
int32_t openal_buffers = 5;
uint32_t source, *buffers;
uint8_t encoded_payload[RTP_PAYLOAD_SIZE];
const int32_t frame_size = AUDIO_FRAME_SIZE;
/* Prepare buffers */
buffers = calloc(sizeof(uint32_t), openal_buffers);
alGenBuffers(openal_buffers, buffers);
alGenSources((uint32_t)1, &source);
alSourcei(source, AL_LOOPING, AL_FALSE);
uint16_t zeros[frame_size];
memset(zeros, 0, frame_size);
int16_t PCM[frame_size];
int32_t i = 0;
for (; i < openal_buffers; ++i) {
alBufferData(buffers[i], AL_FORMAT_MONO16, zeros, frame_size, 48000);
}
alSourceQueueBuffers(source, openal_buffers, buffers);
alSourcePlay(source);
if (alGetError() != AL_NO_ERROR) {
/* Print something? */
/*fprintf(stderr, "Error starting audio\n");*/
// goto cleanup;
}
/* Start transmission */
while (this_call->ttas) {
alcGetIntegerv(ASettins.device[input].dhndl, ALC_CAPTURE_SAMPLES, (int32_t) sizeof(int32_t), &sample);
/* RECORD AND SEND */
if (sample >= frame_size) {
alcCaptureSamples(ASettins.device[input].dhndl, frame, frame_size);
int32_t payload_size = toxav_prepare_audio_frame(ASettins.av, call_index, encoded_payload, RTP_PAYLOAD_SIZE, frame, frame_size);
if ( payload_size <= 0 || toxav_send_audio(ASettins.av, call_index, encoded_payload, payload_size) < 0 ) {
/*fprintf(stderr, "Could not encode audio packet\n");*/
}
}
/* PLAYBACK */
alGetSourcei(source, AL_BUFFERS_PROCESSED, &ready);
if (ready <= 0) continue;
dec_frame_len = toxav_recv_audio(ASettins.av, call_index, frame_size, PCM);
/* Play the packet */
if (dec_frame_len > 0) {
alSourceUnqueueBuffers(source, 1, &buffer);
alBufferData(buffer, AL_FORMAT_MONO16, PCM, dec_frame_len * 2 * 1, 48000);
int32_t error = alGetError();
if (error != AL_NO_ERROR) {
fprintf(stderr, "Error setting buffer %d\n", error);
break;
}
alSourceQueueBuffers(source, 1, &buffer);
if (alGetError() != AL_NO_ERROR) {
fprintf(stderr, "Error: could not buffer audio\n");
break;
}
alGetSourcei(source, AL_SOURCE_STATE, &ready);
if (ready != AL_PLAYING) alSourcePlay(source);
}
else {
/* Error ignored */
}
usleep(1000);
}
cleanup:
alDeleteSources(1, &source);
alDeleteBuffers(openal_buffers, buffers);
/*
device_close(NULL, input);
device_close(NULL, output);*/
_cbend;
}
int start_transmission(ToxWindow *self)
{
if ( !ASettins.av || self->call_index == -1 ) return -1;
/* Don't provide support for video */
if ( 0 != toxav_prepare_transmission(ASettins.av, self->call_index, &ASettins.cs, 0) ) {
line_info_add(self, NULL, NULL, NULL, "Could not prepare transmission", SYS_MSG, 0, 0);
}
if ( !toxav_capability_supported(ASettins.av, self->call_index, AudioDecoding) ||
!toxav_capability_supported(ASettins.av, self->call_index, AudioEncoding) )
return -1;
if ( 0 != pthread_create(&ASettins.calls[self->call_index].ttid, NULL, transmission, self ) &&
0 != pthread_detach(ASettins.calls[self->call_index].ttid) ) {
return -1;
}
}
int stop_transmission(int call_index)
{
toxav_kill_transmission(ASettins.av, call_index);
ASettins.calls[call_index].ttas = _False;
}
/*
* End of transmission
*/
/*
* Callbacks
*/
#define CB_BODY(call_idx, Arg, onFunc) do { ToxWindow* windows = (Arg); int i;\
for (i = 0; i < MAX_WINDOWS_NUM; ++i) if (windows[i].onFunc != NULL) windows[i].onFunc(&windows[i], ASettins.av, call_idx); } while (0)
void callback_recv_invite ( int32_t call_index, void* arg )
{
CB_BODY(call_index, arg, onInvite);
}
void callback_recv_ringing ( int32_t call_index, void* arg )
{
CB_BODY(call_index, arg, onRinging);
}
void callback_recv_starting ( int32_t call_index, void* arg )
{
ToxWindow* windows = arg;
int i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i)
if (windows[i].onStarting != NULL && windows[i].call_index == call_index) {
windows[i].onStarting(&windows[i], ASettins.av, call_index);
if ( 0 != start_transmission(&windows[i]) ) {/* YEAH! */
line_info_add(&windows[i], NULL, NULL, NULL, "Error starting transmission!", SYS_MSG, 0, 0);
return;
}
}
}
void callback_recv_ending ( int32_t call_index, void* arg )
{
CB_BODY(call_index, arg, onEnding);
stop_transmission(call_index);
}
void callback_recv_error ( int32_t call_index, void* arg )
{
CB_BODY(call_index, arg, onError);
}
void callback_call_started ( int32_t call_index, void* arg )
{
ToxWindow* windows = arg;
int i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i)
if (windows[i].onStart != NULL && windows[i].call_index == call_index) {
windows[i].onStart(&windows[i], ASettins.av, call_index);
if ( 0 != start_transmission(&windows[i]) ) {/* YEAH! */
line_info_add(&windows[i], NULL, NULL, NULL, "Error starting transmission!", SYS_MSG, 0, 0);
return;
}
}
}
void callback_call_canceled ( int32_t call_index, void* arg )
{
CB_BODY(call_index, arg, onCancel);
/* In case call is active */
stop_transmission(call_index);
}
void callback_call_rejected ( int32_t call_index, void* arg )
{
CB_BODY(call_index, arg, onReject);
}
void callback_call_ended ( int32_t call_index, void* arg )
{
CB_BODY(call_index, arg, onEnd);
stop_transmission(call_index);
}
void callback_requ_timeout ( int32_t call_index, void* arg )
{
CB_BODY(call_index, arg, onRequestTimeout);
}
void callback_peer_timeout ( int32_t call_index, void* arg )
{
CB_BODY(call_index, arg, onPeerTimeout);
stop_transmission(call_index);
/* Call is stopped manually since there might be some other
* actions that one can possibly take on timeout
*/
toxav_stop_call(ASettins.av, call_index);
}
/*
* End of Callbacks
*/
/*
* Commands from chat_commands.h
*/
void cmd_call(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
uint8_t msg[MAX_STR_SIZE];
uint8_t *error_str;
if (argc != 0) {
error_str = "Invalid syntax!";
goto on_error;
}
if ( !ASettins.av ) {
error_str = "Audio not supported!";
goto on_error;
}
ToxAvError error = toxav_call(ASettins.av, &self->call_index, self->num, TypeAudio, 30);
if ( error != ErrorNone ) {
if ( error == ErrorAlreadyInCall ) error_str = "Already in a call!";
else error_str = "Internal error!";
goto on_error;
}
snprintf(msg, sizeof(msg), "Calling... idx: %d", self->call_index);
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
return;
on_error:
snprintf(msg, sizeof(msg), "%s", error_str);
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
}
void cmd_answer(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
uint8_t *error_str;
if (argc != 0) {
error_str = "Invalid syntax!";
goto on_error;
}
if ( !ASettins.av ) {
error_str = "Audio not supported!";
goto on_error;
}
ToxAvError error = toxav_answer(ASettins.av, self->call_index, TypeAudio);
if ( error != ErrorNone ) {
if ( error == ErrorInvalidState ) error_str = "Cannot answer in invalid state!";
else if ( error == ErrorNoCall ) error_str = "No incoming call!";
else error_str = "Internal error!";
goto on_error;
}
/* Callback will print status... */
return;
on_error:
print_err (self, error_str);
}
void cmd_reject(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
uint8_t *error_str;
if (argc != 0) {
error_str = "Invalid syntax!";
goto on_error;
}
if ( !ASettins.av ) {
error_str = "Audio not supported!";
goto on_error;
}
ToxAvError error = toxav_reject(ASettins.av, self->call_index, "Why not?");
if ( error != ErrorNone ) {
if ( error == ErrorInvalidState ) error_str = "Cannot reject in invalid state!";
else if ( error == ErrorNoCall ) error_str = "No incoming call!";
else error_str = "Internal error!";
goto on_error;
}
/* Callback will print status... */
return;
on_error:
print_err (self, error_str);
}
void cmd_hangup(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
uint8_t *error_str;
if (argc != 0) {
error_str = "Invalid syntax!";
goto on_error;
}
if ( !ASettins.av ) {
error_str = "Audio not supported!";
goto on_error;
}
ToxAvError error = toxav_hangup(ASettins.av, self->call_index);
if ( error != ErrorNone ) {
if ( error == ErrorInvalidState ) error_str = "Cannot hangup in invalid state!";
else if ( error == ErrorNoCall ) error_str = "No call!";
else error_str = "Internal error!";
goto on_error;
}
return;
on_error:
print_err (self, error_str);
}
void cmd_cancel(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
uint8_t *error_str;
if (argc != 0) {
error_str = "Invalid syntax!";
goto on_error;
}
if ( !ASettins.av ) {
error_str = "Audio not supported!";
goto on_error;
}
ToxAvError error = toxav_cancel(ASettins.av, self->call_index, self->num,
"Only those who appreciate small things know the beauty that is life");
if ( error != ErrorNone ) {
if ( error == ErrorNoCall ) error_str = "No call!";
else error_str = "Internal error!";
goto on_error;
}
/* Callback will print status... */
return;
on_error:
print_err (self, error_str);
}
void cmd_list_devices(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
uint8_t msg[MAX_STR_SIZE];
uint8_t *error_str;
if ( argc != 1 ) {
if ( argc < 1 ) error_str = "Type must be specified!";
else error_str = "Only one argument allowed!";
goto on_error;
}
_Devices type;
if ( strcmp(argv[1], "in") == 0 ) /* Input devices */
type = input;
else if ( strcmp(argv[1], "out") == 0 ) /* Output devices */
type = output;
else {
snprintf(msg, sizeof(msg), "Invalid type: %s", argv[1]);
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
return;
}
int i = 0;
for ( ; i < ASettins.device[type].size; i ++) {
snprintf(msg, sizeof(msg), "%d: %s", i, ASettins.device[type].devices[i]);
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
}
return;
on_error:
print_err (self, error_str);
}
void cmd_change_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
uint8_t msg[MAX_STR_SIZE];
uint8_t *error_str;
if ( argc != 2 ) {
if ( argc < 1 ) error_str = "Type must be specified!";
else if ( argc < 2 ) error_str = "Must have id!";
else error_str = "Only two arguments allowed!";
goto on_error;
}
if ( ASettins.calls[self->call_index].ttas ) { /* Transmission is active */
error_str = "Cannot change device while active transmission";
goto on_error;
}
_Devices type;
if ( strcmp(argv[1], "in") == 0 ) /* Input devices */
type = input;
else if ( strcmp(argv[1], "out") == 0 ) /* Output devices */
type = output;
else {
snprintf(msg, sizeof(msg), "Invalid type: %s", argv[1]);
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
return;
}
char *end;
long int selection = strtol(argv[2], &end, 10);
if ( *end ) {
error_str = "Invalid input";
goto on_error;
}
if ( device_close(self, type) != 0 ) {
error_str = "Could not close device!";
goto on_error;
}
if ( device_set(self, type, selection ) == 0) {
/*snprintf(msg, sizeof(msg), "Selected: %s", ASettins.device[type].devices[selection]);
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);*/
}
if ( device_open(self, type) == 0 ) {
snprintf(msg, sizeof(msg), "Now using: %s", ASettins.device[type].devices[selection]);
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
}
return;
on_error:
print_err (self, error_str);
}

35
src/audio_call.h Normal file
View File

@ -0,0 +1,35 @@
/*
* Toxic -- Tox Curses Client
*/
#ifndef _audio_h
#define _audio_h
#include <tox/toxav.h>
#define MAX_DEVICES 32
typedef enum _AudioError {
NoError = 0,
ErrorStartingCaptureDevice = 1 << 0,
ErrorStartingOutputDevice = 1 << 1,
ErrorStartingCoreAudio = 1 << 2
} AudioError;
typedef enum _Devices {
input,
output,
} _Devices;
/* You will have to pass pointer to first member of 'windows'
* declared in windows.c otherwise undefined behaviour will
*/
ToxAv *init_audio(ToxWindow *self, Tox *tox);
void terminate_audio();
int errors();
int start_transmission(ToxWindow *self);
int device_set(ToxWindow *self, _Devices type, long int selection);
#endif /* _audio_h */

File diff suppressed because it is too large Load Diff

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"
ToxWindow new_chat(Tox *m, int friendnum); void kill_chat_window(ToxWindow *self);
ToxWindow new_chat(Tox *m, int32_t 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
@ -12,6 +30,8 @@
#include "toxic_windows.h" #include "toxic_windows.h"
#include "misc_tools.h" #include "misc_tools.h"
#include "friendlist.h" #include "friendlist.h"
#include "execute.h"
#include "line_info.h"
extern ToxWindow *prompt; extern ToxWindow *prompt;
@ -22,76 +42,114 @@ extern uint8_t max_file_senders_index;
void cmd_chat_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_chat_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{ {
wattron(window, COLOR_PAIR(CYAN) | A_BOLD); struct history *hst = self->chatwin->hst;
wprintw(window, "Chat commands:\n"); line_info_clear(hst);
wattroff(window, COLOR_PAIR(CYAN) | A_BOLD); struct line_info *start = hst->line_start;
wprintw(window, " /status <type> <msg> : Set your status with optional note\n"); if (argc == 1) {
wprintw(window, " /note <msg> : Set a personal note\n"); if (!strcmp(argv[1], "global")) {
wprintw(window, " /nick <nick> : Set your nickname\n"); execute(window, self, m, "/help", GLOBAL_COMMAND_MODE);
wprintw(window, " /invite <n> : Invite friend to a group chat\n"); return;
wprintw(window, " /me <action> : Do an action\n"); }
wprintw(window, " /myid : Print your ID\n"); }
wprintw(window, " /join : Join a pending group chat\n");
wprintw(window, " /clear : Clear the screen\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, COLOR_PAIR(CYAN) | A_BOLD); uint8_t *msg = "Chat commands:";
wprintw(window, " * Argument messages must be enclosed in quotation marks.\n\n"); line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 1, CYAN);
wattroff(window, COLOR_PAIR(CYAN) | A_BOLD);
#ifdef _SUPPORT_AUDIO
#define NUMLINES 13
#else
#define NUMLINES 9
#endif
uint8_t lines[NUMLINES][MAX_STR_SIZE] = {
#ifdef _SUPPORT_AUDIO
{ " /call : Audio call" },
{ " /cancel : Cancel call" },
{ " /answer : Answer incomming call" },
{ " /reject : Reject incoming call" },
{ " /hangup : Hangup active call" },
#endif /* _SUPPORT_AUDIO */
{ " /invite <n> : Invite friend to a group chat" },
{ " /join : Join a pending group chat" },
{ " /log <on> or <off> : Enable/disable logging" },
{ " /sendfile <filepath> : Send a file" },
{ " /savefile <n> : Receive a file" },
{ " /close : Close the current chat window" },
{ " /help : Print this message again" },
{ " /help global : Show a list of global commands" },
};
int i;
for (i = 0; i < NUMLINES; ++i)
line_info_add(self, NULL, NULL, NULL, lines[i], SYS_MSG, 0, 0);
msg = " * Use ESC key to toggle history scroll mode\n";
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 1, CYAN);
hst->line_start = start;
} }
void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{ {
uint8_t *errmsg;
if (argc < 1) { if (argc < 1) {
wprintw(window, "Invalid syntax.\n"); errmsg = "Invalid syntax";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return; return;
} }
int groupnum = atoi(argv[1]); int groupnum = atoi(argv[1]);
if (groupnum == 0 && strcmp(argv[1], "0")) { /* atoi returns 0 value on invalid input */ if (groupnum == 0 && strcmp(argv[1], "0")) { /* atoi returns 0 value on invalid input */
wprintw(window, "Invalid syntax.\n"); errmsg = "Invalid syntax.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return; return;
} }
if (tox_invite_friend(m, self->num, groupnum) == -1) { if (tox_invite_friend(m, self->num, groupnum) == -1) {
wprintw(window, "Failed to invite friend.\n"); errmsg = "Failed to invite friend.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return; return;
} }
wprintw(window, "Invited friend to group chat %d.\n", groupnum); uint8_t msg[MAX_STR_SIZE];
snprintf(msg, sizeof(msg), "Invited friend to Room #%d.", groupnum);
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
} }
void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{ {
if (num_active_windows() >= MAX_WINDOWS_NUM) { uint8_t *errmsg;
wattron(window, COLOR_PAIR(RED));
wprintw(window, " * Warning: Too many windows are open.\n"); if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
wattron(window, COLOR_PAIR(RED)); errmsg = " * Warning: Too many windows are open.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, RED);
return; return;
} }
uint8_t *groupkey = friends[self->num].pending_groupchat; uint8_t *groupkey = friends[self->num].pending_groupchat;
if (groupkey[0] == '\0') { if (groupkey[0] == '\0') {
wprintw(window, "No pending group chat invite.\n"); errmsg = "No pending group chat invite.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return; return;
} }
int groupnum = tox_join_groupchat(m, self->num, groupkey); int groupnum = tox_join_groupchat(m, self->num, groupkey);
if (groupnum == -1) { if (groupnum == -1) {
wprintw(window, "Group chat instance failed to initialize.\n"); errmsg = "Group chat instance failed to initialize.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return; return;
} }
if (init_groupchat_win(prompt, m, groupnum) == -1) { if (init_groupchat_win(prompt, m, groupnum) == -1) {
wprintw(window, "Group chat window failed to initialize.\n"); errmsg = "Group chat window failed to initialize.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
tox_del_groupchat(m, groupnum); tox_del_groupchat(m, groupnum);
return; return;
} }
@ -99,64 +157,87 @@ void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{ {
uint8_t *errmsg;
if (argc != 1) { if (argc != 1) {
wprintw(window, "Invalid syntax.\n"); errmsg = "Invalid syntax.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return; return;
} }
uint8_t filenum = atoi(argv[1]); uint8_t filenum = atoi(argv[1]);
if ((filenum == 0 && strcmp(argv[1], "0")) || filenum >= MAX_FILES) { if ((filenum == 0 && strcmp(argv[1], "0")) || filenum >= MAX_FILES) {
wprintw(window, "No pending file transfers with that number.\n"); errmsg = "No pending file transfers with that number.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return; return;
} }
if (!friends[self->num].file_receiver.pending[filenum]) { if (!friends[self->num].file_receiver.pending[filenum]) {
wprintw(window, "No pending file transfers with that number.\n"); errmsg = "No pending file transfers with that number.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return; return;
} }
uint8_t *filename = friends[self->num].file_receiver.filenames[filenum]; uint8_t *filename = friends[self->num].file_receiver.filenames[filenum];
if (tox_file_send_control(m, self->num, 1, filenum, TOX_FILECONTROL_ACCEPT, 0, 0) == 0) if (tox_file_send_control(m, self->num, 1, filenum, TOX_FILECONTROL_ACCEPT, 0, 0) == 0) {
wprintw(window, "Accepted file transfer %u. Saving file as: '%s'\n", filenum, filename); uint8_t msg[MAX_STR_SIZE];
else snprintf(msg, sizeof(msg), "Saving file as: '%s' (%.1f%%)", filename, 0.0);
wprintw(window, "File transfer failed.\n"); line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
friends[self->num].file_receiver.line_id = self->chatwin->hst->line_end->id;
if ((friends[self->num].file_receiver.files[filenum] = fopen(filename, "a")) == NULL) {
errmsg = "* Error writing to file.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, RED);
tox_file_send_control(m, self->num, 1, filenum, TOX_FILECONTROL_KILL, 0, 0);
}
} else {
errmsg = "File transfer failed.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
}
friends[self->num].file_receiver.pending[filenum] = false; friends[self->num].file_receiver.pending[filenum] = false;
} }
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 (max_file_senders_index >= (MAX_FILES-1)) { uint8_t *errmsg;
wprintw(window,"Please wait for some of your outgoing file transfers to complete.\n");
if (max_file_senders_index >= (MAX_FILES - 1)) {
errmsg = "Please wait for some of your outgoing file transfers to complete.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return; return;
} }
if (argc < 1) { if (argc < 1) {
wprintw(window, "Invalid syntax.\n"); errmsg = "Invalid syntax.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return; return;
} }
uint8_t *path = argv[1]; uint8_t *path = argv[1];
if (path[0] != '\"') { if (path[0] != '\"') {
wprintw(window, "File path must be enclosed in quotes.\n"); errmsg = "File path must be enclosed in quotes.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return; return;
} }
path[strlen(++path)-1] = L'\0'; path[strlen(++path) - 1] = L'\0';
int path_len = strlen(path); int path_len = strlen(path);
if (path_len > MAX_STR_SIZE) { if (path_len > MAX_STR_SIZE) {
wprintw(window, "File path exceeds character limit.\n"); errmsg = "File path exceeds character limit.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return; return;
} }
FILE *file_to_send = fopen(path, "r"); FILE *file_to_send = fopen(path, "r");
if (file_to_send == NULL) { if (file_to_send == NULL) {
wprintw(window, "File '%s' not found.\n", path); errmsg = "File not found.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return; return;
} }
@ -166,10 +247,11 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
uint8_t filename[MAX_STR_SIZE]; uint8_t filename[MAX_STR_SIZE];
get_file_name(path, filename); get_file_name(path, filename);
int filenum = tox_new_file_sender(m, self->num, filesize, filename, strlen(filename) + 1); int filenum = tox_new_file_sender(m, self->num, filesize, filename, strlen(filename));
if (filenum == -1) { if (filenum == -1) {
wprintw(window, "Error sending file.\n"); errmsg = "Error sending file.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return; return;
} }
@ -181,13 +263,16 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
file_senders[i].active = true; file_senders[i].active = true;
file_senders[i].toxwin = self; file_senders[i].toxwin = self;
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 = 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 = get_unix_time();
file_senders[i].size = filesize;
file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1, file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1,
tox_file_data_size(m, self->num), file_to_send); tox_file_data_size(m, self->num), file_to_send);
wprintw(window, "Sending file: '%s'\n", path); uint8_t msg[MAX_STR_SIZE];
snprintf(msg, sizeof(msg), "Sending file: '%s'", path);
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
if (i == max_file_senders_index) if (i == max_file_senders_index)
++max_file_senders_index; ++max_file_senders_index;

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]);
@ -7,3 +25,12 @@ void cmd_groupinvite(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_ST
void cmd_join_group(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_join_group(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_savefile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_savefile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_sendfile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_sendfile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
#ifdef _SUPPORT_AUDIO
void cmd_call(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_answer(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_reject(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_hangup(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_cancel(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
#endif /* _SUPPORT_AUDIO */

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/>.
* *
*/ */
@ -108,6 +110,7 @@ char *get_user_config_dir(void)
# else /* __APPLE__ */ # else /* __APPLE__ */
const char *tmp; const char *tmp;
if (!(tmp = getenv("XDG_CONFIG_HOME"))) { if (!(tmp = getenv("XDG_CONFIG_HOME"))) {
len = strlen(home) + strlen("/.config") + 1; len = strlen(home) + strlen("/.config") + 1;
user_config_dir = malloc(len); user_config_dir = malloc(len);

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,14 +1,38 @@
/* /* 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/>.
*
*/ */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <assert.h>
#include "toxic_windows.h" #include "toxic_windows.h"
#include "execute.h" #include "execute.h"
#include "chat_commands.h" #include "chat_commands.h"
#include "global_commands.h" #include "global_commands.h"
#include "line_info.h"
struct cmd_func { struct cmd_func {
const char *name; const char *name;
@ -23,12 +47,17 @@ 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 },
{ "/q", cmd_quit }, { "/q", cmd_quit },
{ "/quit", cmd_quit }, { "/quit", cmd_quit },
{ "/status", cmd_status }, { "/status", cmd_status },
#ifdef _SUPPORT_AUDIO
{ "/lsdev", cmd_list_devices },
{ "/sdev", cmd_change_device },
#endif /* _SUPPORT_AUDIO */
}; };
static struct cmd_func chat_commands[] = { static struct cmd_func chat_commands[] = {
@ -37,23 +66,32 @@ static struct cmd_func chat_commands[] = {
{ "/join", cmd_join_group }, { "/join", cmd_join_group },
{ "/savefile", cmd_savefile }, { "/savefile", cmd_savefile },
{ "/sendfile", cmd_sendfile }, { "/sendfile", cmd_sendfile },
#ifdef _SUPPORT_AUDIO
{ "/call", cmd_call },
{ "/cancel", cmd_cancel },
{ "/answer", cmd_answer },
{ "/reject", cmd_reject },
{ "/hangup", cmd_hangup },
#endif /* _SUPPORT_AUDIO */
}; };
/* Parses input command and puts args into arg array. /* Parses input command and puts args into arg array.
Returns number of arguments on success, -1 on failure. */ Returns number of arguments on success, -1 on failure. */
static int parse_command(WINDOW *w, char *cmd, char (*args)[MAX_STR_SIZE]) static int parse_command(WINDOW *w, ToxWindow *self, char *cmd, char (*args)[MAX_STR_SIZE])
{ {
int num_args = 0; int num_args = 0;
bool cmd_end = false; // flags when we get to the end of cmd bool cmd_end = false; /* flags when we get to the end of cmd */
char *end; // points to the end of the current arg char *end; /* points to the end of the current arg */
/* characters wrapped in double quotes count as one arg */ /* characters wrapped in double quotes count as one arg */
while (!cmd_end && num_args < MAX_NUM_ARGS) { while (!cmd_end && num_args < MAX_NUM_ARGS) {
if (*cmd == '\"') { if (*cmd == '\"') {
end = strchr(cmd+1, '\"'); end = strchr(cmd + 1, '\"');
if (end++ == NULL) { /* Increment past the end quote */ if (end++ == NULL) { /* Increment past the end quote */
wprintw(w, "Invalid argument. Did you forget a closing \"?\n"); uint8_t *errmsg = "Invalid argument. Did you forget a closing \"?";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return -1; return -1;
} }
@ -82,7 +120,7 @@ static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, int num_
for (i = 0; i < num_cmds; ++i) { for (i = 0; i < num_cmds; ++i) {
if (strcmp(args[0], commands[i].name) == 0) { if (strcmp(args[0], commands[i].name) == 0) {
(commands[i].func)(w, self, m, num_args-1, args); (commands[i].func)(w, self, m, num_args - 1, args);
return 0; return 0;
} }
} }
@ -90,13 +128,13 @@ static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, int num_
return 1; return 1;
} }
void execute(WINDOW* w, ToxWindow *self, Tox *m, char *cmd, int mode) void execute(WINDOW *w, ToxWindow *self, Tox *m, char *cmd, int mode)
{ {
if (string_is_empty(cmd)) if (string_is_empty(cmd))
return; return;
char args[MAX_NUM_ARGS][MAX_STR_SIZE] = {0}; char args[MAX_NUM_ARGS][MAX_STR_SIZE] = {0};
int num_args = parse_command(w, cmd, args); int num_args = parse_command(w, self, cmd, args);
if (num_args == -1) if (num_args == -1)
return; return;
@ -109,6 +147,7 @@ void execute(WINDOW* w, ToxWindow *self, Tox *m, char *cmd, int mode)
case CHAT_COMMAND_MODE: case CHAT_COMMAND_MODE:
if (do_command(w, self, m, num_args, CHAT_NUM_COMMANDS, chat_commands, args) == 0) if (do_command(w, self, m, num_args, CHAT_NUM_COMMANDS, chat_commands, args) == 0)
return; return;
break; break;
case GROUPCHAT_COMMAND_MODE: case GROUPCHAT_COMMAND_MODE:
@ -118,5 +157,6 @@ void execute(WINDOW* w, ToxWindow *self, Tox *m, char *cmd, int mode)
if (do_command(w, self, m, num_args, GLOBAL_NUM_COMMANDS, global_commands, args) == 0) if (do_command(w, self, m, num_args, GLOBAL_NUM_COMMANDS, global_commands, args) == 0)
return; return;
wprintw(w, "Invalid command.\n"); uint8_t *errmsg = "Invalid command.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
} }

View File

@ -1,10 +1,34 @@
/* /* 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 13
#ifdef _SUPPORT_AUDIO
#define GLOBAL_NUM_COMMANDS 16
#define CHAT_NUM_COMMANDS 10
#else
#define GLOBAL_NUM_COMMANDS 14
#define CHAT_NUM_COMMANDS 5 #define CHAT_NUM_COMMANDS 5
#endif /* _SUPPORT_AUDIO */
enum { enum {
GLOBAL_COMMAND_MODE, GLOBAL_COMMAND_MODE,

128
src/file_senders.c Normal file
View File

@ -0,0 +1,128 @@
/* file_senders.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
#include "config.h"
#endif
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include "toxic_windows.h"
#include "line_info.h"
FileSender file_senders[MAX_FILES];
uint8_t max_file_senders_index;
static void close_file_sender(int i)
{
fclose(file_senders[i].file);
memset(&file_senders[i], 0, sizeof(FileSender));
int j;
for (j = max_file_senders_index; j > 0; --j) {
if (file_senders[j - 1].active)
break;
}
max_file_senders_index = j;
}
/* Should only be called on exit */
void close_all_file_senders(void)
{
int i;
for (i = 0; i < max_file_senders_index; ++i) {
if (file_senders[i].active)
fclose(file_senders[i].file);
}
}
void do_file_senders(Tox *m)
{
uint8_t msg[MAX_STR_SIZE];
int i;
for (i = 0; i < max_file_senders_index; ++i) {
if (!file_senders[i].active)
continue;
ToxWindow *self = file_senders[i].toxwin;
uint8_t *pathname = file_senders[i].pathname;
int filenum = file_senders[i].filenum;
int32_t friendnum = file_senders[i].friendnum;
FILE *fp = file_senders[i].file;
uint64_t current_time = get_unix_time();
/* If file transfer has timed out kill transfer and send kill control */
if (timed_out(file_senders[i].timestamp, current_time, TIMEOUT_FILESENDER)) {
if (self->chatwin != NULL) {
snprintf(msg, sizeof(msg), "File transfer for '%s' timed out.", pathname);
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
alert_window(file_senders[i].toxwin, WINDOW_ALERT_2, true);
}
tox_file_send_control(m, friendnum, 0, filenum, TOX_FILECONTROL_KILL, 0, 0);
close_file_sender(i);
continue;
}
while (true) {
if (tox_file_send_data(m, friendnum, filenum, file_senders[i].nextpiece,
file_senders[i].piecelen) == -1)
break;
file_senders[i].timestamp = current_time;
file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1,
tox_file_data_size(m, friendnum), fp);
/* refresh line with percentage complete */
if (self->chatwin != NULL) {
uint64_t size = file_senders[i].size;
long double remain = (long double) tox_file_data_remaining(m, friendnum, filenum, 0);
long double pct_remain = 100;
if (remain)
pct_remain = (1 - (remain / size)) * 100;
const uint8_t *name = file_senders[filenum].pathname;
snprintf(msg, sizeof(msg), "File transfer for '%s' accepted (%.1Lf%%)", name, pct_remain);
line_info_set(self, file_senders[filenum].line_id, msg);
}
if (file_senders[i].piecelen == 0) {
if (self->chatwin != NULL) {
snprintf(msg, sizeof(msg), "File '%s' successfuly sent.", pathname);
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
alert_window(file_senders[i].toxwin, WINDOW_ALERT_2, true);
}
tox_file_send_control(m, friendnum, 0, filenum, TOX_FILECONTROL_FINISHED, 0, 0);
close_file_sender(i);
break;
}
}
}
}

26
src/file_senders.h Normal file
View File

@ -0,0 +1,26 @@
/* file_senders.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/>.
*
*/
/* Should only be called on exit */
void close_all_file_senders(void);
void do_file_senders(Tox *m);

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
@ -9,12 +27,19 @@
#include <string.h> #include <string.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <time.h>
#include <tox/tox.h> #include <tox/tox.h>
#include "chat.h" #include "chat.h"
#include "friendlist.h" #include "friendlist.h"
#include "misc_tools.h" #include "misc_tools.h"
#include "line_info.h"
#include "settings.h"
#ifdef _SUPPORT_AUDIO
#include "audio_call.h"
#endif
extern char *DATA_FILE; extern char *DATA_FILE;
extern ToxWindow *prompt; extern ToxWindow *prompt;
@ -23,9 +48,17 @@ static int max_friends_index = 0; /* marks the index of the last friend in fr
static int num_selected = 0; static int num_selected = 0;
static int num_friends = 0; static int num_friends = 0;
extern struct _Winthread Winthread;
extern struct user_settings *user_settings;
ToxicFriend friends[MAX_FRIENDS_NUM]; ToxicFriend friends[MAX_FRIENDS_NUM];
static int friendlist_index[MAX_FRIENDS_NUM] = {0}; static int friendlist_index[MAX_FRIENDS_NUM] = {0};
struct _pendingDel {
int num;
bool active;
} pendingdelete;
#define S_WEIGHT 100 #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)
@ -40,7 +73,7 @@ static int index_name_cmp(const void *n1, const void *n2)
} }
/* sorts friendlist_index first by connection status then alphabetically */ /* sorts friendlist_index first by connection status then alphabetically */
void sort_friendlist_index(Tox *m) void sort_friendlist_index(void)
{ {
int i; int i;
int n = 0; int n = 0;
@ -53,52 +86,70 @@ void sort_friendlist_index(Tox *m)
qsort(friendlist_index, num_friends, sizeof(int), index_name_cmp); qsort(friendlist_index, num_friends, sizeof(int), index_name_cmp);
} }
static void friendlist_onMessage(ToxWindow *self, Tox *m, int num, uint8_t *str, uint16_t len) static void update_friend_last_online(int32_t num, uint64_t timestamp)
{
friends[num].last_online.last_on = timestamp;
friends[num].last_online.tm = *localtime(&timestamp);
/* if the format changes make sure TIME_STR_SIZE is the correct size */
const char *t = user_settings->time == TIME_12 ? "%I:%M %p" : "%H:%M";
strftime(friends[num].last_online.hour_min_str, TIME_STR_SIZE, t,
&friends[num].last_online.tm);
}
static void friendlist_onMessage(ToxWindow *self, Tox *m, int32_t num, uint8_t *str, uint16_t len)
{ {
if (num >= max_friends_index) if (num >= max_friends_index)
return; return;
if (friends[num].chatwin == -1) { if (friends[num].chatwin == -1) {
if (num_active_windows() < MAX_WINDOWS_NUM) { if (get_num_active_windows() < MAX_WINDOWS_NUM) {
friends[num].chatwin = add_window(m, new_chat(m, friends[num].num)); friends[num].chatwin = add_window(m, new_chat(m, friends[num].num));
} else { } else {
str[len] = '\0';
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'}; uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
tox_get_name(m, num, nick); int n_len = tox_get_name(m, num, nick);
nick[TOXIC_MAX_NAME_LENGTH] = '\0'; n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH - 1);
wprintw(prompt->window, "%s: %s\n", nick, str); nick[n_len] = '\0';
prep_prompt_win(); uint8_t timefrmt[TIME_STR_SIZE];
wattron(prompt->window, COLOR_PAIR(RED)); get_time_str(timefrmt);
wprintw(prompt->window, "* Warning: Too many windows are open.\n");
wattron(prompt->window, COLOR_PAIR(RED));
line_info_add(prompt, timefrmt, nick, NULL, str, IN_MSG, 0, 0);
uint8_t *msg = "* Warning: Too many windows are open.";
line_info_add(prompt, NULL, NULL, NULL, msg, SYS_MSG, 0, RED);
alert_window(prompt, WINDOW_ALERT_1, true); alert_window(prompt, WINDOW_ALERT_1, true);
} }
} }
} }
static void friendlist_onConnectionChange(ToxWindow *self, Tox *m, int num, uint8_t status) static void friendlist_onConnectionChange(ToxWindow *self, Tox *m, int32_t num, uint8_t status)
{ {
if (num >= max_friends_index) if (num >= max_friends_index)
return; return;
friends[num].online = status == 1 ? true : false; friends[num].online = status;
sort_friendlist_index(m); update_friend_last_online(num, get_unix_time());
store_data(m, DATA_FILE);
sort_friendlist_index();
} }
static void friendlist_onNickChange(ToxWindow *self, Tox *m, int num, uint8_t *str, uint16_t len) static void friendlist_onNickChange(ToxWindow *self, Tox *m, int32_t num, uint8_t *str, uint16_t len)
{ {
if (len > TOX_MAX_NAME_LENGTH || num >= max_friends_index) if (len > TOX_MAX_NAME_LENGTH || num >= max_friends_index)
return; return;
str[TOXIC_MAX_NAME_LENGTH] = '\0'; len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
len = strlen(str) + 1;
memcpy(friends[num].name, str, len); str[len] = '\0';
strcpy(friends[num].name, str);
friends[num].namelength = len; friends[num].namelength = len;
sort_friendlist_index(m); sort_friendlist_index();
} }
static void friendlist_onStatusChange(ToxWindow *self, Tox *m, int num, TOX_USERSTATUS status) static void friendlist_onStatusChange(ToxWindow *self, Tox *m, int32_t num, uint8_t status)
{ {
if (num >= max_friends_index) if (num >= max_friends_index)
return; return;
@ -106,16 +157,19 @@ static void friendlist_onStatusChange(ToxWindow *self, Tox *m, int num, TOX_USER
friends[num].status = status; friends[num].status = status;
} }
static void friendlist_onStatusMessageChange(ToxWindow *self, int num, uint8_t *str, uint16_t len) static void friendlist_onStatusMessageChange(ToxWindow *self, int32_t num, uint8_t *str, uint16_t len)
{ {
if (len > TOX_MAX_STATUSMESSAGE_LENGTH || num >= max_friends_index) if (len > TOX_MAX_STATUSMESSAGE_LENGTH || num >= max_friends_index)
return; return;
memcpy(friends[num].statusmsg, str, len); str[len] = '\0';
snprintf(friends[num].statusmsg, sizeof(friends[num].statusmsg), "%s", str);
len = strlen(friends[num].statusmsg);
friends[num].statusmsg_len = len; friends[num].statusmsg_len = len;
friends[num].statusmsg[len] = '\0';
} }
static void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int num, bool sort) void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int32_t num, bool sort)
{ {
if (max_friends_index < 0 || max_friends_index >= MAX_FRIENDS_NUM) if (max_friends_index < 0 || max_friends_index >= MAX_FRIENDS_NUM)
return; return;
@ -130,14 +184,16 @@ static void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int num, bool sort
friends[i].online = false; friends[i].online = false;
friends[i].status = TOX_USERSTATUS_NONE; friends[i].status = TOX_USERSTATUS_NONE;
friends[i].namelength = tox_get_name(m, num, friends[i].name); friends[i].namelength = tox_get_name(m, num, friends[i].name);
friends[i].logging_on = (bool) user_settings->autolog == AUTOLOG_ON;
tox_get_client_id(m, num, friends[i].pub_key); tox_get_client_id(m, num, friends[i].pub_key);
update_friend_last_online(i, tox_get_last_online(m, i));
if (friends[i].namelength == -1 || friends[i].name[0] == '\0') { 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);
friends[i].namelength = strlen(UNKNOWN_NAME) + 1; friends[i].namelength = strlen(UNKNOWN_NAME);
} else { /* Enforce toxic's maximum name length */ } else { /* Enforce toxic's maximum name length */
friends[i].name[TOXIC_MAX_NAME_LENGTH] = '\0'; friends[i].namelength = MIN(friends[i].namelength, TOXIC_MAX_NAME_LENGTH);
friends[i].namelength = strlen(friends[i].name) + 1; friends[i].name[friends[i].namelength] = '\0';
} }
num_friends = tox_count_friendlist(m); num_friends = tox_count_friendlist(m);
@ -146,56 +202,56 @@ static void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int num, bool sort
++max_friends_index; ++max_friends_index;
if (sort) if (sort)
sort_friendlist_index(m); sort_friendlist_index();
return; return;
} }
} }
} }
static void friendlist_onFileSendRequest(ToxWindow *self, Tox *m, int num, uint8_t filenum, static void friendlist_onFileSendRequest(ToxWindow *self, Tox *m, int32_t num, uint8_t filenum,
uint64_t filesize, uint8_t *filename, uint16_t filename_len) uint64_t filesize, uint8_t *filename, uint16_t filename_len)
{ {
if (num >= max_friends_index) if (num >= max_friends_index)
return; return;
if (friends[num].chatwin == -1) { if (friends[num].chatwin == -1) {
if (num_active_windows() < MAX_WINDOWS_NUM) { if (get_num_active_windows() < MAX_WINDOWS_NUM) {
friends[num].chatwin = add_window(m, new_chat(m, friends[num].num)); friends[num].chatwin = add_window(m, new_chat(m, friends[num].num));
} else { } else {
tox_file_send_control(m, num, 1, filenum, TOX_FILECONTROL_KILL, 0, 0); tox_file_send_control(m, num, 1, filenum, TOX_FILECONTROL_KILL, 0, 0);
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'}; uint8_t nick[TOX_MAX_NAME_LENGTH];
tox_get_name(m, num, nick); int n_len = tox_get_name(m, num, nick);
nick[TOXIC_MAX_NAME_LENGTH] = '\0'; n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH - 1);
nick[n_len] = '\0';
prep_prompt_win(); uint8_t msg[MAX_STR_SIZE];
wattron(prompt->window, COLOR_PAIR(RED)); snprintf(msg, sizeof(msg), "* File transfer from %s failed: too many windows are open.", nick);
wprintw(prompt->window, "* File transfer from %s failed: too many windows are open.\n", nick); line_info_add(prompt, NULL, NULL, NULL, msg, SYS_MSG, 0, RED);
wattron(prompt->window, COLOR_PAIR(RED));
alert_window(prompt, WINDOW_ALERT_1, true); alert_window(prompt, WINDOW_ALERT_1, true);
} }
} }
} }
static void friendlist_onGroupInvite(ToxWindow *self, Tox *m, int num, uint8_t *group_pub_key) static void friendlist_onGroupInvite(ToxWindow *self, Tox *m, int32_t num, uint8_t *group_pub_key)
{ {
if (num >= max_friends_index) if (num >= max_friends_index)
return; return;
if (friends[num].chatwin == -1) { if (friends[num].chatwin == -1) {
if (num_active_windows() < MAX_WINDOWS_NUM) { if (get_num_active_windows() < MAX_WINDOWS_NUM) {
friends[num].chatwin = add_window(m, new_chat(m, friends[num].num)); friends[num].chatwin = add_window(m, new_chat(m, friends[num].num));
} else { } else {
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'}; uint8_t nick[TOX_MAX_NAME_LENGTH];
tox_get_name(m, num, nick); int n_len = tox_get_name(m, num, nick);
nick[TOXIC_MAX_NAME_LENGTH] = '\0'; n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH - 1);
nick[n_len] = '\0';
prep_prompt_win(); uint8_t msg[MAX_STR_SIZE];
wattron(prompt->window, COLOR_PAIR(RED)); snprintf(msg, sizeof(msg), "* Group chat invite from %s failed: too many windows are open.", nick);
wprintw(prompt->window, "* Group chat invite from %s failed: too many windows are open.\n", nick); line_info_add(prompt, NULL, NULL, NULL, msg, SYS_MSG, 0, RED);
wattron(prompt->window, COLOR_PAIR(RED));
alert_window(prompt, WINDOW_ALERT_1, true); alert_window(prompt, WINDOW_ALERT_1, true);
} }
@ -212,7 +268,7 @@ 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, int32_t f_num)
{ {
tox_del_friend(m, f_num); tox_del_friend(m, f_num);
memset(&friends[f_num], 0, sizeof(ToxicFriend)); memset(&friends[f_num], 0, sizeof(ToxicFriend));
@ -220,7 +276,7 @@ static void delete_friend(Tox *m, ToxWindow *self, int f_num, wint_t key)
int i; int i;
for (i = max_friends_index; i > 0; --i) { for (i = max_friends_index; i > 0; --i) {
if (friends[i-1].active) if (friends[i - 1].active)
break; break;
} }
@ -231,40 +287,91 @@ static void delete_friend(Tox *m, ToxWindow *self, int f_num, wint_t key)
if (num_friends && num_selected == num_friends) if (num_friends && num_selected == num_friends)
--num_selected; --num_selected;
sort_friendlist_index(m); sort_friendlist_index();
store_data(m, DATA_FILE); store_data(m, DATA_FILE);
} }
static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key) /* activates delete friend popup */
static void del_friend_activate(ToxWindow *self, Tox *m, int32_t f_num)
{
int x2, y2;
getmaxyx(self->window, y2, x2);
self->popup = newwin(3, 22 + TOXIC_MAX_NAME_LENGTH, 8, 8);
pendingdelete.active = true;
pendingdelete.num = f_num;
}
/* deactivates delete friend popup and deletes friend if instructed */
static void del_friend_deactivate(ToxWindow *self, Tox *m, wint_t key)
{
if (key == 'y')
delete_friend(m, pendingdelete.num);
memset(&pendingdelete, 0, sizeof(pendingdelete));
delwin(self->popup);
self->popup = NULL;
clear();
refresh();
}
static void draw_popup(ToxWindow *self, Tox *m)
{
if (self->popup == NULL)
return;
wattron(self->popup, A_BOLD);
box(self->popup, ACS_VLINE, ACS_HLINE);
wattroff(self->popup, A_BOLD);
wmove(self->popup, 1, 1);
wprintw(self->popup, "Delete contact ");
wattron(self->popup, A_BOLD);
wprintw(self->popup, "%s", friends[pendingdelete.num].name);
wattroff(self->popup, A_BOLD);
wprintw(self->popup, "? y/n");
wrefresh(self->popup);
}
static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
{ {
if (num_friends == 0) if (num_friends == 0)
return; return;
int f = friendlist_index[num_selected]; int f = friendlist_index[num_selected];
/* lock screen and force decision on deletion popup */
if (pendingdelete.active) {
if (key == 'y' || key == 'n')
del_friend_deactivate(self, m, key);
return;
}
if (key != ltr) {
if (key == '\n') { if (key == '\n') {
/* 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 if (num_active_windows() < MAX_WINDOWS_NUM) { } else if (get_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 { } else {
prep_prompt_win(); uint8_t *msg = "* Warning: Too many windows are open.";
wattron(prompt->window, COLOR_PAIR(RED)); line_info_add(prompt, NULL, NULL, NULL, msg, SYS_MSG, 0, RED);
wprintw(prompt->window, "* Warning: Too many windows are open.\n");
wattron(prompt->window, COLOR_PAIR(RED));
alert_window(prompt, WINDOW_ALERT_1, true); alert_window(prompt, WINDOW_ALERT_1, true);
} }
} else if (key == KEY_DC) { } else if (key == KEY_DC) {
delete_friend(m, self, f, key); del_friend_activate(self, m, f);
} else { } else {
select_friend(self, m, key); select_friend(self, m, key);
} }
}
} }
#define FLIST_OFST 4 /* Accounts for the lines at top */ #define FLIST_OFST 6 /* Accounts for space at top and bottom */
static void friendlist_onDraw(ToxWindow *self, Tox *m) static void friendlist_onDraw(ToxWindow *self, Tox *m)
{ {
@ -273,6 +380,9 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
int x2, y2; int x2, y2;
getmaxyx(self->window, y2, x2); getmaxyx(self->window, y2, x2);
uint64_t cur_time = get_unix_time();
struct tm cur_loc_tm = *localtime(&cur_time);
bool fix_statuses = x2 != self->x; /* true if window x axis has changed */ bool fix_statuses = x2 != self->x; /* true if window x axis has changed */
wattron(self->window, COLOR_PAIR(CYAN)); wattron(self->window, COLOR_PAIR(CYAN));
@ -287,13 +397,20 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
wprintw(self->window, "key.\n\n"); wprintw(self->window, "key.\n\n");
wattroff(self->window, COLOR_PAIR(CYAN)); wattroff(self->window, COLOR_PAIR(CYAN));
pthread_mutex_lock(&Winthread.lock);
int nf = tox_get_num_online_friends(m);
pthread_mutex_unlock(&Winthread.lock);
wattron(self->window, A_BOLD); wattron(self->window, A_BOLD);
wprintw(self->window, " Friends: %d/%d \n\n", tox_get_num_online_friends(m), num_friends); wprintw(self->window, " Online: ");
wattroff(self->window, A_BOLD); wattroff(self->window, A_BOLD);
wprintw(self->window, "%d/%d \n\n", nf, num_friends);
if ((y2 - FLIST_OFST) <= 0) /* don't allow division by zero */ if ((y2 - FLIST_OFST) <= 0) /* don't allow division by zero */
return; return;
int selected_num = 0;
/* Determine which portion of friendlist to draw based on current position */ /* Determine which portion of friendlist to draw based on current position */
int page = num_selected / (y2 - FLIST_OFST); int page = num_selected / (y2 - FLIST_OFST);
int start = (y2 - FLIST_OFST) * page; int start = (y2 - FLIST_OFST) * page;
@ -310,90 +427,171 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
wattron(self->window, A_BOLD); wattron(self->window, A_BOLD);
wprintw(self->window, " > "); wprintw(self->window, " > ");
wattroff(self->window, A_BOLD); wattroff(self->window, A_BOLD);
selected_num = f;
f_selected = true; f_selected = true;
} else { } else {
wprintw(self->window, " "); wprintw(self->window, " ");
} }
if (friends[f].online) { if (friends[f].online) {
TOX_USERSTATUS status = friends[f].status; uint8_t status = friends[f].status;
int colour = WHITE; int colour = WHITE;
switch (status) { switch (status) {
case TOX_USERSTATUS_NONE: case TOX_USERSTATUS_NONE:
colour = GREEN; colour = GREEN;
break; break;
case TOX_USERSTATUS_AWAY: case TOX_USERSTATUS_AWAY:
colour = YELLOW; colour = YELLOW;
break; break;
case TOX_USERSTATUS_BUSY: case TOX_USERSTATUS_BUSY:
colour = RED; colour = RED;
break; break;
case TOX_USERSTATUS_INVALID:
colour = MAGENTA;
break;
} }
wprintw(self->window, "[");
wattron(self->window, COLOR_PAIR(colour) | A_BOLD); wattron(self->window, COLOR_PAIR(colour) | A_BOLD);
wprintw(self->window, "O"); wprintw(self->window, "O ");
wattroff(self->window, COLOR_PAIR(colour) | A_BOLD); wattroff(self->window, COLOR_PAIR(colour) | A_BOLD);
wprintw(self->window, "]");
if (f_selected) if (f_selected)
wattron(self->window, COLOR_PAIR(BLUE));
wattron(self->window, A_BOLD); wattron(self->window, A_BOLD);
wprintw(self->window, "%s", friends[f].name); wprintw(self->window, "%s", friends[f].name);
wattroff(self->window, A_BOLD);
if (f_selected) if (f_selected)
wattroff(self->window, A_BOLD); wattroff(self->window, COLOR_PAIR(BLUE));
/* 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_get_status_message(m, friends[f].num, statusmsg, TOX_MAX_STATUSMESSAGE_LENGTH);
pthread_mutex_lock(&Winthread.lock);
uint16_t s_len = tox_get_status_message(m, friends[f].num, statusmsg,
TOX_MAX_STATUSMESSAGE_LENGTH);
pthread_mutex_unlock(&Winthread.lock);
friends[f].statusmsg_len = s_len;
friends[f].statusmsg[s_len] = '\0';
snprintf(friends[f].statusmsg, sizeof(friends[f].statusmsg), "%s", statusmsg); snprintf(friends[f].statusmsg, sizeof(friends[f].statusmsg), "%s", statusmsg);
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 = x2 - getcurx(self->window) - 4; uint16_t maxlen = x2 - getcurx(self->window) - 2;
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, "...");
friends[f].statusmsg[maxlen] = '\0'; friends[f].statusmsg[maxlen] = '\0';
friends[f].statusmsg_len = maxlen; friends[f].statusmsg_len = maxlen;
} }
wprintw(self->window, " (%s)\n", friends[f].statusmsg); if (friends[f].statusmsg[0])
wprintw(self->window, " %s", friends[f].statusmsg);
wprintw(self->window, "\n");
} else { } else {
wprintw(self->window, "["); wprintw(self->window, "o ");
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, COLOR_PAIR(BLUE));
wprintw(self->window, "%s\n", friends[f].name); wattron(self->window, A_BOLD);
wprintw(self->window, "%s", friends[f].name);
wattroff(self->window, A_BOLD);
if (f_selected) if (f_selected)
wattroff(self->window, A_BOLD); wattroff(self->window, COLOR_PAIR(BLUE));
uint64_t last_seen = friends[f].last_online.last_on;
if (last_seen != 0) {
int day_dist = (cur_loc_tm.tm_yday - friends[f].last_online.tm.tm_yday) % 365;
const uint8_t *hourmin = friends[f].last_online.hour_min_str;
switch (day_dist) {
case 0:
wprintw(self->window, " Last seen: Today %s\n", hourmin);
break;
case 1:
wprintw(self->window, " Last seen: Yesterday %s\n", hourmin);
break;
default:
wprintw(self->window, " Last seen: %d days ago\n", day_dist);
break;
}
} else {
wprintw(self->window, " Last seen: Never\n");
}
} }
} }
} }
self->x = x2; self->x = x2;
wrefresh(self->window); wrefresh(self->window);
draw_popup(self, m);
if (num_friends) {
wmove(self->window, y2 - 1, 1);
wattron(self->window, A_BOLD);
wprintw(self->window, "ID: ");
wattroff(self->window, A_BOLD);
int i;
for (i = 0; i < TOX_CLIENT_ID_SIZE; ++i)
wprintw(self->window, "%02X", friends[selected_num].pub_key[i] & 0xff);
}
} }
void disable_chatwin(int f_num) void disable_chatwin(int32_t f_num)
{ {
friends[f_num].chatwin = -1; friends[f_num].chatwin = -1;
} }
static void friendlist_onInit(ToxWindow *self, Tox *m) #ifdef _SUPPORT_AUDIO
static void friendlist_onAv(ToxWindow *self, ToxAv *av, int call_index)
{ {
int id = toxav_get_peer_id(av, call_index, 0);
/*id++;*/
if ( id != ErrorInternal && id >= max_friends_index)
return;
Tox *m = toxav_get_tox(av);
if (friends[id].chatwin == -1) {
if (get_num_active_windows() < MAX_WINDOWS_NUM) {
friends[id].chatwin = add_window(m, new_chat(m, friends[id].num));
} else {
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
int n_len = tox_get_name(m, id, nick);
n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH - 1);
nick[n_len] = '\0';
uint8_t msg[MAX_STR_SIZE];
snprintf(msg, sizeof(msg), "Audio action from: %s!", nick);
line_info_add(prompt, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
uint8_t *errmsg = "* Warning: Too many windows are open.";
line_info_add(prompt, NULL, NULL, NULL, errmsg, SYS_MSG, 0, RED);
alert_window(prompt, WINDOW_ALERT_0, true);
}
}
} }
#endif /* _SUPPORT_AUDIO */
ToxWindow new_friendlist(void) ToxWindow new_friendlist(void)
{ {
@ -404,17 +602,32 @@ ToxWindow new_friendlist(void)
ret.onKey = &friendlist_onKey; ret.onKey = &friendlist_onKey;
ret.onDraw = &friendlist_onDraw; ret.onDraw = &friendlist_onDraw;
ret.onInit = &friendlist_onInit;
ret.onFriendAdded = &friendlist_onFriendAdded; ret.onFriendAdded = &friendlist_onFriendAdded;
ret.onMessage = &friendlist_onMessage; ret.onMessage = &friendlist_onMessage;
ret.onConnectionChange = &friendlist_onConnectionChange; ret.onConnectionChange = &friendlist_onConnectionChange;
ret.onAction = &friendlist_onMessage; // Action has identical behaviour to message ret.onAction = &friendlist_onMessage; /* Action has identical behaviour to message */
ret.onNickChange = &friendlist_onNickChange; ret.onNickChange = &friendlist_onNickChange;
ret.onStatusChange = &friendlist_onStatusChange; ret.onStatusChange = &friendlist_onStatusChange;
ret.onStatusMessageChange = &friendlist_onStatusMessageChange; ret.onStatusMessageChange = &friendlist_onStatusMessageChange;
ret.onFileSendRequest = &friendlist_onFileSendRequest; ret.onFileSendRequest = &friendlist_onFileSendRequest;
ret.onGroupInvite = &friendlist_onGroupInvite; ret.onGroupInvite = &friendlist_onGroupInvite;
#ifdef _SUPPORT_AUDIO
ret.onInvite = &friendlist_onAv;
ret.onRinging = &friendlist_onAv;
ret.onStarting = &friendlist_onAv;
ret.onEnding = &friendlist_onAv;
ret.onError = &friendlist_onAv;
ret.onStart = &friendlist_onAv;
ret.onCancel = &friendlist_onAv;
ret.onReject = &friendlist_onAv;
ret.onEnd = &friendlist_onAv;
ret.onRequestTimeout = &friendlist_onAv;
ret.onPeerTimeout = &friendlist_onAv;
ret.call_index = -1;
#endif /* _SUPPORT_AUDIO */
strcpy(ret.name, "friends"); strcpy(ret.name, "friends");
return ret; return ret;
} }

View File

@ -1,8 +1,39 @@
/* 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 <time.h>
#include "toxic_windows.h" #include "toxic_windows.h"
#define TIME_STR_SIZE 16
struct LastOnline {
uint64_t last_on;
struct tm tm;
uint8_t hour_min_str[TIME_STR_SIZE]; /* holds 12-hour time string e.g. "10:43 PM" */
};
typedef struct { typedef struct {
uint8_t name[TOX_MAX_NAME_LENGTH]; uint8_t name[TOX_MAX_NAME_LENGTH];
uint16_t namelength; uint16_t namelength;
@ -10,20 +41,24 @@ typedef struct {
uint16_t statusmsg_len; uint16_t statusmsg_len;
uint8_t pending_groupchat[TOX_CLIENT_ID_SIZE]; uint8_t pending_groupchat[TOX_CLIENT_ID_SIZE];
uint8_t pub_key[TOX_CLIENT_ID_SIZE]; uint8_t pub_key[TOX_CLIENT_ID_SIZE];
int num; int32_t num;
int chatwin; int chatwin;
bool active; bool active;
bool online; bool online;
bool is_typing; uint8_t is_typing;
TOX_USERSTATUS status; bool logging_on; /* saves preference for friend irrespective of chat windows */
uint8_t status;
struct LastOnline last_online;
struct FileReceiver file_receiver; struct FileReceiver file_receiver;
} ToxicFriend; } ToxicFriend;
ToxWindow new_friendlist(void); ToxWindow new_friendlist(void);
void disable_chatwin(int f_num); void disable_chatwin(int32_t f_num);
int get_friendnum(uint8_t *name); int get_friendnum(uint8_t *name);
void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int32_t num, bool sort);
/* sorts friendlist_index first by connection status then alphabetically */ /* sorts friendlist_index first by connection status then alphabetically */
void sort_friendlist_index(Tox *m); void sort_friendlist_index(void);
#endif /* end of include guard: FRIENDLIST_H_53I41IM */ #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
@ -12,6 +30,8 @@
#include "toxic_windows.h" #include "toxic_windows.h"
#include "misc_tools.h" #include "misc_tools.h"
#include "friendlist.h" #include "friendlist.h"
#include "log.h"
#include "line_info.h"
extern char *DATA_FILE; extern char *DATA_FILE;
extern ToxWindow *prompt; extern ToxWindow *prompt;
@ -24,30 +44,34 @@ extern uint8_t num_frnd_requests;
/* 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])
{ {
/* check arguments */ uint8_t *msg;
if (argc != 1) { if (argc != 1) {
wprintw(window, "Invalid syntax.\n"); msg = "Invalid syntax.";
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
return; return;
} }
int req = atoi(argv[1]); int req = atoi(argv[1]);
if ((req == 0 && strcmp(argv[1], "0"))|| req >= MAX_FRIENDS_NUM) { if ((req == 0 && strcmp(argv[1], "0")) || req >= MAX_FRIENDS_NUM) {
wprintw(window, "No pending friend request with that number.\n"); msg = "No pending friend request with that number.";
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
return; return;
} }
if (!strlen(pending_frnd_requests[req])) { if (!strlen(pending_frnd_requests[req])) {
wprintw(window, "No pending friend request with that number.\n"); msg = "No pending friend request with that number.";
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
return; return;
} }
int friendnum = tox_add_friend_norequest(m, pending_frnd_requests[req]); int32_t friendnum = tox_add_friend_norequest(m, pending_frnd_requests[req]);
if (friendnum == -1) if (friendnum == -1)
wprintw(window, "Failed to add friend.\n"); msg = "Failed to add friend.";
else { else {
wprintw(window, "Friend request accepted.\n"); msg = "Friend request accepted.";
on_friendadded(m, friendnum, true); on_friendadded(m, friendnum, true);
} }
@ -56,17 +80,21 @@ void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
int i; int i;
for (i = num_frnd_requests; i > 0; --i) { for (i = num_frnd_requests; i > 0; --i) {
if (!strlen(pending_frnd_requests[i-1])) if (!strlen(pending_frnd_requests[i - 1]))
break; break;
} }
num_frnd_requests = i; num_frnd_requests = i;
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
} }
void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{ {
uint8_t *errmsg;
if (argc < 1) { if (argc < 1) {
wprintw(window, "Invalid syntax.\n"); errmsg = "Invalid syntax.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return; return;
} }
@ -77,20 +105,23 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
uint8_t *temp = argv[2]; uint8_t *temp = argv[2];
if (temp[0] != '\"') { if (temp[0] != '\"') {
wprintw(window, "Message must be enclosed in quotes.\n"); errmsg = "Message must be enclosed in quotes.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return; return;
} }
temp[strlen(++temp)-1] = L'\0'; temp[strlen(++temp) - 1] = L'\0';
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_get_self_name(m, selfname, TOX_MAX_NAME_LENGTH); uint16_t n_len = tox_get_self_name(m, selfname);
selfname[n_len] = '\0';
snprintf(msg, sizeof(msg), "Hello, my name is %s. Care to Tox?", selfname); snprintf(msg, sizeof(msg), "Hello, my name is %s. Care to Tox?", selfname);
} }
if (strlen(id) != 2 * TOX_FRIEND_ADDRESS_SIZE) { if (strlen(id) != 2 * TOX_FRIEND_ADDRESS_SIZE) {
wprintw(window, "Invalid ID length.\n"); errmsg = "Invalid ID length.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return; return;
} }
@ -105,7 +136,8 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
xx[2] = '\0'; xx[2] = '\0';
if (sscanf(xx, "%02x", &x) != 1) { if (sscanf(xx, "%02x", &x) != 1) {
wprintw(window, "Invalid ID.\n"); errmsg = "Invalid ID.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return; return;
} }
@ -116,58 +148,76 @@ 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_add_friend(m, id_bin, msg, strlen(msg) + 1); int32_t f_num = tox_add_friend(m, id_bin, msg, strlen(msg));
switch (f_num) { switch (f_num) {
case TOX_FAERR_TOOLONG: case TOX_FAERR_TOOLONG:
wprintw(window, "Message is too long.\n"); errmsg = "Message is too long.";
break; break;
case TOX_FAERR_NOMESSAGE: case TOX_FAERR_NOMESSAGE:
wprintw(window, "Please add a message to your request.\n"); errmsg = "Please add a message to your request.";
break; break;
case TOX_FAERR_OWNKEY: case TOX_FAERR_OWNKEY:
wprintw(window, "That appears to be your own ID.\n"); errmsg = "That appears to be your own ID.";
break; break;
case TOX_FAERR_ALREADYSENT: case TOX_FAERR_ALREADYSENT:
wprintw(window, "Friend request has already been sent.\n"); errmsg = "Friend request has already been sent.";
break; break;
case TOX_FAERR_UNKNOWN: case TOX_FAERR_UNKNOWN:
wprintw(window, "Undefined error when adding friend.\n"); errmsg = "Undefined error when adding friend.";
break; break;
case TOX_FAERR_BADCHECKSUM: case TOX_FAERR_BADCHECKSUM:
wprintw(window, "Bad checksum in address.\n"); errmsg = "Bad checksum in address.";
break; break;
case TOX_FAERR_SETNEWNOSPAM: case TOX_FAERR_SETNEWNOSPAM:
wprintw(window, "Nospam was different (is this contact already added?)\n"); errmsg = "Nospam was different (is this contact already added?";
break; break;
default: default:
wprintw(window, "Friend request sent.\n"); errmsg = "Friend request sent.";
on_friendadded(m, f_num, true); on_friendadded(m, f_num, true);
break; break;
} }
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
} }
void cmd_clear(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_clear(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{ {
line_info_clear(self->chatwin->hst);
wclear(window); wclear(window);
wprintw(window, "\n\n");
if (self->is_prompt) {
int y2, x2;
getmaxyx(window, y2, x2);
wmove(self->chatwin->history, y2 - 1, 2);
}
} }
void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{ {
uint8_t *errmsg;
/* check arguments */ /* check arguments */
if (argc != 3) { if (argc != 3) {
wprintw(window, "Invalid syntax.\n"); errmsg = "Invalid syntax.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return; return;
} }
tox_IP_Port dht;
char *ip = argv[1]; char *ip = argv[1];
char *port = argv[2]; char *port = argv[2];
char *key = argv[3]; char *key = argv[3];
if (atoi(port) == 0) { if (atoi(port) == 0) {
wprintw(window, "Invalid syntax.\n"); errmsg = "Invalid syntax.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return; return;
} }
@ -179,27 +229,80 @@ 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_active_windows() >= MAX_WINDOWS_NUM) { uint8_t *errmsg;
wattron(window, COLOR_PAIR(RED));
wprintw(window, " * Warning: Too many windows are open.\n"); if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
wattron(window, COLOR_PAIR(RED)); errmsg = " * Warning: Too many windows are open.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, RED);
return; return;
} }
int groupnum = tox_add_groupchat(m); int groupnum = tox_add_groupchat(m);
if (groupnum == -1) { if (groupnum == -1) {
wprintw(window, "Group chat instance failed to initialize.\n"); errmsg = "Group chat instance failed to initialize.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return; return;
} }
if (init_groupchat_win(prompt, m, groupnum) == -1) { if (init_groupchat_win(prompt, m, groupnum) == -1) {
wprintw(window, "Group chat window failed to initialize.\n"); errmsg = "Group chat window failed to initialize.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
tox_del_groupchat(m, groupnum); tox_del_groupchat(m, groupnum);
return; return;
} }
wprintw(window, "Group chat created as %d.\n", groupnum); uint8_t msg[MAX_STR_SIZE];
snprintf(msg, sizeof(msg), "Group chat created as %d.", groupnum);
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
}
void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
uint8_t *msg;
struct chatlog *log = self->chatwin->log;
if (argc == 0) {
if (log->log_on)
msg = "Logging for this window is ON. Type \"/log off\" to disable.";
else
msg = "Logging for this window is OFF. Type \"/log on\" to enable.";
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
return;
}
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, log);
} else if (self->is_prompt) {
uint8_t myid[TOX_FRIEND_ADDRESS_SIZE];
tox_get_address(m, myid);
log_enable(self->name, myid, log);
} else if (self->is_groupchat) {
log_enable(self->name, NULL, log);
}
msg = "Logging enabled";
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
return;
} else if (!strcmp(swch, "0") || !strcmp(swch, "off")) {
if (self->is_chat)
friends[self->num].logging_on = false;
log_disable(log);
msg = "Logging disabled";
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
return;
}
msg = "Invalid option. Use \"/log on\" and \"/log off\" to toggle logging.";
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
} }
void cmd_myid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_myid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
@ -216,14 +319,17 @@ void cmd_myid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
strcat(id, xx); strcat(id, xx);
} }
wprintw(window, "%s\n", id); line_info_add(self, NULL, NULL, NULL, id, SYS_MSG, 0, 0);
} }
void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{ {
uint8_t *errmsg;
/* check arguments */ /* check arguments */
if (argc < 1) { if (argc < 1) {
wprintw(window, "Invalid name.\n"); errmsg = "Invalid name.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return; return;
} }
@ -237,65 +343,90 @@ void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
} }
if (!valid_nick(nick)) { if (!valid_nick(nick)) {
wprintw(window, "Invalid name.\n"); errmsg = "Invalid name.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return; return;
} }
if (len > TOXIC_MAX_NAME_LENGTH) { len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
nick[TOXIC_MAX_NAME_LENGTH] = L'\0';
len = TOXIC_MAX_NAME_LENGTH;
}
tox_set_name(m, nick, len+1); nick[len] = L'\0';
prompt_update_nick(prompt, nick, len+1);
tox_set_name(m, nick, len);
prompt_update_nick(prompt, nick, len);
store_data(m, DATA_FILE); store_data(m, DATA_FILE);
} }
void cmd_note(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_note(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{ {
uint8_t *errmsg;
if (argc < 1) { if (argc < 1) {
wprintw(window, "Wrong number of arguments.\n"); errmsg = "Wrong number of arguments.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return; return;
} }
uint8_t *msg = argv[1]; uint8_t *msg = argv[1];
if (msg[0] != '\"') { if (msg[0] != '\"') {
wprintw(window, "Note must be enclosed in quotes.\n"); errmsg = "Note must be enclosed in quotes.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return; return;
} }
msg[strlen(++msg)-1] = L'\0'; msg[strlen(++msg) - 1] = L'\0';
uint16_t len = strlen(msg) + 1; uint16_t len = strlen(msg);
tox_set_status_message(m, msg, len); tox_set_status_message(m, msg, len);
prompt_update_statusmessage(prompt, msg, len); prompt_update_statusmessage(prompt, msg, len);
} }
void cmd_prompt_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_prompt_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{ {
wclear(window); struct history *hst = self->chatwin->hst;
wattron(window, COLOR_PAIR(CYAN) | A_BOLD); line_info_clear(hst);
wprintw(window, "\n\nGlobal commands:\n"); struct line_info *start = hst->line_start;
wattroff(window, COLOR_PAIR(CYAN) | A_BOLD);
wprintw(window, " /add <id> <msg> : Add friend with optional message\n"); uint8_t *msg = "Global commands:";
wprintw(window, " /accept <n> : Accept friend request\n"); line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 1, CYAN);
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, " /note <msg> : Set a personal note\n");
wprintw(window, " /nick <nick> : Set your nickname\n");
wprintw(window, " /groupchat : Create a group chat\n");
wprintw(window, " /myid : Print your ID\n");
wprintw(window, " /quit or /exit : Exit Toxic\n");
wprintw(window, " /help : Print this message again\n");
wprintw(window, " /clear : Clear the window\n");
wattron(window, COLOR_PAIR(CYAN) | A_BOLD); #ifdef _SUPPORT_AUDIO
wprintw(window, " * Argument messages must be enclosed in quotation marks.\n"); #define NUMLINES 14
wprintw(window, " * Use ctrl-o and ctrl-p to navigate through the tabs.\n\n"); #else
wattroff(window, COLOR_PAIR(CYAN) | A_BOLD); #define NUMLINES 12
#endif
uint8_t lines[NUMLINES][MAX_STR_SIZE] = {
{ " /add <id> <msg> : Add friend with optional message" },
{ " /accept <n> : Accept friend request" },
{ " /connect <ip> <port> <key> : Manually connect to a DHT node" },
{ " /status <type> <msg> : Set status with optional note" },
{ " /note <msg> : Set a personal note" },
{ " /nick <nick> : Set your nickname" },
{ " /log <on> or <off> : Enable/disable logging" },
{ " /groupchat : Create a group chat" },
{ " /myid : Print your ID" },
{ " /help : Print this message again" },
{ " /clear : Clear window history" },
{ " /quit or /exit : Exit Toxic" },
#ifdef _SUPPORT_AUDIO
{ " /lsdev <type> : List devices where type: in|out" },
{ " /sdev <type> <id> : Set active device" },
#endif /* _SUPPORT_AUDIO */
};
int i;
for (i = 0; i < NUMLINES; ++i)
line_info_add(self, NULL, NULL, NULL, lines[i], SYS_MSG, 0, 0);
msg = " * Argument messages must be enclosed in quotation marks.\n"
" * Use ctrl-o and ctrl-p to navigate through the tabs.\n";
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 1, CYAN);
hst->line_start = start;
} }
void cmd_quit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_quit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
@ -306,22 +437,25 @@ void cmd_quit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{ {
uint8_t *msg = NULL; uint8_t *msg = NULL;
uint8_t *errmsg;
if (argc >= 2) { if (argc >= 2) {
msg = argv[2]; msg = argv[2];
if (msg[0] != '\"') { if (msg[0] != '\"') {
wprintw(window, "Note must be enclosed in quotes.\n"); errmsg = "Note must be enclosed in quotes.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return; return;
} }
} else if (argc != 1) { } else if (argc != 1) {
wprintw(window, "Wrong number of arguments.\n"); errmsg = "Wrong number of arguments.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return; return;
} }
char *status = argv[1]; char *status = argv[1];
int len = strlen(status); int len = strlen(status);
char l_status[len+1]; char l_status[len + 1];
int i; int i;
for (i = 0; i <= len; ++i) for (i = 0; i <= len; ++i)
@ -336,7 +470,8 @@ void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
else if (!strcmp(l_status, "busy")) else if (!strcmp(l_status, "busy"))
status_kind = TOX_USERSTATUS_BUSY; status_kind = TOX_USERSTATUS_BUSY;
else { else {
wprintw(window, "Invalid status. Valid statuses are: online, busy and away.\n"); errmsg = "Invalid status. Valid statuses are: online, busy and away.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return; return;
} }
@ -344,8 +479,8 @@ void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
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);
tox_set_status_message(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,9 +25,15 @@ 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]);
void cmd_prompt_help(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_prompt_help(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_quit(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_quit(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_status(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_status(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
#ifdef _SUPPORT_AUDIO
void cmd_list_devices(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_change_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
#endif /* _SUPPORT_AUDIO */

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
@ -16,6 +34,9 @@
#include "groupchat.h" #include "groupchat.h"
#include "prompt.h" #include "prompt.h"
#include "toxic_strings.h" #include "toxic_strings.h"
#include "log.h"
#include "line_info.h"
#include "settings.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);
@ -23,8 +44,10 @@ extern int store_data(Tox *m, char *path);
static GroupChat groupchats[MAX_WINDOWS_NUM]; static GroupChat groupchats[MAX_WINDOWS_NUM];
static int max_groupchat_index = 0; static int max_groupchat_index = 0;
extern struct user_settings *user_settings;
/* temporary until group chats have unique commands */ /* temporary until group chats have unique commands */
extern glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE]; extern const uint8_t 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)
{ {
@ -37,9 +60,14 @@ int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum)
groupchats[i].num_peers = 0; groupchats[i].num_peers = 0;
groupchats[i].peer_names = malloc(sizeof(uint8_t) * TOX_MAX_NAME_LENGTH); groupchats[i].peer_names = malloc(sizeof(uint8_t) * TOX_MAX_NAME_LENGTH);
groupchats[i].oldpeer_names = malloc(sizeof(uint8_t) * TOX_MAX_NAME_LENGTH); groupchats[i].oldpeer_names = malloc(sizeof(uint8_t) * TOX_MAX_NAME_LENGTH);
groupchats[i].peer_name_lengths = malloc(sizeof(uint16_t));
groupchats[i].oldpeer_name_lengths = malloc(sizeof(uint16_t));
memset(groupchats[i].peer_names, 0, sizeof(uint8_t) * TOX_MAX_NAME_LENGTH);
memset(groupchats[i].peer_name_lengths, 0, sizeof(uint16_t));
/* temp fix */
memcpy(&groupchats[i].oldpeer_names[0], UNKNOWN_NAME, sizeof(UNKNOWN_NAME)); memcpy(&groupchats[i].oldpeer_names[0], UNKNOWN_NAME, sizeof(UNKNOWN_NAME));
groupchats[i].oldpeer_name_lengths[0] = (uint16_t) strlen(UNKNOWN_NAME);
set_active_window(groupchats[i].chatwin); set_active_window(groupchats[i].chatwin);
@ -53,45 +81,79 @@ 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);
line_info_cleanup(ctx->hst);
delwin(ctx->linewin);
del_window(self);
free(ctx->log);
free(ctx->hst);
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);
free(groupchats[groupnum].oldpeer_names); free(groupchats[groupnum].oldpeer_names);
free(groupchats[groupnum].peer_name_lengths);
free(groupchats[groupnum].oldpeer_name_lengths);
memset(&groupchats[groupnum], 0, sizeof(GroupChat)); memset(&groupchats[groupnum], 0, sizeof(GroupChat));
int i; int i;
for (i = max_groupchat_index; i > 0; --i) { for (i = max_groupchat_index; i > 0; --i) {
if (groupchats[i-1].active) if (groupchats[i - 1].active)
break; break;
} }
max_groupchat_index = i; max_groupchat_index = i;
kill_groupchat_window(self);
} }
static void print_groupchat_help(ChatContext *ctx) static void print_groupchat_help(ToxWindow *self)
{ {
wattron(ctx->history, COLOR_PAIR(CYAN) | A_BOLD); struct history *hst = self->chatwin->hst;
wprintw(ctx->history, "Group chat commands:\n"); line_info_clear(hst);
wattroff(ctx->history, COLOR_PAIR(CYAN) | A_BOLD); struct line_info *start = hst->line_start;
wprintw(ctx->history, " /add <id> <msg> : Add friend with optional message\n"); uint8_t *msg = "Group chat commands:";
wprintw(ctx->history, " /status <type> <msg>: Set your status with optional note\n"); line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 1, CYAN);
wprintw(ctx->history, " /note <msg> : Set a personal note\n");
wprintw(ctx->history, " /nick <nick> : Set your nickname\n");
wprintw(ctx->history, " /groupchat : Create a group chat\n");
wprintw(ctx->history, " /myid : Print your ID\n");
wprintw(ctx->history, " /clear : Clear the screen\n");
wprintw(ctx->history, " /close : Close the current group chat\n");
wprintw(ctx->history, " /quit or /exit : Exit Toxic\n");
wprintw(ctx->history, " /help : Print this message again\n");
wattron(ctx->history, COLOR_PAIR(CYAN) | A_BOLD); #define NUMLINES 9
wprintw(ctx->history, " * Argument messages must be enclosed in quotation marks.\n");
wprintw(ctx->history, " * Scroll peer list with the Page Up/Page Down keys.\n\n"); uint8_t lines[NUMLINES][MAX_STR_SIZE] = {
wattroff(ctx->history, COLOR_PAIR(CYAN) | A_BOLD);
{ " /add <id> <msg> : Add friend with optional message" },
{ " /status <type> <msg>: Set your status with optional note" },
{ " /note <msg> : Set a personal note" },
{ " /nick <nick> : Set your nickname" },
{ " /groupchat : Create a group chat" },
{ " /log <on> or <off> : Enable/disable logging" },
{ " /close : Close the current group chat" },
{ " /help : Print this message again" },
{ " /help global : Show a list of global commands" },
};
int i;
for (i = 0; i < NUMLINES; ++i)
line_info_add(self, NULL, NULL, NULL, lines[i], SYS_MSG, 0, 0);
msg = " * Use ESC key to toggle history scroll mode";
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 1, CYAN);
msg = " * Scroll peer list with the Page Up/Page Down keys.\n";
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 1, CYAN);
msg = " * Notice, some friends will be missing names while finding peers\n";
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 1, 0);
hst->line_start = start;
} }
static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, int groupnum, int peernum, static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, int groupnum, int peernum,
@ -100,18 +162,24 @@ static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, int groupnum, int
if (self->num != groupnum) if (self->num != groupnum)
return; return;
msg[len] = '\0';
ChatContext *ctx = self->chatwin; ChatContext *ctx = self->chatwin;
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'}; uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
tox_group_peername(m, groupnum, peernum, nick); int n_len = tox_group_peername(m, groupnum, peernum, nick);
nick[TOXIC_MAX_NAME_LENGTH] = '\0'; /* enforce client max name length */
n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH - 1); /* enforce client max name length */
nick[n_len] = '\0';
/* check if message contains own name and alert appropriately */ /* check if message contains own name and alert appropriately */
int alert_type = WINDOW_ALERT_1; int alert_type = WINDOW_ALERT_1;
bool beep = false; bool beep = false;
uint8_t selfnick[TOX_MAX_NAME_LENGTH] = {'\0'}; uint8_t selfnick[TOX_MAX_NAME_LENGTH];
tox_get_self_name(m, selfnick, TOX_MAX_NAME_LENGTH); uint16_t sn_len = tox_get_self_name(m, selfnick);
selfnick[sn_len] = '\0';
int nick_clr = strcmp(nick, selfnick) == 0 ? GREEN : CYAN; int nick_clr = strcmp(nick, selfnick) == 0 ? GREEN : CYAN;
bool nick_match = strcasestr(msg, selfnick) && strncmp(selfnick, nick, TOXIC_MAX_NAME_LENGTH); bool nick_match = strcasestr(msg, selfnick) && strncmp(selfnick, nick, TOXIC_MAX_NAME_LENGTH);
@ -124,18 +192,11 @@ static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, int groupnum, int
alert_window(self, alert_type, beep); alert_window(self, alert_type, beep);
print_time(ctx->history); uint8_t timefrmt[TIME_STR_SIZE];
wattron(ctx->history, COLOR_PAIR(nick_clr)); get_time_str(timefrmt);
wprintw(ctx->history, "%s: ", nick);
wattroff(ctx->history, COLOR_PAIR(nick_clr));
if (msg[0] == '>') { line_info_add(self, timefrmt, nick, NULL, msg, IN_MSG, 0, nick_clr);
wattron(ctx->history, COLOR_PAIR(GREEN)); write_to_log(msg, nick, ctx->log, false);
wprintw(ctx->history, "%s\n", msg);
wattroff(ctx->history, COLOR_PAIR(GREEN));
} else {
wprintw(ctx->history, "%s\n", msg);
}
} }
static void groupchat_onGroupAction(ToxWindow *self, Tox *m, int groupnum, int peernum, uint8_t *action, static void groupchat_onGroupAction(ToxWindow *self, Tox *m, int groupnum, int peernum, uint8_t *action,
@ -144,14 +205,17 @@ static void groupchat_onGroupAction(ToxWindow *self, Tox *m, int groupnum, int p
if (self->num != groupnum) if (self->num != groupnum)
return; return;
action[len] = '\0';
ChatContext *ctx = self->chatwin; ChatContext *ctx = self->chatwin;
/* check if message contains own name and alert appropriately */ /* check if message contains own name and alert appropriately */
int alert_type = WINDOW_ALERT_1; int alert_type = WINDOW_ALERT_1;
bool beep = false; bool beep = false;
uint8_t selfnick[TOX_MAX_NAME_LENGTH] = {'\0'}; uint8_t selfnick[TOX_MAX_NAME_LENGTH];
tox_get_self_name(m, selfnick, TOX_MAX_NAME_LENGTH); uint16_t n_len = tox_get_self_name(m, selfnick);
selfnick[n_len] = '\0';
bool nick_match = strcasestr(action, selfnick); bool nick_match = strcasestr(action, selfnick);
@ -163,45 +227,65 @@ static void groupchat_onGroupAction(ToxWindow *self, Tox *m, int groupnum, int p
alert_window(self, alert_type, beep); 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); n_len = tox_group_peername(m, groupnum, peernum, nick);
nick[TOXIC_MAX_NAME_LENGTH] = '\0'; /* enforce client max name length */
print_time(ctx->history); n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH - 1);
wattron(ctx->history, COLOR_PAIR(YELLOW)); nick[n_len] = '\0';
wprintw(ctx->history, "* %s %s\n", nick, action);
wattroff(ctx->history, COLOR_PAIR(YELLOW)); uint8_t timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt);
line_info_add(self, timefrmt, nick, NULL, action, ACTION, 0, 0);
write_to_log(action, nick, ctx->log, true);
} }
/* Puts two copies of peerlist in chat instance */ /* Puts two copies of peerlist/lengths in chat instance */
static void copy_peernames(int gnum, int npeers, uint8_t tmp_peerlist[][TOX_MAX_NAME_LENGTH]) static void copy_peernames(int gnum, uint8_t peerlist[][TOX_MAX_NAME_LENGTH], uint16_t lengths[], int npeers)
{ {
/* Assumes these are initiated in init_groupchat_win */ /* Assumes these are initiated in init_groupchat_win */
free(groupchats[gnum].peer_names); free(groupchats[gnum].peer_names);
free(groupchats[gnum].oldpeer_names); free(groupchats[gnum].oldpeer_names);
free(groupchats[gnum].peer_name_lengths);
free(groupchats[gnum].oldpeer_name_lengths);
int N = TOX_MAX_NAME_LENGTH; int N = TOX_MAX_NAME_LENGTH;
groupchats[gnum].peer_names = malloc(sizeof(uint8_t) * npeers * N); groupchats[gnum].peer_names = malloc(sizeof(uint8_t) * npeers * N);
groupchats[gnum].oldpeer_names = malloc(sizeof(uint8_t) * npeers * N); groupchats[gnum].oldpeer_names = malloc(sizeof(uint8_t) * npeers * N);
groupchats[gnum].peer_name_lengths = malloc(sizeof(uint16_t) * npeers);
groupchats[gnum].oldpeer_name_lengths = malloc(sizeof(uint16_t) * npeers);
if (groupchats[gnum].peer_names == NULL || groupchats[gnum].oldpeer_names == NULL) { memset(groupchats[gnum].peer_names, 0, sizeof(uint8_t) * npeers * N);
memset(groupchats[gnum].peer_name_lengths, 0, sizeof(uint16_t) * npeers);
if (groupchats[gnum].peer_names == NULL || groupchats[gnum].oldpeer_names == NULL
|| groupchats[gnum].peer_name_lengths == NULL || groupchats[gnum].oldpeer_name_lengths == NULL) {
endwin(); endwin();
fprintf(stderr, "malloc() failed. Aborting...\n"); fprintf(stderr, "malloc() failed. Aborting...\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
uint16_t unknown_len = strlen(UNKNOWN_NAME);
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(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));
groupchats[gnum].peer_name_lengths[i] = unknown_len;
} else { } else {
memcpy(&groupchats[gnum].peer_names[i*N], tmp_peerlist[i], N); memcpy(&groupchats[gnum].peer_names[i * N], peerlist[i], N);
groupchats[gnum].peer_names[i*N+TOXIC_MAX_NAME_LENGTH] = '\0'; uint16_t n_len = lengths[i];
n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH - 1);
groupchats[gnum].peer_names[i * N + n_len] = '\0';
groupchats[gnum].peer_name_lengths[i] = n_len;
} }
} }
memcpy(groupchats[gnum].oldpeer_names, groupchats[gnum].peer_names, N*npeers); memcpy(groupchats[gnum].oldpeer_names, groupchats[gnum].peer_names, N * npeers);
memcpy(groupchats[gnum].oldpeer_name_lengths, groupchats[gnum].peer_name_lengths,
sizeof(uint16_t) * npeers);
} }
static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnum, int peernum, static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnum, int peernum,
@ -214,85 +298,82 @@ static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnu
int num_peers = groupchats[groupnum].num_peers; int num_peers = groupchats[groupnum].num_peers;
/* get old peer name before updating name list */ /* get old peer name before updating name list */
uint8_t oldpeername[TOX_MAX_NAME_LENGTH] = {0}; uint8_t oldpeername[TOX_MAX_NAME_LENGTH];
if (change != TOX_CHAT_CHANGE_PEER_ADD) if (change != TOX_CHAT_CHANGE_PEER_ADD) {
memcpy(oldpeername, &groupchats[groupnum].oldpeer_names[peernum*TOX_MAX_NAME_LENGTH], memcpy(oldpeername, &groupchats[groupnum].oldpeer_names[peernum * TOX_MAX_NAME_LENGTH],
sizeof(oldpeername)); sizeof(oldpeername));
uint16_t old_n_len = groupchats[groupnum].oldpeer_name_lengths[peernum];
oldpeername[old_n_len] = '\0';
}
/* Update name lists */ /* Update name/len lists */
uint8_t tmp_peerlist[num_peers][TOX_MAX_NAME_LENGTH]; uint8_t tmp_peerlist[num_peers][TOX_MAX_NAME_LENGTH];
tox_group_get_names(m, groupnum, tmp_peerlist, num_peers); uint16_t tmp_peerlens[num_peers];
copy_peernames(groupnum, num_peers, tmp_peerlist); tox_group_get_names(m, groupnum, tmp_peerlist, tmp_peerlens, num_peers);
copy_peernames(groupnum, tmp_peerlist, tmp_peerlens, num_peers);
/* get current peername then sort namelist */ /* get current peername then sort namelist */
uint8_t peername[TOX_MAX_NAME_LENGTH] = {0}; uint8_t peername[TOX_MAX_NAME_LENGTH];
memcpy(peername, &groupchats[groupnum].peer_names[peernum*TOX_MAX_NAME_LENGTH], sizeof(peername));
if (change != TOX_CHAT_CHANGE_PEER_DEL) {
uint16_t n_len = groupchats[groupnum].peer_name_lengths[peernum];
memcpy(peername, &groupchats[groupnum].peer_names[peernum * TOX_MAX_NAME_LENGTH], sizeof(peername));
peername[n_len] = '\0';
}
qsort(groupchats[groupnum].peer_names, groupchats[groupnum].num_peers, TOX_MAX_NAME_LENGTH, qsort_strcasecmp_hlpr); qsort(groupchats[groupnum].peer_names, groupchats[groupnum].num_peers, TOX_MAX_NAME_LENGTH, qsort_strcasecmp_hlpr);
ChatContext *ctx = self->chatwin; ChatContext *ctx = self->chatwin;
print_time(ctx->history);
uint8_t *event;
uint8_t timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt);
switch (change) { switch (change) {
case TOX_CHAT_CHANGE_PEER_ADD: case TOX_CHAT_CHANGE_PEER_ADD:
wattron(ctx->history, COLOR_PAIR(GREEN)); event = "has joined the room";
wattron(ctx->history, A_BOLD); line_info_add(self, timefrmt, peername, NULL, event, CONNECTION, 0, GREEN);
wprintw(ctx->history, "* %s", peername); write_to_log(event, peername, ctx->log, true);
wattroff(ctx->history, A_BOLD);
wprintw(ctx->history, " has joined the room\n");
wattroff(ctx->history, COLOR_PAIR(GREEN));
break; break;
case TOX_CHAT_CHANGE_PEER_DEL: case TOX_CHAT_CHANGE_PEER_DEL:
wattron(ctx->history, A_BOLD); event = "has left the room";
wprintw(ctx->history, "* %s", oldpeername); line_info_add(self, timefrmt, oldpeername, NULL, event, CONNECTION, 0, 0);
wattroff(ctx->history, A_BOLD);
wprintw(ctx->history, " has left the room\n");
if (groupchats[self->num].side_pos > 0) if (groupchats[self->num].side_pos > 0)
--groupchats[self->num].side_pos; --groupchats[self->num].side_pos;
write_to_log(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)); event = " is now known as ";
wattron(ctx->history, A_BOLD); line_info_add(self, timefrmt, oldpeername, peername, event, NAME_CHANGE, 0, 0);
wprintw(ctx->history, "* %s", oldpeername);
wattroff(ctx->history, A_BOLD);
wprintw(ctx->history, " is now known as "); uint8_t tmp_event[TOXIC_MAX_NAME_LENGTH * 2 + 32];
snprintf(tmp_event, sizeof(tmp_event), "is now known as %s", peername);
wattron(ctx->history, A_BOLD); write_to_log(tmp_event, oldpeername, ctx->log, true);
wprintw(ctx->history, "%s\n", peername);
wattroff(ctx->history, A_BOLD);
wattroff(ctx->history, COLOR_PAIR(MAGENTA));
break; break;
} }
alert_window(self, WINDOW_ALERT_2, false); alert_window(self, WINDOW_ALERT_2, false);
} }
static void send_group_action(ToxWindow *self, ChatContext *ctx, Tox *m, uint8_t *action) { static void send_group_action(ToxWindow *self, ChatContext *ctx, Tox *m, uint8_t *action)
{
if (action == NULL) { if (action == NULL) {
wprintw(ctx->history, "Invalid syntax.\n"); wprintw(ctx->history, "Invalid syntax.\n");
return; return;
} }
// uint8_t selfname[TOX_MAX_NAME_LENGTH]; if (tox_group_action_send(m, self->num, action, strlen(action)) == -1) {
// tox_get_self_name(m, selfname, TOX_MAX_NAME_LENGTH); uint8_t *errmsg = " * Failed to send action.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, RED);
// 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, bool ltr)
{ {
ChatContext *ctx = self->chatwin; ChatContext *ctx = self->chatwin;
@ -301,13 +382,36 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key)
getmaxyx(self->window, y2, x2); getmaxyx(self->window, y2, x2);
int cur_len = 0; int cur_len = 0;
if (!ltr && (key == T_KEY_ESC) ) { /* ESC key: Toggle history scroll mode */
bool scroll = ctx->hst->scroll_mode ? false : true;
line_info_toggle_scroll(self, scroll);
}
/* If we're in scroll mode ignore rest of function */
if (ctx->hst->scroll_mode) {
line_info_onKey(self, key);
return;
}
if (ltr) {
if ( (ctx->len < MAX_STR_SIZE - 1) && (ctx->len < (x2 * (CHATBOX_HEIGHT - 1) - 1)) ) {
add_char_to_buf(ctx->line, &ctx->pos, &ctx->len, key);
if (x == x2 - 1)
wmove(self->window, y + 1, 0);
else
wmove(self->window, y, x + MAX(1, wcwidth(key)));
}
} else { /* if (!ltr) */
if (key == 0x107 || key == 0x8 || key == 0x7f) { /* BACKSPACE key: Remove character behind pos */ if (key == 0x107 || key == 0x8 || key == 0x7f) { /* BACKSPACE key: Remove character behind pos */
if (ctx->pos > 0) { if (ctx->pos > 0) {
cur_len = MAX(1, wcwidth(ctx->line[ctx->pos - 1])); cur_len = MAX(1, wcwidth(ctx->line[ctx->pos - 1]));
del_char_buf_bck(ctx->line, &ctx->pos, &ctx->len); del_char_buf_bck(ctx->line, &ctx->pos, &ctx->len);
if (x == 0) if (x == 0)
wmove(self->window, y-1, x2 - cur_len); wmove(self->window, y - 1, x2 - cur_len);
else else
wmove(self->window, y, x - cur_len); wmove(self->window, y, x - cur_len);
} else { } else {
@ -338,17 +442,17 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key)
beep(); beep();
} }
else if (key == KEY_HOME) { /* HOME key: Move cursor to beginning of line */ else if (key == KEY_HOME || key == T_KEY_C_A) { /* HOME/C-a key: Move cursor to start of line */
if (ctx->pos > 0) { if (ctx->pos > 0) {
ctx->pos = 0; ctx->pos = 0;
wmove(self->window, y2 - CURS_Y_OFFSET, 0); wmove(self->window, y2 - CURS_Y_OFFSET, 0);
} }
} }
else if (key == KEY_END) { /* END key: move cursor to end of line */ 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) { if (ctx->pos != ctx->len) {
ctx->pos = ctx->len; ctx->pos = ctx->len;
mv_curs_end(self->window, MAX(0, wcswidth(ctx->line, (CHATBOX_HEIGHT-1)*x2)), y2, x2); mv_curs_end(self->window, MAX(0, wcswidth(ctx->line, (CHATBOX_HEIGHT - 1)*x2)), y2, x2);
} }
} }
@ -358,7 +462,7 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key)
cur_len = MAX(1, wcwidth(ctx->line[ctx->pos])); cur_len = MAX(1, wcwidth(ctx->line[ctx->pos]));
if (x == 0) if (x == 0)
wmove(self->window, y-1, x2 - cur_len); wmove(self->window, y - 1, x2 - cur_len);
else else
wmove(self->window, y, x - cur_len); wmove(self->window, y, x - cur_len);
} else { } else {
@ -371,8 +475,8 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key)
cur_len = MAX(1, wcwidth(ctx->line[ctx->pos])); cur_len = MAX(1, wcwidth(ctx->line[ctx->pos]));
++ctx->pos; ++ctx->pos;
if (x == x2-1) if (x == x2 - 1)
wmove(self->window, y+1, 0); wmove(self->window, y + 1, 0);
else else
wmove(self->window, y, x + cur_len); wmove(self->window, y, x + cur_len);
} else { } else {
@ -382,13 +486,13 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key)
else if (key == KEY_UP) { /* fetches previous item in history */ 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, fetch_hist_item(ctx->line, &ctx->pos, &ctx->len, ctx->ln_history, ctx->hst_tot,
&ctx->hst_pos, LN_HIST_MV_UP); &ctx->hst_pos, MOVE_UP);
mv_curs_end(self->window, ctx->len, y2, x2); mv_curs_end(self->window, ctx->len, y2, x2);
} }
else if (key == KEY_DOWN) { /* fetches next item in history */ 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, fetch_hist_item(ctx->line, &ctx->pos, &ctx->len, ctx->ln_history, ctx->hst_tot,
&ctx->hst_pos, LN_HIST_MV_DWN); &ctx->hst_pos, MOVE_DOWN);
mv_curs_end(self->window, ctx->len, y2, x2); mv_curs_end(self->window, ctx->len, y2, x2);
} }
@ -406,9 +510,9 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key)
if (diff != -1) { if (diff != -1) {
if (x + diff > x2 - 1) { if (x + diff > x2 - 1) {
int ofst = (x + diff - 1) - (x2 - 1); int ofst = (x + diff - 1) - (x2 - 1);
wmove(self->window, y+1, ofst); wmove(self->window, y + 1, ofst);
} else { } else {
wmove(self->window, y, x+diff); wmove(self->window, y, x + diff);
} }
} else { } else {
beep(); beep();
@ -431,23 +535,6 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key)
--groupchats[self->num].side_pos; --groupchats[self->num].side_pos;
} }
else
#if HAVE_WIDECHAR
if (iswprint(key))
#else
if (isprint(key))
#endif
{ /* prevents buffer overflows and strange behaviour when cursor goes past the window */
if ( (ctx->len < MAX_STR_SIZE-1) && (ctx->len < (x2 * (CHATBOX_HEIGHT - 1)-1)) ) {
add_char_to_buf(ctx->line, &ctx->pos, &ctx->len, key);
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[MAX_STR_SIZE]; uint8_t line[MAX_STR_SIZE];
@ -457,50 +544,55 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key)
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);
bool close_win = false;
if (!string_is_empty(line)) if (!string_is_empty(line))
add_line_to_hist(ctx->line, ctx->len, ctx->ln_history, &ctx->hst_tot, &ctx->hst_pos); 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 (strcmp(line, "/help") == 0)
print_groupchat_help(ctx);
else if (strncmp(line, "/me ", strlen("/me ")) == 0)
send_group_action(self, ctx, m, line + strlen("/me "));
else else
print_groupchat_help(self);
} else if (strncmp(line, "/me ", strlen("/me ")) == 0) {
send_group_action(self, ctx, m, line + strlen("/me "));
} else {
execute(ctx->history, self, m, line, GROUPCHAT_COMMAND_MODE); execute(ctx->history, self, m, line, GROUPCHAT_COMMAND_MODE);
}
} else if (!string_is_empty(line)) { } else if (!string_is_empty(line)) {
if (tox_group_message_send(m, self->num, line, strlen(line) + 1) == -1) { if (tox_group_message_send(m, self->num, line, strlen(line)) == -1) {
wattron(ctx->history, COLOR_PAIR(RED)); uint8_t *errmsg = " * Failed to send message.";
wprintw(ctx->history, " * Failed to send message.\n"); line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, RED);
wattroff(ctx->history, COLOR_PAIR(RED));
} }
} }
if (close_win)
free(ctx);
else
reset_buf(ctx->line, &ctx->pos, &ctx->len); reset_buf(ctx->line, &ctx->pos, &ctx->len);
} }
}
} }
static void groupchat_onDraw(ToxWindow *self, Tox *m) static void groupchat_onDraw(ToxWindow *self, Tox *m)
{ {
curs_set(1);
int x2, y2; int x2, y2;
getmaxyx(self->window, y2, x2); getmaxyx(self->window, y2, x2);
ChatContext *ctx = self->chatwin; ChatContext *ctx = self->chatwin;
line_info_print(self);
wclear(ctx->linewin); wclear(ctx->linewin);
if (ctx->hst->scroll_mode) {
line_info_onDraw(self);
} else {
scrollok(ctx->history, 1);
curs_set(1);
if (ctx->len > 0) { if (ctx->len > 0) {
uint8_t line[MAX_STR_SIZE]; uint8_t line[MAX_STR_SIZE];
@ -511,11 +603,12 @@ static void groupchat_onDraw(ToxWindow *self, Tox *m)
mvwprintw(ctx->linewin, 1, 0, "%s", line); mvwprintw(ctx->linewin, 1, 0, "%s", line);
} }
} }
}
wclear(ctx->sidebar); wclear(ctx->sidebar);
mvwhline(ctx->linewin, 0, 0, ACS_HLINE, x2); mvwhline(ctx->linewin, 0, 0, ACS_HLINE, x2);
mvwvline(ctx->sidebar, 0, 0, ACS_VLINE, y2-CHATBOX_HEIGHT); mvwvline(ctx->sidebar, 0, 0, ACS_VLINE, y2 - CHATBOX_HEIGHT);
mvwaddch(ctx->sidebar, y2-CHATBOX_HEIGHT, 0, ACS_BTEE); 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;
@ -525,20 +618,20 @@ static void groupchat_onDraw(ToxWindow *self, Tox *m)
wattroff(ctx->sidebar, A_BOLD); wattroff(ctx->sidebar, A_BOLD);
mvwaddch(ctx->sidebar, 1, 0, ACS_LTEE); mvwaddch(ctx->sidebar, 1, 0, ACS_LTEE);
mvwhline(ctx->sidebar, 1, 1, ACS_HLINE, SIDEBAR_WIDTH-1); mvwhline(ctx->sidebar, 1, 1, ACS_HLINE, SIDEBAR_WIDTH - 1);
int N = TOX_MAX_NAME_LENGTH; int N = TOX_MAX_NAME_LENGTH;
int maxlines = y2 - SDBAR_OFST - 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+2, 1); wmove(ctx->sidebar, i + 2, 1);
int peer = i + groupchats[self->num].side_pos; int peer = i + groupchats[self->num].side_pos;
/* truncate nick to fit in side panel without modifying list */ /* truncate nick to fit in side panel without modifying list */
uint8_t tmpnck[TOX_MAX_NAME_LENGTH]; uint8_t tmpnck[TOX_MAX_NAME_LENGTH];
memcpy(tmpnck, &groupchats[self->num].peer_names[peer*N], SIDEBAR_WIDTH-2); memcpy(tmpnck, &groupchats[self->num].peer_names[peer * N], SIDEBAR_WIDTH - 2);
tmpnck[SIDEBAR_WIDTH-2] = '\0'; tmpnck[SIDEBAR_WIDTH - 2] = '\0';
wprintw(ctx->sidebar, "%s\n", tmpnck); wprintw(ctx->sidebar, "%s\n", tmpnck);
} }
@ -550,13 +643,32 @@ static void groupchat_onInit(ToxWindow *self, Tox *m)
getmaxyx(self->window, y, x); getmaxyx(self->window, y, x);
ChatContext *ctx = self->chatwin; ChatContext *ctx = self->chatwin;
ctx->history = subwin(self->window, y-CHATBOX_HEIGHT+1, x-SIDEBAR_WIDTH-1, 0, 0);
scrollok(ctx->history, 1);
ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x, y-CHATBOX_HEIGHT, 0);
ctx->sidebar = subwin(self->window, y-CHATBOX_HEIGHT+1, SIDEBAR_WIDTH, 0, x-SIDEBAR_WIDTH);
print_groupchat_help(ctx); ctx->history = subwin(self->window, y - CHATBOX_HEIGHT + 1, x - SIDEBAR_WIDTH - 1, 0, 0);
wmove(self->window, y-CURS_Y_OFFSET, 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->hst = malloc(sizeof(struct history));
ctx->log = malloc(sizeof(struct chatlog));
if (ctx->log == NULL || ctx->hst == NULL) {
endwin();
fprintf(stderr, "malloc() failed. Aborting...\n");
exit(EXIT_FAILURE);
}
memset(ctx->hst, 0, sizeof(struct history));
memset(ctx->log, 0, sizeof(struct chatlog));
line_info_init(ctx->hst);
print_groupchat_help(self);
if (user_settings->autolog == AUTOLOG_ON)
log_enable(self->name, NULL, ctx->log);
execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE);
wmove(self->window, y - CURS_Y_OFFSET, 0);
} }
ToxWindow new_group_chat(Tox *m, int groupnum) ToxWindow new_group_chat(Tox *m, int groupnum)
@ -565,6 +677,7 @@ ToxWindow new_group_chat(Tox *m, int groupnum)
memset(&ret, 0, sizeof(ret)); memset(&ret, 0, sizeof(ret));
ret.active = true; ret.active = true;
ret.is_groupchat = true;
ret.onKey = &groupchat_onKey; ret.onKey = &groupchat_onKey;
ret.onDraw = &groupchat_onDraw; ret.onDraw = &groupchat_onDraw;

View File

@ -1,5 +1,23 @@
/* /* 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
@ -12,7 +30,10 @@ typedef struct {
int side_pos; /* current position of the sidebar - used for scrolling up and down */ 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;
uint16_t *peer_name_lengths;
uint16_t *oldpeer_name_lengths;
} 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); ToxWindow new_group_chat(Tox *m, int groupnum);

455
src/line_info.c Normal file
View File

@ -0,0 +1,455 @@
/* line_info.c
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "toxic_windows.h"
#include "line_info.h"
#include "groupchat.h"
#include "settings.h"
extern struct user_settings *user_settings;
void line_info_init(struct history *hst)
{
hst->line_root = malloc(sizeof(struct line_info));
if (hst->line_root == NULL) {
endwin();
fprintf(stderr, "malloc() failed. Aborting...\n");
exit(EXIT_FAILURE);
}
memset(hst->line_root, 0, sizeof(struct line_info));
hst->line_start = hst->line_root;
hst->line_end = hst->line_start;
}
/* resets line_start when scroll mode is disabled */
static void line_info_reset_start(struct history *hst)
{
struct line_info *line = hst->line_end;
uint32_t start_id = hst->start_id;
while (line) {
if (line->id == start_id) {
hst->line_start = line;
break;
}
line = line->prev;
}
}
void line_info_toggle_scroll(ToxWindow *self, bool scroll)
{
WINDOW *win = self->chatwin->history;
struct history *hst = self->chatwin->hst;
if (scroll) {
hst->scroll_mode = true;
scrollok(win, 0);
curs_set(0);
} else {
hst->scroll_mode = false;
scrollok(win, 1);
curs_set(1);
line_info_reset_start(hst);
}
}
void line_info_cleanup(struct history *hst)
{
struct line_info *tmp1 = hst->line_root;
while (tmp1) {
struct line_info *tmp2 = tmp1->next;
free(tmp1);
tmp1 = tmp2;
}
}
/* moves root forward and frees previous root */
static void line_info_root_fwd(struct history *hst)
{
struct line_info *tmp = hst->line_root->next;
tmp->prev = NULL;
if (hst->line_start->prev == NULL) { /* if line_start is root move it forward as well */
hst->line_start = hst->line_start->next;
hst->line_start->prev = NULL;
++hst->start_id;
}
free(hst->line_root);
hst->line_root = tmp;
}
void line_info_add(ToxWindow *self, uint8_t *tmstmp, uint8_t *name1, uint8_t *name2, uint8_t *msg,
uint8_t type, uint8_t bold, uint8_t colour)
{
struct history *hst = self->chatwin->hst;
struct line_info *new_line = malloc(sizeof(struct line_info));
if (new_line == NULL) {
endwin();
fprintf(stderr, "malloc() failed. Aborting...\n");
exit(EXIT_FAILURE);
}
memset(new_line, 0, sizeof(struct line_info));
int len = 1; /* there will always be a newline */
/* for type-specific formatting in print function */
switch (type) {
case ACTION:
len += 3;
break;
default:
len += 2;
break;
}
if (msg) {
snprintf(new_line->msg, sizeof(new_line->msg), "%s", msg);
len += strlen(new_line->msg);
}
if (tmstmp) {
snprintf(new_line->timestamp, sizeof(new_line->timestamp), "%s", tmstmp);
len += strlen(new_line->timestamp);
}
if (name1) {
snprintf(new_line->name1, sizeof(new_line->name1), "%s", name1);
len += strlen(new_line->name1);
}
if (name2) {
snprintf(new_line->name2, sizeof(new_line->name2), "%s", name2);
len += strlen(new_line->name2);
}
new_line->len = len;
new_line->type = type;
new_line->bold = bold;
new_line->colour = colour;
new_line->id = hst->line_end->id + 1;
new_line->prev = hst->line_end;
hst->line_end->next = new_line;
hst->line_end = new_line;
if (++hst->line_items > user_settings->history_size) {
--hst->line_items;
line_info_root_fwd(hst);
}
int newlines = 0;
int i;
for (i = 0; msg[i]; ++i) {
if (msg[i] == '\n')
++newlines;
}
int y, y2, x, x2;
getmaxyx(self->window, y2, x2);
getyx(self->chatwin->history, y, x);
if (x2 <= SIDEBAR_WIDTH)
return;
int offst = self->is_groupchat ? SIDEBAR_WIDTH : 0; /* offset width of groupchat sidebar */
int lines = (1 + newlines + (len / (x2 - offst)));
hst->queue_lns += lines;
++hst->queue;
int max_y = self->is_prompt ? y2 : y2 - CHATBOX_HEIGHT;
/* move line_start forward proportionate to the number of new lines */
if (y + hst->queue_lns - hst->queue >= max_y) {
while (lines > 0) {
++hst->start_id;
lines -= (1 + hst->line_start->len / (x2 - offst));
if (!hst->scroll_mode && hst->line_start->next)
hst->line_start = hst->line_start->next;
}
}
}
void line_info_print(ToxWindow *self)
{
ChatContext *ctx = self->chatwin;
if (ctx == NULL)
return;
WINDOW *win = ctx->history;
ctx->hst->queue = 0;
ctx->hst->queue_lns = 0;
wclear(win);
int y2, x2;
getmaxyx(self->window, y2, x2);
if (self->is_prompt)
y2 = user_settings->history_size; /* temporary fix to make prompt scroll */
if (x2 <= SIDEBAR_WIDTH)
return;
if (self->is_groupchat)
wmove(win, 0, 0);
else
wmove(win, 2, 0);
struct line_info *line = ctx->hst->line_start->next;
int offst = self->is_groupchat ? SIDEBAR_WIDTH : 0;
int numlines = 0;
while (line && numlines++ <= y2) {
uint8_t type = line->type;
switch (type) {
case OUT_MSG:
case IN_MSG:
wattron(win, COLOR_PAIR(BLUE));
wprintw(win, "%s", line->timestamp);
wattroff(win, COLOR_PAIR(BLUE));
int nameclr = GREEN;
if (line->colour)
nameclr = line->colour;
else if (type == IN_MSG)
nameclr = CYAN;
wattron(win, COLOR_PAIR(nameclr));
wprintw(win, "%s: ", line->name1);
wattroff(win, COLOR_PAIR(nameclr));
if (line->msg[0] == '>')
wattron(win, COLOR_PAIR(GREEN));
wprintw(win, "%s\n", line->msg);
if (line->msg[0] == '>')
wattroff(win, COLOR_PAIR(GREEN));
break;
case ACTION:
wattron(win, COLOR_PAIR(BLUE));
wprintw(win, "%s", line->timestamp);
wattroff(win, COLOR_PAIR(BLUE));
wattron(win, COLOR_PAIR(YELLOW));
wprintw(win, "* %s %s\n", line->name1, line->msg);
wattroff(win, COLOR_PAIR(YELLOW));
break;
case SYS_MSG:
if (line->timestamp[0]) {
wattron(win, COLOR_PAIR(BLUE));
wprintw(win, "%s", line->timestamp);
wattroff(win, COLOR_PAIR(BLUE));
}
if (line->bold)
wattron(win, A_BOLD);
if (line->colour)
wattron(win, COLOR_PAIR(line->colour));
wprintw(win, "%s\n", line->msg);
if (line->bold)
wattroff(win, A_BOLD);
if (line->colour)
wattroff(win, COLOR_PAIR(line->colour));
break;
case PROMPT:
wattron(win, COLOR_PAIR(GREEN));
wprintw(win, "$ ");
wattroff(win, COLOR_PAIR(GREEN));
if (line->msg[0])
wprintw(win, "%s", line->msg);
wprintw(win, "\n");
break;
case CONNECTION:
wattron(win, COLOR_PAIR(BLUE));
wprintw(win, "%s", line->timestamp);
wattroff(win, COLOR_PAIR(BLUE));
wattron(win, COLOR_PAIR(line->colour));
wattron(win, A_BOLD);
wprintw(win, "* %s ", line->name1);
wattroff(win, A_BOLD);
wprintw(win, "%s\n", line->msg);
wattroff(win, COLOR_PAIR(line->colour));
break;
case NAME_CHANGE:
wattron(win, COLOR_PAIR(BLUE));
wprintw(win, "%s", line->timestamp);
wattroff(win, COLOR_PAIR(BLUE));
wattron(win, COLOR_PAIR(MAGENTA));
wattron(win, A_BOLD);
wprintw(win, "* %s", line->name1);
wattroff(win, A_BOLD);
wprintw(win, "%s", line->msg);
wattron(win, A_BOLD);
wprintw(win, "%s\n", line->name2);
wattroff(win, A_BOLD);
wattroff(win, COLOR_PAIR(MAGENTA));
break;
}
line = line->next;
}
}
void line_info_set(ToxWindow *self, uint32_t id, uint8_t *msg)
{
struct line_info *line = self->chatwin->hst->line_end;
while (line) {
if (line->id == id) {
snprintf(line->msg, sizeof(line->msg), "%s", msg);
return;
}
line = line->prev;
}
}
static void line_info_goto_root(struct history *hst)
{
hst->line_start = hst->line_root;
}
static void line_info_scroll_up(struct history *hst)
{
if (hst->line_start->prev)
hst->line_start = hst->line_start->prev;
else beep();
}
static void line_info_scroll_down(struct history *hst)
{
if (hst->line_start->next)
hst->line_start = hst->line_start->next;
else beep();
}
static void line_info_page_up(ToxWindow *self, struct history *hst)
{
int x2, y2;
getmaxyx(self->window, y2, x2);
int jump_dist = y2 / 2;
int i;
for (i = 0; i < jump_dist && hst->line_start->prev; ++i)
hst->line_start = hst->line_start->prev;
}
static void line_info_page_down(ToxWindow *self, struct history *hst)
{
int x2, y2;
getmaxyx(self->window, y2, x2);
int jump_dist = y2 / 2;
int i;
for (i = 0; i < jump_dist && hst->line_start->next; ++i)
hst->line_start = hst->line_start->next;
}
void line_info_onKey(ToxWindow *self, wint_t key)
{
struct history *hst = self->chatwin->hst;
switch (key) {
case KEY_PPAGE:
line_info_page_up(self, hst);
break;
case KEY_NPAGE:
line_info_page_down(self, hst);
break;
case KEY_UP:
line_info_scroll_up(hst);
break;
case KEY_DOWN:
line_info_scroll_down(hst);
break;
case KEY_HOME:
line_info_goto_root(hst);
break;
case KEY_END:
line_info_reset_start(hst);
break;
}
}
void line_info_onDraw(ToxWindow *self)
{
ChatContext *ctx = self->chatwin;
wattron(ctx->linewin, A_BOLD | COLOR_PAIR(BLUE));
mvwprintw(ctx->linewin, 1, 0, "Scroll mode:\n");
wattroff(ctx->linewin, A_BOLD | COLOR_PAIR(BLUE));
mvwprintw(ctx->linewin, 1, 13, "Use up/down arrows, page up/page down, and home/end to navigate.\n"
" ESC to exit.\n");
}
void line_info_clear(struct history *hst)
{
hst->line_start = hst->line_end;
hst->start_id = hst->line_start->id;
}

86
src/line_info.h Normal file
View File

@ -0,0 +1,86 @@
/* line_info.h
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#define MAX_HISTORY 10000
#define MIN_HISTORY 20
enum {
SYS_MSG,
IN_MSG,
OUT_MSG,
PROMPT,
ACTION,
CONNECTION,
NAME_CHANGE,
} LINE_TYPE;
struct line_info {
uint8_t timestamp[TIME_STR_SIZE];
uint8_t name1[TOXIC_MAX_NAME_LENGTH];
uint8_t name2[TOXIC_MAX_NAME_LENGTH];
uint8_t msg[TOX_MAX_MESSAGE_LENGTH];
uint8_t type;
uint8_t bold;
uint8_t colour;
uint32_t id;
uint16_t len; /* combined len of all strings */
struct line_info *prev;
struct line_info *next;
};
/* Linked list containing chat history lines */
struct history {
struct line_info *line_root;
struct line_info *line_start; /* the first line we want to start printing at */
struct line_info *line_end;
uint32_t start_id; /* keeps track of where line_start should be when at bottom of history */
uint32_t line_items;
bool scroll_mode;
/* keeps track of lines added between window refreshes */
uint32_t queue;
uint32_t queue_lns;
};
/* adds a line to history (also moves line_start and/or line_root forward if necessary) */
void line_info_add(ToxWindow *self, uint8_t *tmstmp, uint8_t *name1, uint8_t *name2, uint8_t *msg,
uint8_t type, uint8_t bold, uint8_t colour);
/* Prints a section of history starting at line_start */
void line_info_print(ToxWindow *self);
/* frees all history lines */
void line_info_cleanup(struct history *hst);
/* Toggles scroll mode for current window */
void line_info_toggle_scroll(ToxWindow *self, bool scroll);
/* clears the screen (does not delete anything) */
void line_info_clear(struct history *hst);
/* puts msg in specified line_info msg buffer */
void line_info_set(ToxWindow *self, uint32_t id, uint8_t *msg);
void line_info_init(struct history *hst);
void line_info_onKey(ToxWindow *self, wint_t key);
void line_info_onDraw(ToxWindow *self);

134
src/log.c Normal file
View File

@ -0,0 +1,134 @@
/* 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/>.
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "configdir.h"
#include "toxic_windows.h"
#include "misc_tools.h"
#include "log.h"
#include "settings.h"
extern struct user_settings *user_settings;
/* Creates/fetches log file by appending to the config dir the name and a pseudo-unique identity */
void init_logging_session(uint8_t *name, uint8_t *key, struct chatlog *log)
{
if (!log->log_on)
return;
if (!valid_nick(name))
name = UNKNOWN_NAME;
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[1] & 0xff);
ident[KEY_IDENT_DIGITS * 2 + 1] = '\0';
} else {
strftime(ident, sizeof(ident), "%Y-%m-%d[%H:%M:%S]", get_time());
path_len += strlen(ident) + 1;
}
if (path_len > MAX_STR_SIZE) {
log->log_on = false;
free(user_config_dir);
return;
}
uint8_t log_path[MAX_STR_SIZE];
snprintf(log_path, MAX_STR_SIZE, "%s%s%s-%s.log",
user_config_dir, CONFIGDIR, name, ident);
free(user_config_dir);
log->file = fopen(log_path, "a");
if (log->file == NULL) {
log->log_on = false;
return;
}
fprintf(log->file, "\n*** NEW SESSION ***\n\n");
}
void write_to_log(const uint8_t *msg, uint8_t *name, struct chatlog *log, bool event)
{
if (!log->log_on)
return;
if (log->file == NULL) {
log->log_on = false;
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);
const char *t = user_settings->time == TIME_12 ? "%Y/%m/%d [%I:%M:%S %p]" : "%Y/%m/%d [%H:%M:%S]";
uint8_t s[MAX_STR_SIZE];
strftime(s, MAX_STR_SIZE, t, get_time());
fprintf(log->file, "%s %s %s\n", s, name_frmt, msg);
uint64_t curtime = get_unix_time();
if (timed_out(log->lastwrite, curtime, LOG_FLUSH_LIMIT)) {
fflush(log->file);
log->lastwrite = curtime;
}
}
void log_enable(uint8_t *name, uint8_t *key, struct chatlog *log)
{
log->log_on = true;
if (log->file == NULL)
init_logging_session(name, key, log);
}
void log_disable(struct chatlog *log)
{
log->log_on = false;
if (log->file != NULL) {
fclose(log->file);
log->file = NULL;
}
}

42
src/log.h Normal file
View File

@ -0,0 +1,42 @@
/* 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/>.
*
*/
#define LOG_FLUSH_LIMIT 2 /* limits calls to fflush(logfile) to a max of one per LOG_FLUSH_LIMIT seconds */
struct chatlog {
FILE *file;
uint64_t lastwrite;
int pos;
bool log_on; /* specific to current chat window */
};
/* Creates/fetches log file by appending to the config dir the name and a pseudo-unique identity */
void init_logging_session(uint8_t *name, uint8_t *key, struct chatlog *log);
/* formats/writes line to log file */
void write_to_log(const uint8_t *msg, uint8_t *name, struct chatlog *log, bool event);
/* enables logging for specified log and creates/fetches file if necessary */
void log_enable(uint8_t *name, uint8_t *key, struct chatlog *log);
/* disables logging for specified log and closes file */
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
@ -7,7 +25,7 @@
#endif #endif
#ifndef SIGWINCH #ifndef SIGWINCH
#define SIGWINCH 28 #define SIGWINCH 28
#endif #endif
#include <curses.h> #include <curses.h>
@ -20,16 +38,18 @@
#include <locale.h> #include <locale.h>
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
#include <pthread.h>
#ifdef _WIN32 #ifdef _WIN32
#include <direct.h> #include <direct.h>
#include <winsock2.h> #include <winsock2.h>
#include <ws2tcpip.h> #include <ws2tcpip.h>
#else #else
#include <netdb.h> #include <netdb.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <unistd.h>
#endif #endif
#include <tox/tox.h> #include <tox/tox.h>
@ -39,22 +59,33 @@
#include "friendlist.h" #include "friendlist.h"
#include "prompt.h" #include "prompt.h"
#include "misc_tools.h" #include "misc_tools.h"
#include "file_senders.h"
#include "line_info.h"
#include "settings.h"
#ifdef _SUPPORT_AUDIO
#include "audio_call.h"
#endif /* _SUPPORT_AUDIO */
#ifndef PACKAGE_DATADIR #ifndef PACKAGE_DATADIR
#define PACKAGE_DATADIR "." #define PACKAGE_DATADIR "."
#endif #endif
#ifdef _SUPPORT_AUDIO
ToxAv *av;
#endif /* _SUPPORT_AUDIO */
/* Export for use in Callbacks */ /* Export for use in Callbacks */
char *DATA_FILE = NULL; char *DATA_FILE = NULL;
char *SRVLIST_FILE = NULL;
ToxWindow *prompt = NULL; ToxWindow *prompt = NULL;
FileSender file_senders[MAX_FILES]; static int f_loadfromfile; /* 1 if we want to load from/save the data file, 0 otherwise */
uint8_t max_file_senders_index;
struct _Winthread Winthread;
struct user_settings *user_settings = NULL;
void on_window_resize(int sig) void on_window_resize(int sig)
{ {
endwin();
refresh(); refresh();
clear(); clear();
} }
@ -64,11 +95,13 @@ static void init_term(void)
/* Setup terminal */ /* Setup terminal */
signal(SIGWINCH, on_window_resize); signal(SIGWINCH, on_window_resize);
#if HAVE_WIDECHAR #if HAVE_WIDECHAR
if (setlocale(LC_ALL, "") == NULL) { if (setlocale(LC_ALL, "") == NULL) {
fprintf(stderr, "Could not set your locale, plese check your locale settings or" fprintf(stderr, "Could not set your locale, plese check your locale settings or"
"disable wide char support\n"); "disable wide char support\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
#endif #endif
initscr(); initscr();
cbreak(); cbreak();
@ -77,14 +110,21 @@ static void init_term(void)
timeout(100); timeout(100);
if (has_colors()) { if (has_colors()) {
short bg_color = COLOR_BLACK;
start_color(); start_color();
if (user_settings->colour_theme == NATIVE_COLS) {
if (assume_default_colors(-1, -1) == OK)
bg_color = -1;
}
init_pair(0, COLOR_WHITE, COLOR_BLACK); init_pair(0, COLOR_WHITE, COLOR_BLACK);
init_pair(1, COLOR_GREEN, COLOR_BLACK); init_pair(1, COLOR_GREEN, bg_color);
init_pair(2, COLOR_CYAN, COLOR_BLACK); init_pair(2, COLOR_CYAN, bg_color);
init_pair(3, COLOR_RED, COLOR_BLACK); init_pair(3, COLOR_RED, bg_color);
init_pair(4, COLOR_BLUE, COLOR_BLACK); init_pair(4, COLOR_BLUE, bg_color);
init_pair(5, COLOR_YELLOW, COLOR_BLACK); init_pair(5, COLOR_YELLOW, bg_color);
init_pair(6, COLOR_MAGENTA, COLOR_BLACK); init_pair(6, COLOR_MAGENTA, bg_color);
init_pair(7, COLOR_BLACK, COLOR_BLACK); init_pair(7, COLOR_BLACK, COLOR_BLACK);
init_pair(8, COLOR_BLACK, COLOR_WHITE); init_pair(8, COLOR_BLACK, COLOR_WHITE);
} }
@ -98,8 +138,12 @@ static Tox *init_tox(int ipv4)
int ipv6 = !ipv4; int ipv6 = !ipv4;
Tox *m = tox_new(ipv6); Tox *m = tox_new(ipv6);
if (TOX_ENABLE_IPV6_DEFAULT && m == NULL) { /*
fprintf(stderr, "IPv6 didn't initialize, trying IPv4 only\n"); * TOX_ENABLE_IPV6_DEFAULT is always 1.
* Checking it is redundant, this *should* be doing ipv4 fallback
*/
if (ipv6 && m == NULL) {
fprintf(stderr, "IPv6 didn't initialize, trying IPv4\n");
m = tox_new(0); m = tox_new(0);
} }
@ -124,13 +168,13 @@ static Tox *init_tox(int ipv4)
tox_callback_file_data(m, on_file_data, NULL); tox_callback_file_data(m, on_file_data, NULL);
#ifdef __linux__ #ifdef __linux__
tox_set_name(m, (uint8_t *) "Cool guy", sizeof("Cool guy")); tox_set_name(m, (uint8_t *) "Cool dude", strlen("Cool dude"));
#elif defined(_WIN32) #elif defined(__FreeBSD__)
tox_set_name(m, (uint8_t *) "I should install GNU/Linux", sizeof("I should install GNU/Linux")); tox_set_name(m, (uint8_t *) "Nerd", strlen("Nerd"));
#elif defined(__APPLE__) #elif defined(__APPLE__)
tox_set_name(m, (uint8_t *) "Hipster", sizeof("Hipster")); //This used to users of other Unixes are hipsters tox_set_name(m, (uint8_t *) "Hipster", strlen("Hipster")); /* This used to users of other Unixes are hipsters */
#else #else
tox_set_name(m, (uint8_t *) "Registered Minix user #4", sizeof("Registered Minix user #4")); tox_set_name(m, (uint8_t *) "Registered Minix user #4", strlen("Registered Minix user #4"));
#endif #endif
return m; return m;
@ -138,35 +182,38 @@ static Tox *init_tox(int ipv4)
#define MINLINE 50 /* IP: 7 + port: 5 + key: 38 + spaces: 2 = 70. ! (& e.g. tox.im = 6) */ #define MINLINE 50 /* IP: 7 + port: 5 + key: 38 + spaces: 2 = 70. ! (& e.g. tox.im = 6) */
#define MAXLINE 256 /* Approx max number of chars in a sever line (name + port + key) */ #define MAXLINE 256 /* Approx max number of chars in a sever line (name + port + key) */
#define MAXSERVERS 50 #define MAXNODES 50
#define SERVERLEN (MAXLINE - TOX_CLIENT_ID_SIZE - 7) #define NODELEN (MAXLINE - TOX_CLIENT_ID_SIZE - 7)
static int linecnt = 0; static int linecnt = 0;
static char servers[MAXSERVERS][SERVERLEN]; static char nodes[MAXNODES][NODELEN];
static uint16_t ports[MAXSERVERS]; static uint16_t ports[MAXNODES];
static uint8_t keys[MAXSERVERS][TOX_CLIENT_ID_SIZE]; static uint8_t keys[MAXNODES][TOX_CLIENT_ID_SIZE];
int serverlist_load(void) static int nodelist_load(char *filename)
{ {
FILE *fp = NULL; if (!filename)
return 1;
fp = fopen(SRVLIST_FILE, "r"); FILE *fp = fopen(filename, "r");
if (fp == NULL) if (fp == NULL)
return 1; return 1;
char line[MAXLINE]; char line[MAXLINE];
while (fgets(line, sizeof(line), fp) && linecnt < MAXSERVERS) {
while (fgets(line, sizeof(line), fp) && linecnt < MAXNODES) {
if (strlen(line) > MINLINE) { if (strlen(line) > MINLINE) {
char *name = strtok(line, " "); char *name = strtok(line, " ");
char *port = strtok(NULL, " "); char *port = strtok(NULL, " ");
char *key_ascii = strtok(NULL, " "); char *key_ascii = strtok(NULL, " ");
/* invalid line */ /* invalid line */
if (name == NULL || port == NULL || key_ascii == NULL) if (name == NULL || port == NULL || key_ascii == NULL)
continue; continue;
strncpy(servers[linecnt], name, SERVERLEN); strncpy(nodes[linecnt], name, NODELEN);
servers[linecnt][SERVERLEN - 1] = 0; nodes[linecnt][NODELEN - 1] = 0;
ports[linecnt] = htons(atoi(port)); ports[linecnt] = htons(atoi(port));
uint8_t *key_binary = hex_string_to_bin(key_ascii); uint8_t *key_binary = hex_string_to_bin(key_ascii);
@ -186,55 +233,60 @@ 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, nodes[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 node listed in the DHTnodes file
* *
* return codes: * return codes:
* 1: failed to open server file * 1: failed to open node file
* 2: no line of sufficient length in server file * 2: no line of sufficient length in node 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: nodelist file contains no acceptable line
* 5: serverlist file contains no acceptable line
*/ */
static int 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 nodelist */
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 nodelist
* - 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 = nodelist_load(PACKAGE_DATADIR "/DHTnodes");
if (res)
if (linecnt < 1)
return res; return res;
if (!linecnt) res = 3;
return 4; int i;
int n = MIN(NUM_INIT_NODES, linecnt);
res = 6; for (i = 0; i < n; ++i) {
int linenumber; if (init_connection_helper(m, rand() % linecnt))
for(linenumber = 0; linenumber < linecnt; linenumber++)
if (init_connection_helper(m, linenumber))
res = 0; res = 0;
}
return res; return res;
} }
/* empty serverlist file */ /* empty nodelist file */
return 5; return 4;
} }
static void do_connection(Tox *m, ToxWindow *prompt) static void do_connection(Tox *m, ToxWindow *prompt)
{ {
uint8_t msg[MAX_STR_SIZE] = {0};
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;
@ -242,28 +294,33 @@ static void do_connection(Tox *m, ToxWindow *prompt)
bool is_connected = tox_isconnected(m); bool is_connected = tox_isconnected(m);
if (!dht_on && !is_connected && !(conn_try++ % 100)) { if (!dht_on && !is_connected && !(conn_try++ % 100)) {
prep_prompt_win();
if (!conn_err) { if (!conn_err) {
wprintw(prompt->window, "Establishing connection...\n"); if ((conn_err = init_connection(m))) {
if ((conn_err = init_connection(m))) snprintf(msg, sizeof(msg), "\nAuto-connect failed with error code %d", conn_err);
wprintw(prompt->window, "\nAuto-connect failed with error code %d\n", conn_err); }
} }
} else if (!dht_on && is_connected) { } else if (!dht_on && is_connected) {
dht_on = true; dht_on = true;
prompt_update_connectionstatus(prompt, dht_on); prompt_update_connectionstatus(prompt, dht_on);
snprintf(msg, sizeof(msg), "DHT connected.");
prep_prompt_win();
wprintw(prompt->window, "\nDHT connected.\n");
} else if (dht_on && !is_connected) { } else if (dht_on && !is_connected) {
dht_on = false; dht_on = false;
prompt_update_connectionstatus(prompt, dht_on); prompt_update_connectionstatus(prompt, dht_on);
snprintf(msg, sizeof(msg), "\nDHT disconnected. Attempting to reconnect.");
prep_prompt_win();
wprintw(prompt->window, "\nDHT disconnected. Attempting to reconnect.\n");
} }
if (msg[0])
line_info_add(prompt, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
} }
int f_loadfromfile; static void load_friendlist(Tox *m)
{
uint32_t i;
uint32_t numfriends = tox_count_friendlist(m);
for (i = 0; i < numfriends; ++i)
friendlist_onFriendAdded(NULL, m, i, false);
}
/* /*
* Store Messenger to given location * Store Messenger to given location
@ -343,12 +400,7 @@ static void load_data(Tox *m, char *path)
} }
tox_load(m, buf, len); tox_load(m, buf, len);
load_friendlist(m);
uint32_t i = 0;
uint8_t name[TOX_MAX_NAME_LENGTH];
while (tox_get_name(m, i, name) != -1)
on_friendadded(m, i++, false);
free(buf); free(buf);
fclose(fd); fclose(fd);
@ -363,102 +415,48 @@ static void load_data(Tox *m, char *path)
} }
} }
void close_file_sender(int i)
{
fclose(file_senders[i].file);
memset(&file_senders[i], 0, sizeof(FileSender));
int j;
for (j = max_file_senders_index; j > 0; --j) {
if (file_senders[j-1].active)
break;
}
max_file_senders_index = j;
}
static void do_file_senders(Tox *m)
{
int i;
for (i = 0; i < max_file_senders_index; ++i) {
if (!file_senders[i].active)
continue;
uint8_t *pathname = file_senders[i].pathname;
uint8_t filenum = file_senders[i].filenum;
int friendnum = file_senders[i].friendnum;
FILE *fp = file_senders[i].file;
uint64_t current_time = (uint64_t) time(NULL);
/* If file transfer has timed out kill transfer and send kill control */
if (timed_out(file_senders[i].timestamp, current_time, TIMEOUT_FILESENDER)) {
ChatContext *ctx = file_senders[i].toxwin->chatwin;
if (ctx != NULL) {
wprintw(ctx->history, "File transfer for '%s' timed out.\n", pathname);
alert_window(file_senders[i].toxwin, WINDOW_ALERT_2, true);
}
tox_file_send_control(m, friendnum, 0, filenum, TOX_FILECONTROL_KILL, 0, 0);
close_file_sender(i);
continue;
}
while (true) {
if (tox_file_send_data(m, friendnum, filenum, file_senders[i].nextpiece,
file_senders[i].piecelen) == -1)
break;
file_senders[i].timestamp = current_time;
file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1,
tox_file_data_size(m, friendnum), fp);
if (file_senders[i].piecelen == 0) {
ChatContext *ctx = file_senders[i].toxwin->chatwin;
if (ctx != NULL) {
wprintw(ctx->history, "File '%s' successfuly sent.\n", pathname);
alert_window(file_senders[i].toxwin, WINDOW_ALERT_2, true);
}
tox_file_send_control(m, friendnum, 0, filenum, TOX_FILECONTROL_FINISHED, 0, 0);
close_file_sender(i);
break;
}
}
}
}
void exit_toxic(Tox *m) void exit_toxic(Tox *m)
{ {
store_data(m, DATA_FILE); store_data(m, DATA_FILE);
close_all_file_senders();
int i; kill_all_windows();
log_disable(prompt->chatwin->log);
for (i = 0; i < max_file_senders_index; ++i) { line_info_cleanup(prompt->chatwin->hst);
if (file_senders[i].active)
fclose(file_senders[i].file);
}
free(DATA_FILE); free(DATA_FILE);
free(SRVLIST_FILE);
free(prompt->stb); free(prompt->stb);
free(prompt->promptbuf); free(prompt->chatwin->log);
free(prompt->chatwin->hst);
free(prompt->chatwin);
free(user_settings);
tox_kill(m); tox_kill(m);
#ifdef _SUPPORT_AUDIO
terminate_audio();
#endif /* _SUPPORT_AUDIO */
endwin(); endwin();
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }
static void do_toxic(Tox *m, ToxWindow *prompt) static void do_toxic(Tox *m, ToxWindow *prompt)
{ {
pthread_mutex_lock(&Winthread.lock);
do_connection(m, prompt); do_connection(m, prompt);
draw_active_window(m);
do_file_senders(m); do_file_senders(m);
/* main tox-core loop */ /* main tox-core loop */
tox_do(m); tox_do(m);
pthread_mutex_unlock(&Winthread.lock);
}
void *thread_winref(void *data)
{
Tox *m = (Tox *) data;
while (true) {
draw_active_window(m);
refresh_inactive_windows();
}
} }
int main(int argc, char *argv[]) int main(int argc, char *argv[])
@ -471,7 +469,7 @@ int main(int argc, char *argv[])
int i = 0; int i = 0;
int f_use_ipv4 = 0; int f_use_ipv4 = 0;
// Make sure all written files are read/writeable only by the current user. /* Make sure all written files are read/writeable only by the current user. */
umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
for (i = 0; i < argc; ++i) { for (i = 0; i < argc; ++i) {
@ -492,11 +490,13 @@ int main(int argc, char *argv[])
} }
config_err = create_user_config_dir(user_config_dir); config_err = create_user_config_dir(user_config_dir);
if (DATA_FILE == NULL ) { if (DATA_FILE == NULL ) {
if (config_err) { if (config_err) {
DATA_FILE = strdup("data"); DATA_FILE = strdup("data");
} else { } else {
DATA_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen("data") + 1); DATA_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen("data") + 1);
if (DATA_FILE != NULL) { if (DATA_FILE != NULL) {
strcpy(DATA_FILE, user_config_dir); strcpy(DATA_FILE, user_config_dir);
strcat(DATA_FILE, CONFIGDIR); strcat(DATA_FILE, CONFIGDIR);
@ -509,25 +509,22 @@ int main(int argc, char *argv[])
} }
} }
if (config_err) { free(user_config_dir);
SRVLIST_FILE = strdup(PACKAGE_DATADIR "/DHTservers");
} else { /* init user_settings struct and load settings from conf file */
SRVLIST_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen("DHTservers") + 1); user_settings = malloc(sizeof(struct user_settings));
if (SRVLIST_FILE != NULL) {
strcpy(SRVLIST_FILE, user_config_dir); if (user_settings == NULL) {
strcat(SRVLIST_FILE, CONFIGDIR);
strcat(SRVLIST_FILE, "DHTservers");
} else {
endwin(); endwin();
fprintf(stderr, "malloc() failed. Aborting...\n"); fprintf(stderr, "malloc() failed. Aborting...\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
}
free(user_config_dir); memset(user_settings, 0, sizeof(struct user_settings));
int settings_err = settings_load(user_settings, NULL);
init_term();
Tox *m = init_tox(f_use_ipv4); Tox *m = init_tox(f_use_ipv4);
init_term();
if (m == NULL) { if (m == NULL) {
endwin(); endwin();
@ -535,30 +532,67 @@ int main(int argc, char *argv[])
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
prompt = init_windows(m);
if (f_loadfromfile) if (f_loadfromfile)
load_data(m, DATA_FILE); load_data(m, DATA_FILE);
prompt = init_windows(m);
/* create new thread for ncurses stuff */
if (pthread_mutex_init(&Winthread.lock, NULL) != 0) {
endwin();
fprintf(stderr, "Mutex init failed. Aborting...\n");
exit(EXIT_FAILURE);
}
if (pthread_create(&Winthread.tid, NULL, thread_winref, (void *) m) != 0) {
endwin();
fprintf(stderr, "Thread creation failed. Aborting...\n");
exit(EXIT_FAILURE);
}
uint8_t *msg;
#ifdef _SUPPORT_AUDIO
av = init_audio(prompt, m);
device_set(prompt, input, user_settings->audio_in_dev);
device_set(prompt, output, user_settings->audio_out_dev);
if ( errors() == NoError )
msg = "Audio initiated with no problems.";
else /* Get error code and stuff */
msg = "Error initiating audio!";
line_info_add(prompt, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
#endif /* _SUPPORT_AUDIO */
if (f_flag == -1) { if (f_flag == -1) {
attron(COLOR_PAIR(RED) | A_BOLD); msg = "You passed '-f' without giving an argument. Defaulting to 'data' for a keyfile...";
wprintw(prompt->window, "You passed '-f' without giving an argument.\n" line_info_add(prompt, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
"defaulting to 'data' for a keyfile...\n");
attroff(COLOR_PAIR(RED) | A_BOLD);
} }
if (config_err) { if (config_err) {
attron(COLOR_PAIR(RED) | A_BOLD); msg = "Unable to determine configuration directory. Defaulting to 'data' for a keyfile...";
wprintw(prompt->window, "Unable to determine configuration directory.\n" line_info_add(prompt, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
"defaulting to 'data' for a keyfile...\n");
attroff(COLOR_PAIR(RED) | A_BOLD);
} }
prompt_init_statusbar(prompt, m);
sort_friendlist_index(m);
while (true) if (settings_err == -1) {
msg = "Failed to load user settings";
line_info_add(prompt, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
}
sort_friendlist_index();
prompt_init_statusbar(prompt, m);
while (true) {
update_unix_time();
do_toxic(m, prompt); do_toxic(m, prompt);
usleep(10000);
}
return 0; return 0;
} }

View File

@ -1,17 +1,69 @@
/* /* 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/>.
*
*/ */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
#include <limits.h> #include <limits.h>
#include "toxic_windows.h" #include "toxic_windows.h"
#include "misc_tools.h"
#include "settings.h"
extern ToxWindow *prompt; extern ToxWindow *prompt;
extern struct user_settings *user_settings;
// XXX: FIX static uint64_t current_unix_time;
void update_unix_time(void)
{
current_unix_time = (uint64_t) time(NULL);
}
uint64_t get_unix_time(void)
{
return current_unix_time;
}
/* Get the current local time */
struct tm *get_time(void)
{
struct tm *timeinfo;
uint64_t t = get_unix_time();
timeinfo = localtime(&t);
return timeinfo;
}
void get_time_str(uint8_t *buf)
{
const char *t = user_settings->time == TIME_12 ? "[%-I:%M:%S] " : "[%H:%M:%S] ";
strftime(buf, TIME_STR_SIZE, t, get_time());
}
/* XXX: FIX */
unsigned char *hex_string_to_bin(char hex_string[]) unsigned char *hex_string_to_bin(char hex_string[])
{ {
size_t len = strlen(hex_string); size_t len = strlen(hex_string);
@ -32,26 +84,6 @@ unsigned char *hex_string_to_bin(char hex_string[])
return val; return val;
} }
/* Get the current local time */
struct tm *get_time(void)
{
struct tm *timeinfo;
time_t now;
time(&now);
timeinfo = localtime(&now);
return timeinfo;
}
/* Prints the time to given window */
void print_time(WINDOW *window)
{
struct tm *timeinfo = get_time();
wattron(window, COLOR_PAIR(BLUE));
wprintw(window, "[%02d:%02d:%02d] ", timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec);
wattroff(window,COLOR_PAIR(BLUE));
}
/* Returns 1 if the string is empty, 0 otherwise */ /* Returns 1 if the string is empty, 0 otherwise */
int string_is_empty(char *string) int string_is_empty(char *string)
{ {
@ -66,7 +98,7 @@ int mbs_to_wcs_buf(wchar_t *buf, const uint8_t *string, size_t n)
if (n < len) if (n < len)
return -1; return -1;
if ((len = mbstowcs(buf, string, n)) == (size_t) -1) if ((len = mbstowcs(buf, string, n)) == (size_t) - 1)
return -1; return -1;
return len; return len;
@ -81,23 +113,23 @@ int wcs_to_mbs_buf(uint8_t *buf, const wchar_t *string, size_t n)
if (n < len) if (n < len)
return -1; return -1;
if ((len = wcstombs(buf, string, n)) == (size_t) -1) if ((len = wcstombs(buf, string, n)) == (size_t) - 1)
return -1; return -1;
return len; return len;
} }
/* convert wide characters to multibyte string: string returned must be free'd */ /* convert wide characters to multibyte string: string returned must be freed */
uint8_t *wcs_to_mbs(wchar_t *string) 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);
if (len != (size_t) -1) { if (len != (size_t) - 1) {
ret = malloc(++len); ret = malloc(++len);
if (ret != NULL) { if (ret != NULL) {
if (wcstombs(ret, string, len) == (size_t) -1) if (wcstombs(ret, string, len) == (size_t) - 1)
return NULL; return NULL;
} }
} else { } else {
@ -134,8 +166,8 @@ char *wc_to_char(wchar_t ch)
return ret; return ret;
} }
/* Returns true if connection has timed out, false otherwise */ /* Returns 1 if connection has timed out, 0 otherwise */
bool timed_out(uint64_t timestamp, uint64_t curtime, uint64_t timeout) int timed_out(uint64_t timestamp, uint64_t curtime, uint64_t timeout)
{ {
return timestamp + timeout <= curtime; return timestamp + timeout <= curtime;
} }
@ -147,9 +179,11 @@ void alert_window(ToxWindow *self, int type, bool is_beep)
case WINDOW_ALERT_0: case WINDOW_ALERT_0:
self->alert0 = true; self->alert0 = true;
break; break;
case WINDOW_ALERT_1: case WINDOW_ALERT_1:
self->alert1 = true; self->alert1 = true;
break; break;
case WINDOW_ALERT_2: case WINDOW_ALERT_2:
self->alert2 = true; self->alert2 = true;
break; break;
@ -157,7 +191,7 @@ void alert_window(ToxWindow *self, int type, bool is_beep)
StatusBar *stb = prompt->stb; StatusBar *stb = prompt->stb;
if (is_beep && stb->status != TOX_USERSTATUS_BUSY) if (is_beep && stb->status != TOX_USERSTATUS_BUSY && user_settings->alerts == ALERTS_ENABLED)
beep(); beep();
} }
@ -167,23 +201,27 @@ int qsort_strcasecmp_hlpr(const void *nick1, const void *nick2)
return strcasecmp((const char *) nick1, (const char *) nick2); return strcasecmp((const char *) nick1, (const char *) nick2);
} }
/* Returns true if nick is valid. A valid toxic nick: /* Returns 1 if nick is valid, 0 if not. A valid toxic nick:
- cannot be empty - cannot be empty
- cannot start with a space - cannot start with a space
- must not contain a forward slash (for logfile naming purposes)
- must not contain contiguous spaces */ - must not contain contiguous spaces */
bool valid_nick(uint8_t *nick) int valid_nick(uint8_t *nick)
{ {
if (!nick[0] || nick[0] == ' ') if (!nick[0] || nick[0] == ' ')
return false; return 0;
int i; int i;
for (i = 0; nick[i]; ++i) { for (i = 0; nick[i]; ++i) {
if (nick[i] == ' ' && nick[i+1] == ' ') if (nick[i] == ' ' && nick[i + 1] == ' ')
return false; return 0;
if (nick[i] == '/')
return 0;
} }
return true; return 1;
} }
/* Moves cursor to the end of the line in given window */ /* Moves cursor to the end of the line in given window */
@ -202,13 +240,13 @@ void get_file_name(uint8_t *pathname, uint8_t *namebuf)
while (idx >= 0 && pathname[idx] == '/') while (idx >= 0 && pathname[idx] == '/')
pathname[idx--] = '\0'; pathname[idx--] = '\0';
uint8_t *filename = strrchr(pathname, '/'); // Try unix style paths uint8_t *filename = strrchr(pathname, '/'); /* Try unix style paths */
if (filename != NULL) { if (filename != NULL) {
if (!strlen(++filename)) if (!strlen(++filename))
filename = pathname; filename = pathname;
} else { } else {
filename = strrchr(pathname, '\\'); // Try windows style paths filename = strrchr(pathname, '\\'); /* Try windows style paths */
if (filename == NULL) if (filename == NULL)
filename = pathname; filename = pathname;

View File

@ -1,18 +1,42 @@
/* /* 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)) #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[]);
/* get the current unix time */
uint64_t get_unix_time(void);
/*Puts the current time in buf in the format of [Hour:Min:Sec] */
void get_time_str(uint8_t *buf);
/* get the current local time */ /* get the current local time */
struct tm *get_time(void); struct tm *get_time(void);
/* Prints the time to given window */ /* updates current unix time (should be run once per do_toxic loop) */
void print_time(WINDOW *window); void update_unix_time(void);
/* Returns 1 if the string is empty, 0 otherwise */ /* Returns 1 if the string is empty, 0 otherwise */
int string_is_empty(char *string); int string_is_empty(char *string);
@ -30,8 +54,8 @@ uint8_t *wcs_to_mbs(wchar_t *string);
/* convert a wide char to multibyte char */ /* 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 1 if connection has timed out, 0 otherwise */
bool timed_out(uint64_t timestamp, uint64_t timeout, uint64_t curtime); int timed_out(uint64_t timestamp, uint64_t timeout, uint64_t curtime);
/* Colours the window tab according to type. Beeps if is_beep is true */ /* Colours the window tab according to type. Beeps if is_beep is true */
void alert_window(ToxWindow *self, int type, bool is_beep); void alert_window(ToxWindow *self, int type, bool is_beep);
@ -43,7 +67,7 @@ int qsort_strcasecmp_hlpr(const void *nick1, const void *nick2);
- 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); int valid_nick(uint8_t *nick);
/* Moves the cursor to the end of the line in given window */ /* 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); void mv_curs_end(WINDOW *w, size_t len, int max_y, int max_x);

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
@ -14,10 +32,16 @@
#include "execute.h" #include "execute.h"
#include "misc_tools.h" #include "misc_tools.h"
#include "toxic_strings.h" #include "toxic_strings.h"
#include "log.h"
#include "line_info.h"
#include "settings.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; extern ToxWindow *prompt;
struct _Winthread Winthread;
extern struct user_settings *user_settings;
/* Array of global command names used for tab completion. */ /* Array of global command names used for tab completion. */
const uint8_t glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = { const uint8_t glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
@ -30,38 +54,27 @@ const uint8_t glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
{ "/groupchat" }, { "/groupchat" },
{ "/help" }, { "/help" },
{ "/join" }, { "/join" },
{ "/log" },
{ "/myid" }, { "/myid" },
{ "/nick" }, { "/nick" },
{ "/note" }, { "/note" },
{ "/quit" }, { "/quit" },
{ "/status" }, { "/status" },
#ifdef _SUPPORT_AUDIO
{ "/lsdev" },
{ "/sdev" },
#endif /* _SUPPORT_AUDIO */
}; };
/* prevents input string from eating system messages: call this prior to printing a prompt message
TODO: This is only a partial fix */
void prep_prompt_win(void)
{
PromptBuf *prt = prompt->promptbuf;
if (prt->len <= 0)
return;
wprintw(prompt->window, "\n");
if (!prt->at_bottom) {
wmove(prompt->window, prt->orig_y - 1, X_OFST);
++prt->orig_y;
} else {
wmove(prompt->window, prt->orig_y - 2, X_OFST);
}
}
/* Updates own nick in prompt statusbar */ /* 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 = 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 = strlen(statusbar->nick);
} }
/* Updates own statusmessage in prompt statusbar */ /* Updates own statusmessage in prompt statusbar */
@ -69,11 +82,11 @@ void prompt_update_statusmessage(ToxWindow *prompt, uint8_t *statusmsg, uint16_t
{ {
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 = strlen(statusbar->statusmsg);
} }
/* 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, uint8_t status)
{ {
StatusBar *statusbar = prompt->stb; StatusBar *statusbar = prompt->stb;
statusbar->status = status; statusbar->status = status;
@ -109,106 +122,124 @@ static int add_friend_request(uint8_t *public_key)
return -1; return -1;
} }
static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key) static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
{ {
PromptBuf *prt = self->promptbuf; ChatContext *ctx = self->chatwin;
int x, y, y2, x2; int x, y, y2, x2;
getyx(self->window, y, x); getyx(ctx->history, y, x);
getmaxyx(self->window, y2, x2); getmaxyx(ctx->history, y2, x2);
/* TODO this is buggy */
/* ESC key: Toggle history scroll mode */
/*
if (key == T_KEY_ESC) {
bool scroll = ctx->hst->scroll_mode ? false : true;
line_info_toggle_scroll(self, scroll);
}
*/
/* If we're in scroll mode ignore rest of function */
if (ctx->hst->scroll_mode) {
line_info_onKey(self, key);
return;
}
if (ltr) {
if (ctx->len < (MAX_STR_SIZE - 1)) {
add_char_to_buf(ctx->line, &ctx->pos, &ctx->len, key);
}
} else { /* if (!ltr) */
/* 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 (prt->pos > 0) { if (ctx->pos > 0) {
del_char_buf_bck(prt->line, &prt->pos, &prt->len); del_char_buf_bck(ctx->line, &ctx->pos, &ctx->len);
wmove(self->window, y, x-1); /* not necessary but fixes a display glitch */ wmove(ctx->history, y, x - 1); /* not necessary but fixes a display glitch */
prt->scroll = false;
} else { } else {
beep(); beep();
} }
} }
else if (key == KEY_DC) { /* DEL key: Remove character at pos */ else if (key == KEY_DC) { /* DEL key: Remove character at pos */
if (prt->pos != prt->len) { if (ctx->pos != ctx->len) {
del_char_buf_frnt(prt->line, &prt->pos, &prt->len); del_char_buf_frnt(ctx->line, &ctx->pos, &ctx->len);
prt->scroll = false;
} else { } else {
beep(); beep();
} }
} }
else if (key == T_KEY_DISCARD) { /* CTRL-U: Delete entire line behind pos */ else if (key == T_KEY_DISCARD) { /* CTRL-U: Delete entire line behind pos */
if (prt->pos > 0) { if (ctx->pos > 0) {
wmove(self->window, prt->orig_y, X_OFST); wmove(ctx->history, ctx->orig_y, X_OFST);
wclrtobot(self->window); wclrtobot(ctx->history);
discard_buf(prt->line, &prt->pos, &prt->len); discard_buf(ctx->line, &ctx->pos, &ctx->len);
} else { } else {
beep(); beep();
} }
} }
else if (key == T_KEY_KILL) { /* CTRL-K: Delete entire line in front of pos */ else if (key == T_KEY_KILL) { /* CTRL-K: Delete entire line in front of pos */
if (prt->len != prt->pos) if (ctx->len != ctx->pos)
kill_buf(prt->line, &prt->pos, &prt->len); kill_buf(ctx->line, &ctx->pos, &ctx->len);
else else
beep(); beep();
} }
else if (key == KEY_HOME) { /* HOME key: Move cursor to beginning of line */ else if (key == KEY_HOME || key == T_KEY_C_A) { /* HOME/C-a key: Move cursor to start of line */
if (prt->pos != 0) if (ctx->pos != 0)
prt->pos = 0; ctx->pos = 0;
} }
else if (key == KEY_END) { /* END key: move cursor to end of line */ 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) if (ctx->pos != ctx->len)
prt->pos = prt->len; ctx->pos = ctx->len;
} }
else if (key == KEY_LEFT) { else if (key == KEY_LEFT) {
if (prt->pos > 0) if (ctx->pos > 0)
--prt->pos; --ctx->pos;
else else
beep(); beep();
} }
else if (key == KEY_RIGHT) { else if (key == KEY_RIGHT) {
if (prt->pos < prt->len) if (ctx->pos < ctx->len)
++prt->pos; ++ctx->pos;
else else
beep(); beep();
} }
else if (key == KEY_UP) { /* fetches previous item in history */ else if (key == KEY_UP) { /* fetches previous item in history */
wmove(self->window, prt->orig_y, X_OFST); wmove(ctx->history, ctx->orig_y, X_OFST);
fetch_hist_item(prt->line, &prt->pos, &prt->len, prt->ln_history, prt->hst_tot, fetch_hist_item(ctx->line, &ctx->pos, &ctx->len, ctx->ln_history, ctx->hst_tot,
&prt->hst_pos, LN_HIST_MV_UP); &ctx->hst_pos, MOVE_UP);
/* adjust line y origin appropriately when window scrolls down */ /* adjust line y origin appropriately when window scrolls down */
if (prt->at_bottom && prt->len >= x2 - X_OFST) { if (ctx->at_bottom && ctx->len >= x2 - X_OFST) {
int px2 = prt->len >= x2 ? x2 : x2 - X_OFST; int px2 = ctx->len >= x2 ? x2 : x2 - X_OFST;
int p_ofst = px2 != x2 ? 0 : X_OFST; int p_ofst = px2 != x2 ? 0 : X_OFST;
if (px2 <= 0) if (px2 <= 0)
return; return;
int k = prt->orig_y + ((prt->len + p_ofst) / px2); int k = ctx->orig_y + ((ctx->len + p_ofst) / px2);
if (k >= y2) { if (k >= y2) {
wprintw(self->window, "\n"); --ctx->orig_y;
--prt->orig_y;
} }
} }
} }
else if (key == KEY_DOWN) { /* fetches next item in history */ else if (key == KEY_DOWN) { /* fetches next item in history */
wmove(self->window, prt->orig_y, X_OFST); wmove(ctx->history, ctx->orig_y, X_OFST);
fetch_hist_item(prt->line, &prt->pos, &prt->len, prt->ln_history, prt->hst_tot, fetch_hist_item(ctx->line, &ctx->pos, &ctx->len, ctx->ln_history, ctx->hst_tot,
&prt->hst_pos, LN_HIST_MV_DWN); &ctx->hst_pos, MOVE_DOWN);
} }
else if (key == '\t') { /* TAB key: completes command */ else if (key == '\t') { /* TAB key: completes command */
if (prt->len > 1 && prt->line[0] == '/') { if (ctx->len > 1 && ctx->line[0] == '/') {
if (complete_line(prt->line, &prt->pos, &prt->len, glob_cmd_list, AC_NUM_GLOB_COMMANDS, if (complete_line(ctx->line, &ctx->pos, &ctx->len, glob_cmd_list, AC_NUM_GLOB_COMMANDS,
MAX_CMDNAME_SIZE) == -1) MAX_CMDNAME_SIZE) == -1)
beep(); beep();
} else { } else {
@ -216,46 +247,41 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key)
} }
} }
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(ctx->history, "\n");
uint8_t line[MAX_STR_SIZE]; uint8_t line[MAX_STR_SIZE] = {0};
if (wcs_to_mbs_buf(line, prt->line, MAX_STR_SIZE) == -1) if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1)
memset(&line, 0, sizeof(line)); memset(&line, 0, sizeof(line));
if (!string_is_empty(line)) if (!string_is_empty(line))
add_line_to_hist(prt->line, prt->len, prt->ln_history, &prt->hst_tot, &prt->hst_pos); add_line_to_hist(ctx->line, ctx->len, ctx->ln_history, &ctx->hst_tot, &ctx->hst_pos);
execute(self->window, self, m, line, GLOBAL_COMMAND_MODE); line_info_add(self, NULL, NULL, NULL, line, PROMPT, 0, 0);
reset_buf(prt->line, &prt->pos, &prt->len); execute(ctx->history, self, m, line, GLOBAL_COMMAND_MODE);
reset_buf(ctx->line, &ctx->pos, &ctx->len);
}
} }
} }
static void prompt_onDraw(ToxWindow *self, Tox *m) static void prompt_onDraw(ToxWindow *self, Tox *m)
{ {
PromptBuf *prt = self->promptbuf; ChatContext *ctx = self->chatwin;
curs_set(1);
int x, y, x2, y2; int x, y, x2, y2;
getyx(self->window, y, x); getyx(ctx->history, y, x);
getmaxyx(self->window, y2, x2); getmaxyx(ctx->history, y2, x2);
wclrtobot(self->window);
if (!ctx->hst->scroll_mode) {
curs_set(1);
scrollok(ctx->history, 1);
}
line_info_print(self);
/* if len is >= screen width offset max x by X_OFST to account for prompt char */ /* if len is >= screen width offset max x by X_OFST to account for prompt char */
int px2 = prt->len >= x2 ? x2 : x2 - X_OFST; int px2 = ctx->len >= x2 ? x2 : x2 - X_OFST;
if (px2 <= 0) if (px2 <= 0)
return; return;
@ -263,33 +289,31 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
/* len offset to account for prompt char (0 if len is < width of screen) */ /* len offset to account for prompt char (0 if len is < width of screen) */
int p_ofst = px2 != x2 ? 0 : X_OFST; int p_ofst = px2 != x2 ? 0 : X_OFST;
if (prt->len > 0) { if (ctx->len > 0) {
uint8_t line[MAX_STR_SIZE]; uint8_t line[MAX_STR_SIZE];
if (wcs_to_mbs_buf(line, prt->line, MAX_STR_SIZE) == -1) if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1)
reset_buf(prt->line, &prt->pos, &prt->len); reset_buf(ctx->line, &ctx->pos, &ctx->len);
else else
mvwprintw(self->window, prt->orig_y, X_OFST, line); mvwprintw(ctx->history, ctx->orig_y, X_OFST, line);
int k = prt->orig_y + ((prt->len + p_ofst) / px2); int k = ctx->orig_y + ((ctx->len + p_ofst) / px2);
prt->at_bottom = k == y2 - 1; ctx->at_bottom = k == y2 - 1;
bool botm = k == y2; bool botm = k == y2;
bool edge = (prt->len + p_ofst) % px2 == 0; bool edge = (ctx->len + p_ofst) % px2 == 0;
/* move point of line origin up when input scrolls screen down */ /* move point of line origin up when input scrolls screen down */
if (prt->scroll && edge && botm) { if (edge && botm)
--prt->orig_y; --ctx->orig_y;
prt->scroll = false;
}
} else { /* Mark point of origin for new line */ } else { /* Mark point of origin for new line */
prt->orig_y = y; ctx->orig_y = y;
} }
wattron(self->window, COLOR_PAIR(GREEN)); wattron(ctx->history, COLOR_PAIR(GREEN));
mvwprintw(self->window, prt->orig_y, 0, "$ "); mvwprintw(ctx->history, ctx->orig_y, 0, "$ ");
wattroff(self->window, COLOR_PAIR(GREEN)); wattroff(ctx->history, COLOR_PAIR(GREEN));
StatusBar *statusbar = self->stb; StatusBar *statusbar = self->stb;
werase(statusbar->topline); werase(statusbar->topline);
@ -298,141 +322,189 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
if (statusbar->is_online) { if (statusbar->is_online) {
int colour = WHITE; int colour = WHITE;
char *status_text = "Unknown"; const uint8_t *status_text = "Unknown";
switch (statusbar->status) { switch (statusbar->status) {
case TOX_USERSTATUS_NONE: case TOX_USERSTATUS_NONE:
status_text = "Online"; status_text = "Online";
colour = GREEN; colour = GREEN;
break; break;
case TOX_USERSTATUS_AWAY: case TOX_USERSTATUS_AWAY:
status_text = "Away"; status_text = "Away";
colour = YELLOW; colour = YELLOW;
break; break;
case TOX_USERSTATUS_BUSY: case TOX_USERSTATUS_BUSY:
status_text = "Busy"; status_text = "Busy";
colour = RED; colour = RED;
break; break;
case TOX_USERSTATUS_INVALID:
status_text = "ERROR";
colour = MAGENTA;
break;
} }
wattron(statusbar->topline, A_BOLD);
wprintw(statusbar->topline, " %s ", statusbar->nick);
wattron(statusbar->topline, A_BOLD);
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);
wattron(statusbar->topline, A_BOLD);
wprintw(statusbar->topline, " %s", statusbar->nick);
wattroff(statusbar->topline, A_BOLD);
} else { } else {
wprintw(statusbar->topline, "[Offline]");
wattron(statusbar->topline, A_BOLD); wattron(statusbar->topline, A_BOLD);
wprintw(statusbar->topline, " %s ", statusbar->nick); wprintw(statusbar->topline, " %s ", statusbar->nick);
wattroff(statusbar->topline, A_BOLD); wattroff(statusbar->topline, A_BOLD);
wprintw(statusbar->topline, "[Offline]");
} }
wattron(statusbar->topline, A_BOLD); if (statusbar->statusmsg[0])
wprintw(statusbar->topline, " - %s", statusbar->statusmsg); wprintw(statusbar->topline, " - %s", statusbar->statusmsg);
wattroff(statusbar->topline, A_BOLD);
/* put cursor back in correct spot */ /* put cursor back in correct spot */
int y_m = prt->orig_y + ((prt->pos + p_ofst) / px2); int y_m = ctx->orig_y + ((ctx->pos + p_ofst) / px2);
int x_m = (prt->pos + X_OFST) % x2; int x_m = (ctx->pos + X_OFST) % x2;
wmove(self->window, y_m, x_m); wmove(self->window, y_m, x_m);
} }
static void prompt_onInit(ToxWindow *self, Tox *m) static void prompt_onConnectionChange(ToxWindow *self, Tox *m, int32_t friendnum , uint8_t status)
{
scrollok(self->window, true);
execute(self->window, self, m, "/help", GLOBAL_COMMAND_MODE);
wclrtoeol(self->window);
}
static void prompt_onConnectionChange(ToxWindow *self, Tox *m, int friendnum , uint8_t status)
{ {
if (friendnum < 0) if (friendnum < 0)
return; return;
prep_prompt_win(); ChatContext *ctx = self->chatwin;
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'}; uint8_t nick[TOX_MAX_NAME_LENGTH] = {0};
int n_len = tox_get_name(m, friendnum, nick);
n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH - 1);
if (tox_get_name(m, friendnum, nick) == -1) if (!nick[0]) {
return;
if (!nick[0])
snprintf(nick, sizeof(nick), "%s", UNKNOWN_NAME); snprintf(nick, sizeof(nick), "%s", UNKNOWN_NAME);
n_len = strlen(UNKNOWN_NAME);
}
wprintw(self->window, "\n"); nick[n_len] = '\0';
print_time(self->window);
uint8_t timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt);
uint8_t *msg;
if (status == 1) { if (status == 1) {
wattron(self->window, COLOR_PAIR(GREEN)); msg = "has come online";
wattron(self->window, A_BOLD); line_info_add(self, timefrmt, nick, NULL, msg, CONNECTION, 0, GREEN);
wprintw(self->window, "* %s ", nick); write_to_log(msg, nick, ctx->log, true);
wattroff(self->window, A_BOLD);
wprintw(self->window, "has come online\n");
wattroff(self->window, COLOR_PAIR(GREEN));
alert_window(self, WINDOW_ALERT_2, false); alert_window(self, WINDOW_ALERT_2, false);
} else { } else {
wattron(self->window, COLOR_PAIR(RED)); msg = "has gone offline";
wattron(self->window, A_BOLD); line_info_add(self, timefrmt, nick, NULL, msg, CONNECTION, 0, RED);
wprintw(self->window, "* %s ", nick); write_to_log(msg, nick, ctx->log, true);
wattroff(self->window, A_BOLD);
wprintw(self->window, "has gone offline\n");
wattroff(self->window, COLOR_PAIR(RED));
} }
} }
static void prompt_onFriendRequest(ToxWindow *self, uint8_t *key, uint8_t *data, uint16_t length) static void prompt_onFriendRequest(ToxWindow *self, Tox *m, uint8_t *key, uint8_t *data, uint16_t length)
{ {
// make sure message data is null-terminated data[length] = '\0';
data[length - 1] = 0;
prep_prompt_win(); ChatContext *ctx = self->chatwin;
wprintw(self->window, "\n");
print_time(self->window); uint8_t timefrmt[TIME_STR_SIZE];
wprintw(self->window, "Friend request with the message: '%s'\n", data); get_time_str(timefrmt);
uint8_t msg[MAX_STR_SIZE];
snprintf(msg, sizeof(msg), "Friend request with the message '%s'", data);
line_info_add(self, timefrmt, NULL, NULL, msg, SYS_MSG, 0, 0);
write_to_log(msg, "", ctx->log, true);
int n = add_friend_request(key); 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.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
write_to_log(errmsg, "", ctx->log, true);
return; return;
} }
wprintw(self->window, "Type \"/accept %d\" to accept it.\n", n); snprintf(msg, sizeof(msg), "Type \"/accept %d\" to accept it.", n);
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
alert_window(self, WINDOW_ALERT_1, true); alert_window(self, WINDOW_ALERT_1, true);
} }
void prompt_init_statusbar(ToxWindow *self, Tox *m) void prompt_init_statusbar(ToxWindow *self, Tox *m)
{ {
int x, y; int x2, y2;
getmaxyx(self->window, y, x); getmaxyx(self->window, y2, x2);
/* Init statusbar info */ /* Init statusbar info */
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];
tox_get_self_name(m, nick, TOX_MAX_NAME_LENGTH);
snprintf(statusbar->nick, sizeof(statusbar->nick), "%s", nick);
/* temporary until statusmessage saving works */
uint8_t ver[strlen(TOXICVER) + 1];
uint8_t statusmsg[MAX_STR_SIZE]; uint8_t statusmsg[MAX_STR_SIZE];
pthread_mutex_lock(&Winthread.lock);
uint16_t n_len = tox_get_self_name(m, nick);
uint16_t s_len = tox_get_self_status_message(m, statusmsg, MAX_STR_SIZE);
uint8_t status = tox_get_self_user_status(m);
pthread_mutex_unlock(&Winthread.lock);
nick[n_len] = '\0';
statusmsg[s_len] = '\0';
/* load prev status message or show toxic version if it has never been set */
uint8_t ver[strlen(TOXICVER) + 1];
strcpy(ver, TOXICVER); strcpy(ver, TOXICVER);
uint8_t *toxic_ver = strtok(ver, "_"); const uint8_t *toxic_ver = strtok(ver, "_");
if (toxic_ver != NULL) if ( (!strcmp("Online", statusmsg) || !strncmp("Toxing on Toxic", statusmsg, 15)) && toxic_ver != NULL) {
snprintf(statusmsg, MAX_STR_SIZE, "Toxing on Toxic v.%s", toxic_ver); snprintf(statusmsg, MAX_STR_SIZE, "Toxing on Toxic v.%s", toxic_ver);
else s_len = strlen(statusmsg);
snprintf(statusmsg, MAX_STR_SIZE, "Toxing on Toxic hacker edition"); statusmsg[s_len] = '\0';
}
m_set_statusmessage(m, statusmsg, strlen(statusmsg) + 1); prompt_update_statusmessage(prompt, statusmsg, s_len);
snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg); prompt_update_status(prompt, status);
prompt_update_nick(prompt, nick, n_len);
/* Init statusbar subwindow */ /* Init statusbar subwindow */
statusbar->topline = subwin(self->window, 2, x, 0, 0); statusbar->topline = subwin(self->window, 2, x2, 0, 0);
}
static void prompt_onInit(ToxWindow *self, Tox *m)
{
ChatContext *ctx = self->chatwin;
curs_set(1);
int y2, x2;
getmaxyx(self->window, y2, x2);
ctx->history = subwin(self->window, y2, x2, 0, 0);
scrollok(ctx->history, 1);
ctx->log = malloc(sizeof(struct chatlog));
ctx->hst = malloc(sizeof(struct history));
if (ctx->log == NULL || ctx->hst == NULL) {
endwin();
fprintf(stderr, "malloc() failed. Aborting...\n");
exit(EXIT_FAILURE);
}
memset(ctx->log, 0, sizeof(struct chatlog));
memset(ctx->hst, 0, sizeof(struct history));
line_info_init(ctx->hst);
if (user_settings->autolog == AUTOLOG_ON) {
uint8_t myid[TOX_FRIEND_ADDRESS_SIZE];
tox_get_address(m, myid);
log_enable(self->name, myid, ctx->log);
}
execute(ctx->history, self, m, "/help", GLOBAL_COMMAND_MODE);
wmove(ctx->history, y2 - 1, 2);
} }
ToxWindow new_prompt(void) ToxWindow new_prompt(void)
@ -441,6 +513,7 @@ ToxWindow new_prompt(void)
memset(&ret, 0, sizeof(ret)); memset(&ret, 0, sizeof(ret));
ret.active = true; ret.active = true;
ret.is_prompt = true;
ret.onKey = &prompt_onKey; ret.onKey = &prompt_onKey;
ret.onDraw = &prompt_onDraw; ret.onDraw = &prompt_onDraw;
@ -450,11 +523,11 @@ ToxWindow new_prompt(void)
strcpy(ret.name, "prompt"); strcpy(ret.name, "prompt");
PromptBuf *promptbuf = calloc(1, sizeof(PromptBuf)); ChatContext *chatwin = calloc(1, sizeof(ChatContext));
StatusBar *stb = calloc(1, sizeof(StatusBar)); StatusBar *stb = calloc(1, sizeof(StatusBar));
if (stb != NULL && promptbuf != NULL) { if (stb != NULL && chatwin != NULL) {
ret.promptbuf = promptbuf; ret.chatwin = chatwin;
ret.stb = stb; ret.stb = stb;
} else { } else {
endwin(); endwin();

View File

@ -1,5 +1,23 @@
/* /* 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
@ -7,14 +25,19 @@
#define X_OFST 2 /* offset to account for prompt char */ #define X_OFST 2 /* offset to account for prompt char */
#define AC_NUM_GLOB_COMMANDS 14
#ifdef _SUPPORT_AUDIO
#define AC_NUM_GLOB_COMMANDS 17
#else
#define AC_NUM_GLOB_COMMANDS 15
#endif /* _SUPPORT_AUDIO */
ToxWindow new_prompt(void); ToxWindow new_prompt(void);
void prep_prompt_win(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);
void prompt_update_status(ToxWindow *prompt, TOX_USERSTATUS status); void prompt_update_status(ToxWindow *prompt, uint8_t status);
void prompt_update_connectionstatus(ToxWindow *prompt, bool is_connected); void prompt_update_connectionstatus(ToxWindow *prompt, bool is_connected);
#endif /* end of include guard: PROMPT_H_UZYGWFFL */ #endif /* end of include guard: PROMPT_H_UZYGWFFL */

159
src/settings.c Normal file
View File

@ -0,0 +1,159 @@
/* settings.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 "configdir.h"
#include "audio_call.h"
#include "settings.h"
#include "line_info.h"
static void uset_autolog(struct user_settings *s, int val);
static void uset_time(struct user_settings *s, int val);
static void uset_alerts(struct user_settings *s, int val);
static void uset_colours(struct user_settings *s, int val);
static void uset_ain_dev(struct user_settings *s, int val);
static void uset_aout_dev(struct user_settings *s, int val);
static void uset_hst_size(struct user_settings *s, int val);
struct {
const char *name;
void (*func)(struct user_settings *s, int val);
} user_settings_list[] = {
{ "autolog", uset_autolog },
{ "time", uset_time },
{ "disable_alerts", uset_alerts },
{ "colour_theme", uset_colours },
{ "audio_in_dev", uset_ain_dev },
{ "audio_out_dev", uset_aout_dev },
{ "history_size", uset_hst_size },
};
static void uset_autolog(struct user_settings *s, int val)
{
/* default off if invalid value */
s->autolog = val == AUTOLOG_ON ? AUTOLOG_ON : AUTOLOG_OFF;
}
static void uset_time(struct user_settings *s, int val)
{
/* default to 24 hour time if invalid value */
s->time = val == TIME_12 ? TIME_12 : TIME_24;
}
static void uset_alerts(struct user_settings *s, int val)
{
/* alerts default on if invalid value */
s->alerts = val == ALERTS_DISABLED ? ALERTS_DISABLED : ALERTS_ENABLED;
}
static void uset_colours(struct user_settings *s, int val)
{
/* use default toxic colours if invalid value */
s->colour_theme = val == NATIVE_COLS ? NATIVE_COLS : DFLT_COLS;
}
static void uset_ain_dev(struct user_settings *s, int val)
{
if (val < 0 || val > MAX_DEVICES)
val = (long int) 0;
s->audio_in_dev = (long int) val;
}
static void uset_aout_dev(struct user_settings *s, int val)
{
if (val < 0 || val > MAX_DEVICES)
val = (long int) 0;
s->audio_out_dev = (long int) val;
}
static void uset_hst_size(struct user_settings *s, int val)
{
/* if val is out of range use default history size */
s->history_size = (val > MAX_HISTORY || val < MIN_HISTORY) ? DFLT_HST_SIZE : val;
}
static void set_default_settings(struct user_settings *s)
{
uset_autolog(s, AUTOLOG_OFF);
uset_time(s, TIME_24);
uset_alerts(s, ALERTS_ENABLED);
uset_colours(s, DFLT_COLS);
uset_ain_dev(s, 0);
uset_aout_dev(s, 0);
uset_hst_size(s, DFLT_HST_SIZE);
}
int settings_load(struct user_settings *s, char *path)
{
char *user_config_dir = get_user_config_dir();
FILE *fp = NULL;
char dflt_path[MAX_STR_SIZE];
if (path) {
fp = fopen(path, "r");
} else {
snprintf(dflt_path, sizeof(dflt_path), "%s%stoxic.conf", user_config_dir, CONFIGDIR);
fp = fopen(dflt_path, "r");
}
free(user_config_dir);
set_default_settings(s);
if (fp == NULL && !path) {
if ((fp = fopen(dflt_path, "w")) == NULL)
return -1;
} else if (fp == NULL && path) {
return -1;
}
char line[MAX_STR_SIZE];
while (fgets(line, sizeof(line), fp)) {
if (line[0] == '#' || !line[0])
continue;
char *name = strtok(line, ":");
char *val_s = strtok(NULL, ";");
if (name == NULL || val_s == NULL)
continue;
int val = atoi(val_s);
int i;
for (i = 0; i < NUM_SETTINGS; ++i) {
if (!strcmp(user_settings_list[i].name, name)) {
(user_settings_list[i].func)(s, val);
break;
}
}
}
fclose(fp);
return 0;
}

52
src/settings.h Normal file
View File

@ -0,0 +1,52 @@
/* settings.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/>.
*
*/
#define NUM_SETTINGS 7
/* holds user setting values */
struct user_settings {
int autolog; /* boolean */
int alerts; /* boolean */
int time; /* 12 or 24 */
int colour_theme; /* boolean (0 for default toxic colours) */
long int audio_in_dev;
long int audio_out_dev;
int history_size; /* int between MIN_HISTORY and MAX_HISTORY */
};
enum {
AUTOLOG_OFF = 0,
AUTOLOG_ON = 1,
TIME_24 = 24,
TIME_12 = 12,
ALERTS_DISABLED = 1,
ALERTS_ENABLED = 0,
NATIVE_COLS = 1,
DFLT_COLS = 0,
DFLT_HST_SIZE = 700,
};
int settings_load(struct user_settings *s, char *path);

View File

@ -1,7 +1,29 @@
/* /* toxic_strings.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
#include "config.h"
#endif
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -19,7 +41,7 @@ void add_char_to_buf(wchar_t *buf, size_t *pos, size_t *len, wint_t ch)
int i; int i;
for (i = *len; i >= *pos && i >= 0; --i) for (i = *len; i >= *pos && i >= 0; --i)
buf[i+1] = buf[i]; buf[i + 1] = buf[i];
buf[(*pos)++] = ch; buf[(*pos)++] = ch;
++(*len); ++(*len);
@ -34,8 +56,8 @@ void del_char_buf_bck(wchar_t *buf, size_t *pos, size_t *len)
int i; int i;
/* similar to add_char_to_buf but deletes a char */ /* similar to add_char_to_buf but deletes a char */
for (i = *pos-1; i <= *len; ++i) for (i = *pos - 1; i <= *len; ++i)
buf[i] = buf[i+1]; buf[i] = buf[i + 1];
--(*pos); --(*pos);
--(*len); --(*len);
@ -50,7 +72,7 @@ void del_char_buf_frnt(wchar_t *buf, size_t *pos, size_t *len)
int i; int i;
for (i = *pos; i < *len; ++i) for (i = *pos; i < *len; ++i)
buf[i] = buf[i+1]; buf[i] = buf[i + 1];
--(*len); --(*len);
} }
@ -98,7 +120,7 @@ static void shift_hist_back(wchar_t (*hst)[MAX_STR_SIZE], int *hst_tot)
int n = MAX_LINE_HIST - HIST_PURGE; int n = MAX_LINE_HIST - HIST_PURGE;
for (i = 0; i < n; ++i) for (i = 0; i < n; ++i)
wmemcpy(hst[i], hst[i+HIST_PURGE], MAX_STR_SIZE); wmemcpy(hst[i], hst[i + HIST_PURGE], MAX_STR_SIZE);
*hst_tot = n; *hst_tot = n;
} }
@ -116,23 +138,25 @@ void add_line_to_hist(const wchar_t *buf, size_t len, wchar_t (*hst)[MAX_STR_SIZ
++(*hst_tot); ++(*hst_tot);
*hst_pos = *hst_tot; *hst_pos = *hst_tot;
wmemcpy(hst[*hst_tot-1], buf, len + 1); 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. /* 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. */ 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], 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) int hst_tot, int *hst_pos, int key_dir)
{ {
if (key_dir == LN_HIST_MV_UP) { if (key_dir == MOVE_UP) {
if (--(*hst_pos) < 0) { if (--(*hst_pos) < 0) {
++(*hst_pos); *hst_pos = 0;
beep(); beep();
} }
} else { } else {
if (++(*hst_pos) >= hst_tot) { if (++(*hst_pos) >= hst_tot) {
--(*hst_pos); *hst_pos = hst_tot;
beep(); reset_buf(buf, pos, len);
return; return;
} }
} }
@ -162,6 +186,7 @@ int complete_line(wchar_t *buf, size_t *pos, size_t *len, const void *list, int
const uint8_t *L = (uint8_t *) list; const uint8_t *L = (uint8_t *) list;
uint8_t ubuf[MAX_STR_SIZE]; uint8_t ubuf[MAX_STR_SIZE];
/* work with multibyte string copy of buf for simplicity */ /* work with multibyte string copy of buf for simplicity */
if (wcs_to_mbs_buf(ubuf, buf, MAX_STR_SIZE) == -1) if (wcs_to_mbs_buf(ubuf, buf, MAX_STR_SIZE) == -1)
return -1; return -1;
@ -175,6 +200,7 @@ int complete_line(wchar_t *buf, size_t *pos, size_t *len, const void *list, int
if (!sub++) { if (!sub++) {
sub = tmp; sub = tmp;
if (sub[0] != '/') /* make sure it's not a command */ if (sub[0] != '/') /* make sure it's not a command */
n_endchrs = 2; n_endchrs = 2;
} }
@ -189,7 +215,8 @@ int complete_line(wchar_t *buf, size_t *pos, size_t *len, const void *list, int
/* look for a match in list */ /* look for a match in list */
for (i = 0; i < n_items; ++i) { for (i = 0; i < n_items; ++i) {
match = &L[i*size]; match = &L[i * size];
if (is_match = strncasecmp(match, sub, s_len) == 0) if (is_match = strncasecmp(match, sub, s_len) == 0)
break; break;
} }
@ -200,7 +227,7 @@ int complete_line(wchar_t *buf, size_t *pos, size_t *len, const void *list, int
/* put match in correct spot in buf and append endchars (space or ": ") */ /* put match in correct spot in buf and append endchars (space or ": ") */
const uint8_t *endchrs = n_endchrs == 1 ? " " : ": "; const uint8_t *endchrs = n_endchrs == 1 ? " " : ": ";
int m_len = strlen(match); int m_len = strlen(match);
int strt = (int) *pos - s_len; int strt = (int) * pos - s_len;
int diff = m_len - s_len + n_endchrs; int diff = m_len - s_len + n_endchrs;
if (*len + diff > MAX_STR_SIZE) if (*len + diff > MAX_STR_SIZE)
@ -209,8 +236,8 @@ int complete_line(wchar_t *buf, size_t *pos, size_t *len, const void *list, int
uint8_t tmpend[MAX_STR_SIZE]; uint8_t tmpend[MAX_STR_SIZE];
strcpy(tmpend, &ubuf[*pos]); strcpy(tmpend, &ubuf[*pos]);
strcpy(&ubuf[strt], match); strcpy(&ubuf[strt], match);
strcpy(&ubuf[strt+m_len], endchrs); strcpy(&ubuf[strt + m_len], endchrs);
strcpy(&ubuf[strt+m_len+n_endchrs], tmpend); strcpy(&ubuf[strt + m_len + n_endchrs], tmpend);
/* convert to widechar and copy back to original buf */ /* convert to widechar and copy back to original buf */
wchar_t newbuf[MAX_STR_SIZE]; wchar_t newbuf[MAX_STR_SIZE];

View File

@ -1,5 +1,23 @@
/* /* toxic_strings.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/>.
*
*/ */
/* Adds char to buffer at pos */ /* Adds char to buffer at pos */
@ -30,11 +48,6 @@ void reset_buf(wchar_t *buf, size_t *pos, size_t *len);
Returns the difference between the old len and new len of buf on success, -1 if error */ 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); 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. */ /* 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, void add_line_to_hist(const wchar_t *buf, size_t len, wchar_t (*hst)[MAX_STR_SIZE], int *hst_tot,
int *hst_pos); int *hst_pos);

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
@ -10,12 +28,17 @@
#endif #endif
#include <curses.h> #include <curses.h>
#include <pthread.h>
#include <wctype.h> #include <wctype.h>
#include <wchar.h> #include <wchar.h>
#include <tox/tox.h> #include <tox/tox.h>
#define UNKNOWN_NAME "Unknown" #ifdef _SUPPORT_AUDIO
#include <tox/toxav.h>
#endif /* _SUPPORT_AUDIO */
#define UNKNOWN_NAME "Anonymous"
#define MAX_WINDOWS_NUM 32 #define MAX_WINDOWS_NUM 32
#define MAX_FRIENDS_NUM 100 #define MAX_FRIENDS_NUM 100
@ -27,6 +50,7 @@
#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 KEY_IDENT_DIGITS 2 /* number of hex digits to display for the pub-key based identifier */
#define TIME_STR_SIZE 16
#define EXIT_SUCCESS 0 #define EXIT_SUCCESS 0
#define EXIT_FAILURE 1 #define EXIT_FAILURE 1
@ -36,6 +60,9 @@
#define T_KEY_DISCARD 0x15 /* ctrl-u */ #define T_KEY_DISCARD 0x15 /* ctrl-u */
#define T_KEY_NEXT 0x10 /* ctrl-p */ #define T_KEY_NEXT 0x10 /* ctrl-p */
#define T_KEY_PREV 0x0F /* ctrl-o */ #define T_KEY_PREV 0x0F /* ctrl-o */
#define T_KEY_C_E 0x05 /* ctrl-e */
#define T_KEY_C_A 0x01 /* ctrl-a */
#define T_KEY_ESC 0x1B /* ESC key */
/* Curses foreground colours (background is black) */ /* Curses foreground colours (background is black) */
enum { enum {
@ -56,9 +83,14 @@ enum {
WINDOW_ALERT_2, WINDOW_ALERT_2,
}; };
enum {
MOVE_UP,
MOVE_DOWN,
};
/* 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 StatusBar StatusBar;
@ -66,39 +98,62 @@ typedef struct PromptBuf PromptBuf;
typedef struct ChatContext ChatContext; typedef struct ChatContext ChatContext;
struct ToxWindow { struct ToxWindow {
void(*onKey)(ToxWindow *, Tox *, wint_t); void(*onKey)(ToxWindow *, Tox *, wint_t, bool);
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 *, Tox *, uint8_t *, uint8_t *, uint16_t);
void(*onFriendAdded)(ToxWindow *, Tox *, int, bool); void(*onFriendAdded)(ToxWindow *, Tox *, int32_t, bool);
void(*onConnectionChange)(ToxWindow *, Tox *, int, uint8_t); void(*onConnectionChange)(ToxWindow *, Tox *, int32_t, uint8_t);
void(*onMessage)(ToxWindow *, Tox *, int, uint8_t *, uint16_t); void(*onMessage)(ToxWindow *, Tox *, int32_t, uint8_t *, uint16_t);
void(*onNickChange)(ToxWindow *, Tox *, int, uint8_t *, uint16_t); void(*onNickChange)(ToxWindow *, Tox *, int32_t, uint8_t *, uint16_t);
void(*onStatusChange)(ToxWindow *, Tox *, int, TOX_USERSTATUS); void(*onStatusChange)(ToxWindow *, Tox *, int32_t, uint8_t);
void(*onStatusMessageChange)(ToxWindow *, int, uint8_t *, uint16_t); void(*onStatusMessageChange)(ToxWindow *, int32_t, uint8_t *, uint16_t);
void(*onAction)(ToxWindow *, Tox *, int, uint8_t *, uint16_t); void(*onAction)(ToxWindow *, Tox *, int32_t, uint8_t *, uint16_t);
void(*onGroupMessage)(ToxWindow *, Tox *, int, int, uint8_t *, uint16_t); void(*onGroupMessage)(ToxWindow *, Tox *, int, int, uint8_t *, uint16_t);
void(*onGroupAction)(ToxWindow *, Tox *, int, int, uint8_t *, uint16_t); void(*onGroupAction)(ToxWindow *, Tox *, int, int, uint8_t *, uint16_t);
void(*onGroupInvite)(ToxWindow *, Tox *, int, uint8_t *); void(*onGroupInvite)(ToxWindow *, Tox *, int32_t, uint8_t *);
void(*onGroupNamelistChange)(ToxWindow *, Tox*, int, int, uint8_t); void(*onGroupNamelistChange)(ToxWindow *, Tox *, int, int, uint8_t);
void(*onFileSendRequest)(ToxWindow *, Tox *, int, uint8_t, uint64_t, uint8_t *, uint16_t); void(*onFileSendRequest)(ToxWindow *, Tox *, int32_t, 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 *, int32_t, 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 *, int32_t, uint8_t, uint8_t *, uint16_t);
void(*onTypingChange)(ToxWindow *, Tox *, int, int); void(*onTypingChange)(ToxWindow *, Tox *, int32_t, uint8_t);
#ifdef _SUPPORT_AUDIO
void(*onInvite)(ToxWindow *, ToxAv *, int);
void(*onRinging)(ToxWindow *, ToxAv *, int);
void(*onStarting)(ToxWindow *, ToxAv *, int);
void(*onEnding)(ToxWindow *, ToxAv *, int);
void(*onError)(ToxWindow *, ToxAv *, int);
void(*onStart)(ToxWindow *, ToxAv *, int);
void(*onCancel)(ToxWindow *, ToxAv *, int);
void(*onReject)(ToxWindow *, ToxAv *, int);
void(*onEnd)(ToxWindow *, ToxAv *, int);
void(*onRequestTimeout)(ToxWindow *, ToxAv *, int);
void(*onPeerTimeout)(ToxWindow *, ToxAv *, int);
int call_index; /* If in a call will have this index set, otherwise it's -1.
* Don't modify outside av callbacks. */
#endif /* _SUPPORT_AUDIO */
char name[TOX_MAX_NAME_LENGTH]; char name[TOX_MAX_NAME_LENGTH];
int num; int32_t num; /* corresponds to friendnumber in chat windows */
bool active; bool active;
int x; int x;
/* window type identifiers */
bool is_chat;
bool is_groupchat;
bool is_prompt;
bool alert0; bool alert0;
bool alert1; bool alert1;
bool alert2; bool alert2;
ChatContext *chatwin; ChatContext *chatwin;
PromptBuf *promptbuf;
StatusBar *stb; StatusBar *stb;
WINDOW *popup;
WINDOW *window; WINDOW *window;
}; };
@ -109,7 +164,7 @@ struct StatusBar {
uint16_t statusmsg_len; uint16_t statusmsg_len;
uint8_t nick[TOX_MAX_NAME_LENGTH]; uint8_t nick[TOX_MAX_NAME_LENGTH];
uint16_t nick_len; uint16_t nick_len;
TOX_USERSTATUS status; uint8_t status;
bool is_online; bool is_online;
}; };
@ -121,79 +176,85 @@ struct ChatContext {
size_t pos; size_t pos;
size_t len; size_t len;
wchar_t ln_history[MAX_LINE_HIST][MAX_STR_SIZE]; wchar_t ln_history[MAX_LINE_HIST][MAX_STR_SIZE]; /* history for input lines/commands */
int hst_pos; int hst_pos;
int hst_tot; int hst_tot;
bool self_is_typing; struct history *hst;
struct chatlog *log;
uint8_t self_is_typing;
WINDOW *history; WINDOW *history;
WINDOW *linewin; WINDOW *linewin;
WINDOW *sidebar; WINDOW *sidebar;
};
/* prompt window/buffer holder */ /* specific for prompt */
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 */ bool at_bottom; /* true if line end is at bottom of window */
int orig_y; /* y axis point of line origin */ int orig_y; /* y axis point of line origin */
bool scroll; /* used for prompt window hack to determine when to scroll down */
wchar_t ln_history[MAX_LINE_HIST][MAX_STR_SIZE];
int hst_pos;
int hst_tot;
WINDOW *linewin;
}; };
/* Start file transfer code */ /* Start file transfer code */
#define FILE_PIECE_SIZE 2048 /* must be >= (MAX_CRYPTO_DATA_SIZE - 2) in toxcore/net_crypto.h */
#define MAX_FILES 256 #define MAX_FILES 256
#define FILE_PIECE_SIZE 1024
#define TIMEOUT_FILESENDER 300 #define TIMEOUT_FILESENDER 300
typedef struct { typedef struct {
FILE *file; FILE *file;
ToxWindow *toxwin; ToxWindow *toxwin;
int friendnum; int32_t friendnum;
bool active; bool active;
uint8_t filenum; int filenum;
uint8_t nextpiece[FILE_PIECE_SIZE]; uint8_t nextpiece[FILE_PIECE_SIZE];
uint16_t piecelen; uint16_t piecelen;
uint8_t pathname[MAX_STR_SIZE]; uint8_t pathname[MAX_STR_SIZE];
uint64_t timestamp; uint64_t timestamp;
uint64_t size;
uint32_t line_id;
} FileSender; } FileSender;
struct FileReceiver { struct FileReceiver {
uint8_t filenames[MAX_FILES][MAX_STR_SIZE]; uint8_t filenames[MAX_FILES][MAX_STR_SIZE];
FILE *files[MAX_FILES];
bool pending[MAX_FILES]; bool pending[MAX_FILES];
uint64_t size[MAX_FILES];
uint32_t line_id;
}; };
/* End file transfer code */ /* End file transfer code */
void on_request(uint8_t *public_key, uint8_t *data, uint16_t length, void *userdata); struct _Winthread {
void on_connectionchange(Tox *m, int friendnumber, uint8_t status, void *userdata); pthread_t tid;
void on_message(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void *userdata); pthread_mutex_t lock;
void on_action(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void *userdata); };
void on_nickchange(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void *userdata);
void on_statuschange(Tox *m, int friendnumber, TOX_USERSTATUS status, void *userdata); void on_request(Tox *m, uint8_t *public_key, uint8_t *data, uint16_t length, void *userdata);
void on_statusmessagechange(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void *userdata); void on_connectionchange(Tox *m, int32_t friendnumber, uint8_t status, void *userdata);
void on_friendadded(Tox *m, int friendnumber, bool sort); void on_message(Tox *m, int32_t friendnumber, uint8_t *string, uint16_t length, void *userdata);
void on_action(Tox *m, int32_t friendnumber, uint8_t *string, uint16_t length, void *userdata);
void on_nickchange(Tox *m, int32_t friendnumber, uint8_t *string, uint16_t length, void *userdata);
void on_statuschange(Tox *m, int32_t friendnumber, uint8_t status, void *userdata);
void on_statusmessagechange(Tox *m, int32_t friendnumber, uint8_t *string, uint16_t length, void *userdata);
void on_friendadded(Tox *m, int32_t friendnumber, bool sort);
void on_groupmessage(Tox *m, int groupnumber, int peernumber, uint8_t *message, uint16_t length, void *userdata); void on_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_groupaction(Tox *m, int groupnumber, int peernumber, uint8_t *action, uint16_t length, void *userdata);
void on_groupinvite(Tox *m, int friendnumber, uint8_t *group_pub_key, void *userdata); void on_groupinvite(Tox *m, int32_t friendnumber, uint8_t *group_pub_key, void *userdata);
void on_group_namelistchange(Tox *m, int groupnumber, int peernumber, uint8_t change, void *userdata); void on_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, int32_t friendnumber, uint8_t filenumber, uint64_t filesize, uint8_t *pathname,
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); uint16_t pathname_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_control(Tox *m, int32_t friendnumber, uint8_t receive_send, uint8_t filenumber, uint8_t control_type,
void on_typing_change(Tox *m, int friendnumber, int is_typing, void *userdata); uint8_t *data, uint16_t length, void *userdata);
void on_file_data(Tox *m, int32_t friendnumber, uint8_t filenumber, uint8_t *data, uint16_t length, void *userdata);
void on_typing_change(Tox *m, int32_t friendnumber, uint8_t is_typing, void *userdata);
ToxWindow *init_windows(Tox *m); 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); int get_num_active_windows(void);
/* cleans up all chat and groupchat windows (should only be called on shutdown) */
void kill_all_windows(void);
#endif #endif

View File

@ -1,33 +1,59 @@
/* 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
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <pthread.h>
#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;
extern struct _Winthread Winthread;
static ToxWindow windows[MAX_WINDOWS_NUM]; static ToxWindow windows[MAX_WINDOWS_NUM];
static ToxWindow *active_window; static ToxWindow *active_window;
extern ToxWindow *prompt; extern ToxWindow *prompt;
static int num_active_windows;
/* CALLBACKS START */ /* CALLBACKS START */
void on_request(uint8_t *public_key, uint8_t *data, uint16_t length, void *userdata) void on_request(Tox *m, uint8_t *public_key, uint8_t *data, uint16_t length, void *userdata)
{ {
int i; int i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) { for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onFriendRequest != NULL) if (windows[i].onFriendRequest != NULL)
windows[i].onFriendRequest(&windows[i], public_key, data, length); windows[i].onFriendRequest(&windows[i], m, public_key, data, length);
} }
} }
void on_connectionchange(Tox *m, int friendnumber, uint8_t status, void *userdata) void on_connectionchange(Tox *m, int32_t friendnumber, uint8_t status, void *userdata)
{ {
int i; int i;
@ -37,7 +63,7 @@ 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) void on_typing_change(Tox *m, int32_t friendnumber, uint8_t is_typing, void *userdata)
{ {
int i; int i;
@ -47,7 +73,7 @@ void on_typing_change(Tox *m, int friendnumber, int is_typing, void *userdata)
} }
} }
void on_message(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void *userdata) void on_message(Tox *m, int32_t friendnumber, uint8_t *string, uint16_t length, void *userdata)
{ {
int i; int i;
@ -57,7 +83,7 @@ void on_message(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void
} }
} }
void on_action(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void *userdata) void on_action(Tox *m, int32_t friendnumber, uint8_t *string, uint16_t length, void *userdata)
{ {
int i; int i;
@ -67,7 +93,7 @@ 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, int32_t friendnumber, uint8_t *string, uint16_t length, void *userdata)
{ {
if (friendnumber < 0 || friendnumber > MAX_FRIENDS_NUM) if (friendnumber < 0 || friendnumber > MAX_FRIENDS_NUM)
return; return;
@ -83,7 +109,7 @@ void on_nickchange(Tox *m, int friendnumber, uint8_t *string, uint16_t length, v
wprintw(prompt->window, "\nCould not store Tox data\n"); wprintw(prompt->window, "\nCould not store Tox data\n");
} }
void on_statusmessagechange(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void *userdata) void on_statusmessagechange(Tox *m, int32_t friendnumber, uint8_t *string, uint16_t length, void *userdata)
{ {
int i; int i;
@ -93,7 +119,7 @@ void on_statusmessagechange(Tox *m, int friendnumber, uint8_t *string, uint16_t
} }
} }
void on_statuschange(Tox *m, int friendnumber, TOX_USERSTATUS status, void *userdata) void on_statuschange(Tox *m, int32_t friendnumber, uint8_t status, void *userdata)
{ {
int i; int i;
@ -103,7 +129,7 @@ void on_statuschange(Tox *m, int friendnumber, TOX_USERSTATUS status, void *user
} }
} }
void on_friendadded(Tox *m, int friendnumber, bool sort) void on_friendadded(Tox *m, int32_t friendnumber, bool sort)
{ {
int i; int i;
@ -138,7 +164,7 @@ void on_groupaction(Tox *m, int groupnumber, int peernumber, uint8_t *action, ui
} }
} }
void on_groupinvite(Tox *m, int friendnumber, uint8_t *group_pub_key, void *userdata) void on_groupinvite(Tox *m, int32_t friendnumber, uint8_t *group_pub_key, void *userdata)
{ {
int i; int i;
@ -158,7 +184,7 @@ void on_group_namelistchange(Tox *m, int groupnumber, int peernumber, uint8_t ch
} }
} }
void on_file_sendrequest(Tox *m, int friendnumber, uint8_t filenumber, uint64_t filesize, void on_file_sendrequest(Tox *m, int32_t friendnumber, uint8_t filenumber, uint64_t filesize,
uint8_t *filename, uint16_t filename_length, void *userdata) uint8_t *filename, uint16_t filename_length, void *userdata)
{ {
int i; int i;
@ -170,7 +196,7 @@ void on_file_sendrequest(Tox *m, int friendnumber, uint8_t filenumber, uint64_t
} }
} }
void on_file_control (Tox *m, int friendnumber, uint8_t receive_send, uint8_t filenumber, void on_file_control (Tox *m, int32_t friendnumber, uint8_t receive_send, uint8_t filenumber,
uint8_t control_type, uint8_t *data, uint16_t length, void *userdata) uint8_t control_type, uint8_t *data, uint16_t length, void *userdata)
{ {
int i; int i;
@ -182,7 +208,7 @@ void on_file_control (Tox *m, int friendnumber, uint8_t receive_send, uint8_t fi
} }
} }
void on_file_data(Tox *m, int friendnumber, uint8_t filenumber, uint8_t *data, uint16_t length, void on_file_data(Tox *m, int32_t friendnumber, uint8_t filenumber, uint8_t *data, uint16_t length,
void *userdata) void *userdata)
{ {
int i; int i;
@ -210,13 +236,18 @@ int add_window(Tox *m, ToxWindow w)
if (w.window == NULL) if (w.window == NULL)
return -1; return -1;
#ifdef URXVT_FIX #ifdef URXVT_FIX
/* Fixes text color problem on some terminals. */ /* Fixes text color problem on some terminals. */
wbkgd(w.window, COLOR_PAIR(6)); wbkgd(w.window, COLOR_PAIR(6));
#endif #endif
windows[i] = w; windows[i] = w;
if (w.onInit)
w.onInit(&w, m); w.onInit(&w, m);
++num_active_windows;
return i; return i;
} }
@ -226,13 +257,14 @@ int add_window(Tox *m, ToxWindow w)
/* Deletes window w and cleans up */ /* Deletes window w and cleans up */
void del_window(ToxWindow *w) void del_window(ToxWindow *w)
{ {
active_window = windows; // Go to prompt screen active_window = windows; /* Go to prompt screen */
delwin(w->window); delwin(w->window);
memset(w, 0, sizeof(ToxWindow)); memset(w, 0, sizeof(ToxWindow));
clear(); clear();
refresh(); refresh();
--num_active_windows;
} }
/* Shows next window when tab or back-tab is pressed */ /* Shows next window when tab or back-tab is pressed */
@ -251,7 +283,7 @@ void set_next_window(int ch)
if (active_window->window) if (active_window->window)
return; return;
if (active_window == inf) { // infinite loop check if (active_window == inf) { /* infinite loop check */
endwin(); endwin();
fprintf(stderr, "set_next_window() failed. Aborting...\n"); fprintf(stderr, "set_next_window() failed. Aborting...\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
@ -363,27 +395,65 @@ void draw_active_window(Tox *m)
wrefresh(a->window); wrefresh(a->window);
/* Handle input */ /* Handle input */
bool ltr;
#ifdef HAVE_WIDECHAR #ifdef HAVE_WIDECHAR
wget_wch(stdscr, &ch); int status = wget_wch(stdscr, &ch);
if (status == ERR)
return;
if (status == OK)
ltr = iswprint(ch);
else /* if (status == KEY_CODE_YES) */
ltr = false;
#else #else
ch = getch(); ch = getch();
if (ch == ERR)
return;
/* TODO verify if this works */
ltr = isprint(ch);
#endif #endif
if (ch == T_KEY_NEXT || ch == T_KEY_PREV) if (!ltr && (ch == T_KEY_NEXT || ch == T_KEY_PREV)) {
set_next_window((int) ch); set_next_window((int) ch);
else if (ch != ERR) } else {
a->onKey(a, m, ch); pthread_mutex_lock(&Winthread.lock);
a->onKey(a, m, ch, ltr);
pthread_mutex_unlock(&Winthread.lock);
}
} }
int num_active_windows(void) /* refresh inactive windows to prevent scrolling bugs.
call at least once per second */
void refresh_inactive_windows(void)
{ {
int count = 0;
int i; int i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) { for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].active) ToxWindow *a = &windows[i];
++count;
}
return count; if (a->active && a != active_window && !a->is_prompt) /* if prompt doesn't have scroll mode */
line_info_print(a);
}
}
int get_num_active_windows(void)
{
return num_active_windows;
}
/* 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]);
}
} }