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

Compare commits

...

220 Commits
0.2.6 ... 0.4.0

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
0013dae552 Merge branch 'master' of https://github.com/Tox/toxic 2014-02-23 05:40:21 -05:00
b018aa384e small fix and don't show typing alert for /commands 2014-02-23 05:38:44 -05:00
ad8f99dae4 Revert that 2014-02-23 02:19:22 -08:00
5b9d3f6f62 fix segfault 2014-02-23 05:15:48 -05:00
93bcecde70 Update README.md 2014-02-23 02:11:51 -08:00
6be1c907d9 Fallback to loading /usr/share/toxic/DHTservers.
First try ~/.config/tox/DHTservers, and fallback to the /usr/share
one if the former fails.

This should allow people just starting toxic for the first time to
connect to the DHT.
2014-02-23 09:52:07 +00:00
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 1552 deletions

View File

@ -4,23 +4,52 @@ compiler:
- clang
before_script:
# installing libsodium, needed for Core
- git clone git://github.com/jedisct1/libsodium.git
#installing libsodium, needed for Core
- git clone git://github.com/jedisct1/libsodium.git > /dev/null
- cd libsodium
- git checkout tags/0.4.2
- ./autogen.sh
- ./configure && make -j3
- sudo make install
- git checkout tags/0.4.2 > /dev/null
- ./autogen.sh > /dev/null
- ./configure > /dev/null
- make check -j3 > /dev/null
- sudo make install >/dev/null
- 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
- sudo ldconfig
- git clone https://github.com/irungentoo/ProjectTox-Core.git toxcore
- cd toxcore
- autoreconf -i
- ./configure --disable-tests --disable-ntox --disable-dht-bootstrap-daemon
- ./configure --disable-tests --disable-ntox --disable-daemon --enable-av
- make -j2
- sudo make install
- cd ..
- sudo apt-get install libopenal-dev -yq
script:
- autoreconf -i
- ./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.):
```
./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/
* Audio calling support requires openal installed
* Compile with --disable-av to build without audio call support
* Compile and install the program with ```make && sudo make install```
```
*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:
#### Notes
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
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
```
## 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.h \
$(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) \
$(NCURSES_CFLAGS) \
$(LIBSODIUM_CFLAGS) \
$(LIBTOXCORE_CFLAGS)
$(LIBTOXCORE_CFLAGS) \
$(PTHREAD_CFLAGS)
toxic_CPPFLAGS = '-DTOXICVER="$(TOXIC_VERSION)"'
@ -39,5 +48,19 @@ toxic_LDADD = $(LIBTOXCORE_LDFLAGS) \
$(NCURSES_LIBS) \
$(LIBTOXCORE_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.
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_SRCDIR([src/main.c])
AC_CONFIG_HEADERS([config.h])
@ -113,6 +113,8 @@ AC_CHECK_FUNCS(
[],
[ 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_PROG_PKG_CONFIG
@ -381,6 +383,74 @@ if test "x$LIBTOXCORE_FOUND" = "xno"; then
AC_SUBST(LIBTOXCORE_LDFLAGS)
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"
AC_PATH_PROG([GIT], [git], [no])
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
#define CHAT_H_6489PZ13
#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 */

View File

@ -1,5 +1,23 @@
/*
* Toxic -- Tox Curses Client
/* chat_commands.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
@ -12,6 +30,8 @@
#include "toxic_windows.h"
#include "misc_tools.h"
#include "friendlist.h"
#include "execute.h"
#include "line_info.h"
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])
{
wattron(window, COLOR_PAIR(CYAN) | A_BOLD);
wprintw(window, "Chat commands:\n");
wattroff(window, COLOR_PAIR(CYAN) | A_BOLD);
struct history *hst = self->chatwin->hst;
line_info_clear(hst);
struct line_info *start = hst->line_start;
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, " /invite <n> : Invite friend to a group chat\n");
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);
wprintw(window, " * Argument messages must be enclosed in quotation marks.\n\n");
wattroff(window, COLOR_PAIR(CYAN) | A_BOLD);
if (argc == 1) {
if (!strcmp(argv[1], "global")) {
execute(window, self, m, "/help", GLOBAL_COMMAND_MODE);
return;
}
}
uint8_t *msg = "Chat commands:";
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 1, CYAN);
#ifdef _SUPPORT_AUDIO
#define NUMLINES 13
#else
#define NUMLINES 9
#endif
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])
{
uint8_t *errmsg;
if (argc < 1) {
wprintw(window, "Invalid syntax.\n");
return;
errmsg = "Invalid syntax";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return;
}
int groupnum = atoi(argv[1]);
if (groupnum == 0 && strcmp(argv[1], "0")) { /* atoi returns 0 value on invalid input */
wprintw(window, "Invalid syntax.\n");
errmsg = "Invalid syntax.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return;
}
if (tox_invite_friend(m, self->num, groupnum) == -1) {
wprintw(window, "Failed to invite friend.\n");
errmsg = "Failed to invite friend.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return;
}
wprintw(window, "Invited friend to group chat %d.\n", groupnum);
uint8_t msg[MAX_STR_SIZE];
snprintf(msg, sizeof(msg), "Invited friend to Room #%d.", groupnum);
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
}
void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (num_active_windows() >= MAX_WINDOWS_NUM) {
wattron(window, COLOR_PAIR(RED));
wprintw(window, " * Warning: Too many windows are open.\n");
wattron(window, COLOR_PAIR(RED));
uint8_t *errmsg;
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
errmsg = " * Warning: Too many windows are open.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, RED);
return;
}
uint8_t *groupkey = friends[self->num].pending_groupchat;
if (groupkey[0] == '\0') {
wprintw(window, "No pending group chat invite.\n");
errmsg = "No pending group chat invite.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return;
}
int groupnum = tox_join_groupchat(m, self->num, groupkey);
if (groupnum == -1) {
wprintw(window, "Group chat instance failed to initialize.\n");
errmsg = "Group chat instance failed to initialize.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return;
}
if (init_groupchat_win(prompt, m, groupnum) == -1) {
wprintw(window, "Group chat window failed to initialize.\n");
errmsg = "Group chat window failed to initialize.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
tox_del_groupchat(m, groupnum);
return;
}
@ -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])
{
uint8_t *errmsg;
if (argc != 1) {
wprintw(window, "Invalid syntax.\n");
return;
errmsg = "Invalid syntax.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return;
}
uint8_t filenum = atoi(argv[1]);
if ((filenum == 0 && strcmp(argv[1], "0")) || filenum >= MAX_FILES) {
wprintw(window, "No pending file transfers with that number.\n");
errmsg = "No pending file transfers with that number.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return;
}
if (!friends[self->num].file_receiver.pending[filenum]) {
wprintw(window, "No pending file transfers with that number.\n");
errmsg = "No pending file transfers with that number.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return;
}
uint8_t *filename = friends[self->num].file_receiver.filenames[filenum];
if (tox_file_send_control(m, self->num, 1, filenum, TOX_FILECONTROL_ACCEPT, 0, 0) == 0)
wprintw(window, "Accepted file transfer %u. Saving file as: '%s'\n", filenum, filename);
else
wprintw(window, "File transfer failed.\n");
if (tox_file_send_control(m, self->num, 1, filenum, TOX_FILECONTROL_ACCEPT, 0, 0) == 0) {
uint8_t msg[MAX_STR_SIZE];
snprintf(msg, sizeof(msg), "Saving file as: '%s' (%.1f%%)", filename, 0.0);
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
friends[self->num].file_receiver.line_id = self->chatwin->hst->line_end->id;
if ((friends[self->num].file_receiver.files[filenum] = fopen(filename, "a")) == NULL) {
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;
}
void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (max_file_senders_index >= (MAX_FILES-1)) {
wprintw(window,"Please wait for some of your outgoing file transfers to complete.\n");
uint8_t *errmsg;
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;
}
if (argc < 1) {
wprintw(window, "Invalid syntax.\n");
return;
errmsg = "Invalid syntax.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return;
}
uint8_t *path = argv[1];
if (path[0] != '\"') {
wprintw(window, "File path must be enclosed in quotes.\n");
errmsg = "File path must be enclosed in quotes.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return;
}
path[strlen(++path)-1] = L'\0';
path[strlen(++path) - 1] = L'\0';
int path_len = strlen(path);
if (path_len > MAX_STR_SIZE) {
wprintw(window, "File path exceeds character limit.\n");
errmsg = "File path exceeds character limit.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return;
}
FILE *file_to_send = fopen(path, "r");
if (file_to_send == NULL) {
wprintw(window, "File '%s' not found.\n", path);
errmsg = "File not found.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return;
}
@ -166,10 +247,11 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
uint8_t filename[MAX_STR_SIZE];
get_file_name(path, filename);
int filenum = tox_new_file_sender(m, self->num, filesize, filename, strlen(filename) + 1);
int filenum = tox_new_file_sender(m, self->num, filesize, filename, strlen(filename));
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;
}
@ -181,18 +263,21 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
file_senders[i].active = true;
file_senders[i].toxwin = self;
file_senders[i].file = file_to_send;
file_senders[i].filenum = (uint8_t) filenum;
file_senders[i].filenum = filenum;
file_senders[i].friendnum = self->num;
file_senders[i].timestamp = (uint64_t) time(NULL);
file_senders[i].timestamp = get_unix_time();
file_senders[i].size = filesize;
file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1,
tox_file_data_size(m, self->num), file_to_send);
wprintw(window, "Sending file: '%s'\n", path);
uint8_t msg[MAX_STR_SIZE];
snprintf(msg, sizeof(msg), "Sending file: '%s'", path);
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
if (i == max_file_senders_index)
++max_file_senders_index;
return;
}
}
}
}

View File

@ -1,5 +1,23 @@
/*
* Toxic -- Tox Curses Client
/* chat_commands.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/>.
*
*/
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_savefile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_sendfile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
#ifdef _SUPPORT_AUDIO
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 @@
/*
* Copyright (C) 2013 Tox project All Rights Reserved.
/* configdir.c
*
* 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
* the Free Software Foundation, either version 3 of the License, or
* (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
* 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 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__ */
const char *tmp;
if (!(tmp = getenv("XDG_CONFIG_HOME"))) {
len = strlen(home) + strlen("/.config") + 1;
user_config_dir = malloc(len);

View File

@ -1,20 +1,22 @@
/*
* Copyright (C) 2013 Tox project All Rights Reserved.
/* configdir.h
*
* 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
* the Free Software Foundation, either version 3 of the License, or
* (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
* 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 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 @@
/*
* Toxic -- Tox Curses Client
/* execute.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 <assert.h>
#include "toxic_windows.h"
#include "execute.h"
#include "chat_commands.h"
#include "global_commands.h"
#include "line_info.h"
struct cmd_func {
const char *name;
@ -16,19 +40,24 @@ struct cmd_func {
};
static struct cmd_func global_commands[] = {
{ "/accept", cmd_accept },
{ "/add", cmd_add },
{ "/clear", cmd_clear },
{ "/connect", cmd_connect },
{ "/exit", cmd_quit },
{ "/groupchat", cmd_groupchat },
{ "/help", cmd_prompt_help },
{ "/myid", cmd_myid },
{ "/nick", cmd_nick },
{ "/note", cmd_note },
{ "/q", cmd_quit },
{ "/quit", cmd_quit },
{ "/status", cmd_status },
{ "/accept", cmd_accept },
{ "/add", cmd_add },
{ "/clear", cmd_clear },
{ "/connect", cmd_connect },
{ "/exit", cmd_quit },
{ "/groupchat", cmd_groupchat },
{ "/help", cmd_prompt_help },
{ "/log", cmd_log },
{ "/myid", cmd_myid },
{ "/nick", cmd_nick },
{ "/note", cmd_note },
{ "/q", cmd_quit },
{ "/quit", cmd_quit },
{ "/status", cmd_status },
#ifdef _SUPPORT_AUDIO
{ "/lsdev", cmd_list_devices },
{ "/sdev", cmd_change_device },
#endif /* _SUPPORT_AUDIO */
};
static struct cmd_func chat_commands[] = {
@ -37,23 +66,32 @@ static struct cmd_func chat_commands[] = {
{ "/join", cmd_join_group },
{ "/savefile", cmd_savefile },
{ "/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. */
static int parse_command(WINDOW *w, char *cmd, char (*args)[MAX_STR_SIZE])
static int parse_command(WINDOW *w, ToxWindow *self, char *cmd, char (*args)[MAX_STR_SIZE])
{
int num_args = 0;
bool cmd_end = false; // flags when we get to the end of cmd
char *end; // points to the end of the current arg
bool cmd_end = false; /* flags when we get to the end of cmd */
char *end; /* points to the end of the current arg */
/* characters wrapped in double quotes count as one arg */
while (!cmd_end && num_args < MAX_NUM_ARGS) {
if (*cmd == '\"') {
end = strchr(cmd+1, '\"');
end = strchr(cmd + 1, '\"');
if (end++ == NULL) { /* Increment past the end quote */
wprintw(w, "Invalid argument. Did you forget a closing \"?\n");
uint8_t *errmsg = "Invalid argument. Did you forget a closing \"?";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return -1;
}
@ -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) {
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;
}
}
@ -90,33 +128,35 @@ static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, int num_
return 1;
}
void execute(WINDOW* w, ToxWindow *self, Tox *m, char *cmd, int mode)
void execute(WINDOW *w, ToxWindow *self, Tox *m, char *cmd, int mode)
{
if (string_is_empty(cmd))
return;
char args[MAX_NUM_ARGS][MAX_STR_SIZE] = {0};
int num_args = parse_command(w, cmd, args);
int num_args = parse_command(w, self, cmd, args);
if (num_args == -1)
return;
/* Try to match input command to command functions. If non-global command mode is specified,
try specified mode's commands first, then upon failure try global commands.
/* Try to match input command to command functions. If non-global command mode is specified,
try specified mode's commands first, then upon failure try global commands.
Note: Global commands must come last in case of duplicate command names */
switch (mode) {
case CHAT_COMMAND_MODE:
if (do_command(w, self, m, num_args, CHAT_NUM_COMMANDS, chat_commands, args) == 0)
return;
break;
case CHAT_COMMAND_MODE:
if (do_command(w, self, m, num_args, CHAT_NUM_COMMANDS, chat_commands, args) == 0)
return;
case GROUPCHAT_COMMAND_MODE:
break;
break;
case GROUPCHAT_COMMAND_MODE:
break;
}
if (do_command(w, self, m, num_args, GLOBAL_NUM_COMMANDS, global_commands, args) == 0)
return;
wprintw(w, "Invalid command.\n");
uint8_t *errmsg = "Invalid command.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
}

View File

@ -1,10 +1,34 @@
/*
* Toxic -- Tox Curses Client
/* execute.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_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
#endif /* _SUPPORT_AUDIO */
enum {
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 @@
/*
* Toxic -- Tox Curses Client
/* friendlist.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
@ -9,12 +27,19 @@
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <time.h>
#include <tox/tox.h>
#include "chat.h"
#include "friendlist.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 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_friends = 0;
extern struct _Winthread Winthread;
extern struct user_settings *user_settings;
ToxicFriend friends[MAX_FRIENDS_NUM];
static int friendlist_index[MAX_FRIENDS_NUM] = {0};
struct _pendingDel {
int num;
bool active;
} pendingdelete;
#define S_WEIGHT 100
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 */
void sort_friendlist_index(Tox *m)
void sort_friendlist_index(void)
{
int i;
int n = 0;
@ -53,52 +86,70 @@ void sort_friendlist_index(Tox *m)
qsort(friendlist_index, num_friends, sizeof(int), index_name_cmp);
}
static void friendlist_onMessage(ToxWindow *self, Tox *m, int num, uint8_t *str, uint16_t len)
static void update_friend_last_online(int32_t num, uint64_t timestamp)
{
friends[num].last_online.last_on = timestamp;
friends[num].last_online.tm = *localtime(&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)
return;
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));
} else {
str[len] = '\0';
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
tox_get_name(m, num, nick);
nick[TOXIC_MAX_NAME_LENGTH] = '\0';
wprintw(prompt->window, "%s: %s\n", nick, str);
int n_len = tox_get_name(m, num, nick);
n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH - 1);
nick[n_len] = '\0';
prep_prompt_win();
wattron(prompt->window, COLOR_PAIR(RED));
wprintw(prompt->window, "* Warning: Too many windows are open.\n");
wattron(prompt->window, COLOR_PAIR(RED));
uint8_t timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt);
line_info_add(prompt, timefrmt, nick, NULL, str, IN_MSG, 0, 0);
uint8_t *msg = "* Warning: Too many windows are open.";
line_info_add(prompt, NULL, NULL, NULL, msg, SYS_MSG, 0, RED);
alert_window(prompt, WINDOW_ALERT_1, true);
}
}
}
static void friendlist_onConnectionChange(ToxWindow *self, Tox *m, int num, uint8_t status)
static void friendlist_onConnectionChange(ToxWindow *self, Tox *m, int32_t num, uint8_t status)
{
if (num >= max_friends_index)
return;
friends[num].online = status == 1 ? true : false;
sort_friendlist_index(m);
friends[num].online = status;
update_friend_last_online(num, get_unix_time());
store_data(m, DATA_FILE);
sort_friendlist_index();
}
static void friendlist_onNickChange(ToxWindow *self, Tox *m, int num, uint8_t *str, uint16_t len)
static void friendlist_onNickChange(ToxWindow *self, Tox *m, int32_t num, uint8_t *str, uint16_t len)
{
if (len > TOX_MAX_NAME_LENGTH || num >= max_friends_index)
return;
str[TOXIC_MAX_NAME_LENGTH] = '\0';
len = strlen(str) + 1;
memcpy(friends[num].name, str, len);
len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
str[len] = '\0';
strcpy(friends[num].name, str);
friends[num].namelength = len;
sort_friendlist_index(m);
sort_friendlist_index();
}
static void friendlist_onStatusChange(ToxWindow *self, Tox *m, int num, TOX_USERSTATUS status)
static void friendlist_onStatusChange(ToxWindow *self, Tox *m, int32_t num, uint8_t status)
{
if (num >= max_friends_index)
return;
@ -106,16 +157,19 @@ static void friendlist_onStatusChange(ToxWindow *self, Tox *m, int num, TOX_USER
friends[num].status = status;
}
static void friendlist_onStatusMessageChange(ToxWindow *self, int num, uint8_t *str, uint16_t len)
static void friendlist_onStatusMessageChange(ToxWindow *self, int32_t num, uint8_t *str, uint16_t len)
{
if (len > TOX_MAX_STATUSMESSAGE_LENGTH || num >= max_friends_index)
return;
memcpy(friends[num].statusmsg, str, len);
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] = '\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)
return;
@ -130,14 +184,16 @@ static void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int num, bool sort
friends[i].online = false;
friends[i].status = TOX_USERSTATUS_NONE;
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);
update_friend_last_online(i, tox_get_last_online(m, i));
if (friends[i].namelength == -1 || friends[i].name[0] == '\0') {
strcpy(friends[i].name, (uint8_t *) UNKNOWN_NAME);
friends[i].namelength = strlen(UNKNOWN_NAME) + 1;
friends[i].namelength = strlen(UNKNOWN_NAME);
} else { /* Enforce toxic's maximum name length */
friends[i].name[TOXIC_MAX_NAME_LENGTH] = '\0';
friends[i].namelength = strlen(friends[i].name) + 1;
friends[i].namelength = MIN(friends[i].namelength, TOXIC_MAX_NAME_LENGTH);
friends[i].name[friends[i].namelength] = '\0';
}
num_friends = tox_count_friendlist(m);
@ -146,56 +202,56 @@ static void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int num, bool sort
++max_friends_index;
if (sort)
sort_friendlist_index(m);
sort_friendlist_index();
return;
}
}
}
static void friendlist_onFileSendRequest(ToxWindow *self, Tox *m, int num, uint8_t filenum,
uint64_t filesize, uint8_t *filename, uint16_t filename_len)
static void friendlist_onFileSendRequest(ToxWindow *self, Tox *m, int32_t num, uint8_t filenum,
uint64_t filesize, uint8_t *filename, uint16_t filename_len)
{
if (num >= max_friends_index)
return;
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));
} else {
tox_file_send_control(m, num, 1, filenum, TOX_FILECONTROL_KILL, 0, 0);
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
tox_get_name(m, num, nick);
nick[TOXIC_MAX_NAME_LENGTH] = '\0';
uint8_t nick[TOX_MAX_NAME_LENGTH];
int n_len = tox_get_name(m, num, nick);
n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH - 1);
nick[n_len] = '\0';
prep_prompt_win();
wattron(prompt->window, COLOR_PAIR(RED));
wprintw(prompt->window, "* File transfer from %s failed: too many windows are open.\n", nick);
wattron(prompt->window, COLOR_PAIR(RED));
uint8_t msg[MAX_STR_SIZE];
snprintf(msg, sizeof(msg), "* File transfer from %s failed: too many windows are open.", nick);
line_info_add(prompt, NULL, NULL, NULL, msg, SYS_MSG, 0, RED);
alert_window(prompt, WINDOW_ALERT_1, true);
}
}
}
static void friendlist_onGroupInvite(ToxWindow *self, Tox *m, int num, uint8_t *group_pub_key)
static void friendlist_onGroupInvite(ToxWindow *self, Tox *m, int32_t num, uint8_t *group_pub_key)
{
if (num >= max_friends_index)
return;
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));
} else {
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
tox_get_name(m, num, nick);
nick[TOXIC_MAX_NAME_LENGTH] = '\0';
uint8_t nick[TOX_MAX_NAME_LENGTH];
int n_len = tox_get_name(m, num, nick);
n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH - 1);
nick[n_len] = '\0';
prep_prompt_win();
wattron(prompt->window, COLOR_PAIR(RED));
wprintw(prompt->window, "* Group chat invite from %s failed: too many windows are open.\n", nick);
wattron(prompt->window, COLOR_PAIR(RED));
uint8_t msg[MAX_STR_SIZE];
snprintf(msg, sizeof(msg), "* Group chat invite from %s failed: too many windows are open.", nick);
line_info_add(prompt, NULL, NULL, NULL, msg, SYS_MSG, 0, RED);
alert_window(prompt, WINDOW_ALERT_1, true);
}
@ -212,15 +268,15 @@ 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);
memset(&friends[f_num], 0, sizeof(ToxicFriend));
int i;
for (i = max_friends_index; i > 0; --i) {
if (friends[i-1].active)
if (friends[i - 1].active)
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)
--num_selected;
sort_friendlist_index(m);
sort_friendlist_index();
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)
return;
int f = friendlist_index[num_selected];
if (key == '\n') {
/* Jump to chat window if already open */
if (friends[f].chatwin != -1) {
set_active_window(friends[f].chatwin);
} else if (num_active_windows() < MAX_WINDOWS_NUM) {
friends[f].chatwin = add_window(m, new_chat(m, friends[f].num));
set_active_window(friends[f].chatwin);
} else {
prep_prompt_win();
wattron(prompt->window, COLOR_PAIR(RED));
wprintw(prompt->window, "* Warning: Too many windows are open.\n");
wattron(prompt->window, COLOR_PAIR(RED));
/* lock screen and force decision on deletion popup */
if (pendingdelete.active) {
if (key == 'y' || key == 'n')
del_friend_deactivate(self, m, key);
alert_window(prompt, WINDOW_ALERT_1, true);
return;
}
if (key != ltr) {
if (key == '\n') {
/* Jump to chat window if already open */
if (friends[f].chatwin != -1) {
set_active_window(friends[f].chatwin);
} else if (get_num_active_windows() < MAX_WINDOWS_NUM) {
friends[f].chatwin = add_window(m, new_chat(m, friends[f].num));
set_active_window(friends[f].chatwin);
} else {
uint8_t *msg = "* Warning: Too many windows are open.";
line_info_add(prompt, NULL, NULL, NULL, msg, SYS_MSG, 0, RED);
alert_window(prompt, WINDOW_ALERT_1, true);
}
} else if (key == KEY_DC) {
del_friend_activate(self, m, f);
} else {
select_friend(self, m, key);
}
} else if (key == KEY_DC) {
delete_friend(m, self, f, key);
} else {
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)
{
@ -273,6 +380,9 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
int x2, y2;
getmaxyx(self->window, y2, x2);
uint64_t cur_time = get_unix_time();
struct tm cur_loc_tm = *localtime(&cur_time);
bool fix_statuses = x2 != self->x; /* true if window x axis has changed */
wattron(self->window, COLOR_PAIR(CYAN));
@ -287,13 +397,20 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
wprintw(self->window, "key.\n\n");
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);
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);
wprintw(self->window, "%d/%d \n\n", nf, num_friends);
if ((y2 - FLIST_OFST) <= 0) /* don't allow division by zero */
return;
int selected_num = 0;
/* Determine which portion of friendlist to draw based on current position */
int page = num_selected / (y2 - FLIST_OFST);
int start = (y2 - FLIST_OFST) * page;
@ -310,90 +427,171 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
wattron(self->window, A_BOLD);
wprintw(self->window, " > ");
wattroff(self->window, A_BOLD);
selected_num = f;
f_selected = true;
} else {
wprintw(self->window, " ");
}
if (friends[f].online) {
TOX_USERSTATUS status = friends[f].status;
uint8_t status = friends[f].status;
int colour = WHITE;
switch (status) {
case TOX_USERSTATUS_NONE:
colour = GREEN;
break;
case TOX_USERSTATUS_AWAY:
colour = YELLOW;
break;
case TOX_USERSTATUS_BUSY:
colour = RED;
break;
case TOX_USERSTATUS_NONE:
colour = GREEN;
break;
case TOX_USERSTATUS_AWAY:
colour = YELLOW;
break;
case TOX_USERSTATUS_BUSY:
colour = RED;
break;
case TOX_USERSTATUS_INVALID:
colour = MAGENTA;
break;
}
wprintw(self->window, "[");
wattron(self->window, COLOR_PAIR(colour) | A_BOLD);
wprintw(self->window, "O");
wprintw(self->window, "O ");
wattroff(self->window, COLOR_PAIR(colour) | A_BOLD);
wprintw(self->window, "]");
if (f_selected)
wattron(self->window, A_BOLD);
wattron(self->window, COLOR_PAIR(BLUE));
wattron(self->window, A_BOLD);
wprintw(self->window, "%s", friends[f].name);
wattroff(self->window, A_BOLD);
if (f_selected)
wattroff(self->window, A_BOLD);
wattroff(self->window, COLOR_PAIR(BLUE));
/* Reset friends[f].statusmsg on window resize */
if (fix_statuses) {
uint8_t statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH] = {'\0'};
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);
friends[f].statusmsg_len = tox_get_status_message_size(m, f);
}
/* Truncate note if it doesn't fit on one line */
uint16_t maxlen = x2 - getcurx(self->window) - 4;
uint16_t maxlen = x2 - getcurx(self->window) - 2;
if (friends[f].statusmsg_len > maxlen) {
friends[f].statusmsg[maxlen-3] = '\0';
friends[f].statusmsg[maxlen - 3] = '\0';
strcat(friends[f].statusmsg, "...");
friends[f].statusmsg[maxlen] = '\0';
friends[f].statusmsg_len = maxlen;
}
wprintw(self->window, " (%s)\n", friends[f].statusmsg);
if (friends[f].statusmsg[0])
wprintw(self->window, " %s", friends[f].statusmsg);
wprintw(self->window, "\n");
} else {
wprintw(self->window, "[");
wprintw(self->window, "o ");
if (f_selected)
wattron(self->window, COLOR_PAIR(BLUE));
wattron(self->window, A_BOLD);
wprintw(self->window, "O");
wprintw(self->window, "%s", friends[f].name);
wattroff(self->window, A_BOLD);
wprintw(self->window, "]");
if (f_selected)
wattron(self->window, A_BOLD);
wattroff(self->window, COLOR_PAIR(BLUE));
wprintw(self->window, "%s\n", friends[f].name);
uint64_t last_seen = friends[f].last_online.last_on;
if (f_selected)
wattroff(self->window, A_BOLD);
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;
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;
}
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)
{
@ -404,17 +602,32 @@ ToxWindow new_friendlist(void)
ret.onKey = &friendlist_onKey;
ret.onDraw = &friendlist_onDraw;
ret.onInit = &friendlist_onInit;
ret.onFriendAdded = &friendlist_onFriendAdded;
ret.onMessage = &friendlist_onMessage;
ret.onConnectionChange = &friendlist_onConnectionChange;
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.onStatusChange = &friendlist_onStatusChange;
ret.onStatusMessageChange = &friendlist_onStatusMessageChange;
ret.onFileSendRequest = &friendlist_onFileSendRequest;
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");
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
#define FRIENDLIST_H_53I41IM
#include <time.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 {
uint8_t name[TOX_MAX_NAME_LENGTH];
uint16_t namelength;
@ -10,20 +41,24 @@ typedef struct {
uint16_t statusmsg_len;
uint8_t pending_groupchat[TOX_CLIENT_ID_SIZE];
uint8_t pub_key[TOX_CLIENT_ID_SIZE];
int num;
int32_t num;
int chatwin;
bool active;
bool online;
bool is_typing;
TOX_USERSTATUS status;
uint8_t is_typing;
bool logging_on; /* saves preference for friend irrespective of chat windows */
uint8_t status;
struct LastOnline last_online;
struct FileReceiver file_receiver;
} ToxicFriend;
ToxWindow new_friendlist(void);
void disable_chatwin(int f_num);
void disable_chatwin(int32_t f_num);
int get_friendnum(uint8_t *name);
void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int32_t num, bool sort);
/* sorts friendlist_index first by connection status then alphabetically */
void sort_friendlist_index(Tox *m);
void sort_friendlist_index(void);
#endif /* end of include guard: FRIENDLIST_H_53I41IM */

View File

@ -1,5 +1,23 @@
/*
* Toxic -- Tox Curses Client
/* global_commands.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
@ -12,6 +30,8 @@
#include "toxic_windows.h"
#include "misc_tools.h"
#include "friendlist.h"
#include "log.h"
#include "line_info.h"
extern char *DATA_FILE;
extern ToxWindow *prompt;
@ -24,30 +44,34 @@ extern uint8_t num_frnd_requests;
/* command functions */
void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
/* check arguments */
uint8_t *msg;
if (argc != 1) {
wprintw(window, "Invalid syntax.\n");
return;
msg = "Invalid syntax.";
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
return;
}
int req = atoi(argv[1]);
if ((req == 0 && strcmp(argv[1], "0"))|| req >= MAX_FRIENDS_NUM) {
wprintw(window, "No pending friend request with that number.\n");
if ((req == 0 && strcmp(argv[1], "0")) || req >= MAX_FRIENDS_NUM) {
msg = "No pending friend request with that number.";
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
return;
}
if (!strlen(pending_frnd_requests[req])) {
wprintw(window, "No pending friend request with that number.\n");
msg = "No pending friend request with that number.";
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
return;
}
int friendnum = tox_add_friend_norequest(m, pending_frnd_requests[req]);
int32_t friendnum = tox_add_friend_norequest(m, pending_frnd_requests[req]);
if (friendnum == -1)
wprintw(window, "Failed to add friend.\n");
msg = "Failed to add friend.";
else {
wprintw(window, "Friend request accepted.\n");
msg = "Friend request accepted.";
on_friendadded(m, friendnum, true);
}
@ -56,17 +80,21 @@ void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
int i;
for (i = num_frnd_requests; i > 0; --i) {
if (!strlen(pending_frnd_requests[i-1]))
if (!strlen(pending_frnd_requests[i - 1]))
break;
}
num_frnd_requests = i;
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
}
void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
uint8_t *errmsg;
if (argc < 1) {
wprintw(window, "Invalid syntax.\n");
errmsg = "Invalid syntax.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return;
}
@ -77,20 +105,23 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
uint8_t *temp = argv[2];
if (temp[0] != '\"') {
wprintw(window, "Message must be enclosed in quotes.\n");
errmsg = "Message must be enclosed in quotes.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return;
}
temp[strlen(++temp)-1] = L'\0';
temp[strlen(++temp) - 1] = L'\0';
snprintf(msg, sizeof(msg), "%s", temp);
} else {
uint8_t selfname[TOX_MAX_NAME_LENGTH];
tox_get_self_name(m, selfname, TOX_MAX_NAME_LENGTH);
uint16_t n_len = tox_get_self_name(m, selfname);
selfname[n_len] = '\0';
snprintf(msg, sizeof(msg), "Hello, my name is %s. Care to Tox?", selfname);
}
if (strlen(id) != 2 * TOX_FRIEND_ADDRESS_SIZE) {
wprintw(window, "Invalid ID length.\n");
errmsg = "Invalid ID length.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return;
}
@ -105,7 +136,8 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
xx[2] = '\0';
if (sscanf(xx, "%02x", &x) != 1) {
wprintw(window, "Invalid ID.\n");
errmsg = "Invalid ID.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return;
}
@ -116,58 +148,76 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
id[i] = toupper(id[i]);
}
int f_num = tox_add_friend(m, id_bin, msg, strlen(msg) + 1);
int32_t f_num = tox_add_friend(m, id_bin, msg, strlen(msg));
switch (f_num) {
case TOX_FAERR_TOOLONG:
wprintw(window, "Message is too long.\n");
break;
case TOX_FAERR_NOMESSAGE:
wprintw(window, "Please add a message to your request.\n");
break;
case TOX_FAERR_OWNKEY:
wprintw(window, "That appears to be your own ID.\n");
break;
case TOX_FAERR_ALREADYSENT:
wprintw(window, "Friend request has already been sent.\n");
break;
case TOX_FAERR_UNKNOWN:
wprintw(window, "Undefined error when adding friend.\n");
break;
case TOX_FAERR_BADCHECKSUM:
wprintw(window, "Bad checksum in address.\n");
break;
case TOX_FAERR_SETNEWNOSPAM:
wprintw(window, "Nospam was different (is this contact already added?)\n");
break;
default:
wprintw(window, "Friend request sent.\n");
on_friendadded(m, f_num, true);
break;
case TOX_FAERR_TOOLONG:
errmsg = "Message is too long.";
break;
case TOX_FAERR_NOMESSAGE:
errmsg = "Please add a message to your request.";
break;
case TOX_FAERR_OWNKEY:
errmsg = "That appears to be your own ID.";
break;
case TOX_FAERR_ALREADYSENT:
errmsg = "Friend request has already been sent.";
break;
case TOX_FAERR_UNKNOWN:
errmsg = "Undefined error when adding friend.";
break;
case TOX_FAERR_BADCHECKSUM:
errmsg = "Bad checksum in address.";
break;
case TOX_FAERR_SETNEWNOSPAM:
errmsg = "Nospam was different (is this contact already added?";
break;
default:
errmsg = "Friend request sent.";
on_friendadded(m, f_num, true);
break;
}
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
}
void cmd_clear(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
line_info_clear(self->chatwin->hst);
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])
{
uint8_t *errmsg;
/* check arguments */
if (argc != 3) {
wprintw(window, "Invalid syntax.\n");
return;
errmsg = "Invalid syntax.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return;
}
tox_IP_Port dht;
char *ip = argv[1];
char *port = argv[2];
char *key = argv[3];
if (atoi(port) == 0) {
wprintw(window, "Invalid syntax.\n");
errmsg = "Invalid syntax.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return;
}
@ -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])
{
if (num_active_windows() >= MAX_WINDOWS_NUM) {
wattron(window, COLOR_PAIR(RED));
wprintw(window, " * Warning: Too many windows are open.\n");
wattron(window, COLOR_PAIR(RED));
uint8_t *errmsg;
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
errmsg = " * Warning: Too many windows are open.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, RED);
return;
}
int groupnum = tox_add_groupchat(m);
if (groupnum == -1) {
wprintw(window, "Group chat instance failed to initialize.\n");
errmsg = "Group chat instance failed to initialize.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return;
}
if (init_groupchat_win(prompt, m, groupnum) == -1) {
wprintw(window, "Group chat window failed to initialize.\n");
errmsg = "Group chat window failed to initialize.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
tox_del_groupchat(m, groupnum);
return;
}
wprintw(window, "Group chat created as %d.\n", groupnum);
uint8_t msg[MAX_STR_SIZE];
snprintf(msg, sizeof(msg), "Group chat created as %d.", groupnum);
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
}
void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
uint8_t *msg;
struct chatlog *log = self->chatwin->log;
if (argc == 0) {
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])
@ -216,15 +319,18 @@ void cmd_myid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
strcat(id, xx);
}
wprintw(window, "%s\n", id);
line_info_add(self, NULL, NULL, NULL, id, SYS_MSG, 0, 0);
}
void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
uint8_t *errmsg;
/* check arguments */
if (argc < 1) {
wprintw(window, "Invalid name.\n");
return;
errmsg = "Invalid name.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return;
}
uint8_t *nick = argv[1];
@ -237,65 +343,90 @@ void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
}
if (!valid_nick(nick)) {
wprintw(window, "Invalid name.\n");
errmsg = "Invalid name.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return;
}
if (len > TOXIC_MAX_NAME_LENGTH) {
nick[TOXIC_MAX_NAME_LENGTH] = L'\0';
len = TOXIC_MAX_NAME_LENGTH;
}
len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
tox_set_name(m, nick, len+1);
prompt_update_nick(prompt, nick, len+1);
nick[len] = L'\0';
tox_set_name(m, nick, len);
prompt_update_nick(prompt, nick, len);
store_data(m, DATA_FILE);
}
void cmd_note(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
uint8_t *errmsg;
if (argc < 1) {
wprintw(window, "Wrong number of arguments.\n");
errmsg = "Wrong number of arguments.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return;
}
uint8_t *msg = argv[1];
if (msg[0] != '\"') {
wprintw(window, "Note must be enclosed in quotes.\n");
errmsg = "Note must be enclosed in quotes.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return;
}
msg[strlen(++msg)-1] = L'\0';
uint16_t len = strlen(msg) + 1;
msg[strlen(++msg) - 1] = L'\0';
uint16_t len = strlen(msg);
tox_set_status_message(m, msg, len);
prompt_update_statusmessage(prompt, msg, len);
}
void cmd_prompt_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
wclear(window);
wattron(window, COLOR_PAIR(CYAN) | A_BOLD);
wprintw(window, "\n\nGlobal commands:\n");
wattroff(window, COLOR_PAIR(CYAN) | A_BOLD);
struct history *hst = self->chatwin->hst;
line_info_clear(hst);
struct line_info *start = hst->line_start;
wprintw(window, " /add <id> <msg> : Add friend with optional message\n");
wprintw(window, " /accept <n> : Accept friend request\n");
wprintw(window, " /connect <ip> <port> <key> : Manually connect to a DHT server\n");
wprintw(window, " /status <type> <msg> : Set your status with optional note\n");
wprintw(window, " /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");
uint8_t *msg = "Global commands:";
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 1, CYAN);
wattron(window, COLOR_PAIR(CYAN) | A_BOLD);
wprintw(window, " * Argument messages must be enclosed in quotation marks.\n");
wprintw(window, " * Use ctrl-o and ctrl-p to navigate through the tabs.\n\n");
wattroff(window, COLOR_PAIR(CYAN) | A_BOLD);
#ifdef _SUPPORT_AUDIO
#define NUMLINES 14
#else
#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])
@ -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])
{
uint8_t *msg = NULL;
uint8_t *errmsg;
if (argc >= 2) {
msg = argv[2];
if (msg[0] != '\"') {
wprintw(window, "Note must be enclosed in quotes.\n");
errmsg = "Note must be enclosed in quotes.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return;
}
} else if (argc != 1) {
wprintw(window, "Wrong number of arguments.\n");
errmsg = "Wrong number of arguments.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return;
}
char *status = argv[1];
int len = strlen(status);
char l_status[len+1];
char l_status[len + 1];
int 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"))
status_kind = TOX_USERSTATUS_BUSY;
else {
wprintw(window, "Invalid status. Valid statuses are: online, busy and away.\n");
errmsg = "Invalid status. Valid statuses are: online, busy and away.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return;
}
@ -344,8 +479,8 @@ void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
prompt_update_status(prompt, status_kind);
if (msg != NULL) {
msg[strlen(++msg)-1] = L'\0'; /* remove opening and closing quotes */
uint16_t len = strlen(msg) + 1;
msg[strlen(++msg) - 1] = L'\0'; /* remove opening and closing quotes */
uint16_t len = strlen(msg);
tox_set_status_message(m, msg, len);
prompt_update_statusmessage(prompt, msg, len);
}

View File

@ -1,5 +1,23 @@
/*
* Toxic -- Tox Curses Client
/* global_commands.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/>.
*
*/
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_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_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_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_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_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 @@
/*
* Toxic -- Tox Curses Client
/* groupchat.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
@ -16,6 +34,9 @@
#include "groupchat.h"
#include "prompt.h"
#include "toxic_strings.h"
#include "log.h"
#include "line_info.h"
#include "settings.h"
extern char *DATA_FILE;
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 int max_groupchat_index = 0;
extern struct user_settings *user_settings;
/* 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)
{
@ -37,9 +60,14 @@ int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum)
groupchats[i].num_peers = 0;
groupchats[i].peer_names = malloc(sizeof(uint8_t) * TOX_MAX_NAME_LENGTH);
groupchats[i].oldpeer_names = malloc(sizeof(uint8_t) * TOX_MAX_NAME_LENGTH);
groupchats[i].peer_name_lengths = malloc(sizeof(uint16_t));
groupchats[i].oldpeer_name_lengths = malloc(sizeof(uint16_t));
memset(groupchats[i].peer_names, 0, sizeof(uint8_t) * TOX_MAX_NAME_LENGTH);
memset(groupchats[i].peer_name_lengths, 0, sizeof(uint16_t));
/* temp fix */
memcpy(&groupchats[i].oldpeer_names[0], UNKNOWN_NAME, sizeof(UNKNOWN_NAME));
groupchats[i].oldpeer_name_lengths[0] = (uint16_t) strlen(UNKNOWN_NAME);
set_active_window(groupchats[i].chatwin);
@ -53,45 +81,79 @@ int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum)
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);
free(groupchats[groupnum].peer_names);
free(groupchats[groupnum].oldpeer_names);
free(groupchats[groupnum].peer_name_lengths);
free(groupchats[groupnum].oldpeer_name_lengths);
memset(&groupchats[groupnum], 0, sizeof(GroupChat));
int i;
for (i = max_groupchat_index; i > 0; --i) {
if (groupchats[i-1].active)
if (groupchats[i - 1].active)
break;
}
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);
wprintw(ctx->history, "Group chat commands:\n");
wattroff(ctx->history, COLOR_PAIR(CYAN) | A_BOLD);
struct history *hst = self->chatwin->hst;
line_info_clear(hst);
struct line_info *start = hst->line_start;
wprintw(ctx->history, " /add <id> <msg> : Add friend with optional message\n");
wprintw(ctx->history, " /status <type> <msg>: Set your status with optional note\n");
wprintw(ctx->history, " /note <msg> : Set a personal note\n");
wprintw(ctx->history, " /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);
wprintw(ctx->history, " * Argument messages must be enclosed in quotation marks.\n");
wprintw(ctx->history, " * Scroll peer list with the Page Up/Page Down keys.\n\n");
wattroff(ctx->history, COLOR_PAIR(CYAN) | A_BOLD);
uint8_t *msg = "Group chat commands:";
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 1, CYAN);
#define NUMLINES 9
uint8_t lines[NUMLINES][MAX_STR_SIZE] = {
{ " /add <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,
@ -100,18 +162,24 @@ static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, int groupnum, int
if (self->num != groupnum)
return;
msg[len] = '\0';
ChatContext *ctx = self->chatwin;
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
tox_group_peername(m, groupnum, peernum, nick);
nick[TOXIC_MAX_NAME_LENGTH] = '\0'; /* enforce client max name length */
int n_len = tox_group_peername(m, groupnum, peernum, nick);
n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH - 1); /* enforce client max name length */
nick[n_len] = '\0';
/* check if message contains own name and alert appropriately */
int alert_type = WINDOW_ALERT_1;
bool beep = false;
uint8_t selfnick[TOX_MAX_NAME_LENGTH] = {'\0'};
tox_get_self_name(m, selfnick, TOX_MAX_NAME_LENGTH);
uint8_t selfnick[TOX_MAX_NAME_LENGTH];
uint16_t sn_len = tox_get_self_name(m, selfnick);
selfnick[sn_len] = '\0';
int nick_clr = strcmp(nick, selfnick) == 0 ? GREEN : CYAN;
bool nick_match = strcasestr(msg, selfnick) && strncmp(selfnick, nick, TOXIC_MAX_NAME_LENGTH);
@ -124,18 +192,11 @@ static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, int groupnum, int
alert_window(self, alert_type, beep);
print_time(ctx->history);
wattron(ctx->history, COLOR_PAIR(nick_clr));
wprintw(ctx->history, "%s: ", nick);
wattroff(ctx->history, COLOR_PAIR(nick_clr));
if (msg[0] == '>') {
wattron(ctx->history, COLOR_PAIR(GREEN));
wprintw(ctx->history, "%s\n", msg);
wattroff(ctx->history, COLOR_PAIR(GREEN));
} else {
wprintw(ctx->history, "%s\n", msg);
}
uint8_t timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt);
line_info_add(self, timefrmt, nick, NULL, msg, IN_MSG, 0, nick_clr);
write_to_log(msg, nick, ctx->log, false);
}
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)
return;
action[len] = '\0';
ChatContext *ctx = self->chatwin;
/* check if message contains own name and alert appropriately */
int alert_type = WINDOW_ALERT_1;
bool beep = false;
uint8_t selfnick[TOX_MAX_NAME_LENGTH] = {'\0'};
tox_get_self_name(m, selfnick, TOX_MAX_NAME_LENGTH);
uint8_t selfnick[TOX_MAX_NAME_LENGTH];
uint16_t n_len = tox_get_self_name(m, selfnick);
selfnick[n_len] = '\0';
bool nick_match = strcasestr(action, selfnick);
@ -163,49 +227,69 @@ static void groupchat_onGroupAction(ToxWindow *self, Tox *m, int groupnum, int p
alert_window(self, alert_type, beep);
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
tox_group_peername(m, groupnum, peernum, nick);
nick[TOXIC_MAX_NAME_LENGTH] = '\0'; /* enforce client max name length */
n_len = tox_group_peername(m, groupnum, peernum, nick);
print_time(ctx->history);
wattron(ctx->history, COLOR_PAIR(YELLOW));
wprintw(ctx->history, "* %s %s\n", nick, action);
wattroff(ctx->history, COLOR_PAIR(YELLOW));
n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH - 1);
nick[n_len] = '\0';
uint8_t timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt);
line_info_add(self, timefrmt, nick, NULL, action, ACTION, 0, 0);
write_to_log(action, nick, ctx->log, true);
}
/* Puts two copies of peerlist in chat instance */
static void copy_peernames(int gnum, int npeers, uint8_t tmp_peerlist[][TOX_MAX_NAME_LENGTH])
/* Puts two copies of peerlist/lengths in chat instance */
static void copy_peernames(int gnum, uint8_t peerlist[][TOX_MAX_NAME_LENGTH], uint16_t lengths[], int npeers)
{
/* Assumes these are initiated in init_groupchat_win */
free(groupchats[gnum].peer_names);
free(groupchats[gnum].oldpeer_names);
free(groupchats[gnum].peer_name_lengths);
free(groupchats[gnum].oldpeer_name_lengths);
int N = TOX_MAX_NAME_LENGTH;
groupchats[gnum].peer_names = malloc(sizeof(uint8_t) * npeers * N);
groupchats[gnum].oldpeer_names = malloc(sizeof(uint8_t) * npeers * N);
groupchats[gnum].peer_name_lengths = malloc(sizeof(uint16_t) * npeers);
groupchats[gnum].oldpeer_name_lengths = malloc(sizeof(uint16_t) * npeers);
if (groupchats[gnum].peer_names == NULL || groupchats[gnum].oldpeer_names == NULL) {
memset(groupchats[gnum].peer_names, 0, sizeof(uint8_t) * npeers * N);
memset(groupchats[gnum].peer_name_lengths, 0, sizeof(uint16_t) * npeers);
if (groupchats[gnum].peer_names == NULL || groupchats[gnum].oldpeer_names == NULL
|| groupchats[gnum].peer_name_lengths == NULL || groupchats[gnum].oldpeer_name_lengths == NULL) {
endwin();
fprintf(stderr, "malloc() failed. Aborting...\n");
exit(EXIT_FAILURE);
}
uint16_t unknown_len = strlen(UNKNOWN_NAME);
int i;
for (i = 0; i < npeers; ++i) {
if (string_is_empty(tmp_peerlist[i])) {
memcpy(&groupchats[gnum].peer_names[i*N], UNKNOWN_NAME, sizeof(UNKNOWN_NAME));
if (string_is_empty(peerlist[i])) {
memcpy(&groupchats[gnum].peer_names[i * N], UNKNOWN_NAME, sizeof(UNKNOWN_NAME));
groupchats[gnum].peer_name_lengths[i] = unknown_len;
} else {
memcpy(&groupchats[gnum].peer_names[i*N], tmp_peerlist[i], N);
groupchats[gnum].peer_names[i*N+TOXIC_MAX_NAME_LENGTH] = '\0';
memcpy(&groupchats[gnum].peer_names[i * N], peerlist[i], N);
uint16_t n_len = lengths[i];
n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH - 1);
groupchats[gnum].peer_names[i * N + n_len] = '\0';
groupchats[gnum].peer_name_lengths[i] = n_len;
}
}
memcpy(groupchats[gnum].oldpeer_names, groupchats[gnum].peer_names, N*npeers);
memcpy(groupchats[gnum].oldpeer_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,
uint8_t change)
uint8_t change)
{
if (self->num != groupnum)
return;
@ -214,85 +298,82 @@ static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnu
int num_peers = groupchats[groupnum].num_peers;
/* get old peer name before updating name list */
uint8_t oldpeername[TOX_MAX_NAME_LENGTH] = {0};
uint8_t oldpeername[TOX_MAX_NAME_LENGTH];
if (change != TOX_CHAT_CHANGE_PEER_ADD)
memcpy(oldpeername, &groupchats[groupnum].oldpeer_names[peernum*TOX_MAX_NAME_LENGTH],
if (change != TOX_CHAT_CHANGE_PEER_ADD) {
memcpy(oldpeername, &groupchats[groupnum].oldpeer_names[peernum * TOX_MAX_NAME_LENGTH],
sizeof(oldpeername));
uint16_t old_n_len = groupchats[groupnum].oldpeer_name_lengths[peernum];
oldpeername[old_n_len] = '\0';
}
/* Update name lists */
/* Update name/len lists */
uint8_t tmp_peerlist[num_peers][TOX_MAX_NAME_LENGTH];
tox_group_get_names(m, groupnum, tmp_peerlist, num_peers);
copy_peernames(groupnum, num_peers, tmp_peerlist);
uint16_t tmp_peerlens[num_peers];
tox_group_get_names(m, groupnum, tmp_peerlist, tmp_peerlens, num_peers);
copy_peernames(groupnum, tmp_peerlist, tmp_peerlens, num_peers);
/* get current peername then sort namelist */
uint8_t peername[TOX_MAX_NAME_LENGTH] = {0};
memcpy(peername, &groupchats[groupnum].peer_names[peernum*TOX_MAX_NAME_LENGTH], sizeof(peername));
uint8_t peername[TOX_MAX_NAME_LENGTH];
if (change != TOX_CHAT_CHANGE_PEER_DEL) {
uint16_t n_len = groupchats[groupnum].peer_name_lengths[peernum];
memcpy(peername, &groupchats[groupnum].peer_names[peernum * TOX_MAX_NAME_LENGTH], sizeof(peername));
peername[n_len] = '\0';
}
qsort(groupchats[groupnum].peer_names, groupchats[groupnum].num_peers, TOX_MAX_NAME_LENGTH, qsort_strcasecmp_hlpr);
ChatContext *ctx = self->chatwin;
print_time(ctx->history);
uint8_t *event;
uint8_t timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt);
switch (change) {
case TOX_CHAT_CHANGE_PEER_ADD:
wattron(ctx->history, COLOR_PAIR(GREEN));
wattron(ctx->history, A_BOLD);
wprintw(ctx->history, "* %s", peername);
wattroff(ctx->history, A_BOLD);
wprintw(ctx->history, " has joined the room\n");
wattroff(ctx->history, COLOR_PAIR(GREEN));
break;
case TOX_CHAT_CHANGE_PEER_DEL:
wattron(ctx->history, A_BOLD);
wprintw(ctx->history, "* %s", oldpeername);
wattroff(ctx->history, A_BOLD);
wprintw(ctx->history, " has left the room\n");
case TOX_CHAT_CHANGE_PEER_ADD:
event = "has joined the room";
line_info_add(self, timefrmt, peername, NULL, event, CONNECTION, 0, GREEN);
write_to_log(event, peername, ctx->log, true);
break;
if (groupchats[self->num].side_pos > 0)
--groupchats[self->num].side_pos;
case TOX_CHAT_CHANGE_PEER_DEL:
event = "has left the room";
line_info_add(self, timefrmt, oldpeername, NULL, event, CONNECTION, 0, 0);
break;
case TOX_CHAT_CHANGE_PEER_NAME:
wattron(ctx->history, COLOR_PAIR(MAGENTA));
wattron(ctx->history, A_BOLD);
wprintw(ctx->history, "* %s", oldpeername);
wattroff(ctx->history, A_BOLD);
if (groupchats[self->num].side_pos > 0)
--groupchats[self->num].side_pos;
wprintw(ctx->history, " is now known as ");
write_to_log(event, oldpeername, ctx->log, true);
break;
wattron(ctx->history, A_BOLD);
wprintw(ctx->history, "%s\n", peername);
wattroff(ctx->history, A_BOLD);
wattroff(ctx->history, COLOR_PAIR(MAGENTA));
break;
case TOX_CHAT_CHANGE_PEER_NAME:
event = " is now known as ";
line_info_add(self, timefrmt, oldpeername, peername, event, NAME_CHANGE, 0, 0);
uint8_t tmp_event[TOXIC_MAX_NAME_LENGTH * 2 + 32];
snprintf(tmp_event, sizeof(tmp_event), "is now known as %s", peername);
write_to_log(tmp_event, oldpeername, ctx->log, true);
break;
}
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) {
wprintw(ctx->history, "Invalid syntax.\n");
return;
}
// uint8_t selfname[TOX_MAX_NAME_LENGTH];
// tox_get_self_name(m, selfname, TOX_MAX_NAME_LENGTH);
// print_time(ctx->history);
// wattron(ctx->history, COLOR_PAIR(YELLOW));
// wprintw(ctx->history, "* %s %s\n", selfname, action);
// wattroff(ctx->history, COLOR_PAIR(YELLOW));
if (tox_group_action_send(m, self->num, action, strlen(action) + 1) == -1) {
wattron(ctx->history, COLOR_PAIR(RED));
wprintw(ctx->history, " * Failed to send action\n");
wattroff(ctx->history, COLOR_PAIR(RED));
if (tox_group_action_send(m, self->num, action, strlen(action)) == -1) {
uint8_t *errmsg = " * Failed to send action.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, RED);
}
}
static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key)
static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
{
ChatContext *ctx = self->chatwin;
@ -301,221 +382,233 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key)
getmaxyx(self->window, y2, x2);
int cur_len = 0;
if (key == 0x107 || key == 0x8 || key == 0x7f) { /* BACKSPACE key: Remove character behind pos */
if (ctx->pos > 0) {
cur_len = MAX(1, wcwidth(ctx->line[ctx->pos - 1]));
del_char_buf_bck(ctx->line, &ctx->pos, &ctx->len);
if (!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 (x == 0)
wmove(self->window, y-1, x2 - cur_len);
/* 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 - cur_len);
} else {
beep();
wmove(self->window, y, x + MAX(1, wcwidth(key)));
}
}
else if (key == KEY_DC) { /* DEL key: Remove character at pos */
if (ctx->pos != ctx->len)
del_char_buf_frnt(ctx->line, &ctx->pos, &ctx->len);
else
beep();
}
} else { /* if (!ltr) */
else if (key == T_KEY_DISCARD) { /* CTRL-U: Delete entire line behind pos */
if (ctx->pos > 0) {
discard_buf(ctx->line, &ctx->pos, &ctx->len);
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
} else {
beep();
if (key == 0x107 || key == 0x8 || key == 0x7f) { /* BACKSPACE key: Remove character behind pos */
if (ctx->pos > 0) {
cur_len = MAX(1, wcwidth(ctx->line[ctx->pos - 1]));
del_char_buf_bck(ctx->line, &ctx->pos, &ctx->len);
if (x == 0)
wmove(self->window, y - 1, x2 - cur_len);
else
wmove(self->window, y, x - cur_len);
} else {
beep();
}
}
}
else if (key == T_KEY_KILL) { /* CTRL-K: Delete entire line in front of pos */
if (ctx->pos != ctx->len)
kill_buf(ctx->line, &ctx->pos, &ctx->len);
else
beep();
}
else if (key == KEY_HOME) { /* HOME key: Move cursor to beginning of line */
if (ctx->pos > 0) {
ctx->pos = 0;
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
}
}
else if (key == KEY_END) { /* END key: move cursor to end of line */
if (ctx->pos != ctx->len) {
ctx->pos = ctx->len;
mv_curs_end(self->window, MAX(0, wcswidth(ctx->line, (CHATBOX_HEIGHT-1)*x2)), y2, x2);
}
}
else if (key == KEY_LEFT) {
if (ctx->pos > 0) {
--ctx->pos;
cur_len = MAX(1, wcwidth(ctx->line[ctx->pos]));
if (x == 0)
wmove(self->window, y-1, x2 - cur_len);
else if (key == KEY_DC) { /* DEL key: Remove character at pos */
if (ctx->pos != ctx->len)
del_char_buf_frnt(ctx->line, &ctx->pos, &ctx->len);
else
wmove(self->window, y, x - cur_len);
} else {
beep();
beep();
}
}
else if (key == KEY_RIGHT) {
if (ctx->pos < ctx->len) {
cur_len = MAX(1, wcwidth(ctx->line[ctx->pos]));
++ctx->pos;
if (x == x2-1)
wmove(self->window, y+1, 0);
else
wmove(self->window, y, x + cur_len);
} else {
beep();
else if (key == T_KEY_DISCARD) { /* CTRL-U: Delete entire line behind pos */
if (ctx->pos > 0) {
discard_buf(ctx->line, &ctx->pos, &ctx->len);
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
} else {
beep();
}
}
}
else if (key == KEY_UP) { /* fetches previous item in history */
fetch_hist_item(ctx->line, &ctx->pos, &ctx->len, ctx->ln_history, ctx->hst_tot,
&ctx->hst_pos, LN_HIST_MV_UP);
mv_curs_end(self->window, ctx->len, y2, x2);
}
else if (key == KEY_DOWN) { /* fetches next item in history */
fetch_hist_item(ctx->line, &ctx->pos, &ctx->len, ctx->ln_history, ctx->hst_tot,
&ctx->hst_pos, LN_HIST_MV_DWN);
mv_curs_end(self->window, ctx->len, y2, x2);
}
else if (key == '\t') { /* TAB key: completes peer name */
if (ctx->len > 0) {
int diff;
if ((ctx->line[0] != '/') || (ctx->line[1] == 'm' && ctx->line[2] == 'e'))
diff = complete_line(ctx->line, &ctx->pos, &ctx->len, groupchats[self->num].peer_names,
groupchats[self->num].num_peers, TOX_MAX_NAME_LENGTH);
else if (key == T_KEY_KILL) { /* CTRL-K: Delete entire line in front of pos */
if (ctx->pos != ctx->len)
kill_buf(ctx->line, &ctx->pos, &ctx->len);
else
diff = complete_line(ctx->line, &ctx->pos, &ctx->len, glob_cmd_list, AC_NUM_GLOB_COMMANDS,
MAX_CMDNAME_SIZE);
beep();
}
if (diff != -1) {
if (x + diff > x2 - 1) {
int ofst = (x + diff - 1) - (x2 - 1);
wmove(self->window, y+1, ofst);
else if (key == KEY_HOME || key == T_KEY_C_A) { /* HOME/C-a key: Move cursor to start of line */
if (ctx->pos > 0) {
ctx->pos = 0;
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
}
}
else if (key == KEY_END || key == T_KEY_C_E) { /* END/C-e key: move cursor to end of line */
if (ctx->pos != ctx->len) {
ctx->pos = ctx->len;
mv_curs_end(self->window, MAX(0, wcswidth(ctx->line, (CHATBOX_HEIGHT - 1)*x2)), y2, x2);
}
}
else if (key == KEY_LEFT) {
if (ctx->pos > 0) {
--ctx->pos;
cur_len = MAX(1, wcwidth(ctx->line[ctx->pos]));
if (x == 0)
wmove(self->window, y - 1, x2 - cur_len);
else
wmove(self->window, y, x - cur_len);
} else {
beep();
}
}
else if (key == KEY_RIGHT) {
if (ctx->pos < ctx->len) {
cur_len = MAX(1, wcwidth(ctx->line[ctx->pos]));
++ctx->pos;
if (x == x2 - 1)
wmove(self->window, y + 1, 0);
else
wmove(self->window, y, x + cur_len);
} else {
beep();
}
}
else if (key == KEY_UP) { /* fetches previous item in history */
fetch_hist_item(ctx->line, &ctx->pos, &ctx->len, ctx->ln_history, ctx->hst_tot,
&ctx->hst_pos, MOVE_UP);
mv_curs_end(self->window, ctx->len, y2, x2);
}
else if (key == KEY_DOWN) { /* fetches next item in history */
fetch_hist_item(ctx->line, &ctx->pos, &ctx->len, ctx->ln_history, ctx->hst_tot,
&ctx->hst_pos, MOVE_DOWN);
mv_curs_end(self->window, ctx->len, y2, x2);
}
else if (key == '\t') { /* TAB key: completes peer name */
if (ctx->len > 0) {
int diff;
if ((ctx->line[0] != '/') || (ctx->line[1] == 'm' && ctx->line[2] == 'e'))
diff = complete_line(ctx->line, &ctx->pos, &ctx->len, groupchats[self->num].peer_names,
groupchats[self->num].num_peers, TOX_MAX_NAME_LENGTH);
else
diff = complete_line(ctx->line, &ctx->pos, &ctx->len, glob_cmd_list, AC_NUM_GLOB_COMMANDS,
MAX_CMDNAME_SIZE);
if (diff != -1) {
if (x + diff > x2 - 1) {
int ofst = (x + diff - 1) - (x2 - 1);
wmove(self->window, y + 1, ofst);
} else {
wmove(self->window, y, x + diff);
}
} else {
wmove(self->window, y, x+diff);
beep();
}
} else {
beep();
}
} else {
beep();
}
}
/* Scroll peerlist up and down one position if list overflows window */
else if (key == KEY_NPAGE) {
int L = y2 - CHATBOX_HEIGHT - SDBAR_OFST;
/* Scroll peerlist up and down one position if list overflows window */
else if (key == KEY_NPAGE) {
int L = y2 - CHATBOX_HEIGHT - SDBAR_OFST;
if (groupchats[self->num].side_pos < groupchats[self->num].num_peers - L)
++groupchats[self->num].side_pos;
}
else if (key == KEY_PPAGE) {
if (groupchats[self->num].side_pos > 0)
--groupchats[self->num].side_pos;
}
else
#if HAVE_WIDECHAR
if (iswprint(key))
#else
if (isprint(key))
#endif
{ /* prevents buffer overflows and strange behaviour when cursor goes past the window */
if ( (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)));
if (groupchats[self->num].side_pos < groupchats[self->num].num_peers - L)
++groupchats[self->num].side_pos;
}
}
/* RETURN key: Execute command or print line */
else if (key == '\n') {
uint8_t line[MAX_STR_SIZE];
else if (key == KEY_PPAGE) {
if (groupchats[self->num].side_pos > 0)
--groupchats[self->num].side_pos;
}
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1)
memset(&line, 0, sizeof(line));
/* RETURN key: Execute command or print line */
else if (key == '\n') {
uint8_t line[MAX_STR_SIZE];
wclear(ctx->linewin);
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
wclrtobot(self->window);
bool close_win = false;
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1)
memset(&line, 0, sizeof(line));
if (!string_is_empty(line))
add_line_to_hist(ctx->line, ctx->len, ctx->ln_history, &ctx->hst_tot, &ctx->hst_pos);
wclear(ctx->linewin);
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
if (line[0] == '/') {
if (close_win = strcmp(line, "/close") == 0) {
set_active_window(0);
int groupnum = self->num;
delwin(ctx->linewin);
del_window(self);
close_groupchatwin(m, groupnum);
} 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
execute(ctx->history, self, m, line, GROUPCHAT_COMMAND_MODE);
} else if (!string_is_empty(line)) {
if (tox_group_message_send(m, self->num, line, strlen(line) + 1) == -1) {
wattron(ctx->history, COLOR_PAIR(RED));
wprintw(ctx->history, " * Failed to send message.\n");
wattroff(ctx->history, COLOR_PAIR(RED));
if (!string_is_empty(line))
add_line_to_hist(ctx->line, ctx->len, ctx->ln_history, &ctx->hst_tot, &ctx->hst_pos);
if (line[0] == '/') {
if (strcmp(line, "/close") == 0) {
close_groupchat(self, m, self->num);
return;
} else if (strcmp(line, "/help") == 0) {
if (strcmp(line, "help global") == 0)
execute(ctx->history, self, m, "/help", GLOBAL_COMMAND_MODE);
else
print_groupchat_help(self);
} else if (strncmp(line, "/me ", strlen("/me ")) == 0) {
send_group_action(self, ctx, m, line + strlen("/me "));
} else {
execute(ctx->history, self, m, line, GROUPCHAT_COMMAND_MODE);
}
} else if (!string_is_empty(line)) {
if (tox_group_message_send(m, self->num, line, strlen(line)) == -1) {
uint8_t *errmsg = " * Failed to send message.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, RED);
}
}
}
if (close_win)
free(ctx);
else
reset_buf(ctx->line, &ctx->pos, &ctx->len);
}
}
}
static void groupchat_onDraw(ToxWindow *self, Tox *m)
{
curs_set(1);
int x2, y2;
getmaxyx(self->window, y2, x2);
ChatContext *ctx = self->chatwin;
line_info_print(self);
wclear(ctx->linewin);
if (ctx->len > 0) {
uint8_t line[MAX_STR_SIZE];
if (ctx->hst->scroll_mode) {
line_info_onDraw(self);
} else {
scrollok(ctx->history, 1);
curs_set(1);
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) {
reset_buf(ctx->line, &ctx->pos, &ctx->len);
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
} else {
mvwprintw(ctx->linewin, 1, 0, "%s", line);
if (ctx->len > 0) {
uint8_t line[MAX_STR_SIZE];
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) {
reset_buf(ctx->line, &ctx->pos, &ctx->len);
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
} else {
mvwprintw(ctx->linewin, 1, 0, "%s", line);
}
}
}
wclear(ctx->sidebar);
mvwhline(ctx->linewin, 0, 0, ACS_HLINE, x2);
mvwvline(ctx->sidebar, 0, 0, ACS_VLINE, y2-CHATBOX_HEIGHT);
mvwaddch(ctx->sidebar, y2-CHATBOX_HEIGHT, 0, ACS_BTEE);
mvwvline(ctx->sidebar, 0, 0, ACS_VLINE, y2 - CHATBOX_HEIGHT);
mvwaddch(ctx->sidebar, y2 - CHATBOX_HEIGHT, 0, ACS_BTEE);
int num_peers = groupchats[self->num].num_peers;
@ -525,20 +618,20 @@ static void groupchat_onDraw(ToxWindow *self, Tox *m)
wattroff(ctx->sidebar, A_BOLD);
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 maxlines = y2 - SDBAR_OFST - CHATBOX_HEIGHT;
int 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;
/* truncate nick to fit in side panel without modifying list */
uint8_t tmpnck[TOX_MAX_NAME_LENGTH];
memcpy(tmpnck, &groupchats[self->num].peer_names[peer*N], SIDEBAR_WIDTH-2);
tmpnck[SIDEBAR_WIDTH-2] = '\0';
memcpy(tmpnck, &groupchats[self->num].peer_names[peer * N], SIDEBAR_WIDTH - 2);
tmpnck[SIDEBAR_WIDTH - 2] = '\0';
wprintw(ctx->sidebar, "%s\n", tmpnck);
}
@ -550,13 +643,32 @@ static void groupchat_onInit(ToxWindow *self, Tox *m)
getmaxyx(self->window, y, x);
ChatContext *ctx = self->chatwin;
ctx->history = subwin(self->window, y-CHATBOX_HEIGHT+1, x-SIDEBAR_WIDTH-1, 0, 0);
scrollok(ctx->history, 1);
ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x, y-CHATBOX_HEIGHT, 0);
ctx->sidebar = subwin(self->window, y-CHATBOX_HEIGHT+1, SIDEBAR_WIDTH, 0, x-SIDEBAR_WIDTH);
print_groupchat_help(ctx);
wmove(self->window, y-CURS_Y_OFFSET, 0);
ctx->history = subwin(self->window, y - CHATBOX_HEIGHT + 1, x - SIDEBAR_WIDTH - 1, 0, 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)
@ -565,6 +677,7 @@ ToxWindow new_group_chat(Tox *m, int groupnum)
memset(&ret, 0, sizeof(ret));
ret.active = true;
ret.is_groupchat = true;
ret.onKey = &groupchat_onKey;
ret.onDraw = &groupchat_onDraw;

View File

@ -1,5 +1,23 @@
/*
* Toxic -- Tox Curses Client
/* groupchat.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 SIDEBAR_WIDTH 16
@ -10,9 +28,12 @@ typedef struct {
bool active;
int num_peers;
int side_pos; /* current position of the sidebar - used for scrolling up and down */
uint8_t *peer_names;
uint8_t *oldpeer_names;
uint8_t *peer_names;
uint8_t *oldpeer_names;
uint16_t *peer_name_lengths;
uint16_t *oldpeer_name_lengths;
} GroupChat;
void kill_groupchat_window(ToxWindow *self);
int init_groupchat_win(ToxWindow *prompt, 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 @@
/*
* Toxic -- Tox Curses Client
/* main.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
@ -7,7 +25,7 @@
#endif
#ifndef SIGWINCH
#define SIGWINCH 28
#define SIGWINCH 28
#endif
#include <curses.h>
@ -20,16 +38,18 @@
#include <locale.h>
#include <string.h>
#include <time.h>
#include <pthread.h>
#ifdef _WIN32
#include <direct.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <direct.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <netdb.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#endif
#include <tox/tox.h>
@ -39,22 +59,33 @@
#include "friendlist.h"
#include "prompt.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
#define PACKAGE_DATADIR "."
#endif
#ifdef _SUPPORT_AUDIO
ToxAv *av;
#endif /* _SUPPORT_AUDIO */
/* Export for use in Callbacks */
char *DATA_FILE = NULL;
char *SRVLIST_FILE = NULL;
ToxWindow *prompt = NULL;
FileSender file_senders[MAX_FILES];
uint8_t max_file_senders_index;
static int f_loadfromfile; /* 1 if we want to load from/save the data file, 0 otherwise */
struct _Winthread Winthread;
struct user_settings *user_settings = NULL;
void on_window_resize(int sig)
{
endwin();
refresh();
clear();
}
@ -64,11 +95,13 @@ static void init_term(void)
/* Setup terminal */
signal(SIGWINCH, on_window_resize);
#if HAVE_WIDECHAR
if (setlocale(LC_ALL, "") == NULL) {
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);
}
#endif
initscr();
cbreak();
@ -77,14 +110,21 @@ static void init_term(void)
timeout(100);
if (has_colors()) {
short bg_color = COLOR_BLACK;
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(1, COLOR_GREEN, COLOR_BLACK);
init_pair(2, COLOR_CYAN, COLOR_BLACK);
init_pair(3, COLOR_RED, COLOR_BLACK);
init_pair(4, COLOR_BLUE, COLOR_BLACK);
init_pair(5, COLOR_YELLOW, COLOR_BLACK);
init_pair(6, COLOR_MAGENTA, COLOR_BLACK);
init_pair(1, COLOR_GREEN, bg_color);
init_pair(2, COLOR_CYAN, bg_color);
init_pair(3, COLOR_RED, bg_color);
init_pair(4, COLOR_BLUE, bg_color);
init_pair(5, COLOR_YELLOW, bg_color);
init_pair(6, COLOR_MAGENTA, bg_color);
init_pair(7, COLOR_BLACK, COLOR_BLACK);
init_pair(8, COLOR_BLACK, COLOR_WHITE);
}
@ -98,8 +138,12 @@ static Tox *init_tox(int ipv4)
int ipv6 = !ipv4;
Tox *m = tox_new(ipv6);
if (TOX_ENABLE_IPV6_DEFAULT && m == NULL) {
fprintf(stderr, "IPv6 didn't initialize, trying IPv4 only\n");
/*
* 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);
}
@ -124,49 +168,52 @@ static Tox *init_tox(int ipv4)
tox_callback_file_data(m, on_file_data, NULL);
#ifdef __linux__
tox_set_name(m, (uint8_t *) "Cool guy", sizeof("Cool guy"));
#elif defined(_WIN32)
tox_set_name(m, (uint8_t *) "I should install GNU/Linux", sizeof("I should install GNU/Linux"));
tox_set_name(m, (uint8_t *) "Cool dude", strlen("Cool dude"));
#elif defined(__FreeBSD__)
tox_set_name(m, (uint8_t *) "Nerd", strlen("Nerd"));
#elif defined(__APPLE__)
tox_set_name(m, (uint8_t *) "Hipster", sizeof("Hipster")); //This used to users of other Unixes are hipsters
tox_set_name(m, (uint8_t *) "Hipster", strlen("Hipster")); /* This used to users of other Unixes are hipsters */
#else
tox_set_name(m, (uint8_t *) "Registered Minix user #4", sizeof("Registered Minix user #4"));
tox_set_name(m, (uint8_t *) "Registered Minix user #4", strlen("Registered Minix user #4"));
#endif
return m;
}
#define MINLINE 50 /* IP: 7 + port: 5 + key: 38 + spaces: 2 = 70. ! (& e.g. tox.im = 6) */
#define MAXLINE 256 /* Approx max number of chars in a sever line (name + port + key) */
#define MAXSERVERS 50
#define SERVERLEN (MAXLINE - TOX_CLIENT_ID_SIZE - 7)
#define MINLINE 50 /* IP: 7 + port: 5 + key: 38 + spaces: 2 = 70. ! (& e.g. tox.im = 6) */
#define MAXLINE 256 /* Approx max number of chars in a sever line (name + port + key) */
#define MAXNODES 50
#define NODELEN (MAXLINE - TOX_CLIENT_ID_SIZE - 7)
static int linecnt = 0;
static char servers[MAXSERVERS][SERVERLEN];
static uint16_t ports[MAXSERVERS];
static uint8_t keys[MAXSERVERS][TOX_CLIENT_ID_SIZE];
static char nodes[MAXNODES][NODELEN];
static uint16_t ports[MAXNODES];
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)
return 1;
char line[MAXLINE];
while (fgets(line, sizeof(line), fp) && linecnt < MAXSERVERS) {
while (fgets(line, sizeof(line), fp) && linecnt < MAXNODES) {
if (strlen(line) > MINLINE) {
char *name = strtok(line, " ");
char *port = strtok(NULL, " ");
char *key_ascii = strtok(NULL, " ");
/* invalid line */
if (name == NULL || port == NULL || key_ascii == NULL)
continue;
strncpy(servers[linecnt], name, SERVERLEN);
servers[linecnt][SERVERLEN - 1] = 0;
strncpy(nodes[linecnt], name, NODELEN);
nodes[linecnt][NODELEN - 1] = 0;
ports[linecnt] = htons(atoi(port));
uint8_t *key_binary = hex_string_to_bin(key_ascii);
@ -186,55 +233,60 @@ int serverlist_load(void)
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,
ports[linenumber], keys[linenumber]);
return tox_bootstrap_from_address(m, nodes[line], TOX_ENABLE_IPV6_DEFAULT,
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:
* 1: failed to open server file
* 2: no line of sufficient length in server file
* 3: (old, removed) failed to split a selected line in the server file
* 4: failed to resolve name to IP
* 5: serverlist file contains no acceptable line
* 1: failed to open node file
* 2: no line of sufficient length in node file
* 3: failed to resolve name to IP
* 4: nodelist 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)
{
if (linecnt > 0) /* already loaded serverlist */
return init_connection_helper(m, rand() % linecnt) ? 0 : 4;
if (linecnt > 0) /* already loaded nodelist */
return init_connection_helper(m, rand() % linecnt) ? 0 : 3;
/* only once:
* - load the serverlist
* - load the nodelist
* - connect to "everyone" inside
*/
if (!init_connection_serverlist_loaded) {
init_connection_serverlist_loaded = 1;
int res = serverlist_load();
if (res)
if (!srvlist_loaded) {
srvlist_loaded = true;
int res = nodelist_load(PACKAGE_DATADIR "/DHTnodes");
if (linecnt < 1)
return res;
if (!linecnt)
return 4;
res = 3;
int i;
int n = MIN(NUM_INIT_NODES, linecnt);
res = 6;
int linenumber;
for(linenumber = 0; linenumber < linecnt; linenumber++)
if (init_connection_helper(m, linenumber))
for (i = 0; i < n; ++i) {
if (init_connection_helper(m, rand() % linecnt))
res = 0;
}
return res;
}
/* empty serverlist file */
return 5;
/* empty nodelist file */
return 4;
}
static void do_connection(Tox *m, ToxWindow *prompt)
{
uint8_t msg[MAX_STR_SIZE] = {0};
static int conn_try = 0;
static int conn_err = 0;
static bool dht_on = false;
@ -242,28 +294,33 @@ static void do_connection(Tox *m, ToxWindow *prompt)
bool is_connected = tox_isconnected(m);
if (!dht_on && !is_connected && !(conn_try++ % 100)) {
prep_prompt_win();
if (!conn_err) {
wprintw(prompt->window, "Establishing connection...\n");
if ((conn_err = init_connection(m)))
wprintw(prompt->window, "\nAuto-connect failed with error code %d\n", conn_err);
if ((conn_err = init_connection(m))) {
snprintf(msg, sizeof(msg), "\nAuto-connect failed with error code %d", conn_err);
}
}
} else if (!dht_on && is_connected) {
dht_on = true;
prompt_update_connectionstatus(prompt, dht_on);
prep_prompt_win();
wprintw(prompt->window, "\nDHT connected.\n");
snprintf(msg, sizeof(msg), "DHT connected.");
} else if (dht_on && !is_connected) {
dht_on = false;
prompt_update_connectionstatus(prompt, dht_on);
prep_prompt_win();
wprintw(prompt->window, "\nDHT disconnected. Attempting to reconnect.\n");
snprintf(msg, sizeof(msg), "\nDHT disconnected. Attempting to reconnect.");
}
if (msg[0])
line_info_add(prompt, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
}
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
@ -343,12 +400,7 @@ static void load_data(Tox *m, char *path)
}
tox_load(m, buf, len);
uint32_t i = 0;
uint8_t name[TOX_MAX_NAME_LENGTH];
while (tox_get_name(m, i, name) != -1)
on_friendadded(m, i++, false);
load_friendlist(m);
free(buf);
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)
{
store_data(m, DATA_FILE);
int i;
for (i = 0; i < max_file_senders_index; ++i) {
if (file_senders[i].active)
fclose(file_senders[i].file);
}
close_all_file_senders();
kill_all_windows();
log_disable(prompt->chatwin->log);
line_info_cleanup(prompt->chatwin->hst);
free(DATA_FILE);
free(SRVLIST_FILE);
free(prompt->stb);
free(prompt->promptbuf);
free(prompt->chatwin->log);
free(prompt->chatwin->hst);
free(prompt->chatwin);
free(user_settings);
tox_kill(m);
#ifdef _SUPPORT_AUDIO
terminate_audio();
#endif /* _SUPPORT_AUDIO */
endwin();
exit(EXIT_SUCCESS);
}
static void do_toxic(Tox *m, ToxWindow *prompt)
{
pthread_mutex_lock(&Winthread.lock);
do_connection(m, prompt);
draw_active_window(m);
do_file_senders(m);
/* main tox-core loop */
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[])
@ -471,7 +469,7 @@ int main(int argc, char *argv[])
int i = 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);
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);
if (DATA_FILE == NULL ) {
if (config_err) {
DATA_FILE = strdup("data");
} else {
DATA_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen("data") + 1);
if (DATA_FILE != NULL) {
strcpy(DATA_FILE, user_config_dir);
strcat(DATA_FILE, CONFIGDIR);
@ -509,25 +509,22 @@ int main(int argc, char *argv[])
}
}
if (config_err) {
SRVLIST_FILE = strdup(PACKAGE_DATADIR "/DHTservers");
} else {
SRVLIST_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen("DHTservers") + 1);
if (SRVLIST_FILE != NULL) {
strcpy(SRVLIST_FILE, user_config_dir);
strcat(SRVLIST_FILE, CONFIGDIR);
strcat(SRVLIST_FILE, "DHTservers");
} else {
endwin();
fprintf(stderr, "malloc() failed. Aborting...\n");
exit(EXIT_FAILURE);
}
}
free(user_config_dir);
init_term();
/* init user_settings struct and load settings from conf file */
user_settings = malloc(sizeof(struct user_settings));
if (user_settings == NULL) {
endwin();
fprintf(stderr, "malloc() failed. Aborting...\n");
exit(EXIT_FAILURE);
}
memset(user_settings, 0, sizeof(struct user_settings));
int settings_err = settings_load(user_settings, NULL);
Tox *m = init_tox(f_use_ipv4);
init_term();
if (m == NULL) {
endwin();
@ -535,30 +532,67 @@ int main(int argc, char *argv[])
exit(EXIT_FAILURE);
}
prompt = init_windows(m);
if (f_loadfromfile)
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) {
attron(COLOR_PAIR(RED) | A_BOLD);
wprintw(prompt->window, "You passed '-f' without giving an argument.\n"
"defaulting to 'data' for a keyfile...\n");
attroff(COLOR_PAIR(RED) | A_BOLD);
msg = "You passed '-f' without giving an argument. Defaulting to 'data' for a keyfile...";
line_info_add(prompt, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
}
if (config_err) {
attron(COLOR_PAIR(RED) | A_BOLD);
wprintw(prompt->window, "Unable to determine configuration directory.\n"
"defaulting to 'data' for a keyfile...\n");
attroff(COLOR_PAIR(RED) | A_BOLD);
msg = "Unable to determine configuration directory. Defaulting to 'data' for a keyfile...";
line_info_add(prompt, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
}
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);
usleep(10000);
}
return 0;
}

View File

@ -1,17 +1,69 @@
/*
* Toxic -- Tox Curses Client
/* misc_tools.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 <limits.h>
#include "toxic_windows.h"
#include "misc_tools.h"
#include "settings.h"
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[])
{
size_t len = strlen(hex_string);
@ -32,26 +84,6 @@ unsigned char *hex_string_to_bin(char hex_string[])
return val;
}
/* Get the current local time */
struct tm *get_time(void)
{
struct tm *timeinfo;
time_t now;
time(&now);
timeinfo = localtime(&now);
return timeinfo;
}
/* Prints the time to given window */
void print_time(WINDOW *window)
{
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 */
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)
return -1;
if ((len = mbstowcs(buf, string, n)) == (size_t) -1)
if ((len = mbstowcs(buf, string, n)) == (size_t) - 1)
return -1;
return len;
@ -81,23 +113,23 @@ int wcs_to_mbs_buf(uint8_t *buf, const wchar_t *string, size_t n)
if (n < len)
return -1;
if ((len = wcstombs(buf, string, n)) == (size_t) -1)
if ((len = wcstombs(buf, string, n)) == (size_t) - 1)
return -1;
return len;
}
/* convert wide characters to multibyte string: string returned must be free'd */
/* convert wide characters to multibyte string: string returned must be freed */
uint8_t *wcs_to_mbs(wchar_t *string)
{
uint8_t *ret = NULL;
size_t len = wcstombs(NULL, string, 0);
if (len != (size_t) -1) {
if (len != (size_t) - 1) {
ret = malloc(++len);
if (ret != NULL) {
if (wcstombs(ret, string, len) == (size_t) -1)
if (wcstombs(ret, string, len) == (size_t) - 1)
return NULL;
}
} else {
@ -134,8 +166,8 @@ char *wc_to_char(wchar_t ch)
return ret;
}
/* Returns true if connection has timed out, false otherwise */
bool timed_out(uint64_t timestamp, uint64_t curtime, uint64_t timeout)
/* Returns 1 if connection has timed out, 0 otherwise */
int timed_out(uint64_t timestamp, uint64_t curtime, uint64_t timeout)
{
return timestamp + timeout <= curtime;
}
@ -144,20 +176,22 @@ bool timed_out(uint64_t timestamp, uint64_t curtime, uint64_t timeout)
void alert_window(ToxWindow *self, int type, bool is_beep)
{
switch (type) {
case WINDOW_ALERT_0:
self->alert0 = true;
break;
case WINDOW_ALERT_1:
self->alert1 = true;
break;
case WINDOW_ALERT_2:
self->alert2 = true;
break;
case WINDOW_ALERT_0:
self->alert0 = true;
break;
case WINDOW_ALERT_1:
self->alert1 = true;
break;
case WINDOW_ALERT_2:
self->alert2 = true;
break;
}
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();
}
@ -167,23 +201,27 @@ int qsort_strcasecmp_hlpr(const void *nick1, const void *nick2)
return strcasecmp((const char *) nick1, (const char *) nick2);
}
/* Returns true if nick is valid. A valid toxic nick:
/* Returns 1 if nick is valid, 0 if not. A valid toxic nick:
- cannot be empty
- cannot start with a space
- must not contain a forward slash (for logfile naming purposes)
- must not contain contiguous spaces */
bool valid_nick(uint8_t *nick)
int valid_nick(uint8_t *nick)
{
if (!nick[0] || nick[0] == ' ')
return false;
return 0;
int i;
for (i = 0; nick[i]; ++i) {
if (nick[i] == ' ' && nick[i+1] == ' ')
return false;
if (nick[i] == ' ' && nick[i + 1] == ' ')
return 0;
if (nick[i] == '/')
return 0;
}
return true;
return 1;
}
/* Moves cursor to the end of the line in given window */
@ -191,7 +229,7 @@ void mv_curs_end(WINDOW *w, size_t len, int max_y, int max_x)
{
int end_y = (len / max_x) + (max_y - CURS_Y_OFFSET);
int end_x = len % max_x;
wmove(w, end_y, end_x);
wmove(w, end_y, end_x);
}
/* gets base file name from path or original file name if no path is supplied */
@ -200,15 +238,15 @@ void get_file_name(uint8_t *pathname, uint8_t *namebuf)
int idx = strlen(pathname) - 1;
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 (!strlen(++filename))
filename = pathname;
} else {
filename = strrchr(pathname, '\\'); // Try windows style paths
filename = strrchr(pathname, '\\'); /* Try windows style paths */
if (filename == NULL)
filename = pathname;

View File

@ -1,18 +1,42 @@
/*
* Toxic -- Tox Curses Client
/* misc_tools.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 MIN(x, y) (((x) < (y)) ? (x) : (y))
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
/* convert a hex string to binary */
unsigned char *hex_string_to_bin(char hex_string[]);
/* get the current unix time */
uint64_t get_unix_time(void);
/*Puts the current time in buf in the format of [Hour:Min:Sec] */
void get_time_str(uint8_t *buf);
/* get the current local time */
struct tm *get_time(void);
/* Prints the time to given window */
void print_time(WINDOW *window);
/* updates current unix time (should be run once per do_toxic loop) */
void update_unix_time(void);
/* Returns 1 if the string is empty, 0 otherwise */
int string_is_empty(char *string);
@ -30,8 +54,8 @@ uint8_t *wcs_to_mbs(wchar_t *string);
/* convert a wide char to multibyte char */
char *wc_to_char(wchar_t ch);
/* Returns true if connection has timed out, false otherwise */
bool timed_out(uint64_t timestamp, uint64_t timeout, uint64_t curtime);
/* Returns 1 if connection has timed out, 0 otherwise */
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 */
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 start with a space
- must not contain contiguous spaces */
bool valid_nick(uint8_t *nick);
int valid_nick(uint8_t *nick);
/* Moves the cursor to the end of the line in given window */
void mv_curs_end(WINDOW *w, size_t len, int max_y, int max_x);

View File

@ -1,5 +1,23 @@
/*
* Toxic -- Tox Curses Client
/* prompt.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
@ -14,10 +32,16 @@
#include "execute.h"
#include "misc_tools.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 num_frnd_requests = 0;
extern ToxWindow *prompt;
struct _Winthread Winthread;
extern struct user_settings *user_settings;
/* Array of global command names used for tab completion. */
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" },
{ "/help" },
{ "/join" },
{ "/log" },
{ "/myid" },
{ "/nick" },
{ "/note" },
{ "/quit" },
{ "/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 */
void prompt_update_nick(ToxWindow *prompt, uint8_t *nick, uint16_t len)
{
StatusBar *statusbar = prompt->stb;
snprintf(statusbar->nick, sizeof(statusbar->nick), "%s", nick);
statusbar->nick_len = len;
statusbar->nick_len = strlen(statusbar->nick);
}
/* 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;
snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg);
statusbar->statusmsg_len = len;
statusbar->statusmsg_len = strlen(statusbar->statusmsg);
}
/* Updates own status in prompt statusbar */
void prompt_update_status(ToxWindow *prompt, TOX_USERSTATUS status)
void prompt_update_status(ToxWindow *prompt, uint8_t status)
{
StatusBar *statusbar = prompt->stb;
statusbar->status = status;
@ -86,7 +99,7 @@ void prompt_update_connectionstatus(ToxWindow *prompt, bool is_connected)
statusbar->is_online = is_connected;
}
/* Adds friend request to pending friend requests.
/* Adds friend request to pending friend requests.
Returns request number on success, -1 if queue is full or other error. */
static int add_friend_request(uint8_t *public_key)
{
@ -109,153 +122,166 @@ static int add_friend_request(uint8_t *public_key)
return -1;
}
static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key)
static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
{
PromptBuf *prt = self->promptbuf;
ChatContext *ctx = self->chatwin;
int x, y, y2, x2;
getyx(self->window, y, x);
getmaxyx(self->window, y2, x2);
getyx(ctx->history, y, x);
getmaxyx(ctx->history, y2, x2);
/* BACKSPACE key: Remove one character from line */
if (key == 0x107 || key == 0x8 || key == 0x7f) {
if (prt->pos > 0) {
del_char_buf_bck(prt->line, &prt->pos, &prt->len);
wmove(self->window, y, x-1); /* not necessary but fixes a display glitch */
prt->scroll = false;
} else {
beep();
/* TODO this is buggy */
/* ESC key: Toggle history scroll mode */
/*
if (key == T_KEY_ESC) {
bool scroll = ctx->hst->scroll_mode ? false : true;
line_info_toggle_scroll(self, scroll);
}
*/
/* If we're in scroll mode ignore rest of function */
if (ctx->hst->scroll_mode) {
line_info_onKey(self, key);
return;
}
if (ltr) {
if (ctx->len < (MAX_STR_SIZE - 1)) {
add_char_to_buf(ctx->line, &ctx->pos, &ctx->len, key);
}
}
} else { /* if (!ltr) */
else if (key == KEY_DC) { /* DEL key: Remove character at pos */
if (prt->pos != prt->len) {
del_char_buf_frnt(prt->line, &prt->pos, &prt->len);
prt->scroll = false;
} else {
beep();
}
}
else if (key == T_KEY_DISCARD) { /* CTRL-U: Delete entire line behind pos */
if (prt->pos > 0) {
wmove(self->window, prt->orig_y, X_OFST);
wclrtobot(self->window);
discard_buf(prt->line, &prt->pos, &prt->len);
} else {
beep();
}
}
else if (key == T_KEY_KILL) { /* CTRL-K: Delete entire line in front of pos */
if (prt->len != prt->pos)
kill_buf(prt->line, &prt->pos, &prt->len);
else
beep();
}
else if (key == KEY_HOME) { /* HOME key: Move cursor to beginning of line */
if (prt->pos != 0)
prt->pos = 0;
}
else if (key == KEY_END) { /* END key: move cursor to end of line */
if (prt->pos != prt->len)
prt->pos = prt->len;
}
else if (key == KEY_LEFT) {
if (prt->pos > 0)
--prt->pos;
else
beep();
}
else if (key == KEY_RIGHT) {
if (prt->pos < prt->len)
++prt->pos;
else
beep();
}
else if (key == KEY_UP) { /* fetches previous item in history */
wmove(self->window, prt->orig_y, X_OFST);
fetch_hist_item(prt->line, &prt->pos, &prt->len, prt->ln_history, prt->hst_tot,
&prt->hst_pos, LN_HIST_MV_UP);
/* adjust line y origin appropriately when window scrolls down */
if (prt->at_bottom && prt->len >= x2 - X_OFST) {
int px2 = prt->len >= x2 ? x2 : x2 - X_OFST;
int p_ofst = px2 != x2 ? 0 : X_OFST;
if (px2 <= 0)
return;
int k = prt->orig_y + ((prt->len + p_ofst) / px2);
if (k >= y2) {
wprintw(self->window, "\n");
--prt->orig_y;
/* BACKSPACE key: Remove one character from line */
if (key == 0x107 || key == 0x8 || key == 0x7f) {
if (ctx->pos > 0) {
del_char_buf_bck(ctx->line, &ctx->pos, &ctx->len);
wmove(ctx->history, y, x - 1); /* not necessary but fixes a display glitch */
} else {
beep();
}
}
}
else if (key == KEY_DOWN) { /* fetches next item in history */
wmove(self->window, prt->orig_y, X_OFST);
fetch_hist_item(prt->line, &prt->pos, &prt->len, prt->ln_history, prt->hst_tot,
&prt->hst_pos, LN_HIST_MV_DWN);
}
else if (key == '\t') { /* TAB key: completes command */
if (prt->len > 1 && prt->line[0] == '/') {
if (complete_line(prt->line, &prt->pos, &prt->len, glob_cmd_list, AC_NUM_GLOB_COMMANDS,
MAX_CMDNAME_SIZE) == -1)
else if (key == KEY_DC) { /* DEL key: Remove character at pos */
if (ctx->pos != ctx->len) {
del_char_buf_frnt(ctx->line, &ctx->pos, &ctx->len);
} else {
beep();
} else {
beep();
}
}
}
else
#if HAVE_WIDECHAR
if (iswprint(key))
#else
if (isprint(key))
#endif
{
if (prt->len < (MAX_STR_SIZE-1)) {
add_char_to_buf(prt->line, &prt->pos, &prt->len, key);
prt->scroll = true;
else if (key == T_KEY_DISCARD) { /* CTRL-U: Delete entire line behind pos */
if (ctx->pos > 0) {
wmove(ctx->history, ctx->orig_y, X_OFST);
wclrtobot(ctx->history);
discard_buf(ctx->line, &ctx->pos, &ctx->len);
} else {
beep();
}
}
}
/* RETURN key: execute command */
else if (key == '\n') {
wprintw(self->window, "\n");
uint8_t line[MAX_STR_SIZE];
if (wcs_to_mbs_buf(line, prt->line, MAX_STR_SIZE) == -1)
memset(&line, 0, sizeof(line));
else if (key == T_KEY_KILL) { /* CTRL-K: Delete entire line in front of pos */
if (ctx->len != ctx->pos)
kill_buf(ctx->line, &ctx->pos, &ctx->len);
else
beep();
}
if (!string_is_empty(line))
add_line_to_hist(prt->line, prt->len, prt->ln_history, &prt->hst_tot, &prt->hst_pos);
else if (key == KEY_HOME || key == T_KEY_C_A) { /* HOME/C-a key: Move cursor to start of line */
if (ctx->pos != 0)
ctx->pos = 0;
}
execute(self->window, self, m, line, GLOBAL_COMMAND_MODE);
reset_buf(prt->line, &prt->pos, &prt->len);
else if (key == KEY_END || key == T_KEY_C_E) { /* END/C-e key: move cursor to end of line */
if (ctx->pos != ctx->len)
ctx->pos = ctx->len;
}
else if (key == KEY_LEFT) {
if (ctx->pos > 0)
--ctx->pos;
else
beep();
}
else if (key == KEY_RIGHT) {
if (ctx->pos < ctx->len)
++ctx->pos;
else
beep();
}
else if (key == KEY_UP) { /* fetches previous item in history */
wmove(ctx->history, ctx->orig_y, X_OFST);
fetch_hist_item(ctx->line, &ctx->pos, &ctx->len, ctx->ln_history, ctx->hst_tot,
&ctx->hst_pos, MOVE_UP);
/* adjust line y origin appropriately when window scrolls down */
if (ctx->at_bottom && ctx->len >= x2 - X_OFST) {
int px2 = ctx->len >= x2 ? x2 : x2 - X_OFST;
int p_ofst = px2 != x2 ? 0 : X_OFST;
if (px2 <= 0)
return;
int k = ctx->orig_y + ((ctx->len + p_ofst) / px2);
if (k >= y2) {
--ctx->orig_y;
}
}
}
else if (key == KEY_DOWN) { /* fetches next item in history */
wmove(ctx->history, ctx->orig_y, X_OFST);
fetch_hist_item(ctx->line, &ctx->pos, &ctx->len, ctx->ln_history, ctx->hst_tot,
&ctx->hst_pos, MOVE_DOWN);
}
else if (key == '\t') { /* TAB key: completes command */
if (ctx->len > 1 && ctx->line[0] == '/') {
if (complete_line(ctx->line, &ctx->pos, &ctx->len, glob_cmd_list, AC_NUM_GLOB_COMMANDS,
MAX_CMDNAME_SIZE) == -1)
beep();
} else {
beep();
}
}
/* RETURN key: execute command */
else if (key == '\n') {
wprintw(ctx->history, "\n");
uint8_t line[MAX_STR_SIZE] = {0};
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1)
memset(&line, 0, sizeof(line));
if (!string_is_empty(line))
add_line_to_hist(ctx->line, ctx->len, ctx->ln_history, &ctx->hst_tot, &ctx->hst_pos);
line_info_add(self, NULL, NULL, NULL, line, PROMPT, 0, 0);
execute(ctx->history, self, m, line, GLOBAL_COMMAND_MODE);
reset_buf(ctx->line, &ctx->pos, &ctx->len);
}
}
}
static void prompt_onDraw(ToxWindow *self, Tox *m)
{
PromptBuf *prt = self->promptbuf;
ChatContext *ctx = self->chatwin;
curs_set(1);
int x, y, x2, y2;
getyx(self->window, y, x);
getmaxyx(self->window, y2, x2);
wclrtobot(self->window);
getyx(ctx->history, y, x);
getmaxyx(ctx->history, y2, x2);
if (!ctx->hst->scroll_mode) {
curs_set(1);
scrollok(ctx->history, 1);
}
line_info_print(self);
/* if len is >= screen width offset max x by X_OFST to account for prompt char */
int px2 = prt->len >= x2 ? x2 : x2 - X_OFST;
int px2 = ctx->len >= x2 ? x2 : x2 - X_OFST;
if (px2 <= 0)
return;
@ -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) */
int p_ofst = px2 != x2 ? 0 : X_OFST;
if (prt->len > 0) {
if (ctx->len > 0) {
uint8_t line[MAX_STR_SIZE];
if (wcs_to_mbs_buf(line, prt->line, MAX_STR_SIZE) == -1)
reset_buf(prt->line, &prt->pos, &prt->len);
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1)
reset_buf(ctx->line, &ctx->pos, &ctx->len);
else
mvwprintw(self->window, prt->orig_y, X_OFST, line);
mvwprintw(ctx->history, ctx->orig_y, X_OFST, line);
int k = prt->orig_y + ((prt->len + p_ofst) / px2);
int k = ctx->orig_y + ((ctx->len + p_ofst) / px2);
prt->at_bottom = k == y2 - 1;
ctx->at_bottom = k == y2 - 1;
bool botm = k == y2;
bool edge = (prt->len + p_ofst) % px2 == 0;
bool edge = (ctx->len + p_ofst) % px2 == 0;
/* move point of line origin up when input scrolls screen down */
if (prt->scroll && edge && botm) {
--prt->orig_y;
prt->scroll = false;
}
if (edge && botm)
--ctx->orig_y;
} else { /* Mark point of origin for new line */
prt->orig_y = y;
ctx->orig_y = y;
}
wattron(self->window, COLOR_PAIR(GREEN));
mvwprintw(self->window, prt->orig_y, 0, "$ ");
wattroff(self->window, COLOR_PAIR(GREEN));
wattron(ctx->history, COLOR_PAIR(GREEN));
mvwprintw(ctx->history, ctx->orig_y, 0, "$ ");
wattroff(ctx->history, COLOR_PAIR(GREEN));
StatusBar *statusbar = self->stb;
werase(statusbar->topline);
@ -298,141 +322,189 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
if (statusbar->is_online) {
int colour = WHITE;
char *status_text = "Unknown";
const uint8_t *status_text = "Unknown";
switch (statusbar->status) {
case TOX_USERSTATUS_NONE:
status_text = "Online";
colour = GREEN;
break;
case TOX_USERSTATUS_AWAY:
status_text = "Away";
colour = YELLOW;
break;
case TOX_USERSTATUS_BUSY:
status_text = "Busy";
colour = RED;
break;
case TOX_USERSTATUS_NONE:
status_text = "Online";
colour = GREEN;
break;
case TOX_USERSTATUS_AWAY:
status_text = "Away";
colour = YELLOW;
break;
case TOX_USERSTATUS_BUSY:
status_text = "Busy";
colour = RED;
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);
wprintw(statusbar->topline, "[%s]", status_text);
wprintw(statusbar->topline, " [%s]", status_text);
wattroff(statusbar->topline, COLOR_PAIR(colour) | A_BOLD);
wattron(statusbar->topline, A_BOLD);
wprintw(statusbar->topline, " %s", statusbar->nick);
wattroff(statusbar->topline, A_BOLD);
} else {
wprintw(statusbar->topline, "[Offline]");
wattron(statusbar->topline, A_BOLD);
wprintw(statusbar->topline, " %s ", statusbar->nick);
wattroff(statusbar->topline, A_BOLD);
wprintw(statusbar->topline, "[Offline]");
}
wattron(statusbar->topline, A_BOLD);
wprintw(statusbar->topline, " - %s", statusbar->statusmsg);
wattroff(statusbar->topline, A_BOLD);
if (statusbar->statusmsg[0])
wprintw(statusbar->topline, " - %s", statusbar->statusmsg);
/* put cursor back in correct spot */
int y_m = prt->orig_y + ((prt->pos + p_ofst) / px2);
int x_m = (prt->pos + X_OFST) % x2;
int y_m = ctx->orig_y + ((ctx->pos + p_ofst) / px2);
int x_m = (ctx->pos + X_OFST) % x2;
wmove(self->window, y_m, x_m);
}
static void prompt_onInit(ToxWindow *self, Tox *m)
{
scrollok(self->window, true);
execute(self->window, self, m, "/help", GLOBAL_COMMAND_MODE);
wclrtoeol(self->window);
}
static void prompt_onConnectionChange(ToxWindow *self, Tox *m, int friendnum , uint8_t status)
static void prompt_onConnectionChange(ToxWindow *self, Tox *m, int32_t friendnum , uint8_t status)
{
if (friendnum < 0)
return;
prep_prompt_win();
ChatContext *ctx = self->chatwin;
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
uint8_t nick[TOX_MAX_NAME_LENGTH] = {0};
int n_len = tox_get_name(m, friendnum, nick);
n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH - 1);
if (tox_get_name(m, friendnum, nick) == -1)
return;
if (!nick[0])
if (!nick[0]) {
snprintf(nick, sizeof(nick), "%s", UNKNOWN_NAME);
n_len = strlen(UNKNOWN_NAME);
}
wprintw(self->window, "\n");
print_time(self->window);
nick[n_len] = '\0';
uint8_t timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt);
uint8_t *msg;
if (status == 1) {
wattron(self->window, COLOR_PAIR(GREEN));
wattron(self->window, A_BOLD);
wprintw(self->window, "* %s ", nick);
wattroff(self->window, A_BOLD);
wprintw(self->window, "has come online\n");
wattroff(self->window, COLOR_PAIR(GREEN));
msg = "has come online";
line_info_add(self, timefrmt, nick, NULL, msg, CONNECTION, 0, GREEN);
write_to_log(msg, nick, ctx->log, true);
alert_window(self, WINDOW_ALERT_2, false);
} else {
wattron(self->window, COLOR_PAIR(RED));
wattron(self->window, A_BOLD);
wprintw(self->window, "* %s ", nick);
wattroff(self->window, A_BOLD);
wprintw(self->window, "has gone offline\n");
wattroff(self->window, COLOR_PAIR(RED));
msg = "has gone offline";
line_info_add(self, timefrmt, nick, NULL, msg, CONNECTION, 0, RED);
write_to_log(msg, nick, ctx->log, true);
}
}
static void prompt_onFriendRequest(ToxWindow *self, uint8_t *key, uint8_t *data, uint16_t length)
static void prompt_onFriendRequest(ToxWindow *self, Tox *m, uint8_t *key, uint8_t *data, uint16_t length)
{
// make sure message data is null-terminated
data[length - 1] = 0;
data[length] = '\0';
prep_prompt_win();
wprintw(self->window, "\n");
print_time(self->window);
wprintw(self->window, "Friend request with the message: '%s'\n", data);
ChatContext *ctx = self->chatwin;
uint8_t timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt);
uint8_t msg[MAX_STR_SIZE];
snprintf(msg, sizeof(msg), "Friend request with the message '%s'", data);
line_info_add(self, timefrmt, NULL, NULL, msg, SYS_MSG, 0, 0);
write_to_log(msg, "", ctx->log, true);
int n = add_friend_request(key);
if (n == -1) {
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;
}
wprintw(self->window, "Type \"/accept %d\" to accept it.\n", n);
snprintf(msg, sizeof(msg), "Type \"/accept %d\" to accept it.", n);
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
alert_window(self, WINDOW_ALERT_1, true);
}
void prompt_init_statusbar(ToxWindow *self, Tox *m)
{
int x, y;
getmaxyx(self->window, y, x);
int x2, y2;
getmaxyx(self->window, y2, x2);
/* Init statusbar info */
StatusBar *statusbar = self->stb;
statusbar->status = TOX_USERSTATUS_NONE;
statusbar->is_online = false;
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
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 nick[TOX_MAX_NAME_LENGTH];
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);
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);
else
snprintf(statusmsg, MAX_STR_SIZE, "Toxing on Toxic hacker edition");
s_len = strlen(statusmsg);
statusmsg[s_len] = '\0';
}
m_set_statusmessage(m, statusmsg, strlen(statusmsg) + 1);
snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg);
prompt_update_statusmessage(prompt, statusmsg, s_len);
prompt_update_status(prompt, status);
prompt_update_nick(prompt, nick, n_len);
/* Init statusbar subwindow */
statusbar->topline = subwin(self->window, 2, x, 0, 0);
statusbar->topline = subwin(self->window, 2, x2, 0, 0);
}
static void prompt_onInit(ToxWindow *self, Tox *m)
{
ChatContext *ctx = self->chatwin;
curs_set(1);
int y2, x2;
getmaxyx(self->window, y2, x2);
ctx->history = subwin(self->window, y2, x2, 0, 0);
scrollok(ctx->history, 1);
ctx->log = malloc(sizeof(struct chatlog));
ctx->hst = malloc(sizeof(struct history));
if (ctx->log == NULL || ctx->hst == NULL) {
endwin();
fprintf(stderr, "malloc() failed. Aborting...\n");
exit(EXIT_FAILURE);
}
memset(ctx->log, 0, sizeof(struct chatlog));
memset(ctx->hst, 0, sizeof(struct history));
line_info_init(ctx->hst);
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)
@ -441,6 +513,7 @@ ToxWindow new_prompt(void)
memset(&ret, 0, sizeof(ret));
ret.active = true;
ret.is_prompt = true;
ret.onKey = &prompt_onKey;
ret.onDraw = &prompt_onDraw;
@ -450,11 +523,11 @@ ToxWindow new_prompt(void)
strcpy(ret.name, "prompt");
PromptBuf *promptbuf = calloc(1, sizeof(PromptBuf));
ChatContext *chatwin = calloc(1, sizeof(ChatContext));
StatusBar *stb = calloc(1, sizeof(StatusBar));
if (stb != NULL && promptbuf != NULL) {
ret.promptbuf = promptbuf;
if (stb != NULL && chatwin != NULL) {
ret.chatwin = chatwin;
ret.stb = stb;
} else {
endwin();

View File

@ -1,5 +1,23 @@
/*
* Toxic -- Tox Curses Client
/* prompt.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 PROMPT_H_UZYGWFFL
@ -7,14 +25,19 @@
#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);
void prep_prompt_win(void);
void prompt_init_statusbar(ToxWindow *self, Tox *m);
void prompt_update_nick(ToxWindow *prompt, uint8_t *nick, uint16_t len);
void prompt_update_statusmessage(ToxWindow *prompt, uint8_t *statusmsg, uint16_t len);
void prompt_update_status(ToxWindow *prompt, TOX_USERSTATUS status);
void prompt_update_status(ToxWindow *prompt, uint8_t status);
void prompt_update_connectionstatus(ToxWindow *prompt, bool is_connected);
#endif /* end of include guard: PROMPT_H_UZYGWFFL */

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 -- Tox Curses Client
/* toxic_strings.c
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.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;
for (i = *len; i >= *pos && i >= 0; --i)
buf[i+1] = buf[i];
buf[i + 1] = buf[i];
buf[(*pos)++] = ch;
++(*len);
@ -34,8 +56,8 @@ void del_char_buf_bck(wchar_t *buf, size_t *pos, size_t *len)
int i;
/* similar to add_char_to_buf but deletes a char */
for (i = *pos-1; i <= *len; ++i)
buf[i] = buf[i+1];
for (i = *pos - 1; i <= *len; ++i)
buf[i] = buf[i + 1];
--(*pos);
--(*len);
@ -50,7 +72,7 @@ void del_char_buf_frnt(wchar_t *buf, size_t *pos, size_t *len)
int i;
for (i = *pos; i < *len; ++i)
buf[i] = buf[i+1];
buf[i] = buf[i + 1];
--(*len);
}
@ -98,13 +120,13 @@ static void shift_hist_back(wchar_t (*hst)[MAX_STR_SIZE], int *hst_tot)
int n = MAX_LINE_HIST - HIST_PURGE;
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;
}
/* adds a line to the ln_history buffer at hst_pos and sets hst_pos to end of history. */
void add_line_to_hist(const wchar_t *buf, size_t len, wchar_t (*hst)[MAX_STR_SIZE], int *hst_tot,
void add_line_to_hist(const wchar_t *buf, size_t len, wchar_t (*hst)[MAX_STR_SIZE], int *hst_tot,
int *hst_pos)
{
if (len > MAX_STR_SIZE)
@ -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_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.
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],
int hst_tot, int *hst_pos, int key_dir)
{
if (key_dir == LN_HIST_MV_UP) {
if (key_dir == MOVE_UP) {
if (--(*hst_pos) < 0) {
++(*hst_pos);
*hst_pos = 0;
beep();
}
} else {
if (++(*hst_pos) >= hst_tot) {
--(*hst_pos);
beep();
*hst_pos = hst_tot;
reset_buf(buf, pos, len);
return;
}
}
@ -146,12 +170,12 @@ void fetch_hist_item(wchar_t *buf, size_t *pos, size_t *len, wchar_t (*hst)[MAX_
*len = h_len;
}
/* looks for the first instance in list that begins with the last entered word in buf according to pos,
then fills buf with the complete word. e.g. "Hello jo" would complete the buffer
/* looks for the first instance in list that begins with the last entered word in buf according to pos,
then fills buf with the complete word. e.g. "Hello jo" would complete the buffer
with "Hello john".
list is a pointer to the list of strings being compared, n_items is the number of items
in the list, and size is the size of each item in the list.
in the list, and size is the size of each item in the list.
Returns the difference between the old len and new len of buf on success, -1 if error */
int complete_line(wchar_t *buf, size_t *pos, size_t *len, const void *list, int n_items, int size)
@ -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;
uint8_t ubuf[MAX_STR_SIZE];
/* work with multibyte string copy of buf for simplicity */
if (wcs_to_mbs_buf(ubuf, buf, MAX_STR_SIZE) == -1)
return -1;
@ -171,10 +196,11 @@ int complete_line(wchar_t *buf, size_t *pos, size_t *len, const void *list, int
snprintf(tmp, sizeof(tmp), "%s", ubuf);
tmp[*pos] = '\0';
uint8_t *sub = strrchr(tmp, ' ');
int n_endchrs = 1; /* 1 = append space to end of match, 2 = append ": " */
int n_endchrs = 1; /* 1 = append space to end of match, 2 = append ": " */
if (!sub++) {
sub = tmp;
if (sub[0] != '/') /* make sure it's not a command */
n_endchrs = 2;
}
@ -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 */
for (i = 0; i < n_items; ++i) {
match = &L[i*size];
match = &L[i * size];
if (is_match = strncasecmp(match, sub, s_len) == 0)
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 ": ") */
const uint8_t *endchrs = n_endchrs == 1 ? " " : ": ";
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;
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];
strcpy(tmpend, &ubuf[*pos]);
strcpy(&ubuf[strt], match);
strcpy(&ubuf[strt+m_len], endchrs);
strcpy(&ubuf[strt+m_len+n_endchrs], tmpend);
strcpy(&ubuf[strt + m_len], endchrs);
strcpy(&ubuf[strt + m_len + n_endchrs], tmpend);
/* convert to widechar and copy back to original buf */
wchar_t newbuf[MAX_STR_SIZE];

View File

@ -1,5 +1,23 @@
/*
* Toxic -- Tox Curses Client
/* toxic_strings.h
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
/* Adds char to buffer at pos */
@ -20,23 +38,18 @@ void kill_buf(wchar_t *buf, size_t *pos, size_t *len);
/* nulls buf and sets pos and len to 0 */
void reset_buf(wchar_t *buf, size_t *pos, size_t *len);
/* looks for the first instance in list that begins with the last entered word in buf according to pos,
then fills buf with the complete word. e.g. "Hello jo" would complete the buffer
/* looks for the first instance in list that begins with the last entered word in buf according to pos,
then fills buf with the complete word. e.g. "Hello jo" would complete the buffer
with "Hello john".
list is a pointer to the list of strings being compared, n_items is the number of items
in the list, and size is the size of each item in the list.
in the list, and size is the size of each item in the list.
Returns the difference between the old len and new len of buf on success, -1 if error */
int complete_line(wchar_t *buf, size_t *pos, size_t *len, const void *list, int n_items, int size);
enum {
LN_HIST_MV_UP,
LN_HIST_MV_DWN,
};
/* adds a line to the ln_history buffer at hst_pos and sets hst_pos to last history item. */
void add_line_to_hist(const wchar_t *buf, size_t len, wchar_t (*hst)[MAX_STR_SIZE], int *hst_tot,
void add_line_to_hist(const wchar_t *buf, size_t len, wchar_t (*hst)[MAX_STR_SIZE], int *hst_tot,
int *hst_pos);
/* copies history item at hst_pos to buf. Sets pos and len to the len of the history item.

View File

@ -1,5 +1,23 @@
/*
* Toxic -- Tox Curses Client
/* toxic_windows.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 _windows_h
@ -10,12 +28,17 @@
#endif
#include <curses.h>
#include <pthread.h>
#include <wctype.h>
#include <wchar.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_FRIENDS_NUM 100
@ -27,6 +50,7 @@
#define CURS_Y_OFFSET 3 /* y-axis cursor offset for chat contexts */
#define CHATBOX_HEIGHT 4
#define KEY_IDENT_DIGITS 2 /* number of hex digits to display for the pub-key based identifier */
#define TIME_STR_SIZE 16
#define EXIT_SUCCESS 0
#define EXIT_FAILURE 1
@ -36,6 +60,9 @@
#define T_KEY_DISCARD 0x15 /* ctrl-u */
#define T_KEY_NEXT 0x10 /* ctrl-p */
#define T_KEY_PREV 0x0F /* ctrl-o */
#define T_KEY_C_E 0x05 /* ctrl-e */
#define T_KEY_C_A 0x01 /* ctrl-a */
#define T_KEY_ESC 0x1B /* ESC key */
/* Curses foreground colours (background is black) */
enum {
@ -56,9 +83,14 @@ enum {
WINDOW_ALERT_2,
};
/* Fixes text color problem on some terminals.
enum {
MOVE_UP,
MOVE_DOWN,
};
/* Fixes text color problem on some terminals.
Uncomment if necessary */
//#define URXVT_FIX
/* #define URXVT_FIX */
typedef struct ToxWindow ToxWindow;
typedef struct StatusBar StatusBar;
@ -66,50 +98,73 @@ typedef struct PromptBuf PromptBuf;
typedef struct ChatContext ChatContext;
struct ToxWindow {
void(*onKey)(ToxWindow *, Tox *, wint_t);
void(*onKey)(ToxWindow *, Tox *, wint_t, bool);
void(*onDraw)(ToxWindow *, Tox *);
void(*onInit)(ToxWindow *, Tox *);
void(*onFriendRequest)(ToxWindow *, uint8_t *, uint8_t *, uint16_t);
void(*onFriendAdded)(ToxWindow *, Tox *, int, bool);
void(*onConnectionChange)(ToxWindow *, Tox *, int, uint8_t);
void(*onMessage)(ToxWindow *, Tox *, int, uint8_t *, uint16_t);
void(*onNickChange)(ToxWindow *, Tox *, int, uint8_t *, uint16_t);
void(*onStatusChange)(ToxWindow *, Tox *, int, TOX_USERSTATUS);
void(*onStatusMessageChange)(ToxWindow *, int, uint8_t *, uint16_t);
void(*onAction)(ToxWindow *, Tox *, int, uint8_t *, uint16_t);
void(*onFriendRequest)(ToxWindow *, Tox *, uint8_t *, uint8_t *, uint16_t);
void(*onFriendAdded)(ToxWindow *, Tox *, int32_t, bool);
void(*onConnectionChange)(ToxWindow *, Tox *, int32_t, uint8_t);
void(*onMessage)(ToxWindow *, Tox *, int32_t, uint8_t *, uint16_t);
void(*onNickChange)(ToxWindow *, Tox *, int32_t, uint8_t *, uint16_t);
void(*onStatusChange)(ToxWindow *, Tox *, int32_t, uint8_t);
void(*onStatusMessageChange)(ToxWindow *, int32_t, uint8_t *, uint16_t);
void(*onAction)(ToxWindow *, Tox *, int32_t, uint8_t *, uint16_t);
void(*onGroupMessage)(ToxWindow *, Tox *, int, int, uint8_t *, uint16_t);
void(*onGroupAction)(ToxWindow *, Tox *, int, int, uint8_t *, uint16_t);
void(*onGroupInvite)(ToxWindow *, Tox *, int, uint8_t *);
void(*onGroupNamelistChange)(ToxWindow *, Tox*, int, int, uint8_t);
void(*onFileSendRequest)(ToxWindow *, Tox *, int, uint8_t, uint64_t, uint8_t *, uint16_t);
void(*onFileControl)(ToxWindow *, Tox *, int, uint8_t, uint8_t, uint8_t, uint8_t *, uint16_t);
void(*onFileData)(ToxWindow *, Tox *, int, uint8_t, uint8_t *, uint16_t);
void(*onTypingChange)(ToxWindow *, Tox *, int, int);
void(*onGroupInvite)(ToxWindow *, Tox *, int32_t, uint8_t *);
void(*onGroupNamelistChange)(ToxWindow *, Tox *, int, int, uint8_t);
void(*onFileSendRequest)(ToxWindow *, Tox *, int32_t, uint8_t, uint64_t, uint8_t *, uint16_t);
void(*onFileControl)(ToxWindow *, Tox *, int32_t, uint8_t, uint8_t, uint8_t, uint8_t *, uint16_t);
void(*onFileData)(ToxWindow *, Tox *, int32_t, uint8_t, uint8_t *, uint16_t);
void(*onTypingChange)(ToxWindow *, Tox *, int32_t, uint8_t);
#ifdef _SUPPORT_AUDIO
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];
int num;
int32_t num; /* corresponds to friendnumber in chat windows */
bool active;
int x;
/* window type identifiers */
bool is_chat;
bool is_groupchat;
bool is_prompt;
bool alert0;
bool alert1;
bool alert2;
ChatContext *chatwin;
PromptBuf *promptbuf;
StatusBar *stb;
WINDOW *popup;
WINDOW *window;
};
/* statusbar info holder */
struct StatusBar {
WINDOW *topline;
WINDOW *topline;
uint8_t statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH];
uint16_t statusmsg_len;
uint8_t nick[TOX_MAX_NAME_LENGTH];
uint16_t nick_len;
TOX_USERSTATUS status;
uint8_t status;
bool is_online;
};
@ -121,79 +176,85 @@ struct ChatContext {
size_t pos;
size_t len;
wchar_t ln_history[MAX_LINE_HIST][MAX_STR_SIZE];
wchar_t ln_history[MAX_LINE_HIST][MAX_STR_SIZE]; /* history for input lines/commands */
int hst_pos;
int hst_tot;
bool self_is_typing;
struct history *hst;
struct chatlog *log;
uint8_t self_is_typing;
WINDOW *history;
WINDOW *linewin;
WINDOW *sidebar;
};
/* prompt window/buffer holder */
struct PromptBuf {
wchar_t line[MAX_STR_SIZE];
size_t pos;
size_t len;
/* specific for prompt */
bool at_bottom; /* true if line end is at bottom of window */
int orig_y; /* y axis point of line origin */
bool scroll; /* used for prompt window hack to determine when to scroll down */
wchar_t ln_history[MAX_LINE_HIST][MAX_STR_SIZE];
int hst_pos;
int hst_tot;
WINDOW *linewin;
};
/* 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 FILE_PIECE_SIZE 1024
#define TIMEOUT_FILESENDER 300
typedef struct {
FILE *file;
ToxWindow *toxwin;
int friendnum;
int32_t friendnum;
bool active;
uint8_t filenum;
int filenum;
uint8_t nextpiece[FILE_PIECE_SIZE];
uint16_t piecelen;
uint8_t pathname[MAX_STR_SIZE];
uint64_t timestamp;
uint64_t size;
uint32_t line_id;
} FileSender;
struct FileReceiver {
uint8_t filenames[MAX_FILES][MAX_STR_SIZE];
FILE *files[MAX_FILES];
bool pending[MAX_FILES];
uint64_t size[MAX_FILES];
uint32_t line_id;
};
/* End file transfer code */
void on_request(uint8_t *public_key, uint8_t *data, uint16_t length, void *userdata);
void on_connectionchange(Tox *m, int friendnumber, uint8_t status, void *userdata);
void on_message(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void *userdata);
void on_action(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void *userdata);
void on_nickchange(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void *userdata);
void on_statuschange(Tox *m, int friendnumber, TOX_USERSTATUS status, void *userdata);
void on_statusmessagechange(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void *userdata);
void on_friendadded(Tox *m, int friendnumber, bool sort);
struct _Winthread {
pthread_t tid;
pthread_mutex_t lock;
};
void on_request(Tox *m, uint8_t *public_key, uint8_t *data, uint16_t length, void *userdata);
void on_connectionchange(Tox *m, int32_t friendnumber, uint8_t status, void *userdata);
void on_message(Tox *m, int32_t friendnumber, uint8_t *string, uint16_t length, void *userdata);
void on_action(Tox *m, int32_t friendnumber, uint8_t *string, uint16_t length, void *userdata);
void on_nickchange(Tox *m, int32_t friendnumber, uint8_t *string, uint16_t length, void *userdata);
void on_statuschange(Tox *m, int32_t friendnumber, uint8_t status, void *userdata);
void on_statusmessagechange(Tox *m, int32_t friendnumber, uint8_t *string, uint16_t length, void *userdata);
void on_friendadded(Tox *m, int32_t friendnumber, bool sort);
void on_groupmessage(Tox *m, int groupnumber, int peernumber, uint8_t *message, uint16_t length, void *userdata);
void on_groupaction(Tox *m, int groupnumber, int peernumber, uint8_t *action, uint16_t length, void *userdata);
void on_groupinvite(Tox *m, int friendnumber, uint8_t *group_pub_key, void *userdata);
void on_groupinvite(Tox *m, int32_t friendnumber, uint8_t *group_pub_key, void *userdata);
void on_group_namelistchange(Tox *m, int groupnumber, int peernumber, uint8_t change, void *userdata);
void on_file_sendrequest(Tox *m, int friendnumber, uint8_t filenumber, uint64_t filesize, uint8_t *pathname, uint16_t pathname_length, void *userdata);
void on_file_control(Tox *m, int friendnumber, uint8_t receive_send, uint8_t filenumber, uint8_t control_type, uint8_t *data, uint16_t length, void *userdata);
void on_file_data(Tox *m, int friendnumber, uint8_t filenumber, uint8_t *data, uint16_t length, void *userdata);
void on_typing_change(Tox *m, int friendnumber, int is_typing, void *userdata);
void on_file_sendrequest(Tox *m, int32_t friendnumber, uint8_t filenumber, uint64_t filesize, uint8_t *pathname,
uint16_t pathname_length, void *userdata);
void on_file_control(Tox *m, int32_t friendnumber, uint8_t receive_send, uint8_t filenumber, uint8_t control_type,
uint8_t *data, uint16_t length, void *userdata);
void on_file_data(Tox *m, int32_t friendnumber, uint8_t filenumber, uint8_t *data, uint16_t length, void *userdata);
void on_typing_change(Tox *m, int32_t friendnumber, uint8_t is_typing, void *userdata);
ToxWindow *init_windows(Tox *m);
void draw_active_window(Tox *m);
int add_window(Tox *m, ToxWindow w);
void del_window(ToxWindow *w);
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

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
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include "friendlist.h"
#include "prompt.h"
#include "toxic_windows.h"
#include "groupchat.h"
extern char *DATA_FILE;
extern struct _Winthread Winthread;
static ToxWindow windows[MAX_WINDOWS_NUM];
static ToxWindow *active_window;
extern ToxWindow *prompt;
static int num_active_windows;
/* CALLBACKS START */
void on_request(uint8_t *public_key, uint8_t *data, uint16_t length, void *userdata)
void on_request(Tox *m, uint8_t *public_key, uint8_t *data, uint16_t length, void *userdata)
{
int i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onFriendRequest != NULL)
windows[i].onFriendRequest(&windows[i], public_key, data, length);
windows[i].onFriendRequest(&windows[i], m, public_key, data, length);
}
}
void on_connectionchange(Tox *m, int friendnumber, uint8_t status, void *userdata)
void on_connectionchange(Tox *m, int32_t friendnumber, uint8_t status, void *userdata)
{
int i;
@ -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;
@ -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;
@ -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;
@ -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)
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");
}
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;
@ -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;
@ -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;
@ -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;
@ -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)
{
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)
{
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)
{
int i;
@ -210,12 +236,17 @@ int add_window(Tox *m, ToxWindow w)
if (w.window == NULL)
return -1;
#ifdef URXVT_FIX
/* Fixes text color problem on some terminals. */
wbkgd(w.window, COLOR_PAIR(6));
#endif
windows[i] = w;
w.onInit(&w, m);
if (w.onInit)
w.onInit(&w, m);
++num_active_windows;
return i;
}
@ -226,13 +257,14 @@ int add_window(Tox *m, ToxWindow w)
/* Deletes window w and cleans up */
void del_window(ToxWindow *w)
{
active_window = windows; // Go to prompt screen
active_window = windows; /* Go to prompt screen */
delwin(w->window);
memset(w, 0, sizeof(ToxWindow));
clear();
refresh();
--num_active_windows;
}
/* Shows next window when tab or back-tab is pressed */
@ -251,7 +283,7 @@ void set_next_window(int ch)
if (active_window->window)
return;
if (active_window == inf) { // infinite loop check
if (active_window == inf) { /* infinite loop check */
endwin();
fprintf(stderr, "set_next_window() failed. Aborting...\n");
exit(EXIT_FAILURE);
@ -363,27 +395,65 @@ void draw_active_window(Tox *m)
wrefresh(a->window);
/* Handle input */
bool ltr;
#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
ch = getch();
if (ch == ERR)
return;
/* TODO verify if this works */
ltr = isprint(ch);
#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);
else if (ch != ERR)
a->onKey(a, m, ch);
} else {
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;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].active)
++count;
}
ToxWindow *a = &windows[i];
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]);
}
}