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

Compare commits

...

476 Commits

Author SHA1 Message Date
69be1bc398 Fix nodes parsing bug 2015-08-28 02:44:38 -04:00
b4464eda4d Fix more threading issues 2015-08-27 21:29:34 -04:00
28dd43608d Fix a few notify/threading bugs and fix DHTnodes loading bug 2015-08-27 15:13:13 -04:00
11701d22a1 Lots of bug fixes and general code cleanup 2015-08-27 03:38:08 -04:00
92d76c7f99 Display a user warning when log fails to initialize 2015-08-19 00:42:28 -04:00
2a787c1097 Fix some filetransfer issues
- File transfers now timeout properly
- Small refactor related to creating new transfers
2015-08-18 15:12:48 -04:00
327259c4c8 simplify timeout function 2015-08-18 01:46:22 -04:00
f173f4275e Merge pull request #350 from vinegret/patch-1
fix a broken link
2015-08-15 12:10:56 -04:00
48eaf8a14f fix a broken link 2015-08-15 15:42:32 +00:00
083611f18e Update DHTnodes 2015-08-13 15:03:02 -04:00
48ffae68a9 <not having red text 2015-08-11 19:22:57 -04:00
c39f8909cd Send file control cancel on failure to resume transfer 2015-08-10 20:41:11 -04:00
32e541bd1c Fix file transfers breaking when friends go offline 2015-08-10 14:22:13 -04:00
f559bdabfe Remove links to libtoxcore.so from readme 2015-08-08 16:51:04 -04:00
0047ba0e9f fix rare bug preventing toxcore from sleeping 2015-08-08 12:55:46 -04:00
ecefc19b23 Merge branch 'ony-master' 2015-07-14 02:15:38 -04:00
e83b397494 Makefile: allow overriding pkg-config 2015-07-14 08:27:07 +03:00
688ea927f8 Update dht nodes list and fix URL's 2015-07-08 22:40:17 -04:00
904f58c0e8 Add option to enable acting as TCP relay server 2015-07-08 21:01:32 -04:00
035420e5c7 Revert commit 312d0c3 (localization/gettext)
There were serious problems with branch merging that need to be resolved first
2015-07-04 01:19:16 -04:00
444d8e7a74 update translations 2015-06-30 23:51:27 -04:00
84a0276668 Fix some possible race conditions related to line printing 2015-06-30 22:40:45 -04:00
312d0c3f42 Merge branch 'Ansa89-add_translation' 2015-06-29 03:17:52 -04:00
d8eca8393c s/tox.im/tox.chat/g -- reflect new tox domain 2015-06-29 03:16:09 -04:00
374b78c763 manually fix merge conflicts 2015-06-29 03:11:21 -04:00
409e4ddd96 Merge pull request #340 from Ansa89/update_travis_deps
travis.yml: update dependencies
2015-06-12 18:23:22 +02:00
1beb35025b travis.yml: update dependencies 2015-06-12 18:22:08 +02:00
51a1c660b4 Add localization system (gettext) 2015-05-30 11:55:59 +02:00
85d3c18ba6 Add localization system (gettext) 2015-05-28 15:50:55 +02:00
d0a7ca17d2 separate bootstrapping and adding TCP relays per toxcore API changes 2015-05-24 17:56:30 -04:00
36640224af toxcore API changes to tox_new 2015-05-24 17:36:13 -04:00
231078b6b9 default tox options on initialization 2015-05-18 19:52:22 -04:00
414f58d896 correctly handle tox_new errors 2015-04-14 17:18:09 -04:00
4d73f8b241 not clear which error message is given for ipv6 failure 2015-04-13 00:58:33 -04:00
82e76a3b5b try to fall back to ipv4 if tox fails to init with ipv6 2015-04-13 00:32:35 -04:00
0bc610e18d null terminate status message 2015-04-10 00:16:01 -04:00
02e6d2db3c fix possible segfault 2015-04-09 02:14:23 -04:00
5a2c341259 Makefile: try to fix Tox/toxic#307
@henriqueleng please test it and report back if it works for you.
2015-04-06 11:44:02 +02:00
1a7eaeddba fix a few avatar bugs 2015-04-04 21:15:34 -04:00
f656d0a722 Update .travis.yml 2015-04-04 03:42:43 -04:00
09c1ad4566 Merge branch 'master' of https://github.com/Tox/toxic 2015-04-04 03:27:27 -04:00
8b9e34db75 Merge branch 'master' of https://github.com/JFreegman/toxic 2015-04-04 03:27:01 -04:00
dd9186e834 re-implement avatar setting 2015-04-04 03:26:38 -04:00
5ff1517b28 Merge pull request #322 from Ansa89/trivial-fix
Makefile: add uninstall target
2015-04-03 15:43:18 +02:00
bbb639c5aa Makefile: add uninstall target 2015-04-03 13:04:28 +02:00
860db2f612 Update .travis.yml 2015-04-02 22:44:19 -04:00
523f205646 re-implement friendlist last seen 2015-04-02 22:19:09 -04:00
e998c8a866 fix encryption API breakage 2015-03-31 21:56:11 -04:00
eaea68c33e fix bug where contacts would appear online twice in a row 2015-03-31 18:39:42 -04:00
4780cfeafc remove non-existant group commands 2015-03-31 17:11:43 -04:00
bdb0951c84 a few small filetransfer fixes 2015-03-29 19:29:38 -04:00
e3130c92c0 small fix 2015-03-29 19:26:16 -04:00
12c880ab51 fix bootstrapping 2015-03-29 19:05:24 -04:00
522aabd4e4 Refactor and clean up file transfers
(No longer rely on undefined core filenumber property for indexing)
2015-03-29 18:33:51 -04:00
bf09b3b6c4 Merge branch 'master' of https://github.com/Tox/toxic 2015-03-28 02:58:24 -04:00
2d3c5c9450 Mostly finished with new API port
- File transfers currently don't support pausing/resuming
- Avatars are not yet done
2015-03-28 02:56:54 -04:00
ae87b2eb2d begin port to new API 2015-03-25 22:56:45 -04:00
c34ecc8dfd Merge pull request #318 from Ansa89/trivial-fix
README.md: fix typo
2015-03-23 15:44:19 +01:00
2c2938c647 README.md: fix typo 2015-03-23 15:36:58 +01:00
22dd883f28 Merge pull request #316 from Ansa89/trivial-fix
Makefile: be less aggressive when cleaning
2015-03-21 14:32:23 -04:00
dfbb1338a5 Makefile: be less aggressive when cleaning 2015-03-21 19:31:00 +01:00
f8dc82516e Merge pull request #315 from Ansa89/trivial-fix
Move makefile into root directory
2015-03-21 19:25:05 +01:00
59b16f7760 Fix travis build 2015-03-21 18:38:28 +01:00
a11289de79 Move makefile into root directory
Requested by @mannol.
2015-03-21 18:35:40 +01:00
a64b8cae89 Merge branch 'hardlyeven-master' 2015-03-18 15:51:18 -04:00
470eaeb97b Merge branch 'master' of https://github.com/hardlyeven/toxic into hardlyeven-master 2015-03-18 15:45:37 -04:00
6503349ed3 Merge pull request #314 from al42and/fd_leak
Fixing couple leaking file descriptors
2015-03-18 15:38:40 -04:00
ec23cedc3a Fixing couple leaking file descriptors 2015-03-18 19:28:04 +03:00
jlo
ce1ae1d7dc added tab autocomplete for "/status o" => "/status online" 2015-03-17 14:25:15 -05:00
6669d5632f Merge pull request #310 from Ansa89/trivial-fix
Some cosmetics changes
2015-03-16 13:26:17 +01:00
59e1114997 Some cosmetics changes 2015-03-16 13:16:03 +01:00
c20510e5aa Merge pull request #308 from henriqueleng/openbsd
Openbsd
2015-03-15 15:52:37 -04:00
2b2e746549 added gnumake as dep., because you can't compile with *BSDmake 2015-03-15 16:34:56 -03:00
dc5a8d44e5 Add OpenBSD option to Makefile 2015-03-15 16:27:00 -03:00
090d3339a0 fix auto-away bugs
Manually set aways no longer unaway when they shouldn't, and busy status is no longer overridden
2015-03-13 20:33:23 -04:00
442d9e22b4 use pthread for mplex detach polling for better portability 2015-03-11 18:38:07 -04:00
0c39e7b158 Add support for auto-away based on screen attach/detach 2015-03-09 01:38:30 +02:00
3e3f2614b5 Merge pull request #303 from louipc/interface
Add support for custom timestamps in chat and logs.
2015-02-23 22:38:50 -05:00
53e4b2c971 Add support for custom timestamps in chat and logs.
Internal representations of the time_format setting are replaced by timestamp format strings.
time_format is only used in the setup.

Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2015-02-23 00:46:33 -05:00
d6c57c7aa3 Merge pull request #302 from Ansa89/trivial-fix
README.md: update download section
2015-02-19 12:49:50 +01:00
3efc984ae9 README.md: update download section 2015-02-18 15:56:19 +01:00
7618a0871a Merge pull request #301 from Ansa89/trivial-fix
Add INSTALL.md
2015-02-17 14:37:17 -05:00
b87f73cc47 Add INSTALL.md 2015-02-17 11:12:58 +01:00
785d39d78f Merge pull request #296 from louipc/interface
Interface
2015-02-13 14:23:44 -05:00
38a0f6fae4 Make little action indicators on each output line configurable.
Also added a new DISCONNECTION line info type.

Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2015-02-13 02:09:40 -05:00
710be51cad Merge pull request #298 from Ansa89/trivial-fix
travis.yml: use latest libsodium stable
2015-02-11 19:19:47 -05:00
50438425e5 Update README.md 2015-02-11 19:06:40 -05:00
4b4bfe8876 travis.yml: use latest libsodium stable 2015-02-11 10:12:05 +01:00
67da4bdd5b Merge pull request #297 from urras/patch-1
Travis should build with Libsodium stable, fix clang
2015-02-10 14:27:13 -05:00
e230badb47 Fix Clang
I didn't expect this to work, but it is and I'm not complaining.
2015-02-10 10:01:44 -05:00
d48d9ed4dc Travis should build with Libsodium stable 2015-02-10 09:47:06 -05:00
b9fe6bfa10 Merge pull request #280 from DomT4/darwin
Darwin Build
2015-02-09 21:05:10 -08:00
5cbbb62ce2 Fix whitespace
Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2015-02-09 23:33:49 -05:00
3369c9d808 Merge pull request #293 from Spagy/patch-1
Correct filename comment from main.c to toxic.c
2015-02-04 15:47:58 -05:00
dfc594f949 Correct filename comment from main.c to toxic.c 2015-02-04 13:56:31 +00:00
d714cda145 Vital commit fixing serious Toxic bug 2015-02-01 17:10:44 -08:00
428e1e4100 Merge pull request #292 from Ansa89/trivial-fix
Update for toxcore API break
2015-02-01 15:16:29 -05:00
2b19f56e63 Update for toxcore API break 2015-02-01 21:09:48 +01:00
4badc983ea Merge pull request #291 from dantok/master
Fix some edge cases when obtaining paths
2015-01-25 15:54:27 -05:00
c8a4f88ff3 Update configdir.c 2015-01-25 21:53:13 +01:00
eeeaf8d707 Update autocomplete.c 2015-01-25 21:52:07 +01:00
37b3b5a5ed Fix an edge case when obtaining home directory.
Make the path to home directory empty before calling get_home_dir(), otherwise if the call fails, the contents are undefined.
2015-01-25 20:49:52 +01:00
a561ef8c49 Fixed emptying avatar_path if the path is too long. 2015-01-25 20:17:32 +01:00
89f200e870 Merge pull request #290 from urras/master
Update DHT nodes again
2015-01-11 15:11:04 -05:00
2823115a6c Update DHT nodes again 2015-01-11 15:06:15 -05:00
5599d73760 remove invalid node from nodes list 2015-01-11 14:28:52 -05:00
bac66cb5ad Merge pull request #289 from urras/master
Update DHT node list
2015-01-11 14:16:50 -05:00
61c50972e3 Update DHT node list 2015-01-11 14:06:33 -05:00
ff0fbbc379 small fix 2015-01-01 12:50:51 -05:00
4a124dea43 Merge branch 'flussence-master' 2015-01-01 01:56:31 -05:00
e722b665d1 Make "Last seen" handle year rollover correctly 2015-01-01 05:44:18 +00:00
4365b8d5ad update man page 2014-12-27 13:17:05 -05:00
e55e5f3f7e add HTTP proxy support 2014-12-27 13:12:16 -05:00
d1036c8538 Darwin Build
Explains/Better Enables building on the OS X Platform. I’ve written a
Darwin.mk file for the purpose, given one is referenced in the Makefile
but doesn’t actually exist yet. I presume this is due to Toxic’s recent
move away from configure scripts?

Anyhow. It’s not perfect, and it could be prettier, but it should fix
2014-12-18 03:49:52 +00:00
9240f62829 Merge branch 'jdp5087-tour' 2014-12-17 17:35:01 -05:00
1b89bc9051 Made the keys section of settings_load more readable in settings.c 2014-12-17 12:15:18 -05:00
7700ef83e0 disable broken callback 2014-12-09 20:30:13 -05:00
9922b3a4f3 forgot IFDEFs 2014-12-09 18:55:14 -05:00
d561d2e1ac fix 2014-12-09 18:35:14 -05:00
7865cea284 group audio stuff 2014-12-09 18:29:07 -05:00
424189cad9 small fix 2014-12-07 21:48:29 -05:00
82aa64e8f7 properly close all groupchats on exit 2014-12-07 13:51:12 -05:00
a0418520a2 Merge branch 'master' of https://github.com/Tox/toxic 2014-12-07 12:19:14 -05:00
f451d961bc close output device on group exit 2014-12-07 12:16:49 -05:00
a4da9fd49e "Refactor" this super important file 2014-12-07 01:50:39 -08:00
0a0891fa98 fix building without audio 2014-12-06 13:40:45 -05:00
9464b369a4 fix possible crash 2014-12-06 13:26:27 -05:00
47692edee4 update DHTnodes for real 2014-12-05 20:33:13 -05:00
73c94b25ba update DHTnodes, fix comment 2014-12-05 20:26:10 -05:00
ab2ea5936d correct value 2014-12-05 13:47:47 -05:00
90985af007 properly handle file pausing 2014-12-05 13:23:45 -05:00
5aad8764b1 ensure messages in queue are always delivered in order 2014-12-03 22:10:21 -05:00
5a175f374a fix file statusbar draw bug 2014-12-03 21:38:37 -05:00
4acfe84171 remove spammy connection messages 2014-11-30 12:20:01 -05:00
e995a1cb69 AV api changes 2014-11-30 09:27:45 -05:00
867f041c23 more group stuff 2014-11-29 09:39:55 -05:00
7e49ba92b2 Merge pull request #283 from stal888/master
Destroy AL context before closing dhndl
2014-11-29 08:18:56 -05:00
2fa53c0531 Destroy AL context before closing dhndl
fixes seggy on os x, i think
2014-11-28 22:08:31 -08:00
673800c947 forgot something 2014-11-28 23:51:32 -05:00
835b821b75 put toxav loop in separate thread 2014-11-28 23:47:22 -05:00
78587ad20b disable group audio callback until it works properly 2014-11-26 17:39:02 -05:00
09bbec79cf group audio almost works 2014-11-26 15:22:34 -05:00
751b5f9943 only include audio_call.h if audio is defined 2014-11-26 12:24:52 -05:00
4a337ae3cb update for core audio refactor 2014-11-26 00:57:02 -05:00
1baeb15073 update DHTnodes 2014-11-20 02:57:42 -05:00
141b36af04 handle errors for name functions 2014-11-15 22:39:24 -05:00
4238c20e72 api update 2014-11-15 15:59:19 -05:00
4e177d60b0 group title fixes and api update 2014-11-15 15:55:45 -05:00
1bd880708b a couple formatting fixes 2014-11-14 22:35:23 -05:00
76c21c8b34 implement group titles 2014-11-14 22:15:59 -05:00
dd8c2caac7 more prep for audio groups 2014-11-14 20:13:08 -05:00
7a7e8a7f8d prep for audio groups 2014-11-11 20:49:05 -05:00
43f45d67a4 core API update 2014-11-11 18:30:23 -05:00
adb36a39bb minor visual adjustment 2014-11-05 18:46:18 -05:00
5a0d9f5e9f Merge pull request #278 from Ansa89/trivial-fix
Fix Tox/toxic#276
2014-10-30 15:41:17 +01:00
a4e8cb3971 Fix Tox/toxic#276 2014-10-30 15:23:47 +01:00
e22b7a336c Merge pull request #274 from Ansa89/trivial-fix
Makefile: revert back to mkdir
2014-10-21 06:20:51 -07:00
317fa42b20 Makefile: revert back to mkdir 2014-10-21 11:51:02 +02:00
884f8dda37 Merge pull request #273 from Ansa89/trivial-fix
Makefile: add toxic.desktop to install target
2014-10-21 01:38:29 -07:00
f5090532fb Makefile: add toxic.desktop to install target 2014-10-21 10:31:30 +02:00
ae04dd8fa5 Create toxic.desktop 2014-10-20 13:00:14 -07:00
00e4075937 Merge pull request #271 from Ansa89/trivial-fix
Toxic.conf.exmaple: fix sound namefile
2014-10-20 15:35:52 -04:00
d65f3ea58d Fix sound namefile 2014-10-20 10:55:06 +02:00
008163bf6c fix 2014-10-18 17:29:23 -04:00
f09002a49a Merge branch 'Ansa89-trivial-fix' 2014-10-16 14:27:58 -04:00
431290d47a Version: fix revision calculation 2014-10-16 20:26:00 +02:00
b66874b7b3 Merge pull request #262 from Ansa89/trivial-fix
Version: add revision only if git is available
2014-10-15 14:26:54 +02:00
1b9fd7f936 Install: check if manpages exist 2014-10-15 14:25:14 +02:00
0a1457acde Version: add revision only if git is available 2014-10-15 10:39:49 +02:00
d2008aa92f Merge branch 'louipc-new' 2014-10-13 00:16:05 -04:00
e83356faef Merge branch 'new' of https://github.com/louipc/toxic into louipc-new 2014-10-13 00:14:29 -04:00
fd4161832b travis.yml: Remove asciidoc
Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2014-10-12 23:35:48 -04:00
0a132b0eb5 doc: Unlink docs from the rest of the build process.
Since generated man pages are also included, we can't
guarantee proper timestamps. This should fix issues that trigger doc
building when it isn't needed.

Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2014-10-12 23:21:48 -04:00
9c7ac9043a prompt: Add version line to startup message.
Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2014-10-12 23:12:56 -04:00
b86c0d357f Update toxencryptsave api change.
Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2014-10-12 23:04:30 -04:00
75dd26518f Finished drag and drop support 2014-10-12 23:26:28 +02:00
2cc261c619 Some random fixups 2014-10-12 22:54:18 +02:00
f7d46f1100 possibly fix sound notification bug 2014-10-12 01:49:56 -04:00
be264528d2 possibly fix sound notification bug 2014-10-12 00:28:28 -04:00
c825adc1e3 Dnd works 2014-10-11 23:53:02 +02:00
4e9f125e95 core API function name change 2014-10-10 22:44:05 -04:00
d26c8fe447 Merge pull request #264 from zetok/indicator
Change action messages indicator
2014-10-09 16:46:51 -04:00
e42d635195 non-critical thread failures shouldn't be fatal 2014-10-09 16:39:01 -04:00
a32cd1c19b Change action messages indicator 2014-10-09 21:36:42 +01:00
14dc02ac83 fix segfault 2014-10-09 15:47:48 -04:00
e4a28d1839 make groupchat announcements behave properly 2014-10-09 01:39:22 -04:00
0908920b51 fix segfault 2014-10-08 18:06:15 -04:00
8a882916fb fix setting custom keybinding 2014-10-08 15:22:49 -04:00
55ada8ad2f fix groupchat resizing when peerlist is off 2014-10-08 14:53:09 -04:00
2094186c0f update DHTnodes 2014-10-08 14:44:15 -04:00
d3a489b756 update settings and docs with peerlist-toggle keybinding 2014-10-08 03:08:08 -04:00
e6b18231c0 add ability to toggle groupchat peerlist with ctrl-b 2014-10-08 02:45:08 -04:00
cc0145d561 reduce system calls, increase time between message queue re-send attempts 2014-10-08 01:50:34 -04:00
9f0daca5eb filter escape sequences from all inbound nicks and status messages 2014-10-07 16:18:06 -04:00
1f2bd44dce don't allow newlines and tabs in nicks 2014-10-07 00:43:03 -04:00
6492bd12f9 escape newlines in contact names/status messages 2014-10-06 16:23:43 -04:00
43552161f9 implement word deletion via ^W 2014-10-05 23:12:58 -04:00
e5d45fdf1d correct return code check 2014-10-05 02:29:40 -04:00
bd69c8da18 small fix 2014-10-04 17:01:02 -04:00
8c3e3ebe24 fix blocklist saving bug 2014-10-03 23:52:54 -04:00
7eebbd982d fix 2014-10-03 21:12:59 -04:00
5c66f5c161 fixes 2014-10-03 19:29:12 -04:00
40f70fc1e3 groupchat fixes 2014-10-03 18:57:32 -04:00
9cf4427375 core groupchat names should be working 2014-10-03 17:55:36 -04:00
23cf9686cb safer string handling 2014-10-03 17:53:50 -04:00
70add920fe Making progress with dnd 2014-10-03 16:59:32 +02:00
c56c6cc219 some temporary fixes for new wip groupchats 2014-10-02 23:22:37 -04:00
e9a0296851 ignore tox_load errors until toxcore is fixed 2014-10-02 22:25:21 -04:00
de30433e26 better data operation error handling 2014-10-02 02:07:54 -04:00
fa64fa8b93 replace call sounds
Signed-off-by: Jfreegman <jfreegman@gmail.com>
2014-10-01 19:38:09 -04:00
50c7942cb5 Started working on dnd for toxic 2014-10-01 23:28:34 +02:00
e7697b8fba make ctrl-L force refresh the window 2014-10-01 16:24:36 -04:00
b46a77f977 forced end of page is not working out 2014-10-01 15:01:23 -04:00
38909afa89 refine a couple sound effects 2014-10-01 13:46:19 -04:00
5af561c811 new default sounds; rm unused sounds 2014-10-01 01:16:17 -04:00
ae984d4f04 don't show version in default status message 2014-09-30 17:02:03 -04:00
97a8ecd115 small fix and bump version to 0.5.2 2014-09-29 13:56:17 -04:00
64d782569a add asciidoc and libnotify-dev to install script 2014-09-29 01:54:53 -04:00
6248baf98b fix segfault 2014-09-29 01:30:22 -04:00
33cfca7ecc bug fixes and code cleanup 2014-09-28 17:49:48 -04:00
a01cc35368 Merge branch 'master' of github.com:Tox/toxic 2014-09-28 16:48:09 +02:00
b10eebd77e This should fix audio problem 2014-09-28 16:47:31 +02:00
05661ca9b6 core API update and refactor group invites a bit 2014-09-27 22:50:20 -04:00
784883f773 What if, like, photosynthesis is a lie?! 2014-09-28 00:13:45 +02:00
a9e5723ca4 a couple fixes 2014-09-27 03:08:19 -04:00
95dfea8d29 keep chatlog names consistent with friend name changes 2014-09-27 02:28:11 -04:00
e574af7d68 fix CPU usage issue regarding audio drivers, courtesy of mannol 2014-09-26 20:32:48 -04:00
2918ca45a2 Merge pull request #255 from Ansa89/trivial-fix
Fix "error: unknown type name 'off_t'"
2014-09-26 13:01:44 -04:00
6ad744f770 Add stdlib.h header to allow off_t type 2014-09-26 10:50:42 +02:00
5b394e6f35 notify.c: better headers order 2014-09-26 10:46:14 +02:00
940af2c711 a few fixes, update example config file 2014-09-26 03:39:36 -04:00
544c402f78 implement avatars (setting only) and generalize path tab-complete 2014-09-26 03:10:44 -04:00
cee9e624b8 more UI changes 2014-09-25 15:23:50 -04:00
2e65ee3609 a few UI changes 2014-09-25 04:31:45 -04:00
3a176e1cab move to page end after pressing return 2014-09-25 00:42:08 -04:00
00cccad22c a few general fixes 2014-09-24 15:20:15 -04:00
a432d733d7 use 64-bit off_t for file sizes 2014-09-24 14:23:08 -04:00
893e88294b more succinct way to get file sizes 2014-09-24 00:06:02 -04:00
b071a9e992 more thorough error checking 2014-09-23 22:51:56 -04:00
48cf4ebf02 fix for issue #254 2014-09-23 21:32:05 -04:00
773a75b948 update makefile checks 2014-09-22 21:51:55 -04:00
133c0e8d63 rename reserved identifiers per C standard 2014-09-22 21:24:45 -04:00
bf54cb36ef set signal handlers at more appropriate places 2014-09-22 20:00:36 -04:00
032853b5c9 Merge branch 'Pyrohh-master' 2014-09-22 19:41:23 -04:00
650c13ca7a There's no need for rm -rf, just rm -f 2014-09-22 18:45:45 -04:00
9d5fe03285 small fix 2014-09-22 18:38:46 -04:00
c3f68b60d3 bigger log identifiers to reduce chance of collision 2014-09-22 17:34:30 -04:00
1c16467eb9 don't share chatlogs across data files (changes log naming convention) 2014-09-22 17:09:39 -04:00
3c74385f5c use proper variable type for sig handler flags 2014-09-22 14:27:33 -04:00
c07c0028bb increase line_info msg buffer size so won't cut off long log lines 2014-09-22 13:49:09 -04:00
dc3b2e04ab load chatlog history when opening a chatwindow 2014-09-22 04:29:28 -04:00
7fd3aa9164 Merge pull request #246 from urras/patch-2
Update screenshot
2014-09-21 13:17:13 -04:00
4c60312e2d Update screenshot 2014-09-21 12:42:04 -04:00
16e29aa4e0 Merge pull request #243 from Ansa89/trivial-fix
Makefile: use single quotes also for PACKAGE_DATADIR
2014-09-19 12:28:32 -04:00
bd7b073155 Makefile: fix typo 2014-09-19 10:47:25 +02:00
f25cf870e6 Makefile: use single quotes also for PACKAGE_DATADIR 2014-09-19 10:45:27 +02:00
13291d0365 bump version to 0.5.1 2014-09-19 01:24:39 -04:00
9ee7a48910 add setting to disable welcome message 2014-09-19 01:11:23 -04:00
daf4614ba6 improve behaviour of misused encryption options 2014-09-19 00:29:30 -04:00
919d36369c add option to unencrypt an encrypted data file 2014-09-18 23:50:51 -04:00
618704df76 remove duplicate DHT node 2014-09-18 22:52:05 -04:00
9a70dd9651 possibly fix display bug on some platforms 2014-09-18 13:14:15 -04:00
e2d310b10f Add TCP nodes on 443 2014-09-17 14:00:41 -07:00
a1015a366a update DHTnodes, small fixes 2014-09-17 13:52:27 -04:00
913ec7b3fe Merge pull request #242 from Ansa89/trivial-fix
Usage help: add missing comma
2014-09-16 13:55:20 +02:00
4a52b06954 Usage help: add missing comma 2014-09-16 11:54:58 +02:00
89f9c07b9e add confirmation when creating new password 2014-09-15 19:00:36 -04:00
388d78d11e add option to encrypt an existing unencrypted data file 2014-09-15 16:17:58 -04:00
a2a23b3932 password prompt fixes 2014-09-15 04:13:12 -04:00
f405ae8b42 improvements to password entry screen 2014-09-14 21:49:38 -04:00
cf3f6750eb small fix 2014-09-14 18:25:13 -04:00
4de22d067a Update .travis.yml 2014-09-14 18:08:42 -04:00
51e274ea38 implement data file encryption 2014-09-14 17:46:28 -04:00
0a6ce62363 don't write unsent messages to log 2014-09-11 01:36:33 -04:00
13c5de5531 increase time between message send retries 2014-09-10 16:18:37 -04:00
21f8e7f398 couple small fixes 2014-09-09 15:16:27 -04:00
bcf4a5af90 message queue improvements 2014-09-09 14:06:28 -04:00
09f90d095b properly flag lines with unread mark 2014-09-07 22:28:54 -04:00
416ebc9ab8 another fix 2014-09-07 22:05:17 -04:00
3ca22aa714 fix 2014-09-07 21:43:16 -04:00
8dd25e1f0b code cleanup, add delay for unread message notification 2014-09-07 15:08:01 -04:00
5b9bd603ea implement read receipts 2014-09-07 02:43:53 -04:00
3c2c1f15ce message sending queue for fake offline messaging 2014-09-06 13:18:42 -04:00
6876df4a45 Merge pull request #240 from s3erios/master
Fix some 'clang --analyze' warnings
2014-09-05 13:16:43 -04:00
1ff97161fb temporarily revert a862874740. 2014-09-05 14:15:25 +03:00
667410e879 fix another potential memory leak 2014-09-05 13:17:10 +03:00
a862874740 mark exit_toxic_* functions as noreturn 2014-09-05 13:15:56 +03:00
79bde4e5bf fix potential memory leak and move cleanup to the end of the function 2014-09-05 13:02:26 +03:00
833b724e9f fix bug 2014-09-04 18:48:55 -04:00
96b68058bb code cleanup and fix bug that sometimes prevented tab notifications from working 2014-09-04 18:28:20 -04:00
e823233149 Merge pull request #238 from Ansa89/trivial-fix
Addition to Tox/toxic#235
2014-09-04 13:23:33 -04:00
3ac22fafe4 Add info about DISABLE_X11 usage 2014-09-04 19:20:51 +02:00
71f2ac170c Merge pull request #235 from s3erios/master_cfg
Add X11 option
2014-09-04 13:18:46 -04:00
0ef888eea3 Merge pull request #236 from s3erios/master
Some code simplification
2014-09-04 11:10:55 +02:00
a9b0028a15 move some code inside _X11 ifdef 2014-09-04 01:41:02 +03:00
b18e6cff5a move duplicate code into separate function 2014-09-04 01:36:37 +03:00
424a1c94d9 Add X11 option 2014-09-04 01:19:32 +03:00
009095af24 slight simplification for command list iteration 2014-09-02 02:23:44 -04:00
2ce42ab057 bump version to 0.5.0 2014-09-01 18:56:20 -04:00
934459dea8 show connection change message in chat windows 2014-08-31 14:17:33 -04:00
52bc874675 Merge pull request #234 from Ansa89/trivial-fix
Cosmetic fixes
2014-08-31 11:26:48 +02:00
511907fbc5 better way to check if files exist 2014-08-27 22:51:46 -04:00
155e194174 fix segfault on unknown long option 2014-08-27 14:30:06 -04:00
b1c7e21ca9 fix possible segfaults 2014-08-27 13:54:02 -04:00
7edcf6cb45 add setting to set path for chatlogs 2014-08-26 23:56:37 -04:00
9581940cfa Cosmetic fixes 2014-08-26 10:39:43 +02:00
f2aa57c4fa additional messages for startup options 2014-08-26 02:28:05 -04:00
8bf4405fd0 fix error code 2014-08-25 19:55:00 -04:00
21ef1788ca print init messages to prompt window intead of stderr 2014-08-25 19:26:41 -04:00
68f1dffba7 add command to decline friend requests 2014-08-25 14:08:01 -04:00
092df2c0e4 Merge pull request #231 from Ansa89/trivial-fix
Reworked manpage build system
2014-08-25 14:12:12 +02:00
691f94c75c README.md: second try 2014-08-25 12:59:34 +02:00
d6d4476e85 README.md: better look 2014-08-25 12:56:14 +02:00
924e8e0860 Reworked manpage build system 2014-08-25 12:54:44 +02:00
53193e933f Merge pull request #230 from louipc/manpage
Manpage
2014-08-25 00:49:31 -04:00
328587ad9c doc: Make asciidoc extra optional.
Completely separate man page generation from the main build to satisfy
travis-ci behaviour.

Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2014-08-24 17:00:25 -04:00
1a8fdb1b99 Make asciidoc 'optional'
Run `make doc` in the build directory after editing the asciidoc to
regenerate the manpages. Changes to asciidoc source and generated man
pages will need to be commited together.

Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2014-08-24 16:34:16 -04:00
690f0221b5 Add generated man pages.
Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2014-08-24 00:16:55 -04:00
e117bd3985 Add Asciidoc to README and travis
Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2014-08-21 05:00:15 -04:00
15cc87bffd Remove old man pages.
Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2014-08-21 04:40:10 -04:00
97d4c97c52 Generate man pages from asciidoc
Working directly with g/roff is a pain.
Also changed the formatting of toxic.conf.5 a bit.

Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2014-08-21 04:39:24 -04:00
c8b22d7e8a Merge pull request #229 from Ansa89/trivial-fix
toxic.conf.example: better formatting
2014-08-20 13:12:41 -04:00
f48ec4f49b array bounds fix 2014-08-20 13:10:21 -04:00
d4ce697bd9 toxic.conf.example: better formatting 2014-08-20 12:40:32 +02:00
bd20513493 a few fixes 2014-08-19 23:41:43 -04:00
fd3f4eb724 Merge branch 'master' of https://github.com/Tox/toxic 2014-08-19 20:55:38 -04:00
75e8486061 add command to list pending friend requests, a few related fixes 2014-08-19 20:52:17 -04:00
bd216709fc Merge pull request #226 from Ansa89/freebsd-man
Fix Tox/toxic#222 and reorganize cfg dir
2014-08-19 13:04:52 -04:00
a3a8f7608a Fix Tox/toxic#222 and reorganize cfg dir 2014-08-19 18:22:57 +02:00
affc88d0a8 error checking for file senders, fixes 2014-08-17 15:32:38 -04:00
eca4882ce2 transfer progress bar fix 2014-08-17 13:50:22 -04:00
58b0a04019 separate file transfer updates from callbacks 2014-08-17 00:11:49 -04:00
b870679f2c allow resuming of broken file transfers (needs testing) 2014-08-16 17:20:53 -04:00
ae83725cb6 Merge pull request #223 from louipc/master
Add debug flag and update man page.
2014-08-16 02:59:55 -04:00
595e42b587 Clarify function of -b switch.
Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2014-08-16 02:35:21 -04:00
f5401df2c7 doc/toxic: Add missing flags and sort alphabetically.
Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2014-08-15 19:26:06 -04:00
3e79a5ca8b Add -b flag for debugging
For now this just prints messages to stderr, so the user should manually
redirect stderr to avoid breaking the ui. This can be later expanded
upon to provide debugging messages in the command window.

Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2014-08-15 19:06:16 -04:00
327081945e update DHTnodes 2014-08-15 18:17:03 -04:00
68ec484a58 don't allow DNS lookups when using a proxy + forced TCP & fix arg parsing 2014-08-15 15:13:35 -04:00
1d6ccf56a8 name conflict with core function might cause linking error 2014-08-15 12:22:39 -04:00
67f637a1e1 redirect stderr before audio init to prevent error spam 2014-08-14 21:31:28 -04:00
5e175d5319 Merge pull request #220 from louipc/master
new tox_bootstrap_from_address() behaviour and a minor ui change
2014-08-14 20:47:17 -04:00
778db0fece core change: ports no longer in network byte order 2014-08-14 20:38:33 -04:00
72010dd2e1 allow connections through proxies 2014-08-14 20:31:52 -04:00
39556b36f3 port is no longer passed in network byte order to tox_bootstrap_from_address()
Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2014-08-14 20:10:14 -04:00
dc9ffa6e56 ipv6enabled parameter was removed in toxcore.
Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2014-08-14 19:51:52 -04:00
1e92bb3c2b help: lowercase keybinding hints for clarity.
Seeing the uppercase character I expected it to be the actual key.

Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2014-08-14 19:22:59 -04:00
edbdf2966a toxcore API update, add option to force TCP connection 2014-08-14 18:45:41 -04:00
3f6fd734d3 Merge pull request #218 from louipc/louipc
toxic.conf.5: Remove default config from man page
2014-08-14 12:05:33 -04:00
919a06d282 toxic.conf.5: Remove default config from man page
The reference to the config example in the FILES section should be sufficient.

Signed-off-by: Loui Chang <louipc.ist@gmail.com>
2014-08-14 01:13:27 -04:00
35cc815cdb add option to specify DNSservers path 2014-08-12 22:32:36 -04:00
a318bdb034 add option to not connect to the DHT network 2014-08-12 18:59:27 -04:00
d6aaa95b25 ignore invalid file data callbacks 2014-08-12 15:27:42 -04:00
5718ad52db a few fixes 2014-08-12 03:01:18 -04:00
0f4cffbacc a few dns fixes 2014-08-11 01:59:01 -04:00
d9a861331f Use DNS server list instead of hard coding domains/keys 2014-08-10 21:40:19 -04:00
2f12a8d429 prevent statusmessage from wrapping on prompt screen 2014-08-10 00:37:26 -04:00
e75cf4f3ad fix bug where tab alert colours weren't being properly prioritized 2014-08-08 13:39:05 -04:00
bb85f31bb2 convert bytes for file transfer message, general fixes 2014-08-07 19:31:36 -04:00
396d08f0d2 refactor friend/blocklist, dynamically allocate memory 2014-08-07 13:24:29 -04:00
6ab184e7ce file transfer improvements and bug fixes 2014-08-05 17:38:33 -04:00
fd65fbfd0c temporarily disabling clang in travis script due to weird errors unrelated to toxic 2014-08-05 00:27:55 -04:00
637ea0ed55 attempt to fix travis with clang 2014-08-05 00:16:01 -04:00
dc2d20f4c4 bump version to 0.4.7 2014-08-04 22:28:12 -04:00
d712ccc17e Merge pull request #215 from mannol1/master
Fix ringing sounds
2014-08-05 01:41:21 +02:00
147030e06f Fix ringing sounds 2014-08-05 01:41:06 +02:00
09fd5cb69f small fix 2014-08-04 18:04:54 -04:00
64db9f73a2 update file transfers for core changes 2014-08-04 16:47:45 -04:00
28633be2dd a few fixes and improve error messages 2014-08-04 14:35:34 -04:00
6fdafceda8 add ability to cancel file transfers 2014-08-04 02:03:23 -04:00
38ed0c86ad use hangup cmd to cancel outgoing call requests 2014-08-03 23:11:19 -04:00
ad23816096 fix filetransfer bug 2014-08-03 16:44:21 -04:00
bc4a730e76 re-adding until better solution found 2014-08-03 15:42:14 -04:00
79372cc80d don't show full path when sending files 2014-08-03 15:12:55 -04:00
c9e4246ac5 Merge branch 'master' of https://github.com/Tox/toxic 2014-08-03 11:54:49 -04:00
dcd6a238b6 Merge pull request #213 from doughdemon/master
Add missing includes
2014-08-03 11:48:03 -04:00
c49de7733c Add missing includes
Fixes compilation with musl libc
2014-08-03 09:44:48 +02:00
3fc7c90630 stderr redirect doesn't work as intended, add invalid command message when no sound 2014-08-03 01:31:33 -04:00
53663a7832 try to end curses session gracefully on SIGSEGV 2014-08-03 00:26:44 -04:00
c14f2a3fcd put chatlogs in their own directory 2014-08-02 21:03:59 -04:00
3cbe61e111 fix a few formatting bugs and simplify code a bit 2014-08-02 15:35:57 -04:00
fa023c6a99 Merge pull request #211 from mannol1/master
Fix bug
2014-08-02 19:22:45 +02:00
f98e6bdcb4 Fix bug 2014-08-02 19:21:40 +02:00
0884954c84 fix data file init bug 2014-08-02 13:04:29 -04:00
43727c6730 Merge pull request #210 from mannol1/master
Fresh pack of backdoors
2014-08-02 17:31:36 +02:00
618b731d5a fix possible buffer overflow 2014-08-01 23:00:52 -04:00
46975bf38b Updated with latest core 2014-08-02 02:10:21 +02:00
8f3989000d Box notifications are like ready 2014-08-02 00:37:02 +02:00
9fe75fbc47 fix 2014-08-01 15:05:10 -04:00
c455e79604 dynamically adjust main loop sleep time according to recommended value 2014-08-01 13:56:49 -04:00
a223545853 close chatwindow if its associated contact is blocked/deleted 2014-07-31 22:05:09 -04:00
b243f7aa62 Merge branch 'master' of https://github.com/Tox/toxic 2014-07-31 15:13:10 -04:00
899452d7cd Merge branch 'block' 2014-07-31 15:04:05 -04:00
af68fa7ee0 endian correctness 2014-07-31 15:02:19 -04:00
5da789cc37 save last online data in blocked list 2014-07-31 14:53:02 -04:00
7e5b41c8e0 another apple include: <sys/syslimits.h>
for NAME_MAX
2014-07-31 10:52:36 -07:00
0254596c73 add help menu for friendlist 2014-07-31 13:49:15 -04:00
67c02404b7 alut is not part of OpenAL.framework on OS X
therefore the linux include path works fine
2014-07-31 10:38:04 -07:00
0b5ee7e2c7 Merge pull request #208 from Ansa89/makefile-update
Makefile: refactoring and adding desktop notifications support
2014-07-31 12:51:44 -04:00
fba0732faa implement contact blocking 2014-07-31 12:48:49 -04:00
d06086a656 Try to support older versions of OpenAL 2014-07-31 13:14:33 +02:00
e74b678739 check_features.mk: modify string comparison 2014-07-31 11:54:22 +02:00
b62787ce47 Makefile: refactoring and adding desktop notifications support 2014-07-30 14:14:13 +02:00
75708f7600 couple small fixes 2014-07-30 02:46:08 -04:00
476dec46b6 add settings to toggle typing notifications for self and others 2014-07-29 20:14:44 -04:00
973f6206ee enforce const correctess, fix undefined behaviour with string literals 2014-07-29 14:54:34 -04:00
cbe47b3660 Merge pull request #207 from Ansa89/manpage-update
Update toxic.conf manpage
2014-07-29 11:51:58 -04:00
1c58c339bb Update toxic.conf manpage 2014-07-29 12:07:24 +02:00
15e91cfa99 formatting, use case-insensitive string compare, use defines for keycodes 2014-07-28 21:47:35 -04:00
f4fb6ea4fc Merge branch 'gracchus163-master' 2014-07-28 20:45:52 -04:00
0d2ff2c0a8 Fixed toxic.conf.example mistakes 2014-07-29 01:28:10 +01:00
5275da5a6b Merge branch 'keybinds'
configurable keybinds finished and working, merging with master branch
2014-07-29 00:55:39 +01:00
e891b1281b Configurable keybinds implemented and example conf updated 2014-07-29 00:53:44 +01:00
ca7110b37c small visual adjustment to progress bar 2014-07-28 19:36:30 -04:00
8960eb98f4 Example conf updated 2014-07-28 22:47:33 +01:00
18610668b8 Human readable config parsing complete, few tests left to confirm 2014-07-28 22:44:12 +01:00
efe61e32e2 Merge pull request #204 from zetok/symbol
Fix ONLINE_CHAR being identical to OFFLINE_CHAR
2014-07-28 13:39:40 -04:00
7a7e4f573a Fix ONLINE_CHAR being identical to OFFLINE_CHAR 2014-07-28 18:38:15 +01:00
a7e6ab7758 reads correctly from config file now, need to go through and check each one and then look at usability 2014-07-28 15:19:50 +01:00
a0cde4ae8c Merge remote-tracking branch 'origin/master' into keybinds 2014-07-28 14:06:54 +01:00
7566aa9d26 Merge pull request #205 from loadedice/master
Lowered volume of sounds
2014-07-28 03:14:29 -04:00
d2332a5b77 Lowered volume of sounds 2014-07-28 16:42:57 +10:00
94a8ce5aa8 fix buggy path autocomplete behaviour 2014-07-28 01:33:12 -04:00
b18a67d656 put file senders in a round-robin queue so multiple transfers upload at a uniform pace 2014-07-27 22:27:27 -04:00
02708534c0 Merge pull request #200 from mannol1/master
Core adjustments
2014-07-28 01:35:58 +02:00
d5710d80e0 Upstream adaption 2014-07-28 01:35:40 +02:00
8dcba3219d Merge remote-tracking branch 'origin/master' into keybinds 2014-07-28 00:32:05 +01:00
9f01a45b1f Keybind defaults moved to settings.c; conf keybinds not reading correctly yet 2014-07-28 00:06:25 +01:00
dd2cb93ecc Merge pull request #203 from zetok/manpages
Put man pages in right place by default (#202)
2014-07-27 18:15:32 -04:00
49538a986c improve file transfer progress line 2014-07-27 18:00:33 -04:00
26c2331d0f Put man pages in right place by default (#202) 2014-07-27 21:06:37 +01:00
a0758643c2 fix blurry screenshot? 2014-07-27 03:36:06 -04:00
77e152ad36 fix security flaw where untrusted input wasn't being sanitized 2014-07-26 21:22:55 -04:00
4834642b80 Merge remote-tracking branch 'upstream/master' 2014-07-27 01:50:06 +02:00
18a6f621f0 Started adding support for popup notifications and adjustments to new core 2014-07-27 01:49:59 +02:00
3cae1d92cd change online/offline symbols 2014-07-26 19:16:07 -04:00
02b192d6ee fix bug and typo 2014-07-25 20:17:22 -04:00
f630a3e604 fix possible buffer overflows and undefined behaviour 2014-07-25 17:55:21 -04:00
fb5a9bc043 fix backspace bug, simplify a bit 2014-07-25 15:51:29 -04:00
26ad5a00a3 Fixed out of bounds read. 2014-07-25 15:17:46 -04:00
18e1f08e31 Merge branch 'master' of https://github.com/Tox/toxic 2014-07-24 23:04:07 -04:00
b68deef6db allow line_info_add to take formatted strings with variable # of args 2014-07-24 23:03:55 -04:00
30ec7debba Merge pull request #199 from mannol1/master
Fixed sounds not playing
2014-07-25 02:13:24 +02:00
3a1e23a3ff Fixed sounds not playing 2014-07-25 02:12:32 +02:00
0887bb7662 Merge pull request #198 from Ansa89/master
README.md: add precompiled binaries
2014-07-24 13:24:51 +02:00
5a55f738a9 README.md: add precompiled binaries 2014-07-24 13:05:56 +02:00
8d8df585ad sort directories and skip special symbols for path autocomplete 2014-07-23 18:34:32 -04:00
8660047ec1 fix small bug, update readme, bump version 2014-07-23 15:15:14 -04:00
9476db02a9 allow disabling of individual sounds 2014-07-23 14:59:36 -04:00
04fbf5f724 Merge pull request #195 from Ansa89/notify-fix
Add hardcoded path for sound notifications
2014-07-23 11:56:20 +02:00
3cc629cbc1 Add hardcoded path for sound notifications 2014-07-23 11:25:38 +02:00
50fca4cddf Merge pull request #193 from Ansa89/makefile-fix
Makefile: little refactoring
2014-07-23 03:51:52 +02:00
a047cead05 fix a few notification issues 2014-07-22 14:38:32 -04:00
1131b73299 Update toxic.conf.5 manpage 2014-07-22 16:38:20 +02:00
196af10d01 Move init and exit in right place 2014-07-22 12:30:35 +02:00
1e0e93e5c6 Makefile: little refactoring 2014-07-22 09:59:44 +02:00
ac01d6d316 fix config file loading, fix makefile compile errors 2014-07-21 18:57:16 -04:00
654e404e0e Merge pull request #190 from mannol1/master
Fixed some build errors
2014-07-21 23:49:08 +02:00
f0f1138c54 Fixed some build errors 2014-07-21 23:48:39 +02:00
bc94e08970 sendfile tab-complete for ~/ shortcut 2014-07-21 15:11:30 -04:00
42c3ede963 Merge pull request #188 from Ansa89/makefile-fix
Makefile fix
2014-07-21 13:02:25 -04:00
174568d769 README.md: add dependencies for sound notifications 2014-07-21 14:19:01 +02:00
4587d8ebbd Forgot libalut-dev dependency 2014-07-21 12:50:29 +02:00
98aae242fb Makefile fix 2014-07-21 12:33:19 +02:00
7abd8d5ee5 Merge pull request #187 from mannol1/master
Added sound notifications, libconfig support, and more...
2014-07-21 12:08:19 +02:00
98ac4d7983 Added sound notifications and prepared for later system notifications. Also, now using libconfig for configuration loading. 2014-07-21 03:25:21 +02:00
933d46553f Added sounds 2014-07-21 01:12:13 +02:00
7e667a8028 print multiple matches for all tab-complete operations, fix bug when tab-completing at end of line 2014-07-18 13:48:21 -04:00
d4e41d6053 autocomplete pathnames with spaces 2014-07-18 03:20:40 -04:00
f30dccf726 return correct value, fix comment 2014-07-18 01:52:33 -04:00
ea3fcd5b79 auto-completion for paths when sending file & improved auto-complete algorithm to do partial matches 2014-07-18 01:37:33 -04:00
e61d070def cleanup/error checks 2014-07-17 03:35:18 -04:00
b5f34f42a8 small fix 2014-07-16 21:34:50 -04:00
4426eaddd9 increase max friends 2014-07-16 15:59:54 -04:00
83f0720a39 another UB fix 2014-07-16 15:55:04 -04:00
ce4f293574 fix undefined behaviour with string literals 2014-07-16 12:51:07 -04:00
95 changed files with 10449 additions and 3282 deletions

View File

@ -5,26 +5,26 @@ compiler:
before_script:
# Installing yasm (needed for compiling vpx) and openal
- sudo apt-get -yq install yasm libopenal-dev
- sudo apt-get -yq install yasm libopenal-dev libconfig-dev libalut-dev libnotify-dev clang llvm-dev
# Installing libsodium, needed for toxcore
- git clone https://github.com/jedisct1/libsodium.git libsodium
- cd libsodium
- git checkout tags/0.4.2 > /dev/null
- git checkout tags/1.0.3 > /dev/null
- ./autogen.sh > /dev/null
- ./configure > /dev/null
- make check -j2 || make check || exit 1 > /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
- tar xzf opus-1.0.3.tar.gz > /dev/null
- cd opus-1.0.3
- wget http://downloads.xiph.org/releases/opus/opus-1.1.tar.gz
- tar xzf opus-1.1.tar.gz > /dev/null
- cd opus-1.1
- ./configure > /dev/null
- make -j2 || make || exit 1 > /dev/null
- sudo make install > /dev/null
- cd ..
# Installing vpx
- git clone http://git.chromium.org/webm/libvpx.git libvpx
- git clone https://chromium.googlesource.com/webm/libvpx libvpx
- cd libvpx
- ./configure --enable-shared > /dev/null
- make -j2 || make || exit 1 > /dev/null
@ -41,12 +41,11 @@ before_script:
- sudo make install
- cd ..
script:
- cd build
- make -j2 || make || exit 1
notifications:
email: false
irc:
irc:
channels:
- "chat.freenode.net#tox-dev"
on_success: always

68
INSTALL.md Normal file
View File

@ -0,0 +1,68 @@
# Installation
* [Dependencies](#deps)
* [OS X Notes](#deps_osx)
* [Compiling](#compiling)
* [Documentation](#docs)
* [Notes](#notes)
* [Compilation variables](#comp_vars)
* [Packaging](#packaging)
<a name="deps" />
## Dependencies
| Name | Needed by | Debian package |
|------------------------------------------------------|----------------------------|------------------|
| [Tox Core](https://github.com/irungentoo/toxcore) | BASE | *None* |
| [NCurses](https://www.gnu.org/software/ncurses) | BASE | libncursesw5-dev |
| [LibConfig](http://www.hyperrealm.com/libconfig) | BASE | libconfig-dev |
| [GNUmake](https://www.gnu.org/software/make) | BASE | make |
| [Tox Core AV](https://github.com/irungentoo/toxcore) | AUDIO | *None* |
| [OpenAL](http://openal.org) | AUDIO, SOUND NOTIFICATIONS | libopenal-dev |
| [OpenALUT](http://openal.org) | SOUND NOTIFICATIONS | libalut-dev |
| [LibNotify](https://developer.gnome.org/libnotify) | DESKTOP NOTIFICATIONS | libnotify-dev |
| [AsciiDoc](http://asciidoc.org/index.html) | DOCUMENTATION<sup>1</sup> | asciidoc |
<sup>1</sup>: see [Documentation](#docs)
<a name="deps_osx" />
#### OS X Notes
Using [Homebrew](http://brew.sh):
```
brew install openal-soft freealut libconfig
brew install https://raw.githubusercontent.com/Tox/homebrew-tox/master/Formula/libtoxcore.rb
brew install https://raw.githubusercontent.com/Homebrew/homebrew-x11/master/libnotify.rb
```
You can omit `libnotify` if you intend to build without desktop notifications enabled.
<a name="Compiling">
## Compiling
```
make PREFIX="/where/to/install"
sudo make install PREFIX="/where/to/install"
```
<a name="docs" />
#### Documentation
Run `make doc` in the build directory after editing the asciidoc files to regenerate the manpages.<br />
**NOTE FOR DEVELOPERS**: asciidoc files and generated manpages will need to be commited together.<br />
**NOTE FOR EVERYONE**: [asciidoc](http://asciidoc.org/index.html) (and this step) is only required for regenerating manpages when you modify them.
<a name="notes" />
## Notes
<a name="comp_vars" />
#### Compilation variables
* You can add specific flags to the Makefile with `USER_CFLAGS=""` and/or `USER_LDFLAGS=""`
* You can pass your own flags to the Makefile with `CFLAGS=""` and/or `LDFLAGS=""` (this will supersede the default ones)
* Additional features are automatically enabled if all dependencies are found, but you can disable them by using special variables:
* `DISABLE_X11=1` → build toxic without X11 support (needed for focus tracking)
* `DISABLE_AV=1` → build toxic without audio call support
* `DISABLE_SOUND_NOTIFY=1` → build toxic without sound notifications support
* `DISABLE_DESKTOP_NOTIFY=1` → build toxic without desktop notifications support
<a name="packaging" />
#### Packaging
* For packaging purpose, you can use `DESTDIR=""` to specify a directory where to store installed files
* `DESTDIR=""` can be used in addition to `PREFIX=""`:
* `DESTDIR=""` is meant to specify a directory where to store installed files (ex: "/tmp/build/pkg")
* `PREFIX=""` is meant to specify a prefix directory for binaries and data files (ex: "/usr/local")

77
Makefile Normal file
View File

@ -0,0 +1,77 @@
BASE_DIR = $(shell pwd -P)
CFG_DIR = $(BASE_DIR)/cfg
-include $(CFG_DIR)/global_vars.mk
LIBS = libtoxcore ncursesw libconfig
CFLAGS = -std=gnu99 -pthread -Wall -g
CFLAGS += '-DTOXICVER="$(VERSION)"' -DHAVE_WIDECHAR -D_XOPEN_SOURCE_EXTENDED -D_FILE_OFFSET_BITS=64
CFLAGS += '-DPACKAGE_DATADIR="$(abspath $(DATADIR))"'
CFLAGS += $(USER_CFLAGS)
LDFLAGS = $(USER_LDFLAGS)
OBJ = chat.o chat_commands.o configdir.o dns.o execute.o file_transfers.o notify.o
OBJ += friendlist.o global_commands.o groupchat.o line_info.o input.o help.o autocomplete.o
OBJ += log.o misc_tools.o prompt.o settings.o toxic.o toxic_strings.o windows.o message_queue.o
OBJ += group_commands.o term_mplex.o avatars.o
# Check on wich system we are running
UNAME_S = $(shell uname -s)
ifeq ($(UNAME_S), Linux)
-include $(CFG_DIR)/systems/Linux.mk
endif
ifeq ($(UNAME_S), FreeBSD)
-include $(CFG_DIR)/systems/FreeBSD.mk
endif
ifeq ($(UNAME_S), OpenBSD)
-include $(CFG_DIR)/systems/FreeBSD.mk
endif
ifeq ($(UNAME_S), Darwin)
-include $(CFG_DIR)/systems/Darwin.mk
endif
ifeq ($(UNAME_S), Solaris)
-include $(CFG_DIR)/systems/Solaris.mk
endif
# Check on which platform we are running
UNAME_M = $(shell uname -m)
ifeq ($(UNAME_M), x86_64)
-include $(CFG_DIR)/platforms/x86_64.mk
endif
ifneq ($(filter %86, $(UNAME_M)),)
-include $(CFG_DIR)/platforms/x86.mk
endif
ifneq ($(filter arm%, $(UNAME_M)),)
-include $(CFG_DIR)/platforms/arm.mk
endif
# Include all needed checks
-include $(CFG_DIR)/checks/check_features.mk
# Fix path for object files
OBJ := $(addprefix $(BUILD_DIR)/, $(OBJ))
# Targets
all: $(BUILD_DIR)/toxic
$(BUILD_DIR)/toxic: $(OBJ)
@echo " LD $(@:$(BUILD_DIR)/%=%)"
@$(CC) $(CFLAGS) -o $(BUILD_DIR)/toxic $(OBJ) $(LDFLAGS)
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
@if [ ! -e $(BUILD_DIR) ]; then \
mkdir -p $(BUILD_DIR) ;\
fi
@echo " CC $(@:$(BUILD_DIR)/%=%)"
@$(CC) $(CFLAGS) -o $(BUILD_DIR)/$*.o -c $(SRC_DIR)/$*.c
@$(CC) -MM $(CFLAGS) $(SRC_DIR)/$*.c > $(BUILD_DIR)/$*.d
clean:
rm -f $(BUILD_DIR)/*.d $(BUILD_DIR)/*.o $(BUILD_DIR)/toxic
-include $(BUILD_DIR)/$(OBJ:.o=.d)
-include $(CFG_DIR)/targets/*.mk
.PHONY: clean all

View File

@ -1,37 +1,17 @@
# Toxic [![Build Status](https://travis-ci.org/Tox/toxic.png?branch=master)](https://travis-ci.org/Tox/toxic)
Toxic is a [Tox](https://tox.im)-based instant messenging client which formerly resided in the [Tox core repository](https://github.com/irungentoo/toxcore), and is now available as a standalone application.
Toxic is a [Tox](https://tox.chat)-based instant messenging client which formerly resided in the [Tox core repository](https://github.com/irungentoo/toxcore), and is now available as a standalone application.
![Toxic Screenshot](https://i.imgur.com/YOZ5NIB.png "Main Screen").
[![Toxic Screenshot](https://i.imgur.com/san99Z2.png "Home Screen")](https://i.imgur.com/san99Z2.png)
## Installation
[Use our repositories](https://wiki.tox.chat/binaries#other_linux)<br />
[Compile it yourself](/INSTALL.md)
### Dependencies
##### Base
* [libtoxcore](https://github.com/irungentoo/toxcore)
* [ncurses](https://www.gnu.org/software/ncurses) (for Debian based systems, 'libncursesw5-dev')
## Settings
Running Toxic for the first time creates an empty file called toxic.conf in your home configuration directory ("~/.config/tox" for Linux users). Adding options to this file allows you to enable auto-logging, change the time format (12/24 hour), and much more.
You can view our example config file [here](misc/toxic.conf.example).
##### Audio
* libtoxav (libtoxcore compiled with audio support)
* [openal](http://openal.org)
### Compiling
1. `cd build/`
2. `make PREFIX="/where/to/install"`
3. `sudo make install PREFIX="/where/to/install"`
### Compilation Notes
* You can add specific flags to the Makefile with `USER_CFLAGS=""` and/or `USER_LDFLAGS=""`
* You can pass your own flags to the Makefile with `CFLAGS=""` and/or `LDFLAGS=""` (this will supersede the default ones)
* Audio call support is automatically enabled if all dependencies are found
* If you want to build toxic without audio call support, you can use `make DISABLE_AV=1`
### Packaging
* For packaging purpose, you can use `DESTDIR=""` to specify a directory where to store installed files
* `DESTDIR=""` can be used in addition to `PREFIX=""`:
* `DESTDIR=""` is meant to specify a directory where to store installed files (ex: "/tmp/build/pkg")
* `PREFIX=""` is meant to specify a prefix directory for binaries and data files (ex: "/usr/local")
### Troubleshooting
## Troubleshooting
If your default prefix is "/usr/local" and you receive the following:
```
error while loading shared libraries: libtoxcore.so.0: cannot open shared object file: No such file or directory
@ -42,7 +22,3 @@ echo '/usr/local/lib/' | sudo tee -a /etc/ld.so.conf.d/locallib.conf
sudo ldconfig
```
## Settings
Running Toxic for the first time creates an empty file called toxic.conf in your home configuration directory ("~/.config/tox" for Linux users). Adding options to this file allows you to enable auto-logging, change the time format (12/24 hour), and much more.
You can view our example config file [here](misc/toxic.conf.example).

View File

@ -1,132 +0,0 @@
TOXIC_VERSION = 0.4.5
REV = $(shell git rev-list HEAD --count)
VERSION = $(TOXIC_VERSION)_r$(REV)
CFG_DIR = ../cfg
SRC_DIR = ../src
MISC_DIR = ../misc
DOC_DIR = ../doc
PREFIX = /usr/local
BINDIR = $(PREFIX)/bin
DATADIR = $(PREFIX)/share/toxic
MANDIR = $(PREFIX)/man
DATAFILES = DHTnodes toxic.conf.example
MANFILES = toxic.1 toxic.conf.5
LIBS = libtoxcore ncursesw
CFLAGS = -std=gnu99 -pthread -Wall -g
CFLAGS += -DTOXICVER="\"$(VERSION)\"" -DHAVE_WIDECHAR -D_XOPEN_SOURCE_EXTENDED
CFLAGS += -DPACKAGE_DATADIR="\"$(abspath $(DATADIR))\""
CFLAGS += $(USER_CFLAGS)
LDFLAGS = $(USER_LDFLAGS)
OBJ = chat.o chat_commands.o configdir.o dns.o execute.o file_senders.o
OBJ += friendlist.o global_commands.o groupchat.o line_info.o input.o help.o
OBJ += log.o misc_tools.o prompt.o settings.o toxic.o toxic_strings.o windows.o
# Variables for audio support
AUDIO_LIBS = libtoxav openal
AUDIO_CFLAGS = -D_SUPPORT_AUDIO
AUDIO_OBJ = device.o audio_call.o
# Check on wich system we are running
UNAME_S = $(shell uname -s)
ifeq ($(UNAME_S), Linux)
-include $(CFG_DIR)/Linux.mk
endif
ifeq ($(UNAME_S), FreeBSD)
-include $(CFG_DIR)/FreeBSD.mk
endif
ifeq ($(UNAME_S), Darwin)
-include $(CFG_DIR)/Darwin.mk
endif
ifeq ($(UNAME_S), Solaris)
-include $(CFG_DIR)/Solaris.mk
endif
# Check on which platform we are running
UNAME_M = $(shell uname -m)
ifeq ($(UNAME_M), x86_64)
-include $(CFG_DIR)/x86_64.mk
endif
ifneq ($(filter %86, $(UNAME_M)),)
-include $(CFG_DIR)/x86.mk
endif
ifneq ($(filter arm%, $(UNAME_M)),)
-include $(CFG_DIR)/arm.mk
endif
# Check if we want/can build audio
ifneq ($(DISABLE_AV), 1)
CHECK_AUDIO_LIBS = $(shell pkg-config $(AUDIO_LIBS) || echo -n "error")
ifneq ($(CHECK_AUDIO_LIBS), error)
LIBS += $(AUDIO_LIBS)
CFLAGS += $(AUDIO_CFLAGS)
OBJ += $(AUDIO_OBJ)
else
ifneq ($(MAKECMDGOALS), clean)
MISSING_AUDIO_LIBS = $(shell for lib in $(AUDIO_LIBS) ; do if ! pkg-config $$lib ; then echo $$lib ; fi ; done)
$(warning WARNING -- Toxic will be compiled without audio support)
$(warning WARNING -- You need these libraries for audio support)
$(warning WARNING -- $(MISSING_AUDIO_LIBS))
endif
endif
endif
# Check if we can build Toxic
CHECK_LIBS = $(shell pkg-config $(LIBS) || echo -n "error")
ifneq ($(CHECK_LIBS), error)
CFLAGS += $(shell pkg-config --cflags $(LIBS))
LDFLAGS += $(shell pkg-config --libs $(LIBS))
else
ifneq ($(MAKECMDGOALS), clean)
MISSING_LIBS = $(shell for lib in $(LIBS) ; do if ! pkg-config $$lib ; then echo $$lib ; fi ; done)
$(warning ERROR -- Cannot compile Toxic)
$(warning ERROR -- You need these libraries)
$(warning ERROR -- $(MISSING_LIBS))
$(error ERROR)
endif
endif
# Targets
all: toxic
toxic: $(OBJ)
@echo " LD $@"
@$(CC) $(CFLAGS) -o toxic $(OBJ) $(LDFLAGS)
install: toxic
mkdir -p $(abspath $(DESTDIR)/$(BINDIR))
mkdir -p $(abspath $(DESTDIR)/$(DATADIR))
mkdir -p $(abspath $(DESTDIR)/$(MANDIR))
@echo "Installing toxic executable"
@install -m 0755 toxic $(abspath $(DESTDIR)/$(BINDIR))
@echo "Installing data files"
@for f in $(DATAFILES) ; do \
install -m 0644 $(MISC_DIR)/$$f $(abspath $(DESTDIR)/$(DATADIR)) ;\
done
@echo "Installing man pages"
@for f in $(MANFILES) ; do \
section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $$f | rev | cut -d "." -f 1` ;\
file=$$section/$$f ;\
mkdir -p $$section ;\
install -m 0644 $(DOC_DIR)/$$f $$file ;\
sed -i'' -e 's:__VERSION__:'$(VERSION)':g' $$file ;\
sed -i'' -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$file ;\
gzip -f -9 $$file ;\
done
%.o: $(SRC_DIR)/%.c
@echo " CC $@"
@$(CC) $(CFLAGS) -o $*.o -c $(SRC_DIR)/$*.c
@$(CC) -MM $(CFLAGS) $(SRC_DIR)/$*.c > $*.d
clean:
rm -rf *.d *.o toxic
-include $(CFG_DIR)/help.mk
-include $(OBJ:.o=.d)
.PHONY: clean all install

View File

@ -1,3 +0,0 @@
# Specials options for linux systems
CFLAGS +=
LDFLAGS += -ldl -lresolv

21
cfg/checks/av.mk Normal file
View File

@ -0,0 +1,21 @@
# Variables for audio call support
AUDIO_LIBS = libtoxav openal
AUDIO_CFLAGS = -DAUDIO
ifneq (, $(findstring device.o, $(OBJ)))
AUDIO_OBJ = audio_call.o
else
AUDIO_OBJ = audio_call.o device.o
endif
# Check if we can build audio support
CHECK_AUDIO_LIBS = $(shell $(PKG_CONFIG) --exists $(AUDIO_LIBS) || echo -n "error")
ifneq ($(CHECK_AUDIO_LIBS), error)
LIBS += $(AUDIO_LIBS)
CFLAGS += $(AUDIO_CFLAGS)
OBJ += $(AUDIO_OBJ)
else ifneq ($(MAKECMDGOALS), clean)
MISSING_AUDIO_LIBS = $(shell for lib in $(AUDIO_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
$(warning WARNING -- Toxic will be compiled without audio support)
$(warning WARNING -- You need these libraries for audio support)
$(warning WARNING -- $(MISSING_AUDIO_LIBS))
endif

View File

@ -0,0 +1,38 @@
CHECKS_DIR = $(CFG_DIR)/checks
# Check if we want build X11 support
X11 = $(shell if [ -z "$(DISABLE_X11)" ] || [ "$(DISABLE_X11)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
ifneq ($(X11), disabled)
-include $(CHECKS_DIR)/x11.mk
endif
# Check if we want build audio support
AUDIO = $(shell if [ -z "$(DISABLE_AV)" ] || [ "$(DISABLE_AV)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
ifneq ($(AUDIO), disabled)
-include $(CHECKS_DIR)/av.mk
endif
# Check if we want build sound notifications support
SND_NOTIFY = $(shell if [ -z "$(DISABLE_SOUND_NOTIFY)" ] || [ "$(DISABLE_SOUND_NOTIFY)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
ifneq ($(SND_NOTIFY), disabled)
-include $(CHECKS_DIR)/sound_notifications.mk
endif
# Check if we want build desktop notifications support
DESK_NOTIFY = $(shell if [ -z "$(DISABLE_DESKTOP_NOTIFY)" ] || [ "$(DISABLE_DESKTOP_NOTIFY)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
ifneq ($(DESK_NOTIFY), disabled)
-include $(CHECKS_DIR)/desktop_notifications.mk
endif
# Check if we can build Toxic
CHECK_LIBS = $(shell $(PKG_CONFIG) --exists $(LIBS) || echo -n "error")
ifneq ($(CHECK_LIBS), error)
CFLAGS += $(shell $(PKG_CONFIG) --cflags $(LIBS))
LDFLAGS += $(shell $(PKG_CONFIG) --libs $(LIBS))
else ifneq ($(MAKECMDGOALS), clean)
MISSING_LIBS = $(shell for lib in $(LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
$(warning ERROR -- Cannot compile Toxic)
$(warning ERROR -- You need these libraries)
$(warning ERROR -- $(MISSING_LIBS))
$(error ERROR)
endif

View File

@ -0,0 +1,15 @@
# Variables for desktop notifications support
DESK_NOTIFY_LIBS = libnotify
DESK_NOTIFY_CFLAGS = -DBOX_NOTIFY
# Check if we can build desktop notifications support
CHECK_DESK_NOTIFY_LIBS = $(shell $(PKG_CONFIG) --exists $(DESK_NOTIFY_LIBS) || echo -n "error")
ifneq ($(CHECK_DESK_NOTIFY_LIBS), error)
LIBS += $(DESK_NOTIFY_LIBS)
CFLAGS += $(DESK_NOTIFY_CFLAGS)
else ifneq ($(MAKECMDGOALS), clean)
MISSING_DESK_NOTIFY_LIBS = $(shell for lib in $(DESK_NOTIFY_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
$(warning WARNING -- Toxic will be compiled without desktop notifications support)
$(warning WARNING -- You need these libraries for desktop notifications support)
$(warning WARNING -- $(MISSING_DESK_NOTIFY_LIBS))
endif

View File

@ -0,0 +1,21 @@
# Variables for sound notifications support
SND_NOTIFY_LIBS = openal freealut
SND_NOTIFY_CFLAGS = -DSOUND_NOTIFY
ifneq (, $(findstring device.o, $(OBJ)))
SND_NOTIFY_OBJ =
else
SND_NOTIFY_OBJ = device.o
endif
# Check if we can build sound notifications support
CHECK_SND_NOTIFY_LIBS = $(shell $(PKG_CONFIG) --exists $(SND_NOTIFY_LIBS) || echo -n "error")
ifneq ($(CHECK_SND_NOTIFY_LIBS), error)
LIBS += $(SND_NOTIFY_LIBS)
CFLAGS += $(SND_NOTIFY_CFLAGS)
OBJ += $(SND_NOTIFY_OBJ)
else ifneq ($(MAKECMDGOALS), clean)
MISSING_SND_NOTIFY_LIBS = $(shell for lib in $(SND_NOTIFY_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
$(warning WARNING -- Toxic will be compiled without sound notifications support)
$(warning WARNING -- You need these libraries for sound notifications support)
$(warning WARNING -- $(MISSING_SND_NOTIFY_LIBS))
endif

17
cfg/checks/x11.mk Normal file
View File

@ -0,0 +1,17 @@
# Variables for X11 support
X11_LIBS = x11
X11_CFLAGS = -DX11
X11_OBJ = xtra.o
# Check if we can build X11 support
CHECK_X11_LIBS = $(shell $(PKG_CONFIG) --exists $(X11_LIBS) || echo -n "error")
ifneq ($(CHECK_X11_LIBS), error)
LIBS += $(X11_LIBS)
CFLAGS += $(X11_CFLAGS)
OBJ += $(X11_OBJ)
else ifneq ($(MAKECMDGOALS), clean)
MISSING_X11_LIBS = $(shell for lib in $(X11_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
$(warning WARNING -- Toxic will be compiled without x11 support (needed for focus tracking and drag&drop support))
$(warning WARNING -- You need these libraries for x11 support)
$(warning WARNING -- $(MISSING_X11_LIBS))
endif

33
cfg/global_vars.mk Normal file
View File

@ -0,0 +1,33 @@
# Version
TOXIC_VERSION = 0.6.0
REV = $(shell git rev-list HEAD --count 2>/dev/null || echo -n "error")
ifneq (, $(findstring error, $(REV)))
VERSION = $(TOXIC_VERSION)
else
VERSION = $(TOXIC_VERSION)_r$(REV)
endif
# Project directories
BUILD_DIR = $(BASE_DIR)/build
DOC_DIR = $(BASE_DIR)/doc
SRC_DIR = $(BASE_DIR)/src
SND_DIR = $(BASE_DIR)/sounds
MISC_DIR = $(BASE_DIR)/misc
# Project files
MANFILES = toxic.1 toxic.conf.5
DATAFILES = DHTnodes DNSservers toxic.conf.example
DESKFILE = toxic.desktop
SNDFILES = ToxicContactOnline.wav ToxicContactOffline.wav ToxicError.wav
SNDFILES += ToxicRecvMessage.wav ToxicOutgoingCall.wav ToxicIncomingCall.wav
SNDFILES += ToxicTransferComplete.wav ToxicTransferStart.wav
# Install directories
PREFIX = /usr/local
BINDIR = $(PREFIX)/bin
DATADIR = $(PREFIX)/share/toxic
MANDIR = $(PREFIX)/share/man
APPDIR = $(PREFIX)/share/applications
# Platform tools
PKG_CONFIG = pkg-config

View File

@ -1,17 +0,0 @@
# Help target
help:
@echo "-- Targets --"
@echo " all: Build toxic [DEFAULT]"
@echo " toxic: Build toxic"
@echo " install: Build toxic and install it in PREFIX (default PREFIX is \"$(abspath $(PREFIX))\")"
@echo " clean: Remove built files"
@echo " help: This help"
@echo
@echo "-- Variables --"
@echo " DISABLE_AV: Set to \"1\" to force building without audio call support"
@echo " USER_CFLAGS: Add custom flags to default CFLAGS"
@echo " USER_LDFLAGS: Add custom flags to default LDFLAGS"
@echo " PREFIX: Specify a prefix directory for binaries, data files,... (default is \"$(abspath $(PREFIX))\")"
@echo " DESTDIR: Specify a directory where to store installed files (mainly for packaging purpose)"
.PHONY: help

4
cfg/platforms/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

10
cfg/systems/Darwin.mk Normal file
View File

@ -0,0 +1,10 @@
# Special options for OS X
# This assumes the use of Homebrew. Change the paths if using MacPorts or Fink.
PKG_CONFIG_PATH = $(shell export PKG_CONFIG_PATH=/usr/lib/pkgconfig:/usr/local/opt/libconfig/lib/pkgconfig:/usr/local/lib/pkgconfig:/opt/X11/lib/pkgconfig)
LIBS := $(filter-out ncursesw, $(LIBS))
# OS X ships a usable, recent version of ncurses, but calls it ncurses not ncursesw.
LDFLAGS += -lncurses -lalut -ltoxav -ltoxcore -ltoxdns -lresolv -lconfig -ltoxencryptsave -g
CFLAGS += -I/usr/local/opt/freealut/include/AL -I/usr/local/opt/glib/include/glib-2.0 -g

View File

@ -1,3 +1,4 @@
# Specials options for freebsd systems
LIBS := $(filter-out ncursesw, $(LIBS))
LDFLAGS += -lncursesw
MANDIR = $(PREFIX)/man

4
cfg/systems/Linux.mk Normal file
View File

@ -0,0 +1,4 @@
# Specials options for linux systems
CFLAGS +=
LDFLAGS += -ldl -lresolv -lrt
MANDIR = $(PREFIX)/share/man

10
cfg/targets/doc.mk Normal file
View File

@ -0,0 +1,10 @@
# Doc target
doc: $(MANFILES:%=$(DOC_DIR)/%)
$(DOC_DIR)/%: $(DOC_DIR)/%.asc
@echo " MAN $(@F)"
@a2x -f manpage -a revdate=$(shell git log -1 --date=short --format="%ad" $<) \
-a manmanual="Toxic Manual" -a mansource=toxic \
-a manversion=__VERSION__ -a datadir=__DATADIR__ $<
.PHONY: doc

22
cfg/targets/help.mk Normal file
View File

@ -0,0 +1,22 @@
# Help target
help:
@echo "-- Targets --"
@echo " all: Build toxic and documentation [DEFAULT]"
@echo " toxic: Build toxic"
@echo " doc: Build documentation"
@echo " install: Build toxic and install it in PREFIX (default PREFIX is \"$(abspath $(PREFIX))\")"
@echo " uninstall: Remove toxic from PREFIX (default PREFIX is \"$(abspath $(PREFIX))\")"
@echo " clean: Remove built files"
@echo " help: This help"
@echo
@echo "-- Variables --"
@echo " DISABLE_X11: Set to \"1\" to force building without X11 support"
@echo " DISABLE_AV: Set to \"1\" to force building without audio call support"
@echo " DISABLE_SOUND_NOTIFY: Set to \"1\" to force building without sound notification support"
@echo " DISABLE_DESKTOP_NOTIFY: Set to \"1\" to force building without desktop notifications support"
@echo " USER_CFLAGS: Add custom flags to default CFLAGS"
@echo " USER_LDFLAGS: Add custom flags to default LDFLAGS"
@echo " PREFIX: Specify a prefix directory for binaries, data files,... (default is \"$(abspath $(PREFIX))\")"
@echo " DESTDIR: Specify a directory where to store installed files (mainly for packaging purpose)"
.PHONY: help

41
cfg/targets/install.mk Normal file
View File

@ -0,0 +1,41 @@
# Install target
install: $(BUILD_DIR)/toxic
@echo "Installing toxic executable"
@mkdir -p $(abspath $(DESTDIR)/$(BINDIR))
@install -m 0755 $(BUILD_DIR)/toxic $(abspath $(DESTDIR)/$(BINDIR)/toxic)
@echo "Installing desktop file"
@mkdir -p $(abspath $(DESTDIR)/$(APPDIR))
@install -m 0644 $(MISC_DIR)/$(DESKFILE) $(abspath $(DESTDIR)/$(APPDIR)/$(DESKFILE))
@echo "Installing data files"
@mkdir -p $(abspath $(DESTDIR)/$(DATADIR))
@for f in $(DATAFILES) ; do \
install -m 0644 $(MISC_DIR)/$$f $(abspath $(DESTDIR)/$(DATADIR)/$$f) ;\
file=$(abspath $(DESTDIR)/$(DATADIR)/$$f) ;\
sed -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$file > temp_file && \
mv temp_file $$file ;\
done
@mkdir -p $(abspath $(DESTDIR)/$(DATADIR))/sounds
@for f in $(SNDFILES) ; do \
install -m 0644 $(SND_DIR)/$$f $(abspath $(DESTDIR)/$(DATADIR)/sounds/$$f) ;\
done
@echo "Installing man pages"
@mkdir -p $(abspath $(DESTDIR)/$(MANDIR))
@for f in $(MANFILES) ; do \
if [ ! -e "$(DOC_DIR)/$$f" ]; then \
continue ;\
fi ;\
section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $$f | rev | cut -d "." -f 1` ;\
file=$$section/$$f ;\
mkdir -p $$section ;\
install -m 0644 $(DOC_DIR)/$$f $$file ;\
sed -e 's:__VERSION__:'$(VERSION)':g' $$file > temp_file && \
mv temp_file $$file ;\
sed -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$file > temp_file && \
mv temp_file $$file ;\
gzip -f -9 $$file ;\
done
.PHONY: install

24
cfg/targets/uninstall.mk Normal file
View File

@ -0,0 +1,24 @@
# Uninstall target
uninstall:
@echo "Removing toxic executable"
@rm -f $(abspath $(DESTDIR)/$(BINDIR)/toxic)
@echo "Removing desktop file"
@rm -f $(abspath $(DESTDIR)/$(APPDIR)/$(DESKFILE))
@echo "Removing data files"
@for f in $(DATAFILES) ; do \
rm -f $(abspath $(DESTDIR)/$(DATADIR)/$$f) ;\
done
@for f in $(SNDFILES) ; do \
rm -f $(abspath $(DESTDIR)/$(DATADIR)/sounds/$$f) ;\
done
@echo "Removing man pages"
@for f in $(MANFILES) ; do \
section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $$f | rev | cut -d "." -f 1` ;\
file=$$section/$$f ;\
rm -f $$file $$file.gz ;\
done
.PHONY: uninstall

View File

@ -1,63 +1,158 @@
.TH TOXIC 1 "June 2014" "Toxic v__VERSION__" "User Manual"
.SH NAME
Toxic \- CLI client for Tox
.SH SYNOPSYS
.B toxic [\-f
.I data\-file
.B ] [\-x] [\-4] [\-c
.I config\-file
.B ] [\-n
.I nodes\-file
.B ] [\-h]
.SH DESCRIPTION
Toxic is an ncurses-based instant messaging client for Tox which formerly
resided in the Tox core repository, and is now available as a standalone
application.
.SH OPTIONS
.IP "\-f, \-\-file data\-file"
Use specified
.I data\-file
instead of
.IR ~/.config/tox/data
.IP "\-x, \-\-nodata"
Ignore data file
.IP "\-4, \-\-ipv4"
'\" t
.\" Title: toxic
.\" Author: [see the "AUTHORS" section]
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
.\" Date: 2015-03-28
.\" Manual: Toxic Manual
.\" Source: toxic __VERSION__
.\" Language: English
.\"
.TH "TOXIC" "1" "2015\-03\-28" "toxic __VERSION__" "Toxic Manual"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" http://bugs.debian.org/507673
.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
.\" disable hyphenation
.nh
.\" disable justification (adjust text to left margin only)
.ad l
.\" -----------------------------------------------------------------
.\" * MAIN CONTENT STARTS HERE *
.\" -----------------------------------------------------------------
.SH "NAME"
toxic \- CLI client for Tox
.SH "SYNOPSIS"
.sp
\fBtoxic\fR [\-f \fIdata\-file\fR] [\-x] [\-4] [\-c \fIconfig\-file\fR] [\-n \fInodes\-file\fR] [\-h]
.SH "DESCRIPTION"
.sp
toxic is an ncurses\-based instant messaging client for Tox which formerly resided in the Tox core repository, and is now available as a standalone application\&.
.SH "OPTIONS"
.PP
\-4, \-\-ipv4
.RS 4
Force IPv4 connection
.IP "\-d, \-\-default_locale
Use default locale
.IP "\-c, \-\-config config\-file"
.RE
.PP
\-b, \-\-debug
.RS 4
Enable stderr for debugging\&. Redirect output to avoid breaking the curses interface and better capture messages\&.
.RE
.PP
\-c, \-\-config config\-file
.RS 4
Use specified
.I config\-file
\fIconfig\-file\fR
instead of
.IR ~/.config/tox/toxic.conf
.IP "\-n, \-\-nodes nodes\-file"
\fI~/\&.config/tox/toxic\&.conf\fR
.RE
.PP
\-d, \-\-default\-locale
.RS 4
Use default locale
.RE
.PP
\-e, \-\-encrypt\-data
.RS 4
Encrypt an unencrypted data file\&. An error will occur if this option is used with an encrypted data file\&.
.RE
.PP
\-f, \-\-file data\-file
.RS 4
Use specified
.I nodes\-file
for DHT bootstrap nodes, instead of
.IR __DATADIR__/DHTnodes
.IP "\-h, \-\-help"
\fIdata\-file\fR
instead of
\fI~/\&.config/tox/data\fR
.RE
.PP
\-h, \-\-help
.RS 4
Show help message
.SH FILES
.IP __DATADIR__/DHTnodes
Default list of DHT bootstrap nodes.
.IP ~/.config/tox/data
Savestate which contains your personal info (nickname, Tox ID,...) and
your contacts list.
.IP ~/.config/tox/toxic.conf
Configuration file. See
.BR toxic.conf (5)
for more details.
.IP __DATADIR__/toxic.conf.example
Configuration example.
.SH BUGS
Unicode characters with a width larger than 1 column may cause
strange behaviour. Expect more bugs and bad
behaviour: this software is in a pre\-alpha stage.
.SH AUTHORS
JFreegman <JFreegman@gmail.com>
.SH SEE ALSO
.BR toxic.conf (5)
.SH LINKS
Project page on github: https://github.com/Tox/toxic
.br
IRC channel on Freenode: chat.freenode.net#tox
.RE
.PP
\-n, \-\-nodes nodes\-file
.RS 4
Use specified
\fInodes\-file\fR
for DHT bootstrap nodes, instead of
\fI__DATADIR__/DHTnodes\fR
.RE
.PP
\-o, \-\-noconnect
.RS 4
Do not connect to the DHT network
.RE
.PP
\-p, \-\-SOCKS5\-proxy
.RS 4
Use a SOCKS5 proxy: Requires [IP] [port]
.RE
.PP
\-P, \-\-HTTP\-proxy
.RS 4
Use a HTTP proxy: Requires [IP] [port]
.RE
.PP
\-r, \-\-dnslist
.RS 4
Use specified DNSservers file
.RE
.PP
\-t, \-\-force\-tcp
.RS 4
Force TCP connection (use this with proxies)
.RE
.PP
\-T, \-\-tcp\-relay
.RS 4
Act as a TCP relay server for the network (Note: this uses significantly more bandwidth)
.RE
.PP
\-u, \-\-unencrypt\-data
.RS 4
Unencrypt a data file\&. A warning will appear if this option is used with a data file that is already unencrypted\&.
.RE
.SH "FILES"
.PP
__DATADIR__/DHTnodes
.RS 4
Default list of DHT bootstrap nodes\&.
.RE
.PP
~/\&.config/tox/data
.RS 4
Savestate which contains your personal info (nickname, Tox ID, contacts, etc)
.RE
.PP
~/\&.config/tox/toxic\&.conf
.RS 4
Configuration file\&. See
\fBtoxic\&.conf\fR(5) for more details\&.
.RE
.PP
__DATADIR__/toxic\&.conf\&.example
.RS 4
Configuration example\&.
.RE
.SH "BUGS"
.sp
Unicode characters with a width larger than 1 column may cause strange behaviour\&. Expect more bugs and bad behaviour: this software is in a pre\-alpha stage\&.
.SH "AUTHORS"
.sp
JFreegman <JFreegman@gmail\&.com>
.SH "SEE ALSO"
.sp
\fBtoxic\&.conf\fR(5)
.SH "LINKS"
.sp
Project page: https://github\&.com/Tox/toxic
.sp
IRC channel: chat\&.freenode\&.net#tox

102
doc/toxic.1.asc Normal file
View File

@ -0,0 +1,102 @@
toxic(1)
========
NAME
----
toxic - CLI client for Tox
SYNOPSIS
--------
*toxic* [-f 'data-file'] [-x] [-4] [-c 'config-file'] [-n 'nodes-file'] [-h]
DESCRIPTION
-----------
toxic is an ncurses-based instant messaging client for Tox which formerly
resided in the Tox core repository, and is now available as a standalone
application.
OPTIONS
-------
-4, --ipv4::
Force IPv4 connection
-b, --debug::
Enable stderr for debugging. Redirect output to
avoid breaking the curses interface and better capture messages.
-c, --config config-file::
Use specified 'config-file' instead of '~/.config/tox/toxic.conf'
-d, --default-locale::
Use default locale
-e, --encrypt-data::
Encrypt an unencrypted data file. An error will occur if this option
is used with an encrypted data file.
-f, --file data-file::
Use specified 'data-file' instead of '~/.config/tox/data'
-h, --help::
Show help message
-n, --nodes nodes-file::
Use specified 'nodes-file' for DHT bootstrap nodes, instead of
'{datadir}/DHTnodes'
-o, --noconnect::
Do not connect to the DHT network
-p, --SOCKS5-proxy::
Use a SOCKS5 proxy: Requires [IP] [port]
-P, --HTTP-proxy::
Use a HTTP proxy: Requires [IP] [port]
-r, --dnslist::
Use specified DNSservers file
-t, --force-tcp::
Force TCP connection (use this with proxies)
-T, --tcp-relay::
Act as a TCP relay server for the network (Note: this uses significantly more bandwidth)
-u, --unencrypt-data::
Unencrypt a data file. A warning will appear if this option is used
with a data file that is already unencrypted.
FILES
-----
{datadir}/DHTnodes::
Default list of DHT bootstrap nodes.
~/.config/tox/data::
Savestate which contains your personal info (nickname, Tox ID, contacts,
etc)
~/.config/tox/toxic.conf::
Configuration file. See *toxic.conf*(5) for more details.
{datadir}/toxic.conf.example::
Configuration example.
BUGS
----
Unicode characters with a width larger than 1 column may cause strange
behaviour. Expect more bugs and bad behaviour: this software is in a
pre-alpha stage.
AUTHORS
-------
JFreegman <JFreegman@gmail.com>
SEE ALSO
--------
*toxic.conf*(5)
LINKS
-----
Project page: <https://github.com/Tox/toxic>
IRC channel: chat.freenode.net#tox

View File

@ -1,111 +1,328 @@
.TH TOXIC.CONF 5 "June 2014" "Toxic v__VERSION__" "User Manual"
.SH NAME
toxic.conf \- Configuration file for toxic(1)
.SH DESCRIPTION
The
.I toxic.conf
file is the main configuration file for
.BR toxic (1)
client.
.SH SYNTAX
.IB <KEY> : <VALUE> ;
'\" t
.\" Title: toxic.conf
.\" Author: [see the "AUTHORS" section]
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
.\" Date: 2015-03-28
.\" Manual: Toxic Manual
.\" Source: toxic __VERSION__
.\" Language: English
.\"
.TH "TOXIC\&.CONF" "5" "2015\-03\-28" "toxic __VERSION__" "Toxic Manual"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" http://bugs.debian.org/507673
.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
.\" disable hyphenation
.nh
.\" disable justification (adjust text to left margin only)
.ad l
.\" -----------------------------------------------------------------
.\" * MAIN CONTENT STARTS HERE *
.\" -----------------------------------------------------------------
.SH "NAME"
toxic.conf \- Configuration file for toxic
.SH "SYNOPSIS"
.sp
~/\&.config/tox/toxic\&.conf
.SH "DESCRIPTION"
.sp
The \fItoxic\&.conf\fR file is the main configuration file for \fBtoxic\fR(1) client\&. It uses syntax accepted by \fBlibconfig\fR\&. Lines starting with "//" are comments and will be ignored\&.
.SH "EXAMPLE"
.sp
.if n \{\
.RS 4
.\}
.nf
// Configuration for interface
ui = {
timestamps = true;
alerts = false;
};
// Configuration for audio
audio = {
input_device = 1;
};
.fi
.if n \{\
.RE
.\}
.SH "OPTIONS"
.PP
Lines starting with "#" are comments and will be ignored.
\fBui\fR
.RS 4
Configuration related to interface elements\&.
.PP
Keys:
.PP
.B time
.RS
Select between 24 and 12 hour time.
.br
Values: 24, 12
\fBtimestamps\fR
.RS 4
Enable or disable timestamps\&. true or false
.RE
.PP
.B timestamps
.RS
Enable or disable timestamps.
.br
Values: 1 to enable, 0 to disable
\fBtime_format\fR
.RS 4
Select between 24 and 12 hour time\&. Specify 24 or 12\&. Setting timestamp_format and log_timestamp_format will override this setting\&.
.RE
.PP
.B autolog
.RS
Enable or disable autologging.
.br
Values: 1 to enable, 0 to disable
\fBtimestamp_format\fR
.RS 4
Time format string for the interface enclosed by double quotes\&. See
\fBdate\fR(1)
.RE
.PP
.B alerts
.RS
Enable or disable terminal alerts on events.
.br
Values: 1 to enable, 0 to disable
\fBlog_timestamp_format\fR
.RS 4
Time format string for logging enclosed by double quotes\&. See
\fBdate\fR(1)
.RE
.PP
.B history_size
.RS
Maximum lines for chat window history.
.br
Values: <INTEGER> (for example: 700)
\fBalerts\fR
.RS 4
Enable or disable terminal alerts on events\&. true or false
.RE
.PP
.B colour_theme
.RS
Select between toxic colour theme and native terminal colours.
.br
Values: 0 for toxic colours, 1 for terminal colours
\fBnative_colors\fR
.RS 4
Select between native terminal colors and toxic color theme\&. true or false
.RE
.PP
.B audio_in_dev
.RS
Audio input device.
.br
Values: <INTEGER> (number correspond to "/lsdev in")
\fBautolog\fR
.RS 4
Enable or disable autologging\&. true or false
.RE
.PP
.B audio_out_dev
.RS
Audio output device.
.br
Values: <INTEGER> (number correspond to "/lsdev out")
\fBshow_typing_other\fR
.RS 4
Show when others are typing in a 1\-on\-1 chat\&. true or false
.RE
.PP
.B download_path
.RS
Default path for downloads.
.br
Values: <STRING> (absolute path where to store downloaded files)
\fBshow_typing_self\fR
.RS 4
Show others when you\(cqre typing in a 1\-on\-1 chat\&. true or false
.RE
.SH EXAMPLES
Default settings from __DATADIR__/toxic.conf.exmaple:
.PP
time:24;
.br
timestamps:1;
.br
autolog:0;
.br
alerts:1;
.br
history_size:700;
.br
colour_theme:0;
.br
audio_in_dev:0;
.br
audio_out_dev:0;
.br
download_path:/home/USERNAME/Downloads/;
.SH FILES
.IP ~/.config/tox/toxic.conf
Main configuration file.
.IP __DATADIR__/toxic.conf.example
Configuration example.
.SH AUTHORS
JFreegman <JFreegman@gmail.com>
.SH SEE ALSO
.BR toxic (1)
.SH LINKS
Project page on github: https://github.com/Tox/toxic
.br
IRC channel on Freenode: chat.freenode.net#tox
\fBshow_welcome_msg\fR
.RS 4
Show welcome message on startup\&. true or false
.RE
.PP
\fBhistory_size\fR
.RS 4
Maximum lines for chat window history\&. Integer value\&. (for example: 700)
.RE
.PP
\fBline_join\fR
.RS 4
Indicator for when someone connects or joins a group\&. Three characters max for line_ settings\&.
.RE
.PP
\fBline_quit\fR
.RS 4
Indicator for when someone disconnects or leaves a group\&.
.RE
.PP
\fBline_alert\fR
.RS 4
Indicator for alert messages\&.
.RE
.PP
\fBline_normal\fR
.RS 4
Indicator for normal messages\&.
.RE
.PP
\fBmplex_away\fR
.RS 4
Set user status when attaching and detaching from GNU screen or tmux\&. true or false
.RE
.PP
\fBmplex_away_note\fR
.RS 4
Status message to set when status is set to away due to screen/tmux detach\&. When attaching, the status message is set back to the original value\&.
.RE
.RE
.PP
\fBaudio\fR
.RS 4
Configuration related to audio devices\&.
.PP
\fBinput_device\fR
.RS 4
Audio input device\&. Integer value\&. Number corresponds to
/lsdev in
.RE
.PP
\fBoutput_device\fR
.RS 4
Audio output device\&. Integer value\&. Number corresponds to
/lsdev out
.RE
.PP
\fBVAD_treshold\fR
.RS 4
Voice Activity Detection treshold\&. Float value\&. Recommended values are around 40\&.0
.RE
.RE
.PP
\fBtox\fR
.RS 4
Configuration related to paths\&.
.PP
\fBdownload_path\fR
.RS 4
Default path for downloads\&. String value\&. Absolute path for downloaded files\&.
.RE
.PP
\fBavatar_path\fR
.RS 4
Path for your avatar (file must be a \&.png and cannot exceed 16\&.3 KiB)
.RE
.PP
\fBchatlogs_path\fR
.RS 4
Default path for chatlogs\&. String value\&. Absolute path for chatlog files\&.
.RE
.RE
.PP
\fBsounds\fR
.RS 4
Configuration related to notification sounds\&. Special value "silent" can be used to disable a specific notification\&.
Each value is a string which corresponds to the absolute path of a wav sound file\&.
.PP
\fBnotif_error\fR
.RS 4
Sound to play when an error occurs\&.
.RE
.PP
\fBself_log_in\fR
.RS 4
Sound to play when you log in\&.
.RE
.PP
\fBself_log_out\fR
.RS 4
Sound to play when you log out\&.
.RE
.PP
\fBuser_log_in\fR
.RS 4
Sound to play when a contact become online\&.
.RE
.PP
\fBuser_log_out\fR
.RS 4
Sound to play when a contact become offline\&.
.RE
.PP
\fBcall_incoming\fR
.RS 4
Sound to play when you receive an incoming call\&.
.RE
.PP
\fBcall_outgoing\fR
.RS 4
Sound to play when you start a call\&.
.RE
.PP
\fBgeneric_message\fR
.RS 4
Sound to play when an event occurs\&.
.RE
.PP
\fBtransfer_pending\fR
.RS 4
Sound to play when you receive a file transfer request\&.
.RE
.PP
\fBtransfer_completed\fR
.RS 4
Sound to play when a file transfer is completed\&.
.RE
.RE
.PP
\fBkeys\fR
.RS 4
Configuration related to user interface interaction\&. Currently supported: Ctrl modified keys, Tab, PAGEUP and PAGEDOWN\&.
Each value is a string which corresponds to a key combination\&.
.PP
\fBnext_tab\fR
.RS 4
Key combination to switch next tab\&.
.RE
.PP
\fBprev_tab\fR
.RS 4
Key combination to switch previous tab\&.
.RE
.PP
\fBscroll_line_up\fR
.RS 4
Key combination to scroll one line up\&.
.RE
.PP
\fBscroll_line_down\fR
.RS 4
Key combination to scroll one line down\&.
.RE
.PP
\fBhalf_page_up\fR
.RS 4
Key combination to scroll half page up\&.
.RE
.PP
\fBhalf_page_down\fR
.RS 4
Key combination to scroll half page down\&.
.RE
.PP
\fBpage_bottom\fR
.RS 4
Key combination to scroll to page bottom\&.
.RE
.PP
\fBpeer_list_up\fR
.RS 4
Key combination to scroll contacts list up\&.
.RE
.PP
\fBpeer_list_down\fR
.RS 4
Key combination to scroll contacts list down\&.
.RE
.PP
\fBtoggle_peerlist\fR
.RS 4
Toggle the peer list on and off\&.
.RE
.RE
.SH "FILES"
.PP
~/\&.config/tox/toxic\&.conf
.RS 4
Main configuration file\&.
.RE
.PP
__DATADIR__/toxic\&.conf\&.example
.RS 4
Configuration example\&.
.RE
.SH "SEE ALSO"
.sp
\fBtoxic\fR(1)
.SH "RESOURCES"
.sp
Project page: https://github\&.com/Tox/toxic
.sp
IRC channel: chat\&.freenode\&.net#tox
.SH "AUTHORS"
.sp
JFreegman <JFreegman@gmail\&.com>

221
doc/toxic.conf.5.asc Normal file
View File

@ -0,0 +1,221 @@
toxic.conf(5)
=============
NAME
----
toxic.conf - Configuration file for toxic
SYNOPSIS
--------
~/.config/tox/toxic.conf
DESCRIPTION
-----------
The 'toxic.conf' file is the main configuration file for *toxic*(1) client.
It uses syntax accepted by *libconfig*.
Lines starting with "//" are comments and will be ignored.
EXAMPLE
-------
----
// Configuration for interface
ui = {
timestamps = true;
alerts = false;
};
// Configuration for audio
audio = {
input_device = 1;
};
----
OPTIONS
-------
*ui*::
Configuration related to interface elements.
*timestamps*;;
Enable or disable timestamps. true or false
*time_format*;;
Select between 24 and 12 hour time. Specify 24 or 12. Setting
timestamp_format and log_timestamp_format will override this setting.
*timestamp_format*;;
Time format string for the interface enclosed by double quotes.
See *date*(1)
*log_timestamp_format*;;
Time format string for logging enclosed by double quotes.
See *date*(1)
*alerts*;;
Enable or disable terminal alerts on events. true or false
*native_colors*;;
Select between native terminal colors and toxic color theme. true or false
*autolog*;;
Enable or disable autologging. true or false
*show_typing_other*;;
Show when others are typing in a 1-on-1 chat. true or false
*show_typing_self*;;
Show others when you're typing in a 1-on-1 chat. true or false
*show_welcome_msg*;;
Show welcome message on startup. true or false
*history_size*;;
Maximum lines for chat window history. Integer value. (for example: 700)
*line_join*;;
Indicator for when someone connects or joins a group.
Three characters max for line_ settings.
*line_quit*;;
Indicator for when someone disconnects or leaves a group.
*line_alert*;;
Indicator for alert messages.
*line_normal*;;
Indicator for normal messages.
*mplex_away*;;
Set user status when attaching and detaching from GNU screen or tmux.
true or false
*mplex_away_note*;;
Status message to set when status is set to away due to screen/tmux
detach. When attaching, the status message is set back to the original
value.
*audio*::
Configuration related to audio devices.
*input_device*;;
Audio input device. Integer value. Number corresponds to `/lsdev in`
*output_device*;;
Audio output device. Integer value. Number corresponds to `/lsdev out`
*VAD_treshold*;;
Voice Activity Detection treshold. Float value. Recommended values are
around 40.0
*tox*::
Configuration related to paths.
*download_path*;;
Default path for downloads. String value. Absolute path for downloaded
files.
*avatar_path*;;
Path for your avatar (file must be a .png and cannot exceed 16.3 KiB)
*chatlogs_path*;;
Default path for chatlogs. String value. Absolute path for chatlog files.
*sounds*::
Configuration related to notification sounds.
Special value "silent" can be used to disable a specific notification. +
Each value is a string which corresponds to the absolute path of a wav
sound file.
*notif_error*;;
Sound to play when an error occurs.
*self_log_in*;;
Sound to play when you log in.
*self_log_out*;;
Sound to play when you log out.
*user_log_in*;;
Sound to play when a contact become online.
*user_log_out*;;
Sound to play when a contact become offline.
*call_incoming*;;
Sound to play when you receive an incoming call.
*call_outgoing*;;
Sound to play when you start a call.
*generic_message*;;
Sound to play when an event occurs.
*transfer_pending*;;
Sound to play when you receive a file transfer request.
*transfer_completed*;;
Sound to play when a file transfer is completed.
*keys*::
Configuration related to user interface interaction.
Currently supported: Ctrl modified keys, Tab, PAGEUP and PAGEDOWN. +
Each value is a string which corresponds to a key combination.
*next_tab*;;
Key combination to switch next tab.
*prev_tab*;;
Key combination to switch previous tab.
*scroll_line_up*;;
Key combination to scroll one line up.
*scroll_line_down*;;
Key combination to scroll one line down.
*half_page_up*;;
Key combination to scroll half page up.
*half_page_down*;;
Key combination to scroll half page down.
*page_bottom*;;
Key combination to scroll to page bottom.
*peer_list_up*;;
Key combination to scroll contacts list up.
*peer_list_down*;;
Key combination to scroll contacts list down.
*toggle_peerlist*;;
Toggle the peer list on and off.
FILES
-----
~/.config/tox/toxic.conf::
Main configuration file.
{datadir}/toxic.conf.example::
Configuration example.
SEE ALSO
--------
*toxic*(1)
RESOURCES
---------
Project page: <https://github.com/Tox/toxic>
IRC channel: chat.freenode.net#tox
AUTHORS
-------
JFreegman <JFreegman@gmail.com>

View File

@ -1,8 +1,25 @@
192.254.75.98 33445 951C88B7E75C867418ACDB5D273821372BB5BD652740BCDF623A4FA293E75D2F
37.187.46.132 33445 A9D98212B3F972BD11DA52BEB0658C326FCCC1BFD49F347F9C2D3D8B61E1B927
192.254.75.102 33445 951C88B7E75C867418ACDB5D273821372BB5BD652740BCDF623A4FA293E75D2F
144.76.60.215 33445 04119E835DF3E78BACF0F84235B300546AF8B936F035185E2A8E9E0A67C8924F
23.226.230.47 33445 A09162D68618E742FFBCA1C2C70385E6679604B2D80EA6E84AD0996A1AC8A074
37.187.20.216 33445 4FD54CFD426A338399767E56FD0F44F5E35FA8C38C8E87C8DC3FEAC0160F8E17
54.199.139.199 33445 7F9C31FE850E97CEFD4C4591DF93FC757C7C12549DDD55F8EEAECC34FE76C029
109.169.46.133 33445 7F31BFC93B8E4016A902144D0B110C3EA97CB7D43F1C4D21BCAE998A7C838821
192.210.149.121 33445 F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67
178.62.125.224 33445 10B20C49ACBD968D7C80F2E8438F92EA51F189F4E70CFBBB2C2C8C799E97F03E
178.21.112.187 33445 4B2C19E924972CB9B57732FB172F8A8604DE13EEDA2A6234E348983344B23057
195.154.119.113 33445 E398A69646B8CEACA9F0B84F553726C1C49270558C57DF5F3C368F05A7D71354
192.210.149.121 33445 F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67
76.191.23.96 33445 93574A3FAB7D612FEA29FD8D67D3DD10DFD07A075A5D62E8AF3DD9F5D0932E11
46.38.239.179 33445 F5A1A38EFB6BD3C2C8AF8B10D85F0F89E931704D349F1D0720C3C4059AF2440A
178.62.250.138 33445 788236D34978D1D5BD822F0A5BEBD2C53C64CC31CD3149350EE27D4D9A2F9B6B
78.225.128.39 33445 7A2306BFBA665E5480AE59B31E116BE9C04DCEFE04D9FE25082316FA34B4DA0C
130.133.110.14 33445 461FA3776EF0FA655F1A05477DF1B3B614F7D6B124F7DB1DD4FE3C08B03B640F
104.167.101.29 33445 5918AC3C06955962A75AD7DF4F80A5D7C34F7DB9E1498D2E0495DE35B3FE8A57
195.154.109.148 33445 391C96CB67AE893D4782B8E4495EB9D89CF1031F48460C06075AA8CE76D50A21
192.3.173.88 33445 3E1FFDEB667BFF549F619EC6737834762124F50A89C8D0DBF1DDF64A2DD6CD1B
205.185.116.116 33445 A179B09749AC826FF01F37A9613F6B57118AE014D4196A0E1105A98F93A54702
198.98.51.198 33445 1D5A5F2F5D6233058BF0259B09622FB40B482E4FA0931EB8FD3AB8E7BF7DAF6F
80.232.246.79 33445 A7A060D553B017D9D8F038E265C7AFB6C70BAAC55070197F9C007432D0038E0F
108.61.165.198 33445 8E7D0B859922EF569298B4D261A8CCB5FEA14FB91ED412A7603A585A25698832
212.71.252.109 33445 C4CEB8C7AC607C6B374E2E782B3C00EA3A63B80D4910B8649CCACDD19F260819
194.249.212.109 33445 3CEE1F054081E7A011234883BC4FC39F661A55B73637A5AC293DDF1251D9432B
194.249.212.109 443 3CEE1F054081E7A011234883BC4FC39F661A55B73637A5AC293DDF1251D9432B
103.38.216.87 33445 601AEE6FC8C17E8CD8F8F1FFC4D4AD84E59A73BE451F037194E7A404E3795320
185.25.116.107 33445 DA4E4ED4B697F2E9B000EEFE3A34B554ACD3F45F5C96EAEA2516DD7FF9AF7B43
192.99.168.140 33445 6A4D0607A296838434A6A7DDF99F50EF9D60A2C510BBF31FE538A25CB6B4652F

2
misc/DNSservers Normal file
View File

@ -0,0 +1,2 @@
utox.org d3154f65d28a5b41a05d4ac7e4b39c6b1c233cc857fb365c56e8392737462a12
toxme.se 5d72c517df6aec54f1e977a6b6f25914ea4cf7277a85027cd9f5196df17e0b13

View File

@ -1,26 +1,102 @@
# 24 or 12 hour time
time:24;
// SAMPLE TOXIC CONFIGURATION
// USES LIBCONFIG-ACCEPTED SYNTAX
# 1 to enable timestamps, 0 to disable
timestamps:1;
ui = {
// true to enable timestamps, false to disable
timestamps=true;
# 1 to enable autologging, 0 to disable
autolog:0;
// true to enable terminal alerts on messages, false to disable
alerts=true;
# 1 to disbale terminal alerts on messages, 0 to enable
alerts:1;
// true to use native terminal colours, false to use toxic default colour theme
native_colors=false;
# maximum lines for chat window history
history_size:700;
// true to enable autologging, false to disable
autolog=false;
# 1 to use native terminal colours, 0 to use toxic default colour theme
colour_theme:0;
// 24 or 12 hour time
time_format=24;
# preferred audio input device; numbers correspond to /lsdev in
audio_in_dev:0;
// timestamp format string according to date/strftime format. Overrides time_format setting
timestamp_format="%H:%M:%S";
# preferred audio output device; numbers correspond to /lsdev out
audio_out_dev:0;
// true to show you when others are typing a message in 1-on-1 chats
show_typing_other=true;
// true to show others when you're typing a message in 1-on-1 chats
show_typing_self=true;
// true to show the welcome message on startup
show_welcome_msg=true;
// maximum lines for chat window history
history_size=700;
// Indicator for display when someone connects or joins a group.
line_join="-->";
// Indicator for display when someone disconnects or leaves a group.
line_quit="<--";
// Indicator for alert messages.
line_alert="-!-";
// Indicator for normal messages.
line_normal="---";
// true to change status based on screen/tmux attach/detach, false to disable
mplex_away=true;
// Status message to use when status set to away due to screen/tmux detach
mplex_away_note="Away from keyboard, be back soon!"
};
audio = {
// preferred audio input device; numbers correspond to /lsdev in
input_device=2;
// preferred audio output device; numbers correspond to /lsdev out
output_device=0;
// default VAD treshold; float (recommended values are around 40)
VAD_treshold=40.0;
};
tox = {
// Path for downloaded files
// download_path="/home/USERNAME/Downloads/";
// Path for your avatar (file must be a .png and cannot exceed 16.3 KiB)
// avatar_path="/home/USERNAME/Pictures/youravatar.png";
// Path for chatlogs
// chatlogs_path="/home/USERNAME/toxic_chatlogs/";
};
// To disable a sound set the path to "silent"
sounds = {
error="__DATADIR__/sounds/ToxicError.wav";
user_log_in="__DATADIR__/sounds/ToxicContactOnline.wav";
user_log_out="__DATADIR__/sounds/ToxicContactOffline.wav";
call_incoming="__DATADIR__/sounds/ToxicIncomingCall.wav";
call_outgoing="__DATADIR__/sounds/ToxicOutgoingCall.wav";
generic_message="__DATADIR__/sounds/ToxicRecvMessage.wav";
transfer_pending="__DATADIR__/sounds/ToxicTransferStart.wav";
transfer_completed="__DATADIR__/sounds/ToxicTransferComplete.wav";
};
// Currently supported: Ctrl modified keys, Tab, PAGEUP and PAGEDOWN (case insensitive)
// Note: All printable keys register as input
keys = {
next_tab="Ctrl+P";
prev_tab="Ctrl+O";
scroll_line_up="PAGEUP";
scroll_line_down="PAGEDOWN";
half_page_up="Ctrl+F";
half_page_down="Ctrl+V";
page_bottom="Ctrl+H";
peer_list_up="Ctrl+[";
peer_list_down="Ctrl+]";
toggle_peerlist="Ctrl+b";
};
# preferred path for downloads
download_path:/home/USERNAME/Downloads/;

11
misc/toxic.desktop Normal file
View File

@ -0,0 +1,11 @@
[Desktop Entry]
Version=1.0
Type=Application
Name=Toxic
Comment=A CLI based Tox client
TryExec=toxic
Exec=toxic
Icon=utilities-terminal
Categories=InstantMessaging;AudioVideo;Network;
Terminal=true
MimeType=x-scheme-handler/tox;

Binary file not shown.

Binary file not shown.

BIN
sounds/ToxicError.wav Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
sounds/ToxicRecvMessage.wav Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

2
sounds/license Normal file
View File

@ -0,0 +1,2 @@
ToxicError.wav, ToxicRecvMessage.wav, ToxicContactOffline.wav, ToxicIncomingCall.wav, ToxicTransferComplete.wav, ToxicContactOnline.wav, ToxicOutgoingCall.wav and ToxicTransferStart.wav
are licensed under the "Creative Commons Attribution 3.0 Unported". All credit attributed to Jfreegman.

View File

@ -27,7 +27,9 @@
#include "chat_commands.h"
#include "global_commands.h"
#include "line_info.h"
#include "notify.h"
#include <stdbool.h>
#include <curses.h>
#include <string.h>
#include <pthread.h>
@ -41,77 +43,77 @@
#else
#include <AL/al.h>
#include <AL/alc.h>
/* compatibility with older versions of OpenAL */
#ifndef ALC_ALL_DEVICES_SPECIFIER
#include <AL/alext.h>
#endif
#endif
#define _cbend pthread_exit(NULL)
#define cbend pthread_exit(NULL)
#define MAX_CALLS 10
#define frame_size (av_DefaultSettings.audio_sample_rate * av_DefaultSettings.audio_frame_duration / 1000)
typedef struct _Call {
pthread_t ttid; /* Transmission thread id */
_Bool ttas, has_output; /* Transmission thread active status (0 - stopped, 1- running) */
uint32_t in_idx, out_idx;
pthread_mutex_t mutex;
} Call;
void set_call(Call* call, _Bool start)
static int set_call(Call* call, bool start)
{
call->in_idx = -1;
call->out_idx = -1;
if ( start ) {
call->ttas = _True;
pthread_mutex_init(&call->mutex, NULL);
call->ttas = true;
if (pthread_mutex_init(&call->mutex, NULL) != 0)
return -1;
}
else {
call->ttid = 0;
pthread_mutex_destroy(&call->mutex);
if (pthread_mutex_destroy(&call->mutex) != 0)
return -1;
}
return 0;
}
struct _ASettings {
struct ASettings {
AudioError errors;
ToxAv *av;
ToxAvCodecSettings cs;
ToxAvCSettings 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 );
void callback_recv_invite ( void* av, int32_t call_index, void *arg );
void callback_recv_ringing ( void* av, int32_t call_index, void *arg );
void callback_recv_starting ( void* av, int32_t call_index, void *arg );
void callback_recv_ending ( void* av, int32_t call_index, void *arg );
void callback_call_started ( void* av, int32_t call_index, void *arg );
void callback_call_canceled ( void* av, int32_t call_index, void *arg );
void callback_call_rejected ( void* av, int32_t call_index, void *arg );
void callback_call_ended ( void* av, int32_t call_index, void *arg );
void callback_requ_timeout ( void* av, int32_t call_index, void *arg );
void callback_peer_timeout ( void* av, int32_t call_index, void *arg );
void callback_media_change ( void* av, int32_t call_index, void *arg );
int stop_transmission(int call_index);
void write_device_callback(ToxAv* av, int32_t call_index, int16_t* data, int size);
void write_device_callback( void* agent, int32_t call_index, const int16_t* PCM, uint16_t size, void* arg );
static void print_err (ToxWindow *self, char *error_str)
static void print_err (ToxWindow *self, const char *error_str)
{
line_info_add(self, NULL, NULL, NULL, error_str, SYS_MSG, 0, 0);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", error_str);
}
ToxAv *init_audio(ToxWindow *self, Tox *tox)
{
{
ASettins.cs = av_DefaultSettings;
ASettins.cs.max_video_height = ASettins.cs.max_video_width = 0;
ASettins.errors = ae_None;
memset(ASettins.calls, 0, sizeof(Call) * 10);
ASettins.errors = ae_None;
memset(ASettins.calls, 0, sizeof(ASettins.calls));
/* Streaming stuff from core */
ASettins.av = toxav_new(tox, MAX_CALLS);
@ -120,28 +122,28 @@ ToxAv *init_audio(ToxWindow *self, Tox *tox)
ASettins.errors |= ae_StartingCoreAudio;
return NULL;
}
if ( init_devices(ASettins.av) == de_InternalError ) {
line_info_add(self, NULL, NULL, NULL, "Failed to init devices", SYS_MSG, 0, 0);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to init devices");
toxav_kill(ASettins.av);
return ASettins.av = 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(ASettins.av, callback_call_started, av_OnStart, self);
toxav_register_callstate_callback(ASettins.av, callback_call_canceled, av_OnCancel, self);
toxav_register_callstate_callback(ASettins.av, callback_call_rejected, av_OnReject, self);
toxav_register_callstate_callback(ASettins.av, callback_call_ended, av_OnEnd, self);
toxav_register_callstate_callback(ASettins.av, 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(ASettins.av, callback_recv_ringing, av_OnRinging, self);
toxav_register_callstate_callback(ASettins.av, callback_recv_starting, av_OnStart, self);
toxav_register_callstate_callback(ASettins.av, callback_recv_ending, av_OnEnd, 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);
toxav_register_audio_recv_callback(ASettins.av, write_device_callback);
toxav_register_callstate_callback(ASettins.av, callback_requ_timeout, av_OnRequestTimeout, self);
toxav_register_callstate_callback(ASettins.av, callback_peer_timeout, av_OnPeerTimeout, self);
//toxav_register_callstate_callback(ASettins.av, callback_media_change, av_OnMediaChange, self);
toxav_register_audio_callback(ASettins.av, write_device_callback, NULL);
return ASettins.av;
}
@ -149,19 +151,19 @@ ToxAv *init_audio(ToxWindow *self, Tox *tox)
void terminate_audio()
{
int i;
for (i = 0; i < MAX_CALLS; i ++)
stop_transmission(i);
for (i = 0; i < MAX_CALLS; ++i)
stop_transmission(&ASettins.calls[i], i);
if ( ASettins.av )
toxav_kill(ASettins.av);
terminate_devices();
}
void read_device_callback (const int16_t* captured, uint32_t size, void* data)
{
int32_t call_index = *((int32_t*)data); /* TODO: Or pass an array of call_idx's */
uint8_t encoded_payload[RTP_PAYLOAD_SIZE];
int32_t payload_size = toxav_prepare_audio_frame(ASettins.av, call_index, encoded_payload, RTP_PAYLOAD_SIZE, captured, size);
if ( payload_size <= 0 || toxav_send_audio(ASettins.av, call_index, encoded_payload, payload_size) < 0 ) {
@ -169,60 +171,77 @@ void read_device_callback (const int16_t* captured, uint32_t size, void* data)
}
}
void write_device_callback(ToxAv* av, int32_t call_index, int16_t* data, int size)
void write_device_callback(void *agent, int32_t call_index, const int16_t* PCM, uint16_t size, void* arg)
{
if (ASettins.calls[call_index].ttas)
write_out(ASettins.calls[call_index].out_idx, data, size, 1);
(void)arg;
(void)agent;
if (call_index >= 0 && ASettins.calls[call_index].ttas) {
ToxAvCSettings csettings = ASettins.cs;
toxav_get_peer_csettings(ASettins.av, call_index, 0, &csettings);
write_out(ASettins.calls[call_index].out_idx, PCM, size, csettings.audio_channels);
}
}
int start_transmission(ToxWindow *self)
int start_transmission(ToxWindow *self, Call *call)
{
if ( !ASettins.av || self->call_idx == -1 ) return -1;
/* Don't provide support for video */
if ( 0 != toxav_prepare_transmission(ASettins.av, self->call_idx, &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_idx, AudioDecoding) ||
!toxav_capability_supported(ASettins.av, self->call_idx, AudioEncoding) )
if ( !self || !ASettins.av || self->call_idx == -1 ) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Could not prepare transmission");
return -1;
set_call(&ASettins.calls[self->call_idx], _True);
if ( open_primary_device(input, &ASettins.calls[self->call_idx].in_idx) != de_None )
line_info_add(self, NULL, NULL, NULL, "Failed to open input device!", SYS_MSG, 0, 0);
if ( register_device_callback(self->call_idx, ASettins.calls[self->call_idx].in_idx,
read_device_callback, &self->call_idx, _True) != de_None)
/* Set VAD as true for all; TODO: Make it more dynamic */
line_info_add(self, NULL, NULL, NULL, "Failed to register input handler!", SYS_MSG, 0, 0);
if ( open_primary_device(output, &ASettins.calls[self->call_idx].out_idx) != de_None ) {
line_info_add(self, NULL, NULL, NULL, "Failed to open output device!", SYS_MSG, 0, 0);
ASettins.calls[self->call_idx].has_output = 0;
}
/* Don't provide support for video */
if ( 0 != toxav_prepare_transmission(ASettins.av, self->call_idx, 0) ) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Could not prepare transmission");
return -1;
}
if ( !toxav_capability_supported(ASettins.av, self->call_idx, av_AudioDecoding) ||
!toxav_capability_supported(ASettins.av, self->call_idx, av_AudioEncoding) )
return -1;
if (set_call(call, true) == -1)
return -1;
ToxAvCSettings csettings;
toxav_get_peer_csettings(ASettins.av, self->call_idx, 0, &csettings);
if ( open_primary_device(input, &call->in_idx,
csettings.audio_sample_rate, csettings.audio_frame_duration, csettings.audio_channels) != de_None )
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to open input device!");
if ( register_device_callback(self->call_idx, call->in_idx,
read_device_callback, &self->call_idx, true) != de_None)
/* Set VAD as true for all; TODO: Make it more dynamic */
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to register input handler!");
if ( open_primary_device(output, &call->out_idx,
csettings.audio_sample_rate, csettings.audio_frame_duration, csettings.audio_channels) != de_None ) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to open output device!");
call->has_output = 0;
}
return 0;
}
int stop_transmission(int call_index)
{
if ( ASettins.calls[call_index].ttas ) {
int stop_transmission(Call *call, int32_t call_index)
{
if ( call->ttas ) {
toxav_kill_transmission(ASettins.av, call_index);
ASettins.calls[call_index].ttas = _False;
if ( ASettins.calls[call_index].in_idx != -1 )
close_device(input, ASettins.calls[call_index].in_idx);
if ( ASettins.calls[call_index].out_idx != -1 )
close_device(output, ASettins.calls[call_index].out_idx);
set_call(&ASettins.calls[call_index], _False);
call->ttas = false;
if ( call->in_idx != -1 )
close_device(input, call->in_idx);
if ( call->out_idx != -1 )
close_device(output, call->out_idx);
if (set_call(call, false) == -1)
return -1;
return 0;
}
return -1;
}
/*
@ -240,80 +259,82 @@ int stop_transmission(int call_index)
#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 )
void callback_recv_invite ( void* av, int32_t call_index, void* arg )
{
CB_BODY(call_index, arg, onInvite);
}
void callback_recv_ringing ( int32_t call_index, void* arg )
void callback_recv_ringing ( void* av, int32_t call_index, void* arg )
{
CB_BODY(call_index, arg, onRinging);
}
void callback_recv_starting ( int32_t call_index, void* arg )
void callback_recv_starting ( void* av, int32_t call_index, void* arg )
{
ToxWindow* windows = arg;
ToxWindow* windows = arg;
int i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i)
if (windows[i].onStarting != NULL && windows[i].call_idx == call_index) {
for (i = 0; i < MAX_WINDOWS_NUM; ++i)
if (windows[i].onStarting != NULL && windows[i].call_idx == 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);
if ( 0 != start_transmission(&windows[i], &ASettins.calls[call_index])) {/* YEAH! */
line_info_add(&windows[i], NULL, NULL, NULL, SYS_MSG, 0, 0 , "Error starting transmission!");
}
return;
}
}
void callback_recv_ending ( int32_t call_index, void* arg )
void callback_recv_ending ( void* av, int32_t call_index, void* arg )
{
CB_BODY(call_index, arg, onEnding);
stop_transmission(call_index);
stop_transmission(&ASettins.calls[call_index], call_index);
}
void callback_recv_error ( int32_t call_index, void* arg )
void callback_call_started ( void* av, int32_t call_index, void* arg )
{
CB_BODY(call_index, arg, onError);
stop_transmission(call_index);
}
void callback_call_started ( int32_t call_index, void* arg )
{
ToxWindow* windows = arg;
ToxWindow* windows = arg;
int i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i)
if (windows[i].onStart != NULL && windows[i].call_idx == call_index) {
for (i = 0; i < MAX_WINDOWS_NUM; ++i)
if (windows[i].onStart != NULL && windows[i].call_idx == 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);
if ( 0 != start_transmission(&windows[i], &ASettins.calls[call_index]) ) {/* YEAH! */
line_info_add(&windows[i], NULL, NULL, NULL, SYS_MSG, 0, 0, "Error starting transmission!");
return;
}
}
}
void callback_call_canceled ( int32_t call_index, void* arg )
void callback_call_canceled ( void* av, int32_t call_index, void* arg )
{
CB_BODY(call_index, arg, onCancel);
/* In case call is active */
stop_transmission(call_index);
stop_transmission(&ASettins.calls[call_index], call_index);
}
void callback_call_rejected ( int32_t call_index, void* arg )
void callback_call_rejected ( void* av, int32_t call_index, void* arg )
{
CB_BODY(call_index, arg, onReject);
}
void callback_call_ended ( int32_t call_index, void* arg )
void callback_call_ended ( void* av, int32_t call_index, void* arg )
{
CB_BODY(call_index, arg, onEnd);
stop_transmission(call_index);
stop_transmission(&ASettins.calls[call_index], call_index);
}
void callback_requ_timeout ( int32_t call_index, void* arg )
void callback_requ_timeout ( void* av, int32_t call_index, void* arg )
{
CB_BODY(call_index, arg, onRequestTimeout);
}
void callback_peer_timeout ( int32_t call_index, void* arg )
void callback_peer_timeout ( void* av, int32_t call_index, void* arg )
{
CB_BODY(call_index, arg, onPeerTimeout);
stop_transmission(call_index);
stop_transmission(&ASettins.calls[call_index], 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);
}
// void callback_media_change(void* av, int32_t call_index, void* arg)
// {
/*... TODO cancel all media change requests */
// }
/*
* End of Callbacks
*/
@ -324,11 +345,10 @@ void callback_peer_timeout ( int32_t call_index, void* arg )
*/
void cmd_call(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
char msg[MAX_STR_SIZE];
char *error_str;
const char *error_str;
if (argc != 0) {
error_str = "Invalid syntax!";
error_str = "Unknown arguments.";
goto on_error;
}
@ -337,35 +357,33 @@ void cmd_call(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
goto on_error;
}
if (!self->stb->is_online) {
if (!self->stb->connection) {
error_str = "Friend is offline.";
goto on_error;
}
ToxAvError error = toxav_call(ASettins.av, &self->call_idx, self->num, TypeAudio, 30);
ToxAvError error = toxav_call(ASettins.av, &self->call_idx, self->num, &ASettins.cs, 30);
if ( error != ErrorNone ) {
if ( error == ErrorAlreadyInCall ) error_str = "Already in a call!";
if ( error != av_ErrorNone ) {
if ( error == av_ErrorAlreadyInCallWithPeer ) error_str = "Already in a call!";
else error_str = "Internal error!";
goto on_error;
}
snprintf(msg, sizeof(msg), "Calling... idx: %d", self->call_idx);
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Calling... idx: %d", self->call_idx);
return;
on_error:
snprintf(msg, sizeof(msg), "%s", error_str);
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
print_err(self, error_str);
}
void cmd_answer(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
char *error_str;
const char *error_str;
if (argc != 0) {
error_str = "Invalid syntax!";
error_str = "Unknown arguments.";
goto on_error;
}
@ -374,11 +392,11 @@ void cmd_answer(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
goto on_error;
}
ToxAvError error = toxav_answer(ASettins.av, self->call_idx, TypeAudio);
ToxAvError error = toxav_answer(ASettins.av, self->call_idx, &ASettins.cs);
if ( error != ErrorNone ) {
if ( error == ErrorInvalidState ) error_str = "Cannot answer in invalid state!";
else if ( error == ErrorNoCall ) error_str = "No incoming call!";
if ( error != av_ErrorNone ) {
if ( error == av_ErrorInvalidState ) error_str = "Cannot answer in invalid state!";
else if ( error == av_ErrorNoCall ) error_str = "No incoming call!";
else error_str = "Internal error!";
goto on_error;
@ -393,10 +411,10 @@ on_error:
void cmd_reject(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
char *error_str;
const char *error_str;
if (argc != 0) {
error_str = "Invalid syntax!";
error_str = "Unknown arguments.";
goto on_error;
}
@ -407,9 +425,9 @@ void cmd_reject(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
ToxAvError error = toxav_reject(ASettins.av, self->call_idx, "Why not?");
if ( error != ErrorNone ) {
if ( error == ErrorInvalidState ) error_str = "Cannot reject in invalid state!";
else if ( error == ErrorNoCall ) error_str = "No incoming call!";
if ( error != av_ErrorNone ) {
if ( error == av_ErrorInvalidState ) error_str = "Cannot reject in invalid state!";
else if ( error == av_ErrorNoCall ) error_str = "No incoming call!";
else error_str = "Internal error!";
goto on_error;
@ -424,10 +442,10 @@ on_error:
void cmd_hangup(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
char *error_str;
const char *error_str;
if (argc != 0) {
error_str = "Invalid syntax!";
error_str = "Unknown arguments.";
goto on_error;
}
@ -436,11 +454,22 @@ void cmd_hangup(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
goto on_error;
}
ToxAvError error = toxav_hangup(ASettins.av, self->call_idx);
ToxAvError error;
if ( error != ErrorNone ) {
if ( error == ErrorInvalidState ) error_str = "Cannot hangup in invalid state!";
else if ( error == ErrorNoCall ) error_str = "No call!";
if (toxav_get_call_state(ASettins.av, self->call_idx) == av_CallInviting) {
error = toxav_cancel(ASettins.av, self->call_idx, self->num,
"Only those who appreciate small things know the beauty that is life");
#ifdef SOUND_NOTIFY
stop_sound(self->ringing_sound);
#endif
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call canceled!");
} else {
error = toxav_hangup(ASettins.av, self->call_idx);
}
if ( error != av_ErrorNone ) {
if ( error == av_ErrorInvalidState ) error_str = "Cannot hangup in invalid state!";
else if ( error == av_ErrorNoCall ) error_str = "No call!";
else error_str = "Internal error!";
goto on_error;
@ -451,43 +480,9 @@ on_error:
print_err (self, error_str);
}
void cmd_cancel(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
char *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_idx, 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 if ( error == ErrorInvalidState ) error_str = "Cannot cancel in invalid state!";
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])
{
char msg[MAX_STR_SIZE];
char *error_str;
const char *error_str;
if ( argc != 1 ) {
if ( argc < 1 ) error_str = "Type must be specified!";
@ -498,15 +493,14 @@ void cmd_list_devices(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*
DeviceType type;
if ( strcmp(argv[1], "in") == 0 ) /* Input devices */
if ( strcasecmp(argv[1], "in") == 0 ) /* Input devices */
type = input;
else if ( strcmp(argv[1], "out") == 0 ) /* Output devices */
else if ( strcasecmp(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);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]);
return;
}
@ -520,8 +514,7 @@ on_error:
/* This changes primary device only */
void cmd_change_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
char msg[MAX_STR_SIZE];
char *error_str;
const char *error_str;
if ( argc != 2 ) {
if ( argc < 1 ) error_str = "Type must be specified!";
@ -540,8 +533,7 @@ void cmd_change_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (
type = output;
else {
snprintf(msg, sizeof(msg), "Invalid type: %s", argv[1]);
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]);
return;
}
@ -553,118 +545,120 @@ void cmd_change_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (
error_str = "Invalid input";
goto on_error;
}
if ( set_primary_device(type, selection) == de_InvalidSelection ) {
error_str="Invalid selection!";
goto on_error;
}
return;
on_error:
print_err (self, error_str);
}
void cmd_ccur_device(WINDOW * window, ToxWindow * self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
char msg[MAX_STR_SIZE];
char *error_str;
void cmd_ccur_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
const char *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;
}
DeviceType 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);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]);
return;
}
char *end;
long int selection = strtol(argv[2], &end, 10);
if ( *end ) {
error_str = "Invalid input";
goto on_error;
}
if ( selection_valid(type, selection) == de_InvalidSelection ) {
error_str="Invalid selection!";
goto on_error;
}
/* If call is active, change device */
if ( self->call_idx > -1) {
Call* this_call = &ASettins.calls[self->call_idx];
if (this_call->ttas) {
ToxAvCSettings csettings;
toxav_get_peer_csettings(ASettins.av, self->call_idx, 0, &csettings);
if (type == output) {
pthread_mutex_lock(&this_call->mutex);
close_device(output, this_call->out_idx);
this_call->has_output = open_device(output, selection, &this_call->out_idx)
this_call->has_output = open_device(output, selection, &this_call->out_idx,
csettings.audio_sample_rate, csettings.audio_frame_duration, csettings.audio_channels)
== de_None ? 1 : 0;
pthread_mutex_unlock(&this_call->mutex);
}
else {
/* TODO: check for failure */
close_device(input, this_call->in_idx);
open_device(input, selection, &this_call->in_idx);
open_device(input, selection, &this_call->in_idx, csettings.audio_sample_rate,
csettings.audio_frame_duration, csettings.audio_channels);
/* Set VAD as true for all; TODO: Make it more dynamic */
register_device_callback(self->call_idx, this_call->in_idx, read_device_callback, &self->call_idx, _True);
register_device_callback(self->call_idx, this_call->in_idx, read_device_callback, &self->call_idx, true);
}
}
}
self->device_selection[type] = selection;
return;
on_error:
print_err (self, error_str);
}
void cmd_mute(WINDOW * window, ToxWindow * self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
char msg[MAX_STR_SIZE];
char *error_str;
void cmd_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
const char *error_str;
if ( argc != 1 ) {
if ( argc < 1 ) error_str = "Type must be specified!";
else error_str = "Only two arguments allowed!";
goto on_error;
}
DeviceType type;
if ( strcmp(argv[1], "in") == 0 ) /* Input devices */
if ( strcasecmp(argv[1], "in") == 0 ) /* Input devices */
type = input;
else if ( strcmp(argv[1], "out") == 0 ) /* Output devices */
else if ( strcasecmp(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);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]);
return;
}
/* If call is active, use this_call values */
if ( self->call_idx > -1) {
Call* this_call = &ASettins.calls[self->call_idx];
pthread_mutex_lock(&this_call->mutex);
pthread_mutex_lock(&this_call->mutex);
if (type == input) {
device_mute(type, this_call->in_idx);
self->chatwin->infobox.in_is_muted ^= 1;
@ -674,50 +668,50 @@ void cmd_mute(WINDOW * window, ToxWindow * self, Tox *m, int argc, char (*argv)[
}
pthread_mutex_unlock(&this_call->mutex);
}
return;
on_error:
print_err (self, error_str);
}
void cmd_sense(WINDOW * window, ToxWindow * self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
void cmd_sense(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
char *error_str;
const char *error_str;
if ( argc != 1 ) {
if ( argc < 1 ) error_str = "Must have value!";
else error_str = "Only two arguments allowed!";
goto on_error;
}
char *end;
float value = strtof(argv[1], &end);
if ( *end ) {
error_str = "Invalid input";
goto on_error;
}
/* Call must be active */
if ( self->call_idx > -1) {
device_set_VAD_treshold(ASettins.calls[self->call_idx].in_idx, value);
self->chatwin->infobox.vad_lvl = value;
}
}
return;
on_error:
print_err (self, error_str);
}
void stop_current_call(ToxWindow* self)
{
{
ToxAvCallState callstate;
if ( ASettins.av != NULL && self->call_idx != -1 &&
( callstate = toxav_get_call_state(ASettins.av, self->call_idx) ) != av_CallNonExistant) {
if ( ASettins.av != NULL && self->call_idx != -1 &&
( callstate = toxav_get_call_state(ASettins.av, self->call_idx) ) != av_CallNonExistent) {
switch (callstate)
{
case av_CallActive:

View File

@ -20,15 +20,13 @@
*
*/
#ifndef _audio_h
#define _audio_h
#ifndef AUDIO_H
#define AUDIO_H
#include <tox/toxav.h>
#include "device.h"
#define VAD_THRESHOLD_DEFAULT 40.0
typedef enum _AudioError {
ae_None = 0,
ae_StartingCaptureDevice = 1 << 0,
@ -36,12 +34,18 @@ typedef enum _AudioError {
ae_StartingCoreAudio = 1 << 2
} AudioError;
/* You will have to pass pointer to first member of 'windows'
* declared in windows.c otherwise undefined behaviour will
*/
typedef struct Call {
pthread_t ttid; /* Transmission thread id */
bool ttas, has_output; /* Transmission thread active status (0 - stopped, 1- running) */
uint32_t in_idx, out_idx;
pthread_mutex_t mutex;
} Call;
/* You will have to pass pointer to first member of 'windows' declared in windows.c */
ToxAv *init_audio(ToxWindow *self, Tox *tox);
void terminate_audio();
int start_transmission(ToxWindow *self, Call *call);
int stop_transmission(Call *call, int call_index);
void stop_current_call(ToxWindow *self);
#endif /* _audio_h */
#endif /* AUDIO_H */

298
src/autocomplete.c Normal file
View File

@ -0,0 +1,298 @@
/* autocomplete.c
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#ifdef __APPLE__
#include <sys/types.h>
#include <sys/dir.h>
#else
#include <dirent.h>
#endif /* ifdef __APPLE__ */
#include "windows.h"
#include "toxic.h"
#include "misc_tools.h"
#include "line_info.h"
#include "execute.h"
#include "configdir.h"
static void print_matches(ToxWindow *self, Tox *m, const void *list, int n_items, int size)
{
if (m)
execute(self->chatwin->history, self, m, "/clear", GLOBAL_COMMAND_MODE);
const char *L = (char *) list;
int i;
for (i = 0; i < n_items; ++i)
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", &L[i * size]);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, ""); /* formatting */
}
/* puts match in match buffer. if more than one match, add first n chars that are identical.
e.g. if matches contains: [foo, foobar, foe] we put fo in matches. */
static void get_str_match(ToxWindow *self, char *match, char (*matches)[MAX_STR_SIZE], int n)
{
if (n == 1) {
strcpy(match, matches[0]);
return;
}
int i;
for (i = 0; i < MAX_STR_SIZE; ++i) {
char ch1 = matches[0][i];
int j;
for (j = 0; j < n; ++j) {
char ch2 = matches[j][i];
if (ch1 != ch2 || !ch1) {
strcpy(match, matches[0]);
match[i] = '\0';
return;
}
}
}
strcpy(match, matches[0]);
}
/* looks for all instances in list that begin with the last entered word in line according to pos,
then fills line with the complete word. e.g. "Hello jo" would complete the line
with "Hello john". If multiple matches, prints out all the matches and semi-completes line.
list is a pointer to the list of strings being compared, n_items is the number of items
in the list, and size is the size of each item in the list.
Returns the difference between the old len and new len of line on success, -1 if error */
int complete_line(ToxWindow *self, const void *list, int n_items, int size)
{
ChatContext *ctx = self->chatwin;
if (ctx->pos <= 0 || ctx->len <= 0 || ctx->pos > ctx->len || ctx->len >= MAX_STR_SIZE || size > MAX_STR_SIZE)
return -1;
const char *L = (char *) list;
const char *endchrs = " ";
char ubuf[MAX_STR_SIZE];
/* work with multibyte string copy of buf for simplicity */
if (wcs_to_mbs_buf(ubuf, ctx->line, sizeof(ubuf)) == -1)
return -1;
/* TODO: generalize this */
bool dir_search = !strncmp(ubuf, "/sendfile", strlen("/sendfile"))
|| !strncmp(ubuf, "/avatar", strlen("/avatar"));
/* isolate substring from space behind pos to pos */
char tmp[MAX_STR_SIZE];
snprintf(tmp, sizeof(tmp), "%s", ubuf);
tmp[ctx->pos] = '\0';
const char *s = dir_search ? strchr(tmp, '\"') : strrchr(tmp, ' ');
char *sub = malloc(strlen(ubuf) + 1);
if (sub == NULL)
exit_toxic_err("failed in complete_line", FATALERR_MEMORY);
if (!s && !dir_search) {
strcpy(sub, tmp);
if (sub[0] != '/')
endchrs = ": ";
} else if (s) {
strcpy(sub, &s[1]);
if (dir_search) {
int sub_len = strlen(sub);
int si = char_rfind(sub, '/', sub_len);
if (si || *sub == '/')
memmove(sub, &sub[si + 1], sub_len - si);
}
}
if (!sub[0]) {
free(sub);
return -1;
}
int s_len = strlen(sub);
const char *str;
int n_matches = 0;
char matches[n_items][MAX_STR_SIZE];
int i = 0;
/* put all list matches in matches array */
for (i = 0; i < n_items; ++i) {
str = &L[i * size];
if (strncasecmp(str, sub, s_len) == 0)
strcpy(matches[n_matches++], str);
}
free(sub);
if (!n_matches)
return -1;
if (!dir_search && n_matches > 1)
print_matches(self, NULL, matches, n_matches, MAX_STR_SIZE);
char match[MAX_STR_SIZE];
get_str_match(self, match, matches, n_matches);
if (dir_search) {
if (n_matches == 1)
endchrs = char_rfind(match, '.', strlen(match)) ? "\"" : "/";
else
endchrs = "";
} else if (n_matches > 1) {
endchrs = "";
}
/* put match in correct spot in buf and append endchars */
int n_endchrs = strlen(endchrs);
int m_len = strlen(match);
int strt = ctx->pos - s_len;
int diff = m_len - s_len + n_endchrs;
if (ctx->len + diff >= MAX_STR_SIZE)
return -1;
char tmpend[MAX_STR_SIZE];
snprintf(tmpend, sizeof(tmpend), "%s", &ubuf[ctx->pos]);
strcpy(&ubuf[strt], match);
strcpy(&ubuf[strt + m_len], endchrs);
strcpy(&ubuf[strt + m_len + n_endchrs], tmpend);
/* convert to widechar and copy back to original buf */
wchar_t newbuf[MAX_STR_SIZE];
if (mbs_to_wcs_buf(newbuf, ubuf, sizeof(newbuf) / sizeof(wchar_t)) == -1)
return -1;
wcscpy(ctx->line, newbuf);
ctx->len += diff;
ctx->pos += diff;
return diff;
}
/* transforms a tab complete starting with the shorthand "~" into the full home directory.*/
static void complt_home_dir(ToxWindow *self, char *path, int pathsize, const char *cmd, int cmdlen)
{
ChatContext *ctx = self->chatwin;
char homedir[MAX_STR_SIZE] = {0};
get_home_dir(homedir, sizeof(homedir));
char newline[MAX_STR_SIZE];
snprintf(newline, sizeof(newline), "%s \"%s%s", cmd, homedir, path + 1);
snprintf(path, pathsize, "%s", &newline[cmdlen]);
wchar_t wline[MAX_STR_SIZE];
if (mbs_to_wcs_buf(wline, newline, sizeof(wline) / sizeof(wchar_t)) == -1)
return;
int newlen = wcslen(wline);
if (ctx->len + newlen >= MAX_STR_SIZE)
return;
wmemcpy(ctx->line, wline, newlen + 1);
ctx->pos = newlen;
ctx->len = ctx->pos;
}
/* attempts to match /command "<incomplete-dir>" line to matching directories.
if only one match, auto-complete line.
return diff between old len and new len of ctx->line, -1 if no matches or > 1 match */
#define MAX_DIRS 512
int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd)
{
char b_path[MAX_STR_SIZE];
char b_name[MAX_STR_SIZE];
char b_cmd[MAX_STR_SIZE];
const wchar_t *tmpline = &line[wcslen(cmd) + 2]; /* start after "/command \"" */
if (wcs_to_mbs_buf(b_path, tmpline, sizeof(b_path)) == -1)
return -1;
if (wcs_to_mbs_buf(b_cmd, cmd, sizeof(b_cmd)) == -1)
return -1;
if (b_path[0] == '~')
complt_home_dir(self, b_path, sizeof(b_path), b_cmd, strlen(b_cmd) + 2);
int si = char_rfind(b_path, '/', strlen(b_path));
if (!b_path[0]) { /* list everything in pwd */
b_path[0] = '.';
b_path[1] = '\0';
} else if (!si && b_path[0] != '/') { /* look for matches in pwd */
char tmp[MAX_STR_SIZE];
snprintf(tmp, sizeof(tmp), ".%s", b_path);
snprintf(b_path, sizeof(b_path), "%s", tmp);
}
snprintf(b_name, sizeof(b_name), "%s", &b_path[si + 1]);
b_path[si + 1] = '\0';
int b_name_len = strlen(b_name);
DIR *dp = opendir(b_path);
if (dp == NULL)
return -1;
char dirnames[MAX_DIRS][NAME_MAX];
struct dirent *entry;
int dircount = 0;
while ((entry = readdir(dp)) && dircount < MAX_DIRS) {
if (strncmp(entry->d_name, b_name, b_name_len) == 0
&& strcmp(".", entry->d_name) && strcmp("..", entry->d_name)) {
snprintf(dirnames[dircount], sizeof(dirnames[dircount]), "%s", entry->d_name);
++dircount;
}
}
closedir(dp);
if (dircount == 0)
return -1;
if (dircount > 1) {
qsort(dirnames, dircount, NAME_MAX, qsort_strcasecmp_hlpr);
print_matches(self, m, dirnames, dircount, NAME_MAX);
}
return complete_line(self, dirnames, dircount, NAME_MAX);
}

42
src/autocomplete.h Normal file
View File

@ -0,0 +1,42 @@
/* autocomplete.h
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef AUTOCOMPLETE_H
#define AUTOCOMPLETE_H
/* looks for all instances in list that begin with the last entered word in line according to pos,
then fills line with the complete word. e.g. "Hello jo" would complete the line
with "Hello john". If multiple matches, prints out all the matches and semi-completes line.
list is a pointer to the list of strings being compared, n_items is the number of items
in the list, and size is the size of each item in the list.
Returns the difference between the old len and new len of line on success, -1 if error */
int complete_line(ToxWindow *self, const void *list, int n_items, int size);
/* attempts to match /command "<incomplete-dir>" line to matching directories.
if only one match, auto-complete line.
return diff between old len and new len of ctx->line, -1 if no matches or > 1 match */
int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd);
#endif /* #define AUTOCOMPLETE_H */

205
src/avatars.c Normal file
View File

@ -0,0 +1,205 @@
/* avatars.c
*
*
* Copyright (C) 2015 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 <stdio.h>
#include <string.h>
#include "misc_tools.h"
#include "file_transfers.h"
#include "friendlist.h"
#include "avatars.h"
extern FriendsList Friends;
static struct Avatar {
char name[TOX_MAX_FILENAME_LENGTH + 1];
size_t name_len;
char path[PATH_MAX + 1];
size_t path_len;
off_t size;
} Avatar;
static void avatar_clear(void)
{
memset(&Avatar, 0, sizeof(struct Avatar));
}
/* Sends avatar to friendnum.
*
* Returns 0 on success.
* Returns -1 on failure.
*/
int avatar_send(Tox *m, uint32_t friendnum)
{
TOX_ERR_FILE_SEND err;
uint32_t filenum = tox_file_send(m, friendnum, TOX_FILE_KIND_AVATAR, (size_t) Avatar.size,
NULL, (uint8_t *) Avatar.name, Avatar.name_len, &err);
if (Avatar.size == 0)
return 0;
if (err != TOX_ERR_FILE_SEND_OK) {
fprintf(stderr, "tox_file_send failed for friendnumber %d (error %d)\n", friendnum, err);
return -1;
}
struct FileTransfer *ft = new_file_transfer(NULL, friendnum, filenum, FILE_TRANSFER_SEND, TOX_FILE_KIND_AVATAR);
if (!ft)
return -1;
ft->file = fopen(Avatar.path, "r");
if (ft->file == NULL)
return -1;
snprintf(ft->file_name, sizeof(ft->file_name), "%s", Avatar.name);
ft->file_size = Avatar.size;
return 0;
}
/* Sends avatar to all friends */
static void avatar_send_all(Tox *m)
{
size_t i;
for (i = 0; i < Friends.max_idx; ++i) {
if (Friends.list[i].connection_status != TOX_CONNECTION_NONE)
avatar_send(m, Friends.list[i].num);
}
}
/* Sets avatar to path and sends it to all online contacts.
*
* Returns 0 on success.
* Returns -1 on failure.
*/
int avatar_set(Tox *m, const char *path, size_t path_len)
{
if (path_len == 0 || path_len >= sizeof(Avatar.path))
return -1;
FILE *fp = fopen(path, "rb");
if (fp == NULL)
return -1;
char PNG_signature[8] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
if (check_file_signature(PNG_signature, sizeof(PNG_signature), fp) != 0) {
fclose(fp);
return -1;
}
fclose(fp);
off_t size = file_size(path);
if (size == 0 || size > MAX_AVATAR_FILE_SIZE)
return -1;
get_file_name(Avatar.name, sizeof(Avatar.name), path);
Avatar.name_len = strlen(Avatar.name);
snprintf(Avatar.path, sizeof(Avatar.path), "%s", path);
Avatar.path_len = path_len;
Avatar.size = size;
avatar_send_all(m);
return 0;
}
/* Unsets avatar and sends to all online contacts.
*
* Returns 0 on success.
* Returns -1 on failure.
*/
void avatar_unset(Tox *m)
{
avatar_clear();
avatar_send_all(m);
}
void on_avatar_file_control(Tox *m, struct FileTransfer *ft, TOX_FILE_CONTROL control)
{
switch (control) {
case TOX_FILE_CONTROL_RESUME:
if (ft->state == FILE_TRANSFER_PENDING) {
ft->state = FILE_TRANSFER_STARTED;
} else if (ft->state == FILE_TRANSFER_PAUSED) {
ft->state = FILE_TRANSFER_STARTED;
}
break;
case TOX_FILE_CONTROL_PAUSE:
ft->state = FILE_TRANSFER_PAUSED;
break;
case TOX_FILE_CONTROL_CANCEL:
close_file_transfer(NULL, m, ft, -1, NULL, silent);
break;
}
}
void on_avatar_chunk_request(Tox *m, struct FileTransfer *ft, uint64_t position, size_t length)
{
if (ft->state != FILE_TRANSFER_STARTED)
return;
if (length == 0) {
close_file_transfer(NULL, m, ft, -1, NULL, silent);
return;
}
if (ft->file == NULL) {
close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent);
return;
}
if (ft->position != position) {
if (fseek(ft->file, position, SEEK_SET) == -1) {
close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent);
return;
}
ft->position = position;
}
uint8_t send_data[length];
size_t send_length = fread(send_data, 1, sizeof(send_data), ft->file);
if (send_length != length) {
close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent);
return;
}
TOX_ERR_FILE_SEND_CHUNK err;
tox_file_send_chunk(m, ft->friendnum, ft->filenum, position, send_data, send_length, &err);
if (err != TOX_ERR_FILE_SEND_CHUNK_OK)
fprintf(stderr, "tox_file_send_chunk failed in avatar callback (error %d)\n", err);
ft->position += send_length;
ft->last_keep_alive = get_unix_time();
}

52
src/avatars.h Normal file
View File

@ -0,0 +1,52 @@
/* avatars.h
*
*
* Copyright (C) 2015 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 AVATARS_H
#define AVATARS_H
#define MAX_AVATAR_FILE_SIZE 65536
/* Sends avatar to friendnum.
*
* Returns 0 on success.
* Returns -1 on failure.
*/
int avatar_send(Tox *m, uint32_t friendnum);
/* Sets avatar to path and sends it to all online contacts.
*
* Returns 0 on success.
* Returns -1 on failure.
*/
int avatar_set(Tox *m, const char *path, size_t length);
/* Unsets avatar and sends to all online contacts.
*
* Returns 0 on success.
* Returns -1 on failure.
*/
void avatar_unset(Tox *m);
void on_avatar_chunk_request(Tox *m, struct FileTransfer *ft, uint64_t position, size_t length);
void on_avatar_file_control(Tox *m, struct FileTransfer *ft, TOX_FILE_CONTROL control);
#endif /* AVATARS_H */

File diff suppressed because it is too large Load Diff

View File

@ -20,13 +20,16 @@
*
*/
#ifndef CHAT_H_6489PZ13
#define CHAT_H_6489PZ13
#ifndef CHAT_H
#define CHAT_H
#include "windows.h"
#include "toxic.h"
void kill_chat_window(ToxWindow *self);
/* set CTRL to -1 if we don't want to send a control signal.
set msg to NULL if we don't want to display a message */
void chat_close_file_receiver(Tox *m, int filenum, int friendnum, int CTRL);
void kill_chat_window(ToxWindow *self, Tox *m);
ToxWindow new_chat(Tox *m, int32_t friendnum);
#endif /* end of include guard: CHAT_H_6489PZ13 */
#endif /* end of include guard: CHAT_H */

View File

@ -30,201 +30,281 @@
#include "execute.h"
#include "line_info.h"
#include "groupchat.h"
#include "chat.h"
#include "file_transfers.h"
extern ToxWindow *prompt;
extern FriendsList Friends;
extern ToxicFriend friends[MAX_FRIENDS_NUM];
void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 2) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Requires type in|out and the file ID.");
return;
}
extern FileSender file_senders[MAX_FILES];
extern uint8_t max_file_senders_index;
char msg[MAX_STR_SIZE];
const char *inoutstr = argv[1];
int idx = atoi(argv[2]);
if (idx >= MAX_FILES || idx < 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
return;
}
struct FileTransfer *ft = NULL;
/* cancel an incoming file transfer */
if (strcasecmp(inoutstr, "in") == 0) {
ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_RECV);
} else if (strcasecmp(inoutstr, "out") == 0) {
ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_SEND);
} else {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type must be 'in' or 'out'.");
return;
}
if (!ft) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
return;
}
if (ft->state == FILE_TRANSFER_INACTIVE) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
return;
}
snprintf(msg, sizeof(msg), "File transfer for '%s' aborted.", ft->file_name);
close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, silent);
}
void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
char *errmsg;
if (argc < 1) {
errmsg = "Invalid syntax";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group number required.");
return;
}
int groupnum = atoi(argv[1]);
if (groupnum == 0 && strcmp(argv[1], "0")) { /* atoi returns 0 value on invalid input */
errmsg = "Invalid syntax.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid group number.");
return;
}
if (tox_invite_friend(m, self->num, groupnum) == -1) {
errmsg = "Failed to invite contact to group.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to invite contact to group.");
return;
}
char msg[MAX_STR_SIZE];
snprintf(msg, sizeof(msg), "Invited contact to Group %d.", groupnum);
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invited contact to Group %d.", groupnum);
}
void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
char *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);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open.");
return;
}
char *groupkey = friends[self->num].groupchat_key;
const char *groupkey = Friends.list[self->num].group_invite.key;
uint16_t length = Friends.list[self->num].group_invite.length;
uint8_t type = Friends.list[self->num].group_invite.type;
if (!friends[self->num].groupchat_pending) {
errmsg = "No pending group chat invite.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
if (!Friends.list[self->num].group_invite.pending) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending group chat invite.");
return;
}
int groupnum = tox_join_groupchat(m, self->num, (uint8_t *) groupkey);
int groupnum = -1;
if (type == TOX_GROUPCHAT_TYPE_TEXT)
groupnum = tox_join_groupchat(m, self->num, (uint8_t *) groupkey, length);
#ifdef AUDIO
else
groupnum = toxav_join_av_groupchat(m, self->num, (uint8_t *) groupkey, length,
NULL, NULL);
#endif
if (groupnum == -1) {
errmsg = "Group chat instance failed to initialize.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize.");
return;
}
if (init_groupchat_win(prompt, m, groupnum) == -1) {
errmsg = "Group chat window failed to initialize.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
if (init_groupchat_win(prompt, m, groupnum, type) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize.");
tox_del_groupchat(m, groupnum);
return;
}
}
void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
char *errmsg;
if (argc != 1) {
errmsg = "Invalid syntax.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File ID required.");
return;
}
uint8_t filenum = atoi(argv[1]);
int idx = atoi(argv[1]);
if ((filenum == 0 && strcmp(argv[1], "0")) || filenum >= MAX_FILES) {
errmsg = "No pending file transfers with that number.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
if ((idx == 0 && strcmp(argv[1], "0")) || idx >= MAX_FILES) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
return;
}
if (!friends[self->num].file_receiver.pending[filenum]) {
errmsg = "No pending file transfers with that number.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
struct FileTransfer *ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_RECV);
if (!ft) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
return;
}
char *filename = friends[self->num].file_receiver.filenames[filenum];
if (tox_file_send_control(m, self->num, 1, filenum, TOX_FILECONTROL_ACCEPT, 0, 0) == 0) {
char 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[filenum] = self->chatwin->hst->line_end->id + 1;
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);
if (ft->state != FILE_TRANSFER_PENDING) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
return;
}
friends[self->num].file_receiver.pending[filenum] = false;
if ((ft->file = fopen(ft->file_path, "a")) == NULL) {
const char *msg = "File transfer failed: Invalid file path.";
close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
return;
}
TOX_ERR_FILE_CONTROL err;
tox_file_control(m, self->num, ft->filenum, TOX_FILE_CONTROL_RESUME, &err);
if (err != TOX_ERR_FILE_CONTROL_OK)
goto on_recv_error;
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Saving file [%d] as: '%s'", idx, ft->file_path);
/* prep progress bar line */
char progline[MAX_STR_SIZE];
init_progress_bar(progline);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", progline);
ft->line_id = self->chatwin->hst->line_end->id + 2;
ft->state = FILE_TRANSFER_STARTED;
return;
on_recv_error:
switch (err) {
case TOX_ERR_FILE_CONTROL_FRIEND_NOT_FOUND:
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Friend not found.");
return;
case TOX_ERR_FILE_CONTROL_FRIEND_NOT_CONNECTED:
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Friend is not online.");
return;
case TOX_ERR_FILE_CONTROL_NOT_FOUND:
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Invalid filenumber.");
return;
case TOX_ERR_FILE_CONTROL_SENDQ:
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Connection error.");
return;
default:
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed (error %d)\n", err);
return;
}
}
void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
char *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;
}
const char *errmsg = NULL;
if (argc < 1) {
errmsg = "Invalid syntax.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path required.");
return;
}
char *path = argv[1];
if (path[0] != '\"') {
errmsg = "File path must be enclosed in quotes.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
if (argv[1][0] != '\"') {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path must be enclosed in quotes.");
return;
}
++path;
/* remove opening and closing quotes */
char path[MAX_STR_SIZE];
snprintf(path, sizeof(path), "%s", &argv[1][1]);
int path_len = strlen(path) - 1;
path[path_len] = '\0';
if (path_len > MAX_STR_SIZE) {
errmsg = "File path exceeds character limit.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
if (path_len >= MAX_STR_SIZE) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path exceeds character limit.");
return;
}
FILE *file_to_send = fopen(path, "r");
if (file_to_send == NULL) {
errmsg = "File not found.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File not found.");
return;
}
fseek(file_to_send, 0, SEEK_END);
uint64_t filesize = ftell(file_to_send);
fseek(file_to_send, 0, SEEK_SET);
off_t filesize = file_size(path);
char filename[MAX_STR_SIZE];
get_file_name(filename, sizeof(filename), path);
int filenum = tox_new_file_sender(m, self->num, filesize, (const uint8_t *) filename, strlen(filename));
if (filenum == -1) {
errmsg = "Error sending file.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
if (filesize == 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file.");
fclose(file_to_send);
return;
}
int i;
char file_name[TOX_MAX_FILENAME_LENGTH];
size_t namelen = get_file_name(file_name, sizeof(file_name), path);
for (i = 0; i < MAX_FILES; ++i) {
if (!file_senders[i].active) {
memcpy(file_senders[i].pathname, path, path_len + 1);
file_senders[i].active = true;
file_senders[i].toxwin = self;
file_senders[i].file = file_to_send;
file_senders[i].filenum = filenum;
file_senders[i].friendnum = self->num;
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);
TOX_ERR_FILE_SEND err;
uint32_t filenum = tox_file_send(m, self->num, TOX_FILE_KIND_DATA, (uint64_t) filesize, NULL,
(uint8_t *) file_name, namelen, &err);
char 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 (err != TOX_ERR_FILE_SEND_OK)
goto on_send_error;
if (i == max_file_senders_index)
++max_file_senders_index;
struct FileTransfer *ft = new_file_transfer(self, self->num, filenum, FILE_TRANSFER_SEND, TOX_FILE_KIND_DATA);
return;
}
if (!ft) {
err = TOX_ERR_FILE_SEND_TOO_MANY;
goto on_send_error;
}
memcpy(ft->file_name, file_name, namelen + 1);
ft->file = file_to_send;
ft->file_size = filesize;
tox_file_get_file_id(m, self->num, filenum, ft->file_id, NULL);
char sizestr[32];
bytes_convert_str(sizestr, sizeof(sizestr), filesize);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Sending file [%d]: '%s' (%s)", filenum, file_name, sizestr);
return;
on_send_error:
switch (err) {
case TOX_ERR_FILE_SEND_FRIEND_NOT_FOUND:
errmsg = "File transfer failed: Invalid friend.";
break;
case TOX_ERR_FILE_SEND_FRIEND_NOT_CONNECTED:
errmsg = "File transfer failed: Friend is offline.";
break;
case TOX_ERR_FILE_SEND_NAME_TOO_LONG:
errmsg = "File transfer failed: Filename is too long.";
break;
case TOX_ERR_FILE_SEND_TOO_MANY:
errmsg = "File transfer failed: Too many concurrent file transfers.";
break;
default:
errmsg = "File transfer failed.";
break;
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", errmsg);
tox_file_control(m, self->num, filenum, TOX_FILE_CONTROL_CANCEL, NULL);
fclose(file_to_send);
}

View File

@ -20,18 +20,19 @@
*
*/
#ifndef _chat_commands_h
#define _chat_commands_h
#ifndef CHAT_COMMANDS_H
#define CHAT_COMMANDS_H
#include "windows.h"
#include "toxic.h"
void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_groupinvite(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_join_group(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_savefile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_sendfile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
#ifdef _SUPPORT_AUDIO
#ifdef AUDIO
void cmd_call(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_answer(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_reject(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
@ -40,6 +41,6 @@ void cmd_cancel(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZ
void cmd_ccur_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_mute(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_sense(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
#endif /* _SUPPORT_AUDIO */
#endif /* AUDIO */
#endif /* #define _chat_commands_h */
#endif /* #define CHAT_COMMANDS_H */

View File

@ -29,53 +29,55 @@
#include <unistd.h>
#include <pwd.h>
#include "toxic.h"
#include "configdir.h"
/* get the user's home directory */
void get_home_dir(char *home, int size)
{
struct passwd pwd;
struct passwd *pwdbuf;
const char *hmstr;
char buf[NSS_BUFLEN_PASSWD];
int rc = getpwuid_r(getuid(), &pwd, buf, NSS_BUFLEN_PASSWD, &pwdbuf);
if (rc == 0) {
hmstr = pwd.pw_dir;
} else {
hmstr = getenv("HOME");
if (hmstr == NULL)
return;
snprintf(buf, sizeof(buf), "%s", hmstr);
hmstr = buf;
}
snprintf(home, size, "%s", hmstr);
}
/**
* @brief Get the users config directory.
* @brief Get the user's config directory.
*
* This is without a trailing slash.
* This is without a trailing slash. Resulting string must be freed.
*
* @return The users config dir or NULL on error.
*/
char *get_user_config_dir(void)
{
char home[NSS_BUFLEN_PASSWD] = {0};
get_home_dir(home, sizeof(home));
char *user_config_dir;
#ifndef NSS_BUFLEN_PASSWD
#define NSS_BUFLEN_PASSWD 4096
#endif /* NSS_BUFLEN_PASSWD */
struct passwd pwd;
struct passwd *pwdbuf;
const char *home;
char buf[NSS_BUFLEN_PASSWD];
size_t len;
int rc;
rc = getpwuid_r(getuid(), &pwd, buf, NSS_BUFLEN_PASSWD, &pwdbuf);
if (rc == 0) {
home = pwd.pw_dir;
} else {
home = getenv("HOME");
if (home == NULL) {
return NULL;
}
/* env variables can be tainted */
snprintf(buf, sizeof(buf), "%s", home);
home = buf;
}
# if defined(__APPLE__)
len = strlen(home) + strlen("/Library/Application Support") + 1;
user_config_dir = malloc(len);
if (user_config_dir == NULL) {
if (user_config_dir == NULL)
return NULL;
}
snprintf(user_config_dir, len, "%s/Library/Application Support", home);
# else /* __APPLE__ */
@ -86,9 +88,8 @@ char *get_user_config_dir(void)
len = strlen(home) + strlen("/.config") + 1;
user_config_dir = malloc(len);
if (user_config_dir == NULL) {
if (user_config_dir == NULL)
return NULL;
}
snprintf(user_config_dir, len, "%s/.config", home);
} else {
@ -98,34 +99,48 @@ char *get_user_config_dir(void)
# endif /* __APPLE__ */
return user_config_dir;
#undef NSS_BUFLEN_PASSWD
}
/*
* Creates the config directory.
* Creates the config and chatlog directories.
*/
int create_user_config_dir(char *path)
int create_user_config_dirs(char *path)
{
int mkdir_err;
mkdir_err = mkdir(path, 0700);
struct stat buf;
int mkdir_err = mkdir(path, 0700);
if (mkdir_err && (errno != EEXIST || stat(path, &buf) || !S_ISDIR(buf.st_mode))) {
if (mkdir_err && (errno != EEXIST || stat(path, &buf) || !S_ISDIR(buf.st_mode)))
return -1;
}
char *fullpath = malloc(strlen(path) + strlen(CONFIGDIR) + 1);
char *logpath = malloc(strlen(path) + strlen(LOGDIR) + 1);
if (fullpath == NULL || logpath == NULL)
exit_toxic_err("failed in load_data_structures", FATALERR_MEMORY);
strcpy(fullpath, path);
strcat(fullpath, CONFIGDIR);
strcpy(logpath, path);
strcat(logpath, LOGDIR);
mkdir_err = mkdir(fullpath, 0700);
if (mkdir_err && (errno != EEXIST || stat(fullpath, &buf) || !S_ISDIR(buf.st_mode))) {
free(fullpath);
free(logpath);
return -1;
}
mkdir_err = mkdir(logpath, 0700);
if (mkdir_err && (errno != EEXIST || stat(logpath, &buf) || !S_ISDIR(buf.st_mode))) {
free(fullpath);
free(logpath);
return -1;
}
free(logpath);
free(fullpath);
return 0;
}

View File

@ -20,17 +20,22 @@
*
*/
#ifndef _configdir_h
#define _configdir_h
#ifndef CONFIGDIR_H
#define CONFIGDIR_H
#ifndef NSS_BUFLEN_PASSWD
#define NSS_BUFLEN_PASSWD 4096
#endif
#define CONFIGDIR "/tox/"
#define LOGDIR "/tox/chatlogs/"
#ifndef S_ISDIR
#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
#endif
char *get_user_config_dir(void);
void get_home_dir(char *home, int size);
int create_user_config_dirs(char *path);
int create_user_config_dir(char *path);
#endif /* #define _configdir_h */
#endif /* #define CONFIGDIR_H */

View File

@ -20,8 +20,14 @@
*
*/
#include "device.h"
#ifdef AUDIO
#include "audio_call.h"
#endif
#include "line_info.h"
#include "settings.h"
#ifdef __APPLE__
#include <OpenAL/al.h>
@ -29,44 +35,53 @@
#else
#include <AL/al.h>
#include <AL/alc.h>
#endif
/* compatibility with older versions of OpenAL */
#ifndef ALC_ALL_DEVICES_SPECIFIER
#include <AL/alext.h>
#endif /* ALC_ALL_DEVICES_SPECIFIER */
#endif /* __APPLE__ */
#include <stdbool.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <tox/toxav.h>
#define openal_bufs 5
#define sample_rate 48000
#define inline__ inline __attribute__((always_inline))
#define frame_size (av_DefaultSettings.audio_sample_rate * av_DefaultSettings.audio_frame_duration / 1000)
typedef struct _Device {
extern struct user_settings *user_settings;
typedef struct Device {
ALCdevice *dhndl; /* Handle of device selected/opened */
ALCcontext *ctx; /* Device context */
DataHandleCallback cb; /* Use this to handle data from input device usually */
void* cb_data; /* Data to be passed to callback */
int32_t call_idx; /* ToxAv call index */
uint32_t source, buffers[openal_bufs]; /* Playback source/buffers */
size_t ref_count;
uint32_t source, buffers[OPENAL_BUFS]; /* Playback source/buffers */
uint32_t ref_count;
int32_t selection;
_Bool enable_VAD;
_Bool muted;
float VAD_treshold; /* 40 is usually recommended value */
bool enable_VAD;
bool muted;
pthread_mutex_t mutex[1];
uint32_t sample_rate;
uint32_t frame_duration;
int32_t sound_mode;
#ifdef AUDIO
float VAD_treshold; /* 40 is usually recommended value */
#endif
} Device;
const char *ddevice_names[2]; /* Default device */
const char *devices_names[2][MAX_DEVICES]; /* Container of available devices */
static int size[2]; /* Size of above containers */
Device *running[2][MAX_DEVICES]; /* Running devices */
Device *running[2][MAX_DEVICES] = {{NULL}}; /* Running devices */
uint32_t primary_device[2]; /* Primary device */
#ifdef AUDIO
static ToxAv* av = NULL;
#endif /* AUDIO */
/* q_mutex */
#define lock pthread_mutex_lock(&mutex)
@ -74,189 +89,214 @@ static ToxAv* av = NULL;
pthread_mutex_t mutex;
_Bool thread_running = _True,
thread_paused = _True; /* Thread control */
bool thread_running = true,
thread_paused = true; /* Thread control */
void* thread_poll(void*);
/* Meet devices */
#ifdef AUDIO
DeviceError init_devices(ToxAv* av_)
#else
DeviceError init_devices()
#endif /* AUDIO */
{
const char *stringed_device_list;
size[input] = 0;
if ( (stringed_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER)) ) {
ddevice_names[input] = alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
for ( ; *stringed_device_list && size[input] < MAX_DEVICES; ++size[input] ) {
devices_names[input][size[input]] = stringed_device_list;
devices_names[input][size[input]] = stringed_device_list;
stringed_device_list += strlen( stringed_device_list ) + 1;
}
}
size[output] = 0;
if ( (stringed_device_list = alcGetString(NULL, ALC_DEVICE_SPECIFIER)) ) {
ddevice_names[output] = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
for ( ; *stringed_device_list && size[output] < MAX_DEVICES; ++size[output] ) {
devices_names[output][size[output]] = stringed_device_list;
devices_names[output][size[output]] = stringed_device_list;
stringed_device_list += strlen( stringed_device_list ) + 1;
}
}
// Start poll thread
pthread_mutex_init(&mutex, NULL);
if (pthread_mutex_init(&mutex, NULL) != 0)
return de_InternalError;
pthread_t thread_id;
if ( pthread_create(&thread_id, NULL, thread_poll, NULL) != 0 || pthread_detach(thread_id) != 0)
return de_InternalError;
if ( pthread_create(&thread_id, NULL, thread_poll, NULL) != 0 || pthread_detach(thread_id) != 0)
return de_InternalError;
#ifdef AUDIO
av = av_;
return (DeviceError) ae_None;
#endif /* AUDIO */
return (DeviceError) de_None;
}
DeviceError terminate_devices()
{
/* Cleanup if needed */
lock;
thread_running = false;
unlock;
usleep(20000);
pthread_mutex_destroy(&mutex);
return (DeviceError) ae_None;
if (pthread_mutex_destroy(&mutex) != 0)
return (DeviceError) de_InternalError;
return (DeviceError) de_None;
}
DeviceError device_mute(DeviceType type, uint32_t device_idx)
{
if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
lock;
Device* device = running[type][device_idx];
if (!device) {
if (!device) {
unlock;
return de_DeviceNotActive;
}
device->muted = !device->muted;
unlock;
return de_None;
}
#ifdef AUDIO
DeviceError device_set_VAD_treshold(uint32_t device_idx, float value)
{
if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
lock;
Device* device = running[input][device_idx];
if (!device) {
if (!device) {
unlock;
return de_DeviceNotActive;
}
device->VAD_treshold = value;
unlock;
return de_None;
}
#endif
DeviceError set_primary_device(DeviceType type, int32_t selection)
{
if (size[type] <= selection || selection < 0) return de_InvalidSelection;
primary_device[type] = selection;
return de_None;
}
DeviceError open_primary_device(DeviceType type, uint32_t* device_idx)
DeviceError open_primary_device(DeviceType type, uint32_t* device_idx, uint32_t sample_rate, uint32_t frame_duration, uint8_t channels)
{
return open_device(type, primary_device[type], device_idx);
return open_device(type, primary_device[type], device_idx, sample_rate, frame_duration, channels);
}
void get_primary_device_name(DeviceType type, char *buf, int size)
{
memcpy(buf, ddevice_names[type], size);
}
// TODO: generate buffers separately
DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx)
DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx, uint32_t sample_rate, uint32_t frame_duration, uint8_t channels)
{
if (size[type] <= selection || selection < 0) return de_InvalidSelection;
if (channels != 1 && channels != 2) return de_UnsupportedMode;
lock;
const uint32_t frame_size = (sample_rate * frame_duration / 1000);
uint32_t i;
for (i = 0; i < MAX_DEVICES && running[type][i] != NULL; i ++);
for (i = 0; i < MAX_DEVICES && running[type][i] != NULL; ++i);
if (i == MAX_DEVICES) { unlock; return de_AllDevicesBusy; }
else *device_idx = i;
Device* device = running[type][*device_idx] = calloc(1, sizeof(Device));;
device->selection = selection;
for (i = 0; i < *device_idx; i ++) { /* Check if any previous has the same selection */
if ( running[type][i]->selection == selection ) {
device->dhndl = running[type][i]->dhndl;
if (type == output) {
device->ctx = running[type][i]->ctx;
memcpy(device->buffers, running[type][i]->buffers, sizeof(running[type][i]->buffers));
device->source = running[type][i]->source;
}
device->ref_count++;
pthread_mutex_init(device->mutex, NULL);
for (i = 0; i < MAX_DEVICES; i ++) { /* Check if any device has the same selection */
if ( running[type][i] && running[type][i]->selection == selection ) {
// printf("a%d-%d:%p ", selection, i, running[type][i]->dhndl);
running[type][*device_idx] = running[type][i];
running[type][i]->ref_count ++;
unlock;
return de_None;
}
}
if (type == input) {
device->dhndl = alcCaptureOpenDevice(devices_names[type][selection],
av_DefaultSettings.audio_sample_rate, AL_FORMAT_MONO16, frame_size * 2);
device->VAD_treshold = VAD_THRESHOLD_DEFAULT;
Device* device = running[type][*device_idx] = calloc(1, sizeof(Device));
device->selection = selection;
device->sample_rate = sample_rate;
device->frame_duration = frame_duration;
device->sound_mode = channels == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
if (pthread_mutex_init(device->mutex, NULL) != 0) {
free(device);
unlock;
return de_InternalError;
}
else {
if (type == input) {
device->dhndl = alcCaptureOpenDevice(devices_names[type][selection],
sample_rate, device->sound_mode, frame_size * 2);
#ifdef AUDIO
device->VAD_treshold = user_settings->VAD_treshold;
#endif
}
else {
device->dhndl = alcOpenDevice(devices_names[type][selection]);
if ( !device->dhndl ) {
if ( !device->dhndl ) {
free(device);
running[type][*device_idx] = NULL;
unlock;
return de_FailedStart;
}
device->ctx = alcCreateContext(device->dhndl, NULL);
alcMakeContextCurrent(device->ctx);
alGenBuffers(openal_bufs, device->buffers);
alGenBuffers(OPENAL_BUFS, device->buffers);
alGenSources((uint32_t)1, &device->source);
alSourcei(device->source, AL_LOOPING, AL_FALSE);
uint16_t zeros[frame_size];
memset(zeros, 0, frame_size*2);
for ( i =0; i < openal_bufs; ++i) {
alBufferData(device->buffers[i], AL_FORMAT_MONO16, zeros, frame_size*2, sample_rate);
for ( i = 0; i < OPENAL_BUFS; ++i ) {
alBufferData(device->buffers[i], device->sound_mode, zeros, frame_size*2, sample_rate);
}
alSourceQueueBuffers(device->source, openal_bufs, device->buffers);
alSourceQueueBuffers(device->source, OPENAL_BUFS, device->buffers);
alSourcePlay(device->source);
}
if (alcGetError(device->dhndl) != AL_NO_ERROR) {
free(device);
running[type][*device_idx] = NULL;
unlock;
return de_FailedStart;
}
if (type == input) {
alcCaptureStart(device->dhndl);
thread_paused = _False;
thread_paused = false;
}
pthread_mutex_init(device->mutex, NULL);
unlock;
return de_None;
}
@ -264,97 +304,97 @@ DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx
DeviceError close_device(DeviceType type, uint32_t device_idx)
{
if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
lock;
Device* device = running[type][device_idx];
if (!device) {
DeviceError rc = de_None;
if (!device) {
unlock;
return de_DeviceNotActive;
}
if ( !(device->ref_count--) ) {
running[type][device_idx] = NULL;
unlock;
DeviceError rc = de_None;
running[type][device_idx] = NULL;
if ( !device->ref_count ) {
// printf("Closed device ");
if (type == input) {
if ( !alcCaptureCloseDevice(device->dhndl) ) rc = de_AlError;
}
else {
else {
if (alcGetCurrentContext() != device->ctx) alcMakeContextCurrent(device->ctx);
alDeleteSources(1, &device->source);
alDeleteBuffers(openal_bufs, device->buffers);
if ( !alcCloseDevice(device->dhndl) ) rc = de_AlError;
alDeleteBuffers(OPENAL_BUFS, device->buffers);
alcMakeContextCurrent(NULL);
if ( device->ctx ) alcDestroyContext(device->ctx);
if ( !alcCloseDevice(device->dhndl) ) rc = de_AlError;
}
free(device);
return rc;
}
else device->ref_count--;
unlock;
return de_None;
return rc;
}
DeviceError register_device_callback( int32_t call_idx, uint32_t device_idx, DataHandleCallback callback, void* data, _Bool enable_VAD)
DeviceError register_device_callback( int32_t call_idx, uint32_t device_idx, DataHandleCallback callback, void* data, bool enable_VAD)
{
if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL)
if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL)
return de_InvalidSelection;
lock;
running[input][device_idx]->cb = callback;
running[input][device_idx]->cb_data = data;
running[input][device_idx]->enable_VAD = enable_VAD;
running[input][device_idx]->call_idx = call_idx;
unlock;
return de_None;
}
inline__ DeviceError write_out(uint32_t device_idx, int16_t* data, uint32_t lenght, uint8_t channels)
inline__ DeviceError write_out(uint32_t device_idx, const int16_t* data, uint32_t length, uint8_t channels)
{
if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
Device* device = running[output][device_idx];
if (!device || device->muted) return de_DeviceNotActive;
pthread_mutex_lock(device->mutex);
ALuint bufid;
ALint processed, queued;
alGetSourcei(device->source, AL_BUFFERS_PROCESSED, &processed);
alGetSourcei(device->source, AL_BUFFERS_QUEUED, &queued);
if(processed) {
ALuint bufids[processed];
alSourceUnqueueBuffers(device->source, processed, bufids);
alDeleteBuffers(processed - 1, bufids + 1);
bufid = bufids[0];
}
}
else if(queued < 16) alGenBuffers(1, &bufid);
else {
else {
pthread_mutex_unlock(device->mutex);
return de_Busy;
}
alBufferData(bufid, AL_FORMAT_MONO16, data, lenght * 2 * channels, av_DefaultSettings.audio_sample_rate);
alBufferData(bufid, device->sound_mode, data, length * 2 * channels, device->sample_rate);
alSourceQueueBuffers(device->source, 1, &bufid);
ALint state;
alGetSourcei(device->source, AL_SOURCE_STATE, &state);
if(state != AL_PLAYING) alSourcePlay(device->source);
pthread_mutex_unlock(device->mutex);
return de_None;
}
@ -367,54 +407,61 @@ void* thread_poll (void* arg) // TODO: maybe use thread for every input source
(void)arg;
uint32_t i;
int32_t sample = 0;
int f_size = frame_size;
while (thread_running)
while (true)
{
lock;
if (!thread_running) {
unlock;
break;
}
unlock;
if (thread_paused) usleep(10000); /* Wait for unpause. */
else
{
for (i = 0; i < size[input]; i ++)
for (i = 0; i < size[input]; ++i)
{
lock;
if (running[input][i] != NULL)
if (running[input][i] != NULL)
{
alcGetIntegerv(running[input][i]->dhndl, ALC_CAPTURE_SAMPLES, sizeof(int32_t), &sample);
if (sample < f_size) {
int f_size = (running[input][i]->sample_rate * running[input][i]->frame_duration / 1000);
if (sample < f_size) {
unlock;
continue;
}
Device* device = running[input][i];
int16_t frame[4096];
int16_t frame[16000];
alcCaptureSamples(device->dhndl, frame, f_size);
if ( device->muted ||
(device->enable_VAD && !toxav_has_activity(av, device->call_idx, frame, f_size, device->VAD_treshold)))
{ unlock; continue; } /* Skip if no voice activity */
if (device->muted) {
unlock;
continue;
}
if ( device->cb ) device->cb(frame, f_size, device->cb_data);
}
}
unlock;
}
usleep(5000);
}
}
pthread_exit(NULL);
}
void print_devices(ToxWindow* self, DeviceType type)
{
int i = 0;
for ( ; i < size[type]; i ++) {
char msg[MAX_STR_SIZE];
snprintf(msg, sizeof(msg), "%d: %s", i, devices_names[type][i]);
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
}
int i;
for (i = 0; i < size[type]; ++i)
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%d: %s", i, devices_names[type][i]);
return;
}
@ -425,8 +472,8 @@ DeviceError selection_valid(DeviceType type, int32_t selection)
void* get_device_callback_data(uint32_t device_idx)
{
if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL)
if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL)
return NULL;
return running[input][device_idx]->cb_data;
}
}

View File

@ -26,16 +26,14 @@
* Read from running input device(s) via select()/callback combo.
*/
#ifndef _device_h
#define _device_h
#ifndef DEVICE_H
#define DEVICE_H
#define OPENAL_BUFS 5
#define MAX_DEVICES 32
#include <inttypes.h>
#include "windows.h"
#define _True 1
#define _False 0
typedef enum DeviceType {
input,
output,
@ -50,35 +48,44 @@ typedef enum DeviceError {
de_AllDevicesBusy = -5,
de_DeviceNotActive = -6,
de_BufferError = -7,
de_AlError = -8,
de_UnsupportedMode = -8,
de_AlError = -9,
} DeviceError;
typedef void (*DataHandleCallback) (const int16_t*, uint32_t size, void* data);
#ifdef AUDIO
DeviceError init_devices(ToxAv* av);
#else
DeviceError init_devices();
#endif /* AUDIO */
DeviceError terminate_devices();
/* Callback handles ready data from INPUT device */
DeviceError register_device_callback(int32_t call_idx, uint32_t device_idx, DataHandleCallback callback, void* data, _Bool enable_VAD);
DeviceError register_device_callback(int32_t call_idx, uint32_t device_idx, DataHandleCallback callback, void* data, bool enable_VAD);
void* get_device_callback_data(uint32_t device_idx);
/* toggle device mute */
DeviceError device_mute(DeviceType type, uint32_t device_idx);
#ifdef AUDIO
DeviceError device_set_VAD_treshold(uint32_t device_idx, float value);
#endif
DeviceError set_primary_device(DeviceType type, int32_t selection);
DeviceError open_primary_device(DeviceType type, uint32_t* device_idx);
DeviceError open_primary_device(DeviceType type, uint32_t* device_idx, uint32_t sample_rate, uint32_t frame_duration, uint8_t channels);
/* Start device */
DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx);
DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx, uint32_t sample_rate, uint32_t frame_duration, uint8_t channels);
/* Stop device */
DeviceError close_device(DeviceType type, uint32_t device_idx);
/* Write data to device */
DeviceError write_out(uint32_t device_idx, int16_t* data, uint32_t lenght, uint8_t channels);
DeviceError write_out(uint32_t device_idx, const int16_t* data, uint32_t length, uint8_t channels);
void print_devices(ToxWindow* self, DeviceType type);
void get_primary_device_name(DeviceType type, char *buf, int size);
DeviceError selection_valid(DeviceType type, int32_t selection);
#endif /* _device_h */
#endif /* DEVICE_H */

272
src/dns.c
View File

@ -22,13 +22,14 @@
#include <stdlib.h>
#include <string.h>
#include <sys/types.h> /* for u_char */
#include <netinet/in.h>
#include <resolv.h>
#ifdef __APPLE__
#include <arpa/nameser_compat.h>
#include <arpa/nameser_compat.h>
#else
#include <arpa/nameser.h>
#include <arpa/nameser.h>
#endif /* ifdef __APPLE__ */
#include <tox/toxdns.h>
@ -39,19 +40,23 @@
#include "dns.h"
#include "global_commands.h"
#include "misc_tools.h"
#include "configdir.h"
#define MAX_DNS_REQST_SIZE 256
#define NUM_DNS3_SERVERS 2 /* must correspond to number of items in dns3_servers array */
#define DNS3_KEY_SIZE 32
#define MAX_DNS_REQST_SIZE 255
#define TOX_DNS3_TXT_PREFIX "v=tox3;id="
#define DNS3_KEY_SZ 32
extern struct _Winthread Winthread;
extern struct Winthread Winthread;
extern struct dns3_servers dns3_servers;
extern struct arg_opts arg_opts;
/* TODO: process keys from key file instead of hard-coding like a noob */
static struct dns3_server {
char *name;
char key[DNS3_KEY_SZ];
} dns3_servers[] = {
#define NUM_DNS3_BACKUP_SERVERS 2
/* Hardcoded backup in case domain list is not loaded */
static struct dns3_server_backup {
const char *name;
char key[DNS3_KEY_SIZE];
} dns3_servers_backup[] = {
{
"utox.org",
{
@ -68,39 +73,91 @@ static struct dns3_server {
},
};
static struct _thread_data {
static struct thread_data {
ToxWindow *self;
char id_bin[TOX_FRIEND_ADDRESS_SIZE];
char id_bin[TOX_ADDRESS_SIZE];
char addr[MAX_STR_SIZE];
char msg[MAX_STR_SIZE];
uint8_t busy;
Tox *m;
} t_data;
static struct _dns_thread {
static struct dns_thread {
pthread_t tid;
pthread_attr_t attr;
} dns_thread;
static int dns_error(ToxWindow *self, char *errmsg)
{
char msg[MAX_STR_SIZE];
snprintf(msg, sizeof(msg), "User lookup failed: %s", errmsg);
#define MAX_DNS_SERVERS 50
#define MAX_DOMAIN_SIZE 32
#define MAX_DNS_LINE MAX_DOMAIN_SIZE + (DNS3_KEY_SIZE * 2) + 3
struct dns3_servers {
bool loaded;
int lines;
char names[MAX_DNS_SERVERS][MAX_DOMAIN_SIZE];
char keys[MAX_DNS_SERVERS][DNS3_KEY_SIZE];
} dns3_servers;
static int load_dns_domainlist(const char *path)
{
FILE *fp = fopen(path, "r");
if (fp == NULL)
return -1;
char line[MAX_DNS_LINE];
while (fgets(line, sizeof(line), fp) && dns3_servers.lines < MAX_DNS_SERVERS) {
int linelen = strlen(line);
if (linelen < DNS3_KEY_SIZE * 2 + 5)
continue;
if (line[linelen - 1] == '\n')
line[--linelen] = '\0';
const char *name = strtok(line, " ");
const char *keystr = strtok(NULL, " ");
if (name == NULL || keystr == NULL)
continue;
if (strlen(keystr) != DNS3_KEY_SIZE * 2)
continue;
snprintf(dns3_servers.names[dns3_servers.lines], sizeof(dns3_servers.names[dns3_servers.lines]), "%s", name);
int res = hex_string_to_bytes(dns3_servers.keys[dns3_servers.lines], DNS3_KEY_SIZE, keystr);
if (res == -1)
continue;
++dns3_servers.lines;
}
fclose(fp);
if (dns3_servers.lines < 1)
return -2;
return 0;
}
static int dns_error(ToxWindow *self, const char *errmsg)
{
pthread_mutex_lock(&Winthread.lock);
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "User lookup failed: %s", errmsg);
pthread_mutex_unlock(&Winthread.lock);
return -1;
}
static void kill_dns_thread(void *dns_obj)
static void killdns_thread(void *dns_obj)
{
if (dns_obj)
tox_dns3_kill(dns_obj);
memset(&t_data, 0, sizeof(struct _thread_data));
memset(&t_data, 0, sizeof(struct thread_data));
pthread_attr_destroy(&dns_thread.attr);
pthread_exit(NULL);
}
@ -111,39 +168,39 @@ static int parse_dns_response(ToxWindow *self, u_char *answer, int ans_len, char
uint8_t *ans_pt = answer + sizeof(HEADER);
uint8_t *ans_end = answer + ans_len;
char exp_ans[PACKETSZ];
int len = dn_expand(answer, ans_end, ans_pt, exp_ans, sizeof(exp_ans));
if (len == -1)
return dns_error(self, "dn_expand failed.");
return dns_error(self, "dn_expand failed.");
ans_pt += len;
if (ans_pt > ans_end - 4)
return dns_error(self, "DNS reply was too short.");
return dns_error(self, "DNS reply was too short.");
int type;
GETSHORT(type, ans_pt);
if (type != T_TXT)
return dns_error(self, "Broken DNS reply.");
return dns_error(self, "Broken DNS reply.");
ans_pt += INT16SZ; /* class */
uint32_t size = 0;
/* recurse through CNAME rr's */
do {
do {
ans_pt += size;
len = dn_expand(answer, ans_end, ans_pt, exp_ans, sizeof(exp_ans));
if (len == -1)
return dns_error(self, "Second dn_expand failed.");
return dns_error(self, "Second dn_expand failed.");
ans_pt += len;
if (ans_pt > ans_end - 10)
return dns_error(self, "DNS reply was too short.");
return dns_error(self, "DNS reply was too short.");
GETSHORT(type, ans_pt);
ans_pt += INT16SZ;
@ -151,18 +208,21 @@ static int parse_dns_response(ToxWindow *self, u_char *answer, int ans_len, char
GETSHORT(size, ans_pt);
if (ans_pt + size < answer || ans_pt + size > ans_end)
return dns_error(self, "RR overflow.");
return dns_error(self, "RR overflow.");
} while (type == T_CNAME);
if (type != T_TXT)
return dns_error(self, "DNS response failed.");
return dns_error(self, "DNS response failed.");
uint32_t txt_len = *ans_pt;
if (!size || txt_len >= size || !txt_len)
return dns_error(self, "No record found.");
if (txt_len > MAX_DNS_REQST_SIZE)
return dns_error(self, "Invalid DNS response.");
ans_pt++;
ans_pt[txt_len] = '\0';
memcpy(buf, ans_pt, txt_len + 1);
@ -170,16 +230,20 @@ static int parse_dns_response(ToxWindow *self, u_char *answer, int ans_len, char
return txt_len;
}
/* Takes address addr in the form "username@domain", puts the username in namebuf,
/* Takes address addr in the form "username@domain", puts the username in namebuf,
and the domain in dombuf.
return length of username on success, -1 on failure */
static int parse_addr(char *addr, char *namebuf, char *dombuf)
static int parse_addr(const char *addr, char *namebuf, size_t namebuf_sz, char *dombuf, size_t dombuf_sz)
{
char tmpaddr[MAX_STR_SIZE];
char *tmpname, *tmpdom;
if (strlen(addr) >= MAX_STR_SIZE)
return -1;
strcpy(tmpaddr, addr);
char tmpaddr[MAX_STR_SIZE];
char *tmpname = NULL;
char *tmpdom = NULL;
snprintf(tmpaddr, sizeof(tmpaddr), "%s", addr);
tmpname = strtok(tmpaddr, "@");
tmpdom = strtok(NULL, "");
@ -187,117 +251,158 @@ static int parse_addr(char *addr, char *namebuf, char *dombuf)
return -1;
str_to_lower(tmpdom);
strcpy(namebuf, tmpname);
strcpy(dombuf, tmpdom);
snprintf(namebuf, namebuf_sz, "%s", tmpname);
snprintf(dombuf, dombuf_sz, "%s", tmpdom);
return strlen(namebuf);
}
/* matches input domain name with domains in list and obtains key. Return 0 on success, -1 on failure */
static int get_domain_match(char *pubkey, char *domain, const char *inputdomain)
{
/* check server list first */
int i;
bool match = false;
for (i = 0; i < dns3_servers.lines; ++i) {
if (strcmp(dns3_servers.names[i], inputdomain) == 0) {
memcpy(pubkey, dns3_servers.keys[i], DNS3_KEY_SIZE);
snprintf(domain, MAX_DOMAIN_SIZE, "%s", dns3_servers.names[i]);
match = true;
break;
}
}
/* fall back to hard-coded domains on server list failure */
if (!match) {
for (i = 0; i < NUM_DNS3_BACKUP_SERVERS; ++i) {
if (strcmp(dns3_servers_backup[i].name, inputdomain) == 0) {
memcpy(pubkey, dns3_servers_backup[i].key, DNS3_KEY_SIZE);
snprintf(domain, MAX_DOMAIN_SIZE, "%s", dns3_servers_backup[i].name);
match = true;
break;
}
}
if (!match)
return -1;
}
return 0;
}
/* Does DNS lookup for addr and puts resulting tox id in id_bin. */
void *dns3_lookup_thread(void *data)
{
ToxWindow *self = t_data.self;
char domain[MAX_STR_SIZE];
char inputdomain[MAX_STR_SIZE];
char name[MAX_STR_SIZE];
int namelen = parse_addr(t_data.addr, name, domain);
int namelen = parse_addr(t_data.addr, name, sizeof(name), inputdomain, sizeof(inputdomain));
if (namelen == -1) {
dns_error(self, "Must be a Tox ID or an address in the form username@domain");
kill_dns_thread(NULL);
killdns_thread(NULL);
}
/* get domain name/pub key */
char *DNS_pubkey = NULL;
char *domname = NULL;
int i;
char DNS_pubkey[DNS3_KEY_SIZE];
char domain[MAX_DOMAIN_SIZE];
for (i = 0; i < NUM_DNS3_SERVERS; ++i) {
if (strcmp(dns3_servers[i].name, domain) == 0) {
DNS_pubkey = dns3_servers[i].key;
domname = dns3_servers[i].name;
break;
}
}
int match = get_domain_match(DNS_pubkey, domain, inputdomain);
if (domname == NULL) {
if (match == -1) {
dns_error(self, "Domain not found.");
kill_dns_thread(NULL);
killdns_thread(NULL);
}
void *dns_obj = tox_dns3_new((uint8_t *) DNS_pubkey);
if (dns_obj == NULL) {
dns_error(self, "Core failed to create DNS object.");
kill_dns_thread(NULL);
killdns_thread(NULL);
}
char string[MAX_DNS_REQST_SIZE];
char string[MAX_DNS_REQST_SIZE + 1];
uint32_t request_id;
int str_len = tox_generate_dns3_string(dns_obj, (uint8_t *) string, sizeof(string), &request_id,
int str_len = tox_generate_dns3_string(dns_obj, (uint8_t *) string, sizeof(string), &request_id,
(uint8_t *) name, namelen);
if (str_len == -1) {
dns_error(self, "Core failed to generate DNS3 string.");
kill_dns_thread(dns_obj);
killdns_thread(dns_obj);
}
string[str_len] = '\0';
u_char answer[PACKETSZ];
char d_string[MAX_DNS_REQST_SIZE];
char d_string[MAX_DOMAIN_SIZE + MAX_DNS_REQST_SIZE + 10];
/* format string and create dns query */
snprintf(d_string, sizeof(d_string), "_%s._tox.%s", string, domname);
snprintf(d_string, sizeof(d_string), "_%s._tox.%s", string, domain);
int ans_len = res_query(d_string, C_IN, T_TXT, answer, sizeof(answer));
if (ans_len <= 0) {
dns_error(self, "DNS query failed.");
kill_dns_thread(dns_obj);
killdns_thread(dns_obj);
}
char ans_id[MAX_DNS_REQST_SIZE];
char ans_id[MAX_DNS_REQST_SIZE + 1];
/* extract TXT from DNS response */
if (parse_dns_response(self, answer, ans_len, ans_id) == -1)
kill_dns_thread(dns_obj);
killdns_thread(dns_obj);
char encrypted_id[MAX_DNS_REQST_SIZE];
char encrypted_id[MAX_DNS_REQST_SIZE + 1];
int prfx_len = strlen(TOX_DNS3_TXT_PREFIX);
/* extract the encrypted ID from TXT response */
if (strncmp(ans_id, TOX_DNS3_TXT_PREFIX, prfx_len) != 0) {
dns_error(self, "Bad DNS3 TXT response.");
kill_dns_thread(dns_obj);
killdns_thread(dns_obj);
}
memcpy(encrypted_id, ans_id + prfx_len, ans_len - prfx_len);
if (tox_decrypt_dns3_TXT(dns_obj, (uint8_t *) t_data.id_bin, (uint8_t *) encrypted_id,
if (tox_decrypt_dns3_TXT(dns_obj, (uint8_t *) t_data.id_bin, (uint8_t *) encrypted_id,
strlen(encrypted_id), request_id) == -1) {
dns_error(self, "Core failed to decrypt DNS response.");
kill_dns_thread(dns_obj);
killdns_thread(dns_obj);
}
pthread_mutex_lock(&Winthread.lock);
cmd_add_helper(self, t_data.m, t_data.id_bin, t_data.msg);
pthread_mutex_unlock(&Winthread.lock);
kill_dns_thread(dns_obj);
killdns_thread(dns_obj);
return 0;
}
/* creates new thread for dns3 lookup. Only allows one lookup at a time. */
void dns3_lookup(ToxWindow *self, Tox *m, char *id_bin, char *addr, char *msg)
void dns3_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *msg)
{
if (t_data.busy) {
char *err = "Please wait for previous user lookup to finish.";
line_info_add(self, NULL, NULL, NULL, err, SYS_MSG, 0, 0);
if (arg_opts.proxy_type != TOX_PROXY_TYPE_NONE && arg_opts.force_tcp) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "DNS lookups are disabled.");
return;
}
if (t_data.busy) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Please wait for previous user lookup to finish.");
return;
}
if (!dns3_servers.loaded) {
const char *path = arg_opts.dns_path[0] ? arg_opts.dns_path : PACKAGE_DATADIR "/DNSservers";
dns3_servers.loaded = true;
int ret = load_dns_domainlist(path);
if (ret < 0) {
const char *errmsg = "DNS server list failed to load with error code %d. Falling back to hard-coded list.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg, ret);
}
}
snprintf(t_data.id_bin, sizeof(t_data.id_bin), "%s", id_bin);
snprintf(t_data.addr, sizeof(t_data.addr), "%s", addr);
snprintf(t_data.msg, sizeof(t_data.msg), "%s", msg);
@ -305,12 +410,23 @@ void dns3_lookup(ToxWindow *self, Tox *m, char *id_bin, char *addr, char *msg)
t_data.m = m;
t_data.busy = 1;
if (pthread_attr_init(&dns_thread.attr) != 0)
exit_toxic_err("failed in dns3_lookup", FATALERR_THREAD_ATTR);
if (pthread_attr_init(&dns_thread.attr) != 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "Error: DNS thread attr failed to init");
memset(&t_data, 0, sizeof(struct thread_data));
return;
}
if (pthread_attr_setdetachstate(&dns_thread.attr, PTHREAD_CREATE_DETACHED) != 0)
exit_toxic_err("failed in dns3_lookup", FATALERR_THREAD_ATTR);
if (pthread_attr_setdetachstate(&dns_thread.attr, PTHREAD_CREATE_DETACHED) != 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "Error: DNS thread attr failed to set");
pthread_attr_destroy(&dns_thread.attr);
memset(&t_data, 0, sizeof(struct thread_data));
return;
}
if (pthread_create(&dns_thread.tid, &dns_thread.attr, dns3_lookup_thread, NULL) != 0)
exit_toxic_err("failed in dns3_lookup", FATALERR_THREAD_CREATE);
if (pthread_create(&dns_thread.tid, &dns_thread.attr, dns3_lookup_thread, NULL) != 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "Error: DNS thread failed to init");
pthread_attr_destroy(&dns_thread.attr);
memset(&t_data, 0, sizeof(struct thread_data));
return;
}
}

View File

@ -23,10 +23,10 @@
/* Does DNS lookup for addr and puts resulting tox id in id_bin.
Return 0 on success, -1 on failure. */
#ifndef _dns_h
#define _dns_h
#ifndef DNS_H
#define DNS_H
/* creates new thread for dns3 lookup. Only allows one lookup at a time. */
void dns3_lookup(ToxWindow *self, Tox *m, char *id_bin, char *addr, char *msg);
void dns3_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *msg);
#endif /* #define _dns_h */
#endif /* #define DNS_H */

View File

@ -29,8 +29,10 @@
#include "execute.h"
#include "chat_commands.h"
#include "global_commands.h"
#include "group_commands.h"
#include "line_info.h"
#include "misc_tools.h"
#include "notify.h"
struct cmd_func {
const char *name;
@ -40,10 +42,12 @@ struct cmd_func {
static struct cmd_func global_commands[] = {
{ "/accept", cmd_accept },
{ "/add", cmd_add },
{ "/avatar", cmd_avatar },
{ "/clear", cmd_clear },
{ "/connect", cmd_connect },
{ "/decline", cmd_decline },
{ "/exit", cmd_quit },
{ "/groupchat", cmd_groupchat },
{ "/group", cmd_groupchat },
{ "/help", cmd_prompt_help },
{ "/log", cmd_log },
{ "/myid", cmd_myid },
@ -51,75 +55,94 @@ static struct cmd_func global_commands[] = {
{ "/note", cmd_note },
{ "/q", cmd_quit },
{ "/quit", cmd_quit },
{ "/requests", cmd_requests },
{ "/status", cmd_status },
#ifdef _SUPPORT_AUDIO
#ifdef AUDIO
{ "/lsdev", cmd_list_devices },
{ "/sdev", cmd_change_device },
#endif /* _SUPPORT_AUDIO */
#endif /* AUDIO */
{ NULL, NULL },
};
static struct cmd_func chat_commands[] = {
{ "/cancel", cmd_cancelfile },
{ "/invite", cmd_groupinvite },
{ "/join", cmd_join_group },
{ "/savefile", cmd_savefile },
{ "/sendfile", cmd_sendfile },
#ifdef _SUPPORT_AUDIO
#ifdef AUDIO
{ "/call", cmd_call },
{ "/cancel", cmd_cancel },
{ "/answer", cmd_answer },
{ "/reject", cmd_reject },
{ "/hangup", cmd_hangup },
{ "/sdev", cmd_ccur_device },
{ "/mute", cmd_mute },
{ "/sense", cmd_sense },
#endif /* _SUPPORT_AUDIO */
#endif /* AUDIO */
{ NULL, NULL },
};
static struct cmd_func group_commands[] = {
{ "/title", cmd_set_title },
#ifdef AUDIO
{ "/mute", cmd_mute },
{ "/sense", cmd_sense },
#endif /* AUDIO */
{ NULL, NULL },
};
/* Parses input command and puts args into arg array.
Returns number of arguments on success, -1 on failure. */
static int parse_command(WINDOW *w, ToxWindow *self, char *cmd, char (*args)[MAX_STR_SIZE])
static int parse_command(WINDOW *w, ToxWindow *self, const char *input, char (*args)[MAX_STR_SIZE])
{
char *cmd = strdup(input);
if (cmd == NULL)
exit_toxic_err("failed in parse_command", FATALERR_MEMORY);
int num_args = 0;
bool cmd_end = false; /* flags when we get to the end of cmd */
char *end; /* points to the end of the current arg */
int i = 0; /* index of last char in an argument */
/* characters wrapped in double quotes count as one arg */
while (!cmd_end && num_args < MAX_NUM_ARGS) {
if (*cmd == '\"') {
end = strchr(cmd + 1, '\"');
while (num_args < MAX_NUM_ARGS) {
int qt_ofst = 0; /* set to 1 to offset index for quote char at end of arg */
if (end++ == NULL) { /* Increment past the end quote */
char *errmsg = "Invalid argument. Did you forget a closing \"?";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
if (*cmd == '\"') {
qt_ofst = 1;
i = char_find(1, cmd, '\"');
if (cmd[i] == '\0') {
const char *errmsg = "Invalid argument. Did you forget a closing \"?";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
free(cmd);
return -1;
}
cmd_end = *end == '\0';
} else {
end = strchr(cmd, ' ');
cmd_end = end == NULL;
i = char_find(0, cmd, ' ');
}
if (!cmd_end)
*end++ = '\0'; /* mark end of current argument */
memcpy(args[num_args], cmd, i + qt_ofst);
args[num_args++][i + qt_ofst] = '\0';
/* Copy from start of current arg to where we just inserted the null byte */
strcpy(args[num_args++], cmd);
cmd = end;
if (cmd[i] == '\0') /* no more args */
break;
char tmp[MAX_STR_SIZE];
snprintf(tmp, sizeof(tmp), "%s", &cmd[i + 1]);
strcpy(cmd, tmp); /* tmp will always fit inside cmd */
}
free(cmd);
return num_args;
}
/* Matches command to respective function. Returns 0 on match, 1 on no match */
static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, int num_cmds,
struct cmd_func *commands, char (*args)[MAX_STR_SIZE])
static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, struct cmd_func *commands,
char (*args)[MAX_STR_SIZE])
{
int i;
for (i = 0; i < num_cmds; ++i) {
for (i = 0; commands[i].name != NULL; ++i) {
if (strcmp(args[0], commands[i].name) == 0) {
(commands[i].func)(w, self, m, num_args - 1, args);
return 0;
@ -129,13 +152,13 @@ static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, int num_
return 1;
}
void execute(WINDOW *w, ToxWindow *self, Tox *m, char *cmd, int mode)
void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode)
{
if (string_is_empty(cmd))
if (string_is_empty(input))
return;
char args[MAX_NUM_ARGS][MAX_STR_SIZE];
int num_args = parse_command(w, self, cmd, args);
int num_args = parse_command(w, self, input, args);
if (num_args == -1)
return;
@ -146,17 +169,19 @@ void execute(WINDOW *w, ToxWindow *self, Tox *m, char *cmd, int mode)
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)
if (do_command(w, self, m, num_args, chat_commands, args) == 0)
return;
break;
case GROUPCHAT_COMMAND_MODE:
if (do_command(w, self, m, num_args, group_commands, args) == 0)
return;
break;
}
if (do_command(w, self, m, num_args, GLOBAL_NUM_COMMANDS, global_commands, args) == 0)
if (do_command(w, self, m, num_args, global_commands, args) == 0)
return;
line_info_add(self, NULL, NULL, NULL, "Invalid command.", SYS_MSG, 0, 0);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid command.");
}

View File

@ -20,28 +20,20 @@
*
*/
#ifndef _execute_h
#define _execute_h
#ifndef EXECUTE_H
#define EXECUTE_H
#include "toxic.h"
#include "windows.h"
#define MAX_NUM_ARGS 4 /* Includes command */
#ifdef _SUPPORT_AUDIO
#define GLOBAL_NUM_COMMANDS 16
#define CHAT_NUM_COMMANDS 12
#else
#define GLOBAL_NUM_COMMANDS 14
#define CHAT_NUM_COMMANDS 4
#endif /* _SUPPORT_AUDIO */
enum {
GLOBAL_COMMAND_MODE,
CHAT_COMMAND_MODE,
GROUPCHAT_COMMAND_MODE,
};
void execute(WINDOW *w, ToxWindow *self, Tox *m, char *cmd, int mode);
void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode);
#endif /* #define _execute_h */
#endif /* #define EXECUTE_H */

View File

@ -1,129 +0,0 @@
/* 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/>.
*
*/
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include "toxic.h"
#include "windows.h"
#include "file_senders.h"
#include "line_info.h"
#include "misc_tools.h"
FileSender file_senders[MAX_FILES];
uint8_t max_file_senders_index;
static void set_max_file_senders_index(void)
{
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 close_file_sender(ToxWindow *self, Tox *m, int i, char *msg, int CTRL, int filenum, int32_t friendnum)
{
if (self->chatwin != NULL) {
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, CTRL, 0, 0);
fclose(file_senders[i].file);
memset(&file_senders[i], 0, sizeof(FileSender));
set_max_file_senders_index();
}
void close_all_file_senders(Tox *m)
{
int i;
for (i = 0; i < max_file_senders_index; ++i) {
if (file_senders[i].active) {
fclose(file_senders[i].file);
tox_file_send_control(m, file_senders[i].friendnum, 0, file_senders[i].filenum,
TOX_FILECONTROL_KILL, 0, 0);
memset(&file_senders[i], 0, sizeof(FileSender));
}
set_max_file_senders_index();
}
}
void do_file_senders(Tox *m)
{
char 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;
char *pathname = file_senders[i].pathname;
int filenum = file_senders[i].filenum;
int32_t friendnum = file_senders[i].friendnum;
FILE *fp = file_senders[i].file;
/* If file transfer has timed out kill transfer and send kill control */
if (timed_out(file_senders[i].timestamp, get_unix_time(), TIMEOUT_FILESENDER)) {
snprintf(msg, sizeof(msg), "File transfer for '%s' timed out.", pathname);
close_file_sender(self, m, i, msg, TOX_FILECONTROL_KILL, filenum, friendnum);
continue;
}
while (true) {
if (tox_file_send_data(m, friendnum, filenum, (uint8_t *) file_senders[i].nextpiece,
file_senders[i].piecelen) == -1)
break;
uint64_t curtime = get_unix_time();
file_senders[i].timestamp = curtime;
file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1,
tox_file_data_size(m, friendnum), fp);
long double remain = (long double) tox_file_data_remaining(m, friendnum, filenum, 0);
/* refresh line with percentage complete */
if ((self->chatwin != NULL && timed_out(file_senders[i].last_progress, curtime, 1)) || !remain) {
file_senders[i].last_progress = curtime;
uint64_t size = file_senders[i].size;
long double pct_remain = remain ? (1 - (remain / size)) * 100 : 100;
snprintf(msg, sizeof(msg), "File transfer for '%s' accepted (%.1Lf%%)", pathname, pct_remain);
line_info_set(self, file_senders[i].line_id, msg);
}
if (file_senders[i].piecelen == 0) {
snprintf(msg, sizeof(msg), "File '%s' successfuly sent.", pathname);
close_file_sender(self, m, i, msg, TOX_FILECONTROL_FINISHED, filenum, friendnum);
break;
}
}
}
}

316
src/file_transfers.c Normal file
View File

@ -0,0 +1,316 @@
/* file_transfers.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 <string.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include "toxic.h"
#include "windows.h"
#include "friendlist.h"
#include "file_transfers.h"
#include "line_info.h"
#include "misc_tools.h"
#include "notify.h"
extern FriendsList Friends;
/* number of "#"'s in file transfer progress bar. Keep well below MAX_STR_SIZE */
#define NUM_PROG_MARKS 50
/* Checks for timed out file transfers and closes them. */
#define CHECK_FILE_TIMEOUT_INTERAVAL 5
void check_file_transfer_timeouts(Tox *m)
{
char msg[MAX_STR_SIZE];
static uint64_t last_check = 0;
if (!timed_out(last_check, CHECK_FILE_TIMEOUT_INTERAVAL))
return;
last_check = get_unix_time();
size_t i, j;
for (i = 0; i < Friends.max_idx; ++i) {
if (!Friends.list[i].active)
continue;
for (j = 0; j < MAX_FILES; ++j) {
struct FileTransfer *ft_send = &Friends.list[i].file_sender[j];
if (ft_send->state > FILE_TRANSFER_PAUSED) {
if (timed_out(ft_send->last_keep_alive, TIMEOUT_FILESENDER)) {
snprintf(msg, sizeof(msg), "File transfer for '%s' timed out.", ft_send->file_name);
close_file_transfer(ft_send->window, m, ft_send, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
}
}
struct FileTransfer *ft_recv = &Friends.list[i].file_receiver[j];
if (ft_recv->state > FILE_TRANSFER_PAUSED) {
if (timed_out(ft_recv->last_keep_alive, TIMEOUT_FILESENDER)) {
snprintf(msg, sizeof(msg), "File transfer for '%s' timed out.", ft_recv->file_name);
close_file_transfer(ft_recv->window, m, ft_recv, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
}
}
}
}
}
/* creates initial progress line that will be updated during file transfer.
Assumes progline is of size MAX_STR_SIZE */
void init_progress_bar(char *progline)
{
strcpy(progline, "0.0 B/s [");
int i;
for (i = 0; i < NUM_PROG_MARKS; ++i)
strcat(progline, "-");
strcat(progline, "] 0%");
}
/* prints a progress bar for file transfers.
if friendnum is -1 we're sending the file, otherwise we're receiving. */
void print_progress_bar(ToxWindow *self, double bps, double pct_done, uint32_t line_id)
{
if (bps < 0 || pct_done < 0 || pct_done > 100)
return;
char msg[MAX_STR_SIZE];
bytes_convert_str(msg, sizeof(msg), bps);
strcat(msg, "/s [");
int n = pct_done / (100 / NUM_PROG_MARKS);
int i, j;
for (i = 0; i < n; ++i)
strcat(msg, "#");
for (j = i; j < NUM_PROG_MARKS; ++j)
strcat(msg, "-");
strcat(msg, "] ");
char pctstr[16];
const char *frmt = pct_done == 100 ? "%.f%%" : "%.1f%%";
snprintf(pctstr, sizeof(pctstr), frmt, pct_done);
strcat(msg, pctstr);
line_info_set(self, line_id, msg);
}
static void refresh_progress_helper(ToxWindow *self, Tox *m, struct FileTransfer *ft)
{
if (ft->state == FILE_TRANSFER_INACTIVE)
return;
/* Timeout must be set to 1 second to show correct bytes per second */
if (!timed_out(ft->last_line_progress, 1))
return;
double remain = ft->file_size - ft->position;
double pct_done = remain > 0 ? (1 - (remain / ft->file_size)) * 100 : 100;
print_progress_bar(self, ft->bps, pct_done, ft->line_id);
ft->bps = 0;
ft->last_line_progress = get_unix_time();
}
/* refreshes active file transfer status bars. */
void refresh_file_transfer_progress(ToxWindow *self, Tox *m, uint32_t friendnum)
{
size_t i;
for (i = 0; i < MAX_FILES; ++i) {
refresh_progress_helper(self, m, &Friends.list[friendnum].file_receiver[i]);
refresh_progress_helper(self, m, &Friends.list[friendnum].file_sender[i]);
}
}
/* Returns a pointer to friendnum's FileTransfer struct associated with filenum.
* Returns NULL if filenum is invalid.
*/
struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filenum)
{
size_t i;
for (i = 0; i < MAX_FILES; ++i) {
struct FileTransfer *ft_send = &Friends.list[friendnum].file_sender[i];
if (ft_send->state != FILE_TRANSFER_INACTIVE && ft_send->filenum == filenum)
return ft_send;
struct FileTransfer *ft_recv = &Friends.list[friendnum].file_receiver[i];
if (ft_recv->state != FILE_TRANSFER_INACTIVE && ft_recv->filenum == filenum)
return ft_recv;
}
return NULL;
}
/* Returns a pointer to the FileTransfer struct associated with index with the direction specified.
* Returns NULL on failure.
*/
struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index,
FILE_TRANSFER_DIRECTION direction)
{
if (direction != FILE_TRANSFER_RECV && direction != FILE_TRANSFER_SEND)
return NULL;
size_t i;
for (i = 0; i < MAX_FILES; ++i) {
struct FileTransfer *ft = direction == FILE_TRANSFER_SEND ?
&Friends.list[friendnum].file_sender[i] :
&Friends.list[friendnum].file_receiver[i];
if (ft->state != FILE_TRANSFER_INACTIVE && ft->index == index)
return ft;
}
return NULL;
}
/* Returns a pointer to an unused file sender.
* Returns NULL if all file senders are in use.
*/
static struct FileTransfer *new_file_sender(ToxWindow *window, uint32_t friendnum, uint32_t filenum, uint8_t type)
{
size_t i;
for (i = 0; i < MAX_FILES; ++i) {
struct FileTransfer *ft = &Friends.list[friendnum].file_sender[i];
if (ft->state == FILE_TRANSFER_INACTIVE) {
memset(ft, 0, sizeof(struct FileTransfer));
ft->window = window;
ft->index = i;
ft->friendnum = friendnum;
ft->filenum = filenum;
ft->file_type = type;
ft->last_keep_alive = get_unix_time();
ft->state = FILE_TRANSFER_PENDING;
ft->direction = FILE_TRANSFER_SEND;
return ft;
}
}
return NULL;
}
/* Returns a pointer to an unused file receiver.
* Returns NULL if all file receivers are in use.
*/
static struct FileTransfer *new_file_receiver(ToxWindow *window, uint32_t friendnum, uint32_t filenum, uint8_t type)
{
size_t i;
for (i = 0; i < MAX_FILES; ++i) {
struct FileTransfer *ft = &Friends.list[friendnum].file_receiver[i];
if (ft->state == FILE_TRANSFER_INACTIVE) {
memset(ft, 0, sizeof(struct FileTransfer));
ft->window = window;
ft->index = i;
ft->friendnum = friendnum;
ft->filenum = filenum;
ft->file_type = type;
ft->last_keep_alive = get_unix_time();
ft->state = FILE_TRANSFER_PENDING;
ft->direction = FILE_TRANSFER_RECV;
return ft;
}
}
return NULL;
}
/* Initializes an unused file transfer and returns its pointer.
* Returns NULL on failure.
*/
struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnum, uint32_t filenum,
FILE_TRANSFER_DIRECTION direction, uint8_t type)
{
if (direction == FILE_TRANSFER_RECV)
return new_file_receiver(window, friendnum, filenum, type);
if (direction == FILE_TRANSFER_SEND)
return new_file_sender(window, friendnum, filenum, type);
return NULL;
}
/* Closes file transfer ft.
*
* Set CTRL to -1 if we don't want to send a control signal.
* Set message or self to NULL if we don't want to display a message.
*/
void close_file_transfer(ToxWindow *self, Tox *m, struct FileTransfer *ft, int CTRL, const char *message,
Notification sound_type)
{
if (!ft)
return;
if (ft->state == FILE_TRANSFER_INACTIVE)
return;
if (ft->file)
fclose(ft->file);
if (CTRL >= 0)
tox_file_control(m, ft->friendnum, ft->filenum, (TOX_FILE_CONTROL) CTRL, NULL);
if (message && self) {
if (self->active_box != -1 && sound_type != silent)
box_notify2(self, sound_type, NT_NOFOCUS | NT_WNDALERT_2, self->active_box, "%s", message);
else
box_notify(self, sound_type, NT_NOFOCUS | NT_WNDALERT_2, &self->active_box, self->name, "%s", message);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", message);
}
memset(ft, 0, sizeof(struct FileTransfer));
}
/* Kills all active file transfers for friendnum */
void kill_all_file_transfers_friend(Tox *m, uint32_t friendnum)
{
size_t i;
for (i = 0; i < MAX_FILES; ++i) {
close_file_transfer(NULL, m, &Friends.list[friendnum].file_sender[i], TOX_FILE_CONTROL_CANCEL, NULL, silent);
close_file_transfer(NULL, m, &Friends.list[friendnum].file_receiver[i], TOX_FILE_CONTROL_CANCEL, NULL, silent);
}
}
void kill_all_file_transfers(Tox *m)
{
size_t i;
for (i = 0; i < Friends.max_idx; ++i)
kill_all_file_transfers_friend(m, Friends.list[i].num);
}

115
src/file_transfers.h Normal file
View File

@ -0,0 +1,115 @@
/* file_transfers.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 FILE_TRANSFERS_H
#define FILE_TRANSFERS_H
#include <limits.h>
#include "toxic.h"
#include "windows.h"
#include "notify.h"
#define KiB 1024
#define MiB 1048576 /* 1024^2 */
#define GiB 1073741824 /* 1024^3 */
#define MAX_FILES 32
#define TIMEOUT_FILESENDER 120
typedef enum FILE_TRANSFER_STATE {
FILE_TRANSFER_INACTIVE,
FILE_TRANSFER_PAUSED,
FILE_TRANSFER_PENDING,
FILE_TRANSFER_STARTED,
} FILE_TRANSFER_STATE;
typedef enum FILE_TRANSFER_DIRECTION {
FILE_TRANSFER_SEND,
FILE_TRANSFER_RECV
} FILE_TRANSFER_DIRECTION;
struct FileTransfer {
ToxWindow *window;
FILE *file;
FILE_TRANSFER_STATE state;
FILE_TRANSFER_DIRECTION direction;
uint8_t file_type;
char file_name[TOX_MAX_FILENAME_LENGTH + 1];
char file_path[PATH_MAX + 1]; /* Not used by senders */
double bps;
uint32_t filenum;
uint32_t friendnum;
size_t index;
uint64_t file_size;
uint64_t position;
uint64_t last_line_progress; /* The last time we updated the progress bar */
uint64_t last_keep_alive; /* The last time we sent or received data */
uint32_t line_id;
uint8_t file_id[TOX_FILE_ID_LENGTH];
};
/* Checks for timed out file transfers and closes them. */
void check_file_transfer_timeouts(Tox *m);
/* creates initial progress line that will be updated during file transfer.
progline must be at lesat MAX_STR_SIZE bytes */
void init_progress_bar(char *progline);
/* prints a progress bar for file transfers */
void print_progress_bar(ToxWindow *self, double pct_done, double bps, uint32_t line_id);
/* refreshes active file transfer status bars. */
void refresh_file_transfer_progress(ToxWindow *self, Tox *m, uint32_t friendnum);
/* Returns a pointer to friendnum's FileTransfer struct associated with filenum.
* Returns NULL if filenum is invalid.
*/
struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filenum);
/* Returns a pointer to the FileTransfer struct associated with index with the direction specified.
* Returns NULL on failure.
*/
struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index,
FILE_TRANSFER_DIRECTION direction);
/* Initializes an unused file transfer and returns its pointer.
* Returns NULL on failure.
*/
struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnum, uint32_t filenum,
FILE_TRANSFER_DIRECTION direction, uint8_t type);
/* Closes file transfer ft.
*
* Set CTRL to -1 if we don't want to send a control signal.
* Set message or self to NULL if we don't want to display a message.
*/
void close_file_transfer(ToxWindow *self, Tox *m, struct FileTransfer *ft, int CTRL, const char *message,
Notification sound_type);
/* Kills all active file transfers for friendnum */
void kill_all_file_transfers_friend(Tox *m, uint32_t friendnum);
void kill_all_file_transfers(Tox *m);
#endif /* #define FILE_TRANSFERS_H */

File diff suppressed because it is too large Load Diff

View File

@ -20,23 +20,14 @@
*
*/
#ifndef FRIENDLIST_H_53I41IM
#define FRIENDLIST_H_53I41IM
#ifndef FRIENDLIST_H
#define FRIENDLIST_H
#include <time.h>
#include "toxic.h"
#include "windows.h"
#include "file_senders.h"
struct FileReceiver {
char filenames[MAX_FILES][MAX_STR_SIZE];
FILE *files[MAX_FILES];
bool pending[MAX_FILES];
uint64_t size[MAX_FILES];
uint64_t last_progress[MAX_FILES];
uint32_t line_id[MAX_FILES];
};
#include "file_transfers.h"
struct LastOnline {
uint64_t last_on;
@ -44,32 +35,60 @@ struct LastOnline {
char hour_min_str[TIME_STR_SIZE]; /* holds 12/24-hour time string e.g. "10:43 PM" */
};
struct GroupChatInvite {
char *key;
uint16_t length;
uint8_t type;
bool pending;
};
typedef struct {
char name[TOXIC_MAX_NAME_LENGTH];
char name[TOXIC_MAX_NAME_LENGTH + 1];
int namelength;
char statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH];
uint16_t statusmsg_len;
char groupchat_key[TOX_CLIENT_ID_SIZE];
bool groupchat_pending;
char pub_key[TOX_CLIENT_ID_SIZE];
int32_t num;
char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH + 1];
size_t statusmsg_len;
char pub_key[TOX_PUBLIC_KEY_SIZE];
uint32_t num;
int chatwin;
bool active;
bool online;
uint8_t is_typing;
bool logging_on; /* saves preference for friend irrespective of chat windows */
TOX_CONNECTION connection_status;
bool is_typing;
bool logging_on; /* saves preference for friend irrespective of global settings */
uint8_t status;
struct LastOnline last_online;
struct FileReceiver file_receiver;
struct GroupChatInvite group_invite;
struct FileTransfer file_receiver[MAX_FILES];
struct FileTransfer file_sender[MAX_FILES];
} ToxicFriend;
ToxWindow new_friendlist(void);
void disable_chatwin(int32_t f_num);
int get_friendnum(uint8_t *name);
typedef struct {
char name[TOXIC_MAX_NAME_LENGTH + 1];
int namelength;
char pub_key[TOX_PUBLIC_KEY_SIZE];
uint32_t num;
bool active;
uint64_t last_on;
} BlockedFriend;
void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int32_t num, bool sort);
typedef struct {
int num_selected;
size_t num_friends;
size_t num_online;
size_t max_idx; /* 1 + the index of the last friend in list */
uint32_t *index;
ToxicFriend *list;
} FriendsList;
ToxWindow new_friendlist(void);
void disable_chatwin(uint32_t f_num);
int get_friendnum(uint8_t *name);
int load_blocklist(char *data);
void kill_friendlist(void);
void friendlist_onFriendAdded(ToxWindow *self, Tox *m, uint32_t num, bool sort);
/* sorts friendlist_index first by connection status then alphabetically */
void sort_friendlist_index(void);
#endif /* end of include guard: FRIENDLIST_H_53I41IM */
#endif /* end of include guard: FRIENDLIST_H */

View File

@ -34,154 +34,157 @@
#include "groupchat.h"
#include "prompt.h"
#include "help.h"
#include "term_mplex.h"
#include "avatars.h"
extern char *DATA_FILE;
extern ToxWindow *prompt;
extern ToxicFriend friends[MAX_FRIENDS_NUM];
extern char pending_frnd_requests[MAX_FRIENDS_NUM][TOX_CLIENT_ID_SIZE];
extern uint8_t num_frnd_requests;
extern FriendsList Friends;
extern FriendRequests FrndRequests;
/* command functions */
void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
char *msg;
if (argc != 1) {
msg = "Invalid syntax.";
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Request ID required.");
return;
}
int req = atoi(argv[1]);
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);
if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req > MAX_FRIEND_REQUESTS) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
return;
}
if (!strlen(pending_frnd_requests[req])) {
msg = "No pending friend request with that number.";
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
if (!FrndRequests.request[req].active) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
return;
}
int32_t friendnum = tox_add_friend_norequest(m, (uint8_t *) pending_frnd_requests[req]);
TOX_ERR_FRIEND_ADD err;
uint32_t friendnum = tox_friend_add_norequest(m, FrndRequests.request[req].key, &err);
if (friendnum == -1)
msg = "Failed to add friend.";
else {
msg = "Friend request accepted.";
if (err != TOX_ERR_FRIEND_ADD_OK) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to add friend (error %d\n)", err);
return;
} else {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Friend request accepted.");
on_friendadded(m, friendnum, true);
}
memset(&pending_frnd_requests[req], 0, TOX_CLIENT_ID_SIZE);
memset(&FrndRequests.request[req], 0, sizeof(struct friend_request));
int i;
for (i = num_frnd_requests; i > 0; --i) {
if (!strlen(pending_frnd_requests[i - 1]))
for (i = FrndRequests.max_idx; i > 0; --i) {
if (FrndRequests.request[i - 1].active)
break;
}
num_frnd_requests = i;
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
FrndRequests.max_idx = i;
--FrndRequests.num_requests;
}
void cmd_add_helper(ToxWindow *self, Tox *m, char *id_bin, char *msg)
void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg)
{
char *errmsg;
int32_t f_num = tox_add_friend(m, (uint8_t *) id_bin, (uint8_t *) msg, (uint16_t) strlen(msg));
const char *errmsg;
switch (f_num) {
case TOX_FAERR_TOOLONG:
TOX_ERR_FRIEND_ADD err;
uint32_t f_num = tox_friend_add(m, (uint8_t *) id_bin, (uint8_t *) msg, strlen(msg), &err);
switch (err) {
case TOX_ERR_FRIEND_ADD_TOO_LONG:
errmsg = "Message is too long.";
break;
case TOX_FAERR_NOMESSAGE:
case TOX_ERR_FRIEND_ADD_NO_MESSAGE:
errmsg = "Please add a message to your request.";
break;
case TOX_FAERR_OWNKEY:
case TOX_ERR_FRIEND_ADD_OWN_KEY:
errmsg = "That appears to be your own ID.";
break;
case TOX_FAERR_ALREADYSENT:
case TOX_ERR_FRIEND_ADD_ALREADY_SENT:
errmsg = "Friend request has already been sent.";
break;
case TOX_FAERR_UNKNOWN:
errmsg = "Undefined error when adding friend.";
break;
case TOX_FAERR_BADCHECKSUM:
case TOX_ERR_FRIEND_ADD_BAD_CHECKSUM:
errmsg = "Bad checksum in address.";
break;
case TOX_FAERR_SETNEWNOSPAM:
case TOX_ERR_FRIEND_ADD_SET_NEW_NOSPAM:
errmsg = "Nospam was different.";
break;
default:
case TOX_ERR_FRIEND_ADD_MALLOC:
errmsg = "Core memory allocation failed.";
break;
case TOX_ERR_FRIEND_ADD_OK:
errmsg = "Friend request sent.";
on_friendadded(m, f_num, true);
break;
case TOX_ERR_FRIEND_ADD_NULL:
/* fallthrough */
default:
errmsg = "Faile to add friend: Unknown error.";
break;
}
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
}
void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
char *errmsg;
if (argc < 1) {
errmsg = "Invalid syntax.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Tox ID or address required.");
return;
}
char *id = argv[1];
const char *id = argv[1];
char msg[MAX_STR_SIZE];
if (argc > 1) {
char *temp = argv[2];
if (temp[0] != '\"') {
errmsg = "Message must be enclosed in quotes.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
if (argv[2][0] != '\"') {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Message must be enclosed in quotes.");
return;
}
++temp;
temp[strlen(temp) - 1] = '\0';
snprintf(msg, sizeof(msg), "%s", temp);
/* remove opening and closing quotes */
char tmp[MAX_STR_SIZE];
snprintf(tmp, sizeof(tmp), "%s", &argv[2][1]);
int len = strlen(tmp) - 1;
tmp[len] = '\0';
snprintf(msg, sizeof(msg), "%s", tmp);
} else {
char selfname[TOX_MAX_NAME_LENGTH];
uint16_t n_len = tox_get_self_name(m, (uint8_t *) selfname);
tox_self_get_name(m, (uint8_t *) selfname);
size_t n_len = tox_self_get_name_size(m);
selfname[n_len] = '\0';
snprintf(msg, sizeof(msg), "Hello, my name is %s. Care to Tox?", selfname);
}
char id_bin[TOX_FRIEND_ADDRESS_SIZE] = {0};
char id_bin[TOX_ADDRESS_SIZE] = {0};
uint16_t id_len = (uint16_t) strlen(id);
/* try to add tox ID */
if (id_len == 2 * TOX_FRIEND_ADDRESS_SIZE) {
if (id_len == 2 * TOX_ADDRESS_SIZE) {
size_t i;
char xx[3];
uint32_t x;
for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; ++i) {
for (i = 0; i < TOX_ADDRESS_SIZE; ++i) {
xx[0] = id[2 * i];
xx[1] = id[2 * i + 1];
xx[2] = '\0';
if (sscanf(xx, "%02x", &x) != 1) {
errmsg = "Invalid ID.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid Tox ID.");
return;
}
@ -194,73 +197,173 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
}
}
void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 2 || strlen(argv[1]) < 3) {
avatar_unset(m);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Avatar is not set.");
return;
}
if (argv[1][0] != '\"') {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Path must be enclosed in quotes.");
return;
}
/* remove opening and closing quotes */
char path[MAX_STR_SIZE];
snprintf(path, sizeof(path), "%s", &argv[1][1]);
int len = strlen(path) - 1;
if (len <= 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid path.");
return;
}
path[len] = '\0';
char filename[MAX_STR_SIZE];
get_file_name(filename, sizeof(filename), path);
if (avatar_set(m, path, len) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,
"Failed to set avatar. Avatars must be in PNG format and may not exceed %d bytes.",
MAX_AVATAR_FILE_SIZE);
return;
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Avatar set to '%s'", filename);
}
void cmd_clear(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
line_info_clear(self->chatwin->hst);
wclear(window);
endwin();
refresh();
force_refresh(window);
}
void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
char *errmsg;
/* check arguments */
if (argc != 3) {
errmsg = "Invalid syntax.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Require: <ip> <port> <key>");
return;
}
const char *ip = argv[1];
const char *port = argv[2];
const char *key = argv[3];
const char *ascii_key = argv[3];
if (atoi(port) == 0) {
errmsg = "Invalid syntax.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid port.");
return;
}
char *binary_string = hex_string_to_bin(key);
tox_bootstrap_from_address(m, ip, TOX_ENABLE_IPV6_DEFAULT, htons(atoi(port)), (uint8_t *) binary_string);
free(binary_string);
char key_binary[TOX_PUBLIC_KEY_SIZE * 2 + 1];
if (hex_string_to_bin(ascii_key, strlen(ascii_key), key_binary, TOX_PUBLIC_KEY_SIZE) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid key.");
return;
}
TOX_ERR_BOOTSTRAP err;
tox_bootstrap(m, ip, atoi(port), (uint8_t *) key_binary, &err);
tox_add_tcp_relay(m, ip, atoi(port), (uint8_t *) key_binary, &err);
switch (err) {
case TOX_ERR_BOOTSTRAP_BAD_HOST:
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed: Invalid IP.");
break;
case TOX_ERR_BOOTSTRAP_BAD_PORT:
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed: Invalid port.");
break;
case TOX_ERR_BOOTSTRAP_NULL:
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed.");
break;
default:
break;
}
}
void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Request ID required.");
return;
}
int req = atoi(argv[1]);
if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req > MAX_FRIEND_REQUESTS) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
return;
}
if (!FrndRequests.request[req].active) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
return;
}
memset(&FrndRequests.request[req], 0, sizeof(struct friend_request));
int i;
for (i = FrndRequests.max_idx; i > 0; --i) {
if (FrndRequests.request[i - 1].active)
break;
}
FrndRequests.max_idx = i;
--FrndRequests.num_requests;
}
void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
char *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);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open.");
return;
}
int groupnum = tox_add_groupchat(m);
if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Please specify group type: text | audio");
return;
}
uint8_t type;
if (!strcasecmp(argv[1], "audio"))
type = TOX_GROUPCHAT_TYPE_AV;
else if (!strcasecmp(argv[1], "text"))
type = TOX_GROUPCHAT_TYPE_TEXT;
else {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Valid group types are: text | audio");
return;
}
int groupnum = -1;
if (type == TOX_GROUPCHAT_TYPE_TEXT)
groupnum = tox_add_groupchat(m);
#ifdef AUDIO
else
groupnum = toxav_add_av_groupchat(m, NULL, NULL);
#endif
if (groupnum == -1) {
errmsg = "Group chat instance failed to initialize.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize.");
return;
}
if (init_groupchat_win(prompt, m, groupnum) == -1) {
errmsg = "Group chat window failed to initialize.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
if (init_groupchat_win(prompt, m, groupnum, type) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize.");
tox_del_groupchat(m, groupnum);
return;
}
char 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);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat [%d] created.", groupnum);
}
void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
char *msg;
const char *msg;
struct chatlog *log = self->chatwin->log;
if (argc == 0) {
@ -269,90 +372,90 @@ void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
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);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
return;
}
char *swch = argv[1];
const char *swch = argv[1];
if (!strcmp(swch, "1") || !strcmp(swch, "on")) {
char myid[TOX_ADDRESS_SIZE];
tox_self_get_address(m, (uint8_t *) myid);
int log_ret = -1;
if (self->is_chat) {
friends[self->num].logging_on = true;
log_enable(self->name, friends[self->num].pub_key, log);
Friends.list[self->num].logging_on = true;
log_ret = log_enable(self->name, myid, Friends.list[self->num].pub_key, log, LOG_CHAT);
} else if (self->is_prompt) {
char myid[TOX_FRIEND_ADDRESS_SIZE];
tox_get_address(m, (uint8_t *) myid);
log_enable(self->name, myid, log);
log_ret = log_enable(self->name, myid, NULL, log, LOG_PROMPT);
} else if (self->is_groupchat) {
log_enable(self->name, NULL, log);
log_ret = log_enable(self->name, myid, NULL, log, LOG_GROUP);
}
msg = "Logging enabled";
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
msg = log_ret == 0 ? "Logging enabled." : "Warning: Log failed to initialize.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
return;
} else if (!strcmp(swch, "0") || !strcmp(swch, "off")) {
if (self->is_chat)
friends[self->num].logging_on = false;
Friends.list[self->num].logging_on = false;
log_disable(log);
msg = "Logging disabled";
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
msg = "Logging disabled.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
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);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
}
void cmd_myid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
char id[TOX_FRIEND_ADDRESS_SIZE * 2 + 1] = {0};
char address[TOX_FRIEND_ADDRESS_SIZE];
tox_get_address(m, (uint8_t *) address);
char id[TOX_ADDRESS_SIZE * 2 + 1] = {0};
char address[TOX_ADDRESS_SIZE];
tox_self_get_address(m, (uint8_t *) address);
size_t i;
for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; ++i) {
for (i = 0; i < TOX_ADDRESS_SIZE; ++i) {
char xx[3];
snprintf(xx, sizeof(xx), "%02X", address[i] & 0xff);
strcat(id, xx);
}
line_info_add(self, NULL, NULL, NULL, id, SYS_MSG, 0, 0);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", id);
}
void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
char *errmsg;
/* check arguments */
if (argc < 1) {
errmsg = "Invalid name.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Input required.");
return;
}
char *nick = argv[1];
int len = strlen(nick);
char nick[MAX_STR_SIZE];
size_t len = 0;
if (nick[0] == '\"') {
++nick;
len -= 2;
if (argv[1][0] == '\"') { /* remove opening and closing quotes */
snprintf(nick, sizeof(nick), "%s", &argv[1][1]);
len = strlen(nick) - 1;
nick[len] = '\0';
} else {
snprintf(nick, sizeof(nick), "%s", argv[1]);
len = strlen(nick);
}
if (!valid_nick(nick)) {
errmsg = "Invalid name.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid name.");
return;
}
len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
nick[len] = '\0';
tox_set_name(m, (uint8_t *) nick, (uint16_t) len);
tox_self_set_name(m, (uint8_t *) nick, len, NULL);
prompt_update_nick(prompt, nick);
store_data(m, DATA_FILE);
@ -360,27 +463,23 @@ void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
void cmd_note(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
char *errmsg;
if (argc < 1) {
errmsg = "Wrong number of arguments.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Input required.");
return;
}
char *msg = argv[1];
if (msg[0] != '\"') {
errmsg = "Note must be enclosed in quotes.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
if (argv[1][0] != '\"') {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Note must be enclosed in quotes.");
return;
}
++msg;
/* remove opening and closing quotes */
char msg[MAX_STR_SIZE];
snprintf(msg, sizeof(msg), "%s", &argv[1][1]);
int len = strlen(msg) - 1;
msg[len] = '\0';
tox_set_status_message(m, (uint8_t *) msg, (uint16_t) len);
prompt_update_statusmessage(prompt, msg);
prompt_update_statusmessage(prompt, m, msg);
}
void cmd_prompt_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
@ -393,50 +492,84 @@ void cmd_quit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
exit_toxic_success(m);
}
void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
void cmd_requests(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
char *msg = NULL;
char *errmsg;
if (FrndRequests.num_requests == 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend requests.");
return;
}
if (argc >= 2) {
msg = argv[2];
int i, j;
int count = 0;
if (msg[0] != '\"') {
errmsg = "Note must be enclosed in quotes.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return;
for (i = 0; i < FrndRequests.max_idx; ++i) {
if (!FrndRequests.request[i].active)
continue;
char id[TOX_PUBLIC_KEY_SIZE * 2 + 1] = {0};
for (j = 0; j < TOX_PUBLIC_KEY_SIZE; ++j) {
char d[3];
snprintf(d, sizeof(d), "%02X", FrndRequests.request[i].key[j] & 0xff);
strcat(id, d);
}
} else if (argc != 1) {
errmsg = "Wrong number of arguments.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return;
}
char *status = argv[1];
str_to_lower(status);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%d : %s", i, id);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", FrndRequests.request[i].msg);
TOX_USERSTATUS status_kind;
if (!strcmp(status, "online"))
status_kind = TOX_USERSTATUS_NONE;
else if (!strcmp(status, "away"))
status_kind = TOX_USERSTATUS_AWAY;
else if (!strcmp(status, "busy"))
status_kind = TOX_USERSTATUS_BUSY;
else {
errmsg = "Invalid status. Valid statuses are: online, busy and away.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
return;
}
tox_set_user_status(m, status_kind);
prompt_update_status(prompt, status_kind);
if (msg != NULL) {
++msg;
int len = strlen(msg) - 1;
msg[len] = '\0'; /* remove opening and closing quotes */
tox_set_status_message(m, (uint8_t *) msg, (uint16_t) len);
prompt_update_statusmessage(prompt, msg);
if (++count < FrndRequests.num_requests)
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
}
}
void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
bool have_note = false;
const char *errmsg;
lock_status ();
if (argc >= 2) {
have_note = true;
} else if (argc < 1) {
errmsg = "Require a status. Statuses are: online, busy and away.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
goto finish;
}
const char *status_str = argv[1];
TOX_USER_STATUS status;
if (!strcasecmp(status_str, "online"))
status = TOX_USER_STATUS_NONE;
else if (!strcasecmp(status_str, "away"))
status = TOX_USER_STATUS_AWAY;
else if (!strcasecmp(status_str, "busy"))
status = TOX_USER_STATUS_BUSY;
else {
errmsg = "Invalid status. Valid statuses are: online, busy and away.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
goto finish;
}
tox_self_set_status(m, status);
prompt_update_status(prompt, status);
if (have_note) {
if (argv[2][0] != '\"') {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Note must be enclosed in quotes.");
goto finish;
}
/* remove opening and closing quotes */
char msg[MAX_STR_SIZE];
snprintf(msg, sizeof(msg), "%s", &argv[2][1]);
int len = strlen(msg) - 1;
msg[len] = '\0';
prompt_update_statusmessage(prompt, m, msg);
}
finish:
unlock_status ();
}

View File

@ -20,16 +20,18 @@
*
*/
#ifndef _global_commands_h
#define _global_commands_h
#ifndef GLOBAL_COMMANDS_H
#define GLOBAL_COMMANDS_H
#include "windows.h"
#include "toxic.h"
void cmd_accept(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_add(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, 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_decline(WINDOW *window, ToxWindow *self, Tox *m, 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]);
@ -37,13 +39,14 @@ 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_requests(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_status(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_add_helper(ToxWindow *self, Tox *m, char *id_bin, char *msg);
void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg);
#ifdef _SUPPORT_AUDIO
#ifdef AUDIO
void cmd_list_devices(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_change_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
#endif /* _SUPPORT_AUDIO */
#endif /* AUDIO */
#endif /* #define _global_commands_h */
#endif /* #define GLOBAL_COMMANDS_H */

79
src/group_commands.c Normal file
View File

@ -0,0 +1,79 @@
/* group_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/>.
*
*/
#include <string.h>
#include "toxic.h"
#include "windows.h"
#include "line_info.h"
#include "misc_tools.h"
#include "log.h"
void cmd_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
char title[MAX_STR_SIZE];
if (argc < 1) {
int tlen = tox_group_get_title(m, self->num, (uint8_t *) title, TOX_MAX_NAME_LENGTH);
if (tlen != -1) {
title[tlen] = '\0';
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is set to: %s", title);
} else {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is not set");
}
return;
}
if (argv[1][0] != '\"') {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title must be enclosed in quotes.");
return;
}
/* remove opening and closing quotes */
snprintf(title, sizeof(title), "%s", &argv[1][1]);
int len = strlen(title) - 1;
title[len] = '\0';
if (tox_group_set_title(m, self->num, (uint8_t *) title, len) != 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set title.");
return;
}
set_window_title(self, title, len);
char timefrmt[TIME_STR_SIZE];
char selfnick[TOX_MAX_NAME_LENGTH];
get_time_str(timefrmt, sizeof(timefrmt));
tox_self_get_name(m, (uint8_t *) selfnick);
size_t sn_len = tox_self_get_name_size(m);
selfnick[sn_len] = '\0';
line_info_add(self, timefrmt, selfnick, NULL, NAME_CHANGE, 0, 0, " set the group title to: %s", title);
char tmp_event[MAX_STR_SIZE];
snprintf(tmp_event, sizeof(tmp_event), "set title to %s", title);
write_to_log(tmp_event, selfnick, self->chatwin->log, true);
}

View File

@ -1,4 +1,4 @@
/* file_senders.h
/* group_commands.h
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
@ -20,32 +20,12 @@
*
*/
#ifndef _filesenders_h
#define _filesenders_h
#ifndef GROUP_COMMANDS_H
#define GROUP_COMMANDS_H
#include "toxic.h"
#include "windows.h"
#include "toxic.h"
#define FILE_PIECE_SIZE 2048 /* must be >= (MAX_CRYPTO_DATA_SIZE - 2) in toxcore/net_crypto.h */
#define MAX_FILES 255
#define TIMEOUT_FILESENDER 120
void cmd_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
typedef struct {
FILE *file;
ToxWindow *toxwin;
int32_t friendnum;
bool active;
int filenum;
char nextpiece[FILE_PIECE_SIZE];
uint16_t piecelen;
char pathname[MAX_STR_SIZE];
uint64_t timestamp;
uint64_t last_progress;
uint64_t size;
uint32_t line_id;
} FileSender;
void close_all_file_senders(Tox *m);
void do_file_senders(Tox *m);
#endif /* #define _filesenders_h */
#endif /* GROUP_COMMANDS_H */

View File

@ -26,8 +26,24 @@
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <time.h>
#include <wchar.h>
#include <unistd.h>
#ifdef AUDIO
#ifdef __APPLE__
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#else
#include <AL/al.h>
#include <AL/alc.h>
/* compatibility with older versions of OpenAL */
#ifndef ALC_ALL_DEVICES_SPECIFIER
#include <AL/alext.h>
#endif /* ALC_ALL_DEVICES_SPECIFIER */
#endif /* __APPLE__ */
#endif /* AUDIO */
#include "windows.h"
#include "toxic.h"
@ -41,6 +57,9 @@
#include "settings.h"
#include "input.h"
#include "help.h"
#include "notify.h"
#include "autocomplete.h"
#include "device.h"
extern char *DATA_FILE;
@ -48,30 +67,84 @@ static GroupChat groupchats[MAX_GROUPCHAT_NUM];
static int max_groupchat_index = 0;
extern struct user_settings *user_settings;
extern struct Winthread Winthread;
/* temporary until group chats have unique commands */
extern const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE];
#ifdef AUDIO
#define AC_NUM_GROUP_COMMANDS 22
#else
#define AC_NUM_GROUP_COMMANDS 18
#endif /* AUDIO */
int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum)
/* Array of groupchat command names used for tab completion. */
static const char group_cmd_list[AC_NUM_GROUP_COMMANDS][MAX_CMDNAME_SIZE] = {
{ "/accept" },
{ "/add" },
{ "/avatar" },
{ "/clear" },
{ "/close" },
{ "/connect" },
{ "/decline" },
{ "/exit" },
{ "/group" },
{ "/help" },
{ "/log" },
{ "/myid" },
{ "/nick" },
{ "/note" },
{ "/quit" },
{ "/requests" },
{ "/status" },
{ "/title" },
#ifdef AUDIO
{ "/lsdev" },
{ "/sdev" },
{ "/mute" },
{ "/sense" },
#endif /* AUDIO */
};
#ifdef AUDIO
static int group_audio_open_out_device(int groupnum);
static int group_audio_close_out_device(int groupnum);
#endif /* AUDIO */
int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum, uint8_t type)
{
if (groupnum > MAX_GROUPCHAT_NUM)
return -1;
ToxWindow self = new_group_chat(m, groupnum);
int i;
for (i = 0; i <= max_groupchat_index; ++i) {
if (!groupchats[i].active) {
groupchats[i].chatwin = add_window(m, new_group_chat(m, groupnum));
groupchats[i].chatwin = add_window(m, self);
groupchats[i].active = true;
groupchats[i].num_peers = 0;
groupchats[i].type = type;
groupchats[i].start_time = get_unix_time();
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));
if (groupchats[i].peer_names == NULL || groupchats[i].oldpeer_names == NULL
|| groupchats[i].peer_name_lengths == NULL || groupchats[i].oldpeer_name_lengths == NULL)
exit_toxic_err("failed in init_groupchat_win", FATALERR_MEMORY);
memcpy(&groupchats[i].oldpeer_names[0], UNKNOWN_NAME, sizeof(UNKNOWN_NAME));
groupchats[i].oldpeer_name_lengths[0] = (uint16_t) strlen(UNKNOWN_NAME);
#ifdef AUDIO
if (type == TOX_GROUPCHAT_TYPE_AV)
if (group_audio_open_out_device(i) == -1)
fprintf(stderr, "Group Audio failed to init\n");
#endif /* AUDIO */
set_active_window(groupchats[i].chatwin);
if (i == max_groupchat_index)
@ -84,7 +157,7 @@ int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum)
return -1;
}
void kill_groupchat_window(ToxWindow *self)
static void kill_groupchat_window(ToxWindow *self)
{
ChatContext *ctx = self->chatwin;
@ -94,15 +167,17 @@ void kill_groupchat_window(ToxWindow *self)
delwin(ctx->history);
delwin(ctx->sidebar);
free(ctx->log);
free(ctx->hst);
free(ctx);
free(self->help);
del_window(self);
}
static void close_groupchat(ToxWindow *self, Tox *m, int groupnum)
void close_groupchat(ToxWindow *self, Tox *m, int groupnum)
{
tox_del_groupchat(m, groupnum);
#ifdef AUDIO
group_audio_close_out_device(groupnum);
#endif
free(groupchats[groupnum].peer_names);
free(groupchats[groupnum].oldpeer_names);
@ -121,6 +196,42 @@ static void close_groupchat(ToxWindow *self, Tox *m, int groupnum)
kill_groupchat_window(self);
}
/* destroys and re-creates groupchat window with or without the peerlist */
void redraw_groupchat_win(ToxWindow *self)
{
ChatContext *ctx = self->chatwin;
endwin();
refresh();
clear();
int x2, y2;
getmaxyx(stdscr, y2, x2);
y2 -= 2;
if (ctx->sidebar) {
delwin(ctx->sidebar);
ctx->sidebar = NULL;
}
delwin(ctx->linewin);
delwin(ctx->history);
delwin(self->window);
self->window = newwin(y2, x2, 0, 0);
ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0);
if (self->show_peerlist) {
ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2 - SIDEBAR_WIDTH - 1, 0, 0);
ctx->sidebar = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, SIDEBAR_WIDTH, 0, x2 - SIDEBAR_WIDTH);
} else {
ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0);
}
scrollok(ctx->history, 0);
}
static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, int groupnum, int peernum,
const char *msg, uint16_t len)
{
@ -130,34 +241,35 @@ static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, int groupnum, int
ChatContext *ctx = self->chatwin;
char nick[TOX_MAX_NAME_LENGTH];
int n_len = tox_group_peername(m, groupnum, peernum, (uint8_t *) 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;
get_group_nick_truncate(m, nick, peernum, groupnum);
char selfnick[TOX_MAX_NAME_LENGTH];
uint16_t sn_len = tox_get_self_name(m, (uint8_t *) selfnick);
tox_self_get_name(m, (uint8_t *) selfnick);
size_t sn_len = tox_self_get_name_size(m);
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 - 1);
/* Only play sound if mentioned by someone else */
if (strcasestr(msg, selfnick) && strcmp(selfnick, nick)) {
sound_notify(self, generic_message, NT_WNDALERT_0, NULL);
if (self->active_box != -1)
box_silent_notify2(self, NT_NOFOCUS, self->active_box, "%s %s", nick, msg);
else
box_silent_notify(self, NT_NOFOCUS, &self->active_box, self->name, "%s %s", nick, msg);
if (nick_match) {
alert_type = WINDOW_ALERT_0;
beep = true;
nick_clr = RED;
}
alert_window(self, alert_type, beep);
else {
sound_notify(self, silent, NT_WNDALERT_1, NULL);
}
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
line_info_add(self, timefrmt, nick, NULL, msg, IN_MSG, 0, nick_clr);
line_info_add(self, timefrmt, nick, NULL, IN_MSG, 0, nick_clr, "%s", msg);
write_to_log(msg, nick, ctx->log, false);
}
@ -169,35 +281,60 @@ static void groupchat_onGroupAction(ToxWindow *self, Tox *m, int groupnum, int p
ChatContext *ctx = self->chatwin;
/* check if message contains own name and alert appropriately */
int alert_type = WINDOW_ALERT_1;
bool beep = false;
char nick[TOX_MAX_NAME_LENGTH];
get_group_nick_truncate(m, nick, peernum, groupnum);
char selfnick[TOX_MAX_NAME_LENGTH];
uint16_t n_len = tox_get_self_name(m, (uint8_t *) selfnick);
tox_self_get_name(m, (uint8_t *) selfnick);
size_t n_len = tox_self_get_name_size(m);
selfnick[n_len] = '\0';
bool nick_match = strcasestr(action, selfnick);
if (strcasestr(action, selfnick)) {
sound_notify(self, generic_message, NT_WNDALERT_0, NULL);
if (nick_match) {
alert_type = WINDOW_ALERT_0;
beep = true;
if (self->active_box != -1)
box_silent_notify2(self, NT_NOFOCUS, self->active_box, "* %s %s", nick, action );
else
box_silent_notify(self, NT_NOFOCUS, &self->active_box, self->name, "* %s %s", nick, action);
}
else {
sound_notify(self, silent, NT_WNDALERT_1, NULL);
}
alert_window(self, alert_type, beep);
char nick[TOX_MAX_NAME_LENGTH];
n_len = tox_group_peername(m, groupnum, peernum, (uint8_t *) nick);
n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH - 1);
nick[n_len] = '\0';
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
line_info_add(self, timefrmt, nick, NULL, action, ACTION, 0, 0);
line_info_add(self, timefrmt, nick, NULL, IN_ACTION, 0, 0, "%s", action);
write_to_log(action, nick, ctx->log, true);
}
static void groupchat_onGroupTitleChange(ToxWindow *self, Tox *m, int groupnum, int peernum, const char *title,
uint8_t length)
{
ChatContext *ctx = self->chatwin;
if (self->num != groupnum)
return;
set_window_title(self, title, length);
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
/* don't announce title when we join the room */
if (!timed_out(groupchats[self->num].start_time, GROUP_EVENT_WAIT))
return;
char nick[TOX_MAX_NAME_LENGTH];
get_group_nick_truncate(m, nick, peernum, groupnum);
line_info_add(self, timefrmt, nick, NULL, NAME_CHANGE, 0, 0, " set the group title to: %s", title);
char tmp_event[MAX_STR_SIZE];
snprintf(tmp_event, sizeof(tmp_event), "set title to %s", title);
write_to_log(tmp_event, nick, ctx->log, true);
}
/* 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)
{
@ -219,31 +356,75 @@ static void copy_peernames(int gnum, uint8_t peerlist[][TOX_MAX_NAME_LENGTH], ui
exit_toxic_err("failed in copy_peernames", FATALERR_MEMORY);
}
uint16_t unknown_len = (uint16_t) strlen(UNKNOWN_NAME);
uint16_t u_len = strlen(UNKNOWN_NAME);
int i;
for (i = 0; i < npeers; ++i) {
if (string_is_empty((char *) peerlist[i])) {
memcpy(&groupchats[gnum].peer_names[i * N], UNKNOWN_NAME, sizeof(UNKNOWN_NAME));
groupchats[gnum].peer_name_lengths[i] = unknown_len;
if (!lengths[i]) {
memcpy(&groupchats[gnum].peer_names[i * N], UNKNOWN_NAME, u_len);
groupchats[gnum].peer_names[i * N + u_len] = '\0';
groupchats[gnum].peer_name_lengths[i] = u_len;
} else {
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);
uint16_t n_len = MIN(lengths[i], TOXIC_MAX_NAME_LENGTH - 1);
memcpy(&groupchats[gnum].peer_names[i * N], peerlist[i], n_len);
groupchats[gnum].peer_names[i * N + n_len] = '\0';
groupchats[gnum].peer_name_lengths[i] = n_len;
filter_str((char *) &groupchats[gnum].peer_names[i * N], n_len);
}
}
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);
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)
struct group_add_thrd {
Tox *m;
ToxWindow *self;
int peernum;
int groupnum;
uint64_t timestamp;
pthread_t tid;
pthread_attr_t attr;
};
/* Waits GROUP_EVENT_WAIT seconds for a new peer to set their name before announcing them */
void *group_add_wait(void *data)
{
struct group_add_thrd *thrd = (struct group_add_thrd *) data;
ToxWindow *self = thrd->self;
Tox *m = thrd->m;
char peername[TOX_MAX_NAME_LENGTH];
/* keep polling for a name that differs from the default until we run out of time */
while (true) {
usleep(100000);
pthread_mutex_lock(&Winthread.lock);
get_group_nick_truncate(m, peername, thrd->peernum, thrd->groupnum);
if (strcmp(peername, DEFAULT_TOX_NAME) || timed_out(thrd->timestamp, GROUP_EVENT_WAIT)) {
pthread_mutex_unlock(&Winthread.lock);
break;
}
pthread_mutex_unlock(&Winthread.lock);
}
const char *event = "has joined the room";
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
pthread_mutex_lock(&Winthread.lock);
line_info_add(self, timefrmt, (char *) peername, NULL, CONNECTION, 0, GREEN, event);
write_to_log(event, (char *) peername, self->chatwin->log, true);
pthread_mutex_unlock(&Winthread.lock);
pthread_attr_destroy(&thrd->attr);
free(thrd);
pthread_exit(NULL);
}
static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnum, int peernum, uint8_t change)
{
if (self->num != groupnum)
return;
@ -270,7 +451,12 @@ static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnu
/* Update name/len lists */
uint8_t tmp_peerlist[num_peers][TOX_MAX_NAME_LENGTH];
uint16_t tmp_peerlens[num_peers];
tox_group_get_names(m, groupnum, tmp_peerlist, tmp_peerlens, num_peers);
if (tox_group_get_names(m, groupnum, tmp_peerlist, tmp_peerlens, num_peers) == -1) {
memset(tmp_peerlist, 0, sizeof(tmp_peerlist));
memset(tmp_peerlens, 0, sizeof(tmp_peerlens));
}
copy_peernames(groupnum, tmp_peerlist, tmp_peerlens, num_peers);
/* get current peername then sort namelist */
@ -286,20 +472,44 @@ static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnu
ChatContext *ctx = self->chatwin;
char *event;
const char *event;
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
switch (change) {
case TOX_CHAT_CHANGE_PEER_ADD:
event = "has joined the room";
line_info_add(self, timefrmt, (char *) peername, NULL, event, CONNECTION, 0, GREEN);
write_to_log(event, (char *) peername, ctx->log, true);
if (!timed_out(groupchats[groupnum].start_time, GROUP_EVENT_WAIT))
break;
struct group_add_thrd *thrd = malloc(sizeof(struct group_add_thrd));
thrd->m = m;
thrd->peernum = peernum;
thrd->groupnum = groupnum;
thrd->self = self;
thrd->timestamp = get_unix_time();
if (pthread_attr_init(&thrd->attr) != 0) {
free(thrd);
return;
}
if (pthread_attr_setdetachstate(&thrd->attr, PTHREAD_CREATE_DETACHED) != 0) {
pthread_attr_destroy(&thrd->attr);
free(thrd);
return;
}
if (pthread_create(&thrd->tid, &thrd->attr, group_add_wait, (void *) thrd) != 0) {
pthread_attr_destroy(&thrd->attr);
free(thrd);
return;
}
break;
case TOX_CHAT_CHANGE_PEER_DEL:
event = "has left the room";
line_info_add(self, timefrmt, (char *) oldpeername, NULL, event, CONNECTION, 0, 0);
line_info_add(self, timefrmt, (char *) oldpeername, NULL, DISCONNECTION, 0, RED, event);
if (groupchats[self->num].side_pos > 0)
--groupchats[self->num].side_pos;
@ -308,8 +518,15 @@ static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnu
break;
case TOX_CHAT_CHANGE_PEER_NAME:
if (!timed_out(groupchats[self->num].start_time, GROUP_EVENT_WAIT))
return;
/* ignore initial name change (TODO: this is a bad way to do this) */
if (strcmp((char *) oldpeername, DEFAULT_TOX_NAME) == 0)
return;
event = " is now known as ";
line_info_add(self, timefrmt, (char *) oldpeername, (char *) peername, event, NAME_CHANGE, 0, 0);
line_info_add(self, timefrmt, (char *) oldpeername, (char *) peername, NAME_CHANGE, 0, 0, event);
char tmp_event[TOXIC_MAX_NAME_LENGTH * 2 + 32];
snprintf(tmp_event, sizeof(tmp_event), "is now known as %s", (char *) peername);
@ -317,7 +534,7 @@ static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnu
break;
}
alert_window(self, WINDOW_ALERT_2, false);
sound_notify(self, silent, NT_WNDALERT_2, NULL);
}
static void send_group_action(ToxWindow *self, ChatContext *ctx, Tox *m, char *action)
@ -328,8 +545,8 @@ static void send_group_action(ToxWindow *self, ChatContext *ctx, Tox *m, char *a
}
if (tox_group_action_send(m, self->num, (uint8_t *) action, strlen(action)) == -1) {
char *errmsg = " * Failed to send action.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, RED);
const char *errmsg = " * Failed to send action.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, errmsg);
}
}
@ -364,31 +581,33 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
if (ctx->len > 0) {
int diff;
if ((ctx->line[0] != '/') || (ctx->line[1] == 'm' && ctx->line[2] == 'e'))
diff = complete_line(ctx, groupchats[self->num].peer_names,
groupchats[self->num].num_peers, TOX_MAX_NAME_LENGTH);
else
diff = complete_line(ctx, glob_cmd_list, AC_NUM_GLOB_COMMANDS, MAX_CMDNAME_SIZE);
/* TODO: make this not suck */
if (ctx->line[0] != L'/' || wcscmp(ctx->line, L"/me") == 0) {
diff = complete_line(self, groupchats[self->num].peer_names, groupchats[self->num].num_peers,
TOX_MAX_NAME_LENGTH);
} else if (wcsncmp(ctx->line, L"/avatar \"", wcslen(L"/avatar \"")) == 0) {
diff = dir_match(self, m, ctx->line, L"/avatar");
} else {
diff = complete_line(self, group_cmd_list, AC_NUM_GROUP_COMMANDS, MAX_CMDNAME_SIZE);
}
if (diff != -1) {
if (x + diff > x2 - 1) {
wmove(self->window, y, x + diff);
ctx->start += diff;
} else {
wmove(self->window, y, x + diff);
int wlen = MAX(0, wcswidth(ctx->line, sizeof(ctx->line) / sizeof(wchar_t)));
ctx->start = wlen < x2 ? 0 : wlen - x2 + 1;
}
} else {
beep();
sound_notify(self, notif_error, 0, NULL);
}
} else {
beep();
sound_notify(self, notif_error, 0, NULL);
}
} else if (key == T_KEY_C_RB) { /* Scroll peerlist up and down one position */
} else if (key == user_settings->key_peer_list_down) { /* Scroll peerlist up and down one position */
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 == T_KEY_C_LB) {
} else if (key == user_settings->key_peer_list_up) {
if (groupchats[self->num].side_pos > 0)
--groupchats[self->num].side_pos;
} else if (key == '\n') {
@ -413,8 +632,8 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
}
} else if (!string_is_empty(line)) {
if (tox_group_message_send(m, self->num, (uint8_t *) line, strlen(line)) == -1) {
char *errmsg = " * Failed to send message.";
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, RED);
const char *errmsg = " * Failed to send message.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, errmsg);
}
}
@ -431,7 +650,10 @@ static void groupchat_onDraw(ToxWindow *self, Tox *m)
ChatContext *ctx = self->chatwin;
pthread_mutex_lock(&Winthread.lock);
line_info_print(self);
pthread_mutex_unlock(&Winthread.lock);
wclear(ctx->linewin);
curs_set(1);
@ -440,41 +662,43 @@ static void groupchat_onDraw(ToxWindow *self, Tox *m)
mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]);
wclear(ctx->sidebar);
mvwhline(self->window, y2 - CHATBOX_HEIGHT, 0, ACS_HLINE, x2);
mvwvline(ctx->sidebar, 0, 0, ACS_VLINE, y2 - CHATBOX_HEIGHT);
mvwaddch(ctx->sidebar, y2 - CHATBOX_HEIGHT, 0, ACS_BTEE);
int num_peers = groupchats[self->num].num_peers;
if (self->show_peerlist) {
mvwvline(ctx->sidebar, 0, 0, ACS_VLINE, y2 - CHATBOX_HEIGHT);
mvwaddch(ctx->sidebar, y2 - CHATBOX_HEIGHT, 0, ACS_BTEE);
wmove(ctx->sidebar, 0, 1);
wattron(ctx->sidebar, A_BOLD);
wprintw(ctx->sidebar, "Peers: %d\n", num_peers);
wattroff(ctx->sidebar, A_BOLD);
int num_peers = groupchats[self->num].num_peers;
mvwaddch(ctx->sidebar, 1, 0, ACS_LTEE);
mvwhline(ctx->sidebar, 1, 1, ACS_HLINE, SIDEBAR_WIDTH - 1);
wmove(ctx->sidebar, 0, 1);
wattron(ctx->sidebar, A_BOLD);
wprintw(ctx->sidebar, "Peers: %d\n", num_peers);
wattroff(ctx->sidebar, A_BOLD);
int N = TOX_MAX_NAME_LENGTH;
int maxlines = y2 - SDBAR_OFST - CHATBOX_HEIGHT;
int i;
mvwaddch(ctx->sidebar, 1, 0, ACS_LTEE);
mvwhline(ctx->sidebar, 1, 1, ACS_HLINE, SIDEBAR_WIDTH - 1);
for (i = 0; i < num_peers && i < maxlines; ++i) {
wmove(ctx->sidebar, i + 2, 1);
int peer = i + groupchats[self->num].side_pos;
int maxlines = y2 - SDBAR_OFST - CHATBOX_HEIGHT;
int i;
/* truncate nick to fit in side panel without modifying list */
char tmpnck[TOX_MAX_NAME_LENGTH];
memcpy(tmpnck, &groupchats[self->num].peer_names[peer * N], SIDEBAR_WIDTH - 2);
tmpnck[SIDEBAR_WIDTH - 2] = '\0';
for (i = 0; i < num_peers && i < maxlines; ++i) {
wmove(ctx->sidebar, i + 2, 1);
int peer = i + groupchats[self->num].side_pos;
wprintw(ctx->sidebar, "%s\n", tmpnck);
/* truncate nick to fit in side panel without modifying list */
char tmpnck[TOX_MAX_NAME_LENGTH];
int maxlen = SIDEBAR_WIDTH - 2;
memcpy(tmpnck, &groupchats[self->num].peer_names[peer * TOX_MAX_NAME_LENGTH], maxlen);
tmpnck[maxlen] = '\0';
wprintw(ctx->sidebar, "%s\n", tmpnck);
}
}
int y, x;
getyx(self->window, y, x);
(void) x;
int new_x = ctx->start ? x2 - 1 : wcswidth(ctx->line, ctx->pos);
int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos));
wmove(self->window, y + 1, new_x);
wrefresh(self->window);
@ -502,8 +726,13 @@ static void groupchat_onInit(ToxWindow *self, Tox *m)
line_info_init(ctx->hst);
if (user_settings->autolog == AUTOLOG_ON)
log_enable(self->name, NULL, ctx->log);
if (user_settings->autolog == AUTOLOG_ON) {
char myid[TOX_ADDRESS_SIZE];
tox_self_get_address(m, (uint8_t *) myid);
if (log_enable(self->name, myid, NULL, ctx->log, LOG_GROUP) == -1)
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize.");
}
execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE);
@ -511,6 +740,124 @@ static void groupchat_onInit(ToxWindow *self, Tox *m)
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
}
#ifdef AUDIO
static int group_audio_open_out_device(int groupnum)
{
char dname[MAX_STR_SIZE];
get_primary_device_name(output, dname, sizeof(dname));
dname[MAX_STR_SIZE - 1] = '\0';
groupchats[groupnum].audio.dvhandle = alcOpenDevice(dname);
if (groupchats[groupnum].audio.dvhandle == NULL)
return -1;
groupchats[groupnum].audio.dvctx = alcCreateContext(groupchats[groupnum].audio.dvhandle, NULL);
alcMakeContextCurrent(groupchats[groupnum].audio.dvctx);
alGenBuffers(OPENAL_BUFS, groupchats[groupnum].audio.buffers);
alGenSources((uint32_t) 1, &groupchats[groupnum].audio.source);
alSourcei(groupchats[groupnum].audio.source, AL_LOOPING, AL_FALSE);
if (alcGetError(groupchats[groupnum].audio.dvhandle) != AL_NO_ERROR) {
group_audio_close_out_device(groupnum);
groupchats[groupnum].audio.dvhandle = NULL;
groupchats[groupnum].audio.dvctx = NULL;
return -1;
}
alSourceQueueBuffers(groupchats[groupnum].audio.source, OPENAL_BUFS, groupchats[groupnum].audio.buffers);
alSourcePlay(groupchats[groupnum].audio.source);
return 0;
}
static int group_audio_close_out_device(int groupnum)
{
if (!groupchats[groupnum].audio.dvhandle)
return -1;
if (!groupchats[groupnum].audio.dvctx)
return -1;
if (alcGetCurrentContext() != groupchats[groupnum].audio.dvctx)
alcMakeContextCurrent(groupchats[groupnum].audio.dvctx);
alDeleteSources((uint32_t) 1, &groupchats[groupnum].audio.source);
alDeleteBuffers(OPENAL_BUFS, groupchats[groupnum].audio.buffers);
alcMakeContextCurrent(NULL);
alcDestroyContext(groupchats[groupnum].audio.dvctx);
if (!alcCloseDevice(groupchats[groupnum].audio.dvhandle))
return -1;
return 0;
}
// static int group_audio_write(int peernum, int groupnum, const int16_t *pcm, unsigned int samples, uint8_t channels,
// unsigned int sample_rate)
// {
// if (!pcm)
// return -1;
// if (channels == 0 || channels > 2)
// return -2;
// ALuint bufid;
// ALint processed = 0, queued = 0;
// alGetSourcei(groupchats[groupnum].audio.source, AL_BUFFERS_PROCESSED, &processed);
// alGetSourcei(groupchats[groupnum].audio.source, AL_BUFFERS_QUEUED, &queued);
// fprintf(stderr, "source: %d, queued: %d, processed: %d\n", groupchats[groupnum].audio.source, queued, processed);
// if (processed) {
// ALuint bufids[processed];
// alSourceUnqueueBuffers(groupchats[groupnum].audio.source, processed, bufids);
// alDeleteBuffers(processed - 1, bufids + 1);
// bufid = bufids[0];
// } else if (queued < 16) {
// alGenBuffers(1, &bufid);
// } else {
// return -3;
// }
// int length = samples * channels * sizeof(int16_t);
// alBufferData(bufid, (channels == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16, pcm, length, sample_rate);
// alSourceQueueBuffers(groupchats[groupnum].audio.source, 1, &bufid);
// ALint state;
// alGetSourcei(groupchats[groupnum].audio.source, AL_SOURCE_STATE, &state);
// if (state != AL_PLAYING)
// alSourcePlay(groupchats[groupnum].audio.source);
// return 0;
// }
static void groupchat_onWriteDevice(ToxWindow *self, Tox *m, int groupnum, int peernum, const int16_t *pcm,
unsigned int samples, uint8_t channels, unsigned int sample_rate)
{
return;
// if (groupnum != self->num)
// return;
// if (peernum < 0)
// return;
// if (groupchats[groupnum].audio.dvhandle == NULL)
// fprintf(stderr, "dvhandle is null)\n");
// if (groupchats[groupnum].audio.dvctx == NULL)
// fprintf(stderr, "ctx is null\n");
// int ret = group_audio_write(peernum, groupnum, pcm, samples, channels, sample_rate);
// fprintf(stderr, "write: %d\n", ret);
}
#endif /* AUDIO */
ToxWindow new_group_chat(Tox *m, int groupnum)
{
ToxWindow ret;
@ -525,6 +872,11 @@ ToxWindow new_group_chat(Tox *m, int groupnum)
ret.onGroupMessage = &groupchat_onGroupMessage;
ret.onGroupNamelistChange = &groupchat_onGroupNamelistChange;
ret.onGroupAction = &groupchat_onGroupAction;
ret.onGroupTitleChange = &groupchat_onGroupTitleChange;
#ifdef AUDIO
ret.onWriteDevice = &groupchat_onWriteDevice;
#endif
snprintf(ret.name, sizeof(ret.name), "Group %d", groupnum);
@ -538,6 +890,8 @@ ToxWindow new_group_chat(Tox *m, int groupnum)
ret.help = help;
ret.num = groupnum;
ret.show_peerlist = true;
ret.active_box = -1;
return ret;
}

View File

@ -20,29 +20,67 @@
*
*/
#ifndef _groupchat_h
#define _groupchat_h
#ifndef GROUPCHAT_H
#define GROUPCHAT_H
#include "toxic.h"
#include "windows.h"
#ifdef AUDIO
#include "audio_call.h"
#endif
#ifdef AUDIO
#ifdef __APPLE__
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#else
#include <AL/al.h>
#include <AL/alc.h>
/* compatibility with older versions of OpenAL */
#ifndef ALC_ALL_DEVICES_SPECIFIER
#include <AL/alext.h>
#endif /* ALC_ALL_DEVICES_SPECIFIER */
#endif /* __APPLE__ */
#endif /* AUDIO */
#define SIDEBAR_WIDTH 16
#define SDBAR_OFST 2 /* Offset for the peer number box at the top of the statusbar */
#define MAX_GROUPCHAT_NUM MAX_WINDOWS_NUM - 2
#define GROUP_EVENT_WAIT 3
#ifdef AUDIO
struct GAudio {
ALCdevice *dvhandle; /* Handle of device selected/opened */
ALCcontext *dvctx;
ALuint source;
ALuint buffers[OPENAL_BUFS];
};
#endif /* AUDIO */
typedef struct {
int chatwin;
bool active;
uint8_t type;
int num_peers;
int side_pos; /* current position of the sidebar - used for scrolling up and down */
uint64_t start_time;
uint8_t *peer_names;
uint8_t *oldpeer_names;
uint16_t *peer_name_lengths;
uint16_t *oldpeer_name_lengths;
#ifdef AUDIO
struct GAudio audio;
#endif
} GroupChat;
void kill_groupchat_window(ToxWindow *self);
int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum);
void close_groupchat(ToxWindow *self, Tox *m, int groupnum);
int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum, uint8_t type);
/* destroys and re-creates groupchat window with or without the peerlist */
void redraw_groupchat_win(ToxWindow *self);
ToxWindow new_group_chat(Tox *m, int groupnum);
#endif /* #define _groupchat_h */
#endif /* #define GROUPCHAT_H */

View File

@ -27,7 +27,7 @@
#include "help.h"
#include "misc_tools.h"
#define HELP_MENU_HEIGHT 7
#define HELP_MENU_HEIGHT 9
#define HELP_MENU_WIDTH 26
void help_init_menu(ToxWindow *self)
@ -77,21 +77,32 @@ static void help_draw_menu(ToxWindow *self)
wattroff(win, A_BOLD | COLOR_PAIR(RED));
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, " G");
wprintw(win, " g");
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, "lobal commands\n");
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, " C");
wprintw(win, " c");
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, "hat commands\n");
wprintw(win, " g");
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, " K");
wprintw(win, "r");
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, "oup commands\n");
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, " f");
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, "riendlist controls\n");
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, " k");
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, "ey bindings\n");
wprintw(win, " E");
wprintw(win, " e");
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, "x");
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
@ -110,11 +121,11 @@ static void help_draw_bottom_menu(WINDOW *win)
wmove(win, y2 - 2, 1);
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, " M");
wprintw(win, " m");
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, "ain menu |");
wprintw(win, " E");
wprintw(win, " e");
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, "x");
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
@ -131,27 +142,30 @@ static void help_draw_global(ToxWindow *self)
wprintw(win, "Global Commands:\n");
wattroff(win, A_BOLD | COLOR_PAIR(RED));
wprintw(win, " /add <id> <msg> : Add contact with optional message\n");
wprintw(win, " /accept <n> : Accept friend request\n");
wprintw(win, " /add <addr> <msg> : Add contact with optional message\n");
wprintw(win, " /accept <id> : Accept friend request\n");
wprintw(win, " /avatar <path> : Set an avatar (leave path empty to unset)\n");
wprintw(win, " /decline <id> : Decline friend request\n");
wprintw(win, " /requests : List pending friend requests\n");
wprintw(win, " /connect <ip> <port> <key> : Manually connect to a DHT node\n");
wprintw(win, " /status <type> <msg> : Set status with optional note\n");
wprintw(win, " /note <msg> : Set a personal note\n");
wprintw(win, " /nick <nick> : Set your nickname\n");
wprintw(win, " /log <on> or <off> : Enable/disable logging\n");
wprintw(win, " /groupchat : Create a group chat\n");
wprintw(win, " /myid : Print your ID\n");
wprintw(win, " /group <type> : Create a group chat where type: text | audio\n");
wprintw(win, " /myid : Print your Tox ID\n");
wprintw(win, " /clear : Clear window history\n");
wprintw(win, " /close : Close the current chat window\n");
wprintw(win, " /quit or /exit : Exit Toxic\n");
#ifdef _SUPPORT_AUDIO
#ifdef AUDIO
wattron(win, A_BOLD);
wprintw(win, "\n Audio:\n");
wattroff(win, A_BOLD);
wprintw(win, " /lsdev <type> : List devices where type: in|out\n");
wprintw(win, " /sdev <type> <id> : Set active device\n");
#endif /* _SUPPORT_AUDIO */
#endif /* AUDIO */
help_draw_bottom_menu(win);
@ -172,22 +186,22 @@ static void help_draw_chat(ToxWindow *self)
wprintw(win, " /invite <n> : Invite contact to a group chat\n");
wprintw(win, " /join : Join a pending group chat\n");
wprintw(win, " /sendfile <path> : Send a file\n");
wprintw(win, " /savefile <n> : Receive a file\n");
wprintw(win, " /savefile <id> : Receive a file\n");
wprintw(win, " /cancel <type> <id> : Cancel file transfer where type: in|out\n");
#ifdef _SUPPORT_AUDIO
#ifdef AUDIO
wattron(win, A_BOLD);
wprintw(win, "\n Audio:\n");
wattroff(win, A_BOLD);
wprintw(win, " /call : Audio call\n");
wprintw(win, " /cancel : Cancel call\n");
wprintw(win, " /answer : Answer incomming call\n");
wprintw(win, " /answer : Answer incoming call\n");
wprintw(win, " /reject : Reject incoming call\n");
wprintw(win, " /hangup : Hangup active call\n");
wprintw(win, " /sdev <type> <id> : Change active device\n");
wprintw(win, " /mute <type> : Mute active device if in call\n");
wprintw(win, " /sense <n> : VAD sensitivity treshold\n");
#endif /* _SUPPORT_AUDIO */
wprintw(win, " /sense <n> : VAD sensitivity threshold\n");
#endif /* AUDIO */
help_draw_bottom_menu(win);
@ -205,11 +219,53 @@ static void help_draw_keys(ToxWindow *self)
wprintw(win, "Key bindings:\n");
wattroff(win, A_BOLD | COLOR_PAIR(RED));
wprintw(win, " Ctrl+O and Ctrl+P : Navigate through the tabs\n");
wprintw(win, " Ctrl+O and Ctrl+P : Navigate through the tabs\n");
wprintw(win, " Page Up and Page Down : Scroll window history one line\n");
wprintw(win, " Ctrl+F and Ctrl+V : Scroll window history half a page\n");
wprintw(win, " Ctrl+H : Move to the bottom of window history\n");
wprintw(win, " Ctrl+[ and Ctrl+] : Scroll peer list in groupchats\n");
wprintw(win, " Ctrl+B : Toggle the groupchat peerlist\n\n");
wprintw(win, " (Note: Custom keybindings override these defaults.)\n\n");
help_draw_bottom_menu(win);
box(win, ACS_VLINE, ACS_HLINE);
wrefresh(win);
}
static void help_draw_group(ToxWindow *self)
{
WINDOW *win = self->help->win;
wmove(win, 1, 1);
wattron(win, A_BOLD | COLOR_PAIR(RED));
wprintw(win, "Group commands:\n");
wattroff(win, A_BOLD | COLOR_PAIR(RED));
wprintw(win, " /title <msg> : Set group title (show current title if no msg)\n\n");
help_draw_bottom_menu(win);
box(win, ACS_VLINE, ACS_HLINE);
wrefresh(win);
}
static void help_draw_contacts(ToxWindow *self)
{
WINDOW *win = self->help->win;
wmove(win, 1, 1);
wattron(win, A_BOLD | COLOR_PAIR(RED));
wprintw(win, "Friendlist controls:\n");
wattroff(win, A_BOLD | COLOR_PAIR(RED));
wprintw(win, " Up and Down arrows : Scroll through list\n");
wprintw(win, " Right and Left arrows : Switch between friendlist and blocked list\n");
wprintw(win, " Enter : Open a chat window with selected contact\n");
wprintw(win, " Delete : Permanently delete a contact\n");
wprintw(win, " B : Block or unblock a contact\n");
help_draw_bottom_menu(win);
@ -226,7 +282,7 @@ void help_onKey(ToxWindow *self, wint_t key)
break;
case 'c':
#ifdef _SUPPORT_AUDIO
#ifdef AUDIO
help_init_window(self, 19, 80);
#else
help_init_window(self, 9, 80);
@ -235,16 +291,26 @@ void help_onKey(ToxWindow *self, wint_t key)
break;
case 'g':
#ifdef _SUPPORT_AUDIO
help_init_window(self, 21, 80);
#ifdef AUDIO
help_init_window(self, 24, 80);
#else
help_init_window(self, 17, 80);
help_init_window(self, 20, 80);
#endif
self->help->type = HELP_GLOBAL;
break;
case 'k':
case 'r':
help_init_window(self, 6, 80);
self->help->type = HELP_GROUP;
break;
case 'f':
help_init_window(self, 10, 80);
self->help->type = HELP_CONTACTS;
break;
case 'k':
help_init_window(self, 13, 80);
self->help->type = HELP_KEYS;
break;
@ -276,7 +342,12 @@ void help_onDraw(ToxWindow *self)
help_draw_keys(self);
break;
case HELP_CONTACTS:
help_draw_contacts(self);
break;
case HELP_GROUP:
help_draw_group(self);
break;
}
}

View File

@ -20,8 +20,8 @@
*
*/
#ifndef _help_h
#define _help_h
#ifndef HELP_H
#define HELP_H
#include "toxic.h"
#include "windows.h"
@ -32,10 +32,11 @@ typedef enum {
HELP_CHAT,
HELP_GROUP,
HELP_KEYS,
HELP_CONTACTS,
} HELP_TYPES;
void help_onDraw(ToxWindow *self);
void help_init_menu(ToxWindow *self);
void help_onKey(ToxWindow *self, wint_t key);
#endif /* #define _help_h */
#endif /* #define HELP_H */

View File

@ -31,6 +31,11 @@
#include "misc_tools.h"
#include "toxic_strings.h"
#include "line_info.h"
#include "notify.h"
#include "groupchat.h"
#include "settings.h"
extern struct user_settings *user_settings;
/* add a char to input field and buffer */
void input_new_char(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y)
@ -41,12 +46,12 @@ void input_new_char(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_
/* this is the only place we need to do this check */
if (cur_len == -1) {
beep();
sound_notify(self, notif_error, 0, NULL);
return;
}
if (add_char_to_buf(ctx, key) == -1) {
beep();
sound_notify(self, notif_error, 0, NULL);
return;
}
@ -62,17 +67,15 @@ static void input_backspace(ToxWindow *self, int x, int mx_x)
ChatContext *ctx = self->chatwin;
if (del_char_buf_bck(ctx) == -1) {
beep();
sound_notify(self, notif_error, 0, NULL);
return;
}
int cur_len = wcwidth(ctx->line[ctx->pos - 1]);
int s_len = wcwidth(ctx->line[ctx->start - 1]);
int cur_len = ctx->pos > 0 ? wcwidth(ctx->line[ctx->pos - 1]) : 0;
int s_len = ctx->start > 0 ? wcwidth(ctx->line[ctx->start - 1]) : 0;
if (ctx->start && (x >= mx_x - cur_len))
ctx->start = MAX(0, ctx->start - 1 + (s_len - cur_len));
else if (ctx->start && (ctx->pos == ctx->len))
ctx->start = MAX(0, ctx->start - cur_len);
else if (ctx->start)
ctx->start = MAX(0, ctx->start - cur_len);
}
@ -81,21 +84,32 @@ static void input_backspace(ToxWindow *self, int x, int mx_x)
static void input_delete(ToxWindow *self)
{
if (del_char_buf_frnt(self->chatwin) == -1)
beep();
sound_notify(self, notif_error, 0, NULL);
}
/* delete last typed word */
static void input_del_word(ToxWindow *self, int x, int mx_x)
{
ChatContext *ctx = self->chatwin;
if (del_word_buf(ctx) == -1) {
sound_notify(self, notif_error, 0, NULL);
return;
}
}
/* deletes entire line before cursor from input field and buffer */
static void input_discard(ToxWindow *self)
{
if (discard_buf(self->chatwin) == -1)
beep();
sound_notify(self, notif_error, 0, NULL);
}
/* deletes entire line after cursor from input field and buffer */
static void input_kill(ChatContext *ctx)
{
if (kill_buf(ctx) == -1)
beep();
sound_notify(NULL, notif_error, NT_ALWAYS, NULL);
}
static void input_yank(ToxWindow *self, int x, int mx_x)
@ -103,7 +117,7 @@ static void input_yank(ToxWindow *self, int x, int mx_x)
ChatContext *ctx = self->chatwin;
if (yank_buf(ctx) == -1) {
beep();
sound_notify(self, notif_error, 0, NULL);
return;
}
@ -111,7 +125,7 @@ static void input_yank(ToxWindow *self, int x, int mx_x)
if (x + yank_cols >= mx_x) {
int rmdr = MAX(0, (x + yank_cols) - mx_x);
int s_len = wcswidth(&ctx->line[ctx->start], rmdr);
int s_len = MAX(0, wcswidth(&ctx->line[ctx->start], rmdr));
ctx->start += s_len + 1;
}
}
@ -123,7 +137,7 @@ static void input_mv_end(ToxWindow *self, int y, int mx_x)
ctx->pos = ctx->len;
int wlen = wcswidth(ctx->line, sizeof(ctx->line));
int wlen = MAX(0, wcswidth(ctx->line, sizeof(ctx->line) / sizeof(wchar_t)));
ctx->start = MAX(0, 1 + (mx_x * (wlen / mx_x) - mx_x) + (wlen % mx_x));
}
@ -147,16 +161,13 @@ static void input_mv_left(ToxWindow *self, int x, int mx_x)
if (ctx->pos <= 0)
return;
int cur_len = wcwidth(ctx->line[ctx->pos - 1]);
int cur_len = ctx->pos > 0 ? wcwidth(ctx->line[ctx->pos - 1]) : 0;
int s_len = ctx->start > 0 ? wcwidth(ctx->line[ctx->start - 1]) : 0;
--ctx->pos;
int s_len = wcwidth(ctx->line[ctx->start - 1]);
if (ctx->start && (x >= mx_x - cur_len))
ctx->start = MAX(0, ctx->start - 1 + (s_len - cur_len));
else if (ctx->start && (ctx->pos == ctx->len))
ctx->start = MAX(0, ctx->start - cur_len);
else if (ctx->start)
ctx->start = MAX(0, ctx->start - cur_len);
}
@ -185,7 +196,8 @@ static void input_history(ToxWindow *self, wint_t key, int mx_x)
ChatContext *ctx = self->chatwin;
fetch_hist_item(ctx, key);
ctx->start = mx_x * (ctx->len / mx_x);
int wlen = MAX(0, wcswidth(ctx->line, sizeof(ctx->line) / sizeof(wchar_t)));
ctx->start = wlen < mx_x ? 0 : wlen - mx_x + 1;
}
/* Handles non-printable input keys that behave the same for all types of chat windows.
@ -216,6 +228,10 @@ bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y)
input_yank(self, x, mx_x);
break;
case T_KEY_C_W:
input_del_word(self, x, mx_x);
break;
case KEY_HOME:
case T_KEY_C_A:
input_mv_home(self);
@ -239,10 +255,26 @@ bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y)
input_history(self, key, mx_x);
break;
case T_KEY_C_L:
force_refresh(self->chatwin->history);
break;
default:
match = false;
break;
}
/* TODO: this special case is ugly.
maybe convert entire function to if/else and make them all customizable keys? */
if (!match && key == user_settings->key_toggle_peerlist) {
if (self->is_groupchat) {
self->show_peerlist ^= 1;
redraw_groupchat_win(self);
}
match = true;
}
return match;
}

View File

@ -20,8 +20,8 @@
*
*/
#ifndef _input_h
#define _input_h
#ifndef INPUT_H
#define INPUT_H
/* add a char to input field and buffer for given chatcontext */
void input_new_char(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y);
@ -30,4 +30,4 @@ void input_new_char(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_
return true if key matches a function, false otherwise */
bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y);
#endif /* #define _input_h */
#endif /* #define INPUT_H */

View File

@ -23,12 +23,16 @@
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <stdarg.h>
#include "toxic.h"
#include "windows.h"
#include "line_info.h"
#include "groupchat.h"
#include "settings.h"
#include "notify.h"
#include "message_queue.h"
#include "misc_tools.h"
extern struct user_settings *user_settings;
@ -44,8 +48,8 @@ void line_info_init(struct history *hst)
hst->queue_sz = 0;
}
/* resets line_start (page end) */
static void line_info_reset_start(ToxWindow *self, struct history *hst)
/* resets line_start (moves to end of chat history) */
void line_info_reset_start(ToxWindow *self, struct history *hst)
{
struct line_info *line = hst->line_end;
@ -55,7 +59,7 @@ static void line_info_reset_start(ToxWindow *self, struct history *hst)
int y2, x2;
getmaxyx(self->window, y2, x2);
int side_offst = self->is_groupchat ? SIDEBAR_WIDTH : 0;
int side_offst = self->show_peerlist ? SIDEBAR_WIDTH : 0;
int top_offst = self->is_chat || self->is_prompt ? 2 : 0;
int max_y = (y2 - CHATBOX_HEIGHT - top_offst);
@ -87,6 +91,8 @@ void line_info_cleanup(struct history *hst)
if (hst->queue[i])
free(hst->queue[i]);
}
free(hst);
}
/* moves root forward and frees previous root */
@ -105,22 +111,13 @@ static void line_info_root_fwd(struct history *hst)
hst->line_root = tmp;
}
/* adds a line_info line to queue */
static void line_info_add_queue(struct history *hst, struct line_info *line)
{
if (hst->queue_sz >= MAX_QUEUE)
return;
hst->queue[hst->queue_sz++] = line;
}
/* returns ptr to queue item 0 and removes it from queue */
/* returns ptr to queue item 0 and removes it from queue. Returns NULL if queue is empty. */
static struct line_info *line_info_ret_queue(struct history *hst)
{
if (hst->queue_sz <= 0)
return NULL;
struct line_info *ret = hst->queue[0];
struct line_info *line = hst->queue[0];
int i;
@ -129,31 +126,64 @@ static struct line_info *line_info_ret_queue(struct history *hst)
--hst->queue_sz;
return ret;
return line;
}
/* creates new line_info line and puts it in the queue */
void line_info_add(ToxWindow *self, char *tmstmp, char *name1, char *name2, const char *msg, uint8_t type,
uint8_t bold, uint8_t colour)
/* creates new line_info line and puts it in the queue. */
void line_info_add(ToxWindow *self, const char *timestr, const char *name1, const char *name2, uint8_t type,
uint8_t bold, uint8_t colour, const char *msg, ...)
{
if (!self)
return;
struct history *hst = self->chatwin->hst;
if (hst->queue_sz >= MAX_LINE_INFO_QUEUE)
return;
struct line_info *new_line = calloc(1, sizeof(struct line_info));
if (new_line == NULL)
exit_toxic_err("failed in line_info_add", FATALERR_MEMORY);
char frmt_msg[MAX_LINE_INFO_MSG_SIZE] = {0};
va_list args;
va_start(args, msg);
vsnprintf(frmt_msg, sizeof(frmt_msg), msg, args);
va_end(args);
int len = 1; /* there will always be a newline */
/* for type-specific formatting in print function */
switch (type) {
case ACTION:
case IN_ACTION:
/* fallthrough */
case OUT_ACTION:
len += strlen(user_settings->line_normal) + 2;
break;
case IN_MSG:
/* fallthrough */
case OUT_MSG:
len += strlen(user_settings->line_normal) + 3;
break;
case CONNECTION:
len += 3;
len += strlen(user_settings->line_join) + 2;
break;
case DISCONNECTION:
len += strlen(user_settings->line_quit) + 2;
break;
case SYS_MSG:
break;
case NAME_CHANGE:
len += strlen(user_settings->line_alert) + 1;
break;
case PROMPT:
++len;
break;
@ -163,21 +193,21 @@ void line_info_add(ToxWindow *self, char *tmstmp, char *name1, char *name2, cons
break;
}
if (msg) {
snprintf(new_line->msg, sizeof(new_line->msg), "%s", msg);
if (frmt_msg[0]) {
snprintf(new_line->msg, sizeof(new_line->msg), "%s", frmt_msg);
len += strlen(new_line->msg);
int i;
for (i = 0; msg[i]; ++i) {
if (msg[i] == '\n')
for (i = 0; frmt_msg[i]; ++i) {
if (frmt_msg[i] == '\n')
++new_line->newlines;
}
}
if (tmstmp) {
snprintf(new_line->timestamp, sizeof(new_line->timestamp), "%s", tmstmp);
len += strlen(new_line->timestamp);
if (timestr) {
snprintf(new_line->timestr, sizeof(new_line->timestr), "%s", timestr);
len += strlen(new_line->timestr) + 1;
}
if (name1) {
@ -194,12 +224,14 @@ void line_info_add(ToxWindow *self, char *tmstmp, char *name1, char *name2, cons
new_line->type = type;
new_line->bold = bold;
new_line->colour = colour;
new_line->noread_flag = false;
new_line->timestamp = get_unix_time();
line_info_add_queue(hst, new_line);
hst->queue[hst->queue_sz++] = new_line;
}
/* adds a single queue item to hst if possible. only called once per call to line_info_print() */
static void line_info_check_queue(ToxWindow *self)
static void line_info_check_queue(ToxWindow *self)
{
struct history *hst = self->chatwin->hst;
struct line_info *line = line_info_ret_queue(hst);
@ -223,7 +255,7 @@ static void line_info_check_queue(ToxWindow *self)
if (x2 <= SIDEBAR_WIDTH)
return;
int offst = self->is_groupchat ? SIDEBAR_WIDTH : 0; /* offset width of groupchat sidebar */
int offst = self->show_peerlist ? SIDEBAR_WIDTH : 0; /* offset width of groupchat sidebar */
int lines = 1 + line->newlines + (line->len / (x2 - offst));
int max_y = y2 - CHATBOX_HEIGHT;
@ -237,6 +269,8 @@ static void line_info_check_queue(ToxWindow *self)
}
}
#define NOREAD_FLAG_TIMEOUT 5 /* seconds before a sent message with no read receipt is flagged as unread */
void line_info_print(ToxWindow *self)
{
ChatContext *ctx = self->chatwin;
@ -270,9 +304,12 @@ void line_info_print(ToxWindow *self)
switch (type) {
case OUT_MSG:
/* fallthrough */
case OUT_MSG_READ:
/* fallthrough */
case IN_MSG:
wattron(win, COLOR_PAIR(BLUE));
wprintw(win, "%s", line->timestamp);
wprintw(win, "%s ", line->timestr);
wattroff(win, COLOR_PAIR(BLUE));
int nameclr = GREEN;
@ -283,34 +320,66 @@ void line_info_print(ToxWindow *self)
nameclr = CYAN;
wattron(win, COLOR_PAIR(nameclr));
wprintw(win, "%s: ", line->name1);
wprintw(win, "%s %s: ", user_settings->line_normal, line->name1);
wattroff(win, COLOR_PAIR(nameclr));
if (line->msg[0] == '>')
wattron(win, COLOR_PAIR(GREEN));
else if (line->msg[0] == '<')
wattron(win, COLOR_PAIR(RED));
wprintw(win, "%s\n", line->msg);
wprintw(win, "%s", line->msg);
if (line->msg[0] == '>')
wattroff(win, COLOR_PAIR(GREEN));
else if (line->msg[0] == '<')
wattroff(win, COLOR_PAIR(RED));
if (type == OUT_MSG && timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) {
wattron(win, COLOR_PAIR(RED));
wprintw(win, " x", line->msg);
wattroff(win, COLOR_PAIR(RED));
if (line->noread_flag == false) {
line->noread_flag = true;
line->len += 2;
}
}
wprintw(win, "\n", line->msg);
break;
case ACTION:
case OUT_ACTION_READ:
/* fallthrough */
case OUT_ACTION:
/* fallthrough */
case IN_ACTION:
wattron(win, COLOR_PAIR(BLUE));
wprintw(win, "%s", line->timestamp);
wprintw(win, "%s ", line->timestr);
wattroff(win, COLOR_PAIR(BLUE));
wattron(win, COLOR_PAIR(YELLOW));
wprintw(win, "* %s %s\n", line->name1, line->msg);
wprintw(win, "%s %s %s", user_settings->line_normal, line->name1, line->msg);
wattroff(win, COLOR_PAIR(YELLOW));
if (type == OUT_ACTION && timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) {
wattron(win, COLOR_PAIR(RED));
wprintw(win, " x", line->msg);
wattroff(win, COLOR_PAIR(RED));
if (line->noread_flag == false) {
line->noread_flag = true;
line->len += 2;
}
}
wprintw(win, "\n", line->msg);
break;
case SYS_MSG:
if (line->timestamp[0]) {
if (line->timestr[0]) {
wattron(win, COLOR_PAIR(BLUE));
wprintw(win, "%s", line->timestamp);
wprintw(win, "%s ", line->timestr);
wattroff(win, COLOR_PAIR(BLUE));
}
@ -343,13 +412,33 @@ void line_info_print(ToxWindow *self)
case CONNECTION:
wattron(win, COLOR_PAIR(BLUE));
wprintw(win, "%s", line->timestamp);
wprintw(win, "%s ", line->timestr);
wattroff(win, COLOR_PAIR(BLUE));
wattron(win, COLOR_PAIR(line->colour));
wprintw(win, "%s ", user_settings->line_join);
wattron(win, A_BOLD);
wprintw(win, "* %s ", line->name1);
wprintw(win, "%s ", line->name1);
wattroff(win, A_BOLD);
wprintw(win, "%s\n", line->msg);
wattroff(win, COLOR_PAIR(line->colour));
break;
case DISCONNECTION:
wattron(win, COLOR_PAIR(BLUE));
wprintw(win, "%s ", line->timestr);
wattroff(win, COLOR_PAIR(BLUE));
wattron(win, COLOR_PAIR(line->colour));
wprintw(win, "%s ", user_settings->line_quit);
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));
@ -357,12 +446,13 @@ void line_info_print(ToxWindow *self)
case NAME_CHANGE:
wattron(win, COLOR_PAIR(BLUE));
wprintw(win, "%s", line->timestamp);
wprintw(win, "%s ", line->timestr);
wattroff(win, COLOR_PAIR(BLUE));
wattron(win, COLOR_PAIR(MAGENTA));
wprintw(win, "%s ", user_settings->line_alert);
wattron(win, A_BOLD);
wprintw(win, "* %s", line->name1);
wprintw(win, "%s", line->name1);
wattroff(win, A_BOLD);
wprintw(win, "%s", line->msg);
@ -383,6 +473,7 @@ void line_info_print(ToxWindow *self)
line_info_print(self);
}
/* puts msg in specified line_info msg buffer */
void line_info_set(ToxWindow *self, uint32_t id, char *msg)
{
struct line_info *line = self->chatwin->hst->line_end;
@ -406,14 +497,14 @@ static void line_info_scroll_up(struct history *hst)
{
if (hst->line_start->prev)
hst->line_start = hst->line_start->prev;
else beep();
else sound_notify(NULL, notif_error, NT_ALWAYS, NULL);
}
static void line_info_scroll_down(struct history *hst)
{
if (hst->line_start->next)
hst->line_start = hst->line_start->next;
else beep();
else sound_notify(NULL, notif_error, NT_ALWAYS, NULL);
}
static void line_info_page_up(ToxWindow *self, struct history *hst)
@ -445,35 +536,23 @@ bool line_info_onKey(ToxWindow *self, wint_t key)
struct history *hst = self->chatwin->hst;
bool match = true;
switch (key) {
/* TODO: Find good key bindings for all this stuff */
case T_KEY_C_F:
line_info_page_up(self, hst);
break;
case T_KEY_C_V:
line_info_page_down(self, hst);
break;
case KEY_PPAGE:
line_info_scroll_up(hst);
break;
case KEY_NPAGE:
line_info_scroll_down(hst);
break;
/* case ?:
line_info_goto_root(hst);
break; */
case T_KEY_C_H:
line_info_reset_start(self, hst);
break;
default:
match = false;
break;
if (key == user_settings->key_half_page_up) {
line_info_page_up(self, hst);
}
else if (key == user_settings->key_half_page_down) {
line_info_page_down(self, hst);
}
else if (key == user_settings->key_scroll_line_up) {
line_info_scroll_up(hst);
}
else if (key == user_settings->key_scroll_line_down) {
line_info_scroll_down(hst);
}
else if (key == user_settings->key_page_bottom) {
line_info_reset_start(self, hst);
}
else {
match = false;
}
return match;

View File

@ -20,36 +20,43 @@
*
*/
#ifndef _line_info_h
#define _line_info_h
#ifndef LINE_INFO_H
#define LINE_INFO_H
#include "windows.h"
#include "toxic.h"
#define MAX_HISTORY 10000
#define MAX_HISTORY 100000
#define MIN_HISTORY 40
#define MAX_QUEUE 128
#define MAX_LINE_INFO_QUEUE 1024
#define MAX_LINE_INFO_MSG_SIZE MAX_STR_SIZE + TOXIC_MAX_NAME_LENGTH + 32 /* needs extra room for log loading */
enum {
SYS_MSG,
IN_MSG,
OUT_MSG,
OUT_MSG_READ, /* for sent messages that have received a read reply. don't set this with line_info_add */
IN_ACTION,
OUT_ACTION,
OUT_ACTION_READ, /* same as OUT_MSG_READ but for actions */
PROMPT,
ACTION,
CONNECTION,
DISCONNECTION,
NAME_CHANGE,
} LINE_TYPE;
struct line_info {
char timestamp[TIME_STR_SIZE];
char name1[TOXIC_MAX_NAME_LENGTH];
char name2[TOXIC_MAX_NAME_LENGTH];
char msg[TOX_MAX_MESSAGE_LENGTH];
char timestr[TIME_STR_SIZE];
char name1[TOXIC_MAX_NAME_LENGTH + 1];
char name2[TOXIC_MAX_NAME_LENGTH + 1];
char msg[MAX_LINE_INFO_MSG_SIZE];
uint64_t timestamp;
uint8_t type;
uint8_t bold;
uint8_t colour;
uint8_t noread_flag; /* true if a line should be flagged as unread */
uint32_t id;
uint16_t len; /* combined len of all strings */
uint16_t len; /* combined len of entire line */
uint8_t newlines;
struct line_info *prev;
@ -63,13 +70,13 @@ struct history {
struct line_info *line_end;
uint32_t start_id; /* keeps track of where line_start should be when at bottom of history */
struct line_info *queue[MAX_QUEUE];
struct line_info *queue[MAX_LINE_INFO_QUEUE];
int queue_sz;
};
/* creates new line_info line and puts it in the queue */
void line_info_add(ToxWindow *self, char *tmstmp, char *name1, char *name2, const char *msg,
uint8_t type, uint8_t bold, uint8_t colour);
/* creates new line_info line and puts it in the queue. */
void line_info_add(ToxWindow *self, const char *timestr, const char *name1, const char *name2, uint8_t type,
uint8_t bold, uint8_t colour, const char *msg, ...);
/* Prints a section of history starting at line_start */
void line_info_print(ToxWindow *self);
@ -83,7 +90,10 @@ void line_info_clear(struct history *hst);
/* puts msg in specified line_info msg buffer */
void line_info_set(ToxWindow *self, uint32_t id, char *msg);
/* resets line_start (moves to end of chat history) */
void line_info_reset_start(ToxWindow *self, struct history *hst);
void line_info_init(struct history *hst);
bool line_info_onKey(ToxWindow *self, wint_t key); /* returns true if key is a match */
#endif /* #define _line_info_h */
#endif /* #define LINE_INFO_H */

238
src/log.c
View File

@ -23,6 +23,7 @@
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/stat.h>
#include "configdir.h"
#include "toxic.h"
@ -30,59 +31,93 @@
#include "misc_tools.h"
#include "log.h"
#include "settings.h"
#include "line_info.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(char *name, char *key, struct chatlog *log)
{
if (!log->log_on)
return;
/* There are three types of logs: chat logs, groupchat logs, and prompt logs (see LOG_TYPE in log.h)
A prompt log is in the format: LOGDIR/selfkey-home.log
A chat log is in the format: LOGDIR/selfkey-friendname-otherkey.log
A groupchat log is in the format: LOGDIR/selfkey-groupname-date[time].log
Only the first (KEY_IDENT_DIGITS * 2) numbers of the key are used.
Returns 0 on success, -1 if the path is too long */
static int get_log_path(char *dest, int destsize, char *name, const char *selfkey, const char *otherkey, int logtype)
{
if (!valid_nick(name))
name = UNKNOWN_NAME;
const char *namedash = logtype == LOG_PROMPT ? "" : "-";
const char *set_path = user_settings->chatlogs_path;
char *user_config_dir = get_user_config_dir();
int path_len = strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(name);
int path_len = strlen(name) + strlen(".log") + strlen("-") + strlen(namedash);
path_len += strlen(set_path) ? *set_path : strlen(user_config_dir) + strlen(LOGDIR);
/* use first 4 digits of key as log ident. If no key use a timestamp */
char ident[32];
/* first 6 digits of selfkey */
char self_id[32];
path_len += KEY_IDENT_DIGITS * 2;
sprintf(&self_id[0], "%02X", selfkey[0] & 0xff);
sprintf(&self_id[2], "%02X", selfkey[1] & 0xff);
sprintf(&self_id[4], "%02X", selfkey[2] & 0xff);
self_id[KEY_IDENT_DIGITS * 2] = '\0';
if (key != NULL) {
path_len += (KEY_IDENT_DIGITS * 2 + 5);
char other_id[32] = {0};
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;
switch (logtype) {
case LOG_CHAT:
path_len += KEY_IDENT_DIGITS * 2;
sprintf(&other_id[0], "%02X", otherkey[0] & 0xff);
sprintf(&other_id[2], "%02X", otherkey[1] & 0xff);
sprintf(&other_id[4], "%02X", otherkey[2] & 0xff);
other_id[KEY_IDENT_DIGITS * 2] = '\0';
break;
case LOG_GROUP:
strftime(other_id, sizeof(other_id), "%Y-%m-%d[%H:%M:%S]", get_time());
path_len += strlen(other_id);
break;
}
if (path_len > MAX_STR_SIZE) {
log->log_on = false;
if (path_len >= destsize) {
free(user_config_dir);
return;
return -1;
}
char log_path[MAX_STR_SIZE];
snprintf(log_path, MAX_STR_SIZE, "%s%s%s-%s.log",
user_config_dir, CONFIGDIR, name, ident);
if (!string_is_empty(set_path))
snprintf(dest, destsize, "%s%s-%s%s%s.log", set_path, self_id, name, namedash, other_id);
else
snprintf(dest, destsize, "%s%s%s-%s%s%s.log", user_config_dir, LOGDIR, self_id, name, namedash, other_id);
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");
return 0;
}
void write_to_log(const char *msg, char *name, struct chatlog *log, bool event)
/* Opens log file or creates a new one */
static int init_logging_session(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype)
{
if (selfkey == NULL || (logtype == LOG_CHAT && otherkey == NULL))
return -1;
char log_path[MAX_STR_SIZE];
if (get_log_path(log_path, sizeof(log_path), name, selfkey, otherkey, logtype) == -1)
return -1;
log->file = fopen(log_path, "a+");
snprintf(log->path, sizeof(log->path), "%s", log_path);
if (log->file == NULL)
return -1;
return 0;
}
#define LOG_FLUSH_LIMIT 1 /* limits calls to fflush to a max of one per LOG_FLUSH_LIMIT seconds */
void write_to_log(const char *msg, const char *name, struct chatlog *log, bool event)
{
if (!log->log_on)
return;
@ -99,33 +134,136 @@ void write_to_log(const char *msg, char *name, struct chatlog *log, bool event)
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]";
const char *t = user_settings->log_timestamp_format;
char 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)) {
if (timed_out(log->lastwrite, LOG_FLUSH_LIMIT)) {
fflush(log->file);
log->lastwrite = curtime;
log->lastwrite = get_unix_time();
}
}
void log_enable(char *name, char *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) {
if (log->file != NULL)
fclose(log->file);
log->file = NULL;
}
memset(log, 0, sizeof(struct chatlog));
}
int log_enable(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype)
{
log->log_on = true;
if (log->file != NULL)
return 0;
if (init_logging_session(name, selfkey, otherkey, log, logtype) == -1) {
log_disable(log);
return -1;
}
return 0;
}
/* Loads previous history from chat log */
void load_chat_history(ToxWindow *self, struct chatlog *log)
{
if (log->file == NULL)
return;
off_t sz = file_size(log->path);
if (sz <= 0)
return;
char *hstbuf = malloc(sz + 1);
if (hstbuf == NULL)
exit_toxic_err("failed in load_chat_history", FATALERR_MEMORY);
if (fseek(log->file, 0L, SEEK_SET) == -1) {
free(hstbuf);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to read log file");
return;
}
if (fread(hstbuf, sz, 1, log->file) != 1) {
free(hstbuf);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to read log file");
return;
}
hstbuf[sz] = '\0';
/* Number of history lines to load: must not be larger than MAX_LINE_INFO_QUEUE - 2 */
int L = MIN(MAX_LINE_INFO_QUEUE - 2, user_settings->history_size);
int start, count = 0;
/* start at end and backtrace L lines or to the beginning of buffer */
for (start = sz - 1; start >= 0 && count < L; --start) {
if (hstbuf[start] == '\n')
++count;
}
const char *line = strtok(&hstbuf[start + 1], "\n");
if (line == NULL) {
free(hstbuf);
return;
}
while (line != NULL && count--) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", line);
line = strtok(NULL, "\n");
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
free(hstbuf);
}
/* renames chatlog file replacing src with dest.
Returns 0 on success or if no log exists, -1 on failure. */
int rename_logfile(char *src, char *dest, const char *selfkey, const char *otherkey, int winnum)
{
ToxWindow *toxwin = get_window_ptr(winnum);
struct chatlog *log = NULL;
bool log_on = false;
/* disable log if necessary and save its state */
if (toxwin != NULL) {
log = toxwin->chatwin->log;
log_on = log->log_on;
}
if (log_on)
log_disable(log);
char newpath[MAX_STR_SIZE];
char oldpath[MAX_STR_SIZE];
if (get_log_path(oldpath, sizeof(oldpath), src, selfkey, otherkey, LOG_CHAT) == -1)
goto on_error;
if (!file_exists(oldpath))
return 0;
if (get_log_path(newpath, sizeof(newpath), dest, selfkey, otherkey, LOG_CHAT) == -1)
goto on_error;
if (rename(oldpath, newpath) != 0)
goto on_error;
if (log_on)
log_enable(dest, selfkey, otherkey, log, LOG_CHAT);
return 0;
on_error:
if (log_on)
log_enable(src, selfkey, otherkey, log, LOG_CHAT);
return -1;
}

View File

@ -20,28 +20,40 @@
*
*/
#ifndef _log_h
#define _log_h
#define LOG_FLUSH_LIMIT 2 /* limits calls to fflush(logfile) to a max of one per LOG_FLUSH_LIMIT seconds */
#ifndef LOG_H
#define LOG_H
struct chatlog {
FILE *file;
uint64_t lastwrite;
int pos;
char path[MAX_STR_SIZE];
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(char *name, char *key, struct chatlog *log);
enum {
LOG_GROUP,
LOG_PROMPT,
LOG_CHAT,
} LOG_TYPE;
/* formats/writes line to log file */
void write_to_log(const char *msg, char *name, struct chatlog *log, bool event);
void write_to_log(const char *msg, const char *name, struct chatlog *log, bool event);
/* enables logging for specified log and creates/fetches file if necessary */
void log_enable(char *name, char *key, struct chatlog *log);
/* enables logging for specified log and creates/fetches file if necessary.
*
* Returns 0 on success.
* Returns -1 on failure.
*/
int log_enable(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype);
/* disables logging for specified log and closes file */
void log_disable(struct chatlog *log);
#endif /* #define _log_h */
/* Loads previous history from chat log */
void load_chat_history(ToxWindow *self, struct chatlog *log);
/* renames chatlog file replacing src with dest.
Returns 0 on success or if no log exists, -1 on failure. */
int rename_logfile(char *src, char *dest, const char *selfkey, const char *otherkey, int winnum);
#endif /* #define LOG_H */

154
src/message_queue.c Normal file
View File

@ -0,0 +1,154 @@
/* message_queue.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 "toxic.h"
#include "windows.h"
#include "message_queue.h"
#include "misc_tools.h"
#include "line_info.h"
#include "log.h"
void cqueue_cleanup(struct chat_queue *q)
{
struct cqueue_msg *tmp1 = q->root;
while (tmp1) {
struct cqueue_msg *tmp2 = tmp1->next;
free(tmp1);
tmp1 = tmp2;
}
free(q);
}
void cqueue_add(struct chat_queue *q, const char *msg, size_t len, uint8_t type, uint32_t line_id)
{
struct cqueue_msg *new_m = malloc(sizeof(struct cqueue_msg));
if (new_m == NULL)
exit_toxic_err("failed in cqueue_message", FATALERR_MEMORY);
snprintf(new_m->message, sizeof(new_m->message), "%s", msg);
new_m->len = len;
new_m->type = type;
new_m->line_id = line_id;
new_m->last_send_try = 0;
new_m->receipt = 0;
new_m->next = NULL;
if (q->root == NULL) {
new_m->prev = NULL;
q->root = new_m;
} else {
new_m->prev = q->end;
q->end->next = new_m;
}
q->end = new_m;
}
/* update line to show receipt was received after queue removal */
static void cqueue_mark_read(ToxWindow *self, struct cqueue_msg *msg)
{
struct line_info *line = self->chatwin->hst->line_end;
while (line) {
if (line->id != msg->line_id) {
line = line->prev;
continue;
}
line->type = msg->type == OUT_ACTION ? OUT_ACTION_READ : OUT_MSG_READ;
if (line->noread_flag == true) {
line->len -= 2;
line->noread_flag = false;
}
return;
}
}
/* removes message with matching receipt from queue, writes to log and updates line to show the message was received. */
void cqueue_remove(ToxWindow *self, Tox *m, uint32_t receipt)
{
struct chat_queue *q = self->chatwin->cqueue;
struct cqueue_msg *msg = q->root;
while (msg) {
if (msg->receipt != receipt) {
msg = msg->next;
continue;
}
char selfname[TOX_MAX_NAME_LENGTH];
tox_self_get_name(m, (uint8_t *) selfname);
size_t len = tox_self_get_name_size(m);
selfname[len] = '\0';
write_to_log(msg->message, selfname, self->chatwin->log, msg->type == OUT_ACTION);
cqueue_mark_read(self, msg);
struct cqueue_msg *next = msg->next;
if (msg->prev == NULL) { /* root */
if (next)
next->prev = NULL;
free(msg);
q->root = next;
} else {
struct cqueue_msg *prev = msg->prev;
free(msg);
prev->next = next;
}
return;
}
}
#define CQUEUE_TRY_SEND_INTERVAL 60
/* Tries to send the oldest unsent message in queue. */
void cqueue_try_send(ToxWindow *self, Tox *m)
{
struct chat_queue *q = self->chatwin->cqueue;
struct cqueue_msg *msg = q->root;
if (!msg)
return;
if (msg->receipt != 0 && !timed_out(msg->last_send_try, CQUEUE_TRY_SEND_INTERVAL))
return;
uint32_t receipt = 0;
TOX_MESSAGE_TYPE type = msg->type == OUT_MSG ? TOX_MESSAGE_TYPE_NORMAL : TOX_MESSAGE_TYPE_ACTION;
receipt = tox_friend_send_message(m, self->num, type, (uint8_t *) msg->message, msg->len, NULL);
msg->last_send_try = get_unix_time();
msg->receipt = receipt;
return;
}

51
src/message_queue.h Normal file
View File

@ -0,0 +1,51 @@
/* message_queue.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 MESSAGE_QUEUE_H
#define MESSAGE_QUEUE_H
struct cqueue_msg {
char message[MAX_STR_SIZE];
size_t len;
int line_id;
uint8_t type;
uint32_t receipt;
uint64_t last_send_try;
struct cqueue_msg *next;
struct cqueue_msg *prev;
};
struct chat_queue {
struct cqueue_msg *root;
struct cqueue_msg *end;
};
void cqueue_cleanup(struct chat_queue *q);
void cqueue_add(struct chat_queue *q, const char *msg, size_t len, uint8_t type, uint32_t line_id);
/* Tries to send the oldest unsent message in queue. */
void cqueue_try_send(ToxWindow *self, Tox *m);
/* removes message with matching receipt from queue, writes to log and updates line to show the message was received. */
void cqueue_remove(ToxWindow *self, Tox *m, uint32_t receipt);
#endif /* #define MESSAGE_QUEUE_H */

View File

@ -25,17 +25,36 @@
#include <string.h>
#include <time.h>
#include <limits.h>
#include <dirent.h>
#include <sys/stat.h>
#include "toxic.h"
#include "windows.h"
#include "misc_tools.h"
#include "settings.h"
#include "file_transfers.h"
extern ToxWindow *prompt;
extern struct user_settings *user_settings;
static uint64_t current_unix_time;
void hst_to_net(uint8_t *num, uint16_t numbytes)
{
#ifndef WORDS_BIGENDIAN
uint32_t i;
uint8_t buff[numbytes];
for (i = 0; i < numbytes; ++i) {
buff[i] = num[numbytes - i - 1];
}
memcpy(num, buff, numbytes);
#endif
return;
}
/* Note: The time functions are not thread safe */
void update_unix_time(void)
{
current_unix_time = (uint64_t) time(NULL);
@ -47,9 +66,9 @@ uint64_t get_unix_time(void)
}
/* Returns 1 if connection has timed out, 0 otherwise */
int timed_out(uint64_t timestamp, uint64_t curtime, uint64_t timeout)
int timed_out(uint64_t timestamp, uint64_t timeout)
{
return timestamp + timeout <= curtime;
return timestamp + timeout <= get_unix_time();
}
/* Get the current local time */
@ -69,7 +88,7 @@ void get_time_str(char *buf, int bufsize)
return;
}
const char *t = user_settings->time == TIME_12 ? "[%-I:%M:%S] " : "[%H:%M:%S] ";
const char *t = user_settings->timestamp_format;
strftime(buf, bufsize, t, get_time());
}
@ -91,25 +110,51 @@ void get_elapsed_time_str(char *buf, int bufsize, uint64_t secs)
snprintf(buf, bufsize, "%ld:%.2ld:%.2ld", hours, minutes, seconds);
}
char *hex_string_to_bin(const char *hex_string)
/*
* Converts a hexidecimal string of length hex_len to binary format and puts the result in output.
* output_size must be exactly half of hex_len.
*
* Returns 0 on success.
* Returns -1 on failure.
*/
int hex_string_to_bin(const char *hex_string, size_t hex_len, char *output, size_t output_size)
{
size_t len = strlen(hex_string);
char *val = malloc(len);
if (output_size == 0 || hex_len != output_size * 2)
return -1;
if (val == NULL)
exit_toxic_err("failed in hex_string_to_bin", FATALERR_MEMORY);
for (size_t i = 0; i < output_size; ++i) {
sscanf(hex_string, "%2hhx", &output[i]);
hex_string += 2;
}
size_t i;
return 0;
}
for (i = 0; i < len; ++i, hex_string += 2)
sscanf(hex_string, "%2hhx", &val[i]);
int hex_string_to_bytes(char *buf, int size, const char *keystr)
{
if (size % 2 != 0)
return -1;
return val;
int i, res;
const char *pos = keystr;
for (i = 0; i < size; ++i) {
res = sscanf(pos, "%2hhx", &buf[i]);
pos += 2;
if (res == EOF || res < 1)
return -1;
}
return 0;
}
/* Returns 1 if the string is empty, 0 otherwise */
int string_is_empty(char *string)
int string_is_empty(const char *string)
{
if (!string)
return true;
return string[0] == '\0';
}
@ -141,41 +186,19 @@ int wcs_to_mbs_buf(char *buf, const wchar_t *string, size_t n)
return len;
}
/* Colours the window tab according to type. Beeps if is_beep is true */
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;
}
StatusBar *stb = prompt->stb;
if (is_beep && stb->status != TOX_USERSTATUS_BUSY && user_settings->alerts == ALERTS_ENABLED)
beep();
}
/* case-insensitive string compare function for use with qsort */
int qsort_strcasecmp_hlpr(const void *nick1, const void *nick2)
int qsort_strcasecmp_hlpr(const void *str1, const void *str2)
{
return strcasecmp((const char *) nick1, (const char *) nick2);
return strcasecmp((const char *) str1, (const char *) str2);
}
/* 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 */
int valid_nick(char *nick)
- must not contain contiguous spaces
- must not contain a newline or tab seqeunce */
int valid_nick(const char *nick)
{
if (!nick[0] || nick[0] == ' ')
return 0;
@ -183,37 +206,61 @@ int valid_nick(char *nick)
int i;
for (i = 0; nick[i]; ++i) {
if (nick[i] == ' ' && nick[i + 1] == ' ')
return 0;
if ((nick[i] == ' ' && nick[i + 1] == ' ')
|| nick[i] == '/'
|| nick[i] == '\n'
|| nick[i] == '\t'
|| nick[i] == '\v'
|| nick[i] == '\r')
if (nick[i] == '/')
return 0;
}
return 1;
}
/* gets base file name from path or original file name if no path is supplied */
void get_file_name(char *namebuf, int bufsize, const char *pathname)
/* Converts all newline/tab chars to spaces (use for strings that should be contained to a single line) */
void filter_str(char *str, size_t len)
{
int idx = strlen(pathname) - 1;
size_t i;
char tmpname[MAX_STR_SIZE];
snprintf(tmpname, sizeof(tmpname), "%s", pathname);
for (i = 0; i < len; ++i) {
if (str[i] == '\n' || str[i] == '\r' || str[i] == '\t' || str[i] == '\v' || str[i] == '\0')
str[i] = ' ';
}
}
while (idx >= 0 && pathname[idx] == '/')
tmpname[idx--] = '\0';
/* gets base file name from path or original file name if no path is supplied.
* Returns the file name length
*/
size_t get_file_name(char *namebuf, size_t bufsize, const char *pathname)
{
int len = strlen(pathname) - 1;
char *path = strdup(pathname);
char *filename = strrchr(tmpname, '/');
if (path == NULL)
exit_toxic_err("failed in get_file_name", FATALERR_MEMORY);
if (filename != NULL) {
if (!strlen(++filename))
filename = tmpname;
} else {
filename = tmpname;
while (len >= 0 && pathname[len] == '/')
path[len--] = '\0';
char *finalname = strdup(path);
if (finalname == NULL)
exit_toxic_err("failed in get_file_name", FATALERR_MEMORY);
const char *basenm = strrchr(path, '/');
if (basenm != NULL) {
if (basenm[1])
strcpy(finalname, &basenm[1]);
}
snprintf(namebuf, bufsize, "%s", filename);
snprintf(namebuf, bufsize, "%s", finalname);
free(finalname);
free(path);
return strlen(namebuf);
}
/* converts str to all lowercase */
@ -226,11 +273,152 @@ void str_to_lower(char *str)
}
/* puts friendnum's nick in buf, truncating at TOXIC_MAX_NAME_LENGTH if necessary.
Returns nick len on success, -1 on failure */
int get_nick_truncate(Tox *m, char *buf, int friendnum)
if toxcore API call fails, put UNKNOWN_NAME in buf
Returns nick len */
size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum)
{
int len = tox_get_name(m, friendnum, (uint8_t *) buf);
size_t len = tox_friend_get_name_size(m, friendnum, NULL);
if (len == 0) {
strcpy(buf, UNKNOWN_NAME);
len = strlen(UNKNOWN_NAME);
} else {
tox_friend_get_name(m, friendnum, (uint8_t *) buf, NULL);
}
len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
buf[len] = '\0';
filter_str(buf, len);
return len;
}
/* same as get_nick_truncate but for groupchats */
int get_group_nick_truncate(Tox *m, char *buf, int peernum, int groupnum)
{
int len = tox_group_peername(m, groupnum, peernum, (uint8_t *) buf);
if (len == -1) {
strcpy(buf, UNKNOWN_NAME);
len = strlen(UNKNOWN_NAME);
}
len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
buf[len] = '\0';
filter_str(buf, len);
return len;
}
/* copies data to msg buffer.
returns length of msg, which will be no larger than size-1 */
size_t copy_tox_str(char *msg, size_t size, const char *data, size_t length)
{
size_t len = MIN(length, size - 1);
memcpy(msg, data, len);
msg[len] = '\0';
return len;
}
/* returns index of the first instance of ch in s starting at idx.
returns length of s if char not found */
int char_find(int idx, const char *s, char ch)
{
int i = idx;
for (i = idx; s[i]; ++i) {
if (s[i] == ch)
break;
}
return i;
}
/* returns index of the last instance of ch in s starting at len
returns 0 if char not found (skips 0th index) */
int char_rfind(const char *s, char ch, int len)
{
int i = 0;
for (i = len; i > 0; --i) {
if (s[i] == ch)
break;
}
return i;
}
/* Converts bytes to appropriate unit and puts in buf as a string */
void bytes_convert_str(char *buf, int size, uint64_t bytes)
{
double conv = bytes;
const char *unit;
if (conv < KiB) {
unit = "Bytes";
} else if (conv < MiB) {
unit = "KiB";
conv /= (double) KiB;
} else if (conv < GiB) {
unit = "MiB";
conv /= (double) MiB;
} else {
unit = "GiB";
conv /= (double) GiB;
}
snprintf(buf, size, "%.1f %s", conv, unit);
}
/* checks if a file exists. Returns true or false */
bool file_exists(const char *path)
{
struct stat s;
return stat(path, &s) == 0;
}
/* returns file size. If file doesn't exist returns 0. */
off_t file_size(const char *path)
{
struct stat st;
if (stat(path, &st) == -1)
return 0;
return st.st_size;
}
/* compares the first size bytes of fp to signature.
Returns 0 if they are the same, 1 if they differ, and -1 on error.
On success this function will seek back to the beginning of fp */
int check_file_signature(const char *signature, size_t size, FILE *fp)
{
char buf[size];
if (fread(buf, size, 1, fp) != 1)
return -1;
int ret = memcmp(signature, buf, size);
if (fseek(fp, 0L, SEEK_SET) == -1)
return -1;
return ret == 0 ? 0 : 1;
}
/* sets window title in tab bar. */
void set_window_title(ToxWindow *self, const char *title, int len)
{
char cpy[TOXIC_MAX_NAME_LENGTH + 1];
if (self->is_groupchat) /* keep groupnumber in title */
snprintf(cpy, sizeof(cpy), "%d %s", self->num, title);
else
snprintf(cpy, sizeof(cpy), "%s", title);
if (len > MAX_WINDOW_NAME_LENGTH) {
strcpy(&cpy[MAX_WINDOW_NAME_LENGTH - 3], "...");
cpy[MAX_WINDOW_NAME_LENGTH] = '\0';
}
snprintf(self->name, sizeof(self->name), "%s", cpy);
}

View File

@ -19,8 +19,10 @@
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef _misc_tools_h
#define _misc_tools_h
#ifndef MISC_TOOLS_H
#define MISC_TOOLS_H
#include <sys/stat.h>
#include "windows.h"
#include "toxic.h"
@ -33,26 +35,41 @@
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#endif
/* convert a hex string to binary */
char *hex_string_to_bin(const char *hex_string);
#ifndef net_to_host
#define net_to_host(x, y) hst_to_net(x, y)
#endif
/* get the current unix time */
void hst_to_net(uint8_t *num, uint16_t numbytes);
/*
* Converts a hexidecimal string of length hex_len to binary format and puts the result in output.
* output_size must be exactly half of hex_len.
*
* Returns 0 on success.
* Returns -1 on failure.
*/
int hex_string_to_bin(const char *hex_string, size_t hex_len, char *output, size_t output_size);
/* convert a hex string to bytes. returns 0 on success, -1 on failure */
int hex_string_to_bytes(char *buf, int size, const char *keystr);
/* get the current unix time (not thread safe) */
uint64_t get_unix_time(void);
/*Puts the current time in buf in the format of [HH:mm:ss] */
/* Puts the current time in buf in the format of [HH:mm:ss] (not thread safe) */
void get_time_str(char *buf, int bufsize);
/* Converts seconds to string in format HH:mm:ss; truncates hours and minutes when necessary */
void get_elapsed_time_str(char *buf, int bufsize, uint64_t secs);
/* get the current local time */
/* get the current local time (not thread safe) */
struct tm *get_time(void);
/* 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);
int string_is_empty(const char *string);
/* convert a multibyte string to a wide character string (must provide buffer) */
int char_to_wcs_buf(wchar_t *buf, const char *string, size_t n);
@ -64,29 +81,66 @@ int wcs_to_mbs_buf(char *buf, const wchar_t *string, size_t n);
int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n);
/* Returns 1 if connection has timed out, 0 otherwise */
int timed_out(uint64_t timestamp, uint64_t timeout, uint64_t curtime);
int timed_out(uint64_t timestamp, uint64_t timeout);
/* Colours the window tab according to type. Beeps if is_beep is true */
void alert_window(ToxWindow *self, int type, bool is_beep);
/* case-insensitive string compare function for use with qsort */
int qsort_strcasecmp_hlpr(const void *nick1, const void *nick2);
int qsort_strcasecmp_hlpr(const void *str1, const void *str2);
/* 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 */
int valid_nick(char *nick);
- must not contain contiguous spaces
- must not contain a newline or tab seqeunce */
int valid_nick(const char *nick);
/* Converts all newline/tab chars to spaces (use for strings that should be contained to a single line) */
void filter_str(char *str, size_t len);
/* gets base file name from path or original file name if no path is supplied */
void get_file_name(char *namebuf, int bufsize, const char *pathname);
size_t get_file_name(char *namebuf, size_t bufsize, const char *pathname);
/* converts str to all lowercase */
void str_to_lower(char *str);
/* puts friendnum's nick in buf, truncating at TOXIC_MAX_NAME_LENGTH if necessary.
Returns nick len on success, -1 on failure */
int get_nick_truncate(Tox *m, char *buf, int friendnum);
size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum);
#endif /* #define _misc_tools_h */
/* same as get_nick_truncate but for groupchats */
int get_group_nick_truncate(Tox *m, char *buf, int peernum, int groupnum);
/* copies data to msg buffer.
returns length of msg, which will be no larger than size-1 */
size_t copy_tox_str(char *msg, size_t size, const char *data, size_t length);
/* returns index of the first instance of ch in s starting at idx.
returns length of s if char not found */
int char_find(int idx, const char *s, char ch);
/* returns index of the last instance of ch in s
returns 0 if char not found */
int char_rfind(const char *s, char ch, int len);
/* Converts bytes to appropriate unit and puts in buf as a string */
void bytes_convert_str(char *buf, int size, uint64_t bytes);
/* checks if a file exists. Returns true or false */
bool file_exists(const char *path);
/* returns file size. If file doesn't exist returns 0. */
off_t file_size(const char *path);
/* compares the first size bytes of fp and signature.
Returns 0 if they are the same, 1 if they differ, and -1 on error.
On success this function will seek back to the beginning of fp */
int check_file_signature(const char *signature, size_t size, FILE *fp);
/* sets window title in tab bar. */
void set_window_title(ToxWindow *self, const char *title, int len);
#endif /* #define MISC_TOOLS_H */

805
src/notify.c Normal file
View File

@ -0,0 +1,805 @@
/* notify.c
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
#include <stdarg.h>
#include <time.h>
#include <assert.h>
#include <sys/stat.h>
#include "notify.h"
#include "device.h"
#include "settings.h"
#include "line_info.h"
#include "misc_tools.h"
#include "xtra.h"
#if defined(AUDIO) || defined(SOUND_NOTIFY)
#ifdef __APPLE__
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#else
#include <AL/al.h>
#include <AL/alc.h>
/* compatibility with older versions of OpenAL */
#ifndef ALC_ALL_DEVICES_SPECIFIER
#include <AL/alext.h>
#endif
#endif
#ifdef SOUND_NOTIFY
#include <AL/alut.h> /* freealut packet */
#endif
#endif /* AUDIO */
#ifdef BOX_NOTIFY
#include <libnotify/notify.h>
#endif
#define MAX_BOX_MSG_LEN 127
#define SOUNDS_SIZE 10
#define ACTIVE_NOTIFS_MAX 50
extern struct user_settings *user_settings;
struct Control {
time_t cooldown;
time_t notif_timeout;
#if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY)
pthread_mutex_t poll_mutex[1];
bool poll_active;
#endif
#ifdef SOUND_NOTIFY
uint32_t device_idx; /* index of output device */
char* sounds[SOUNDS_SIZE];
#endif /* SOUND_NOTIFY */
} Control = {0};
struct _ActiveNotifications {
#ifdef SOUND_NOTIFY
uint32_t source;
uint32_t buffer;
bool looping;
#endif
bool active;
int *id_indicator;
#ifdef BOX_NOTIFY
NotifyNotification* box;
char messages[MAX_BOX_MSG_LEN + 1][MAX_BOX_MSG_LEN + 1];
char title[64];
size_t size;
time_t n_timeout;
#endif
} actives[ACTIVE_NOTIFS_MAX];
/**********************************************************************************/
/**********************************************************************************/
/**********************************************************************************/
/**********************************************************************************/
/**********************************************************************************/
/* coloured tab notifications: primary notification type */
static void tab_notify(ToxWindow *self, uint64_t flags)
{
if (self == NULL)
return;
if (flags & NT_WNDALERT_0)
self->alert = WINDOW_ALERT_0;
else if ( (flags & NT_WNDALERT_1) && (!self->alert || self->alert > WINDOW_ALERT_0) )
self->alert = WINDOW_ALERT_1;
else if ( (flags & NT_WNDALERT_2) && (!self->alert || self->alert > WINDOW_ALERT_1) )
self->alert = WINDOW_ALERT_2;
}
static bool notifications_are_disabled(uint64_t flags)
{
if (user_settings->alerts != ALERTS_ENABLED)
return true;
bool res = (flags & NT_RESTOL) && (Control.cooldown > get_unix_time());
#ifdef X11
return res || ((flags & NT_NOFOCUS) && is_focused());
#else
return res;
#endif
}
static void control_lock()
{
#if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY)
pthread_mutex_lock(Control.poll_mutex);
#endif
}
static void control_unlock()
{
#if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY)
pthread_mutex_unlock(Control.poll_mutex);
#endif
}
#ifdef SOUND_NOTIFY
bool is_playing(int source)
{
int ready;
alGetSourcei(source, AL_SOURCE_STATE, &ready);
return ready == AL_PLAYING;
}
/* TODO maybe find better way to do this */
/* cooldown is in seconds */
#define DEVICE_COOLDOWN 5 /* TODO perhaps load this from config? */
static bool device_opened = false;
time_t last_opened_update = 0;
/* Opens primary device. Returns true on succe*/
void m_open_device()
{
last_opened_update = get_unix_time();
if (device_opened) return;
/* Blah error check */
open_primary_device(output, &Control.device_idx, 48000, 20, 1);
device_opened = true;
}
void m_close_device()
{
if (!device_opened) return;
close_device(output, Control.device_idx);
device_opened = false;
}
/* Terminate all sounds but wait for them to finish first */
void graceful_clear()
{
control_lock();
while (1) {
int i;
for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
if (actives[i].active) {
#ifdef BOX_NOTIFY
if (actives[i].box) {
GError* ignore;
notify_notification_close(actives[i].box, &ignore);
actives[i].box = NULL;
}
#endif
if(actives[i].id_indicator)
*actives[i].id_indicator = -1; /* reset indicator value */
if ( actives[i].looping ) {
stop_sound(i);
} else {
if (!is_playing(actives[i].source))
memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
else break;
}
}
}
if (i == ACTIVE_NOTIFS_MAX) {
m_close_device(); /* In case it's opened */
control_unlock();
return;
}
usleep(1000);
}
control_unlock();
}
void* do_playing(void* _p)
{
(void)_p;
while(true) {
control_lock();
if (!Control.poll_active) {
control_unlock();
break;
}
bool has_looping = false;
int i;
for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
if (actives[i].looping) has_looping = true;
if (actives[i].active && !actives[i].looping
#ifdef BOX_NOTIFY
&& !actives[i].box
#endif
) {
if(actives[i].id_indicator)
*actives[i].id_indicator = -1; /* reset indicator value */
if (!is_playing(actives[i].source)) {
/* Close */
alSourceStop(actives[i].source);
alDeleteSources(1, &actives[i].source);
alDeleteBuffers(1, &actives[i].buffer);
memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
}
}
#ifdef BOX_NOTIFY
else if (actives[i].box && time(NULL) >= actives[i].n_timeout)
{
GError* ignore;
notify_notification_close(actives[i].box, &ignore);
actives[i].box = NULL;
if(actives[i].id_indicator)
*actives[i].id_indicator = -1; /* reset indicator value */
if (!actives[i].looping && !is_playing(actives[i].source)) {
/* stop source if not looping or playing, just terminate box */
alSourceStop(actives[i].source);
alDeleteSources(1, &actives[i].source);
alDeleteBuffers(1, &actives[i].buffer);
memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
}
}
#endif
}
/* device is opened and no activity in under DEVICE_COOLDOWN time, close device*/
if (device_opened && !has_looping &&
(time(NULL) - last_opened_update) > DEVICE_COOLDOWN) {
m_close_device();
}
has_looping = false;
control_unlock();
usleep(10000);
}
pthread_exit(NULL);
}
int play_source(uint32_t source, uint32_t buffer, bool looping)
{
int i = 0;
for (; i < ACTIVE_NOTIFS_MAX && actives[i].active; i ++);
if ( i == ACTIVE_NOTIFS_MAX ) {
return -1; /* Full */
}
alSourcePlay(source);
actives[i].active = 1;
actives[i].source = source;
actives[i].buffer = buffer;
actives[i].looping = looping;
return i;
}
#elif BOX_NOTIFY
void* do_playing(void* _p)
{
(void)_p;
while(true) {
control_lock();
if (!Control.poll_active) {
control_unlock();
break;
}
int i;
for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
if (actives[i].box && time(NULL) >= actives[i].n_timeout)
{
GError* ignore;
notify_notification_close(actives[i].box, &ignore);
actives[i].box = NULL;
if(actives[i].id_indicator)
*actives[i].id_indicator = -1; /* reset indicator value */
memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
}
}
control_unlock();
usleep(10000);
}
pthread_exit(NULL);
}
void graceful_clear()
{
int i;
control_lock();
for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
if (actives[i].box) {
GError* ignore;
notify_notification_close(actives[i].box, &ignore);
actives[i].box = NULL;
}
if (actives[i].id_indicator)
*actives[i].id_indicator = -1; /* reset indicator value */
memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
}
control_unlock();
}
#endif
/**********************************************************************************/
/**********************************************************************************/
/**********************************************************************************/
/**********************************************************************************/
/**********************************************************************************/
/* Opens primary device */
int init_notify(int login_cooldown, int notification_timeout)
{
#ifdef SOUND_NOTIFY
alutInitWithoutContext(NULL, NULL);
#endif /* SOUND_NOTIFY */
#if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY)
if (pthread_mutex_init(Control.poll_mutex, NULL) != 0)
return -1;
Control.poll_active = 1;
pthread_t thread;
if (pthread_create(&thread, NULL, do_playing, NULL) != 0 || pthread_detach(thread) != 0 ) {
pthread_mutex_destroy(Control.poll_mutex);
Control.poll_active = 0;
return -1;
}
#endif
Control.cooldown = time(NULL) + login_cooldown;
#ifdef BOX_NOTIFY
notify_init("Toxic");
#endif
Control.notif_timeout = notification_timeout;
return 1;
}
void terminate_notify()
{
#if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY)
control_lock();
if ( !Control.poll_active ) {
control_unlock();
return;
}
Control.poll_active = 0;
control_unlock();
graceful_clear();
#endif
#ifdef SOUND_NOTIFY
int i = 0;
for (; i < SOUNDS_SIZE; i ++) free(Control.sounds[i]);
alutExit();
#endif /* SOUND_NOTIFY */
#ifdef BOX_NOTIFY
notify_uninit();
#endif
}
#ifdef SOUND_NOTIFY
int set_sound(Notification sound, const char* value)
{
if (sound == silent) return 0;
free(Control.sounds[sound]);
size_t len = strlen(value) + 1;
Control.sounds[sound] = calloc(len, 1);
memcpy(Control.sounds[sound], value, len);
struct stat buf;
return stat(value, &buf) == 0;
}
int play_sound_internal(Notification what, bool loop)
{
uint32_t source;
uint32_t buffer;
m_open_device();
alGenSources(1, &source);
alGenBuffers(1, &buffer);
buffer = alutCreateBufferFromFile(Control.sounds[what]);
alSourcei(source, AL_BUFFER, buffer);
alSourcei(source, AL_LOOPING, loop);
int rc = play_source(source, buffer, loop);
if (rc < 0) {
alSourceStop(source);
alDeleteSources(1, &source);
alDeleteBuffers(1,&buffer);
return -1;
}
return rc;
}
int play_notify_sound(Notification notif, uint64_t flags)
{
int rc = -1;
if (flags & NT_BEEP) beep();
else if (notif != silent) {
if ( !Control.poll_active || !Control.sounds[notif] )
return -1;
rc = play_sound_internal(notif, flags & NT_LOOP ? 1 : 0);
}
return rc;
}
void stop_sound(int id)
{
if (id >= 0 && id < ACTIVE_NOTIFS_MAX && actives[id].looping && actives[id].active ) {
#ifdef BOX_NOTIFY
if (actives[id].box) {
GError* ignore;
notify_notification_close(actives[id].box, &ignore);
}
#endif
if (actives[id].id_indicator)
*actives[id].id_indicator = -1;
// alSourcei(actives[id].source, AL_LOOPING, false);
alSourceStop(actives[id].source);
alDeleteSources(1, &actives[id].source);
alDeleteBuffers(1,&actives[id].buffer);
memset(&actives[id], 0, sizeof(struct _ActiveNotifications));
}
}
#endif
static int m_play_sound(Notification notif, uint64_t flags)
{
#ifdef SOUND_NOTIFY
return play_notify_sound(notif, flags);
#else
if (notif != silent)
beep();
return -1;
#endif /* SOUND_NOTIFY */
}
#ifdef BOX_NOTIFY
void m_notify_action(NotifyNotification *box, char *action, void* data)
{
}
#endif
int sound_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indicator)
{
tab_notify(self, flags);
if (notifications_are_disabled(flags))
return -1;
int id = -1;
control_lock();
if (self && (!self->stb || self->stb->status != TOX_USER_STATUS_BUSY))
id = m_play_sound(notif, flags);
else if (flags & NT_ALWAYS)
id = m_play_sound(notif, flags);
#if defined(BOX_NOTIFY) && !defined(SOUND_NOTIFY)
if (id == -1) {
for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].box; id++);
if ( id == ACTIVE_NOTIFS_MAX ) {
control_unlock();
return -1; /* Full */
}
}
#endif
if ( id_indicator && id != -1 ) {
actives[id].id_indicator = id_indicator;
*id_indicator = id;
}
control_unlock();
return id;
}
int sound_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id)
{
tab_notify(self, flags);
if (notifications_are_disabled(flags))
return -1;
if (id < 0 || id >= ACTIVE_NOTIFS_MAX) return -1;
#ifdef SOUND_NOTIFY
control_lock();
if (!actives[id].active || !Control.sounds[notif]) {
control_unlock();
return -1;
}
m_open_device();
alSourceStop(actives[id].source);
alDeleteSources(1, &actives[id].source);
alDeleteBuffers(1,&actives[id].buffer);
alGenSources(1, &actives[id].source);
alGenBuffers(1, &actives[id].buffer);
actives[id].buffer = alutCreateBufferFromFile(Control.sounds[notif]);
alSourcei(actives[id].source, AL_BUFFER, actives[id].buffer);
alSourcei(actives[id].source, AL_LOOPING, flags & NT_LOOP);
alSourcePlay(actives[id].source);
control_unlock();
return id;
#else
if (notif != silent)
beep();
return 0;
#endif /* SOUND_NOTIFY */
}
int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indicator, const char* title, const char* format, ...)
{
if (notifications_are_disabled(flags)) {
tab_notify(self, flags);
return -1;
}
#ifdef BOX_NOTIFY
int id = sound_notify(self, notif, flags, id_indicator);
control_lock();
#ifdef SOUND_NOTIFY
if (id == -1) { /* Could not play */
for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].active; id ++);
if ( id == ACTIVE_NOTIFS_MAX ) {
control_unlock();
return -1; /* Full */
}
actives[id].active = 1;
actives[id].id_indicator = id_indicator;
if (id_indicator) *id_indicator = id;
}
#else
if (id == -1)
return -1;
#endif /* SOUND_NOTIFY */
snprintf(actives[id].title, sizeof(actives[id].title), "%s", title);
if (strlen(title) > 23) strcpy(actives[id].title + 20, "...");
va_list __ARGS__; va_start (__ARGS__, format);
vsnprintf (actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__);
va_end (__ARGS__);
if (strlen(actives[id].messages[0]) > MAX_BOX_MSG_LEN - 3)
strcpy(actives[id].messages[0] + MAX_BOX_MSG_LEN - 3, "...");
actives[id].box = notify_notification_new(actives[id].title, actives[id].messages[0], NULL);
actives[id].size++;
actives[id].n_timeout = get_unix_time() + Control.notif_timeout / 1000;
notify_notification_set_timeout(actives[id].box, Control.notif_timeout);
notify_notification_set_app_name(actives[id].box, "toxic");
/*notify_notification_add_action(actives[id].box, "lel", "default", m_notify_action, self, NULL);*/
notify_notification_show(actives[id].box, NULL);
control_unlock();
return id;
#else
return sound_notify(self, notif, flags, id_indicator);
#endif /* BOX_NOTIFY */
}
int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, const char* format, ...)
{
if (notifications_are_disabled(flags)) {
tab_notify(self, flags);
return -1;
}
#ifdef BOX_NOTIFY
if (sound_notify2(self, notif, flags, id) == -1)
return -1;
control_lock();
if (!actives[id].box || actives[id].size >= MAX_BOX_MSG_LEN + 1) {
control_unlock();
return -1;
}
va_list __ARGS__; va_start (__ARGS__, format);
vsnprintf (actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__);
va_end (__ARGS__);
if (strlen(actives[id].messages[actives[id].size]) > MAX_BOX_MSG_LEN - 3)
strcpy(actives[id].messages[actives[id].size] + MAX_BOX_MSG_LEN - 3, "...");
actives[id].size++;
actives[id].n_timeout = get_unix_time() + Control.notif_timeout / 1000;
char formated[128 * 129] = {'\0'};
int i = 0;
for (; i <actives[id].size; i ++) {
strcat(formated, actives[id].messages[i]);
strcat(formated, "\n");
}
formated[strlen(formated) - 1] = '\0';
notify_notification_update(actives[id].box, actives[id].title, formated, NULL);
notify_notification_show(actives[id].box, NULL);
control_unlock();
return id;
#else
return sound_notify2(self, notif, flags, id);
#endif
}
int box_silent_notify(ToxWindow* self, uint64_t flags, int* id_indicator, const char* title, const char* format, ...)
{
tab_notify(self, flags);
if (notifications_are_disabled(flags))
return -1;
#ifdef BOX_NOTIFY
control_lock();
int id;
for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].active; id ++);
if ( id == ACTIVE_NOTIFS_MAX ) {
control_unlock();
return -1; /* Full */
}
if (id_indicator) {
actives[id].id_indicator = id_indicator;
*id_indicator = id;
}
snprintf(actives[id].title, sizeof(actives[id].title), "%s", title);
if (strlen(title) > 23) strcpy(actives[id].title + 20, "...");
va_list __ARGS__; va_start (__ARGS__, format);
vsnprintf (actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__);
va_end (__ARGS__);
if (strlen(actives[id].messages[0]) > MAX_BOX_MSG_LEN - 3)
strcpy(actives[id].messages[0] + MAX_BOX_MSG_LEN - 3, "...");
actives[id].active = 1;
actives[id].box = notify_notification_new(actives[id].title, actives[id].messages[0], NULL);
actives[id].size ++;
actives[id].n_timeout = get_unix_time() + Control.notif_timeout / 1000;
notify_notification_set_timeout(actives[id].box, Control.notif_timeout);
notify_notification_set_app_name(actives[id].box, "toxic");
/*notify_notification_add_action(actives[id].box, "lel", "default", m_notify_action, self, NULL);*/
notify_notification_show(actives[id].box, NULL);
control_unlock();
return id;
#else
return -1;
#endif
}
int box_silent_notify2(ToxWindow* self, uint64_t flags, int id, const char* format, ...)
{
tab_notify(self, flags);
if (notifications_are_disabled(flags))
return -1;
#ifdef BOX_NOTIFY
control_lock();
if (id < 0 || id >= ACTIVE_NOTIFS_MAX || !actives[id].box || actives[id].size >= MAX_BOX_MSG_LEN + 1 ) {
control_unlock();
return -1;
}
va_list __ARGS__; va_start (__ARGS__, format);
vsnprintf (actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__);
va_end (__ARGS__);
if (strlen(actives[id].messages[actives[id].size]) > MAX_BOX_MSG_LEN - 3)
strcpy(actives[id].messages[actives[id].size] + MAX_BOX_MSG_LEN - 3, "...");
actives[id].size ++;
actives[id].n_timeout = get_unix_time() + Control.notif_timeout / 1000;
char formated[128 * 129] = {'\0'};
int i = 0;
for (; i <actives[id].size; i ++) {
strcat(formated, actives[id].messages[i]);
strcat(formated, "\n");
}
formated[strlen(formated) - 1] = '\0';
notify_notification_update(actives[id].box, actives[id].title, formated, NULL);
notify_notification_show(actives[id].box, NULL);
control_unlock();
return id;
#else
return -1;
#endif
}

80
src/notify.h Normal file
View File

@ -0,0 +1,80 @@
/* notify.h
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NOTIFY_H
#define NOTIFY_H
#include <inttypes.h>
#include "windows.h"
typedef enum _Notification
{
silent = -1,
notif_error,
self_log_in,
self_log_out,
user_log_in,
user_log_out,
call_incoming,
call_outgoing,
generic_message,
transfer_pending,
transfer_completed,
} Notification;
typedef enum _Flags {
NT_NOFOCUS = 1 << 0, /* Notify when focus is not on this terminal. NOTE: only works with x11,
* if no x11 present this flag is ignored
*/
NT_BEEP = 1 << 1, /* Play native sound instead: \a */
NT_LOOP = 1 << 2, /* Loop sound. If this setting active, notify() will return id of the sound
* so it could be stopped. It will return 0 if error or NT_NATIVE flag is set and play \a instead
*/
NT_RESTOL = 1 << 3, /* Respect tolerance. Usually used to stop flood at toxic startup
* Only works if login_cooldown is true when calling init_notify()
*/
NT_NOTIFWND = 1 << 4, /* Pop notify window. NOTE: only works(/WILL WORK) if libnotify is present */
NT_WNDALERT_0 = 1 << 5, /* Alert toxic */
NT_WNDALERT_1 = 1 << 6, /* Alert toxic */
NT_WNDALERT_2 = 1 << 7, /* Alert toxic */
NT_ALWAYS = 1 << 8, /* Force sound to play */
} Flags;
int init_notify(int login_cooldown, int notification_timeout);
void terminate_notify();
int sound_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indicator);
int sound_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id);
void stop_sound(int id);
int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indicator, const char* title, const char* format, ...);
int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, const char* format, ...);
int box_silent_notify(ToxWindow* self, uint64_t flags, int* id_indicator, const char* title, const char* format, ...);
int box_silent_notify2(ToxWindow* self, uint64_t flags, int id, const char* format, ...);
#ifdef SOUND_NOTIFY
int set_sound(Notification sound, const char* value);
#endif /* SOUND_NOTIFY */
#endif /* NOTIFY_H */

View File

@ -31,6 +31,7 @@
#include "toxic.h"
#include "windows.h"
#include "prompt.h"
#include "friendlist.h"
#include "execute.h"
#include "misc_tools.h"
#include "toxic_strings.h"
@ -39,41 +40,50 @@
#include "settings.h"
#include "input.h"
#include "help.h"
#include "notify.h"
#include "autocomplete.h"
char pending_frnd_requests[MAX_FRIENDS_NUM][TOX_CLIENT_ID_SIZE];
uint16_t num_frnd_requests = 0;
extern ToxWindow *prompt;
struct _Winthread Winthread;
extern struct user_settings *user_settings;
extern struct Winthread Winthread;
extern FriendsList Friends;
FriendRequests FrndRequests;
#ifdef AUDIO
#define AC_NUM_GLOB_COMMANDS 18
#else
#define AC_NUM_GLOB_COMMANDS 16
#endif /* AUDIO */
/* Array of global command names used for tab completion. */
const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
static const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
{ "/accept" },
{ "/add" },
{ "/avatar" },
{ "/clear" },
{ "/close" }, /* rm /close when groupchats gets its own list */
{ "/connect" },
{ "/decline" },
{ "/exit" },
{ "/groupchat" },
{ "/group" },
{ "/help" },
{ "/join" },
{ "/log" },
{ "/myid" },
{ "/nick" },
{ "/note" },
{ "/quit" },
{ "/requests" },
{ "/status" },
#ifdef _SUPPORT_AUDIO
#ifdef AUDIO
{ "/lsdev" },
{ "/sdev" },
#endif /* _SUPPORT_AUDIO */
#endif /* AUDIO */
};
void kill_prompt_window(ToxWindow *self)
void kill_prompt_window(ToxWindow *self)
{
ChatContext *ctx = self->chatwin;
StatusBar *statusbar = self->stb;
@ -86,7 +96,6 @@ void kill_prompt_window(ToxWindow *self)
delwin(statusbar->topline);
free(ctx->log);
free(ctx->hst);
free(ctx);
free(self->help);
free(statusbar);
@ -94,51 +103,62 @@ void kill_prompt_window(ToxWindow *self)
del_window(self);
}
/* callback: Updates own connection status in prompt statusbar */
void prompt_onSelfConnectionChange(Tox *m, TOX_CONNECTION connection_status, void *userdata)
{
StatusBar *statusbar = prompt->stb;
statusbar->connection = connection_status;
}
/* Updates own nick in prompt statusbar */
void prompt_update_nick(ToxWindow *prompt, char *nick)
void prompt_update_nick(ToxWindow *prompt, const char *nick)
{
StatusBar *statusbar = prompt->stb;
snprintf(statusbar->nick, sizeof(statusbar->nick), "%s", nick);
statusbar->nick_len = strlen(statusbar->nick);
}
/* Updates own statusmessage in prompt statusbar */
void prompt_update_statusmessage(ToxWindow *prompt, char *statusmsg)
/* Updates own statusmessage */
void prompt_update_statusmessage(ToxWindow *prompt, Tox *m, const char *statusmsg)
{
StatusBar *statusbar = prompt->stb;
snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg);
statusbar->statusmsg_len = strlen(statusbar->statusmsg);
size_t len = strlen(statusbar->statusmsg);
statusbar->statusmsg_len = len;
TOX_ERR_SET_INFO err;
tox_self_set_status_message(m, (uint8_t *) statusmsg, len, &err);
if (err != TOX_ERR_SET_INFO_OK)
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set note (error %d)\n", err);
}
/* Updates own status in prompt statusbar */
void prompt_update_status(ToxWindow *prompt, uint8_t status)
void prompt_update_status(ToxWindow *prompt, TOX_USER_STATUS status)
{
StatusBar *statusbar = prompt->stb;
statusbar->status = status;
}
/* Updates own connection status in prompt statusbar */
void prompt_update_connectionstatus(ToxWindow *prompt, bool is_connected)
{
StatusBar *statusbar = prompt->stb;
statusbar->is_online = is_connected;
}
/* 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(const char *public_key)
Returns request number on success, -1 if queue is full. */
static int add_friend_request(const char *public_key, const char *data)
{
if (num_frnd_requests >= MAX_FRIENDS_NUM)
if (FrndRequests.max_idx >= MAX_FRIEND_REQUESTS)
return -1;
int i;
for (i = 0; i <= num_frnd_requests; ++i) {
if (!strlen(pending_frnd_requests[i])) {
memcpy(pending_frnd_requests[i], public_key, TOX_CLIENT_ID_SIZE);
for (i = 0; i <= FrndRequests.max_idx; ++i) {
if (!FrndRequests.request[i].active) {
FrndRequests.request[i].active = true;
memcpy(FrndRequests.request[i].key, public_key, TOX_PUBLIC_KEY_SIZE);
snprintf(FrndRequests.request[i].msg, sizeof(FrndRequests.request[i].msg), "%s", data);
if (i == num_frnd_requests)
++num_frnd_requests;
if (i == FrndRequests.max_idx)
++FrndRequests.max_idx;
++FrndRequests.num_requests;
return i;
}
@ -176,20 +196,30 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
if (key == '\t') { /* TAB key: auto-completes command */
if (ctx->len > 1 && ctx->line[0] == '/') {
int diff = complete_line(ctx, glob_cmd_list, AC_NUM_GLOB_COMMANDS, MAX_CMDNAME_SIZE);
int diff = -1;
if (wcsncmp(ctx->line, L"/avatar \"", wcslen(L"/avatar \"")) == 0)
diff = dir_match(self, m, ctx->line, L"/avatar");
else if (wcsncmp(ctx->line, L"/status ", wcslen(L"/status ")) == 0){
const char status_cmd_list[3][8] = {
{"online"},
{"away"},
{"busy"},
};
diff = complete_line(self, status_cmd_list, 3, 8);
} else
diff = complete_line(self, glob_cmd_list, AC_NUM_GLOB_COMMANDS, MAX_CMDNAME_SIZE);
if (diff != -1) {
if (x + diff > x2 - 1) {
wmove(self->window, y, x + diff);
ctx->start += diff;
} else {
wmove(self->window, y, x + diff);
int wlen = MAX(0, wcswidth(ctx->line, sizeof(ctx->line) / sizeof(wchar_t)));
ctx->start = wlen < x2 ? 0 : wlen - x2 + 1;
}
} else {
beep();
sound_notify(self, notif_error, 0, NULL);
}
} else {
beep();
sound_notify(self, notif_error, 0, NULL);
}
} else if (key == '\n') {
rm_trailing_spaces_buf(ctx);
@ -202,7 +232,7 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
if (!string_is_empty(line))
add_line_to_hist(ctx);
line_info_add(self, NULL, NULL, NULL, line, PROMPT, 0, 0);
line_info_add(self, NULL, NULL, NULL, PROMPT, 0, 0, "%s", line);
execute(ctx->history, self, m, line, GLOBAL_COMMAND_MODE);
wclear(ctx->linewin);
@ -218,7 +248,10 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
ChatContext *ctx = self->chatwin;
pthread_mutex_lock(&Winthread.lock);
line_info_print(self);
pthread_mutex_unlock(&Winthread.lock);
wclear(ctx->linewin);
curs_set(1);
@ -227,33 +260,35 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]);
StatusBar *statusbar = self->stb;
mvwhline(statusbar->topline, 1, 0, ACS_HLINE, x2);
wmove(statusbar->topline, 0, 0);
if (statusbar->is_online) {
int colour = WHITE;
const char *status_text = "Unknown";
pthread_mutex_lock(&Winthread.lock);
TOX_CONNECTION connection = statusbar->connection;
pthread_mutex_unlock(&Winthread.lock);
switch (statusbar->status) {
case TOX_USERSTATUS_NONE:
if (connection != TOX_CONNECTION_NONE) {
int colour = MAGENTA;
const char *status_text = "ERROR";
pthread_mutex_lock(&Winthread.lock);
TOX_USER_STATUS status = statusbar->status;
pthread_mutex_unlock(&Winthread.lock);
switch (status) {
case TOX_USER_STATUS_NONE:
status_text = "Online";
colour = GREEN;
break;
case TOX_USERSTATUS_AWAY:
case TOX_USER_STATUS_AWAY:
status_text = "Away";
colour = YELLOW;
break;
case TOX_USERSTATUS_BUSY:
case TOX_USER_STATUS_BUSY:
status_text = "Busy";
colour = RED;
break;
case TOX_USERSTATUS_INVALID:
status_text = "ERROR";
colour = MAGENTA;
break;
}
wattron(statusbar->topline, COLOR_PAIR(colour) | A_BOLD);
@ -261,17 +296,49 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
wattroff(statusbar->topline, COLOR_PAIR(colour) | A_BOLD);
wattron(statusbar->topline, A_BOLD);
pthread_mutex_lock(&Winthread.lock);
wprintw(statusbar->topline, " %s", statusbar->nick);
pthread_mutex_unlock(&Winthread.lock);
wattroff(statusbar->topline, A_BOLD);
} else {
wprintw(statusbar->topline, " [Offline]");
wattron(statusbar->topline, A_BOLD);
wprintw(statusbar->topline, " %s ", statusbar->nick);
pthread_mutex_lock(&Winthread.lock);
wprintw(statusbar->topline, " %s", statusbar->nick);
pthread_mutex_unlock(&Winthread.lock);
wattroff(statusbar->topline, A_BOLD);
}
/* Reset statusbar->statusmsg on window resize */
if (x2 != self->x) {
char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH];
pthread_mutex_lock(&Winthread.lock);
size_t slen = tox_self_get_status_message_size(m);
tox_self_get_status_message (m, (uint8_t*) statusmsg);
statusmsg[slen] = '\0';
snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg);
statusbar->statusmsg_len = strlen(statusbar->statusmsg);
pthread_mutex_unlock(&Winthread.lock);
}
self->x = x2;
/* Truncate note if it doesn't fit in statusbar */
uint16_t maxlen = x2 - getcurx(statusbar->topline) - 3;
pthread_mutex_lock(&Winthread.lock);
if (statusbar->statusmsg_len > maxlen) {
statusbar->statusmsg[maxlen - 3] = '\0';
strcat(statusbar->statusmsg, "...");
statusbar->statusmsg_len = maxlen;
}
if (statusbar->statusmsg[0])
wprintw(statusbar->topline, " - %s", statusbar->statusmsg);
wprintw(statusbar->topline, " : %s", statusbar->statusmsg);
pthread_mutex_unlock(&Winthread.lock);
mvwhline(self->window, y2 - CHATBOX_HEIGHT, 0, ACS_HLINE, x2);
@ -279,7 +346,7 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
getyx(self->window, y, x);
(void) x;
int new_x = ctx->start ? x2 - 1 : wcswidth(ctx->line, ctx->pos);
int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos));
wmove(self->window, y + 1, new_x);
wrefresh(self->window);
@ -288,11 +355,8 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
help_onDraw(self);
}
static void prompt_onConnectionChange(ToxWindow *self, Tox *m, int32_t friendnum , uint8_t status)
static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnum , TOX_CONNECTION connection_status)
{
if (friendnum < 0)
return;
ChatContext *ctx = self->chatwin;
char nick[TOX_MAX_NAME_LENGTH] = {0}; /* stop removing this initiation */
@ -303,45 +367,54 @@ static void prompt_onConnectionChange(ToxWindow *self, Tox *m, int32_t friendnum
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
char *msg;
const char *msg;
if (status == 1) {
if (connection_status != TOX_CONNECTION_NONE && Friends.list[friendnum].connection_status == TOX_CONNECTION_NONE) {
msg = "has come online";
line_info_add(self, timefrmt, nick, NULL, msg, CONNECTION, 0, GREEN);
line_info_add(self, timefrmt, nick, NULL, CONNECTION, 0, GREEN, msg);
write_to_log(msg, nick, ctx->log, true);
alert_window(self, WINDOW_ALERT_2, false);
} else {
if (self->active_box != -1)
box_notify2(self, user_log_in, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, self->active_box,
"%s has come online", nick );
else
box_notify(self, user_log_in, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, &self->active_box,
"Toxic", "%s has come online", nick );
}
else if (connection_status == TOX_CONNECTION_NONE) {
msg = "has gone offline";
line_info_add(self, timefrmt, nick, NULL, msg, CONNECTION, 0, RED);
line_info_add(self, timefrmt, nick, NULL, DISCONNECTION, 0, RED, msg);
write_to_log(msg, nick, ctx->log, true);
if (self->active_box != -1)
box_notify2(self, user_log_out, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, self->active_box,
"%s has gone offline", nick );
else
box_notify(self, user_log_out, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, &self->active_box,
"Toxic", "%s has gone offline", nick );
}
}
static void prompt_onFriendRequest(ToxWindow *self, Tox *m, const char *key, const char *data,
uint16_t length)
static void prompt_onFriendRequest(ToxWindow *self, Tox *m, const char *key, const char *data, size_t length)
{
ChatContext *ctx = self->chatwin;
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
char 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);
line_info_add(self, timefrmt, NULL, NULL, SYS_MSG, 0, 0, "Friend request with the message '%s'", data);
write_to_log("Friend request with the message '%s'", "", ctx->log, true);
int n = add_friend_request(key);
int n = add_friend_request(key, data);
if (n == -1) {
char *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);
const char *errmsg = "Friend request queue is full. Discarding request.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
return;
}
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);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type \"/accept %d\" or \"/decline %d\"", n, n);
sound_notify(self, generic_message, NT_WNDALERT_1 | NT_NOTIFWND, NULL);
}
void prompt_init_statusbar(ToxWindow *self, Tox *m)
@ -352,33 +425,30 @@ void prompt_init_statusbar(ToxWindow *self, Tox *m)
/* Init statusbar info */
StatusBar *statusbar = self->stb;
statusbar->status = TOX_USERSTATUS_NONE;
statusbar->is_online = false;
statusbar->status = TOX_USER_STATUS_NONE;
statusbar->connection = TOX_CONNECTION_NONE;
char nick[TOX_MAX_NAME_LENGTH];
char statusmsg[MAX_STR_SIZE];
char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH];
pthread_mutex_lock(&Winthread.lock);
uint16_t n_len = tox_get_self_name(m, (uint8_t *) nick);
uint16_t s_len = tox_get_self_status_message(m, (uint8_t *) statusmsg, MAX_STR_SIZE);
uint8_t status = tox_get_self_user_status(m);
pthread_mutex_unlock(&Winthread.lock);
size_t n_len = tox_self_get_name_size(m);
tox_self_get_name(m, (uint8_t *) nick);
size_t s_len = tox_self_get_status_message_size(m);
tox_self_get_status_message(m, (uint8_t *) statusmsg);
TOX_USER_STATUS status = tox_self_get_status(m);
nick[n_len] = '\0';
statusmsg[s_len] = '\0';
/* load prev status message or show toxic version if it has never been set */
char ver[strlen(TOXICVER) + 1];
strcpy(ver, TOXICVER);
const char *toxic_ver = strtok(ver, "_");
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);
if (s_len == 0 || !strncmp(statusmsg, "Toxing on Toxic", strlen("Toxing on Toxic"))) {
snprintf(statusmsg, sizeof(statusmsg), "Toxing on Toxic");
s_len = strlen(statusmsg);
statusmsg[s_len] = '\0';
}
prompt_update_statusmessage(prompt, statusmsg);
prompt_update_statusmessage(prompt, m, statusmsg);
prompt_update_status(prompt, status);
prompt_update_nick(prompt, nick);
@ -388,18 +458,18 @@ void prompt_init_statusbar(ToxWindow *self, Tox *m)
static void print_welcome_msg(ToxWindow *self)
{
line_info_add(self, NULL, NULL, NULL, " _____ _____ _____ ____ ", SYS_MSG, 1, BLUE);
line_info_add(self, NULL, NULL, NULL, " |_ _/ _ \\ \\/ /_ _/ ___|", SYS_MSG, 1, BLUE);
line_info_add(self, NULL, NULL, NULL, " | || | | \\ / | | | ", SYS_MSG, 1, BLUE);
line_info_add(self, NULL, NULL, NULL, " | || |_| / \\ | | |___ ", SYS_MSG, 1, BLUE);
line_info_add(self, NULL, NULL, NULL, " |_| \\___/_/\\_\\___\\____|", SYS_MSG, 1, BLUE);
line_info_add(self, NULL, NULL, NULL, "", SYS_MSG, 0, 0);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, " _____ _____ _____ ____ ");
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, " |_ _/ _ \\ \\/ /_ _/ ___|");
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, " | || | | \\ / | | | ");
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, " | || |_| / \\ | | |___ ");
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, " |_| \\___/_/\\_\\___\\____| v." TOXICVER);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
char *msg = "Welcome to Toxic, a free, open source Tox-based instant messenging client.";
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 1, CYAN);
const char *msg = "Welcome to Toxic, a free, open source Tox-based instant messenging client.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, CYAN, msg);
msg = "Type \"/help\" for assistance. Further help may be found via the man page.";
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 1, CYAN);
line_info_add(self, NULL, NULL, NULL, "", SYS_MSG, 0, 0);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, CYAN, msg);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
}
static void prompt_onInit(ToxWindow *self, Tox *m)
@ -421,15 +491,18 @@ static void prompt_onInit(ToxWindow *self, Tox *m)
line_info_init(ctx->hst);
if (user_settings->autolog == AUTOLOG_ON) {
char myid[TOX_FRIEND_ADDRESS_SIZE];
tox_get_address(m, (uint8_t *) myid);
log_enable(self->name, myid, ctx->log);
char myid[TOX_ADDRESS_SIZE];
tox_self_get_address(m, (uint8_t *) myid);
if (log_enable(self->name, myid, NULL, ctx->log, LOG_PROMPT) == -1)
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize.");
}
scrollok(ctx->history, 0);
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
print_welcome_msg(self);
if (user_settings->show_welcome_msg == SHOW_WELCOME_MSG_ON)
print_welcome_msg(self);
}
ToxWindow new_prompt(void)
@ -459,5 +532,7 @@ ToxWindow new_prompt(void)
ret.stb = stb;
ret.help = help;
ret.active_box = -1;
return ret;
}

View File

@ -20,25 +20,37 @@
*
*/
#ifndef PROMPT_H_UZYGWFFL
#define PROMPT_H_UZYGWFFL
#ifndef PROMPT_H
#define PROMPT_H
#include "toxic.h"
#include "windows.h"
#ifdef _SUPPORT_AUDIO
#define AC_NUM_GLOB_COMMANDS 17
#else
#define AC_NUM_GLOB_COMMANDS 15
#endif /* _SUPPORT_AUDIO */
#define MAX_FRIEND_REQUESTS 32
struct friend_request {
bool active;
char msg[MAX_STR_SIZE];
uint8_t key[TOX_PUBLIC_KEY_SIZE];
};
typedef struct {
int max_idx;
int num_requests;
struct friend_request request[MAX_FRIEND_REQUESTS];
} FriendRequests;
ToxWindow new_prompt(void);
void prep_prompt_win(void);
void prompt_init_statusbar(ToxWindow *self, Tox *m);
void prompt_update_nick(ToxWindow *prompt, char *nick);
void prompt_update_statusmessage(ToxWindow *prompt, char *statusmsg);
void prompt_update_status(ToxWindow *prompt, uint8_t status);
void prompt_update_nick(ToxWindow *prompt, const char *nick);
void prompt_update_statusmessage(ToxWindow *prompt, Tox *m, const char *statusmsg);
void prompt_update_status(ToxWindow *prompt, TOX_USER_STATUS status);
void prompt_update_connectionstatus(ToxWindow *prompt, bool is_connected);
void kill_prompt_window(ToxWindow *self);
#endif /* end of include guard: PROMPT_H_UZYGWFFL */
/* callback: Updates own connection status in prompt statusbar */
void prompt_onSelfConnectionChange(Tox *m, TOX_CONNECTION connection_status, void *userdata);
#endif /* end of include guard: PROMPT_H */

View File

@ -22,207 +22,444 @@
#include <stdlib.h>
#include <string.h>
#include <libconfig.h>
#include <ctype.h>
#include "toxic.h"
#include "windows.h"
#include "configdir.h"
#include "notify.h"
#include "misc_tools.h"
#ifdef _SUPPORT_AUDIO
#ifdef AUDIO
#include "device.h"
#endif /* _SUPPORT_AUDIO */
#endif /* AUDIO */
#include "settings.h"
#include "line_info.h"
static void uset_autolog(struct user_settings *s, const char *val);
static void uset_time(struct user_settings *s, const char *val);
static void uset_timestamps(struct user_settings *s, const char *val);
static void uset_alerts(struct user_settings *s, const char *val);
static void uset_colours(struct user_settings *s, const char *val);
static void uset_hst_size(struct user_settings *s, const char *val);
static void uset_dwnld_path(struct user_settings *s, const char *val);
#ifdef _SUPPORT_AUDIO
static void uset_ain_dev(struct user_settings *s, const char *val);
static void uset_aout_dev(struct user_settings *s, const char *val);
#ifndef PACKAGE_DATADIR
#define PACKAGE_DATADIR "."
#endif
struct {
const char *key;
void (*func)(struct user_settings *s, const char *val);
} user_settings_list[] = {
{ "autolog", uset_autolog },
{ "time", uset_time },
{ "timestamps", uset_timestamps },
{ "alerts", uset_alerts },
{ "colour_theme", uset_colours },
{ "history_size", uset_hst_size },
{ "download_path", uset_dwnld_path },
#define NO_SOUND "silent"
#ifdef _SUPPORT_AUDIO
{ "audio_in_dev", uset_ain_dev },
{ "audio_out_dev", uset_aout_dev },
#endif
static struct ui_strings {
const char* self;
const char* timestamps;
const char* time_format;
const char* timestamp_format;
const char* log_timestamp_format;
const char* alerts;
const char* native_colors;
const char* autolog;
const char* history_size;
const char* show_typing_self;
const char* show_typing_other;
const char* show_welcome_msg;
const char* line_join;
const char* line_quit;
const char* line_alert;
const char* line_normal;
const char* mplex_away;
const char* mplex_away_note;
} ui_strings = {
"ui",
"timestamps",
"time_format",
"timestamp_format",
"log_timestamp_format",
"alerts",
"native_colors",
"autolog",
"history_size",
"show_typing_self",
"show_typing_other",
"show_welcome_msg",
"line_join",
"line_quit",
"line_alert",
"line_normal",
"mplex_away",
"mplex_away_note",
};
static void uset_autolog(struct user_settings *s, const char *val)
static void ui_defaults(struct user_settings* settings)
{
int n = atoi(val);
settings->timestamps = TIMESTAMPS_ON;
snprintf(settings->timestamp_format, sizeof(settings->timestamp_format), "%s", TIMESTAMP_DEFAULT);
snprintf(settings->log_timestamp_format, sizeof(settings->log_timestamp_format), "%s", LOG_TIMESTAMP_DEFAULT);
/* default off if invalid value */
s->autolog = n == AUTOLOG_ON ? AUTOLOG_ON : AUTOLOG_OFF;
settings->autolog = AUTOLOG_OFF;
settings->alerts = ALERTS_ENABLED;
settings->colour_theme = DFLT_COLS;
settings->history_size = 700;
settings->show_typing_self = SHOW_TYPING_ON;
settings->show_typing_other = SHOW_TYPING_ON;
settings->show_welcome_msg = SHOW_WELCOME_MSG_ON;
snprintf(settings->line_join, LINE_HINT_MAX + 1, "%s", LINE_JOIN);
snprintf(settings->line_quit, LINE_HINT_MAX + 1, "%s", LINE_QUIT);
snprintf(settings->line_alert, LINE_HINT_MAX + 1, "%s", LINE_ALERT);
snprintf(settings->line_normal, LINE_HINT_MAX + 1, "%s", LINE_NORMAL);
settings->mplex_away = MPLEX_ON;
snprintf (settings->mplex_away_note,
sizeof (settings->mplex_away_note),
"%s",
MPLEX_AWAY_NOTE);
}
static void uset_time(struct user_settings *s, const char *val)
{
int n = atoi(val);
static const struct keys_strings {
const char* self;
const char* next_tab;
const char* prev_tab;
const char* scroll_line_up;
const char* scroll_line_down;
const char* half_page_up;
const char* half_page_down;
const char* page_bottom;
const char* peer_list_up;
const char* peer_list_down;
const char* toggle_peerlist;
} key_strings = {
"keys",
"next_tab",
"prev_tab",
"scroll_line_up",
"scroll_line_down",
"half_page_up",
"half_page_down",
"page_bottom",
"peer_list_up",
"peer_list_down",
"toggle_peerlist",
};
/* default to 24 hour time if invalid value */
s->time = n == TIME_12 ? TIME_12 : TIME_24;
/* defines from toxic.h */
static void key_defaults(struct user_settings* settings)
{
settings->key_next_tab = T_KEY_NEXT;
settings->key_prev_tab = T_KEY_PREV;
settings->key_scroll_line_up = KEY_PPAGE;
settings->key_scroll_line_down = KEY_NPAGE;
settings->key_half_page_up = T_KEY_C_F;
settings->key_half_page_down = T_KEY_C_V;
settings->key_page_bottom = T_KEY_C_H;
settings->key_peer_list_up = T_KEY_C_LB;
settings->key_peer_list_down = T_KEY_C_RB;
settings->key_toggle_peerlist = T_KEY_C_B;
}
static void uset_timestamps(struct user_settings *s, const char *val)
{
int n = atoi(val);
static const struct tox_strings {
const char* self;
const char* download_path;
const char* chatlogs_path;
const char* avatar_path;
} tox_strings = {
"tox",
"download_path",
"chatlogs_path",
"avatar_path",
};
/* default on if invalid value */
s->timestamps = n == TIMESTAMPS_OFF ? TIMESTAMPS_OFF : TIMESTAMPS_ON;
static void tox_defaults(struct user_settings* settings)
{
strcpy(settings->download_path, "");
strcpy(settings->chatlogs_path, "");
strcpy(settings->avatar_path, "");
}
static void uset_alerts(struct user_settings *s, const char *val)
{
int n = atoi(val);
#ifdef AUDIO
static const struct audio_strings {
const char* self;
const char* input_device;
const char* output_device;
const char* VAD_treshold;
} audio_strings = {
"audio",
"input_device",
"output_device",
"VAD_treshold",
};
/* alerts default on if invalid value */
s->alerts = n == ALERTS_DISABLED ? ALERTS_DISABLED : ALERTS_ENABLED;
static void audio_defaults(struct user_settings* settings)
{
settings->audio_in_dev = 0;
settings->audio_out_dev = 0;
settings->VAD_treshold = 40.0;
}
static void uset_colours(struct user_settings *s, const char *val)
{
int n = atoi(val);
/* use default toxic colours if invalid value */
s->colour_theme = n == NATIVE_COLS ? NATIVE_COLS : DFLT_COLS;
}
#ifdef _SUPPORT_AUDIO
static void uset_ain_dev(struct user_settings *s, const char *val)
{
int n = atoi(val);
if (n < 0 || n > MAX_DEVICES)
n = (long int) 0;
s->audio_in_dev = (long int) n;
}
static void uset_aout_dev(struct user_settings *s, const char *val)
{
int n = atoi(val);
if (n < 0 || n > MAX_DEVICES)
n = (long int) 0;
s->audio_out_dev = (long int) n;
}
#endif /* _SUPPORT_AUDIO */
static void uset_hst_size(struct user_settings *s, const char *val)
{
int n = atoi(val);
/* if val is out of range use default history size */
s->history_size = (n > MAX_HISTORY || n < MIN_HISTORY) ? DFLT_HST_SIZE : n;
}
static void uset_dwnld_path(struct user_settings *s, const char *val)
{
memset(s->download_path, 0, sizeof(s->download_path));
if (val == NULL)
return;
int len = strlen(val);
if (len >= sizeof(s->download_path) - 2) /* leave room for null and '/' */
return;
FILE *fp = fopen(val, "r");
if (fp == NULL)
return;
strcpy(s->download_path, val);
if (val[len - 1] != '/')
strcat(s->download_path, "/");
}
static void set_default_settings(struct user_settings *s)
{
/* see settings_values enum in settings.h for defaults */
uset_autolog(s, "0");
uset_time(s, "24");
uset_timestamps(s, "1");
uset_alerts(s, "1");
uset_colours(s, "0");
uset_hst_size(s, "700");
uset_dwnld_path(s, NULL);
#ifdef _SUPPORT_AUDIO
uset_ain_dev(s, "0");
uset_aout_dev(s, "0");
#endif
}
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];
#ifdef SOUND_NOTIFY
static const struct sound_strings {
const char* self;
const char* notif_error;
const char* self_log_in;
const char* self_log_out;
const char* user_log_in;
const char* user_log_out;
const char* call_incoming;
const char* call_outgoing;
const char* generic_message;
const char* transfer_pending;
const char* transfer_completed;
} sound_strings = {
"sounds",
"notif_error",
"self_log_in",
"self_log_out",
"user_log_in",
"user_log_out",
"call_incoming",
"call_outgoing",
"generic_message",
"transfer_pending",
"transfer_completed",
};
#endif
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");
static int key_parse(const char** bind){
int len = strlen(*bind);
if (len > 5) {
if(strncasecmp(*bind, "ctrl+", 5) == 0)
return toupper(bind[0][5]) - 'A' + 1;
}
free(user_config_dir);
if (strncasecmp(*bind, "tab", 3) == 0)
return T_KEY_TAB;
set_default_settings(s);
if (strncasecmp(*bind, "page", 4) == 0)
return len == 6 ? KEY_PPAGE : KEY_NPAGE;
if (fp == NULL && !path) {
if ((fp = fopen(dflt_path, "w")) == NULL)
return -1;
} else if (fp == NULL && path) {
return -1;
}
int settings_load(struct user_settings *s, const char *patharg)
{
config_t cfg[1];
config_setting_t *setting;
const char *str = NULL;
/* Load default settings */
ui_defaults(s);
tox_defaults(s);
key_defaults(s);
#ifdef AUDIO
audio_defaults(s);
#endif
config_init(cfg);
char path[MAX_STR_SIZE];
/* use default config file path */
if (patharg == NULL) {
char *user_config_dir = get_user_config_dir();
snprintf(path, sizeof(path), "%s%stoxic.conf", user_config_dir, CONFIGDIR);
free(user_config_dir);
/* make sure path exists or is created on first time running */
if (!file_exists(path)) {
FILE *fp = fopen(path, "w");
if (fp == NULL)
return -1;
fclose(fp);
}
} else {
snprintf(path, sizeof(path), "%s", patharg);
}
if (!config_read_file(cfg, path)) {
config_destroy(cfg);
return -1;
}
char line[MAX_STR_SIZE];
/* ui */
if ((setting = config_lookup(cfg, ui_strings.self)) != NULL) {
config_setting_lookup_bool(setting, ui_strings.timestamps, &s->timestamps);
while (fgets(line, sizeof(line), fp)) {
if (line[0] == '#' || !line[0])
continue;
const char *key = strtok(line, ":");
const char *val = strtok(NULL, ";");
if (key == NULL || val == NULL)
continue;
int i;
for (i = 0; i < NUM_SETTINGS; ++i) {
if (strcmp(user_settings_list[i].key, key) == 0) {
(user_settings_list[i].func)(s, val);
break;
int time = 24;
if ( config_setting_lookup_int(setting, ui_strings.time_format, &time) ) {
if (time == 12) {
snprintf(s->timestamp_format, sizeof(s->timestamp_format), "%s", "%I:%M:%S %p");
snprintf(s->log_timestamp_format, sizeof(s->log_timestamp_format), "%s", "%Y/%m/%d [%I:%M:%S %p]");
}
}
if ( config_setting_lookup_string(setting, ui_strings.timestamp_format, &str) ) {
snprintf(s->timestamp_format, sizeof(s->timestamp_format), "%s", str);
}
if ( config_setting_lookup_string(setting, ui_strings.log_timestamp_format, &str) ) {
snprintf(s->log_timestamp_format, sizeof(s->log_timestamp_format), "%s", str);
}
config_setting_lookup_bool(setting, ui_strings.alerts, &s->alerts);
config_setting_lookup_bool(setting, ui_strings.autolog, &s->autolog);
config_setting_lookup_bool(setting, ui_strings.native_colors, &s->colour_theme);
config_setting_lookup_int(setting, ui_strings.history_size, &s->history_size);
config_setting_lookup_bool(setting, ui_strings.show_typing_self, &s->show_typing_self);
config_setting_lookup_bool(setting, ui_strings.show_typing_other, &s->show_typing_other);
config_setting_lookup_bool(setting, ui_strings.show_welcome_msg, &s->show_welcome_msg);
if ( config_setting_lookup_string(setting, ui_strings.line_join, &str) ) {
snprintf(s->line_join, sizeof(s->line_join), "%s", str);
}
if ( config_setting_lookup_string(setting, ui_strings.line_quit, &str) ) {
snprintf(s->line_quit, sizeof(s->line_quit), "%s", str);
}
if ( config_setting_lookup_string(setting, ui_strings.line_alert, &str) ) {
snprintf(s->line_alert, sizeof(s->line_alert), "%s", str);
}
if ( config_setting_lookup_string(setting, ui_strings.line_normal, &str) ) {
snprintf(s->line_normal, sizeof(s->line_normal), "%s", str);
}
config_setting_lookup_bool (setting, ui_strings.mplex_away, &s->mplex_away);
if (config_setting_lookup_string (setting, ui_strings.mplex_away_note, &str)) {
snprintf (s->mplex_away_note, sizeof (s->mplex_away_note), "%s", str);
}
}
fclose(fp);
/* paths */
if ((setting = config_lookup(cfg, tox_strings.self)) != NULL) {
if ( config_setting_lookup_string(setting, tox_strings.download_path, &str) ) {
snprintf(s->download_path, sizeof(s->download_path), "%s", str);
int len = strlen(s->download_path);
/* make sure path ends with a '/' */
if (len >= sizeof(s->download_path) - 2)
s->download_path[0] = '\0';
else if (s->download_path[len - 1] != '/')
strcat(&s->download_path[len - 1], "/");
}
if ( config_setting_lookup_string(setting, tox_strings.chatlogs_path, &str) ) {
snprintf(s->chatlogs_path, sizeof(s->chatlogs_path), "%s", str);
int len = strlen(s->chatlogs_path);
if (len >= sizeof(s->chatlogs_path) - 2)
s->chatlogs_path[0] = '\0';
else if (s->chatlogs_path[len - 1] != '/')
strcat(&s->chatlogs_path[len - 1], "/");
}
if ( config_setting_lookup_string(setting, tox_strings.avatar_path, &str) ) {
snprintf(s->avatar_path, sizeof(s->avatar_path), "%s", str);
int len = strlen(str);
if (len >= sizeof(s->avatar_path))
s->avatar_path[0] = '\0';
}
}
/* keys */
if ((setting = config_lookup(cfg, key_strings.self)) != NULL) {
const char* tmp = NULL;
if (config_setting_lookup_string(setting, key_strings.next_tab, &tmp))
s->key_next_tab = key_parse(&tmp);
if (config_setting_lookup_string(setting, key_strings.prev_tab, &tmp))
s->key_prev_tab = key_parse(&tmp);
if (config_setting_lookup_string(setting, key_strings.scroll_line_up, &tmp))
s->key_scroll_line_up = key_parse(&tmp);
if (config_setting_lookup_string(setting, key_strings.scroll_line_down, &tmp))
s->key_scroll_line_down= key_parse(&tmp);
if (config_setting_lookup_string(setting, key_strings.half_page_up, &tmp))
s->key_half_page_up = key_parse(&tmp);
if (config_setting_lookup_string(setting, key_strings.half_page_down, &tmp))
s->key_half_page_down = key_parse(&tmp);
if (config_setting_lookup_string(setting, key_strings.page_bottom, &tmp))
s->key_page_bottom = key_parse(&tmp);
if (config_setting_lookup_string(setting, key_strings.peer_list_up, &tmp))
s->key_peer_list_up = key_parse(&tmp);
if (config_setting_lookup_string(setting, key_strings.peer_list_down, &tmp))
s->key_peer_list_down = key_parse(&tmp);
if (config_setting_lookup_string(setting, key_strings.toggle_peerlist, &tmp))
s->key_toggle_peerlist = key_parse(&tmp);
}
#ifdef AUDIO
if ((setting = config_lookup(cfg, audio_strings.self)) != NULL) {
config_setting_lookup_int(setting, audio_strings.input_device, &s->audio_in_dev);
s->audio_in_dev = s->audio_in_dev < 0 || s->audio_in_dev > MAX_DEVICES ? 0 : s->audio_in_dev;
config_setting_lookup_int(setting, audio_strings.output_device, &s->audio_out_dev);
s->audio_out_dev = s->audio_out_dev < 0 || s->audio_out_dev > MAX_DEVICES ? 0 : s->audio_out_dev;
config_setting_lookup_float(setting, audio_strings.VAD_treshold, &s->VAD_treshold);
}
#endif
#ifdef SOUND_NOTIFY
if ((setting = config_lookup(cfg, sound_strings.self)) != NULL) {
if ( (config_setting_lookup_string(setting, sound_strings.notif_error, &str) != CONFIG_TRUE) ||
!set_sound(notif_error, str) ) {
if (str && strcasecmp(str, NO_SOUND) != 0)
set_sound(notif_error, PACKAGE_DATADIR "/sounds/ToxicError.wav");
}
if ( !config_setting_lookup_string(setting, sound_strings.user_log_in, &str) ||
!set_sound(user_log_in, str) ) {
if (str && strcasecmp(str, NO_SOUND) != 0)
set_sound(user_log_in, PACKAGE_DATADIR "/sounds/ToxicContactOnline.wav");
}
if ( !config_setting_lookup_string(setting, sound_strings.user_log_out, &str) ||
!set_sound(user_log_out, str) ) {
if (str && strcasecmp(str, NO_SOUND) != 0)
set_sound(user_log_out, PACKAGE_DATADIR "/sounds/ToxicContactOffline.wav");
}
if ( !config_setting_lookup_string(setting, sound_strings.call_incoming, &str) ||
!set_sound(call_incoming, str) ) {
if (str && strcasecmp(str, NO_SOUND) != 0)
set_sound(call_incoming, PACKAGE_DATADIR "/sounds/ToxicIncomingCall.wav");
}
if ( !config_setting_lookup_string(setting, sound_strings.call_outgoing, &str) ||
!set_sound(call_outgoing, str) ) {
if (str && strcasecmp(str, NO_SOUND) != 0)
set_sound(call_outgoing, PACKAGE_DATADIR "/sounds/ToxicOutgoingCall.wav");
}
if ( !config_setting_lookup_string(setting, sound_strings.generic_message, &str) ||
!set_sound(generic_message, str) ) {
if (str && strcasecmp(str, NO_SOUND) != 0)
set_sound(generic_message, PACKAGE_DATADIR "/sounds/ToxicRecvMessage.wav");
}
if ( !config_setting_lookup_string(setting, sound_strings.transfer_pending, &str) ||
!set_sound(transfer_pending, str) ) {
if (str && strcasecmp(str, NO_SOUND) != 0)
set_sound(transfer_pending, PACKAGE_DATADIR "/sounds/ToxicTransferStart.wav");
}
if ( !config_setting_lookup_string(setting, sound_strings.transfer_completed, &str) ||
!set_sound(transfer_completed, str) ) {
if (str && strcasecmp(str, NO_SOUND) != 0)
set_sound(transfer_completed, PACKAGE_DATADIR "/sounds/ToxicTransferComplete.wav");
}
}
else {
set_sound(notif_error, PACKAGE_DATADIR "/sounds/ToxicError.wav");
set_sound(user_log_in, PACKAGE_DATADIR "/sounds/ToxicContactOnline.wav");
set_sound(user_log_out, PACKAGE_DATADIR "/sounds/ToxicContactOffline.wav");
set_sound(call_incoming, PACKAGE_DATADIR "/sounds/ToxicIncomingCall.wav");
set_sound(call_outgoing, PACKAGE_DATADIR "/sounds/ToxicOutgoingCall.wav");
set_sound(generic_message, PACKAGE_DATADIR "/sounds/ToxicRecvMessage.wav");
set_sound(transfer_pending, PACKAGE_DATADIR "/sounds/ToxicTransferStart.wav");
set_sound(transfer_completed, PACKAGE_DATADIR "/sounds/ToxicTransferComplete.wav");
}
#endif
config_destroy(cfg);
return 0;
}

View File

@ -20,30 +20,58 @@
*
*/
#ifndef _settings_h
#define _settings_h
#ifndef SETTINGS_H
#define SETTINGS_H
#include "toxic.h"
#include <limits.h>
#ifdef _SUPPORT_AUDIO
#define NUM_SETTINGS 9
#else
#define NUM_SETTINGS 7
#endif /* _SUPPORT_AUDIO */
#include <tox/tox.h>
/* Represents line_* hints max strlen */
#define LINE_HINT_MAX 3
/* holds user setting values */
struct user_settings {
int autolog; /* boolean */
int alerts; /* boolean */
int time; /* 12 or 24 */
int timestamps; /* boolean */
char timestamp_format[TIME_STR_SIZE];
char log_timestamp_format[TIME_STR_SIZE];
int colour_theme; /* boolean (0 for default toxic colours) */
int history_size; /* int between MIN_HISTORY and MAX_HISTORY */
char download_path[MAX_STR_SIZE];
int show_typing_self; /* boolean */
int show_typing_other; /* boolean */
int show_welcome_msg; /* boolean */
#ifdef _SUPPORT_AUDIO
long int audio_in_dev;
long int audio_out_dev;
char line_join[LINE_HINT_MAX + 1];
char line_quit[LINE_HINT_MAX + 1];
char line_alert[LINE_HINT_MAX + 1];
char line_normal[LINE_HINT_MAX + 1];
char download_path[PATH_MAX];
char chatlogs_path[PATH_MAX];
char avatar_path[PATH_MAX];
int key_next_tab;
int key_prev_tab;
int key_scroll_line_up;
int key_scroll_line_down;
int key_half_page_up;
int key_half_page_down;
int key_page_bottom;
int key_peer_list_up;
int key_peer_list_down;
int key_toggle_peerlist;
int mplex_away; /* boolean (1 for reaction to terminal attach/detach) */
char mplex_away_note [TOX_MAX_STATUS_MESSAGE_LENGTH];
#ifdef AUDIO
int audio_in_dev;
int audio_out_dev;
double VAD_treshold;
#endif
};
@ -51,21 +79,34 @@ enum {
AUTOLOG_OFF = 0,
AUTOLOG_ON = 1,
TIME_24 = 24,
TIME_12 = 12,
TIMESTAMPS_OFF = 0,
TIMESTAMPS_ON = 1,
ALERTS_DISABLED = 0,
ALERTS_ENABLED = 1,
NATIVE_COLS = 1,
DFLT_COLS = 0,
NATIVE_COLS = 1,
SHOW_TYPING_OFF = 0,
SHOW_TYPING_ON = 1,
SHOW_WELCOME_MSG_OFF = 0,
SHOW_WELCOME_MSG_ON = 1,
DFLT_HST_SIZE = 700,
MPLEX_OFF = 0,
MPLEX_ON = 1,
} settings_values;
int settings_load(struct user_settings *s, char *path);
#define LINE_JOIN "-->"
#define LINE_QUIT "<--"
#define LINE_ALERT "-!-"
#define LINE_NORMAL "---"
#define TIMESTAMP_DEFAULT "%H:%M:%S"
#define LOG_TIMESTAMP_DEFAULT "%Y/%m/%d [%H:%M:%S]"
#define MPLEX_AWAY_NOTE "Detached from screen"
#endif /* #define _settings_h */
int settings_load(struct user_settings *s, const char *patharg);
#endif /* #define SETTINGS_H */

398
src/term_mplex.c Normal file
View File

@ -0,0 +1,398 @@
/* term_mplex.c
*
*
* Copyright (C) 2015 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 <limits.h> /* PATH_MAX */
#include <stdio.h> /* fgets, popen, pclose */
#include <stdlib.h> /* malloc, realloc, free, getenv */
#include <string.h> /* strlen, strcpy, strstr, strchr, strrchr, strcat, strncmp */
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include <tox/tox.h>
#include "global_commands.h"
#include "windows.h"
#include "term_mplex.h"
#include "toxic.h"
#include "settings.h"
extern struct ToxWindow *prompt;
extern struct user_settings *user_settings;
extern struct Winthread Winthread;
#if defined(PATH_MAX) && PATH_MAX > 512
#define BUFFER_SIZE PATH_MAX
#else
#define BUFFER_SIZE 512
#endif
#define PATH_SEP_S "/"
#define PATH_SEP_C '/'
typedef enum
{
MPLEX_NONE,
MPLEX_SCREEN,
MPLEX_TMUX,
} mplex_status;
/* used for:
- storing screen socket name
- storing tmux session number in string form */
static char mplex_data [BUFFER_SIZE];
static char buffer [BUFFER_SIZE];
/* Differentiates between mplex auto-away and manual-away */
static bool auto_away_active = false;
static mplex_status mplex = MPLEX_NONE;
static TOX_USER_STATUS prev_status = TOX_USER_STATUS_NONE;
static char prev_note [TOX_MAX_STATUS_MESSAGE_LENGTH] = "";
/* mutex for access to status data, for sync between:
- user command /status from ncurses thread
- auto-away POSIX timer, which runs from a separate thread
after init, should be accessed only by cmd_status()
*/
static pthread_mutex_t status_lock;
static pthread_t mplex_tid;
void lock_status ()
{
pthread_mutex_lock (&status_lock);
}
void unlock_status ()
{
pthread_mutex_unlock (&status_lock);
}
static char *read_into_dyn_buffer (FILE *stream)
{
const char *input_ptr = NULL;
char *dyn_buffer = NULL;
int dyn_buffer_size = 1; /* account for the \0 */
while ((input_ptr = fgets (buffer, BUFFER_SIZE, stream)) != NULL)
{
int length = dyn_buffer_size + strlen (input_ptr);
if (dyn_buffer)
dyn_buffer = (char*) realloc (dyn_buffer, length);
else
dyn_buffer = (char*) malloc (length);
strcpy (dyn_buffer + dyn_buffer_size - 1, input_ptr);
dyn_buffer_size = length;
}
return dyn_buffer;
}
static char *extract_socket_path (const char *info)
{
const char *search_str = " Socket";
const char *pos = strstr (info, search_str);
char *end = NULL;
char* path = NULL;
if (!pos)
return NULL;
pos += strlen (search_str);
pos = strchr (pos, PATH_SEP_C);
if (!pos)
return NULL;
end = strchr (pos, '\n');
if (!end)
return NULL;
*end = '\0';
end = strrchr (pos, '.');
if (!end)
return NULL;
path = (char*) malloc (end - pos + 1);
*end = '\0';
return strcpy (path, pos);
}
static int detect_gnu_screen ()
{
FILE *session_info_stream = NULL;
char *socket_name = NULL, *socket_path = NULL;
char *dyn_buffer = NULL;
socket_name = getenv ("STY");
if (!socket_name)
goto nomplex;
session_info_stream = popen ("env LC_ALL=C screen -ls", "r");
if (!session_info_stream)
goto nomplex;
dyn_buffer = read_into_dyn_buffer (session_info_stream);
if (!dyn_buffer)
goto nomplex;
pclose (session_info_stream);
session_info_stream = NULL;
socket_path = extract_socket_path (dyn_buffer);
if (!socket_path)
goto nomplex;
free (dyn_buffer);
dyn_buffer = NULL;
if (strlen(socket_path) + strlen(PATH_SEP_S) + strlen(socket_name) >= sizeof(mplex_data))
goto nomplex;
strcpy (mplex_data, socket_path);
strcat (mplex_data, PATH_SEP_S);
strcat (mplex_data, socket_name);
free (socket_path);
socket_path = NULL;
mplex = MPLEX_SCREEN;
return 1;
nomplex:
if (session_info_stream)
pclose (session_info_stream);
if (dyn_buffer)
free (dyn_buffer);
if (socket_path)
free(socket_path);
return 0;
}
static int detect_tmux ()
{
char *tmux_env = getenv ("TMUX"), *pos;
if (!tmux_env)
return 0;
/* find second separator */
pos = strrchr (tmux_env, ',');
if (!pos)
return 0;
/* store the session number string for later use */
snprintf (mplex_data, sizeof(mplex_data), "%s", pos + 1);
mplex = MPLEX_TMUX;
return 1;
}
/* Checks whether a terminal multiplexer (mplex) is present, and finds
its unix socket.
GNU screen and tmux are supported.
Returns 1 if present, 0 otherwise. This value can be used to determine
whether an auto-away detection timer is needed.
*/
static int detect_mplex ()
{
/* try screen, and if fails try tmux */
return detect_gnu_screen () || detect_tmux ();
}
/* Detects gnu screen session attached/detached by examining permissions of
the session's unix socket.
*/
static int gnu_screen_is_detached ()
{
if (mplex != MPLEX_SCREEN)
return 0;
struct stat sb;
if (stat (mplex_data, &sb) != 0)
return 0;
/* execution permission (x) means attached */
return ! (sb.st_mode & S_IXUSR);
}
/* Detects tmux attached/detached by getting session data and finding the
current session's entry. An attached entry ends with "(attached)". Example:
$ tmux list-sessions
0: 1 windows (created Mon Mar 2 21:48:29 2015) [80x23] (attached)
1: 2 windows (created Mon Mar 2 21:48:43 2015) [80x23]
In this example, session 0 is attached and session 1 is detached.
*/
static int tmux_is_detached ()
{
if (mplex != MPLEX_TMUX)
return 0;
FILE *session_info_stream = NULL;
char *dyn_buffer = NULL, *search_str = NULL;
char *entry_pos, *nl_pos, *attached_pos;
const int numstr_len = strlen (mplex_data);
session_info_stream = popen ("env LC_ALL=C tmux list-sessions", "r");
if (!session_info_stream)
goto fail;
dyn_buffer = read_into_dyn_buffer (session_info_stream);
if (!dyn_buffer)
goto fail;
pclose (session_info_stream);
session_info_stream = NULL;
/* prepare search string, for finding the current session's entry */
search_str = (char*) malloc (numstr_len + 4);
search_str[0] = '\n';
strcpy (search_str + 1, mplex_data);
strcat (search_str, ": ");
/* do the search */
if (strncmp (dyn_buffer, search_str + 1, numstr_len + 2) == 0)
entry_pos = dyn_buffer;
else
entry_pos = strstr (dyn_buffer, search_str);
if (! entry_pos)
goto fail;
/* find the next \n and look for the "(attached)" before it */
nl_pos = strchr (entry_pos + 1, '\n');
attached_pos = strstr (entry_pos + 1, "(attached)\n");
free (search_str);
search_str = NULL;
free (dyn_buffer);
dyn_buffer = NULL;
return attached_pos == NULL || attached_pos > nl_pos;
fail:
if (session_info_stream)
pclose (session_info_stream);
if (dyn_buffer)
free (dyn_buffer);
if (search_str)
free (search_str);
return 0;
}
/* Checks whether there is a terminal multiplexer present, but in detached
state. Returns 1 if detached, 0 if attached or if there is no terminal
multiplexer.
If detect_mplex_socket() failed to find a mplex, there is no need to call
this function. If it did find one, this function can be used to periodically
sample its state and update away status according to attached/detached state
of the mplex.
*/
static int mplex_is_detached ()
{
return gnu_screen_is_detached () || tmux_is_detached ();
}
static void mplex_timer_handler (Tox *m)
{
TOX_USER_STATUS current_status, new_status;
const char *new_note;
if (mplex == MPLEX_NONE)
return;
int detached = mplex_is_detached ();
pthread_mutex_lock (&Winthread.lock);
current_status = tox_self_get_status (m);
pthread_mutex_unlock (&Winthread.lock);
if (auto_away_active && current_status == TOX_USER_STATUS_AWAY && !detached)
{
auto_away_active = false;
new_status = prev_status;
new_note = prev_note;
}
else
if (current_status == TOX_USER_STATUS_NONE && detached)
{
auto_away_active = true;
prev_status = current_status;
new_status = TOX_USER_STATUS_AWAY;
pthread_mutex_lock (&Winthread.lock);
size_t slen = tox_self_get_status_message_size(m);
tox_self_get_status_message (m, (uint8_t*) prev_note);
prev_note[slen] = '\0';
pthread_mutex_unlock (&Winthread.lock);
new_note = user_settings->mplex_away_note;
}
else
return;
char argv[3][MAX_STR_SIZE];
strcpy (argv[0], "/status");
strcpy (argv[1], (new_status == TOX_USER_STATUS_AWAY ? "away" :
new_status == TOX_USER_STATUS_BUSY ? "busy" : "online"));
argv[2][0] = '\"';
strcpy (argv[2] + 1, new_note);
strcat (argv[2], "\"");
pthread_mutex_lock (&Winthread.lock);
cmd_status (prompt->chatwin->history, prompt, m, 2, argv);
pthread_mutex_unlock (&Winthread.lock);
}
/* Time in seconds between calls to mplex_timer_handler */
#define MPLEX_TIMER_INTERVAL 5
void *mplex_timer_thread(void *data)
{
Tox *m = (Tox *) data;
while (true) {
sleep(MPLEX_TIMER_INTERVAL);
mplex_timer_handler(m);
}
}
int init_mplex_away_timer (Tox *m)
{
if (! detect_mplex ())
return 0;
if (! user_settings->mplex_away)
return 0;
/* status access mutex */
if (pthread_mutex_init (&status_lock, NULL) != 0)
return -1;
if (pthread_create(&mplex_tid, NULL, mplex_timer_thread, (void *) m) != 0)
return -1;
return 0;
}

35
src/term_mplex.h Normal file
View File

@ -0,0 +1,35 @@
/* term_mplex.h
*
*
* Copyright (C) 2015 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 TERM_MPLEX_H
#define TERM_MPLEX_H
/* Checks if Toxic runs inside a terminal multiplexer (GNU screen or tmux). If
yes, it initializes a timer which periodically checks the attached/detached
state of the terminal and updates away status accordingly.
*/
int init_mplex_away_timer (Tox *m);
void lock_status ();
void unlock_status ();
#endif /* #define TERM_MPLEX_H */

File diff suppressed because it is too large Load Diff

View File

@ -20,11 +20,11 @@
*
*/
#ifndef _toxic_h
#define _toxic_h
#ifndef TOXIC_H
#define TOXIC_H
#ifndef TOXICVER
#define TOXICVER "NOVER_" /* Use the -D flag to set this */
#define TOXICVER "NOVERS" /* Use the -D flag to set this */
#endif
#ifndef SIGWINCH
@ -41,16 +41,16 @@
#include <tox/tox.h>
#define UNKNOWN_NAME "Anonymous"
#define DEFAULT_TOX_NAME "Tox User" /* should always be the same as toxcore's default name */
#define MAX_FRIENDS_NUM 500
#define MAX_STR_SIZE TOX_MAX_MESSAGE_LENGTH
#define MAX_STR_SIZE TOX_MAX_MESSAGE_LENGTH /* must be >= TOX_MAX_MESSAGE_LENGTH */
#define MAX_CMDNAME_SIZE 64
#define TOXIC_MAX_NAME_LENGTH 32 /* Must be <= TOX_MAX_NAME_LENGTH */
#define KEY_IDENT_DIGITS 2 /* number of hex digits to display for the pub-key based identifier */
#define TIME_STR_SIZE 16
#define KEY_IDENT_DIGITS 3 /* number of hex digits to display for the pub-key based identifier */
#define TIME_STR_SIZE 32
/* ASCII key codes */
#define T_KEY_ESC 0X1B /* esc key */
#define T_KEY_ESC 0x1B /* ESC key */
#define T_KEY_KILL 0x0B /* ctrl-k */
#define T_KEY_DISCARD 0x15 /* ctrl-u */
#define T_KEY_NEXT 0x10 /* ctrl-p */
@ -63,46 +63,62 @@
#define T_KEY_C_F 0x06 /* ctrl-f */
#define T_KEY_C_H 0x08 /* ctrl-h */
#define T_KEY_C_Y 0x19 /* ctrl-y */
#define T_KEY_C_L 0x0C /* ctrl-l */
#define T_KEY_C_W 0x17 /* ctrl-w */
#define T_KEY_C_B 0x02 /* ctrl-b */
#define T_KEY_TAB 0x09 /* TAB key */
#define ONLINE_CHAR "*"
#define OFFLINE_CHAR "o"
typedef enum _FATAL_ERRS {
FATALERR_MEMORY = -1, /* malloc() or calloc() failed */
FATALERR_FREAD = -2, /* fread() failed on critical read */
FATALERR_THREAD_CREATE = -3, /* thread creation failed */
FATALERR_MUTEX_INIT = -4, /* mutex init failed */
FATALERR_MEMORY = -1, /* heap memory allocation failed */
FATALERR_FILEOP = -2, /* critical file operation failed */
FATALERR_THREAD_CREATE = -3, /* thread creation failed for critical thread */
FATALERR_MUTEX_INIT = -4, /* mutex init for critical thread failed */
FATALERR_THREAD_ATTR = -5, /* thread attr object init failed */
FATALERR_LOCALE_SET = -6, /* system locale not set */
FATALERR_LOCALE_NOT_SET = -6, /* system locale not set */
FATALERR_STORE_DATA = -7, /* store_data failed in critical section */
FATALERR_NETWORKINIT = -8, /* Tox network failed to init */
FATALERR_INFLOOP = -9, /* infinite loop detected */
FATALERR_WININIT = -10, /* window init failed */
FATALERR_INFLOOP = -8, /* infinite loop detected */
FATALERR_WININIT = -9, /* window init failed */
FATALERR_PROXY = -10, /* Tox network failed to init using a proxy */
FATALERR_ENCRYPT = -11, /* Data file encryption failure */
FATALERR_TOX_INIT = -12, /* Tox instance failed to initialize */
} FATAL_ERRS;
/* Fixes text color problem on some terminals.
Uncomment if necessary */
/* #define URXVT_FIX */
void lock_status ();
void unlock_status ();
void exit_toxic_success(Tox *m);
void exit_toxic_err(const char *errmsg, int errcode);
int store_data(Tox *m, char *path);
int store_data(Tox *m, const char *path);
void on_request(Tox *m, const uint8_t *public_key, const 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, const uint8_t *string, uint16_t length, void *userdata);
void on_action(Tox *m, int32_t friendnumber, const uint8_t *string, uint16_t length, void *userdata);
void on_nickchange(Tox *m, int32_t friendnumber, const 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, const uint8_t *string, uint16_t length, void *userdata);
void on_friendadded(Tox *m, int32_t friendnumber, bool sort);
/* callbacks */
void on_request(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata);
void on_connectionchange(Tox *m, uint32_t friendnumber, TOX_CONNECTION status, void *userdata);
void on_message(Tox *m, uint32_t friendnumber, TOX_MESSAGE_TYPE type, const uint8_t *string, size_t length, void *userdata);
void on_action(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t length, void *userdata);
void on_nickchange(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t length, void *userdata);
void on_statuschange(Tox *m, uint32_t friendnumber, TOX_USER_STATUS status, void *userdata);
void on_statusmessagechange(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t length, void *userdata);
void on_friendadded(Tox *m, uint32_t friendnumber, bool sort);
void on_groupmessage(Tox *m, int groupnumber, int peernumber, const uint8_t *message, uint16_t length, void *userdata);
void on_groupaction(Tox *m, int groupnumber, int peernumber, const uint8_t *action, uint16_t length, void *userdata);
void on_groupinvite(Tox *m, int32_t friendnumber, const uint8_t *group_pub_key, void *userdata);
void on_groupinvite(Tox *m, int32_t friendnumber, uint8_t type, const uint8_t *group_pub_key, uint16_t length, void *userdata);
void on_group_namelistchange(Tox *m, int groupnumber, int peernumber, uint8_t change, void *userdata);
void on_file_sendrequest(Tox *m, int32_t friendnumber, uint8_t filenumber, uint64_t filesize, const 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,
const uint8_t *data, uint16_t length, void *userdata);
void on_file_data(Tox *m, int32_t friendnumber, uint8_t filenumber, const uint8_t *data, uint16_t length, void *userdata);
void on_typing_change(Tox *m, int32_t friendnumber, uint8_t is_typing, void *userdata);
void on_group_titlechange(Tox *m, int groupnumber, int peernumber, const uint8_t *title, uint8_t length, void *userdata);
void on_file_chunk_request(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint64_t position, size_t length, void *userdata);
void on_file_recv_chunk(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint64_t position, const uint8_t *data,
size_t length, void *userdata);
void on_file_control (Tox *m, uint32_t friendnumber, uint32_t filenumber, TOX_FILE_CONTROL control, void *userdata);
void on_file_recv(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint32_t kind, uint64_t file_size,
const uint8_t *filename, size_t filename_length, void *userdata);
void on_typing_change(Tox *m, uint32_t friendnumber, bool is_typing, void *userdata);
void on_read_receipt(Tox *m, uint32_t friendnumber, uint32_t receipt, void *userdata);
#endif /* #define _toxic_h */
#endif /* #define TOXIC_H */

View File

@ -28,6 +28,7 @@
#include "windows.h"
#include "misc_tools.h"
#include "toxic_strings.h"
#include "notify.h"
/* Adds char to line at pos. Return 0 on success, -1 if line buffer is full */
int add_char_to_buf(ChatContext *ctx, wint_t ch)
@ -111,7 +112,7 @@ int yank_buf(ChatContext *ctx)
if (!ctx->yank[0])
return -1;
if (ctx->yank_len + ctx->len >= MAX_STR_SIZE - 1)
if (ctx->yank_len + ctx->len >= MAX_STR_SIZE)
return -1;
wmemmove(&ctx->line[ctx->pos + ctx->yank_len], &ctx->line[ctx->pos], ctx->len - ctx->pos);
@ -120,6 +121,39 @@ int yank_buf(ChatContext *ctx)
ctx->pos += ctx->yank_len;
ctx->len += ctx->yank_len;
ctx->line[ctx->len] = L'\0';
return 0;
}
/* Deletes all characters from line starting at pos and going backwards
until we find a space or run out of characters.
Return 0 on success, -1 if nothing to delete */
int del_word_buf(ChatContext *ctx)
{
if (ctx->len == 0 || ctx->pos == 0)
return -1;
int i = ctx->pos, count = 0;
/* traverse past empty space */
while (i > 0 && ctx->line[i-1] == L' ') {
++count;
--i;
}
/* traverse past last entered word */
while (i > 0 && ctx->line[i-1] != L' ') {
++count;
--i;
}
wmemmove(&ctx->line[i], &ctx->line[ctx->pos], ctx->len - ctx->pos);
ctx->start = MAX(0, ctx->start - count); /* TODO: take into account widechar */
ctx->len -= count;
ctx->pos -= count;
ctx->line[ctx->len] = L'\0';
return 0;
}
@ -170,7 +204,7 @@ static void shift_hist_back(ChatContext *ctx)
/* adds a line to the ln_history buffer at hst_pos and sets hst_pos to end of history. */
void add_line_to_hist(ChatContext *ctx)
{
if (ctx->len > MAX_STR_SIZE)
if (ctx->len >= MAX_STR_SIZE)
return;
if (ctx->hst_tot >= MAX_LINE_HIST)
@ -191,7 +225,7 @@ void fetch_hist_item(ChatContext *ctx, int key_dir)
if (key_dir == KEY_UP) {
if (--ctx->hst_pos < 0) {
ctx->hst_pos = 0;
beep();
sound_notify(NULL, notif_error, NT_ALWAYS, NULL);
}
} else {
if (++ctx->hst_pos >= ctx->hst_tot) {
@ -208,86 +242,3 @@ void fetch_hist_item(ChatContext *ctx, int key_dir)
ctx->pos = h_len;
ctx->len = h_len;
}
/* looks for the first instance in list that begins with the last entered word in line according to pos,
then fills line with the complete word. e.g. "Hello jo" would complete the line
with "Hello john".
list is a pointer to the list of strings being compared, n_items is the number of items
in the list, and size is the size of each item in the list.
Returns the difference between the old len and new len of line on success, -1 if error */
int complete_line(ChatContext *ctx, const void *list, int n_items, int size)
{
if (ctx->pos <= 0 || ctx->len <= 0 || ctx->len >= MAX_STR_SIZE)
return -1;
const char *L = (char *) list;
char ubuf[MAX_STR_SIZE];
/* work with multibyte string copy of buf for simplicity */
if (wcs_to_mbs_buf(ubuf, ctx->line, sizeof(ubuf)) == -1)
return -1;
/* isolate substring from space behind pos to pos */
char tmp[MAX_STR_SIZE];
snprintf(tmp, sizeof(tmp), "%s", ubuf);
tmp[ctx->pos] = '\0';
char *sub = strrchr(tmp, ' ');
int n_endchrs = 1; /* 1 = append space to end of match, 2 = append ": " */
if (!sub++) {
sub = tmp;
if (sub[0] != '/') /* make sure it's not a command */
n_endchrs = 2;
}
if (string_is_empty(sub))
return -1;
int s_len = strlen(sub);
const char *match;
bool is_match = false;
int i;
/* look for a match in list */
for (i = 0; i < n_items; ++i) {
match = &L[i * size];
if ((is_match = strncasecmp(match, sub, s_len) == 0))
break;
}
if (!is_match)
return -1;
/* put match in correct spot in buf and append endchars (space or ": ") */
const char *endchrs = n_endchrs == 1 ? " " : ": ";
int m_len = strlen(match);
int strt = ctx->pos - s_len;
int diff = m_len - s_len + n_endchrs;
if (ctx->len + diff > MAX_STR_SIZE)
return -1;
char tmpend[MAX_STR_SIZE];
strcpy(tmpend, &ubuf[ctx->pos]);
strcpy(&ubuf[strt], match);
strcpy(&ubuf[strt + m_len], endchrs);
strcpy(&ubuf[strt + m_len + n_endchrs], tmpend);
/* convert to widechar and copy back to original buf */
wchar_t newbuf[MAX_STR_SIZE];
if (mbs_to_wcs_buf(newbuf, ubuf, MAX_STR_SIZE) == -1)
return -1;
wcscpy(ctx->line, newbuf);
ctx->len += diff;
ctx->pos += diff;
return diff;
}

View File

@ -20,8 +20,8 @@
*
*/
#ifndef _toxic_strings_h
#define _toxic_strings_h
#ifndef TOXIC_STRINGS_H
#define TOXIC_STRINGS_H
#include "windows.h"
@ -49,19 +49,14 @@ void reset_buf(ChatContext *ctx);
Return 0 on success, -1 if yank buffer is empty or too long */
int yank_buf(ChatContext *ctx);
/* Deletes all characters from line starting at pos and going backwards
until we find a space or run out of characters.
Return 0 on success, -1 if no line or already at the beginning */
int del_word_buf(ChatContext *ctx);
/* Removes trailing spaces from line. */
void rm_trailing_spaces_buf(ChatContext *ctx);
/* looks for the first instance in list that begins with the last entered word in line according to pos,
then fills line with the complete word. e.g. "Hello jo" would complete the line
with "Hello john".
list is a pointer to the list of strings being compared, n_items is the number of items
in the list, and size is the size of each item in the list.
Returns the difference between the old len and new len of line on success, -1 if error */
int complete_line(ChatContext *ctx, const void *list, int n_items, int size);
/* adds a line to the ln_history buffer at hst_pos and sets hst_pos to last history item. */
void add_line_to_hist(ChatContext *ctx);
@ -71,4 +66,4 @@ void add_line_to_hist(ChatContext *ctx);
resets line if at end of history */
void fetch_hist_item(ChatContext *ctx, int key_dir);
#endif /* #define _toxic_strings_h */
#endif /* #define TOXIC_STRINGS_H */

View File

@ -32,40 +32,52 @@
#include "groupchat.h"
#include "chat.h"
#include "line_info.h"
#include "misc_tools.h"
#include "avatars.h"
#include "settings.h"
#include "file_transfers.h"
extern char *DATA_FILE;
extern struct _Winthread Winthread;
extern struct Winthread Winthread;
static ToxWindow windows[MAX_WINDOWS_NUM];
static ToxWindow *active_window;
extern ToxWindow *prompt;
extern struct user_settings *user_settings;
static int num_active_windows;
/* CALLBACKS START */
void on_request(Tox *m, const uint8_t *public_key, const uint8_t *data, uint16_t length, void *userdata)
void on_request(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata)
{
int i;
char msg[MAX_STR_SIZE + 1];
length = copy_tox_str(msg, sizeof(msg), (const char *) data, length);
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onFriendRequest != NULL)
windows[i].onFriendRequest(&windows[i], m, (const char *) public_key, (const char *) data, length);
if (windows[i].onFriendRequest != NULL) {
windows[i].onFriendRequest(&windows[i], m, (const char *) public_key, msg, length);
}
}
}
void on_connectionchange(Tox *m, int32_t friendnumber, uint8_t status, void *userdata)
void on_connectionchange(Tox *m, uint32_t friendnumber, TOX_CONNECTION connection_status, void *userdata)
{
int i;
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onConnectionChange != NULL)
windows[i].onConnectionChange(&windows[i], m, friendnumber, status);
windows[i].onConnectionChange(&windows[i], m, friendnumber, connection_status);
}
}
void on_typing_change(Tox *m, int32_t friendnumber, uint8_t is_typing, void *userdata)
void on_typing_change(Tox *m, uint32_t friendnumber, bool is_typing, void *userdata)
{
int i;
if (user_settings->show_typing_other == SHOW_TYPING_OFF)
return;
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onTypingChange != NULL)
@ -73,54 +85,53 @@ void on_typing_change(Tox *m, int32_t friendnumber, uint8_t is_typing, void *use
}
}
void on_message(Tox *m, int32_t friendnumber, const uint8_t *string, uint16_t length, void *userdata)
void on_message(Tox *m, uint32_t friendnumber, TOX_MESSAGE_TYPE type, const uint8_t *string, size_t length,
void *userdata)
{
int i;
char msg[MAX_STR_SIZE + 1];
length = copy_tox_str(msg, sizeof(msg), (const char *) string, length);
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onMessage != NULL)
windows[i].onMessage(&windows[i], m, friendnumber, (const char *) string, length);
windows[i].onMessage(&windows[i], m, friendnumber, type, msg, length);
}
}
void on_action(Tox *m, int32_t friendnumber, const uint8_t *string, uint16_t length, void *userdata)
void on_nickchange(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t length, void *userdata)
{
int i;
char nick[TOXIC_MAX_NAME_LENGTH + 1];
length = copy_tox_str(nick, sizeof(nick), (const char *) string, length);
filter_str(nick, length);
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onAction != NULL)
windows[i].onAction(&windows[i], m, friendnumber, (const char *) string, length);
}
}
void on_nickchange(Tox *m, int32_t friendnumber, const uint8_t *string, uint16_t length, void *userdata)
{
if (friendnumber < 0 || friendnumber > MAX_FRIENDS_NUM)
return;
int i;
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onNickChange != NULL)
windows[i].onNickChange(&windows[i], m, friendnumber, (const char *) string, length);
windows[i].onNickChange(&windows[i], m, friendnumber, nick, length);
}
store_data(m, DATA_FILE);
}
void on_statusmessagechange(Tox *m, int32_t friendnumber, const uint8_t *string, uint16_t length, void *userdata)
void on_statusmessagechange(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t length, void *userdata)
{
int i;
char msg[TOX_MAX_STATUS_MESSAGE_LENGTH + 1];
length = copy_tox_str(msg, sizeof(msg), (const char *) string, length);
filter_str(msg, length);
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onStatusMessageChange != NULL)
windows[i].onStatusMessageChange(&windows[i], friendnumber, (const char *) string, length);
windows[i].onStatusMessageChange(&windows[i], friendnumber, msg, length);
}
}
void on_statuschange(Tox *m, int32_t friendnumber, uint8_t status, void *userdata)
void on_statuschange(Tox *m, uint32_t friendnumber, TOX_USER_STATUS status, void *userdata)
{
int i;
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onStatusChange != NULL)
@ -128,9 +139,9 @@ void on_statuschange(Tox *m, int32_t friendnumber, uint8_t status, void *userdat
}
}
void on_friendadded(Tox *m, int32_t friendnumber, bool sort)
void on_friendadded(Tox *m, uint32_t friendnumber, bool sort)
{
int i;
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onFriendAdded != NULL)
@ -143,38 +154,45 @@ void on_friendadded(Tox *m, int32_t friendnumber, bool sort)
void on_groupmessage(Tox *m, int groupnumber, int peernumber, const uint8_t *message, uint16_t length,
void *userdata)
{
int i;
char msg[MAX_STR_SIZE + 1];
length = copy_tox_str(msg, sizeof(msg), (const char *) message, length);
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupMessage != NULL)
windows[i].onGroupMessage(&windows[i], m, groupnumber, peernumber, (const char *) message, length);
windows[i].onGroupMessage(&windows[i], m, groupnumber, peernumber, msg, length);
}
}
void on_groupaction(Tox *m, int groupnumber, int peernumber, const uint8_t *action, uint16_t length,
void *userdata)
{
int i;
char msg[MAX_STR_SIZE + 1];
length = copy_tox_str(msg, sizeof(msg), (const char *) action, length);
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupAction != NULL)
windows[i].onGroupAction(&windows[i], m, groupnumber, peernumber, (const char *) action, length);
windows[i].onGroupAction(&windows[i], m, groupnumber, peernumber, msg, length);
}
}
void on_groupinvite(Tox *m, int32_t friendnumber, const uint8_t *group_pub_key, void *userdata)
void on_groupinvite(Tox *m, int32_t friendnumber, uint8_t type, const uint8_t *group_pub_key, uint16_t length,
void *userdata)
{
int i;
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupInvite != NULL)
windows[i].onGroupInvite(&windows[i], m, friendnumber, (const char *) group_pub_key);
windows[i].onGroupInvite(&windows[i], m, friendnumber, type, (char *) group_pub_key, length);
}
}
void on_group_namelistchange(Tox *m, int groupnumber, int peernumber, uint8_t change, void *userdata)
{
int i;
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupNamelistChange != NULL)
@ -182,41 +200,105 @@ void on_group_namelistchange(Tox *m, int groupnumber, int peernumber, uint8_t ch
}
}
void on_file_sendrequest(Tox *m, int32_t friendnumber, uint8_t filenumber, uint64_t filesize,
const uint8_t *filename, uint16_t filename_length, void *userdata)
void on_group_titlechange(Tox *m, int groupnumber, int peernumber, const uint8_t *title, uint8_t length,
void *userdata)
{
int i;
char data[MAX_STR_SIZE + 1];
length = copy_tox_str(data, sizeof(data), (const char *) title, length);
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onFileSendRequest != NULL)
windows[i].onFileSendRequest(&windows[i], m, friendnumber, filenumber, filesize,
(const char *) filename, filename_length);
if (windows[i].onGroupTitleChange != NULL)
windows[i].onGroupTitleChange(&windows[i], m, groupnumber, peernumber, data, length);
}
}
void on_file_control (Tox *m, int32_t friendnumber, uint8_t receive_send, uint8_t filenumber,
uint8_t control_type, const uint8_t *data, uint16_t length, void *userdata)
void on_file_chunk_request(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint64_t position,
size_t length, void *userdata)
{
int i;
struct FileTransfer *ft = get_file_transfer_struct(friendnumber, filenumber);
if (!ft)
return;
if (ft->file_type == TOX_FILE_KIND_AVATAR) {
on_avatar_chunk_request(m, ft, position, length);
return;
}
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onFileChunkRequest != NULL)
windows[i].onFileChunkRequest(&windows[i], m, friendnumber, filenumber, position, length);
}
}
void on_file_recv_chunk(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint64_t position,
const uint8_t *data, size_t length, void *user_data)
{
struct FileTransfer *ft = get_file_transfer_struct(friendnumber, filenumber);
if (!ft)
return;
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onFileRecvChunk != NULL)
windows[i].onFileRecvChunk(&windows[i], m, friendnumber, filenumber, position, (char *) data, length);
}
}
void on_file_control(Tox *m, uint32_t friendnumber, uint32_t filenumber, TOX_FILE_CONTROL control,
void *userdata)
{
struct FileTransfer *ft = get_file_transfer_struct(friendnumber, filenumber);
if (!ft)
return;
if (ft->file_type == TOX_FILE_KIND_AVATAR) {
on_avatar_file_control(m, ft, control);
return;
}
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onFileControl != NULL)
windows[i].onFileControl(&windows[i], m, friendnumber, receive_send, filenumber,
control_type, (const char *) data, length);
windows[i].onFileControl(&windows[i], m, friendnumber, filenumber, control);
}
}
void on_file_data(Tox *m, int32_t friendnumber, uint8_t filenumber, const uint8_t *data, uint16_t length,
void *userdata)
void on_file_recv(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint32_t kind, uint64_t file_size,
const uint8_t *filename, size_t filename_length, void *userdata)
{
int i;
/* We don't care about receiving avatars */
if (kind != TOX_FILE_KIND_DATA) {
tox_file_control(m, friendnumber, filenumber, TOX_FILE_CONTROL_CANCEL, NULL);
return;
}
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onFileData != NULL)
windows[i].onFileData(&windows[i], m, friendnumber, filenumber, (const char *) data, length);
if (windows[i].onFileRecv != NULL)
windows[i].onFileRecv(&windows[i], m, friendnumber, filenumber, file_size, (char *) filename,
filename_length);
}
}
void on_read_receipt(Tox *m, uint32_t friendnumber, uint32_t receipt, void *userdata)
{
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onReadReceipt != NULL)
windows[i].onReadReceipt(&windows[i], m, friendnumber, receipt);
}
}
/* CALLBACKS END */
int add_window(Tox *m, ToxWindow w)
@ -224,7 +306,7 @@ int add_window(Tox *m, ToxWindow w)
if (LINES < 2)
return -1;
int i;
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; i++) {
if (windows[i].active)
@ -267,7 +349,7 @@ void set_next_window(int ch)
ToxWindow *inf = active_window;
while (true) {
if (ch == T_KEY_NEXT) {
if (ch == user_settings->key_next_tab) {
if (++active_window > end)
active_window = windows;
} else if (--active_window < windows)
@ -318,7 +400,7 @@ void on_window_resize(void)
getmaxyx(stdscr, y2, x2);
y2 -= 2;
int i;
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (!windows[i].active)
@ -335,10 +417,12 @@ void on_window_resize(void)
if (w->help->active)
wclear(w->help->win);
if (w->is_groupchat)
if (w->is_groupchat) {
delwin(w->chatwin->sidebar);
else
w->chatwin->sidebar = NULL;
} else {
delwin(w->stb->topline);
}
delwin(w->chatwin->linewin);
delwin(w->chatwin->history);
@ -347,44 +431,39 @@ void on_window_resize(void)
w->window = newwin(y2, x2, 0, 0);
w->chatwin->linewin = subwin(w->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0);
if (w->is_groupchat) {
if (w->show_peerlist) {
w->chatwin->history = subwin(w->window, y2 - CHATBOX_HEIGHT + 1, x2 - SIDEBAR_WIDTH - 1, 0, 0);
w->chatwin->sidebar = subwin(w->window, y2 - CHATBOX_HEIGHT + 1, SIDEBAR_WIDTH, 0, x2 - SIDEBAR_WIDTH);
} else {
w->chatwin->history = subwin(w->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0);
w->stb->topline = subwin(w->window, 2, x2, 0, 0);
if (!w->is_groupchat)
w->stb->topline = subwin(w->window, 2, x2, 0, 0);
}
#ifdef _SUPPORT_AUDIO
#ifdef AUDIO
if (w->chatwin->infobox.active) {
delwin(w->chatwin->infobox.win);
w->chatwin->infobox.win = newwin(INFOBOX_HEIGHT, INFOBOX_WIDTH + 1, 1, x2 - INFOBOX_WIDTH);
}
#endif /* _SUPPORT_AUDIO */
#endif /* AUDIO */
scrollok(w->chatwin->history, 0);
}
}
static void draw_window_tab(ToxWindow toxwin)
static void draw_window_tab(ToxWindow *toxwin)
{
/* alert0 takes priority */
if (toxwin.alert0)
attron(COLOR_PAIR(GREEN));
else if (toxwin.alert1)
attron(COLOR_PAIR(RED));
else if (toxwin.alert2)
attron(COLOR_PAIR(MAGENTA));
pthread_mutex_lock(&Winthread.lock);
if (toxwin->alert != WINDOW_ALERT_NONE) attron(COLOR_PAIR(toxwin->alert));
pthread_mutex_unlock(&Winthread.lock);
clrtoeol();
printw(" [%s]", toxwin.name);
printw(" [%s]", toxwin->name);
if (toxwin.alert0)
attroff(COLOR_PAIR(GREEN));
else if (toxwin.alert1)
attroff(COLOR_PAIR(RED));
else if (toxwin.alert2)
attroff(COLOR_PAIR(MAGENTA));
pthread_mutex_lock(&Winthread.lock);
if (toxwin->alert != WINDOW_ALERT_NONE) attroff(COLOR_PAIR(toxwin->alert));
pthread_mutex_unlock(&Winthread.lock);
}
static void draw_bar(void)
@ -399,7 +478,7 @@ static void draw_bar(void)
printw(" TOXIC " TOXICVER " |");
attroff(COLOR_PAIR(BLUE) | A_BOLD);
int i;
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (!windows[i].active)
@ -414,7 +493,7 @@ static void draw_bar(void)
attron(A_BOLD);
draw_window_tab(windows[i]);
draw_window_tab(&windows[i]);
if (windows + i == active_window)
@ -432,9 +511,10 @@ static void draw_bar(void)
void draw_active_window(Tox *m)
{
ToxWindow *a = active_window;
a->alert0 = false;
a->alert1 = false;
a->alert2 = false;
pthread_mutex_lock(&Winthread.lock);
a->alert = WINDOW_ALERT_NONE;
pthread_mutex_unlock(&Winthread.lock);
wint_t ch = 0;
@ -466,7 +546,7 @@ void draw_active_window(Tox *m)
ltr = isprint(ch);
#endif /* HAVE_WIDECHAR */
if (!ltr && (ch == T_KEY_NEXT || ch == T_KEY_PREV)) {
if (!ltr && (ch == user_settings->key_next_tab || ch == user_settings->key_prev_tab)) {
set_next_window((int) ch);
} else {
pthread_mutex_lock(&Winthread.lock);
@ -475,36 +555,58 @@ void draw_active_window(Tox *m)
}
}
/* refresh inactive windows to prevent scrolling bugs.
/* refresh inactive windows to prevent scrolling bugs.
call at least once per second */
void refresh_inactive_windows(void)
{
int i;
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
ToxWindow *a = &windows[i];
if (a->active && a != active_window && !a->is_friendlist)
if (a->active && a != active_window && !a->is_friendlist) {
pthread_mutex_lock(&Winthread.lock);
line_info_print(a);
pthread_mutex_unlock(&Winthread.lock);
}
}
}
/* returns a pointer to the ToxWindow in the ith index. Returns NULL if no ToxWindow exists */
ToxWindow *get_window_ptr(int i)
{
ToxWindow *toxwin = NULL;
if (i >= 0 && i < MAX_WINDOWS_NUM && windows[i].active)
toxwin = &windows[i];
return toxwin;
}
void force_refresh(WINDOW *w)
{
wclear(w);
endwin();
refresh();
}
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)
void kill_all_windows(Tox *m)
{
kill_prompt_window(prompt);
int i;
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].is_chat)
kill_chat_window(&windows[i]);
kill_chat_window(&windows[i], m);
else if (windows[i].is_groupchat)
kill_groupchat_window(&windows[i]);
close_groupchat(&windows[i], m, windows[i].num);
}
kill_prompt_window(prompt);
kill_friendlist();
}

View File

@ -20,23 +20,24 @@
*
*/
#ifndef _windows_h
#define _windows_h
#ifndef WINDOWS_H
#define WINDOWS_H
#include <pthread.h>
#include <wctype.h>
#include <wchar.h>
#include <signal.h>
#include <tox/tox.h>
#ifdef _SUPPORT_AUDIO
#ifdef AUDIO
#include <tox/toxav.h>
#endif /* _SUPPORT_AUDIO */
#endif /* AUDIO */
#include "toxic.h"
#define MAX_WINDOWS_NUM 32
#define MAX_WINDOW_NAME_LENGTH 16
#define MAX_WINDOW_NAME_LENGTH 22
#define CURS_Y_OFFSET 1 /* y-axis cursor offset for chat contexts */
#define CHATBOX_HEIGHT 2
@ -52,22 +53,52 @@ enum {
BLACK,
} C_COLOURS;
/* tab alert types: lower types take priority */
enum {
WINDOW_ALERT_0,
WINDOW_ALERT_1,
WINDOW_ALERT_2,
/* tab alert types: lower types take priority (this relies on the order of C_COLOURS) */
typedef enum {
WINDOW_ALERT_NONE = 0,
WINDOW_ALERT_0 = GREEN,
WINDOW_ALERT_1 = RED,
WINDOW_ALERT_2 = MAGENTA,
} WINDOW_ALERTS;
/* Fixes text color problem on some terminals.
Uncomment if necessary */
/* #define URXVT_FIX */
struct _Winthread {
struct Winthread {
pthread_t tid;
pthread_mutex_t lock;
bool sig_exit_toxic;
bool flag_resize;
volatile sig_atomic_t sig_exit_toxic;
volatile sig_atomic_t flag_resize;
};
struct cqueue_thread {
pthread_t tid;
};
struct audio_thread {
pthread_t tid;
};
struct arg_opts {
bool use_ipv4;
bool force_tcp;
bool debug;
bool default_locale;
bool use_custom_data;
bool no_connect;
bool encrypt_data;
bool unencrypt_data;
char dns_path[MAX_STR_SIZE];
char config_path[MAX_STR_SIZE];
char nodes_path[MAX_STR_SIZE];
char proxy_address[256];
uint8_t proxy_type;
uint16_t proxy_port;
uint16_t tcp_port;
};
typedef struct ToxWindow ToxWindow;
@ -77,27 +108,32 @@ typedef struct ChatContext ChatContext;
typedef struct Help Help;
struct ToxWindow {
/* ncurses */
void(*onKey)(ToxWindow *, Tox *, wint_t, bool);
void(*onDraw)(ToxWindow *, Tox *);
void(*onInit)(ToxWindow *, Tox *);
void(*onFriendRequest)(ToxWindow *, Tox *, const char *, const char *, uint16_t);
void(*onFriendAdded)(ToxWindow *, Tox *, int32_t, bool);
void(*onConnectionChange)(ToxWindow *, Tox *, int32_t, uint8_t);
void(*onMessage)(ToxWindow *, Tox *, int32_t, const char *, uint16_t);
void(*onNickChange)(ToxWindow *, Tox *, int32_t, const char *, uint16_t);
void(*onStatusChange)(ToxWindow *, Tox *, int32_t, uint8_t);
void(*onStatusMessageChange)(ToxWindow *, int32_t, const char *, uint16_t);
void(*onAction)(ToxWindow *, Tox *, int32_t, const char *, uint16_t);
/* toxcore */
void(*onFriendRequest)(ToxWindow *, Tox *, const char *, const char *, size_t);
void(*onFriendAdded)(ToxWindow *, Tox *, uint32_t, bool);
void(*onConnectionChange)(ToxWindow *, Tox *, uint32_t, TOX_CONNECTION);
void(*onMessage)(ToxWindow *, Tox *, uint32_t, TOX_MESSAGE_TYPE, const char *, size_t);
void(*onNickChange)(ToxWindow *, Tox *, uint32_t, const char *, size_t);
void(*onStatusChange)(ToxWindow *, Tox *, uint32_t, TOX_USER_STATUS);
void(*onStatusMessageChange)(ToxWindow *, uint32_t, const char *, size_t);
void(*onGroupMessage)(ToxWindow *, Tox *, int, int, const char *, uint16_t);
void(*onGroupAction)(ToxWindow *, Tox *, int, int, const char *, uint16_t);
void(*onGroupInvite)(ToxWindow *, Tox *, int32_t, const char *);
void(*onGroupInvite)(ToxWindow *, Tox *, int32_t, uint8_t, const char *, uint16_t);
void(*onGroupNamelistChange)(ToxWindow *, Tox *, int, int, uint8_t);
void(*onFileSendRequest)(ToxWindow *, Tox *, int32_t, uint8_t, uint64_t, const char *, uint16_t);
void(*onFileControl)(ToxWindow *, Tox *, int32_t, uint8_t, uint8_t, uint8_t, const char *, uint16_t);
void(*onFileData)(ToxWindow *, Tox *, int32_t, uint8_t, const char *, uint16_t);
void(*onTypingChange)(ToxWindow *, Tox *, int32_t, uint8_t);
void(*onGroupTitleChange)(ToxWindow *, Tox *, int, int, const char *, uint8_t);
void(*onFileChunkRequest)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, size_t);
void(*onFileRecvChunk)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, const char *, size_t);
void(*onFileControl)(ToxWindow *, Tox *, uint32_t, uint32_t, TOX_FILE_CONTROL);
void(*onFileRecv)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, const char *, size_t);
void(*onTypingChange)(ToxWindow *, Tox *, uint32_t, bool);
void(*onReadReceipt)(ToxWindow *, Tox *, uint32_t, uint32_t);
#ifdef _SUPPORT_AUDIO
#ifdef AUDIO
void(*onInvite)(ToxWindow *, ToxAv *, int);
void(*onRinging)(ToxWindow *, ToxAv *, int);
@ -110,26 +146,29 @@ struct ToxWindow {
void(*onEnd)(ToxWindow *, ToxAv *, int);
void(*onRequestTimeout)(ToxWindow *, ToxAv *, int);
void(*onPeerTimeout)(ToxWindow *, ToxAv *, int);
void(*onWriteDevice)(ToxWindow *, Tox *, int, int, const int16_t *, unsigned int, uint8_t, unsigned int);
int call_idx; /* If in a call will have this index set, otherwise it's -1.
int call_idx; /* If in a call will have this index set, otherwise it's -1.
* Don't modify outside av callbacks. */
int device_selection[2]; /* -1 if not set, if set uses these selections instead of primary device */
#endif /* _SUPPORT_AUDIO */
int ringing_sound;
#endif /* AUDIO */
char name[TOXIC_MAX_NAME_LENGTH];
int32_t num; /* corresponds to friendnumber in chat windows */
int active_box; /* For box notify */
char name[TOXIC_MAX_NAME_LENGTH + 1];
uint32_t num; /* corresponds to friendnumber in chat windows */
bool active;
int x;
bool is_chat;
bool is_groupchat;
bool is_prompt;
bool is_friendlist;
bool is_groupchat;
int show_peerlist; /* used to toggle groupchat peerlist */
bool alert0;
bool alert1;
bool alert2;
WINDOW_ALERTS alert;
ChatContext *chatwin;
StatusBar *stb;
@ -141,19 +180,19 @@ struct ToxWindow {
/* statusbar info holder */
struct StatusBar {
WINDOW *topline;
char statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH];
uint16_t statusmsg_len;
char nick[TOXIC_MAX_NAME_LENGTH];
int nick_len;
uint8_t status;
bool is_online;
char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH + 1];
size_t statusmsg_len;
char nick[TOXIC_MAX_NAME_LENGTH + 1];
size_t nick_len;
TOX_USER_STATUS status;
TOX_CONNECTION connection;
};
#ifdef _SUPPORT_AUDIO
#ifdef AUDIO
#define INFOBOX_HEIGHT 7
#define INFOBOX_WIDTH 21
/* holds display info for audio calls */
struct infobox {
float vad_lvl;
@ -168,7 +207,7 @@ struct infobox {
WINDOW *win;
};
#endif /* _SUPPORT_AUDIO */
#endif /* AUDIO */
#define MAX_LINE_HIST 128
@ -188,8 +227,9 @@ struct ChatContext {
struct history *hst;
struct chatlog *log;
struct chat_queue *cqueue;
#ifdef _SUPPORT_AUDIO
#ifdef AUDIO
struct infobox infobox;
#endif
@ -212,11 +252,13 @@ int add_window(Tox *m, ToxWindow w);
void del_window(ToxWindow *w);
void set_active_window(int ch);
int get_num_active_windows(void);
void kill_all_windows(void); /* should only be called on shutdown */
void kill_all_windows(Tox *m); /* should only be called on shutdown */
void on_window_resize(void);
void force_refresh(WINDOW *w);
ToxWindow *get_window_ptr(int i);
/* refresh inactive windows to prevent scrolling bugs.
/* refresh inactive windows to prevent scrolling bugs.
call at least once per second */
void refresh_inactive_windows(void);
#endif /* #define _windows_h */
#endif /* #define WINDOWS_H */

379
src/xtra.c Normal file
View File

@ -0,0 +1,379 @@
/* xtra.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 "xtra.h"
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <pthread.h>
#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
const Atom XtraTerminate = 1;
const Atom XtraNil = 0;
static Atom XdndAware;
static Atom XdndEnter;
static Atom XdndLeave;
static Atom XdndPosition;
static Atom XdndStatus;
static Atom XdndDrop;
static Atom XdndSelection;
static Atom XdndDATA;
static Atom XdndTypeList;
static Atom XdndActionCopy;
static Atom XdndFinished;
struct _Xtra {
drop_callback on_drop;
Display *display;
Window terminal_window;
Window proxy_window;
Window source_window; /* When we have a drop */
Atom handling_version;
Atom expecting_type;
} Xtra;
typedef struct _Property
{
unsigned char *data;
int read_format;
unsigned long read_num;
Atom read_type;
} Property;
Property read_property(Window s, Atom p)
{
Atom read_type;
int read_format;
unsigned long read_num;
unsigned long left_bytes;
unsigned char *data = NULL;
int read_bytes = 1024;
/* Keep trying to read the property until there are no bytes unread */
do {
if (data) XFree(data);
XGetWindowProperty(Xtra.display, s,
p, 0,
read_bytes,
False, AnyPropertyType,
&read_type, &read_format,
&read_num, &left_bytes,
&data);
read_bytes *= 2;
} while (left_bytes != 0);
Property property = {data, read_format, read_num, read_type};
return property;
}
Atom get_dnd_type(long *a, int l)
{
int i = 0;
for (; i < l; i ++) {
if (a[i] != XtraNil) return a[i]; /* Get first valid */
}
return XtraNil;
}
/* TODO maybe support only certain types in the future */
static void handle_xdnd_enter(XClientMessageEvent* e)
{
Xtra.handling_version = (e->data.l[1] >> 24);
if ((e->data.l[1] & 1)) {
// Fetch the list of possible conversions
Property p = read_property(e->data.l[0], XdndTypeList);
Xtra.expecting_type = get_dnd_type((long*)p.data, p.read_num);
XFree(p.data);
} else {
// Use the available list
Xtra.expecting_type = get_dnd_type(e->data.l + 2, 3);
}
}
static void handle_xdnd_position(XClientMessageEvent* e)
{
XEvent ev = {
.xclient = {
.type = ClientMessage,
.display = e->display,
.window = e->data.l[0],
.message_type = XdndStatus,
.format = 32,
.data = {
.l = {
Xtra.proxy_window,
(Xtra.expecting_type != XtraNil),
0, 0,
XdndActionCopy
}
}
}
};
XSendEvent(Xtra.display, e->data.l[0], False, NoEventMask, &ev);
XFlush(Xtra.display);
}
static void handle_xdnd_drop(XClientMessageEvent* e)
{
/* Not expecting any type */
if (Xtra.expecting_type == XtraNil) {
XEvent ev = {
.xclient = {
.type = ClientMessage,
.display = e->display,
.window = e->data.l[0],
.message_type = XdndFinished,
.format = 32,
.data = {
.l = {Xtra.proxy_window, 0, 0}
}
}
};
XSendEvent(Xtra.display, e->data.l[0], False, NoEventMask, &ev);
} else {
Xtra.source_window = e->data.l[0];
XConvertSelection(Xtra.display,
XdndSelection,
Xtra.expecting_type,
XdndSelection,
Xtra.proxy_window,
Xtra.handling_version >= 1 ? e->data.l[2] : CurrentTime);
}
}
static void handle_xdnd_selection(XSelectionEvent* e)
{
/* DnD succesfully finished, send finished and call callback */
XEvent ev = {
.xclient = {
.type = ClientMessage,
.display = Xtra.display,
.window = Xtra.source_window,
.message_type = XdndFinished,
.format = 32,
.data = {
.l = {Xtra.proxy_window, 1, XdndActionCopy}
}
}
};
XSendEvent(Xtra.display, Xtra.source_window, False, NoEventMask, &ev);
Property p = read_property(Xtra.proxy_window, XdndSelection);
DropType dt;
if (strcmp(XGetAtomName(Xtra.display, p.read_type), "text/uri-list") == 0)
dt = DT_file_list;
else /* text/uri-list */
dt = DT_plain;
/* Call callback for every entry */
if (Xtra.on_drop && p.read_num)
{
char *sptr;
char *str = strtok_r((char *) p.data, "\n\r", &sptr);
if (str) Xtra.on_drop(str, dt);
while ((str = strtok_r(NULL, "\n\r", &sptr)))
Xtra.on_drop(str, dt);
}
if (p.data) XFree(p.data);
}
void *event_loop(void* p)
{
/* Handle events like a real nigga */
(void) p; /* DINDUNOTHIN */
XEvent event;
int pending;
while (Xtra.display)
{
/* NEEDMOEVENTSFODEMPROGRAMS */
XLockDisplay(Xtra.display);
if((pending = XPending(Xtra.display))) XNextEvent(Xtra.display, &event);
if (!pending)
{
XUnlockDisplay(Xtra.display);
usleep(10000);
continue;
}
if (event.type == ClientMessage)
{
Atom type = event.xclient.message_type;
if (type == XdndEnter) handle_xdnd_enter(&event.xclient);
else if (type == XdndPosition) handle_xdnd_position(&event.xclient);
else if (type == XdndDrop) handle_xdnd_drop(&event.xclient);
else if (type == XtraTerminate) break;
}
else if (event.type == SelectionNotify) handle_xdnd_selection(&event.xselection);
/* AINNOBODYCANHANDLEDEMEVENTS*/
else XSendEvent(Xtra.display, Xtra.terminal_window, 0, 0, &event);
XUnlockDisplay(Xtra.display);
}
/* Actual XTRA termination
* Please call xtra_terminate() at exit
* otherwise HEWUSAGUDBOI happens
*/
if (Xtra.display) XCloseDisplay(Xtra.display);
return (Xtra.display = NULL);
}
int init_xtra(drop_callback d)
{
memset(&Xtra, 0, sizeof(Xtra));
if (!d) return -1;
else Xtra.on_drop = d;
XInitThreads();
if ( !(Xtra.display = XOpenDisplay(NULL))) return -1;
Xtra.terminal_window = focused_window_id();
{
/* Create an invisible window which will act as proxy for the DnD operation. */
XSetWindowAttributes attr = {0};
attr.event_mask = EnterWindowMask |
LeaveWindowMask |
ButtonMotionMask |
ButtonPressMask |
ButtonReleaseMask |
ResizeRedirectMask;
attr.do_not_propagate_mask = NoEventMask;
Window root;
int x, y;
unsigned int wht, hht, b, d;
/* Since we cannot capture resize events for parent window we will have to create
* this window to have maximum size as defined in root window
*/
XGetGeometry(Xtra.display,
XDefaultRootWindow(Xtra.display),
&root, &x, &y, &wht, &hht, &b, &d);
if (! (Xtra.proxy_window = XCreateWindow
(Xtra.display, Xtra.terminal_window, /* Parent */
0, 0, /* Position */
wht, hht, /* Width + height */
0, /* Border width */
CopyFromParent, /* Depth */
InputOnly, /* Class */
CopyFromParent, /* Visual */
CWEventMask | CWCursor, /* Value mask */
&attr)) ) /* Attributes for value mask */
return -1;
}
XMapWindow(Xtra.display, Xtra.proxy_window); /* Show window (sandwich) */
XLowerWindow(Xtra.display, Xtra.proxy_window); /* Don't interfere with parent lmao */
XdndAware = XInternAtom(Xtra.display, "XdndAware", False);
XdndEnter = XInternAtom(Xtra.display, "XdndEnter", False);
XdndLeave = XInternAtom(Xtra.display, "XdndLeave", False);
XdndPosition = XInternAtom(Xtra.display, "XdndPosition", False);
XdndStatus = XInternAtom(Xtra.display, "XdndStatus", False);
XdndDrop = XInternAtom(Xtra.display, "XdndDrop", False);
XdndSelection = XInternAtom(Xtra.display, "XdndSelection", False);
XdndDATA = XInternAtom(Xtra.display, "XdndDATA", False);
XdndTypeList = XInternAtom(Xtra.display, "XdndTypeList", False);
XdndActionCopy = XInternAtom(Xtra.display, "XdndActionCopy", False);
XdndFinished = XInternAtom(Xtra.display, "XdndFinished", False);
/* Inform my nigga windows that we are aware of dnd */
Atom XdndVersion = 3;
XChangeProperty(Xtra.display,
Xtra.proxy_window,
XdndAware,
XA_ATOM,
32,
PropModeReplace,
(unsigned char*)&XdndVersion, 1);
pthread_t id;
if (pthread_create(&id, NULL, event_loop, NULL) != 0)
return -1;
pthread_detach(id);
return 0;
}
void terminate_xtra()
{
if (!Xtra.display) return;
XEvent terminate = {
.xclient = {
.type = ClientMessage,
.display = Xtra.display,
.message_type = XtraTerminate,
}
};
XLockDisplay(Xtra.display);
XDeleteProperty(Xtra.display, Xtra.proxy_window, XdndAware);
XSendEvent(Xtra.display, Xtra.proxy_window, 0, NoEventMask, &terminate);
XUnlockDisplay(Xtra.display);
while (Xtra.display); /* Wait for termination */
}
long unsigned int focused_window_id()
{
if (!Xtra.display) return 0;
Window focus;
int revert;
XLockDisplay(Xtra.display);
XGetInputFocus(Xtra.display, &focus, &revert);
XUnlockDisplay(Xtra.display);
return focus;
}
int is_focused()
{
return Xtra.proxy_window == focused_window_id() || Xtra.terminal_window == focused_window_id();
}

41
src/xtra.h Normal file
View File

@ -0,0 +1,41 @@
/* xtra.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 XTRA_H
#define XTRA_H
/* NOTE: If no xlib present don't compile */
typedef enum {
DT_plain,
DT_file_list
}
DropType;
typedef void (*drop_callback) (const char*, DropType);
int init_xtra(drop_callback d);
void terminate_xtra();
long unsigned int focused_window_id();
int is_focused(); /* returns bool */
#endif /* XTRA_H */