mirror of
https://github.com/Tha14/toxic.git
synced 2025-06-26 19:36:45 +02:00
Compare commits
836 Commits
Author | SHA1 | Date | |
---|---|---|---|
bf09b3b6c4 | |||
2d3c5c9450 | |||
ae87b2eb2d | |||
c34ecc8dfd | |||
2c2938c647 | |||
22dd883f28 | |||
dfbb1338a5 | |||
f8dc82516e | |||
59b16f7760 | |||
a11289de79 | |||
a64b8cae89 | |||
470eaeb97b | |||
6503349ed3 | |||
ec23cedc3a | |||
ce1ae1d7dc | |||
6669d5632f | |||
59e1114997 | |||
c20510e5aa | |||
2b2e746549 | |||
dc5a8d44e5 | |||
090d3339a0 | |||
442d9e22b4 | |||
0c39e7b158 | |||
3e3f2614b5 | |||
53e4b2c971 | |||
d6c57c7aa3 | |||
3efc984ae9 | |||
7618a0871a | |||
b87f73cc47 | |||
785d39d78f | |||
38a0f6fae4 | |||
710be51cad | |||
50438425e5 | |||
4b4bfe8876 | |||
67da4bdd5b | |||
e230badb47 | |||
d48d9ed4dc | |||
b9fe6bfa10 | |||
5cbbb62ce2 | |||
3369c9d808 | |||
dfc594f949 | |||
d714cda145 | |||
428e1e4100 | |||
2b19f56e63 | |||
4badc983ea | |||
c8a4f88ff3 | |||
eeeaf8d707 | |||
37b3b5a5ed | |||
a561ef8c49 | |||
89f200e870 | |||
2823115a6c | |||
5599d73760 | |||
bac66cb5ad | |||
61c50972e3 | |||
ff0fbbc379 | |||
4a124dea43 | |||
e722b665d1 | |||
4365b8d5ad | |||
e55e5f3f7e | |||
d1036c8538 | |||
9240f62829 | |||
1b89bc9051 | |||
7700ef83e0 | |||
9922b3a4f3 | |||
d561d2e1ac | |||
7865cea284 | |||
424189cad9 | |||
82aa64e8f7 | |||
a0418520a2 | |||
f451d961bc | |||
a4da9fd49e | |||
0a0891fa98 | |||
9464b369a4 | |||
47692edee4 | |||
73c94b25ba | |||
ab2ea5936d | |||
90985af007 | |||
5aad8764b1 | |||
5a175f374a | |||
4acfe84171 | |||
e995a1cb69 | |||
867f041c23 | |||
7e49ba92b2 | |||
2fa53c0531 | |||
673800c947 | |||
835b821b75 | |||
78587ad20b | |||
09bbec79cf | |||
751b5f9943 | |||
4a337ae3cb | |||
1baeb15073 | |||
141b36af04 | |||
4238c20e72 | |||
4e177d60b0 | |||
1bd880708b | |||
76c21c8b34 | |||
dd8c2caac7 | |||
7a7e8a7f8d | |||
43f45d67a4 | |||
adb36a39bb | |||
5a0d9f5e9f | |||
a4e8cb3971 | |||
e22b7a336c | |||
317fa42b20 | |||
884f8dda37 | |||
f5090532fb | |||
ae04dd8fa5 | |||
00e4075937 | |||
d65f3ea58d | |||
008163bf6c | |||
f09002a49a | |||
431290d47a | |||
b66874b7b3 | |||
1b9fd7f936 | |||
0a1457acde | |||
d2008aa92f | |||
e83356faef | |||
fd4161832b | |||
0a132b0eb5 | |||
9c7ac9043a | |||
b86c0d357f | |||
75dd26518f | |||
2cc261c619 | |||
f7d46f1100 | |||
be264528d2 | |||
c825adc1e3 | |||
4e9f125e95 | |||
d26c8fe447 | |||
e42d635195 | |||
a32cd1c19b | |||
14dc02ac83 | |||
e4a28d1839 | |||
0908920b51 | |||
8a882916fb | |||
55ada8ad2f | |||
2094186c0f | |||
d3a489b756 | |||
e6b18231c0 | |||
cc0145d561 | |||
9f0daca5eb | |||
1f2bd44dce | |||
6492bd12f9 | |||
43552161f9 | |||
e5d45fdf1d | |||
bd69c8da18 | |||
8c3e3ebe24 | |||
7eebbd982d | |||
5c66f5c161 | |||
40f70fc1e3 | |||
9cf4427375 | |||
23cf9686cb | |||
70add920fe | |||
c56c6cc219 | |||
e9a0296851 | |||
de30433e26 | |||
fa64fa8b93 | |||
50c7942cb5 | |||
e7697b8fba | |||
b46a77f977 | |||
38909afa89 | |||
5af561c811 | |||
ae984d4f04 | |||
97a8ecd115 | |||
64d782569a | |||
6248baf98b | |||
33cfca7ecc | |||
a01cc35368 | |||
b10eebd77e | |||
05661ca9b6 | |||
784883f773 | |||
a9e5723ca4 | |||
95dfea8d29 | |||
e574af7d68 | |||
2918ca45a2 | |||
6ad744f770 | |||
5b394e6f35 | |||
940af2c711 | |||
544c402f78 | |||
cee9e624b8 | |||
2e65ee3609 | |||
3a176e1cab | |||
00cccad22c | |||
a432d733d7 | |||
893e88294b | |||
b071a9e992 | |||
48cf4ebf02 | |||
773a75b948 | |||
133c0e8d63 | |||
bf54cb36ef | |||
032853b5c9 | |||
650c13ca7a | |||
9d5fe03285 | |||
c3f68b60d3 | |||
1c16467eb9 | |||
3c74385f5c | |||
c07c0028bb | |||
dc3b2e04ab | |||
7fd3aa9164 | |||
4c60312e2d | |||
16e29aa4e0 | |||
bd7b073155 | |||
f25cf870e6 | |||
13291d0365 | |||
9ee7a48910 | |||
daf4614ba6 | |||
919d36369c | |||
618704df76 | |||
9a70dd9651 | |||
e2d310b10f | |||
a1015a366a | |||
913ec7b3fe | |||
4a52b06954 | |||
89f9c07b9e | |||
388d78d11e | |||
a2a23b3932 | |||
f405ae8b42 | |||
cf3f6750eb | |||
4de22d067a | |||
51e274ea38 | |||
0a6ce62363 | |||
13c5de5531 | |||
21f8e7f398 | |||
bcf4a5af90 | |||
09f90d095b | |||
416ebc9ab8 | |||
3ca22aa714 | |||
8dd25e1f0b | |||
5b9bd603ea | |||
3c2c1f15ce | |||
6876df4a45 | |||
1ff97161fb | |||
667410e879 | |||
a862874740 | |||
79bde4e5bf | |||
833b724e9f | |||
96b68058bb | |||
e823233149 | |||
3ac22fafe4 | |||
71f2ac170c | |||
0ef888eea3 | |||
a9b0028a15 | |||
b18e6cff5a | |||
424a1c94d9 | |||
009095af24 | |||
2ce42ab057 | |||
934459dea8 | |||
52bc874675 | |||
511907fbc5 | |||
155e194174 | |||
b1c7e21ca9 | |||
7edcf6cb45 | |||
9581940cfa | |||
f2aa57c4fa | |||
8bf4405fd0 | |||
21ef1788ca | |||
68f1dffba7 | |||
092df2c0e4 | |||
691f94c75c | |||
d6d4476e85 | |||
924e8e0860 | |||
53193e933f | |||
328587ad9c | |||
1a8fdb1b99 | |||
690f0221b5 | |||
e117bd3985 | |||
15cc87bffd | |||
97d4c97c52 | |||
c8b22d7e8a | |||
f48ec4f49b | |||
d4ce697bd9 | |||
bd20513493 | |||
fd3f4eb724 | |||
75e8486061 | |||
bd216709fc | |||
a3a8f7608a | |||
affc88d0a8 | |||
eca4882ce2 | |||
58b0a04019 | |||
b870679f2c | |||
ae83725cb6 | |||
595e42b587 | |||
f5401df2c7 | |||
3e79a5ca8b | |||
327081945e | |||
68ec484a58 | |||
1d6ccf56a8 | |||
67f637a1e1 | |||
5e175d5319 | |||
778db0fece | |||
72010dd2e1 | |||
39556b36f3 | |||
dc9ffa6e56 | |||
1e92bb3c2b | |||
edbdf2966a | |||
3f6fd734d3 | |||
919a06d282 | |||
35cc815cdb | |||
a318bdb034 | |||
d6aaa95b25 | |||
5718ad52db | |||
0f4cffbacc | |||
d9a861331f | |||
2f12a8d429 | |||
e75cf4f3ad | |||
bb85f31bb2 | |||
396d08f0d2 | |||
6ab184e7ce | |||
fd65fbfd0c | |||
637ea0ed55 | |||
dc2d20f4c4 | |||
d712ccc17e | |||
147030e06f | |||
09fd5cb69f | |||
64db9f73a2 | |||
28633be2dd | |||
6fdafceda8 | |||
38ed0c86ad | |||
ad23816096 | |||
bc4a730e76 | |||
79372cc80d | |||
c9e4246ac5 | |||
dcd6a238b6 | |||
c49de7733c | |||
3fc7c90630 | |||
53663a7832 | |||
c14f2a3fcd | |||
3cbe61e111 | |||
fa023c6a99 | |||
f98e6bdcb4 | |||
0884954c84 | |||
43727c6730 | |||
618b731d5a | |||
46975bf38b | |||
8f3989000d | |||
9fe75fbc47 | |||
c455e79604 | |||
a223545853 | |||
b243f7aa62 | |||
899452d7cd | |||
af68fa7ee0 | |||
5da789cc37 | |||
7e5b41c8e0 | |||
0254596c73 | |||
67c02404b7 | |||
0b5ee7e2c7 | |||
fba0732faa | |||
d06086a656 | |||
e74b678739 | |||
b62787ce47 | |||
75708f7600 | |||
476dec46b6 | |||
973f6206ee | |||
cbe47b3660 | |||
1c58c339bb | |||
15e91cfa99 | |||
f4fb6ea4fc | |||
0d2ff2c0a8 | |||
5275da5a6b | |||
e891b1281b | |||
ca7110b37c | |||
8960eb98f4 | |||
18610668b8 | |||
efe61e32e2 | |||
7a7e4f573a | |||
a7e6ab7758 | |||
a0cde4ae8c | |||
7566aa9d26 | |||
d2332a5b77 | |||
94a8ce5aa8 | |||
b18a67d656 | |||
02708534c0 | |||
d5710d80e0 | |||
8dcba3219d | |||
9f01a45b1f | |||
dd2cb93ecc | |||
49538a986c | |||
26c2331d0f | |||
a0758643c2 | |||
77e152ad36 | |||
4834642b80 | |||
18a6f621f0 | |||
3cae1d92cd | |||
02b192d6ee | |||
f630a3e604 | |||
fb5a9bc043 | |||
26ad5a00a3 | |||
18e1f08e31 | |||
b68deef6db | |||
30ec7debba | |||
3a1e23a3ff | |||
0887bb7662 | |||
5a55f738a9 | |||
8d8df585ad | |||
8660047ec1 | |||
9476db02a9 | |||
04fbf5f724 | |||
3cc629cbc1 | |||
50fca4cddf | |||
a047cead05 | |||
1131b73299 | |||
196af10d01 | |||
1e0e93e5c6 | |||
ac01d6d316 | |||
654e404e0e | |||
f0f1138c54 | |||
bc94e08970 | |||
42c3ede963 | |||
174568d769 | |||
4587d8ebbd | |||
98aae242fb | |||
7abd8d5ee5 | |||
98ac4d7983 | |||
933d46553f | |||
7e667a8028 | |||
d4e41d6053 | |||
f30dccf726 | |||
ea3fcd5b79 | |||
e61d070def | |||
b5f34f42a8 | |||
4426eaddd9 | |||
83f0720a39 | |||
ce4f293574 | |||
b23ae5a4c3 | |||
190e1e73e8 | |||
ee67cf0bf1 | |||
aaeb47dc14 | |||
e19b0ed710 | |||
a774121c13 | |||
df676423a7 | |||
cf8dda6b0d | |||
1ce731471d | |||
f98c77432b | |||
9fa5a3fdb6 | |||
5b9fd70f30 | |||
442f68cd31 | |||
e74212cb9e | |||
57b52f35b4 | |||
27a31a8399 | |||
f1a3ed379e | |||
60f9be7234 | |||
dcfb90bc63 | |||
74b84c4252 | |||
00e6546f0c | |||
a009fbf20c | |||
2ed9448b41 | |||
1575a40d61 | |||
a784fdf9d5 | |||
612c0e1131 | |||
16a82e1897 | |||
ad14baf601 | |||
8b6a5813e6 | |||
f4c76e12f4 | |||
3fa8c4be0b | |||
2f904371ae | |||
455eba3bfd | |||
48f6a0cd5e | |||
50a15d2289 | |||
de1e61bd5a | |||
7fba5a59bf | |||
3a86ee923e | |||
65e726a51a | |||
91c4414889 | |||
6754741f37 | |||
10d0e99d72 | |||
9696acc8bd | |||
0f37e50419 | |||
95d09e4b75 | |||
7c71c35797 | |||
893cfaa543 | |||
3e22c9b829 | |||
a968ca2a2e | |||
c271622670 | |||
a126f9c1a6 | |||
44d524134f | |||
7ae807002e | |||
a194f7ad87 | |||
61d3f7e63e | |||
8715e9c41e | |||
5da69e7f56 | |||
ebc5cd9285 | |||
97536d2a72 | |||
a2e6a25fc8 | |||
f8998b5891 | |||
8d4f7fc32b | |||
828b7fb505 | |||
74525bcd14 | |||
33e98fd720 | |||
134e5873a9 | |||
89a95eca16 | |||
d881312e3e | |||
da65ba4e8d | |||
e8a39e1722 | |||
581261afca | |||
8d68b5cc01 | |||
353be3a7a2 | |||
b9af1b3293 | |||
09badaa9ee | |||
587f6518f7 | |||
6c38e72654 | |||
599c2119d5 | |||
dfd89f2b5c | |||
7db3dcbdf6 | |||
5b268a1a6a | |||
43c1140aa2 | |||
0bc9725b98 | |||
ef097757f3 | |||
6490fa598c | |||
c41464c990 | |||
94d7e3199e | |||
140dd5e5d3 | |||
251a81ef43 | |||
76f81c4d33 | |||
b14a1bb7b9 | |||
5f6f021039 | |||
9b69cecfb1 | |||
f75248d177 | |||
97f8d6c074 | |||
c29d5d1ca2 | |||
48c272acf8 | |||
f66b9137e8 | |||
c3dfaa5935 | |||
9a9ae03e41 | |||
2bbc47d3f7 | |||
f12b0ee472 | |||
46e4ddfaf1 | |||
449b6fa5ff | |||
3913adedb0 | |||
4cf545d334 | |||
9225af06b1 | |||
562483823c | |||
57742bcd87 | |||
5066ea637b | |||
7b8cf65218 | |||
7ac7713268 | |||
490c80dae9 | |||
f324d2d34b | |||
f3ee120c48 | |||
63ea6154f4 | |||
eafa660dee | |||
2a6a5b13d7 | |||
47b9648f85 | |||
55c05a4092 | |||
476b056ed0 | |||
c53600b550 | |||
f47991e18e | |||
773a3f4abf | |||
809f472cb4 | |||
e8ee3d694a | |||
fec501801e | |||
c52fe21237 | |||
f00c218e56 | |||
1daa4c5ca6 | |||
717f8986cd | |||
65aba6e77d | |||
9f8a6a8b6b | |||
b0bfb13241 | |||
34102f72a2 | |||
cb93c6ec65 | |||
78af10fa1f | |||
52b7719180 | |||
48361a003e | |||
34bd4a1c7c | |||
94b271da5d | |||
e47f2c05f3 | |||
6b9ef7e6c9 | |||
62239a1fda | |||
bba81ac884 | |||
9f4248b1e1 | |||
e06f0ffb7e | |||
29b283c176 | |||
3e797db16e | |||
034a8f5d8b | |||
eb7d910683 | |||
85af9f55ba | |||
ca98b49813 | |||
0cff881d69 | |||
7eb82a0fe5 | |||
58a131426a | |||
72e9e7d9c4 | |||
fc148be3e2 | |||
bc51714148 | |||
b24325d879 | |||
b480e22a27 | |||
afa4bc86e8 | |||
6cd2411ec0 | |||
6f9ab56493 | |||
485191d185 | |||
1456cef991 | |||
53cb4b0248 | |||
2c4f0d593d | |||
499c66f411 | |||
1b5da956e5 | |||
c76b541cb8 | |||
ce2d371c4b | |||
eefd981572 | |||
9240295724 | |||
a516724760 | |||
a0ef4d752f | |||
65ad64bf42 | |||
b36680d767 | |||
24a85df15f | |||
f10ce94f38 | |||
612c6c95eb | |||
31a2e648c5 | |||
31acdcada3 | |||
12e33a1760 | |||
328e7f8d57 | |||
33000598fc | |||
6a2ef5cc6c | |||
2a63e62aba | |||
935d8f8770 | |||
8c5ac1f77d | |||
bfa266c604 | |||
901ffbc7c0 | |||
97dedd32fb | |||
789c491c1e | |||
4b8dd3ad72 | |||
c2dce960b8 | |||
f9e15cd60b | |||
4d249c5fe3 | |||
7206a9ea73 | |||
848b4e9a4c | |||
d65d0a08aa | |||
7ccf4b6432 | |||
d18ba78d03 | |||
ab1c97fb2b | |||
b0a66706bd | |||
f0962bd060 | |||
083ca2f3b7 | |||
8481b50f97 | |||
a04f7ee661 | |||
10d47d1ac6 | |||
445f5aa1fd | |||
1b49a89c8e | |||
9bf92d1e48 | |||
da308b2253 | |||
0c834b60f5 | |||
2cf5430b85 | |||
30d2a5514e | |||
1d6a6efb81 | |||
af09961875 | |||
a46fe25283 | |||
dfe3f1c4c2 | |||
b969079af5 | |||
2837c5697f | |||
820b619847 | |||
6b72ef0720 | |||
81125be971 | |||
decc585065 | |||
17ad66346e | |||
e0330c984f | |||
3a9056745f | |||
9b997fbf01 | |||
4cac797b40 | |||
7f5dc0a756 | |||
93d77fdeca | |||
5b3acf3f6b | |||
a29136d6b7 | |||
4a8db6f098 | |||
766ae685c2 | |||
e384f87a04 | |||
688564cfc2 | |||
4643996c3a | |||
eef5b4941f | |||
4b83df3652 | |||
f54cd87abc | |||
be8a0de38d | |||
9fbf7bd1c1 | |||
cd0bccfbeb | |||
c1cb367acf | |||
de3a28c6e6 | |||
5976d33fef | |||
e17b62c98b | |||
92948abcf1 | |||
852c3e89d6 | |||
8ce1a3d3e9 | |||
cb9622136c | |||
46b57feb2f | |||
a9bcab4aee | |||
0fdb01ff97 | |||
b9290c8a83 | |||
246a514e88 | |||
20f126e1d8 | |||
82027a5b4f | |||
3b2010200d | |||
24cd6d772f | |||
c46676daa5 | |||
411ae8d0f5 | |||
e419299487 | |||
f3a8ba6ab3 | |||
6d98f38128 | |||
08f57de9e0 | |||
3b7e161149 | |||
15815bf4bb | |||
43a5ee2d4f | |||
bd817e77f0 | |||
f8a4312fdd | |||
cce7892d94 | |||
1420618eb0 | |||
52d6e8431f | |||
1b89af9063 | |||
d873181306 | |||
26640cda14 | |||
21c48bde5c | |||
8c071fb208 | |||
b36a8fd8fa | |||
94e936575e | |||
1b3c40b539 | |||
bd5453002e | |||
c218e104e7 | |||
f6db888808 | |||
3a804fefd1 | |||
e0deda27da | |||
bb97f3e543 | |||
92c0f737ac | |||
29b549e677 | |||
3baa830afb | |||
a5ce17f44e | |||
2f981ecb12 | |||
5e941427d3 | |||
a40b6b1b1b | |||
e5b6e0ad9f | |||
5956c6acaf | |||
58f33fa1d6 | |||
7384440a3d | |||
c1dfb741c9 | |||
25b5545644 | |||
d49e911fe4 | |||
50a37495f8 | |||
8bea44a44c | |||
3ad82cf3b1 | |||
99e36195f7 | |||
d03a661635 | |||
c5c12385ef | |||
705a55d1b5 | |||
77db725822 | |||
e6c68143bd | |||
b5cbd8e410 | |||
8024757e57 | |||
a5a7361370 | |||
7f70345dae | |||
33a4e806e2 | |||
1a9cd4cd2c | |||
c055af7348 | |||
65eb185a9f | |||
a68fc671e5 | |||
79fbf0a31f | |||
d29836845c | |||
c2d417c78b | |||
d8d198c81c | |||
bc8b1f170e | |||
514cf8e460 | |||
ac82961bea | |||
9d52b6ab5d | |||
aeb70262e0 | |||
f6a85518bc | |||
27c5013697 | |||
aa53076e11 | |||
c37311ae36 | |||
ce76896eb3 | |||
5e377639c8 | |||
93fb9611f7 | |||
cc3513968e | |||
dd697d7af1 | |||
a32d76ed16 | |||
e6d307f65a | |||
b210068c1d | |||
0151b9b49f | |||
ab0da36cb7 | |||
ed3e9b476d | |||
7c63bd80d6 | |||
9f06331a0b | |||
a63cba645f | |||
9d50d52216 | |||
6cb36e71fe | |||
0b52de3773 | |||
22ac65c4a9 | |||
3b90c3495f | |||
f4e4fbbef1 | |||
6aee8c136b | |||
2a0740821c | |||
e6f285adc7 | |||
a80da2b58f | |||
da924f07a9 | |||
e8cd1417b7 | |||
d08feb2cc5 | |||
fe0641e981 | |||
1fd07837ea | |||
6c2ae4ad24 | |||
c678d41709 | |||
63745afe09 | |||
416419a6e7 | |||
33e16fe870 | |||
d712d6c898 | |||
2ae478d546 | |||
4b8de0d16d | |||
2fcfa954ab | |||
675c8fa89f | |||
d1153f96ca | |||
2f473300cd | |||
92d5b2fefc | |||
70f8b103de | |||
41292c1ded | |||
90393f1dba | |||
87bd0f9b34 | |||
13e67f4ce3 | |||
6c9dbfe3bc | |||
24b763bce6 | |||
e41008bd4e | |||
7f38c3c6e7 | |||
7109b8fa18 | |||
1e503b1080 | |||
4fb82cceaa | |||
46b046a209 | |||
6ee1f1ed0f | |||
044b731089 | |||
d83ef1d8be | |||
459eb64dc4 | |||
af5627b050 | |||
9b57c05648 | |||
817f763589 | |||
8e4db369bc | |||
a61f5f6a6d | |||
5ff7065744 | |||
831d8e5f24 | |||
6896292c49 | |||
b6613a015f | |||
2d9f4facf7 | |||
e7920d1da7 | |||
eb09fceed7 | |||
b308e19e6b | |||
5867f1a672 | |||
5187861b69 | |||
6be1c907d9 | |||
fd86f01fd0 | |||
e775c51a06 |
13
.gitignore
vendored
13
.gitignore
vendored
@ -9,19 +9,10 @@
|
||||
*.app
|
||||
*.swp
|
||||
*.la
|
||||
m4/*
|
||||
!m4/pkg.m4
|
||||
configure
|
||||
configure_aux
|
||||
Makefile.in
|
||||
aclocal.m4
|
||||
config.h*
|
||||
config.log
|
||||
config.status
|
||||
stamp-h1
|
||||
autom4te.cache
|
||||
.deps
|
||||
.libs
|
||||
*.orig
|
||||
build/toxic
|
||||
Makefile
|
||||
build/*.o
|
||||
build/*.d
|
||||
|
45
.travis.yml
45
.travis.yml
@ -4,27 +4,44 @@ compiler:
|
||||
- clang
|
||||
|
||||
before_script:
|
||||
# installing libsodium, needed for Core
|
||||
- git clone git://github.com/jedisct1/libsodium.git
|
||||
# Installing yasm (needed for compiling vpx) and openal
|
||||
- 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
|
||||
- ./autogen.sh
|
||||
- ./configure && make -j3
|
||||
- sudo make install
|
||||
- git checkout tags/1.0.2 > /dev/null
|
||||
- ./autogen.sh > /dev/null
|
||||
- ./configure > /dev/null
|
||||
- make check -j2 || make check || exit 1 > /dev/null
|
||||
- sudo make install > /dev/null
|
||||
- cd ..
|
||||
# creating librarys' links and updating cache
|
||||
- sudo ldconfig
|
||||
- git clone https://github.com/irungentoo/ProjectTox-Core.git toxcore
|
||||
# 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
|
||||
- ./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
|
||||
- cd libvpx
|
||||
- ./configure --enable-shared > /dev/null
|
||||
- make -j2 || make || exit 1 > /dev/null
|
||||
- sudo make install > /dev/null
|
||||
- cd ..
|
||||
# Creating libraries links and updating cache
|
||||
- sudo ldconfig > /dev/null
|
||||
# Installing toxcore
|
||||
- git clone https://github.com/irungentoo/toxcore.git toxcore
|
||||
- cd toxcore
|
||||
- autoreconf -i
|
||||
- ./configure --disable-tests --disable-ntox --disable-dht-bootstrap-daemon
|
||||
- make -j2
|
||||
- ./configure --disable-tests --disable-ntox --disable-daemon --enable-av
|
||||
- make -j2 || make || exit 1
|
||||
- sudo make install
|
||||
- cd ..
|
||||
script:
|
||||
- autoreconf -i
|
||||
- ./configure
|
||||
- make -j2
|
||||
- make -j2 || make || exit 1
|
||||
notifications:
|
||||
email: false
|
||||
|
||||
|
1
COPYING
1
COPYING
@ -672,3 +672,4 @@ may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
|
||||
|
365
INSTALL
365
INSTALL
@ -1,365 +0,0 @@
|
||||
Installation Instructions
|
||||
*************************
|
||||
|
||||
Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
|
||||
2006, 2007, 2008, 2009 Free Software Foundation, Inc.
|
||||
|
||||
Copying and distribution of this file, with or without modification,
|
||||
are permitted in any medium without royalty provided the copyright
|
||||
notice and this notice are preserved. This file is offered as-is,
|
||||
without warranty of any kind.
|
||||
|
||||
Basic Installation
|
||||
==================
|
||||
|
||||
Briefly, the shell commands `./configure; make; make install' should
|
||||
configure, build, and install this package. The following
|
||||
more-detailed instructions are generic; see the `README' file for
|
||||
instructions specific to this package. Some packages provide this
|
||||
`INSTALL' file but do not implement all of the features documented
|
||||
below. The lack of an optional feature in a given package is not
|
||||
necessarily a bug. More recommendations for GNU packages can be found
|
||||
in *note Makefile Conventions: (standards)Makefile Conventions.
|
||||
|
||||
The `configure' shell script attempts to guess correct values for
|
||||
various system-dependent variables used during compilation. It uses
|
||||
those values to create a `Makefile' in each directory of the package.
|
||||
It may also create one or more `.h' files containing system-dependent
|
||||
definitions. Finally, it creates a shell script `config.status' that
|
||||
you can run in the future to recreate the current configuration, and a
|
||||
file `config.log' containing compiler output (useful mainly for
|
||||
debugging `configure').
|
||||
|
||||
It can also use an optional file (typically called `config.cache'
|
||||
and enabled with `--cache-file=config.cache' or simply `-C') that saves
|
||||
the results of its tests to speed up reconfiguring. Caching is
|
||||
disabled by default to prevent problems with accidental use of stale
|
||||
cache files.
|
||||
|
||||
If you need to do unusual things to compile the package, please try
|
||||
to figure out how `configure' could check whether to do them, and mail
|
||||
diffs or instructions to the address given in the `README' so they can
|
||||
be considered for the next release. If you are using the cache, and at
|
||||
some point `config.cache' contains results you don't want to keep, you
|
||||
may remove or edit it.
|
||||
|
||||
The file `configure.ac' (or `configure.in') is used to create
|
||||
`configure' by a program called `autoconf'. You need `configure.ac' if
|
||||
you want to change it or regenerate `configure' using a newer version
|
||||
of `autoconf'.
|
||||
|
||||
The simplest way to compile this package is:
|
||||
|
||||
1. `cd' to the directory containing the package's source code and type
|
||||
`./configure' to configure the package for your system.
|
||||
|
||||
Running `configure' might take a while. While running, it prints
|
||||
some messages telling which features it is checking for.
|
||||
|
||||
2. Type `make' to compile the package.
|
||||
|
||||
3. Optionally, type `make check' to run any self-tests that come with
|
||||
the package, generally using the just-built uninstalled binaries.
|
||||
|
||||
4. Type `make install' to install the programs and any data files and
|
||||
documentation. When installing into a prefix owned by root, it is
|
||||
recommended that the package be configured and built as a regular
|
||||
user, and only the `make install' phase executed with root
|
||||
privileges.
|
||||
|
||||
5. Optionally, type `make installcheck' to repeat any self-tests, but
|
||||
this time using the binaries in their final installed location.
|
||||
This target does not install anything. Running this target as a
|
||||
regular user, particularly if the prior `make install' required
|
||||
root privileges, verifies that the installation completed
|
||||
correctly.
|
||||
|
||||
6. You can remove the program binaries and object files from the
|
||||
source code directory by typing `make clean'. To also remove the
|
||||
files that `configure' created (so you can compile the package for
|
||||
a different kind of computer), type `make distclean'. There is
|
||||
also a `make maintainer-clean' target, but that is intended mainly
|
||||
for the package's developers. If you use it, you may have to get
|
||||
all sorts of other programs in order to regenerate files that came
|
||||
with the distribution.
|
||||
|
||||
7. Often, you can also type `make uninstall' to remove the installed
|
||||
files again. In practice, not all packages have tested that
|
||||
uninstallation works correctly, even though it is required by the
|
||||
GNU Coding Standards.
|
||||
|
||||
8. Some packages, particularly those that use Automake, provide `make
|
||||
distcheck', which can by used by developers to test that all other
|
||||
targets like `make install' and `make uninstall' work correctly.
|
||||
This target is generally not run by end users.
|
||||
|
||||
Compilers and Options
|
||||
=====================
|
||||
|
||||
Some systems require unusual options for compilation or linking that
|
||||
the `configure' script does not know about. Run `./configure --help'
|
||||
for details on some of the pertinent environment variables.
|
||||
|
||||
You can give `configure' initial values for configuration parameters
|
||||
by setting variables in the command line or in the environment. Here
|
||||
is an example:
|
||||
|
||||
./configure CC=c99 CFLAGS=-g LIBS=-lposix
|
||||
|
||||
*Note Defining Variables::, for more details.
|
||||
|
||||
Compiling For Multiple Architectures
|
||||
====================================
|
||||
|
||||
You can compile the package for more than one kind of computer at the
|
||||
same time, by placing the object files for each architecture in their
|
||||
own directory. To do this, you can use GNU `make'. `cd' to the
|
||||
directory where you want the object files and executables to go and run
|
||||
the `configure' script. `configure' automatically checks for the
|
||||
source code in the directory that `configure' is in and in `..'. This
|
||||
is known as a "VPATH" build.
|
||||
|
||||
With a non-GNU `make', it is safer to compile the package for one
|
||||
architecture at a time in the source code directory. After you have
|
||||
installed the package for one architecture, use `make distclean' before
|
||||
reconfiguring for another architecture.
|
||||
|
||||
On MacOS X 10.5 and later systems, you can create libraries and
|
||||
executables that work on multiple system types--known as "fat" or
|
||||
"universal" binaries--by specifying multiple `-arch' options to the
|
||||
compiler but only a single `-arch' option to the preprocessor. Like
|
||||
this:
|
||||
|
||||
./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
|
||||
CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
|
||||
CPP="gcc -E" CXXCPP="g++ -E"
|
||||
|
||||
This is not guaranteed to produce working output in all cases, you
|
||||
may have to build one architecture at a time and combine the results
|
||||
using the `lipo' tool if you have problems.
|
||||
|
||||
Installation Names
|
||||
==================
|
||||
|
||||
By default, `make install' installs the package's commands under
|
||||
`/usr/local/bin', include files under `/usr/local/include', etc. You
|
||||
can specify an installation prefix other than `/usr/local' by giving
|
||||
`configure' the option `--prefix=PREFIX', where PREFIX must be an
|
||||
absolute file name.
|
||||
|
||||
You can specify separate installation prefixes for
|
||||
architecture-specific files and architecture-independent files. If you
|
||||
pass the option `--exec-prefix=PREFIX' to `configure', the package uses
|
||||
PREFIX as the prefix for installing programs and libraries.
|
||||
Documentation and other data files still use the regular prefix.
|
||||
|
||||
In addition, if you use an unusual directory layout you can give
|
||||
options like `--bindir=DIR' to specify different values for particular
|
||||
kinds of files. Run `configure --help' for a list of the directories
|
||||
you can set and what kinds of files go in them. In general, the
|
||||
default for these options is expressed in terms of `${prefix}', so that
|
||||
specifying just `--prefix' will affect all of the other directory
|
||||
specifications that were not explicitly provided.
|
||||
|
||||
The most portable way to affect installation locations is to pass the
|
||||
correct locations to `configure'; however, many packages provide one or
|
||||
both of the following shortcuts of passing variable assignments to the
|
||||
`make install' command line to change installation locations without
|
||||
having to reconfigure or recompile.
|
||||
|
||||
The first method involves providing an override variable for each
|
||||
affected directory. For example, `make install
|
||||
prefix=/alternate/directory' will choose an alternate location for all
|
||||
directory configuration variables that were expressed in terms of
|
||||
`${prefix}'. Any directories that were specified during `configure',
|
||||
but not in terms of `${prefix}', must each be overridden at install
|
||||
time for the entire installation to be relocated. The approach of
|
||||
makefile variable overrides for each directory variable is required by
|
||||
the GNU Coding Standards, and ideally causes no recompilation.
|
||||
However, some platforms have known limitations with the semantics of
|
||||
shared libraries that end up requiring recompilation when using this
|
||||
method, particularly noticeable in packages that use GNU Libtool.
|
||||
|
||||
The second method involves providing the `DESTDIR' variable. For
|
||||
example, `make install DESTDIR=/alternate/directory' will prepend
|
||||
`/alternate/directory' before all installation names. The approach of
|
||||
`DESTDIR' overrides is not required by the GNU Coding Standards, and
|
||||
does not work on platforms that have drive letters. On the other hand,
|
||||
it does better at avoiding recompilation issues, and works well even
|
||||
when some directory options were not specified in terms of `${prefix}'
|
||||
at `configure' time.
|
||||
|
||||
Optional Features
|
||||
=================
|
||||
|
||||
If the package supports it, you can cause programs to be installed
|
||||
with an extra prefix or suffix on their names by giving `configure' the
|
||||
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
|
||||
|
||||
Some packages pay attention to `--enable-FEATURE' options to
|
||||
`configure', where FEATURE indicates an optional part of the package.
|
||||
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
|
||||
is something like `gnu-as' or `x' (for the X Window System). The
|
||||
`README' should mention any `--enable-' and `--with-' options that the
|
||||
package recognizes.
|
||||
|
||||
For packages that use the X Window System, `configure' can usually
|
||||
find the X include and library files automatically, but if it doesn't,
|
||||
you can use the `configure' options `--x-includes=DIR' and
|
||||
`--x-libraries=DIR' to specify their locations.
|
||||
|
||||
Some packages offer the ability to configure how verbose the
|
||||
execution of `make' will be. For these packages, running `./configure
|
||||
--enable-silent-rules' sets the default to minimal output, which can be
|
||||
overridden with `make V=1'; while running `./configure
|
||||
--disable-silent-rules' sets the default to verbose, which can be
|
||||
overridden with `make V=0'.
|
||||
|
||||
Particular systems
|
||||
==================
|
||||
|
||||
On HP-UX, the default C compiler is not ANSI C compatible. If GNU
|
||||
CC is not installed, it is recommended to use the following options in
|
||||
order to use an ANSI C compiler:
|
||||
|
||||
./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
|
||||
|
||||
and if that doesn't work, install pre-built binaries of GCC for HP-UX.
|
||||
|
||||
On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
|
||||
parse its `<wchar.h>' header file. The option `-nodtk' can be used as
|
||||
a workaround. If GNU CC is not installed, it is therefore recommended
|
||||
to try
|
||||
|
||||
./configure CC="cc"
|
||||
|
||||
and if that doesn't work, try
|
||||
|
||||
./configure CC="cc -nodtk"
|
||||
|
||||
On Solaris, don't put `/usr/ucb' early in your `PATH'. This
|
||||
directory contains several dysfunctional programs; working variants of
|
||||
these programs are available in `/usr/bin'. So, if you need `/usr/ucb'
|
||||
in your `PATH', put it _after_ `/usr/bin'.
|
||||
|
||||
On Haiku, software installed for all users goes in `/boot/common',
|
||||
not `/usr/local'. It is recommended to use the following options:
|
||||
|
||||
./configure --prefix=/boot/common
|
||||
|
||||
Specifying the System Type
|
||||
==========================
|
||||
|
||||
There may be some features `configure' cannot figure out
|
||||
automatically, but needs to determine by the type of machine the package
|
||||
will run on. Usually, assuming the package is built to be run on the
|
||||
_same_ architectures, `configure' can figure that out, but if it prints
|
||||
a message saying it cannot guess the machine type, give it the
|
||||
`--build=TYPE' option. TYPE can either be a short name for the system
|
||||
type, such as `sun4', or a canonical name which has the form:
|
||||
|
||||
CPU-COMPANY-SYSTEM
|
||||
|
||||
where SYSTEM can have one of these forms:
|
||||
|
||||
OS
|
||||
KERNEL-OS
|
||||
|
||||
See the file `config.sub' for the possible values of each field. If
|
||||
`config.sub' isn't included in this package, then this package doesn't
|
||||
need to know the machine type.
|
||||
|
||||
If you are _building_ compiler tools for cross-compiling, you should
|
||||
use the option `--target=TYPE' to select the type of system they will
|
||||
produce code for.
|
||||
|
||||
If you want to _use_ a cross compiler, that generates code for a
|
||||
platform different from the build platform, you should specify the
|
||||
"host" platform (i.e., that on which the generated programs will
|
||||
eventually be run) with `--host=TYPE'.
|
||||
|
||||
Sharing Defaults
|
||||
================
|
||||
|
||||
If you want to set default values for `configure' scripts to share,
|
||||
you can create a site shell script called `config.site' that gives
|
||||
default values for variables like `CC', `cache_file', and `prefix'.
|
||||
`configure' looks for `PREFIX/share/config.site' if it exists, then
|
||||
`PREFIX/etc/config.site' if it exists. Or, you can set the
|
||||
`CONFIG_SITE' environment variable to the location of the site script.
|
||||
A warning: not all `configure' scripts look for a site script.
|
||||
|
||||
Defining Variables
|
||||
==================
|
||||
|
||||
Variables not defined in a site shell script can be set in the
|
||||
environment passed to `configure'. However, some packages may run
|
||||
configure again during the build, and the customized values of these
|
||||
variables may be lost. In order to avoid this problem, you should set
|
||||
them in the `configure' command line, using `VAR=value'. For example:
|
||||
|
||||
./configure CC=/usr/local2/bin/gcc
|
||||
|
||||
causes the specified `gcc' to be used as the C compiler (unless it is
|
||||
overridden in the site shell script).
|
||||
|
||||
Unfortunately, this technique does not work for `CONFIG_SHELL' due to
|
||||
an Autoconf bug. Until the bug is fixed you can use this workaround:
|
||||
|
||||
CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
|
||||
|
||||
`configure' Invocation
|
||||
======================
|
||||
|
||||
`configure' recognizes the following options to control how it
|
||||
operates.
|
||||
|
||||
`--help'
|
||||
`-h'
|
||||
Print a summary of all of the options to `configure', and exit.
|
||||
|
||||
`--help=short'
|
||||
`--help=recursive'
|
||||
Print a summary of the options unique to this package's
|
||||
`configure', and exit. The `short' variant lists options used
|
||||
only in the top level, while the `recursive' variant lists options
|
||||
also present in any nested packages.
|
||||
|
||||
`--version'
|
||||
`-V'
|
||||
Print the version of Autoconf used to generate the `configure'
|
||||
script, and exit.
|
||||
|
||||
`--cache-file=FILE'
|
||||
Enable the cache: use and save the results of the tests in FILE,
|
||||
traditionally `config.cache'. FILE defaults to `/dev/null' to
|
||||
disable caching.
|
||||
|
||||
`--config-cache'
|
||||
`-C'
|
||||
Alias for `--cache-file=config.cache'.
|
||||
|
||||
`--quiet'
|
||||
`--silent'
|
||||
`-q'
|
||||
Do not print messages saying which checks are being made. To
|
||||
suppress all normal output, redirect it to `/dev/null' (any error
|
||||
messages will still be shown).
|
||||
|
||||
`--srcdir=DIR'
|
||||
Look for the package's source code in directory DIR. Usually
|
||||
`configure' can determine that directory automatically.
|
||||
|
||||
`--prefix=DIR'
|
||||
Use DIR as the installation prefix. *note Installation Names::
|
||||
for more details, including other options available for fine-tuning
|
||||
the installation locations.
|
||||
|
||||
`--no-create'
|
||||
`-n'
|
||||
Run the configure checks, but stop before creating any output
|
||||
files.
|
||||
|
||||
`configure' also accepts some other, not widely useful, options. Run
|
||||
`configure --help' for more details.
|
||||
|
68
INSTALL.md
Normal file
68
INSTALL.md
Normal 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
77
Makefile
Normal 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
|
||||
|
||||
# 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
|
@ -1,3 +0,0 @@
|
||||
SUBDIRS = build misc
|
||||
|
||||
ACLOCAL_AMFLAGS = -I m4
|
40
README.md
40
README.md
@ -1,20 +1,38 @@
|
||||
## Toxic - console client for [Tox](http://tox.im)
|
||||
# Toxic [](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.
|
||||
|
||||
The client formerly resided in the [Tox core repository](https://github.com/irungentoo/ProjectTox-Core) and is now available as a standalone program. It looks like [this](http://wiki.tox.im/images/b/b6/Ncursesclient1.png).
|
||||
[](https://i.imgur.com/san99Z2.png)
|
||||
|
||||
To compile, first generate the configure script by running the ```autoreconf -i``` command.
|
||||
## Installation
|
||||
[Use our repositories](https://wiki.tox.im/Binaries#Linux)<br />
|
||||
[Compile it yourself](/INSTALL.md)
|
||||
|
||||
Then execute the configure script with ./configure (you may need to pass it the location of your dependency libraries, i.e.):
|
||||
## Downloads
|
||||
If you don't like installation methods listed above, you can still download precompiled binaries from [jenkins](https://jenkins.libtoxcore.so):
|
||||
* [Linux 32 bit](https://jenkins.libtoxcore.so/job/toxic_linux_i386/lastSuccessfulBuild/artifact/toxic_linux_i386.tar.xz) [](https://jenkins.libtoxcore.so/job/toxic_linux_i386/lastSuccessfulBuild/artifact/toxic_linux_i386.tar.xz)
|
||||
* [Linux 64 bit](https://jenkins.libtoxcore.so/job/toxic_linux_amd64/lastSuccessfulBuild/artifact/toxic_linux_amd64.tar.xz) [](https://jenkins.libtoxcore.so/job/toxic_linux_amd64/lastSuccessfulBuild/artifact/toxic_linux_amd64.tar.xz)
|
||||
* [~~Linux ARMv6~~](https://jenkins.libtoxcore.so/job/toxic_linux_armv6/lastSuccessfulBuild/artifact/toxic_linux_armv6.tar.xz) **CURRENTLY DISABLED** [](https://jenkins.libtoxcore.so/job/toxic_linux_armv6/lastSuccessfulBuild/artifact/toxic_linux_armv6.tar.xz)
|
||||
|
||||
#### DEBs packages
|
||||
* [toxic-i386.deb](https://jenkins.libtoxcore.so/job/toxic-linux-pkg/lastSuccessfulBuild/artifact/toxic-i386.deb)
|
||||
* [toxic-x86_64.deb](https://jenkins.libtoxcore.so/job/toxic-linux-pkg/lastSuccessfulBuild/artifact/toxic-x86_64.deb)
|
||||
|
||||
#### RPMs packages
|
||||
* [toxic-i386.rpm](https://jenkins.libtoxcore.so/job/toxic-linux-pkg/lastSuccessfulBuild/artifact/toxic-i386.rpm)
|
||||
* [toxic-x86_64.rpm](https://jenkins.libtoxcore.so/job/toxic-linux-pkg/lastSuccessfulBuild/artifact/toxic-x86_64.rpm)
|
||||
|
||||
## 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).
|
||||
|
||||
## Troubleshooting
|
||||
If your default prefix is "/usr/local" and you receive the following:
|
||||
```
|
||||
./configure --prefix=/where/to/install --with-libtoxcore-headers=/path/to/ProjectTox-Core/core --with-libtoxcore-libs=/path/to/ProjectTox-Core/build/core --with-libsodium-headers=/path/to/libsodium/include/ --with-libsodium-libs=/path/to/sodiumtest/lib/
|
||||
|
||||
error while loading shared libraries: libtoxcore.so.0: cannot open shared object file: No such file or directory
|
||||
```
|
||||
*Note:* If your default prefix is /usr/local and you happen to get an error that says "error while loading shared libraries: libtoxcore.so.0: cannot open shared object file: No such file or directory", then you can try running ```sudo ldconfig```. If that doesn't fix it, run:
|
||||
you can attempt to correct it by running `sudo ldconfig`. If that doesn't work, run:
|
||||
```
|
||||
echo '/usr/local/lib/' | sudo tee -a /etc/ld.so.conf.d/locallib.conf
|
||||
sudo ldconfig
|
||||
```
|
||||
If you dont already have them, you might want to install the ncurses libraries, on Debian:
|
||||
```
|
||||
sudo apt-get install libncurses5-dev libncursesw5-dev
|
||||
```
|
||||
|
||||
|
@ -1,43 +0,0 @@
|
||||
#Don't change this unless needed, else you'll break stuff
|
||||
|
||||
bin_PROGRAMS = toxic
|
||||
|
||||
|
||||
toxic_SOURCES = $(top_srcdir)/src/main.c \
|
||||
$(top_srcdir)/src/chat.h \
|
||||
$(top_srcdir)/src/chat.c \
|
||||
$(top_srcdir)/src/configdir.h \
|
||||
$(top_srcdir)/src/configdir.c \
|
||||
$(top_srcdir)/src/prompt.h \
|
||||
$(top_srcdir)/src/prompt.c \
|
||||
$(top_srcdir)/src/friendlist.h \
|
||||
$(top_srcdir)/src/friendlist.c \
|
||||
$(top_srcdir)/src/toxic_windows.h \
|
||||
$(top_srcdir)/src/windows.c \
|
||||
$(top_srcdir)/src/groupchat.c \
|
||||
$(top_srcdir)/src/groupchat.h \
|
||||
$(top_srcdir)/src/global_commands.c \
|
||||
$(top_srcdir)/src/global_commands.h \
|
||||
$(top_srcdir)/src/chat_commands.c \
|
||||
$(top_srcdir)/src/chat_commands.h \
|
||||
$(top_srcdir)/src/execute.c \
|
||||
$(top_srcdir)/src/execute.h \
|
||||
$(top_srcdir)/src/misc_tools.c \
|
||||
$(top_srcdir)/src/misc_tools.h \
|
||||
$(top_srcdir)/src/toxic_strings.c \
|
||||
$(top_srcdir)/src/toxic_strings.h
|
||||
|
||||
toxic_CFLAGS = -I$(top_srcdir) \
|
||||
$(NCURSES_CFLAGS) \
|
||||
$(LIBSODIUM_CFLAGS) \
|
||||
$(LIBTOXCORE_CFLAGS)
|
||||
|
||||
toxic_CPPFLAGS = '-DTOXICVER="$(TOXIC_VERSION)"'
|
||||
|
||||
toxic_LDADD = $(LIBTOXCORE_LDFLAGS) \
|
||||
$(LIBSODIUM_LDFLAGS) \
|
||||
$(NCURSES_LIBS) \
|
||||
$(LIBTOXCORE_LIBS) \
|
||||
$(LIBSODIUM_LIBS) \
|
||||
$(WINSOCK2_LIBS)
|
||||
|
21
cfg/checks/av.mk
Normal file
21
cfg/checks/av.mk
Normal 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
|
38
cfg/checks/check_features.mk
Normal file
38
cfg/checks/check_features.mk
Normal 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
|
15
cfg/checks/desktop_notifications.mk
Normal file
15
cfg/checks/desktop_notifications.mk
Normal 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
|
21
cfg/checks/sound_notifications.mk
Normal file
21
cfg/checks/sound_notifications.mk
Normal 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
17
cfg/checks/x11.mk
Normal 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
|
30
cfg/global_vars.mk
Normal file
30
cfg/global_vars.mk
Normal file
@ -0,0 +1,30 @@
|
||||
# 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
|
4
cfg/platforms/.gitignore
vendored
Normal file
4
cfg/platforms/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
# Ignore everything in this directory
|
||||
*
|
||||
# Except this file
|
||||
!.gitignore
|
10
cfg/systems/Darwin.mk
Normal file
10
cfg/systems/Darwin.mk
Normal 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
|
4
cfg/systems/FreeBSD.mk
Normal file
4
cfg/systems/FreeBSD.mk
Normal file
@ -0,0 +1,4 @@
|
||||
# Specials options for freebsd systems
|
||||
LIBS := $(filter-out ncursesw, $(LIBS))
|
||||
LDFLAGS += -lncursesw
|
||||
MANDIR = $(PREFIX)/man
|
4
cfg/systems/Linux.mk
Normal file
4
cfg/systems/Linux.mk
Normal 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
10
cfg/targets/doc.mk
Normal 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
|
21
cfg/targets/help.mk
Normal file
21
cfg/targets/help.mk
Normal file
@ -0,0 +1,21 @@
|
||||
# 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 " 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
|
38
cfg/targets/install.mk
Normal file
38
cfg/targets/install.mk
Normal file
@ -0,0 +1,38 @@
|
||||
# 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 -i'' -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$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 -i'' -e 's:__VERSION__:'$(VERSION)':g' $$file ;\
|
||||
sed -i'' -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$file ;\
|
||||
gzip -f -9 $$file ;\
|
||||
done
|
||||
|
||||
.PHONY: install
|
406
configure.ac
406
configure.ac
@ -1,406 +0,0 @@
|
||||
# -*- Autoconf -*-
|
||||
# Process this file with autoconf to produce a configure script.
|
||||
|
||||
AC_PREREQ([2.65])
|
||||
AC_INIT([toxic], [0.2.6], [https://tox.im/])
|
||||
AC_CONFIG_AUX_DIR(configure_aux)
|
||||
AC_CONFIG_SRCDIR([src/main.c])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AM_INIT_AUTOMAKE([1.10 -Wall])
|
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
|
||||
if test "x${prefix}" = "xNONE"; then
|
||||
prefix="${ac_default_prefix}"
|
||||
fi
|
||||
|
||||
DEPSEARCH=
|
||||
LIBTOXCORE_SEARCH_HEADERS=
|
||||
LIBTOXCORE_SEARCH_LIBS=
|
||||
LIBSODIUM_SEARCH_HEADERS=
|
||||
LIBSODIUM_SEARCH_LIBS=
|
||||
|
||||
LIBTOXCORE_FOUND="no"
|
||||
NCURSES_FOUND="no"
|
||||
NCURSES_WIDECHAR_SUPPORT="no"
|
||||
|
||||
AC_ARG_WITH(dependency-search,
|
||||
AC_HELP_STRING([--with-dependency-search=DIR],
|
||||
[search for dependencies in DIR, i.e. look for libraries in
|
||||
DIR/lib and for headers in DIR/include]),
|
||||
[
|
||||
DEPSEARCH="$withval"
|
||||
]
|
||||
)
|
||||
|
||||
if test -n "$DEPSEARCH"; then
|
||||
CFLAGS="$CFLAGS -I$DEPSEARCH/include"
|
||||
CPPFLAGS="$CPPFLAGS -I$DEPSEARCH/include"
|
||||
LDFLAGS="$LDFLAGS -L$DEPSEARCH/lib"
|
||||
export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$DEPSEARCH/lib/pkgconfig
|
||||
fi
|
||||
|
||||
AC_ARG_WITH(libtoxcore-headers,
|
||||
AC_HELP_STRING([--with-libtoxcore-headers=DIR],
|
||||
[search for libtoxcore header files in DIR/tox]),
|
||||
[
|
||||
LIBTOXCORE_SEARCH_HEADERS="$withval"
|
||||
AC_MSG_NOTICE([Will search for libtoxcore header files in $withval])
|
||||
]
|
||||
)
|
||||
|
||||
AC_ARG_WITH(libtoxcore-libs,
|
||||
AC_HELP_STRING([--with-libtoxcore-libs=DIR],
|
||||
[search for libtoxcore libraries in DIR]),
|
||||
[
|
||||
LIBTOXCORE_SEARCH_LIBS="$withval"
|
||||
AC_MSG_NOTICE([Will search for libtoxcore libraries in $withval])
|
||||
]
|
||||
)
|
||||
|
||||
AC_ARG_WITH(libsodium-headers,
|
||||
AC_HELP_STRING([--with-libsodium-headers=DIR],
|
||||
[search for libsodium header files in DIR]),
|
||||
[
|
||||
LIBSODIUM_SEARCH_HEADERS="$withval"
|
||||
AC_MSG_NOTICE([Will search for libsodium header files in $withval])
|
||||
]
|
||||
)
|
||||
|
||||
AC_ARG_WITH(libsodium-libs,
|
||||
AC_HELP_STRING([--with-libsodium-libs=DIR],
|
||||
[search for libsodium libraries in DIR]),
|
||||
[
|
||||
LIBSODIUM_SEARCH_LIBS="$withval"
|
||||
AC_MSG_NOTICE([Will search for libsodium libraries in $withval])
|
||||
]
|
||||
)
|
||||
|
||||
WIN32=no
|
||||
AC_CANONICAL_HOST
|
||||
case $host_os in
|
||||
*mingw*)
|
||||
WIN32="yes"
|
||||
;;
|
||||
*freebsd*)
|
||||
LDFLAGS="$LDFLAGS -L/usr/local/lib"
|
||||
CFLAGS="$CFLAGS -I/usr/local/include"
|
||||
CPPFLAGS="$CPPFLAGS -I/usr/local/include"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Checks for programs.
|
||||
AC_PROG_CC
|
||||
AM_PROG_CC_C_O
|
||||
|
||||
AC_CHECK_HEADERS(
|
||||
[limits.h locale.h stdint.h stdlib.h string.h unistd.h wchar.h wctype.h],
|
||||
[],
|
||||
[ AC_MSG_ERROR([required header is missing on your system]) ])
|
||||
|
||||
# Checks for typedefs, structures, and compiler characteristics.
|
||||
AC_HEADER_STDBOOL
|
||||
AC_TYPE_SIZE_T
|
||||
AC_TYPE_UINT16_T
|
||||
AC_TYPE_UINT32_T
|
||||
AC_TYPE_UINT64_T
|
||||
AC_TYPE_UINT8_T
|
||||
|
||||
# Checks for library functions.
|
||||
AC_FUNC_MALLOC
|
||||
AC_CHECK_FUNCS(
|
||||
[iswprint memmove memset mkdir setlocale strchr strdup],
|
||||
[],
|
||||
[ AC_MSG_ERROR([required library function is missing on your system])])
|
||||
|
||||
# pkg-config based tests
|
||||
PKG_PROG_PKG_CONFIG
|
||||
|
||||
if test -n "$PKG_CONFIG"; then
|
||||
if test "x$WIN32" != "xyes"; then
|
||||
PKG_CHECK_MODULES([NCURSES], [ncursesw],
|
||||
[
|
||||
NCURSES_FOUND="yes"
|
||||
NCURSES_WIDECHAR_SUPPORT="yes"
|
||||
],
|
||||
[
|
||||
NCURSES_WIDECHAR_SUPPORT="no"
|
||||
PKG_CHECK_MODULES([NCURSES], [ncurses],
|
||||
[
|
||||
NCURSES_FOUND="yes"
|
||||
],
|
||||
[
|
||||
AC_MSG_WARN([$NCURSES_PKG_ERRORS])
|
||||
])
|
||||
])
|
||||
fi
|
||||
else
|
||||
AC_MSG_WARN([pkg-config was not found on your sytem])
|
||||
fi
|
||||
|
||||
if (test "x$NCURSES_FOUND" = "xno") && (test "x$WIN32" != "xyes"); then
|
||||
AC_PATH_PROG([CURSES_CONFIG], [ncursesw5-config], [no])
|
||||
if test "x$CURSES_CONFIG" != "xno"; then
|
||||
AC_MSG_CHECKING(ncurses cflags)
|
||||
NCURSES_CFLAGS=`${CURSES_CONFIG} --cflags`
|
||||
AC_MSG_RESULT($NCURSES_CFLAGS)
|
||||
|
||||
AC_MSG_CHECKING(ncurses libraries)
|
||||
NCURSES_LIBS=`${CURSES_CONFIG} --libs`
|
||||
AC_MSG_RESULT($NCURSES_LIBS)
|
||||
|
||||
AC_SUBST(NCURSES_CFLAGS)
|
||||
AC_SUBST(NCURSES_LIBS)
|
||||
NCURSES_FOUND="yes"
|
||||
NCURSES_WIDECHAR_SUPPORT="yes"
|
||||
fi
|
||||
fi
|
||||
|
||||
if (test "x$NCURSES_FOUND" = "xno") && (test "x$WIN32" != "xyes"); then
|
||||
unset ac_cv_path_CURSES_CONFIG
|
||||
AC_PATH_PROG([CURSES_CONFIG], [ncursesw5.4-config], [no])
|
||||
if test "x$CURSES_CONFIG" != "xno"; then
|
||||
AC_MSG_CHECKING(ncurses cflags)
|
||||
NCURSES_CFLAGS=`${CURSES_CONFIG} --cflags`
|
||||
AC_MSG_RESULT($NCURSES_CFLAGS)
|
||||
|
||||
AC_MSG_CHECKING(ncurses libraries)
|
||||
NCURSES_LIBS=`${CURSES_CONFIG} --libs`
|
||||
AC_MSG_RESULT($NCURSES_LIBS)
|
||||
|
||||
AC_SUBST(NCURSES_CFLAGS)
|
||||
AC_SUBST(NCURSES_LIBS)
|
||||
NCURSES_FOUND="yes"
|
||||
NCURSES_WIDECHAR_SUPPORT="yes"
|
||||
fi
|
||||
fi
|
||||
|
||||
if (test "x$NCURSES_FOUND" = "xno") && (test "x$WIN32" != "xyes"); then
|
||||
unset ac_cv_path_CURSES_CONFIG
|
||||
AC_PATH_PROG([CURSES_CONFIG], [ncurses5-config], [no])
|
||||
if test "x$CURSES_CONFIG" != "xno"; then
|
||||
AC_MSG_CHECKING(ncurses cflags)
|
||||
NCURSES_CFLAGS=`${CURSES_CONFIG} --cflags`
|
||||
AC_MSG_RESULT($NCURSES_CFLAGS)
|
||||
|
||||
AC_MSG_CHECKING(ncurses libraries)
|
||||
NCURSES_LIBS=`${CURSES_CONFIG} --libs`
|
||||
AC_MSG_RESULT($NCURSES_LIBS)
|
||||
|
||||
AC_SUBST(NCURSES_CFLAGS)
|
||||
AC_SUBST(NCURSES_LIBS)
|
||||
NCURSES_FOUND="yes"
|
||||
fi
|
||||
fi
|
||||
|
||||
if (test "x$NCURSES_FOUND" = "xno") && (test "x$WIN32" != "xyes"); then
|
||||
unset ac_cv_path_CURSES_CONFIG
|
||||
AC_PATH_PROG([CURSES_CONFIG], [ncurses5.4-config], [no])
|
||||
if test "x$CURSES_CONFIG" != "xno"; then
|
||||
AC_MSG_CHECKING(ncurses cflags)
|
||||
NCURSES_CFLAGS=`${CURSES_CONFIG} --cflags`
|
||||
AC_MSG_RESULT($NCURSES_CFLAGS)
|
||||
|
||||
AC_MSG_CHECKING(ncurses libraries)
|
||||
NCURSES_LIBS=`${CURSES_CONFIG} --libs`
|
||||
AC_MSG_RESULT($NCURSES_LIBS)
|
||||
|
||||
AC_SUBST(NCURSES_CFLAGS)
|
||||
AC_SUBST(NCURSES_LIBS)
|
||||
NCURSES_FOUND="yes"
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "x$NCURSES_FOUND" = "xno"; then
|
||||
AC_CHECK_HEADER([curses.h],
|
||||
[],
|
||||
[
|
||||
AC_MSG_ERROR([headers for the ncurses library were not found on your system])
|
||||
]
|
||||
)
|
||||
|
||||
if test "x$WIN32" = "xyes"; then
|
||||
dnl Check if pdcurses provides wide char support
|
||||
NCURSES_WIDECHAR_SUPPORT="no"
|
||||
AC_CHECK_LIB([pdcurses], [clear],
|
||||
[],
|
||||
[
|
||||
AC_MSG_ERROR([required library pdcurses was not found on your system])
|
||||
]
|
||||
)
|
||||
|
||||
AC_CHECK_LIB(ws2_32, main,
|
||||
[
|
||||
WINSOCK2_LIBS="-lws2_32"
|
||||
AC_SUBST(WINSOCK2_LIBS)
|
||||
],
|
||||
[
|
||||
AC_MSG_ERROR([required library winsock2 was not found on the system, please check your MinGW installation])
|
||||
]
|
||||
)
|
||||
AC_DEFINE([_WIN32_WINNT], [0x501],
|
||||
[enable getaddrinfo/freeaddrinfo on XP and higher])
|
||||
else
|
||||
AC_CHECK_LIB([ncursesw], [wget_wch],
|
||||
[
|
||||
NCURSES_WIDECHAR_SUPPORT="yes"
|
||||
],
|
||||
[
|
||||
unset ac_cv_lib_ncursesw_wget_wch
|
||||
AC_CHECK_LIB([ncursesw], [wget_wch],
|
||||
[
|
||||
NCURSES_WIDECHAR_SUPPORT="yes"
|
||||
],
|
||||
[
|
||||
NCURSES_WIDECHAR_SUPPORT="no"
|
||||
AC_CHECK_LIB([ncurses], [clear],
|
||||
[],
|
||||
[
|
||||
unset ac_cv_lib_ncurses_clear
|
||||
AC_CHECK_LIB([ncurses], [clear],
|
||||
[],
|
||||
[
|
||||
AC_MSG_ERROR([required library ncurses was not found on your system])
|
||||
],
|
||||
[
|
||||
-ltinfo
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
],
|
||||
[
|
||||
-ltinfo
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
fi
|
||||
fi
|
||||
|
||||
if test -n "$PKG_CONFIG"; then
|
||||
PKG_CHECK_MODULES(LIBTOXCORE, [libtoxcore],
|
||||
[
|
||||
LIBTOXCORE_FOUND="yes"
|
||||
],
|
||||
[
|
||||
AC_MSG_WARN([required library libsodium was not found in requested location $LIBSODIUM_SEARCH_LIBS])
|
||||
])
|
||||
fi
|
||||
|
||||
if test "x$LIBTOXCORE_FOUND" = "xno"; then
|
||||
LIBSODIUM_LIBS=
|
||||
LIBSODIUM_LDFLAGS=
|
||||
LDFLAGS_SAVE="$LDFLAGS"
|
||||
if test -n "$LIBSODIUM_SEARCH_LIBS"; then
|
||||
LDFLAGS="$LDFLAGS -L$LIBSODIUM_SEARCH_LIBS"
|
||||
AC_CHECK_LIB(sodium, randombytes_random,
|
||||
[
|
||||
LIBSODIUM_LDFLAGS="-L$LIBSODIUM_SEARCH_LIBS"
|
||||
LIBSODIUM_LIBS="-lsodium"
|
||||
],
|
||||
[
|
||||
AC_MSG_ERROR([required library libsodium was not found in requested location $LIBSODIUM_SEARCH_LIBS])
|
||||
]
|
||||
)
|
||||
else
|
||||
AC_CHECK_LIB(sodium, randombytes_random,
|
||||
[],
|
||||
[
|
||||
AC_MSG_ERROR([required library libsodium was not found on your system, please check http://download.libsodium.org/libsodium/releases/])
|
||||
]
|
||||
)
|
||||
fi
|
||||
|
||||
LDFLAGS="$LDFLAGS_SAVE"
|
||||
AC_SUBST(LIBSODIUM_LIBS)
|
||||
AC_SUBST(LIBSODIUM_LDFLAGS)
|
||||
|
||||
|
||||
|
||||
LIBTOXCORE_CFLAGS=
|
||||
CFLAGS_SAVE="$CFLAGS"
|
||||
CPPFLAGS_SAVE="$CPPFLAGS"
|
||||
|
||||
if test -n "$LIBTOXCORE_SEARCH_HEADERS"; then
|
||||
CFLAGS="$CFLAGS -I$LIBTOXCORE_SEARCH_HEADERS"
|
||||
CPPFLAGS="$CPPFLAGS -I$LIBTOXCORE_SEARCH_HEADERS"
|
||||
AC_CHECK_HEADER([tox/tox.h],
|
||||
[
|
||||
LIBTOXCORE_CFLAGS="-I$LIBTOXCORE_SEARCH_HEADERS"
|
||||
],
|
||||
[
|
||||
AC_MSG_ERROR([headers for the toxcore library were not found on your system])
|
||||
]
|
||||
)
|
||||
else
|
||||
AC_CHECK_HEADER([tox/tox.h],
|
||||
[],
|
||||
[
|
||||
AC_MSG_ERROR([headers for the toxcore library were not found on your system])
|
||||
],
|
||||
)
|
||||
fi
|
||||
CFLAGS="$CFLAGS_SAVE"
|
||||
CPPFLAGS="$CPPFLAGS_SAVE"
|
||||
AC_SUBST(LIBTOXCORE_CFLAGS)
|
||||
|
||||
LIBTOXCORE_LIBS=
|
||||
LIBTOXCORE_LDFLAGS=
|
||||
LDFLAGS_SAVE="$LDFLAGS"
|
||||
if test -n "$LIBTOXCORE_SEARCH_LIBS"; then
|
||||
LDFLAGS="$LDFLAGS $LIBSODIUM_LDFLAGS -L$LIBTOXCORE_SEARCH_LIBS"
|
||||
AC_CHECK_LIB([toxcore], [tox_new],
|
||||
[
|
||||
LIBTOXCORE_LDFLAGS="-L$LIBTOXCORE_SEARCH_LIBS"
|
||||
LIBTOXCORE_LIBS="-ltoxcore"
|
||||
],
|
||||
[
|
||||
AC_MSG_ERROR([required library toxcore was not found on your system])
|
||||
],
|
||||
[
|
||||
$WINSOCK2_LIBS
|
||||
$LIBSODIUM_LIBS
|
||||
]
|
||||
)
|
||||
else
|
||||
LDFLAGS="$LDFLAGS $LIBSODIUM_LDFLAGS"
|
||||
AC_CHECK_LIB([toxcore], [tox_new],
|
||||
[],
|
||||
[
|
||||
AC_MSG_ERROR([required library toxcore was not found on your system])
|
||||
],
|
||||
[
|
||||
$WINSOCK2_LIBS
|
||||
$LIBSODIUM_LIBS
|
||||
]
|
||||
)
|
||||
fi
|
||||
LDFLAGS="$LDFLAGS_SAVE"
|
||||
AC_SUBST(LIBTOXCORE_LIBS)
|
||||
AC_SUBST(LIBTOXCORE_LDFLAGS)
|
||||
fi
|
||||
|
||||
TOXIC_VERSION="$PACKAGE_VERSION"
|
||||
AC_PATH_PROG([GIT], [git], [no])
|
||||
if test "x$GIT" != "xno"; then
|
||||
if test -d ${srcdir}/.git; then
|
||||
TOXIC_VERSION="${TOXIC_VERSION}_r`${GIT} rev-list HEAD --count`"
|
||||
fi
|
||||
fi
|
||||
AC_SUBST(TOXIC_VERSION)
|
||||
|
||||
eval PACKAGE_DATADIR="${datadir}/${PACKAGE}"
|
||||
eval PACKAGE_DATADIR="${PACKAGE_DATADIR}"
|
||||
AC_DEFINE_UNQUOTED(PACKAGE_DATADIR, "$PACKAGE_DATADIR", [toxic data directory])
|
||||
|
||||
if test "x$NCURSES_WIDECHAR_SUPPORT" = "xyes"; then
|
||||
AC_DEFINE([HAVE_WIDECHAR], [1], [ncurses wide char support available])
|
||||
AC_DEFINE([_XOPEN_SOURCE_EXTENDED], [1],
|
||||
[enable X/Open Portability Guide functionality])
|
||||
fi
|
||||
|
||||
AC_CONFIG_FILES([Makefile
|
||||
misc/Makefile
|
||||
build/Makefile])
|
||||
AC_OUTPUT
|
153
doc/toxic.1
Normal file
153
doc/toxic.1
Normal file
@ -0,0 +1,153 @@
|
||||
'\" t
|
||||
.\" Title: toxic
|
||||
.\" Author: [see the "AUTHORS" section]
|
||||
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
|
||||
.\" Date: 2014-12-27
|
||||
.\" Manual: Toxic Manual
|
||||
.\" Source: toxic __VERSION__
|
||||
.\" Language: English
|
||||
.\"
|
||||
.TH "TOXIC" "1" "2014\-12\-27" "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
|
||||
.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
|
||||
\fIconfig\-file\fR
|
||||
instead of
|
||||
\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
|
||||
\fIdata\-file\fR
|
||||
instead of
|
||||
\fI~/\&.config/tox/data\fR
|
||||
.RE
|
||||
.PP
|
||||
\-h, \-\-help
|
||||
.RS 4
|
||||
Show help message
|
||||
.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
|
||||
\-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
|
99
doc/toxic.1.asc
Normal file
99
doc/toxic.1.asc
Normal file
@ -0,0 +1,99 @@
|
||||
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)
|
||||
|
||||
-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
|
328
doc/toxic.conf.5
Normal file
328
doc/toxic.conf.5
Normal file
@ -0,0 +1,328 @@
|
||||
'\" t
|
||||
.\" Title: toxic.conf
|
||||
.\" Author: [see the "AUTHORS" section]
|
||||
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
|
||||
.\" Date: 2015-02-26
|
||||
.\" Manual: Toxic Manual
|
||||
.\" Source: toxic __VERSION__
|
||||
.\" Language: English
|
||||
.\"
|
||||
.TH "TOXIC\&.CONF" "5" "2015\-02\-26" "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
|
||||
\fBui\fR
|
||||
.RS 4
|
||||
Configuration related to interface elements\&.
|
||||
.PP
|
||||
\fBtimestamps\fR
|
||||
.RS 4
|
||||
Enable or disable timestamps\&. true or false
|
||||
.RE
|
||||
.PP
|
||||
\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
|
||||
\fBtimestamp_format\fR
|
||||
.RS 4
|
||||
Time format string for the interface enclosed by double quotes\&. See
|
||||
\fBdate\fR(1)
|
||||
.RE
|
||||
.PP
|
||||
\fBlog_timestamp_format\fR
|
||||
.RS 4
|
||||
Time format string for logging enclosed by double quotes\&. See
|
||||
\fBdate\fR(1)
|
||||
.RE
|
||||
.PP
|
||||
\fBalerts\fR
|
||||
.RS 4
|
||||
Enable or disable terminal alerts on events\&. true or false
|
||||
.RE
|
||||
.PP
|
||||
\fBnative_colors\fR
|
||||
.RS 4
|
||||
Select between native terminal colors and toxic color theme\&. true or false
|
||||
.RE
|
||||
.PP
|
||||
\fBautolog\fR
|
||||
.RS 4
|
||||
Enable or disable autologging\&. true or false
|
||||
.RE
|
||||
.PP
|
||||
\fBshow_typing_other\fR
|
||||
.RS 4
|
||||
Show when others are typing in a 1\-on\-1 chat\&. true or false
|
||||
.RE
|
||||
.PP
|
||||
\fBshow_typing_self\fR
|
||||
.RS 4
|
||||
Show others when you\(cqre typing in a 1\-on\-1 chat\&. true or false
|
||||
.RE
|
||||
.PP
|
||||
\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
221
doc/toxic.conf.5.asc
Normal 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>
|
199
m4/pkg.m4
199
m4/pkg.m4
@ -1,199 +0,0 @@
|
||||
# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*-
|
||||
# serial 1 (pkg-config-0.24)
|
||||
#
|
||||
# Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
#
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
# PKG_PROG_PKG_CONFIG([MIN-VERSION])
|
||||
# ----------------------------------
|
||||
AC_DEFUN([PKG_PROG_PKG_CONFIG],
|
||||
[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
|
||||
m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$])
|
||||
m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$])
|
||||
AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])
|
||||
AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path])
|
||||
AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path])
|
||||
|
||||
if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
|
||||
AC_PATH_TOOL([PKG_CONFIG], [pkg-config])
|
||||
fi
|
||||
if test -n "$PKG_CONFIG"; then
|
||||
_pkg_min_version=m4_default([$1], [0.9.0])
|
||||
AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version])
|
||||
if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
|
||||
AC_MSG_RESULT([yes])
|
||||
else
|
||||
AC_MSG_RESULT([no])
|
||||
PKG_CONFIG=""
|
||||
fi
|
||||
fi[]dnl
|
||||
])# PKG_PROG_PKG_CONFIG
|
||||
|
||||
# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
|
||||
#
|
||||
# Check to see whether a particular set of modules exists. Similar
|
||||
# to PKG_CHECK_MODULES(), but does not set variables or print errors.
|
||||
#
|
||||
# Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG])
|
||||
# only at the first occurence in configure.ac, so if the first place
|
||||
# it's called might be skipped (such as if it is within an "if", you
|
||||
# have to call PKG_CHECK_EXISTS manually
|
||||
# --------------------------------------------------------------
|
||||
AC_DEFUN([PKG_CHECK_EXISTS],
|
||||
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
|
||||
if test -n "$PKG_CONFIG" && \
|
||||
AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then
|
||||
m4_default([$2], [:])
|
||||
m4_ifvaln([$3], [else
|
||||
$3])dnl
|
||||
fi])
|
||||
|
||||
# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
|
||||
# ---------------------------------------------
|
||||
m4_define([_PKG_CONFIG],
|
||||
[if test -n "$$1"; then
|
||||
pkg_cv_[]$1="$$1"
|
||||
elif test -n "$PKG_CONFIG"; then
|
||||
PKG_CHECK_EXISTS([$3],
|
||||
[pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`
|
||||
test "x$?" != "x0" && pkg_failed=yes ],
|
||||
[pkg_failed=yes])
|
||||
else
|
||||
pkg_failed=untried
|
||||
fi[]dnl
|
||||
])# _PKG_CONFIG
|
||||
|
||||
# _PKG_SHORT_ERRORS_SUPPORTED
|
||||
# -----------------------------
|
||||
AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
|
||||
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])
|
||||
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
|
||||
_pkg_short_errors_supported=yes
|
||||
else
|
||||
_pkg_short_errors_supported=no
|
||||
fi[]dnl
|
||||
])# _PKG_SHORT_ERRORS_SUPPORTED
|
||||
|
||||
|
||||
# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
|
||||
# [ACTION-IF-NOT-FOUND])
|
||||
#
|
||||
#
|
||||
# Note that if there is a possibility the first call to
|
||||
# PKG_CHECK_MODULES might not happen, you should be sure to include an
|
||||
# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
|
||||
#
|
||||
#
|
||||
# --------------------------------------------------------------
|
||||
AC_DEFUN([PKG_CHECK_MODULES],
|
||||
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
|
||||
AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
|
||||
AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl
|
||||
|
||||
pkg_failed=no
|
||||
AC_MSG_CHECKING([for $1])
|
||||
|
||||
_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
|
||||
_PKG_CONFIG([$1][_LIBS], [libs], [$2])
|
||||
|
||||
m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS
|
||||
and $1[]_LIBS to avoid the need to call pkg-config.
|
||||
See the pkg-config man page for more details.])
|
||||
|
||||
if test $pkg_failed = yes; then
|
||||
AC_MSG_RESULT([no])
|
||||
_PKG_SHORT_ERRORS_SUPPORTED
|
||||
if test $_pkg_short_errors_supported = yes; then
|
||||
$1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1`
|
||||
else
|
||||
$1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1`
|
||||
fi
|
||||
# Put the nasty error message in config.log where it belongs
|
||||
echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD
|
||||
|
||||
m4_default([$4], [AC_MSG_ERROR(
|
||||
[Package requirements ($2) were not met:
|
||||
|
||||
$$1_PKG_ERRORS
|
||||
|
||||
Consider adjusting the PKG_CONFIG_PATH environment variable if you
|
||||
installed software in a non-standard prefix.
|
||||
|
||||
_PKG_TEXT])[]dnl
|
||||
])
|
||||
elif test $pkg_failed = untried; then
|
||||
AC_MSG_RESULT([no])
|
||||
m4_default([$4], [AC_MSG_FAILURE(
|
||||
[The pkg-config script could not be found or is too old. Make sure it
|
||||
is in your PATH or set the PKG_CONFIG environment variable to the full
|
||||
path to pkg-config.
|
||||
|
||||
_PKG_TEXT
|
||||
|
||||
To get pkg-config, see <http://pkg-config.freedesktop.org/>.])[]dnl
|
||||
])
|
||||
else
|
||||
$1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
|
||||
$1[]_LIBS=$pkg_cv_[]$1[]_LIBS
|
||||
AC_MSG_RESULT([yes])
|
||||
$3
|
||||
fi[]dnl
|
||||
])# PKG_CHECK_MODULES
|
||||
|
||||
|
||||
# PKG_INSTALLDIR(DIRECTORY)
|
||||
# -------------------------
|
||||
# Substitutes the variable pkgconfigdir as the location where a module
|
||||
# should install pkg-config .pc files. By default the directory is
|
||||
# $libdir/pkgconfig, but the default can be changed by passing
|
||||
# DIRECTORY. The user can override through the --with-pkgconfigdir
|
||||
# parameter.
|
||||
AC_DEFUN([PKG_INSTALLDIR],
|
||||
[m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])])
|
||||
m4_pushdef([pkg_description],
|
||||
[pkg-config installation directory @<:@]pkg_default[@:>@])
|
||||
AC_ARG_WITH([pkgconfigdir],
|
||||
[AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],,
|
||||
[with_pkgconfigdir=]pkg_default)
|
||||
AC_SUBST([pkgconfigdir], [$with_pkgconfigdir])
|
||||
m4_popdef([pkg_default])
|
||||
m4_popdef([pkg_description])
|
||||
]) dnl PKG_INSTALLDIR
|
||||
|
||||
|
||||
# PKG_NOARCH_INSTALLDIR(DIRECTORY)
|
||||
# -------------------------
|
||||
# Substitutes the variable noarch_pkgconfigdir as the location where a
|
||||
# module should install arch-independent pkg-config .pc files. By
|
||||
# default the directory is $datadir/pkgconfig, but the default can be
|
||||
# changed by passing DIRECTORY. The user can override through the
|
||||
# --with-noarch-pkgconfigdir parameter.
|
||||
AC_DEFUN([PKG_NOARCH_INSTALLDIR],
|
||||
[m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])])
|
||||
m4_pushdef([pkg_description],
|
||||
[pkg-config arch-independent installation directory @<:@]pkg_default[@:>@])
|
||||
AC_ARG_WITH([noarch-pkgconfigdir],
|
||||
[AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],,
|
||||
[with_noarch_pkgconfigdir=]pkg_default)
|
||||
AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir])
|
||||
m4_popdef([pkg_default])
|
||||
m4_popdef([pkg_description])
|
||||
]) dnl PKG_NOARCH_INSTALLDIR
|
20
misc/DHTnodes
Normal file
20
misc/DHTnodes
Normal file
@ -0,0 +1,20 @@
|
||||
192.254.75.102 33445 951C88B7E75C867418ACDB5D273821372BB5BD652740BCDF623A4FA293E75D2F
|
||||
144.76.60.215 33445 04119E835DF3E78BACF0F84235B300546AF8B936F035185E2A8E9E0A67C8924F
|
||||
23.226.230.47 33445 A09162D68618E742FFBCA1C2C70385E6679604B2D80EA6E84AD0996A1AC8A074
|
||||
178.62.125.224 33445 10B20C49ACBD968D7C80F2E8438F92EA51F189F4E70CFBBB2C2C8C799E97F03E
|
||||
178.21.112.187 33445 4B2C19E924972CB9B57732FB172F8A8604DE13EEDA2A6234E348983344B23057
|
||||
195.154.119.113 33445 E398A69646B8CEACA9F0B84F553726C1C49270558C57DF5F3C368F05A7D71354
|
||||
192.210.149.121 33445 F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67
|
||||
104.219.184.206 443 8CD087E31C67568103E8C2A28653337E90E6B8EDA0D765D57C6B5172B4F1F04C
|
||||
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 0B8DCEAA7BDDC44BB11173F987CAE3566A2D7057D8DD3CC642BD472B9391002A
|
||||
|
@ -1,2 +0,0 @@
|
||||
192.254.75.98 33445 FE3914F4616E227F29B2103450D6B55A836AD4BD23F97144E2C4ABE8D504FE1B
|
||||
2607:5600:284::2 33445 FE3914F4616E227F29B2103450D6B55A836AD4BD23F97144E2C4ABE8D504FE1B
|
2
misc/DNSservers
Normal file
2
misc/DNSservers
Normal file
@ -0,0 +1,2 @@
|
||||
utox.org d3154f65d28a5b41a05d4ac7e4b39c6b1c233cc857fb365c56e8392737462a12
|
||||
toxme.se 5d72c517df6aec54f1e977a6b6f25914ea4cf7277a85027cd9f5196df17e0b13
|
@ -1 +0,0 @@
|
||||
dist_pkgdata_DATA = DHTservers
|
102
misc/toxic.conf.example
Normal file
102
misc/toxic.conf.example
Normal file
@ -0,0 +1,102 @@
|
||||
// SAMPLE TOXIC CONFIGURATION
|
||||
// USES LIBCONFIG-ACCEPTED SYNTAX
|
||||
|
||||
ui = {
|
||||
// true to enable timestamps, false to disable
|
||||
timestamps=true;
|
||||
|
||||
// true to enable terminal alerts on messages, false to disable
|
||||
alerts=true;
|
||||
|
||||
// true to use native terminal colours, false to use toxic default colour theme
|
||||
native_colors=false;
|
||||
|
||||
// true to enable autologging, false to disable
|
||||
autolog=false;
|
||||
|
||||
// 24 or 12 hour time
|
||||
time_format=24;
|
||||
|
||||
// timestamp format string according to date/strftime format. Overrides time_format setting
|
||||
timestamp_format="%H:%M:%S";
|
||||
|
||||
// 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";
|
||||
};
|
||||
|
11
misc/toxic.desktop
Normal file
11
misc/toxic.desktop
Normal 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;
|
BIN
sounds/ToxicContactOffline.wav
Normal file
BIN
sounds/ToxicContactOffline.wav
Normal file
Binary file not shown.
BIN
sounds/ToxicContactOnline.wav
Normal file
BIN
sounds/ToxicContactOnline.wav
Normal file
Binary file not shown.
BIN
sounds/ToxicError.wav
Normal file
BIN
sounds/ToxicError.wav
Normal file
Binary file not shown.
BIN
sounds/ToxicIncomingCall.wav
Normal file
BIN
sounds/ToxicIncomingCall.wav
Normal file
Binary file not shown.
BIN
sounds/ToxicOutgoingCall.wav
Normal file
BIN
sounds/ToxicOutgoingCall.wav
Normal file
Binary file not shown.
BIN
sounds/ToxicRecvMessage.wav
Normal file
BIN
sounds/ToxicRecvMessage.wav
Normal file
Binary file not shown.
BIN
sounds/ToxicTransferComplete.wav
Normal file
BIN
sounds/ToxicTransferComplete.wav
Normal file
Binary file not shown.
BIN
sounds/ToxicTransferStart.wav
Normal file
BIN
sounds/ToxicTransferStart.wav
Normal file
Binary file not shown.
2
sounds/license
Normal file
2
sounds/license
Normal 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.
|
731
src/audio_call.c
Normal file
731
src/audio_call.c
Normal file
@ -0,0 +1,731 @@
|
||||
/* audio_call.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 "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "audio_call.h"
|
||||
#include "device.h"
|
||||
#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>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#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
|
||||
|
||||
#define cbend pthread_exit(NULL)
|
||||
|
||||
#define MAX_CALLS 10
|
||||
|
||||
#define frame_size (av_DefaultSettings.audio_sample_rate * av_DefaultSettings.audio_frame_duration / 1000)
|
||||
|
||||
static int set_call(Call* call, bool start)
|
||||
{
|
||||
call->in_idx = -1;
|
||||
call->out_idx = -1;
|
||||
|
||||
if ( start ) {
|
||||
call->ttas = true;
|
||||
|
||||
if (pthread_mutex_init(&call->mutex, NULL) != 0)
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
call->ttid = 0;
|
||||
if (pthread_mutex_destroy(&call->mutex) != 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ASettings {
|
||||
AudioError errors;
|
||||
|
||||
ToxAv *av;
|
||||
|
||||
ToxAvCSettings cs;
|
||||
|
||||
Call calls[MAX_CALLS];
|
||||
} ASettins;
|
||||
|
||||
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 );
|
||||
|
||||
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, const char *error_str)
|
||||
{
|
||||
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(ASettins.calls));
|
||||
|
||||
|
||||
/* Streaming stuff from core */
|
||||
|
||||
ASettins.av = toxav_new(tox, MAX_CALLS);
|
||||
|
||||
if ( !ASettins.av ) {
|
||||
ASettins.errors |= ae_StartingCoreAudio;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ( init_devices(ASettins.av) == de_InternalError ) {
|
||||
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(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(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(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;
|
||||
}
|
||||
|
||||
void terminate_audio()
|
||||
{
|
||||
int 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 ) {
|
||||
/*fprintf(stderr, "Could not encode audio packet\n");*/
|
||||
}
|
||||
}
|
||||
|
||||
void write_device_callback(void *agent, int32_t call_index, const int16_t* PCM, uint16_t size, void* arg)
|
||||
{
|
||||
(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, Call *call)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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(Call *call, int32_t call_index)
|
||||
{
|
||||
if ( call->ttas ) {
|
||||
toxav_kill_transmission(ASettins.av, call_index);
|
||||
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;
|
||||
}
|
||||
/*
|
||||
* End of transmission
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Callbacks
|
||||
*/
|
||||
|
||||
#define CB_BODY(call_idx, Arg, onFunc) do { ToxWindow* windows = (Arg); int i;\
|
||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) if (windows[i].onFunc != NULL) windows[i].onFunc(&windows[i], ASettins.av, call_idx); } while (0)
|
||||
|
||||
void callback_recv_invite ( void* av, int32_t call_index, void* arg )
|
||||
{
|
||||
CB_BODY(call_index, arg, onInvite);
|
||||
}
|
||||
void callback_recv_ringing ( void* av, int32_t call_index, void* arg )
|
||||
{
|
||||
CB_BODY(call_index, arg, onRinging);
|
||||
}
|
||||
void callback_recv_starting ( void* av, int32_t call_index, void* arg )
|
||||
{
|
||||
ToxWindow* windows = arg;
|
||||
int i;
|
||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i)
|
||||
if (windows[i].onStarting != NULL && windows[i].call_idx == call_index) {
|
||||
windows[i].onStarting(&windows[i], ASettins.av, call_index);
|
||||
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 ( void* av, int32_t call_index, void* arg )
|
||||
{
|
||||
CB_BODY(call_index, arg, onEnding);
|
||||
stop_transmission(&ASettins.calls[call_index], call_index);
|
||||
}
|
||||
|
||||
void callback_call_started ( void* av, int32_t call_index, void* arg )
|
||||
{
|
||||
ToxWindow* windows = arg;
|
||||
int i;
|
||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i)
|
||||
if (windows[i].onStart != NULL && windows[i].call_idx == call_index) {
|
||||
windows[i].onStart(&windows[i], ASettins.av, call_index);
|
||||
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 ( void* av, int32_t call_index, void* arg )
|
||||
{
|
||||
CB_BODY(call_index, arg, onCancel);
|
||||
|
||||
/* In case call is active */
|
||||
stop_transmission(&ASettins.calls[call_index], call_index);
|
||||
}
|
||||
void callback_call_rejected ( void* av, int32_t call_index, void* arg )
|
||||
{
|
||||
CB_BODY(call_index, arg, onReject);
|
||||
}
|
||||
void callback_call_ended ( void* av, int32_t call_index, void* arg )
|
||||
{
|
||||
CB_BODY(call_index, arg, onEnd);
|
||||
stop_transmission(&ASettins.calls[call_index], call_index);
|
||||
}
|
||||
|
||||
void callback_requ_timeout ( void* av, int32_t call_index, void* arg )
|
||||
{
|
||||
CB_BODY(call_index, arg, onRequestTimeout);
|
||||
}
|
||||
void callback_peer_timeout ( void* av, int32_t call_index, void* arg )
|
||||
{
|
||||
CB_BODY(call_index, arg, onPeerTimeout);
|
||||
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
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Commands from chat_commands.h
|
||||
*/
|
||||
void cmd_call(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
const char *error_str;
|
||||
|
||||
if (argc != 0) {
|
||||
error_str = "Unknown arguments.";
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
if ( !ASettins.av ) {
|
||||
error_str = "Audio not supported!";
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
if (!self->stb->connection) {
|
||||
error_str = "Friend is offline.";
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
ToxAvError error = toxav_call(ASettins.av, &self->call_idx, self->num, &ASettins.cs, 30);
|
||||
|
||||
if ( error != av_ErrorNone ) {
|
||||
if ( error == av_ErrorAlreadyInCallWithPeer ) error_str = "Already in a call!";
|
||||
else error_str = "Internal error!";
|
||||
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Calling... idx: %d", self->call_idx);
|
||||
|
||||
return;
|
||||
on_error:
|
||||
print_err(self, error_str);
|
||||
}
|
||||
|
||||
void cmd_answer(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
const char *error_str;
|
||||
|
||||
if (argc != 0) {
|
||||
error_str = "Unknown arguments.";
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
if ( !ASettins.av ) {
|
||||
error_str = "Audio not supported!";
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
ToxAvError error = toxav_answer(ASettins.av, self->call_idx, &ASettins.cs);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* Callback will print status... */
|
||||
|
||||
return;
|
||||
on_error:
|
||||
print_err (self, error_str);
|
||||
}
|
||||
|
||||
void cmd_reject(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
const char *error_str;
|
||||
|
||||
if (argc != 0) {
|
||||
error_str = "Unknown arguments.";
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
if ( !ASettins.av ) {
|
||||
error_str = "Audio not supported!";
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
ToxAvError error = toxav_reject(ASettins.av, self->call_idx, "Why not?");
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* Callback will print status... */
|
||||
|
||||
return;
|
||||
on_error:
|
||||
print_err (self, error_str);
|
||||
}
|
||||
|
||||
void cmd_hangup(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
const char *error_str;
|
||||
|
||||
if (argc != 0) {
|
||||
error_str = "Unknown arguments.";
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
if ( !ASettins.av ) {
|
||||
error_str = "Audio not supported!";
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
ToxAvError error;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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])
|
||||
{
|
||||
const char *error_str;
|
||||
|
||||
if ( argc != 1 ) {
|
||||
if ( argc < 1 ) error_str = "Type must be specified!";
|
||||
else error_str = "Only one argument allowed!";
|
||||
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
DeviceType type;
|
||||
|
||||
if ( strcasecmp(argv[1], "in") == 0 ) /* Input devices */
|
||||
type = input;
|
||||
|
||||
else if ( strcasecmp(argv[1], "out") == 0 ) /* Output devices */
|
||||
type = output;
|
||||
|
||||
else {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
print_devices(self, type);
|
||||
|
||||
return;
|
||||
on_error:
|
||||
print_err (self, error_str);
|
||||
}
|
||||
|
||||
/* This changes primary device only */
|
||||
void cmd_change_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 {
|
||||
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 ( 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])
|
||||
{
|
||||
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 {
|
||||
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,
|
||||
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, 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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])
|
||||
{
|
||||
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 ( strcasecmp(argv[1], "in") == 0 ) /* Input devices */
|
||||
type = input;
|
||||
|
||||
else if ( strcasecmp(argv[1], "out") == 0 ) /* Output devices */
|
||||
type = output;
|
||||
|
||||
else {
|
||||
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);
|
||||
if (type == input) {
|
||||
device_mute(type, this_call->in_idx);
|
||||
self->chatwin->infobox.in_is_muted ^= 1;
|
||||
} else {
|
||||
device_mute(type, this_call->out_idx);
|
||||
self->chatwin->infobox.out_is_muted ^= 1;
|
||||
}
|
||||
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])
|
||||
{
|
||||
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_CallNonExistent) {
|
||||
switch (callstate)
|
||||
{
|
||||
case av_CallActive:
|
||||
case av_CallHold:
|
||||
toxav_hangup(ASettins.av, self->call_idx);
|
||||
break;
|
||||
case av_CallInviting:
|
||||
toxav_cancel(ASettins.av, self->call_idx, 0, "Not interested anymore");
|
||||
break;
|
||||
case av_CallStarting:
|
||||
toxav_reject(ASettins.av, self->call_idx, "Not interested");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
51
src/audio_call.h
Normal file
51
src/audio_call.h
Normal file
@ -0,0 +1,51 @@
|
||||
/* audio_call.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 AUDIO_H
|
||||
#define AUDIO_H
|
||||
|
||||
#include <tox/toxav.h>
|
||||
|
||||
#include "device.h"
|
||||
|
||||
typedef enum _AudioError {
|
||||
ae_None = 0,
|
||||
ae_StartingCaptureDevice = 1 << 0,
|
||||
ae_StartingOutputDevice = 1 << 1,
|
||||
ae_StartingCoreAudio = 1 << 2
|
||||
} AudioError;
|
||||
|
||||
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 */
|
298
src/autocomplete.c
Normal file
298
src/autocomplete.c
Normal 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->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 (string_is_empty(sub)) {
|
||||
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];
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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)) == -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);
|
||||
strcpy(b_path, tmp);
|
||||
}
|
||||
|
||||
strcpy(b_name, &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
42
src/autocomplete.h
Normal 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 */
|
1387
src/chat.c
1387
src/chat.c
File diff suppressed because it is too large
Load Diff
37
src/chat.h
37
src/chat.h
@ -1,8 +1,35 @@
|
||||
#ifndef CHAT_H_6489PZ13
|
||||
#define CHAT_H_6489PZ13
|
||||
/* chat.h
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "toxic_windows.h"
|
||||
#ifndef CHAT_H
|
||||
#define CHAT_H
|
||||
|
||||
ToxWindow new_chat(Tox *m, int friendnum);
|
||||
#include "windows.h"
|
||||
#include "toxic.h"
|
||||
|
||||
#endif /* end of include guard: CHAT_H_6489PZ13 */
|
||||
/* 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 */
|
||||
|
@ -1,198 +1,311 @@
|
||||
/*
|
||||
* Toxic -- Tox Curses Client
|
||||
/* chat_commands.c
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "toxic_windows.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "misc_tools.h"
|
||||
#include "friendlist.h"
|
||||
#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];
|
||||
|
||||
extern FileSender file_senders[MAX_FILES];
|
||||
extern uint8_t max_file_senders_index;
|
||||
|
||||
void cmd_chat_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
wattron(window, COLOR_PAIR(CYAN) | A_BOLD);
|
||||
wprintw(window, "Chat commands:\n");
|
||||
wattroff(window, COLOR_PAIR(CYAN) | A_BOLD);
|
||||
if (argc < 2) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Requires type in|out and the file ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
wprintw(window, " /status <type> <msg> : Set your status with optional note\n");
|
||||
wprintw(window, " /note <msg> : Set a personal note\n");
|
||||
wprintw(window, " /nick <nick> : Set your nickname\n");
|
||||
wprintw(window, " /invite <n> : Invite friend to a group chat\n");
|
||||
wprintw(window, " /me <action> : Do an action\n");
|
||||
wprintw(window, " /myid : Print your ID\n");
|
||||
wprintw(window, " /join : Join a pending group chat\n");
|
||||
wprintw(window, " /clear : Clear the screen\n");
|
||||
wprintw(window, " /close : Close the current chat window\n");
|
||||
wprintw(window, " /sendfile <filepath> : Send a file\n");
|
||||
wprintw(window, " /savefile <n> : Receive a file\n");
|
||||
wprintw(window, " /quit or /exit : Exit Toxic\n");
|
||||
wprintw(window, " /help : Print this message again\n");
|
||||
|
||||
wattron(window, COLOR_PAIR(CYAN) | A_BOLD);
|
||||
wprintw(window, " * Argument messages must be enclosed in quotation marks.\n\n");
|
||||
wattroff(window, COLOR_PAIR(CYAN) | A_BOLD);
|
||||
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;
|
||||
}
|
||||
|
||||
if (strcasecmp(inoutstr, "in") == 0) { /* cancel an incoming file transfer */
|
||||
if (!Friends.list[self->num].file_receiver[idx].active) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
const char *file_path = Friends.list[self->num].file_receiver[idx].file_path;
|
||||
char file_name[MAX_STR_SIZE];
|
||||
get_file_name(file_name, sizeof(file_name), file_path);
|
||||
snprintf(msg, sizeof(msg), "File transfer for '%s' canceled.", file_name);
|
||||
close_file_transfer(self, m, get_file_receiver_filenum(idx), self->num, TOX_FILE_CONTROL_CANCEL, msg, silent);
|
||||
return;
|
||||
} else if (strcasecmp(inoutstr, "out") == 0) { /* cancel an outgoing file transfer */
|
||||
if (!Friends.list[self->num].file_sender[idx].active) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf(msg, sizeof(msg), "File transfer for '%s' canceled.", Friends.list[self->num].file_sender[idx].file_name);
|
||||
close_file_transfer(self, m, idx, self->num, TOX_FILE_CONTROL_CANCEL, msg, silent);
|
||||
return;
|
||||
} else {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type must be 'in' or 'out'.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
if (argc < 1) {
|
||||
wprintw(window, "Invalid syntax.\n");
|
||||
return;
|
||||
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 */
|
||||
wprintw(window, "Invalid syntax.\n");
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid group number.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (tox_invite_friend(m, self->num, groupnum) == -1) {
|
||||
wprintw(window, "Failed to invite friend.\n");
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to invite contact to group.");
|
||||
return;
|
||||
}
|
||||
|
||||
wprintw(window, "Invited friend to group chat %d.\n", groupnum);
|
||||
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])
|
||||
{
|
||||
if (num_active_windows() >= MAX_WINDOWS_NUM) {
|
||||
wattron(window, COLOR_PAIR(RED));
|
||||
wprintw(window, " * Warning: Too many windows are open.\n");
|
||||
wattron(window, COLOR_PAIR(RED));
|
||||
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open.");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t *groupkey = friends[self->num].pending_groupchat;
|
||||
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 (groupkey[0] == '\0') {
|
||||
wprintw(window, "No pending group chat invite.\n");
|
||||
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, 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,
|
||||
write_device_callback_group, NULL);
|
||||
#endif
|
||||
|
||||
if (groupnum == -1) {
|
||||
wprintw(window, "Group chat instance failed to initialize.\n");
|
||||
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) {
|
||||
wprintw(window, "Group chat window failed to initialize.\n");
|
||||
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])
|
||||
{
|
||||
if (argc != 1) {
|
||||
wprintw(window, "Invalid syntax.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t filenum = atoi(argv[1]);
|
||||
|
||||
if ((filenum == 0 && strcmp(argv[1], "0")) || filenum >= MAX_FILES) {
|
||||
wprintw(window, "No pending file transfers with that number.\n");
|
||||
if (argc < 1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File ID required.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!friends[self->num].file_receiver.pending[filenum]) {
|
||||
wprintw(window, "No pending file transfers with that number.\n");
|
||||
int idx = atoi(argv[1]);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
uint8_t *filename = friends[self->num].file_receiver.filenames[filenum];
|
||||
uint32_t filenum = get_file_receiver_filenum(idx);
|
||||
|
||||
if (tox_file_send_control(m, self->num, 1, filenum, TOX_FILECONTROL_ACCEPT, 0, 0) == 0)
|
||||
wprintw(window, "Accepted file transfer %u. Saving file as: '%s'\n", filenum, filename);
|
||||
else
|
||||
wprintw(window, "File transfer failed.\n");
|
||||
if (!Friends.list[self->num].file_receiver[idx].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;
|
||||
const char *file_path = Friends.list[self->num].file_receiver[idx].file_path;
|
||||
|
||||
TOX_ERR_FILE_CONTROL err;
|
||||
tox_file_control(m, self->num, 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, file_path);
|
||||
|
||||
/* prep progress bar line */
|
||||
char progline[MAX_STR_SIZE];
|
||||
prep_prog_line(progline);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", progline);
|
||||
Friends.list[self->num].file_receiver[idx].line_id = self->chatwin->hst->line_end->id + 2;
|
||||
Friends.list[self->num].file_receiver[idx].pending = false;
|
||||
|
||||
if ((Friends.list[self->num].file_receiver[idx].file = fopen(file_path, "a")) == NULL) {
|
||||
tox_file_control(m, self->num, filenum, TOX_FILE_CONTROL_CANCEL, NULL);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Invalid file path.");
|
||||
} else {
|
||||
Friends.list[self->num].file_receiver[idx].active = true;
|
||||
}
|
||||
|
||||
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])
|
||||
{
|
||||
if (max_file_senders_index >= (MAX_FILES-1)) {
|
||||
wprintw(window,"Please wait for some of your outgoing file transfers to complete.\n");
|
||||
return;
|
||||
}
|
||||
const char *errmsg = NULL;
|
||||
|
||||
if (argc < 1) {
|
||||
wprintw(window, "Invalid syntax.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t *path = argv[1];
|
||||
|
||||
if (path[0] != '\"') {
|
||||
wprintw(window, "File path must be enclosed in quotes.\n");
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path required.");
|
||||
return;
|
||||
}
|
||||
|
||||
path[strlen(++path)-1] = L'\0';
|
||||
int path_len = strlen(path);
|
||||
if (argv[1][0] != '\"') {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path must be enclosed in quotes.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (path_len > MAX_STR_SIZE) {
|
||||
wprintw(window, "File path exceeds character limit.\n");
|
||||
/* 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) {
|
||||
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) {
|
||||
wprintw(window, "File '%s' not found.\n", path);
|
||||
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);
|
||||
|
||||
uint8_t filename[MAX_STR_SIZE];
|
||||
get_file_name(path, filename);
|
||||
int filenum = tox_new_file_sender(m, self->num, filesize, filename, strlen(filename) + 1);
|
||||
|
||||
if (filenum == -1) {
|
||||
wprintw(window, "Error sending file.\n");
|
||||
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];
|
||||
get_file_name(file_name, sizeof(file_name), path);
|
||||
size_t namelen = strlen(file_name);
|
||||
|
||||
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 = (uint8_t) filenum;
|
||||
file_senders[i].friendnum = self->num;
|
||||
file_senders[i].timestamp = (uint64_t) time(NULL);
|
||||
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);
|
||||
|
||||
wprintw(window, "Sending file: '%s'\n", path);
|
||||
if (err != TOX_ERR_FILE_SEND_OK)
|
||||
goto on_send_error;
|
||||
|
||||
if (i == max_file_senders_index)
|
||||
++max_file_senders_index;
|
||||
uint32_t idx = get_file_transfer_index(filenum);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (idx >= MAX_FILES) {
|
||||
errmsg = "File transfer failed: Too many concurrent file transfers";
|
||||
goto on_send_error;
|
||||
}
|
||||
|
||||
memcpy(Friends.list[self->num].file_sender[idx].file_name, file_name, namelen + 1);
|
||||
Friends.list[self->num].file_sender[idx].active = true;
|
||||
Friends.list[self->num].file_sender[idx].started = false;
|
||||
Friends.list[self->num].file_sender[idx].file = file_to_send;
|
||||
Friends.list[self->num].file_sender[idx].timestamp = get_unix_time();
|
||||
Friends.list[self->num].file_sender[idx].file_size = filesize;
|
||||
|
||||
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)", idx, 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);
|
||||
}
|
||||
|
@ -1,9 +1,46 @@
|
||||
/*
|
||||
* Toxic -- Tox Curses Client
|
||||
/* chat_commands.h
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
void cmd_chat_help(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
#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 AUDIO
|
||||
void cmd_call(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_answer(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_reject(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_hangup(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_cancel(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
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 /* AUDIO */
|
||||
|
||||
#endif /* #define CHAT_COMMANDS_H */
|
||||
|
161
src/configdir.c
161
src/configdir.c
@ -1,120 +1,95 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Tox project All Rights Reserved.
|
||||
/* configdir.c
|
||||
*
|
||||
* This file is part of Tox.
|
||||
*
|
||||
* Tox is free software: you can redistribute it and/or modify
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Tox is distributed in the hope that it will be useful,
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Tox. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <shlobj.h>
|
||||
#include <direct.h>
|
||||
#else /* WIN32 */
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
#endif /* WIN32 */
|
||||
|
||||
#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;
|
||||
#ifdef _WIN32
|
||||
#warning Please fix configdir for Win32
|
||||
return NULL;
|
||||
#if 0
|
||||
char appdata[MAX_PATH];
|
||||
BOOL ok;
|
||||
|
||||
ok = SHGetSpecialFolderPathA(NULL, appdata, CSIDL_PROFILE, TRUE);
|
||||
|
||||
if (!ok) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
user_config_dir = strdup(appdata);
|
||||
|
||||
return user_config_dir;
|
||||
#endif
|
||||
|
||||
#else /* WIN32 */
|
||||
|
||||
#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__ */
|
||||
|
||||
const char *tmp;
|
||||
|
||||
if (!(tmp = getenv("XDG_CONFIG_HOME"))) {
|
||||
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 {
|
||||
@ -124,56 +99,48 @@ char *get_user_config_dir(void)
|
||||
# endif /* __APPLE__ */
|
||||
|
||||
return user_config_dir;
|
||||
#undef NSS_BUFLEN_PASSWD
|
||||
#endif /* WIN32 */
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates the config directory.
|
||||
* Creates the config and chatlog directories.
|
||||
*/
|
||||
int create_user_config_dir(char *path)
|
||||
int create_user_config_dirs(char *path)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
#warning Please fix configdir for Win32
|
||||
return -1;
|
||||
#if 0
|
||||
char *fullpath = malloc(strlen(path) + strlen(CONFIGDIR) + 1);
|
||||
strcpy(fullpath, path);
|
||||
strcat(fullpath, CONFIGDIR);
|
||||
|
||||
mkdir_err = _mkdir(fullpath);
|
||||
struct __stat64 buf;
|
||||
|
||||
if (mkdir_err && (errno != EEXIST || _wstat64(fullpath, &buf) || !S_ISDIR(buf.st_mode))) {
|
||||
free(fullpath);
|
||||
return -1;
|
||||
}
|
||||
|
||||
free(fullpath);
|
||||
#endif
|
||||
|
||||
#else
|
||||
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;
|
||||
#endif
|
||||
}
|
||||
|
@ -1,33 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Tox project All Rights Reserved.
|
||||
/* configdir.h
|
||||
*
|
||||
* This file is part of Tox.
|
||||
*
|
||||
* Tox is free software: you can redistribute it and/or modify
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Tox is distributed in the hope that it will be useful,
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Tox. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef _win32
|
||||
#define CONFIGDIR "\\tox\\"
|
||||
#else
|
||||
#define CONFIGDIR "/tox/"
|
||||
#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 */
|
||||
|
469
src/device.c
Normal file
469
src/device.c
Normal file
@ -0,0 +1,469 @@
|
||||
/* device.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 "device.h"
|
||||
|
||||
#ifdef AUDIO
|
||||
#include "audio_call.h"
|
||||
#endif
|
||||
|
||||
#include "line_info.h"
|
||||
#include "settings.h"
|
||||
|
||||
#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__ */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define inline__ inline __attribute__((always_inline))
|
||||
|
||||
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 */
|
||||
uint32_t ref_count;
|
||||
int32_t selection;
|
||||
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] = {{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)
|
||||
#define unlock pthread_mutex_unlock(&mutex)
|
||||
pthread_mutex_t mutex;
|
||||
|
||||
|
||||
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;
|
||||
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;
|
||||
stringed_device_list += strlen( stringed_device_list ) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Start poll thread
|
||||
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;
|
||||
|
||||
#ifdef AUDIO
|
||||
av = av_;
|
||||
#endif /* AUDIO */
|
||||
|
||||
return (DeviceError) de_None;
|
||||
}
|
||||
|
||||
DeviceError terminate_devices()
|
||||
{
|
||||
/* Cleanup if needed */
|
||||
thread_running = false;
|
||||
usleep(20000);
|
||||
|
||||
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) {
|
||||
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) {
|
||||
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, uint32_t sample_rate, uint32_t frame_duration, uint8_t channels)
|
||||
{
|
||||
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, 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);
|
||||
|
||||
if (i == MAX_DEVICES) { unlock; return de_AllDevicesBusy; }
|
||||
else *device_idx = i;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 ) {
|
||||
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);
|
||||
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], device->sound_mode, zeros, frame_size*2, sample_rate);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
unlock;
|
||||
return de_None;
|
||||
}
|
||||
|
||||
DeviceError close_device(DeviceType type, uint32_t device_idx)
|
||||
{
|
||||
if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
|
||||
|
||||
lock;
|
||||
Device* device = running[type][device_idx];
|
||||
DeviceError rc = de_None;
|
||||
|
||||
if (!device) {
|
||||
unlock;
|
||||
return de_DeviceNotActive;
|
||||
}
|
||||
|
||||
running[type][device_idx] = NULL;
|
||||
|
||||
if ( !device->ref_count ) {
|
||||
|
||||
// printf("Closed device ");
|
||||
|
||||
if (type == input) {
|
||||
if ( !alcCaptureCloseDevice(device->dhndl) ) rc = de_AlError;
|
||||
}
|
||||
else {
|
||||
if (alcGetCurrentContext() != device->ctx) alcMakeContextCurrent(device->ctx);
|
||||
|
||||
alDeleteSources(1, &device->source);
|
||||
alDeleteBuffers(OPENAL_BUFS, device->buffers);
|
||||
|
||||
alcMakeContextCurrent(NULL);
|
||||
if ( device->ctx ) alcDestroyContext(device->ctx);
|
||||
if ( !alcCloseDevice(device->dhndl) ) rc = de_AlError;
|
||||
}
|
||||
|
||||
free(device);
|
||||
}
|
||||
else device->ref_count--;
|
||||
|
||||
unlock;
|
||||
return rc;
|
||||
}
|
||||
|
||||
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)
|
||||
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, 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 {
|
||||
pthread_mutex_unlock(device->mutex);
|
||||
return de_Busy;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void* thread_poll (void* arg) // TODO: maybe use thread for every input source
|
||||
{
|
||||
/*
|
||||
* NOTE: We only need to poll input devices for data.
|
||||
*/
|
||||
(void)arg;
|
||||
uint32_t i;
|
||||
int32_t sample = 0;
|
||||
|
||||
|
||||
while (thread_running)
|
||||
{
|
||||
if (thread_paused) usleep(10000); /* Wait for unpause. */
|
||||
else
|
||||
{
|
||||
for (i = 0; i < size[input]; ++i)
|
||||
{
|
||||
lock;
|
||||
if (running[input][i] != NULL)
|
||||
{
|
||||
alcGetIntegerv(running[input][i]->dhndl, ALC_CAPTURE_SAMPLES, sizeof(int32_t), &sample);
|
||||
|
||||
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[16000];
|
||||
alcCaptureSamples(device->dhndl, frame, f_size);
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
DeviceError selection_valid(DeviceType type, int32_t selection)
|
||||
{
|
||||
return (size[type] <= selection || selection < 0) ? de_InvalidSelection : de_None;
|
||||
}
|
||||
|
||||
void* get_device_callback_data(uint32_t device_idx)
|
||||
{
|
||||
if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL)
|
||||
return NULL;
|
||||
|
||||
return running[input][device_idx]->cb_data;
|
||||
}
|
91
src/device.h
Normal file
91
src/device.h
Normal file
@ -0,0 +1,91 @@
|
||||
/* device.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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* You can have multiple sources (Input devices) but only one output device.
|
||||
* Pass buffers to output device via write();
|
||||
* Read from running input device(s) via select()/callback combo.
|
||||
*/
|
||||
|
||||
#ifndef DEVICE_H
|
||||
#define DEVICE_H
|
||||
|
||||
#define OPENAL_BUFS 5
|
||||
#define MAX_DEVICES 32
|
||||
#include <inttypes.h>
|
||||
#include "windows.h"
|
||||
|
||||
typedef enum DeviceType {
|
||||
input,
|
||||
output,
|
||||
} DeviceType;
|
||||
|
||||
typedef enum DeviceError {
|
||||
de_None,
|
||||
de_InternalError = -1,
|
||||
de_InvalidSelection = -2,
|
||||
de_FailedStart = -3,
|
||||
de_Busy = -4,
|
||||
de_AllDevicesBusy = -5,
|
||||
de_DeviceNotActive = -6,
|
||||
de_BufferError = -7,
|
||||
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);
|
||||
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, 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, 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, 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 */
|
428
src/dns.c
Normal file
428
src/dns.c
Normal file
@ -0,0 +1,428 @@
|
||||
/* dns.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 <sys/types.h> /* for u_char */
|
||||
#include <netinet/in.h>
|
||||
#include <resolv.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <arpa/nameser_compat.h>
|
||||
#else
|
||||
#include <arpa/nameser.h>
|
||||
#endif /* ifdef __APPLE__ */
|
||||
|
||||
#include <tox/toxdns.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "line_info.h"
|
||||
#include "dns.h"
|
||||
#include "global_commands.h"
|
||||
#include "misc_tools.h"
|
||||
#include "configdir.h"
|
||||
|
||||
#define DNS3_KEY_SIZE 32
|
||||
#define MAX_DNS_REQST_SIZE 255
|
||||
#define TOX_DNS3_TXT_PREFIX "v=tox3;id="
|
||||
|
||||
extern struct Winthread Winthread;
|
||||
extern struct dns3_servers dns3_servers;
|
||||
extern struct arg_opts arg_opts;
|
||||
|
||||
#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",
|
||||
{
|
||||
0xD3, 0x15, 0x4F, 0x65, 0xD2, 0x8A, 0x5B, 0x41, 0xA0, 0x5D, 0x4A, 0xC7, 0xE4, 0xB3, 0x9C, 0x6B,
|
||||
0x1C, 0x23, 0x3C, 0xC8, 0x57, 0xFB, 0x36, 0x5C, 0x56, 0xE8, 0x39, 0x27, 0x37, 0x46, 0x2A, 0x12
|
||||
}
|
||||
},
|
||||
{
|
||||
"toxme.se",
|
||||
{
|
||||
0x5D, 0x72, 0xC5, 0x17, 0xDF, 0x6A, 0xEC, 0x54, 0xF1, 0xE9, 0x77, 0xA6, 0xB6, 0xF2, 0x59, 0x14,
|
||||
0xEA, 0x4C, 0xF7, 0x27, 0x7A, 0x85, 0x02, 0x7C, 0xD9, 0xF5, 0x19, 0x6D, 0xF1, 0x7E, 0x0B, 0x13
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
static struct thread_data {
|
||||
ToxWindow *self;
|
||||
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 {
|
||||
pthread_t tid;
|
||||
pthread_attr_t attr;
|
||||
} dns_thread;
|
||||
|
||||
|
||||
#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, SYS_MSG, 0, 0, "User lookup failed: %s", errmsg);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void killdns_thread(void *dns_obj)
|
||||
{
|
||||
if (dns_obj)
|
||||
tox_dns3_kill(dns_obj);
|
||||
|
||||
memset(&t_data, 0, sizeof(struct thread_data));
|
||||
pthread_attr_destroy(&dns_thread.attr);
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
/* puts TXT from dns response in buf. Returns length of TXT on success, -1 on fail.*/
|
||||
static int parse_dns_response(ToxWindow *self, u_char *answer, int ans_len, char *buf)
|
||||
{
|
||||
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.");
|
||||
|
||||
ans_pt += len;
|
||||
|
||||
if (ans_pt > ans_end - 4)
|
||||
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.");
|
||||
|
||||
|
||||
ans_pt += INT16SZ; /* class */
|
||||
uint32_t size = 0;
|
||||
|
||||
/* recurse through CNAME rr's */
|
||||
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.");
|
||||
|
||||
ans_pt += len;
|
||||
|
||||
if (ans_pt > ans_end - 10)
|
||||
return dns_error(self, "DNS reply was too short.");
|
||||
|
||||
GETSHORT(type, ans_pt);
|
||||
ans_pt += INT16SZ;
|
||||
ans_pt += 4;
|
||||
GETSHORT(size, ans_pt);
|
||||
|
||||
if (ans_pt + size < answer || ans_pt + size > ans_end)
|
||||
return dns_error(self, "RR overflow.");
|
||||
|
||||
} while (type == T_CNAME);
|
||||
|
||||
if (type != T_TXT)
|
||||
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);
|
||||
|
||||
return txt_len;
|
||||
}
|
||||
|
||||
/* 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(const char *addr, char *namebuf, char *dombuf)
|
||||
{
|
||||
char tmpaddr[MAX_STR_SIZE];
|
||||
char *tmpname, *tmpdom;
|
||||
|
||||
strcpy(tmpaddr, addr);
|
||||
tmpname = strtok(tmpaddr, "@");
|
||||
tmpdom = strtok(NULL, "");
|
||||
|
||||
if (tmpname == NULL || tmpdom == NULL)
|
||||
return -1;
|
||||
|
||||
str_to_lower(tmpdom);
|
||||
strcpy(namebuf, tmpname);
|
||||
strcpy(dombuf, 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 inputdomain[MAX_STR_SIZE];
|
||||
char name[MAX_STR_SIZE];
|
||||
|
||||
int namelen = parse_addr(t_data.addr, name, inputdomain);
|
||||
|
||||
if (namelen == -1) {
|
||||
dns_error(self, "Must be a Tox ID or an address in the form username@domain");
|
||||
killdns_thread(NULL);
|
||||
}
|
||||
|
||||
char DNS_pubkey[DNS3_KEY_SIZE];
|
||||
char domain[MAX_DOMAIN_SIZE];
|
||||
|
||||
int match = get_domain_match(DNS_pubkey, domain, inputdomain);
|
||||
|
||||
if (match == -1) {
|
||||
dns_error(self, "Domain not found.");
|
||||
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.");
|
||||
killdns_thread(NULL);
|
||||
}
|
||||
|
||||
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,
|
||||
(uint8_t *) name, namelen);
|
||||
|
||||
if (str_len == -1) {
|
||||
dns_error(self, "Core failed to generate DNS3 string.");
|
||||
killdns_thread(dns_obj);
|
||||
}
|
||||
|
||||
string[str_len] = '\0';
|
||||
|
||||
u_char answer[PACKETSZ];
|
||||
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, 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.");
|
||||
killdns_thread(dns_obj);
|
||||
}
|
||||
|
||||
char ans_id[MAX_DNS_REQST_SIZE + 1];
|
||||
|
||||
/* extract TXT from DNS response */
|
||||
if (parse_dns_response(self, answer, ans_len, ans_id) == -1)
|
||||
killdns_thread(dns_obj);
|
||||
|
||||
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.");
|
||||
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,
|
||||
strlen(encrypted_id), request_id) == -1) {
|
||||
dns_error(self, "Core failed to decrypt DNS response.");
|
||||
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);
|
||||
|
||||
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, const char *id_bin, const char *addr, const char *msg)
|
||||
{
|
||||
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);
|
||||
t_data.self = self;
|
||||
t_data.m = m;
|
||||
t_data.busy = 1;
|
||||
|
||||
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) {
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
}
|
32
src/dns.h
Normal file
32
src/dns.h
Normal file
@ -0,0 +1,32 @@
|
||||
/* dns.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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* 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
|
||||
|
||||
/* creates new thread for dns3 lookup. Only allows one lookup at a time. */
|
||||
void dns3_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *msg);
|
||||
|
||||
#endif /* #define DNS_H */
|
171
src/execute.c
171
src/execute.c
@ -1,14 +1,38 @@
|
||||
/*
|
||||
* Toxic -- Tox Curses Client
|
||||
/* execute.c
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "toxic_windows.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#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;
|
||||
@ -16,73 +40,111 @@ struct cmd_func {
|
||||
};
|
||||
|
||||
static struct cmd_func global_commands[] = {
|
||||
{ "/accept", cmd_accept },
|
||||
{ "/add", cmd_add },
|
||||
{ "/clear", cmd_clear },
|
||||
{ "/connect", cmd_connect },
|
||||
{ "/exit", cmd_quit },
|
||||
{ "/groupchat", cmd_groupchat },
|
||||
{ "/help", cmd_prompt_help },
|
||||
{ "/myid", cmd_myid },
|
||||
{ "/nick", cmd_nick },
|
||||
{ "/note", cmd_note },
|
||||
{ "/q", cmd_quit },
|
||||
{ "/quit", cmd_quit },
|
||||
{ "/status", cmd_status },
|
||||
{ "/accept", cmd_accept },
|
||||
{ "/add", cmd_add },
|
||||
{ "/avatar", cmd_avatar },
|
||||
{ "/clear", cmd_clear },
|
||||
{ "/connect", cmd_connect },
|
||||
{ "/decline", cmd_decline },
|
||||
{ "/exit", cmd_quit },
|
||||
{ "/group", cmd_groupchat },
|
||||
{ "/help", cmd_prompt_help },
|
||||
{ "/log", cmd_log },
|
||||
{ "/myid", cmd_myid },
|
||||
{ "/nick", cmd_nick },
|
||||
{ "/note", cmd_note },
|
||||
{ "/q", cmd_quit },
|
||||
{ "/quit", cmd_quit },
|
||||
{ "/requests", cmd_requests },
|
||||
{ "/status", cmd_status },
|
||||
#ifdef AUDIO
|
||||
{ "/lsdev", cmd_list_devices },
|
||||
{ "/sdev", cmd_change_device },
|
||||
#endif /* AUDIO */
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
static struct cmd_func chat_commands[] = {
|
||||
{ "/help", cmd_chat_help },
|
||||
{ "/cancel", cmd_cancelfile },
|
||||
{ "/invite", cmd_groupinvite },
|
||||
{ "/join", cmd_join_group },
|
||||
{ "/savefile", cmd_savefile },
|
||||
{ "/sendfile", cmd_sendfile },
|
||||
#ifdef AUDIO
|
||||
{ "/call", cmd_call },
|
||||
{ "/answer", cmd_answer },
|
||||
{ "/reject", cmd_reject },
|
||||
{ "/hangup", cmd_hangup },
|
||||
{ "/mute", cmd_mute },
|
||||
{ "/sense", cmd_sense },
|
||||
#endif /* AUDIO */
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
/* Parses input command and puts args into arg array.
|
||||
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, 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 */
|
||||
wprintw(w, "Invalid argument. Did you forget a closing \"?\n");
|
||||
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);
|
||||
(commands[i].func)(w, self, m, num_args - 1, args);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -90,33 +152,36 @@ 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] = {0};
|
||||
int num_args = parse_command(w, cmd, args);
|
||||
char args[MAX_NUM_ARGS][MAX_STR_SIZE];
|
||||
int num_args = parse_command(w, self, input, args);
|
||||
|
||||
if (num_args == -1)
|
||||
return;
|
||||
|
||||
/* Try to match input command to command functions. If non-global command mode is specified,
|
||||
try specified mode's commands first, then upon failure try global commands.
|
||||
/* Try to match input command to command functions. If non-global command mode is specified,
|
||||
try specified mode's commands first, then upon failure try global commands.
|
||||
|
||||
Note: Global commands must come last in case of duplicate command names */
|
||||
switch (mode) {
|
||||
case CHAT_COMMAND_MODE:
|
||||
if (do_command(w, self, m, num_args, CHAT_NUM_COMMANDS, chat_commands, args) == 0)
|
||||
return;
|
||||
break;
|
||||
case CHAT_COMMAND_MODE:
|
||||
if (do_command(w, self, m, num_args, chat_commands, args) == 0)
|
||||
return;
|
||||
|
||||
case GROUPCHAT_COMMAND_MODE:
|
||||
break;
|
||||
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;
|
||||
|
||||
wprintw(w, "Invalid command.\n");
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid command.");
|
||||
}
|
||||
|
@ -1,10 +1,32 @@
|
||||
/*
|
||||
* Toxic -- Tox Curses Client
|
||||
/* execute.h
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef EXECUTE_H
|
||||
#define EXECUTE_H
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
#define MAX_NUM_ARGS 4 /* Includes command */
|
||||
#define GLOBAL_NUM_COMMANDS 13
|
||||
#define CHAT_NUM_COMMANDS 5
|
||||
|
||||
enum {
|
||||
GLOBAL_COMMAND_MODE,
|
||||
@ -12,4 +34,6 @@ enum {
|
||||
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 */
|
||||
|
297
src/file_senders.c
Normal file
297
src/file_senders.c
Normal file
@ -0,0 +1,297 @@
|
||||
/* 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 "friendlist.h"
|
||||
#include "file_senders.h"
|
||||
#include "line_info.h"
|
||||
#include "misc_tools.h"
|
||||
#include "notify.h"
|
||||
|
||||
FileSender file_senders[MAX_FILES];
|
||||
uint8_t max_file_senders_index;
|
||||
uint8_t num_active_file_senders;
|
||||
extern FriendsList Friends;
|
||||
|
||||
#define NUM_PROG_MARKS 50 /* number of "#"'s in file transfer progress bar. Keep well below MAX_STR_SIZE */
|
||||
|
||||
/* creates initial progress line that will be updated during file transfer.
|
||||
Assumes progline is of size MAX_STR_SIZE */
|
||||
void prep_prog_line(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, int idx, int friendnum, double pct_done)
|
||||
{
|
||||
double bps;
|
||||
uint32_t line_id;
|
||||
|
||||
if (friendnum < 0) {
|
||||
bps = file_senders[idx].bps;
|
||||
line_id = file_senders[idx].line_id;
|
||||
} else {
|
||||
bps = Friends.list[friendnum].file_receiver[idx].bps;
|
||||
line_id = Friends.list[friendnum].file_receiver[idx].line_id;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/* refreshes active file receiver status bars */
|
||||
static void refresh_recv_prog(Tox *m)
|
||||
{
|
||||
int i;
|
||||
uint64_t curtime = get_unix_time();
|
||||
|
||||
for (i = 2; i < MAX_WINDOWS_NUM; ++i) {
|
||||
ToxWindow *toxwin = get_window_ptr(i);
|
||||
|
||||
if (toxwin == NULL || !toxwin->is_chat)
|
||||
continue;
|
||||
|
||||
int fnum = toxwin->num;
|
||||
int j;
|
||||
|
||||
for (j = 0; j < MAX_FILES; ++j) {
|
||||
if (!Friends.list[fnum].file_receiver[j].active)
|
||||
continue;
|
||||
|
||||
int filenum = Friends.list[fnum].file_receiver[j].filenum;
|
||||
double remain = (double) tox_file_data_remaining(m, fnum, filenum, 1);
|
||||
|
||||
/* must be called once per second */
|
||||
if (timed_out(Friends.list[fnum].file_receiver[filenum].last_progress, curtime, 1)) {
|
||||
Friends.list[fnum].file_receiver[filenum].last_progress = curtime;
|
||||
uint64_t size = Friends.list[fnum].file_receiver[filenum].size;
|
||||
double pct_done = remain > 0 ? (1 - (remain / size)) * 100 : 100;
|
||||
print_progress_bar(toxwin, filenum, fnum, pct_done);
|
||||
Friends.list[fnum].file_receiver[filenum].bps = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* refreshes active file sender status bars */
|
||||
static void refresh_sender_prog(Tox *m)
|
||||
{
|
||||
int i;
|
||||
uint64_t curtime = get_unix_time();
|
||||
|
||||
for (i = 0; i < max_file_senders_index; ++i) {
|
||||
if (!file_senders[i].active || file_senders[i].finished)
|
||||
continue;
|
||||
|
||||
int filenum = file_senders[i].filenum;
|
||||
uint32_t friendnum = file_senders[i].friendnum;
|
||||
double remain = (double) tox_file_data_remaining(m, friendnum, filenum, 0);
|
||||
|
||||
/* must be called once per second */
|
||||
if (timed_out(file_senders[i].last_progress, curtime, 1)) {
|
||||
file_senders[i].last_progress = curtime;
|
||||
double pct_done = remain > 0 ? (1 - (remain / file_senders[i].size)) * 100 : 100;
|
||||
print_progress_bar(file_senders[i].toxwin, i, -1, pct_done);
|
||||
file_senders[i].bps = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* called whenever a file sender is opened or closed */
|
||||
void reset_file_sender_queue(void)
|
||||
{
|
||||
int i;
|
||||
int pos = 0;
|
||||
|
||||
for (i = 0; i < max_file_senders_index; ++i) {
|
||||
if (file_senders[i].active)
|
||||
file_senders[i].queue_pos = pos++;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 close_file_sender(ToxWindow *self, Tox *m, int i, const char *msg, int CTRL, int filenum, uint32_t friendnum)
|
||||
{
|
||||
if (msg != NULL)
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", msg);
|
||||
|
||||
if (CTRL > 0)
|
||||
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();
|
||||
reset_file_sender_queue();
|
||||
--num_active_file_senders;
|
||||
}
|
||||
|
||||
void close_all_file_senders(Tox *m)
|
||||
{
|
||||
uint8_t 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();
|
||||
}
|
||||
}
|
||||
|
||||
static void send_file_data(ToxWindow *self, Tox *m, uint8_t i, uint32_t friendnum, uint32_t filenum,
|
||||
const char *filename)
|
||||
{
|
||||
FILE *fp = file_senders[i].file;
|
||||
|
||||
while (true) {
|
||||
TOX_ERR_FILE_SEND_CHUNK err;
|
||||
if (!tox_file_send_chunk(m, friendnum, filenum, (uint8_t *) file_senders[i].nextpiece,
|
||||
file_senders[i].piecelen, &err) {
|
||||
fprintf(stderr, "tox_file_send_chunk failed with error %d\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
file_senders[i].timestamp = get_unix_time();
|
||||
file_senders[i].bps += file_senders[i].piecelen;
|
||||
file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1,
|
||||
tox_file_data_size(m, friendnum), fp);
|
||||
|
||||
/* note: file sender is closed in chat_onFileControl callback after receiving reply */
|
||||
if (file_senders[i].piecelen == 0) {
|
||||
if (feof(fp) != 0) { /* make sure we're really at eof */
|
||||
print_progress_bar(self, i, -1, 100.0);
|
||||
tox_file_send_control(m, friendnum, 0, filenum, TOX_FILECONTROL_FINISHED, 0, 0);
|
||||
file_senders[i].finished = true;
|
||||
} else {
|
||||
char msg[MAX_STR_SIZE];
|
||||
snprintf(msg, sizeof(msg), "File transfer for '%s' failed: Read error.", file_senders[i].filename);
|
||||
close_file_sender(self, m, i, msg, TOX_FILECONTROL_KILL, filenum, friendnum);
|
||||
sound_notify(self, error, NT_NOFOCUS | NT_WNDALERT_2, NULL);
|
||||
|
||||
if (self->active_box != -1)
|
||||
box_notify2(self, error, NT_NOFOCUS | NT_WNDALERT_2, self->active_box, "%s", msg);
|
||||
else
|
||||
box_notify(self, error, NT_NOFOCUS | NT_WNDALERT_2, &self->active_box, self->name, "%s", msg);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void do_file_senders(Tox *m)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < max_file_senders_index; ++i) {
|
||||
if (!file_senders[i].active)
|
||||
continue;
|
||||
|
||||
if (file_senders[i].queue_pos > 0) {
|
||||
--file_senders[i].queue_pos;
|
||||
continue;
|
||||
}
|
||||
|
||||
ToxWindow *self = file_senders[i].toxwin;
|
||||
char *filename = file_senders[i].filename;
|
||||
int filenum = file_senders[i].filenum;
|
||||
uint32_t friendnum = file_senders[i].friendnum;
|
||||
|
||||
/* kill file transfer if chatwindow is closed */
|
||||
if (self->chatwin == NULL) {
|
||||
close_file_sender(self, m, i, NULL, TOX_FILECONTROL_KILL, filenum, friendnum);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If file transfer has timed out kill transfer and send kill control */
|
||||
if (timed_out(file_senders[i].timestamp, get_unix_time(), TIMEOUT_FILESENDER)
|
||||
&& (!file_senders[i].paused || (file_senders[i].paused && file_senders[i].noconnection))) {
|
||||
char msg[MAX_STR_SIZE];
|
||||
snprintf(msg, sizeof(msg), "File transfer for '%s' timed out.", filename);
|
||||
close_file_sender(self, m, i, msg, TOX_FILECONTROL_KILL, filenum, friendnum);
|
||||
|
||||
if (self->active_box != -1)
|
||||
box_notify2(self, error, NT_NOFOCUS | NT_WNDALERT_2, self->active_box, "%s", msg);
|
||||
else
|
||||
box_notify(self, error, NT_NOFOCUS | NT_WNDALERT_2, &self->active_box, self->name, "%s", msg);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !(file_senders[i].paused | file_senders[i].noconnection | file_senders[i].finished) )
|
||||
send_file_data(self, m, i, friendnum, filenum, filename);
|
||||
|
||||
file_senders[i].queue_pos = num_active_file_senders - 1;
|
||||
}
|
||||
|
||||
refresh_sender_prog(m);
|
||||
refresh_recv_prog(m);
|
||||
}
|
76
src/file_senders.h
Normal file
76
src/file_senders.h
Normal file
@ -0,0 +1,76 @@
|
||||
/* file_senders.h
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef FILESENDERS_H
|
||||
#define FILESENDERS_H
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
#define KiB 1024
|
||||
#define MiB 1048576 /* 1024 ^ 2 */
|
||||
#define GiB 1073741824 /* 1024 ^ 3 */
|
||||
|
||||
#define FILE_PIECE_SIZE 2048 /* must be >= (MAX_CRYPTO_DATA_SIZE - 2) in toxcore/net_crypto.h */
|
||||
#define MAX_FILES 32
|
||||
#define TIMEOUT_FILESENDER 120
|
||||
|
||||
typedef struct {
|
||||
FILE *file;
|
||||
ToxWindow *toxwin;
|
||||
uint32_t friendnum;
|
||||
bool active;
|
||||
bool noconnection; /* set when the connection has been interrupted */
|
||||
bool paused; /* set when transfer has been explicitly paused */
|
||||
bool finished; /* set after entire file has been sent but no TOX_FILECONTROL_FINISHED receieved */
|
||||
bool started; /* set after TOX_FILECONTROL_ACCEPT received */
|
||||
int filenum;
|
||||
char nextpiece[FILE_PIECE_SIZE];
|
||||
size_t piecelen;
|
||||
char filename[MAX_STR_SIZE];
|
||||
uint64_t timestamp; /* marks the last time data was successfully transfered */
|
||||
uint64_t last_progress; /* marks the last time the progress bar was refreshed */
|
||||
double bps;
|
||||
uint64_t size;
|
||||
uint32_t line_id;
|
||||
uint8_t queue_pos;
|
||||
} FileSender;
|
||||
|
||||
/* creates initial progress line that will be updated during file transfer.
|
||||
Assumes progline is of size MAX_STR_SIZE */
|
||||
void prep_prog_line(char *progline);
|
||||
|
||||
/* 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, int idx, int friendnum, double pct_remain);
|
||||
|
||||
/* 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 close_file_sender(ToxWindow *self, Tox *m, int i, const char *msg, int CTRL, int filenum, int32_t friendnum);
|
||||
|
||||
/* called whenever a file sender is opened or closed */
|
||||
void reset_file_sender_queue(void);
|
||||
|
||||
void close_all_file_senders(Tox *m);
|
||||
void do_file_senders(Tox *m);
|
||||
|
||||
#endif /* #define FILESENDERS_H */
|
194
src/file_transfers.c
Normal file
194
src/file_transfers.c
Normal file
@ -0,0 +1,194 @@
|
||||
/* 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;
|
||||
|
||||
#define NUM_PROG_MARKS 50 /* number of "#"'s in file transfer progress bar. Keep well below MAX_STR_SIZE */
|
||||
|
||||
/* creates initial progress line that will be updated during file transfer.
|
||||
Assumes progline is of size MAX_STR_SIZE */
|
||||
void prep_prog_line(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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
/* Filenumbers >= this number are receiving, otherwise sending.
|
||||
* Warning: This behaviour is not defined by the Tox API and is subject to change at any time.
|
||||
*/
|
||||
#define FILE_NUMBER_MAGIC_NUM (1 << 16)
|
||||
|
||||
/* Returns filenum's file transfer array index */
|
||||
uint32_t get_file_transfer_index(uint32_t filenum)
|
||||
{
|
||||
return filenum >= FILE_NUMBER_MAGIC_NUM ? (filenum >> 16) - 1 : filenum;
|
||||
}
|
||||
|
||||
/* Returns the filenumber of a file receiver's index */
|
||||
uint32_t get_file_receiver_filenum(uint32_t idx)
|
||||
{
|
||||
return (idx + 1) << 16;
|
||||
}
|
||||
|
||||
/* Return true if filenum is associated with a file receiver, false if file sender */
|
||||
bool filenum_is_sending(uint32_t filenum)
|
||||
{
|
||||
return filenum < FILE_NUMBER_MAGIC_NUM;
|
||||
}
|
||||
|
||||
/* refreshes active file receiver status bars for friendnum */
|
||||
void refresh_file_transfer_progress(ToxWindow *self, Tox *m, uint32_t friendnum)
|
||||
{
|
||||
uint64_t curtime = get_unix_time();
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < MAX_FILES; ++i) {
|
||||
if (Friends.list[friendnum].file_receiver[i].active) {
|
||||
if (timed_out(Friends.list[friendnum].file_receiver[i].last_progress, curtime, 1)) {
|
||||
uint64_t size = Friends.list[friendnum].file_receiver[i].file_size;
|
||||
double remain = size - Friends.list[friendnum].file_receiver[i].position;
|
||||
double pct_done = remain > 0 ? (1 - (remain / size)) * 100 : 100;
|
||||
|
||||
print_progress_bar(self, Friends.list[friendnum].file_receiver[i].bps, pct_done,
|
||||
Friends.list[friendnum].file_receiver[i].line_id);
|
||||
|
||||
Friends.list[friendnum].file_receiver[i].bps = 0;
|
||||
Friends.list[friendnum].file_receiver[i].last_progress = curtime;
|
||||
}
|
||||
}
|
||||
|
||||
if (Friends.list[friendnum].file_sender[i].active) {
|
||||
if (timed_out(Friends.list[friendnum].file_sender[i].last_progress, curtime, 1)) {
|
||||
uint64_t size = Friends.list[friendnum].file_sender[i].file_size;
|
||||
double remain = size - Friends.list[friendnum].file_sender[i].position;
|
||||
double pct_done = remain > 0 ? (1 - (remain / size)) * 100 : 100;
|
||||
|
||||
print_progress_bar(self, Friends.list[friendnum].file_sender[i].bps, pct_done,
|
||||
Friends.list[friendnum].file_sender[i].line_id);
|
||||
|
||||
Friends.list[friendnum].file_sender[i].bps = 0;
|
||||
Friends.list[friendnum].file_sender[i].last_progress = curtime;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Closes file transfer with filenum.
|
||||
* 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, uint32_t filenum, uint32_t friendnum, int CTRL,
|
||||
const char *message, Notification sound_type)
|
||||
{
|
||||
uint32_t idx = get_file_transfer_index(filenum);
|
||||
bool sending = filenum_is_sending(filenum);
|
||||
|
||||
if (sending && Friends.list[friendnum].file_sender[idx].active) {
|
||||
FILE *fp = Friends.list[friendnum].file_sender[idx].file;
|
||||
|
||||
if (fp)
|
||||
fclose(fp);
|
||||
|
||||
memset(&Friends.list[friendnum].file_sender[idx], 0, sizeof(struct FileSender));
|
||||
}
|
||||
else if (!sending && Friends.list[friendnum].file_receiver[idx].active) {
|
||||
FILE *fp = Friends.list[friendnum].file_receiver[idx].file;
|
||||
|
||||
if (fp)
|
||||
fclose(fp);
|
||||
|
||||
memset(&Friends.list[friendnum].file_receiver[idx], 0, sizeof(struct FileReceiver));
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
||||
if (CTRL >= 0)
|
||||
tox_file_control(m, friendnum, filenum, CTRL, NULL);
|
||||
|
||||
if (message && self) {
|
||||
if (self->active_box != -1)
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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) {
|
||||
fprintf(stderr, "%lu\n", i);
|
||||
if (Friends.list[friendnum].file_sender[i].active)
|
||||
close_file_transfer(NULL, m, i, friendnum, -1, NULL, silent);
|
||||
if (Friends.list[friendnum].file_receiver[i].active)
|
||||
close_file_transfer(NULL, m, get_file_receiver_filenum(i), friendnum, -1, NULL, silent);
|
||||
}
|
||||
}
|
96
src/file_transfers.h
Normal file
96
src/file_transfers.h
Normal file
@ -0,0 +1,96 @@
|
||||
/* 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 FILE_PIECE_SIZE 2048
|
||||
#define MAX_FILES 32
|
||||
#define TIMEOUT_FILESENDER 120
|
||||
|
||||
struct FileSender {
|
||||
FILE *file;
|
||||
char file_name[TOX_MAX_FILENAME_LENGTH];
|
||||
bool active;
|
||||
bool noconnection; /* set when the connection has been interrupted */
|
||||
bool paused; /* set when transfer has been explicitly paused */
|
||||
bool started; /* set after TOX_FILECONTROL_ACCEPT received */
|
||||
uint64_t timestamp; /* marks the last time data was successfully transfered */
|
||||
double bps;
|
||||
uint64_t file_size;
|
||||
uint64_t last_progress; /* marks the last time the progress bar was refreshed */
|
||||
uint64_t position;
|
||||
uint32_t line_id;
|
||||
};
|
||||
|
||||
struct FileReceiver {
|
||||
FILE *file;
|
||||
char file_path[PATH_MAX + 1];
|
||||
bool pending;
|
||||
bool active;
|
||||
double bps;
|
||||
uint64_t file_size;
|
||||
uint64_t last_progress;
|
||||
uint64_t position;
|
||||
uint32_t line_id;
|
||||
};
|
||||
|
||||
/* creates initial progress line that will be updated during file transfer.
|
||||
progline must be at lesat MAX_STR_SIZE bytes */
|
||||
void prep_prog_line(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 receiver status bars for friendnum */
|
||||
void refresh_file_transfer_progress(ToxWindow *self, Tox *m, uint32_t friendnum);
|
||||
|
||||
/* Returns filenum's file transfer array index */
|
||||
uint32_t get_file_transfer_index(uint32_t filenum);
|
||||
|
||||
/* Returns the filenumber of a file receiver's index */
|
||||
uint32_t get_file_receiver_filenum(uint32_t idx);
|
||||
|
||||
/* Return true if filenum is associated with a file receiver, false if file sender */
|
||||
bool filenum_is_sending(uint32_t filenum);
|
||||
|
||||
/* Closes file transfer with filenum.
|
||||
* 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, uint32_t filenum, uint32_t friendnum, 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);
|
||||
|
||||
#endif /* #define FILE_TRANSFERS_H */
|
1184
src/friendlist.c
1184
src/friendlist.c
File diff suppressed because it is too large
Load Diff
@ -1,29 +1,94 @@
|
||||
#ifndef FRIENDLIST_H_53I41IM
|
||||
#define FRIENDLIST_H_53I41IM
|
||||
/* friendlist.h
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "toxic_windows.h"
|
||||
#ifndef FRIENDLIST_H
|
||||
#define FRIENDLIST_H
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "file_transfers.h"
|
||||
|
||||
struct LastOnline {
|
||||
uint64_t last_on;
|
||||
struct tm tm;
|
||||
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 {
|
||||
uint8_t name[TOX_MAX_NAME_LENGTH];
|
||||
uint16_t namelength;
|
||||
uint8_t statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH];
|
||||
uint16_t statusmsg_len;
|
||||
uint8_t pending_groupchat[TOX_CLIENT_ID_SIZE];
|
||||
uint8_t pub_key[TOX_CLIENT_ID_SIZE];
|
||||
int num;
|
||||
char name[TOXIC_MAX_NAME_LENGTH + 1];
|
||||
int namelength;
|
||||
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;
|
||||
TOX_CONNECTION connection_status;
|
||||
bool is_typing;
|
||||
TOX_USERSTATUS status;
|
||||
struct FileReceiver file_receiver;
|
||||
bool logging_on; /* saves preference for friend irrespective of global settings */
|
||||
uint8_t status;
|
||||
|
||||
struct LastOnline last_online;
|
||||
struct GroupChatInvite group_invite;
|
||||
|
||||
struct FileReceiver file_receiver[MAX_FILES];
|
||||
struct FileSender file_sender[MAX_FILES];
|
||||
} ToxicFriend;
|
||||
|
||||
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;
|
||||
|
||||
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(int f_num);
|
||||
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(Tox *m);
|
||||
void sort_friendlist_index(void);
|
||||
|
||||
#endif /* end of include guard: FRIENDLIST_H_53I41IM */
|
||||
#endif /* end of include guard: FRIENDLIST_H */
|
||||
|
@ -1,253 +1,488 @@
|
||||
/*
|
||||
* Toxic -- Tox Curses Client
|
||||
/* global_commands.c
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "toxic_windows.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "misc_tools.h"
|
||||
#include "friendlist.h"
|
||||
#include "log.h"
|
||||
#include "line_info.h"
|
||||
#include "dns.h"
|
||||
#include "groupchat.h"
|
||||
#include "prompt.h"
|
||||
#include "help.h"
|
||||
#include "term_mplex.h"
|
||||
|
||||
extern char *DATA_FILE;
|
||||
extern ToxWindow *prompt;
|
||||
|
||||
extern ToxicFriend friends[MAX_FRIENDS_NUM];
|
||||
|
||||
extern uint8_t pending_frnd_requests[MAX_FRIENDS_NUM][TOX_CLIENT_ID_SIZE];
|
||||
extern uint8_t 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])
|
||||
{
|
||||
/* check arguments */
|
||||
if (argc != 1) {
|
||||
wprintw(window, "Invalid syntax.\n");
|
||||
return;
|
||||
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) {
|
||||
wprintw(window, "No pending friend request with that number.\n");
|
||||
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])) {
|
||||
wprintw(window, "No pending friend request with that number.\n");
|
||||
if (!FrndRequests.request[req].active) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
int friendnum = tox_add_friend_norequest(m, 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)
|
||||
wprintw(window, "Failed to add friend.\n");
|
||||
else {
|
||||
wprintw(window, "Friend request accepted.\n");
|
||||
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;
|
||||
FrndRequests.max_idx = i;
|
||||
--FrndRequests.num_requests;
|
||||
|
||||
}
|
||||
|
||||
void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg)
|
||||
{
|
||||
const char *errmsg;
|
||||
|
||||
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_ERR_FRIEND_ADD_NO_MESSAGE:
|
||||
errmsg = "Please add a message to your request.";
|
||||
break;
|
||||
|
||||
case TOX_ERR_FRIEND_ADD_OWN_KEY:
|
||||
errmsg = "That appears to be your own ID.";
|
||||
break;
|
||||
|
||||
case TOX_ERR_FRIEND_ADD_ALREADY_SENT:
|
||||
errmsg = "Friend request has already been sent.";
|
||||
break;
|
||||
|
||||
case TOX_ERR_FRIEND_ADD_BAD_CHECKSUM:
|
||||
errmsg = "Bad checksum in address.";
|
||||
break;
|
||||
|
||||
case TOX_ERR_FRIEND_ADD_SET_NEW_NOSPAM:
|
||||
errmsg = "Nospam was different.";
|
||||
break;
|
||||
|
||||
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, SYS_MSG, 0, 0, errmsg);
|
||||
}
|
||||
|
||||
void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
if (argc < 1) {
|
||||
wprintw(window, "Invalid syntax.\n");
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Tox ID or address required.");
|
||||
return;
|
||||
}
|
||||
|
||||
char *id = argv[1];
|
||||
uint8_t msg[MAX_STR_SIZE];
|
||||
const char *id = argv[1];
|
||||
char msg[MAX_STR_SIZE];
|
||||
|
||||
if (argc > 1) {
|
||||
uint8_t *temp = argv[2];
|
||||
|
||||
if (temp[0] != '\"') {
|
||||
wprintw(window, "Message must be enclosed in quotes.\n");
|
||||
if (argv[2][0] != '\"') {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Message must be enclosed in quotes.");
|
||||
return;
|
||||
}
|
||||
|
||||
temp[strlen(++temp)-1] = L'\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 {
|
||||
uint8_t selfname[TOX_MAX_NAME_LENGTH];
|
||||
tox_get_self_name(m, selfname, TOX_MAX_NAME_LENGTH);
|
||||
char selfname[TOX_MAX_NAME_LENGTH];
|
||||
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);
|
||||
}
|
||||
|
||||
if (strlen(id) != 2 * TOX_FRIEND_ADDRESS_SIZE) {
|
||||
wprintw(window, "Invalid ID length.\n");
|
||||
return;
|
||||
}
|
||||
char id_bin[TOX_ADDRESS_SIZE] = {0};
|
||||
uint16_t id_len = (uint16_t) strlen(id);
|
||||
|
||||
size_t i;
|
||||
char xx[3];
|
||||
uint32_t x;
|
||||
uint8_t id_bin[TOX_FRIEND_ADDRESS_SIZE];
|
||||
/* try to add tox ID */
|
||||
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) {
|
||||
xx[0] = id[2 * i];
|
||||
xx[1] = id[2 * i + 1];
|
||||
xx[2] = '\0';
|
||||
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) {
|
||||
wprintw(window, "Invalid ID.\n");
|
||||
return;
|
||||
if (sscanf(xx, "%02x", &x) != 1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid Tox ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
id_bin[i] = x;
|
||||
}
|
||||
|
||||
id_bin[i] = x;
|
||||
cmd_add_helper(self, m, id_bin, msg);
|
||||
} else { /* assume id is a username@domain address and do DNS lookup */
|
||||
dns3_lookup(self, m, id_bin, id, msg);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; i++) {
|
||||
id[i] = toupper(id[i]);
|
||||
}
|
||||
void cmd_avatar(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, "Failed to set avatar: No file path supplied.");
|
||||
// return;
|
||||
// }
|
||||
|
||||
int f_num = tox_add_friend(m, id_bin, msg, strlen(msg) + 1);
|
||||
// /* turns the avatar off */
|
||||
// if (strlen(argv[1]) < 3) {
|
||||
// tox_unset_avatar(m);
|
||||
// line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No avatar set.");
|
||||
// return;
|
||||
// }
|
||||
|
||||
switch (f_num) {
|
||||
case TOX_FAERR_TOOLONG:
|
||||
wprintw(window, "Message is too long.\n");
|
||||
break;
|
||||
case TOX_FAERR_NOMESSAGE:
|
||||
wprintw(window, "Please add a message to your request.\n");
|
||||
break;
|
||||
case TOX_FAERR_OWNKEY:
|
||||
wprintw(window, "That appears to be your own ID.\n");
|
||||
break;
|
||||
case TOX_FAERR_ALREADYSENT:
|
||||
wprintw(window, "Friend request has already been sent.\n");
|
||||
break;
|
||||
case TOX_FAERR_UNKNOWN:
|
||||
wprintw(window, "Undefined error when adding friend.\n");
|
||||
break;
|
||||
case TOX_FAERR_BADCHECKSUM:
|
||||
wprintw(window, "Bad checksum in address.\n");
|
||||
break;
|
||||
case TOX_FAERR_SETNEWNOSPAM:
|
||||
wprintw(window, "Nospam was different (is this contact already added?)\n");
|
||||
break;
|
||||
default:
|
||||
wprintw(window, "Friend request sent.\n");
|
||||
on_friendadded(m, f_num, true);
|
||||
break;
|
||||
}
|
||||
// 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;
|
||||
// path[len] = '\0';
|
||||
|
||||
// off_t sz = file_size(path);
|
||||
|
||||
// if (sz <= 8) {
|
||||
// line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set avatar: Invalid file.");
|
||||
// return;
|
||||
// }
|
||||
|
||||
// FILE *fp = fopen(path, "rb");
|
||||
|
||||
// if (fp == NULL) {
|
||||
// line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set avatar: Could not open file.");
|
||||
// return;
|
||||
// }
|
||||
|
||||
// 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);
|
||||
// line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set avatar: File type not supported.");
|
||||
// return;
|
||||
// }
|
||||
|
||||
// char *avatar = malloc(sz);
|
||||
|
||||
// if (avatar == NULL)
|
||||
// exit_toxic_err("Failed in cmd_avatar", FATALERR_MEMORY);
|
||||
|
||||
// if (fread(avatar, sz, 1, fp) != 1) {
|
||||
// fclose(fp);
|
||||
// free(avatar);
|
||||
// line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set avatar: Read fail.");
|
||||
// return;
|
||||
// }
|
||||
|
||||
// if (tox_set_avatar(m, TOX_AVATAR_FORMAT_PNG, (const uint8_t *) avatar, (uint32_t) sz) == -1)
|
||||
// line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set avatar");
|
||||
|
||||
// char filename[MAX_STR_SIZE];
|
||||
// get_file_name(filename, sizeof(filename), path);
|
||||
// line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Avatar set to '%s'", filename);
|
||||
|
||||
// fclose(fp);
|
||||
// free(avatar);
|
||||
}
|
||||
|
||||
void cmd_clear(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
wclear(window);
|
||||
wprintw(window, "\n\n");
|
||||
line_info_clear(self->chatwin->hst);
|
||||
force_refresh(window);
|
||||
}
|
||||
|
||||
void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
/* check arguments */
|
||||
if (argc != 3) {
|
||||
wprintw(window, "Invalid syntax.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
tox_IP_Port dht;
|
||||
char *ip = argv[1];
|
||||
char *port = argv[2];
|
||||
char *key = argv[3];
|
||||
|
||||
if (atoi(port) == 0) {
|
||||
wprintw(window, "Invalid syntax.\n");
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Require: <ip> <port> <key>");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t *binary_string = hex_string_to_bin(key);
|
||||
tox_bootstrap_from_address(m, ip, TOX_ENABLE_IPV6_DEFAULT,
|
||||
htons(atoi(port)), binary_string);
|
||||
const char *ip = argv[1];
|
||||
const char *port = argv[2];
|
||||
const char *key = argv[3];
|
||||
|
||||
if (atoi(port) == 0) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid port.");
|
||||
return;
|
||||
}
|
||||
|
||||
char *binary_string = hex_string_to_bin(key);
|
||||
|
||||
TOX_ERR_BOOTSTRAP err;
|
||||
tox_bootstrap(m, ip, atoi(port), (uint8_t *) binary_string, &err);
|
||||
free(binary_string);
|
||||
|
||||
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])
|
||||
{
|
||||
if (num_active_windows() >= MAX_WINDOWS_NUM) {
|
||||
wattron(window, COLOR_PAIR(RED));
|
||||
wprintw(window, " * Warning: Too many windows are open.\n");
|
||||
wattron(window, COLOR_PAIR(RED));
|
||||
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
|
||||
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, write_device_callback_group, NULL);
|
||||
#endif
|
||||
|
||||
if (groupnum == -1) {
|
||||
wprintw(window, "Group chat instance failed to initialize.\n");
|
||||
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) {
|
||||
wprintw(window, "Group chat window failed to initialize.\n");
|
||||
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;
|
||||
}
|
||||
|
||||
wprintw(window, "Group chat created as %d.\n", groupnum);
|
||||
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])
|
||||
{
|
||||
const char *msg;
|
||||
struct chatlog *log = self->chatwin->log;
|
||||
|
||||
if (argc == 0) {
|
||||
if (log->log_on)
|
||||
msg = "Logging for this window is ON. Type \"/log off\" to disable.";
|
||||
else
|
||||
msg = "Logging for this window is OFF. Type \"/log on\" to enable.";
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (self->is_chat) {
|
||||
Friends.list[self->num].logging_on = true;
|
||||
log_enable(self->name, myid, Friends.list[self->num].pub_key, log, LOG_CHAT);
|
||||
} else if (self->is_prompt) {
|
||||
log_enable(self->name, myid, NULL, log, LOG_PROMPT);
|
||||
} else if (self->is_groupchat) {
|
||||
log_enable(self->name, myid, NULL, log, LOG_GROUP);
|
||||
}
|
||||
|
||||
msg = "Logging enabled";
|
||||
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.list[self->num].logging_on = false;
|
||||
|
||||
log_disable(log);
|
||||
|
||||
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, 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};
|
||||
uint8_t address[TOX_FRIEND_ADDRESS_SIZE];
|
||||
tox_get_address(m, 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);
|
||||
}
|
||||
|
||||
wprintw(window, "%s\n", id);
|
||||
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])
|
||||
{
|
||||
/* check arguments */
|
||||
if (argc < 1) {
|
||||
wprintw(window, "Invalid name.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t *nick = argv[1];
|
||||
int len = strlen(nick);
|
||||
|
||||
if (nick[0] == '\"') {
|
||||
++nick;
|
||||
len -= 2;
|
||||
nick[len] = L'\0';
|
||||
}
|
||||
|
||||
if (!valid_nick(nick)) {
|
||||
wprintw(window, "Invalid name.\n");
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Input required.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (len > TOXIC_MAX_NAME_LENGTH) {
|
||||
nick[TOXIC_MAX_NAME_LENGTH] = L'\0';
|
||||
len = TOXIC_MAX_NAME_LENGTH;
|
||||
char nick[MAX_STR_SIZE];
|
||||
size_t len = 0;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
tox_set_name(m, nick, len+1);
|
||||
prompt_update_nick(prompt, nick, len+1);
|
||||
if (!valid_nick(nick)) {
|
||||
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_self_set_name(m, (uint8_t *) nick, len, NULL);
|
||||
prompt_update_nick(prompt, nick);
|
||||
|
||||
store_data(m, DATA_FILE);
|
||||
}
|
||||
@ -255,98 +490,112 @@ 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])
|
||||
{
|
||||
if (argc < 1) {
|
||||
wprintw(window, "Wrong number of arguments.\n");
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Input required.");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t *msg = argv[1];
|
||||
|
||||
if (msg[0] != '\"') {
|
||||
wprintw(window, "Note must be enclosed in quotes.\n");
|
||||
if (argv[1][0] != '\"') {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Note must be enclosed in quotes.");
|
||||
return;
|
||||
}
|
||||
|
||||
msg[strlen(++msg)-1] = L'\0';
|
||||
uint16_t len = strlen(msg) + 1;
|
||||
tox_set_status_message(m, msg, len);
|
||||
/* 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';
|
||||
|
||||
prompt_update_statusmessage(prompt, msg, len);
|
||||
prompt_update_statusmessage(prompt, m, msg);
|
||||
}
|
||||
|
||||
void cmd_prompt_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
wclear(window);
|
||||
wattron(window, COLOR_PAIR(CYAN) | A_BOLD);
|
||||
wprintw(window, "\n\nGlobal commands:\n");
|
||||
wattroff(window, COLOR_PAIR(CYAN) | A_BOLD);
|
||||
|
||||
wprintw(window, " /add <id> <msg> : Add friend with optional message\n");
|
||||
wprintw(window, " /accept <n> : Accept friend request\n");
|
||||
wprintw(window, " /connect <ip> <port> <key> : Manually connect to a DHT server\n");
|
||||
wprintw(window, " /status <type> <msg> : Set your status with optional note\n");
|
||||
wprintw(window, " /note <msg> : Set a personal note\n");
|
||||
wprintw(window, " /nick <nick> : Set your nickname\n");
|
||||
wprintw(window, " /groupchat : Create a group chat\n");
|
||||
wprintw(window, " /myid : Print your ID\n");
|
||||
wprintw(window, " /quit or /exit : Exit Toxic\n");
|
||||
wprintw(window, " /help : Print this message again\n");
|
||||
wprintw(window, " /clear : Clear the window\n");
|
||||
|
||||
wattron(window, COLOR_PAIR(CYAN) | A_BOLD);
|
||||
wprintw(window, " * Argument messages must be enclosed in quotation marks.\n");
|
||||
wprintw(window, " * Use ctrl-o and ctrl-p to navigate through the tabs.\n\n");
|
||||
wattroff(window, COLOR_PAIR(CYAN) | A_BOLD);
|
||||
help_init_menu(self);
|
||||
}
|
||||
|
||||
void cmd_quit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
exit_toxic(m);
|
||||
exit_toxic_success(m);
|
||||
}
|
||||
|
||||
void cmd_requests(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
if (FrndRequests.num_requests == 0) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend requests.");
|
||||
return;
|
||||
}
|
||||
|
||||
int i, j;
|
||||
int count = 0;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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])
|
||||
{
|
||||
uint8_t *msg = NULL;
|
||||
bool have_note = false;
|
||||
const char *errmsg;
|
||||
|
||||
lock_status ();
|
||||
|
||||
if (argc >= 2) {
|
||||
msg = argv[2];
|
||||
|
||||
if (msg[0] != '\"') {
|
||||
wprintw(window, "Note must be enclosed in quotes.\n");
|
||||
return;
|
||||
}
|
||||
} else if (argc != 1) {
|
||||
wprintw(window, "Wrong number of arguments.\n");
|
||||
return;
|
||||
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;
|
||||
}
|
||||
|
||||
char *status = argv[1];
|
||||
int len = strlen(status);
|
||||
char l_status[len+1];
|
||||
int i;
|
||||
const char *status_str = argv[1];
|
||||
TOX_USER_STATUS status;
|
||||
|
||||
for (i = 0; i <= len; ++i)
|
||||
l_status[i] = tolower(status[i]);
|
||||
|
||||
TOX_USERSTATUS status_kind;
|
||||
|
||||
if (!strcmp(l_status, "online"))
|
||||
status_kind = TOX_USERSTATUS_NONE;
|
||||
else if (!strcmp(l_status, "away"))
|
||||
status_kind = TOX_USERSTATUS_AWAY;
|
||||
else if (!strcmp(l_status, "busy"))
|
||||
status_kind = TOX_USERSTATUS_BUSY;
|
||||
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 {
|
||||
wprintw(window, "Invalid status. Valid statuses are: online, busy and away.\n");
|
||||
return;
|
||||
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_set_user_status(m, status_kind);
|
||||
prompt_update_status(prompt, status_kind);
|
||||
tox_self_set_status(m, status);
|
||||
prompt_update_status(prompt, status);
|
||||
|
||||
if (msg != NULL) {
|
||||
msg[strlen(++msg)-1] = L'\0'; /* remove opening and closing quotes */
|
||||
uint16_t len = strlen(msg) + 1;
|
||||
tox_set_status_message(m, msg, len);
|
||||
prompt_update_statusmessage(prompt, msg, len);
|
||||
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 ();
|
||||
}
|
||||
|
@ -1,15 +1,52 @@
|
||||
/*
|
||||
* Toxic -- Tox Curses Client
|
||||
/* global_commands.h
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#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]);
|
||||
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, const char *id_bin, const char *msg);
|
||||
|
||||
#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 /* AUDIO */
|
||||
|
||||
#endif /* #define GLOBAL_COMMANDS_H */
|
||||
|
79
src/group_commands.c
Normal file
79
src/group_commands.c
Normal 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);
|
||||
}
|
31
src/group_commands.h
Normal file
31
src/group_commands.h
Normal file
@ -0,0 +1,31 @@
|
||||
/* group_commands.h
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GROUP_COMMANDS_H
|
||||
#define GROUP_COMMANDS_H
|
||||
|
||||
#include "windows.h"
|
||||
#include "toxic.h"
|
||||
|
||||
void cmd_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
|
||||
#endif /* GROUP_COMMANDS_H */
|
991
src/groupchat.c
991
src/groupchat.c
File diff suppressed because it is too large
Load Diff
@ -1,18 +1,86 @@
|
||||
/*
|
||||
* Toxic -- Tox Curses Client
|
||||
/* groupchat.h
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#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 */
|
||||
uint8_t *peer_names;
|
||||
uint8_t *oldpeer_names;
|
||||
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;
|
||||
|
||||
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 */
|
||||
|
362
src/help.c
Normal file
362
src/help.c
Normal file
@ -0,0 +1,362 @@
|
||||
/* help.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 "windows.h"
|
||||
#include "toxic.h"
|
||||
#include "help.h"
|
||||
#include "misc_tools.h"
|
||||
|
||||
#define HELP_MENU_HEIGHT 9
|
||||
#define HELP_MENU_WIDTH 26
|
||||
|
||||
void help_init_menu(ToxWindow *self)
|
||||
{
|
||||
if (self->help->win)
|
||||
delwin(self->help->win);
|
||||
|
||||
int y2, x2;
|
||||
getmaxyx(self->window, y2, x2);
|
||||
|
||||
if (y2 < HELP_MENU_HEIGHT || x2 < HELP_MENU_WIDTH)
|
||||
return;
|
||||
|
||||
self->help->win = newwin(HELP_MENU_HEIGHT, HELP_MENU_WIDTH, 3, 3);
|
||||
self->help->active = true;
|
||||
self->help->type = HELP_MENU;
|
||||
}
|
||||
|
||||
static void help_exit(ToxWindow *self)
|
||||
{
|
||||
delwin(self->help->win);
|
||||
memset(self->help, 0, sizeof(Help));
|
||||
}
|
||||
|
||||
static void help_init_window(ToxWindow *self, int height, int width)
|
||||
{
|
||||
if (self->help->win)
|
||||
delwin(self->help->win);
|
||||
|
||||
int y2, x2;
|
||||
getmaxyx(stdscr, y2, x2);
|
||||
|
||||
height = MIN(height, y2);
|
||||
width = MIN(width, x2);
|
||||
|
||||
self->help->win = newwin(height, width, 0, 0);
|
||||
}
|
||||
|
||||
static void help_draw_menu(ToxWindow *self)
|
||||
{
|
||||
WINDOW *win = self->help->win;
|
||||
|
||||
wmove(win, 1, 1);
|
||||
|
||||
wattron(win, A_BOLD | COLOR_PAIR(RED));
|
||||
wprintw(win, " Help Menu\n");
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(RED));
|
||||
|
||||
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
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");
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
wprintw(win, "hat commands\n");
|
||||
|
||||
wprintw(win, " g");
|
||||
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
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");
|
||||
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
wprintw(win, "x");
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
wprintw(win, "it menu\n");
|
||||
|
||||
box(win, ACS_VLINE, ACS_HLINE);
|
||||
wrefresh(win);
|
||||
}
|
||||
|
||||
static void help_draw_bottom_menu(WINDOW *win)
|
||||
{
|
||||
int y2, x2;
|
||||
getmaxyx(win, y2, x2);
|
||||
(void) x2;
|
||||
|
||||
wmove(win, y2 - 2, 1);
|
||||
|
||||
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
wprintw(win, " m");
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
wprintw(win, "ain menu |");
|
||||
|
||||
wprintw(win, " e");
|
||||
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
wprintw(win, "x");
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
wprintw(win, "it");
|
||||
}
|
||||
|
||||
static void help_draw_global(ToxWindow *self)
|
||||
{
|
||||
WINDOW *win = self->help->win;
|
||||
|
||||
wmove(win, 1, 1);
|
||||
|
||||
wattron(win, A_BOLD | COLOR_PAIR(RED));
|
||||
wprintw(win, "Global Commands:\n");
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(RED));
|
||||
|
||||
wprintw(win, " /add <addr> <msg> : Add contact with optional message\n");
|
||||
wprintw(win, " /accept <id> : Accept friend request\n");
|
||||
wprintw(win, " /avatar <path> : Set a personal avatar\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, " /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 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 /* AUDIO */
|
||||
|
||||
help_draw_bottom_menu(win);
|
||||
|
||||
box(win, ACS_VLINE, ACS_HLINE);
|
||||
wrefresh(win);
|
||||
}
|
||||
|
||||
static void help_draw_chat(ToxWindow *self)
|
||||
{
|
||||
WINDOW *win = self->help->win;
|
||||
|
||||
wmove(win, 1, 1);
|
||||
|
||||
wattron(win, A_BOLD | COLOR_PAIR(RED));
|
||||
wprintw(win, "Chat Commands:\n");
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(RED));
|
||||
|
||||
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 <id> : Receive a file\n");
|
||||
wprintw(win, " /cancel <type> <id> : Cancel file transfer where type: in|out\n");
|
||||
|
||||
#ifdef AUDIO
|
||||
wattron(win, A_BOLD);
|
||||
wprintw(win, "\n Audio:\n");
|
||||
wattroff(win, A_BOLD);
|
||||
|
||||
wprintw(win, " /call : Audio 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 threshold\n");
|
||||
#endif /* AUDIO */
|
||||
|
||||
help_draw_bottom_menu(win);
|
||||
|
||||
box(win, ACS_VLINE, ACS_HLINE);
|
||||
wrefresh(win);
|
||||
}
|
||||
|
||||
static void help_draw_keys(ToxWindow *self)
|
||||
{
|
||||
WINDOW *win = self->help->win;
|
||||
|
||||
wmove(win, 1, 1);
|
||||
|
||||
wattron(win, A_BOLD | COLOR_PAIR(RED));
|
||||
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, " 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");
|
||||
|
||||
wattron(win, A_BOLD);
|
||||
wprintw(win, " Audio commands:\n");
|
||||
wattroff(win, A_BOLD);
|
||||
|
||||
wprintw(win, " /mute <type> : Mute active device where type: in | out\n");
|
||||
wprintw(win, " /sense <n> : VAD sensitivity threshold\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);
|
||||
|
||||
box(win, ACS_VLINE, ACS_HLINE);
|
||||
wrefresh(win);
|
||||
}
|
||||
|
||||
void help_onKey(ToxWindow *self, wint_t key)
|
||||
{
|
||||
switch(key) {
|
||||
case 'x':
|
||||
case T_KEY_ESC:
|
||||
help_exit(self);
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
#ifdef AUDIO
|
||||
help_init_window(self, 19, 80);
|
||||
#else
|
||||
help_init_window(self, 9, 80);
|
||||
#endif
|
||||
self->help->type = HELP_CHAT;
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
#ifdef AUDIO
|
||||
help_init_window(self, 24, 80);
|
||||
#else
|
||||
help_init_window(self, 20, 80);
|
||||
#endif
|
||||
self->help->type = HELP_GLOBAL;
|
||||
break;
|
||||
|
||||
#ifdef AUDIO /* remove if/when we add non-audio group commands */
|
||||
case 'r':
|
||||
help_init_window(self, 10, 80);
|
||||
self->help->type = HELP_GROUP;
|
||||
break;
|
||||
#endif
|
||||
|
||||
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;
|
||||
|
||||
case 'm':
|
||||
help_init_menu(self);
|
||||
self->help->type = HELP_MENU;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void help_onDraw(ToxWindow *self)
|
||||
{
|
||||
curs_set(0);
|
||||
|
||||
switch(self->help->type) {
|
||||
case HELP_MENU:
|
||||
help_draw_menu(self);
|
||||
return;
|
||||
|
||||
case HELP_CHAT:
|
||||
help_draw_chat(self);
|
||||
break;
|
||||
|
||||
case HELP_GLOBAL:
|
||||
help_draw_global(self);
|
||||
break;
|
||||
|
||||
case HELP_KEYS:
|
||||
help_draw_keys(self);
|
||||
break;
|
||||
|
||||
case HELP_CONTACTS:
|
||||
help_draw_contacts(self);
|
||||
break;
|
||||
|
||||
case HELP_GROUP:
|
||||
help_draw_group(self);
|
||||
break;
|
||||
}
|
||||
}
|
42
src/help.h
Normal file
42
src/help.h
Normal file
@ -0,0 +1,42 @@
|
||||
/* help.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 HELP_H
|
||||
#define HELP_H
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
typedef enum {
|
||||
HELP_MENU,
|
||||
HELP_GLOBAL,
|
||||
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 */
|
280
src/input.c
Normal file
280
src/input.c
Normal file
@ -0,0 +1,280 @@
|
||||
/* input.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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE /* needed for wcwidth() */
|
||||
#endif
|
||||
|
||||
#include <wchar.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#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)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
int cur_len = wcwidth(key);
|
||||
|
||||
/* this is the only place we need to do this check */
|
||||
if (cur_len == -1) {
|
||||
sound_notify(self, notif_error, 0, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (add_char_to_buf(ctx, key) == -1) {
|
||||
sound_notify(self, notif_error, 0, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (x + cur_len >= mx_x) {
|
||||
int s_len = wcwidth(ctx->line[ctx->start]);
|
||||
ctx->start += 1 + MAX(0, cur_len - s_len);
|
||||
}
|
||||
}
|
||||
|
||||
/* delete a char via backspace key from input field and buffer */
|
||||
static void input_backspace(ToxWindow *self, int x, int mx_x)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
if (del_char_buf_bck(ctx) == -1) {
|
||||
sound_notify(self, notif_error, 0, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
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->start = MAX(0, ctx->start - cur_len);
|
||||
}
|
||||
|
||||
/* delete a char via delete key from input field and buffer */
|
||||
static void input_delete(ToxWindow *self)
|
||||
{
|
||||
if (del_char_buf_frnt(self->chatwin) == -1)
|
||||
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)
|
||||
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)
|
||||
sound_notify(NULL, notif_error, NT_ALWAYS, NULL);
|
||||
}
|
||||
|
||||
static void input_yank(ToxWindow *self, int x, int mx_x)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
if (yank_buf(ctx) == -1) {
|
||||
sound_notify(self, notif_error, 0, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
int yank_cols = MAX(0, wcswidth(ctx->yank, ctx->yank_len));
|
||||
|
||||
if (x + yank_cols >= mx_x) {
|
||||
int rmdr = MAX(0, (x + yank_cols) - mx_x);
|
||||
int s_len = wcswidth(&ctx->line[ctx->start], rmdr);
|
||||
ctx->start += s_len + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* moves cursor/line position to end of line in input field and buffer */
|
||||
static void input_mv_end(ToxWindow *self, int y, int mx_x)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
ctx->pos = ctx->len;
|
||||
|
||||
int wlen = wcswidth(ctx->line, sizeof(ctx->line));
|
||||
ctx->start = MAX(0, 1 + (mx_x * (wlen / mx_x) - mx_x) + (wlen % mx_x));
|
||||
}
|
||||
|
||||
/* moves cursor/line position to start of line in input field and buffer */
|
||||
static void input_mv_home(ToxWindow *self)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
if (ctx->pos <= 0)
|
||||
return;
|
||||
|
||||
ctx->pos = 0;
|
||||
ctx->start = 0;
|
||||
}
|
||||
|
||||
/* moves cursor/line position left in input field and buffer */
|
||||
static void input_mv_left(ToxWindow *self, int x, int mx_x)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
if (ctx->pos <= 0)
|
||||
return;
|
||||
|
||||
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;
|
||||
|
||||
if (ctx->start && (x >= mx_x - cur_len))
|
||||
ctx->start = MAX(0, ctx->start - 1 + (s_len - cur_len));
|
||||
else if (ctx->start)
|
||||
ctx->start = MAX(0, ctx->start - cur_len);
|
||||
}
|
||||
|
||||
/* moves cursor/line position right in input field and buffer */
|
||||
static void input_mv_right(ToxWindow *self, int x, int mx_x)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
if (ctx->pos >= ctx->len)
|
||||
return;
|
||||
|
||||
++ctx->pos;
|
||||
|
||||
int cur_len = wcwidth(ctx->line[ctx->pos - 1]);
|
||||
|
||||
if (x + cur_len >= mx_x) {
|
||||
int s_len = wcwidth(ctx->line[ctx->start]);
|
||||
ctx->start += 1 + MAX(0, cur_len - s_len);
|
||||
}
|
||||
}
|
||||
|
||||
/* puts a line history item in input field and buffer */
|
||||
static void input_history(ToxWindow *self, wint_t key, int mx_x)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
fetch_hist_item(ctx, key);
|
||||
int wlen = wcswidth(ctx->line, sizeof(ctx->line));
|
||||
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.
|
||||
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)
|
||||
{
|
||||
bool match = true;
|
||||
|
||||
switch (key) {
|
||||
case 0x7f:
|
||||
case KEY_BACKSPACE:
|
||||
input_backspace(self, x, mx_x);
|
||||
break;
|
||||
|
||||
case KEY_DC:
|
||||
input_delete(self);
|
||||
break;
|
||||
|
||||
case T_KEY_DISCARD:
|
||||
input_discard(self);
|
||||
break;
|
||||
|
||||
case T_KEY_KILL:
|
||||
input_kill(self->chatwin);
|
||||
break;
|
||||
|
||||
case T_KEY_C_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);
|
||||
break;
|
||||
|
||||
case KEY_END:
|
||||
case T_KEY_C_E:
|
||||
input_mv_end(self, y, mx_x);
|
||||
break;
|
||||
|
||||
case KEY_LEFT:
|
||||
input_mv_left(self, x, mx_x);
|
||||
break;
|
||||
|
||||
case KEY_RIGHT:
|
||||
input_mv_right(self, x, mx_x);
|
||||
break;
|
||||
|
||||
case KEY_UP:
|
||||
case KEY_DOWN:
|
||||
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;
|
||||
}
|
33
src/input.h
Normal file
33
src/input.h
Normal file
@ -0,0 +1,33 @@
|
||||
/* input.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 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);
|
||||
|
||||
/* Handles non-printable input keys that behave the same for all types of chat windows.
|
||||
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 */
|
552
src/line_info.c
Normal file
552
src/line_info.c
Normal file
@ -0,0 +1,552 @@
|
||||
/* line_info.c
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#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;
|
||||
|
||||
void line_info_init(struct history *hst)
|
||||
{
|
||||
hst->line_root = calloc(1, sizeof(struct line_info));
|
||||
|
||||
if (hst->line_root == NULL)
|
||||
exit_toxic_err("failed in line_info_init", FATALERR_MEMORY);
|
||||
|
||||
hst->line_start = hst->line_root;
|
||||
hst->line_end = hst->line_start;
|
||||
hst->queue_sz = 0;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
if (line->prev == NULL)
|
||||
return;
|
||||
|
||||
int y2, x2;
|
||||
getmaxyx(self->window, y2, x2);
|
||||
|
||||
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);
|
||||
|
||||
int curlines = 0;
|
||||
int nxtlines = line->newlines + (line->len / (x2 - side_offst));
|
||||
|
||||
do {
|
||||
curlines += 1 + nxtlines;
|
||||
line = line->prev;
|
||||
nxtlines = line->newlines + (line->len / (x2 - side_offst));
|
||||
} while (line->prev && curlines + nxtlines < max_y);
|
||||
|
||||
hst->line_start = line;
|
||||
}
|
||||
|
||||
void line_info_cleanup(struct history *hst)
|
||||
{
|
||||
struct line_info *tmp1 = hst->line_root;
|
||||
|
||||
while (tmp1) {
|
||||
struct line_info *tmp2 = tmp1->next;
|
||||
free(tmp1);
|
||||
tmp1 = tmp2;
|
||||
}
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; i < hst->queue_sz; ++i) {
|
||||
if (hst->queue[i])
|
||||
free(hst->queue[i]);
|
||||
}
|
||||
|
||||
free(hst);
|
||||
}
|
||||
|
||||
/* moves root forward and frees previous root */
|
||||
static void line_info_root_fwd(struct history *hst)
|
||||
{
|
||||
struct line_info *tmp = hst->line_root->next;
|
||||
tmp->prev = NULL;
|
||||
|
||||
if (hst->line_start->prev == NULL) { /* if line_start is root move it forward as well */
|
||||
hst->line_start = hst->line_start->next;
|
||||
hst->line_start->prev = NULL;
|
||||
++hst->start_id;
|
||||
}
|
||||
|
||||
free(hst->line_root);
|
||||
hst->line_root = tmp;
|
||||
}
|
||||
|
||||
/* 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 *line = hst->queue[0];
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; i < hst->queue_sz; ++i)
|
||||
hst->queue[i] = hst->queue[i + 1];
|
||||
|
||||
--hst->queue_sz;
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
/* 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, ...)
|
||||
{
|
||||
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 IN_ACTION:
|
||||
case OUT_ACTION:
|
||||
len += strlen(user_settings->line_normal) + 2;
|
||||
break;
|
||||
|
||||
case IN_MSG:
|
||||
case OUT_MSG:
|
||||
len += strlen(user_settings->line_normal) + 3;
|
||||
break;
|
||||
|
||||
case CONNECTION:
|
||||
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;
|
||||
|
||||
default:
|
||||
len += 2;
|
||||
break;
|
||||
}
|
||||
|
||||
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; frmt_msg[i]; ++i) {
|
||||
if (frmt_msg[i] == '\n')
|
||||
++new_line->newlines;
|
||||
}
|
||||
}
|
||||
|
||||
if (timestr) {
|
||||
snprintf(new_line->timestr, sizeof(new_line->timestr), "%s", timestr);
|
||||
len += strlen(new_line->timestr) + 1;
|
||||
}
|
||||
|
||||
if (name1) {
|
||||
snprintf(new_line->name1, sizeof(new_line->name1), "%s", name1);
|
||||
len += strlen(new_line->name1);
|
||||
}
|
||||
|
||||
if (name2) {
|
||||
snprintf(new_line->name2, sizeof(new_line->name2), "%s", name2);
|
||||
len += strlen(new_line->name2);
|
||||
}
|
||||
|
||||
new_line->len = len;
|
||||
new_line->type = type;
|
||||
new_line->bold = bold;
|
||||
new_line->colour = colour;
|
||||
new_line->noread_flag = false;
|
||||
new_line->timestamp = get_unix_time();
|
||||
|
||||
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)
|
||||
{
|
||||
struct history *hst = self->chatwin->hst;
|
||||
struct line_info *line = line_info_ret_queue(hst);
|
||||
|
||||
if (line == NULL)
|
||||
return;
|
||||
|
||||
if (hst->start_id > user_settings->history_size)
|
||||
line_info_root_fwd(hst);
|
||||
|
||||
line->id = hst->line_end->id + 1;
|
||||
line->prev = hst->line_end;
|
||||
hst->line_end->next = line;
|
||||
hst->line_end = line;
|
||||
|
||||
int y, y2, x, x2;
|
||||
getmaxyx(self->window, y2, x2);
|
||||
getyx(self->chatwin->history, y, x);
|
||||
(void) x;
|
||||
|
||||
if (x2 <= SIDEBAR_WIDTH)
|
||||
return;
|
||||
|
||||
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;
|
||||
|
||||
/* move line_start forward proportionate to the number of new lines */
|
||||
if (y + lines - 1 >= max_y) {
|
||||
while (lines > 0 && hst->line_start->next) {
|
||||
lines -= 1 + hst->line_start->next->newlines + (hst->line_start->next->len / (x2 - offst));
|
||||
hst->line_start = hst->line_start->next;
|
||||
++hst->start_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#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;
|
||||
|
||||
if (ctx == NULL)
|
||||
return;
|
||||
|
||||
struct history *hst = ctx->hst;
|
||||
|
||||
/* Only allow one new item to be added to chat window per call to this function */
|
||||
line_info_check_queue(self);
|
||||
|
||||
WINDOW *win = ctx->history;
|
||||
wclear(win);
|
||||
int y2, x2;
|
||||
getmaxyx(self->window, y2, x2);
|
||||
|
||||
if (x2 <= SIDEBAR_WIDTH)
|
||||
return;
|
||||
|
||||
if (self->is_groupchat)
|
||||
wmove(win, 0, 0);
|
||||
else
|
||||
wmove(win, 2, 0);
|
||||
|
||||
struct line_info *line = hst->line_start->next;
|
||||
int numlines = 0;
|
||||
|
||||
while (line && numlines++ <= y2) {
|
||||
uint8_t type = line->type;
|
||||
|
||||
switch (type) {
|
||||
case OUT_MSG:
|
||||
case OUT_MSG_READ:
|
||||
case IN_MSG:
|
||||
wattron(win, COLOR_PAIR(BLUE));
|
||||
wprintw(win, "%s ", line->timestr);
|
||||
wattroff(win, COLOR_PAIR(BLUE));
|
||||
|
||||
int nameclr = GREEN;
|
||||
|
||||
if (line->colour)
|
||||
nameclr = line->colour;
|
||||
else if (type == IN_MSG)
|
||||
nameclr = CYAN;
|
||||
|
||||
wattron(win, COLOR_PAIR(nameclr));
|
||||
wprintw(win, "%s %s: ", user_settings->line_normal, line->name1);
|
||||
wattroff(win, COLOR_PAIR(nameclr));
|
||||
|
||||
if (line->msg[0] == '>')
|
||||
wattron(win, COLOR_PAIR(GREEN));
|
||||
|
||||
wprintw(win, "%s", line->msg);
|
||||
|
||||
if (line->msg[0] == '>')
|
||||
wattroff(win, COLOR_PAIR(GREEN));
|
||||
|
||||
if (type == OUT_MSG && timed_out(line->timestamp, get_unix_time(), 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 OUT_ACTION_READ:
|
||||
case OUT_ACTION:
|
||||
case IN_ACTION:
|
||||
wattron(win, COLOR_PAIR(BLUE));
|
||||
wprintw(win, "%s ", line->timestr);
|
||||
wattroff(win, COLOR_PAIR(BLUE));
|
||||
|
||||
wattron(win, COLOR_PAIR(YELLOW));
|
||||
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, get_unix_time(), 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->timestr[0]) {
|
||||
wattron(win, COLOR_PAIR(BLUE));
|
||||
wprintw(win, "%s ", line->timestr);
|
||||
wattroff(win, COLOR_PAIR(BLUE));
|
||||
}
|
||||
|
||||
if (line->bold)
|
||||
wattron(win, A_BOLD);
|
||||
|
||||
if (line->colour)
|
||||
wattron(win, COLOR_PAIR(line->colour));
|
||||
|
||||
wprintw(win, "%s\n", line->msg);
|
||||
|
||||
if (line->bold)
|
||||
wattroff(win, A_BOLD);
|
||||
|
||||
if (line->colour)
|
||||
wattroff(win, COLOR_PAIR(line->colour));
|
||||
|
||||
break;
|
||||
|
||||
case PROMPT:
|
||||
wattron(win, COLOR_PAIR(GREEN));
|
||||
wprintw(win, "$ ");
|
||||
wattroff(win, COLOR_PAIR(GREEN));
|
||||
|
||||
if (line->msg[0])
|
||||
wprintw(win, "%s", line->msg);
|
||||
|
||||
wprintw(win, "\n");
|
||||
break;
|
||||
|
||||
case CONNECTION:
|
||||
wattron(win, COLOR_PAIR(BLUE));
|
||||
wprintw(win, "%s ", line->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);
|
||||
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));
|
||||
|
||||
break;
|
||||
|
||||
case NAME_CHANGE:
|
||||
wattron(win, COLOR_PAIR(BLUE));
|
||||
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);
|
||||
wattroff(win, A_BOLD);
|
||||
|
||||
wprintw(win, "%s", line->msg);
|
||||
|
||||
wattron(win, A_BOLD);
|
||||
wprintw(win, "%s\n", line->name2);
|
||||
wattroff(win, A_BOLD);
|
||||
wattroff(win, COLOR_PAIR(MAGENTA));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
line = line->next;
|
||||
}
|
||||
|
||||
/* keep calling until queue is empty */
|
||||
if (hst->queue_sz > 0)
|
||||
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;
|
||||
|
||||
while (line) {
|
||||
if (line->id == id) {
|
||||
snprintf(line->msg, sizeof(line->msg), "%s", msg);
|
||||
return;
|
||||
}
|
||||
|
||||
line = line->prev;
|
||||
}
|
||||
}
|
||||
|
||||
/* static void line_info_goto_root(struct history *hst)
|
||||
{
|
||||
hst->line_start = hst->line_root;
|
||||
} */
|
||||
|
||||
static void line_info_scroll_up(struct history *hst)
|
||||
{
|
||||
if (hst->line_start->prev)
|
||||
hst->line_start = hst->line_start->prev;
|
||||
else 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 sound_notify(NULL, notif_error, NT_ALWAYS, NULL);
|
||||
}
|
||||
|
||||
static void line_info_page_up(ToxWindow *self, struct history *hst)
|
||||
{
|
||||
int x2, y2;
|
||||
getmaxyx(self->window, y2, x2);
|
||||
(void) x2;
|
||||
int jump_dist = y2 / 2;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < jump_dist && hst->line_start->prev; ++i)
|
||||
hst->line_start = hst->line_start->prev;
|
||||
}
|
||||
|
||||
static void line_info_page_down(ToxWindow *self, struct history *hst)
|
||||
{
|
||||
int x2, y2;
|
||||
getmaxyx(self->window, y2, x2);
|
||||
(void) x2;
|
||||
int jump_dist = y2 / 2;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < jump_dist && hst->line_start->next; ++i)
|
||||
hst->line_start = hst->line_start->next;
|
||||
}
|
||||
|
||||
bool line_info_onKey(ToxWindow *self, wint_t key)
|
||||
{
|
||||
struct history *hst = self->chatwin->hst;
|
||||
bool match = true;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void line_info_clear(struct history *hst)
|
||||
{
|
||||
hst->line_start = hst->line_end;
|
||||
hst->start_id = hst->line_start->id;
|
||||
}
|
99
src/line_info.h
Normal file
99
src/line_info.h
Normal file
@ -0,0 +1,99 @@
|
||||
/* line_info.h
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LINE_INFO_H
|
||||
#define LINE_INFO_H
|
||||
|
||||
#include "windows.h"
|
||||
#include "toxic.h"
|
||||
|
||||
#define MAX_HISTORY 100000
|
||||
#define MIN_HISTORY 40
|
||||
#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,
|
||||
CONNECTION,
|
||||
DISCONNECTION,
|
||||
NAME_CHANGE,
|
||||
} LINE_TYPE;
|
||||
|
||||
struct line_info {
|
||||
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 entire line */
|
||||
uint8_t newlines;
|
||||
|
||||
struct line_info *prev;
|
||||
struct line_info *next;
|
||||
};
|
||||
|
||||
/* Linked list containing chat history lines */
|
||||
struct history {
|
||||
struct line_info *line_root;
|
||||
struct line_info *line_start; /* the first line we want to start printing at */
|
||||
struct line_info *line_end;
|
||||
uint32_t start_id; /* keeps track of where line_start should be when at bottom of history */
|
||||
|
||||
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, 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);
|
||||
|
||||
/* frees all history lines */
|
||||
void line_info_cleanup(struct history *hst);
|
||||
|
||||
/* clears the screen (does not delete anything) */
|
||||
void line_info_clear(struct history *hst);
|
||||
|
||||
/* puts msg in specified line_info msg buffer */
|
||||
void line_info_set(ToxWindow *self, uint32_t id, 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 */
|
265
src/log.c
Normal file
265
src/log.c
Normal file
@ -0,0 +1,265 @@
|
||||
/* log.c
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "configdir.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "misc_tools.h"
|
||||
#include "log.h"
|
||||
#include "settings.h"
|
||||
#include "line_info.h"
|
||||
|
||||
extern struct user_settings *user_settings;
|
||||
|
||||
/* 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(name) + strlen(".log") + strlen("-") + strlen(namedash);
|
||||
path_len += strlen(set_path) ? *set_path : strlen(user_config_dir) + strlen(LOGDIR);
|
||||
|
||||
/* 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';
|
||||
|
||||
char other_id[32] = {0};
|
||||
|
||||
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 >= destsize) {
|
||||
free(user_config_dir);
|
||||
return -1;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
if (log->file == NULL) {
|
||||
log->log_on = false;
|
||||
return;
|
||||
}
|
||||
|
||||
char name_frmt[TOXIC_MAX_NAME_LENGTH + 3];
|
||||
|
||||
if (event)
|
||||
snprintf(name_frmt, sizeof(name_frmt), "* %s", name);
|
||||
else
|
||||
snprintf(name_frmt, sizeof(name_frmt), "%s:", name);
|
||||
|
||||
const char *t = user_settings->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)) {
|
||||
fflush(log->file);
|
||||
log->lastwrite = curtime;
|
||||
}
|
||||
}
|
||||
|
||||
void log_disable(struct chatlog *log)
|
||||
{
|
||||
if (log->file != NULL)
|
||||
fclose(log->file);
|
||||
|
||||
memset(log, 0, sizeof(struct chatlog));
|
||||
}
|
||||
|
||||
void log_enable(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype)
|
||||
{
|
||||
log->log_on = true;
|
||||
|
||||
if (log->file != NULL)
|
||||
return;
|
||||
|
||||
if (init_logging_session(name, selfkey, otherkey, log, logtype) == -1)
|
||||
log_disable(log);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
55
src/log.h
Normal file
55
src/log.h
Normal file
@ -0,0 +1,55 @@
|
||||
/* log.h
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LOG_H
|
||||
#define LOG_H
|
||||
|
||||
struct chatlog {
|
||||
FILE *file;
|
||||
uint64_t lastwrite;
|
||||
char path[MAX_STR_SIZE];
|
||||
bool log_on; /* specific to current chat window */
|
||||
};
|
||||
|
||||
enum {
|
||||
LOG_GROUP,
|
||||
LOG_PROMPT,
|
||||
LOG_CHAT,
|
||||
} LOG_TYPE;
|
||||
|
||||
/* formats/writes line to log file */
|
||||
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, 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);
|
||||
|
||||
/* 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 */
|
564
src/main.c
564
src/main.c
@ -1,564 +0,0 @@
|
||||
/*
|
||||
* Toxic -- Tox Curses Client
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifndef SIGWINCH
|
||||
#define SIGWINCH 28
|
||||
#endif
|
||||
|
||||
#include <curses.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <signal.h>
|
||||
#include <locale.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <direct.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <netdb.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
#include <tox/tox.h>
|
||||
|
||||
#include "configdir.h"
|
||||
#include "toxic_windows.h"
|
||||
#include "friendlist.h"
|
||||
#include "prompt.h"
|
||||
#include "misc_tools.h"
|
||||
|
||||
#ifndef PACKAGE_DATADIR
|
||||
#define PACKAGE_DATADIR "."
|
||||
#endif
|
||||
|
||||
/* Export for use in Callbacks */
|
||||
char *DATA_FILE = NULL;
|
||||
char *SRVLIST_FILE = NULL;
|
||||
ToxWindow *prompt = NULL;
|
||||
|
||||
FileSender file_senders[MAX_FILES];
|
||||
uint8_t max_file_senders_index;
|
||||
|
||||
void on_window_resize(int sig)
|
||||
{
|
||||
endwin();
|
||||
refresh();
|
||||
clear();
|
||||
}
|
||||
|
||||
static void init_term(void)
|
||||
{
|
||||
/* Setup terminal */
|
||||
signal(SIGWINCH, on_window_resize);
|
||||
#if HAVE_WIDECHAR
|
||||
if (setlocale(LC_ALL, "") == NULL) {
|
||||
fprintf(stderr, "Could not set your locale, plese check your locale settings or"
|
||||
"disable wide char support\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
#endif
|
||||
initscr();
|
||||
cbreak();
|
||||
keypad(stdscr, 1);
|
||||
noecho();
|
||||
timeout(100);
|
||||
|
||||
if (has_colors()) {
|
||||
start_color();
|
||||
init_pair(0, COLOR_WHITE, COLOR_BLACK);
|
||||
init_pair(1, COLOR_GREEN, COLOR_BLACK);
|
||||
init_pair(2, COLOR_CYAN, COLOR_BLACK);
|
||||
init_pair(3, COLOR_RED, COLOR_BLACK);
|
||||
init_pair(4, COLOR_BLUE, COLOR_BLACK);
|
||||
init_pair(5, COLOR_YELLOW, COLOR_BLACK);
|
||||
init_pair(6, COLOR_MAGENTA, COLOR_BLACK);
|
||||
init_pair(7, COLOR_BLACK, COLOR_BLACK);
|
||||
init_pair(8, COLOR_BLACK, COLOR_WHITE);
|
||||
}
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
static Tox *init_tox(int ipv4)
|
||||
{
|
||||
/* Init core */
|
||||
int ipv6 = !ipv4;
|
||||
Tox *m = tox_new(ipv6);
|
||||
|
||||
if (TOX_ENABLE_IPV6_DEFAULT && m == NULL) {
|
||||
fprintf(stderr, "IPv6 didn't initialize, trying IPv4 only\n");
|
||||
m = tox_new(0);
|
||||
}
|
||||
|
||||
if (m == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Callbacks */
|
||||
tox_callback_connection_status(m, on_connectionchange, NULL);
|
||||
tox_callback_typing_change(m, on_typing_change, NULL);
|
||||
tox_callback_friend_request(m, on_request, NULL);
|
||||
tox_callback_friend_message(m, on_message, NULL);
|
||||
tox_callback_name_change(m, on_nickchange, NULL);
|
||||
tox_callback_user_status(m, on_statuschange, NULL);
|
||||
tox_callback_status_message(m, on_statusmessagechange, NULL);
|
||||
tox_callback_friend_action(m, on_action, NULL);
|
||||
tox_callback_group_invite(m, on_groupinvite, NULL);
|
||||
tox_callback_group_message(m, on_groupmessage, NULL);
|
||||
tox_callback_group_action(m, on_groupaction, NULL);
|
||||
tox_callback_group_namelist_change(m, on_group_namelistchange, NULL);
|
||||
tox_callback_file_send_request(m, on_file_sendrequest, NULL);
|
||||
tox_callback_file_control(m, on_file_control, NULL);
|
||||
tox_callback_file_data(m, on_file_data, NULL);
|
||||
|
||||
#ifdef __linux__
|
||||
tox_set_name(m, (uint8_t *) "Cool guy", sizeof("Cool guy"));
|
||||
#elif defined(_WIN32)
|
||||
tox_set_name(m, (uint8_t *) "I should install GNU/Linux", sizeof("I should install GNU/Linux"));
|
||||
#elif defined(__APPLE__)
|
||||
tox_set_name(m, (uint8_t *) "Hipster", sizeof("Hipster")); //This used to users of other Unixes are hipsters
|
||||
#else
|
||||
tox_set_name(m, (uint8_t *) "Registered Minix user #4", sizeof("Registered Minix user #4"));
|
||||
#endif
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
#define MINLINE 50 /* IP: 7 + port: 5 + key: 38 + spaces: 2 = 70. ! (& e.g. tox.im = 6) */
|
||||
#define MAXLINE 256 /* Approx max number of chars in a sever line (name + port + key) */
|
||||
#define MAXSERVERS 50
|
||||
#define SERVERLEN (MAXLINE - TOX_CLIENT_ID_SIZE - 7)
|
||||
|
||||
static int linecnt = 0;
|
||||
static char servers[MAXSERVERS][SERVERLEN];
|
||||
static uint16_t ports[MAXSERVERS];
|
||||
static uint8_t keys[MAXSERVERS][TOX_CLIENT_ID_SIZE];
|
||||
|
||||
int serverlist_load(void)
|
||||
{
|
||||
FILE *fp = NULL;
|
||||
|
||||
fp = fopen(SRVLIST_FILE, "r");
|
||||
|
||||
if (fp == NULL)
|
||||
return 1;
|
||||
|
||||
char line[MAXLINE];
|
||||
while (fgets(line, sizeof(line), fp) && linecnt < MAXSERVERS) {
|
||||
if (strlen(line) > MINLINE) {
|
||||
char *name = strtok(line, " ");
|
||||
char *port = strtok(NULL, " ");
|
||||
char *key_ascii = strtok(NULL, " ");
|
||||
/* invalid line */
|
||||
if (name == NULL || port == NULL || key_ascii == NULL)
|
||||
continue;
|
||||
|
||||
strncpy(servers[linecnt], name, SERVERLEN);
|
||||
servers[linecnt][SERVERLEN - 1] = 0;
|
||||
ports[linecnt] = htons(atoi(port));
|
||||
|
||||
uint8_t *key_binary = hex_string_to_bin(key_ascii);
|
||||
memcpy(keys[linecnt], key_binary, TOX_CLIENT_ID_SIZE);
|
||||
free(key_binary);
|
||||
|
||||
linecnt++;
|
||||
}
|
||||
}
|
||||
|
||||
if (linecnt < 1) {
|
||||
fclose(fp);
|
||||
return 2;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int init_connection_helper(Tox *m, int linenumber)
|
||||
{
|
||||
return tox_bootstrap_from_address(m, servers[linenumber], TOX_ENABLE_IPV6_DEFAULT,
|
||||
ports[linenumber], keys[linenumber]);
|
||||
}
|
||||
|
||||
/* Connects to a random DHT server listed in the DHTservers file
|
||||
*
|
||||
* return codes:
|
||||
* 1: failed to open server file
|
||||
* 2: no line of sufficient length in server file
|
||||
* 3: (old, removed) failed to split a selected line in the server file
|
||||
* 4: failed to resolve name to IP
|
||||
* 5: serverlist file contains no acceptable line
|
||||
*/
|
||||
static int init_connection_serverlist_loaded = 0;
|
||||
int init_connection(Tox *m)
|
||||
{
|
||||
if (linecnt > 0) /* already loaded serverlist */
|
||||
return init_connection_helper(m, rand() % linecnt) ? 0 : 4;
|
||||
|
||||
/* only once:
|
||||
* - load the serverlist
|
||||
* - connect to "everyone" inside
|
||||
*/
|
||||
if (!init_connection_serverlist_loaded) {
|
||||
init_connection_serverlist_loaded = 1;
|
||||
int res = serverlist_load();
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
if (!linecnt)
|
||||
return 4;
|
||||
|
||||
res = 6;
|
||||
int linenumber;
|
||||
for(linenumber = 0; linenumber < linecnt; linenumber++)
|
||||
if (init_connection_helper(m, linenumber))
|
||||
res = 0;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* empty serverlist file */
|
||||
return 5;
|
||||
}
|
||||
|
||||
static void do_connection(Tox *m, ToxWindow *prompt)
|
||||
{
|
||||
static int conn_try = 0;
|
||||
static int conn_err = 0;
|
||||
static bool dht_on = false;
|
||||
|
||||
bool is_connected = tox_isconnected(m);
|
||||
|
||||
if (!dht_on && !is_connected && !(conn_try++ % 100)) {
|
||||
prep_prompt_win();
|
||||
if (!conn_err) {
|
||||
wprintw(prompt->window, "Establishing connection...\n");
|
||||
if ((conn_err = init_connection(m)))
|
||||
wprintw(prompt->window, "\nAuto-connect failed with error code %d\n", conn_err);
|
||||
}
|
||||
} else if (!dht_on && is_connected) {
|
||||
dht_on = true;
|
||||
prompt_update_connectionstatus(prompt, dht_on);
|
||||
|
||||
prep_prompt_win();
|
||||
wprintw(prompt->window, "\nDHT connected.\n");
|
||||
} else if (dht_on && !is_connected) {
|
||||
dht_on = false;
|
||||
prompt_update_connectionstatus(prompt, dht_on);
|
||||
|
||||
prep_prompt_win();
|
||||
wprintw(prompt->window, "\nDHT disconnected. Attempting to reconnect.\n");
|
||||
}
|
||||
}
|
||||
|
||||
int f_loadfromfile;
|
||||
|
||||
/*
|
||||
* Store Messenger to given location
|
||||
* Return 0 stored successfully
|
||||
* Return 1 file path is NULL
|
||||
* Return 2 malloc failed
|
||||
* Return 3 opening path failed
|
||||
* Return 4 fwrite failed
|
||||
*/
|
||||
int store_data(Tox *m, char *path)
|
||||
{
|
||||
if (f_loadfromfile == 0) /*If file loading/saving is disabled*/
|
||||
return 0;
|
||||
|
||||
if (path == NULL)
|
||||
return 1;
|
||||
|
||||
FILE *fd;
|
||||
size_t len;
|
||||
uint8_t *buf;
|
||||
|
||||
len = tox_size(m);
|
||||
buf = malloc(len);
|
||||
|
||||
if (buf == NULL)
|
||||
return 2;
|
||||
|
||||
tox_save(m, buf);
|
||||
|
||||
fd = fopen(path, "wb");
|
||||
|
||||
if (fd == NULL) {
|
||||
free(buf);
|
||||
return 3;
|
||||
}
|
||||
|
||||
if (fwrite(buf, len, 1, fd) != 1) {
|
||||
free(buf);
|
||||
fclose(fd);
|
||||
return 4;
|
||||
}
|
||||
|
||||
free(buf);
|
||||
fclose(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void load_data(Tox *m, char *path)
|
||||
{
|
||||
if (f_loadfromfile == 0) /*If file loading/saving is disabled*/
|
||||
return;
|
||||
|
||||
FILE *fd;
|
||||
size_t len;
|
||||
uint8_t *buf;
|
||||
|
||||
if ((fd = fopen(path, "rb")) != NULL) {
|
||||
fseek(fd, 0, SEEK_END);
|
||||
len = ftell(fd);
|
||||
fseek(fd, 0, SEEK_SET);
|
||||
|
||||
buf = malloc(len);
|
||||
|
||||
if (buf == NULL) {
|
||||
fclose(fd);
|
||||
endwin();
|
||||
fprintf(stderr, "malloc() failed. Aborting...\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (fread(buf, len, 1, fd) != 1) {
|
||||
free(buf);
|
||||
fclose(fd);
|
||||
endwin();
|
||||
fprintf(stderr, "fread() failed. Aborting...\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
tox_load(m, buf, len);
|
||||
|
||||
uint32_t i = 0;
|
||||
uint8_t name[TOX_MAX_NAME_LENGTH];
|
||||
|
||||
while (tox_get_name(m, i, name) != -1)
|
||||
on_friendadded(m, i++, false);
|
||||
|
||||
free(buf);
|
||||
fclose(fd);
|
||||
} else {
|
||||
int st;
|
||||
|
||||
if ((st = store_data(m, path)) != 0) {
|
||||
endwin();
|
||||
fprintf(stderr, "Store messenger failed with return code: %d\n", st);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void close_file_sender(int i)
|
||||
{
|
||||
fclose(file_senders[i].file);
|
||||
memset(&file_senders[i], 0, sizeof(FileSender));
|
||||
|
||||
int j;
|
||||
|
||||
for (j = max_file_senders_index; j > 0; --j) {
|
||||
if (file_senders[j-1].active)
|
||||
break;
|
||||
}
|
||||
|
||||
max_file_senders_index = j;
|
||||
}
|
||||
|
||||
static void do_file_senders(Tox *m)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < max_file_senders_index; ++i) {
|
||||
if (!file_senders[i].active)
|
||||
continue;
|
||||
|
||||
uint8_t *pathname = file_senders[i].pathname;
|
||||
uint8_t filenum = file_senders[i].filenum;
|
||||
int friendnum = file_senders[i].friendnum;
|
||||
FILE *fp = file_senders[i].file;
|
||||
uint64_t current_time = (uint64_t) time(NULL);
|
||||
|
||||
/* If file transfer has timed out kill transfer and send kill control */
|
||||
if (timed_out(file_senders[i].timestamp, current_time, TIMEOUT_FILESENDER)) {
|
||||
ChatContext *ctx = file_senders[i].toxwin->chatwin;
|
||||
|
||||
if (ctx != NULL) {
|
||||
wprintw(ctx->history, "File transfer for '%s' timed out.\n", pathname);
|
||||
alert_window(file_senders[i].toxwin, WINDOW_ALERT_2, true);
|
||||
}
|
||||
|
||||
tox_file_send_control(m, friendnum, 0, filenum, TOX_FILECONTROL_KILL, 0, 0);
|
||||
close_file_sender(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (tox_file_send_data(m, friendnum, filenum, file_senders[i].nextpiece,
|
||||
file_senders[i].piecelen) == -1)
|
||||
break;
|
||||
|
||||
file_senders[i].timestamp = current_time;
|
||||
file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1,
|
||||
tox_file_data_size(m, friendnum), fp);
|
||||
|
||||
if (file_senders[i].piecelen == 0) {
|
||||
ChatContext *ctx = file_senders[i].toxwin->chatwin;
|
||||
|
||||
if (ctx != NULL) {
|
||||
wprintw(ctx->history, "File '%s' successfuly sent.\n", pathname);
|
||||
alert_window(file_senders[i].toxwin, WINDOW_ALERT_2, true);
|
||||
}
|
||||
|
||||
tox_file_send_control(m, friendnum, 0, filenum, TOX_FILECONTROL_FINISHED, 0, 0);
|
||||
close_file_sender(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void exit_toxic(Tox *m)
|
||||
{
|
||||
store_data(m, DATA_FILE);
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; i < max_file_senders_index; ++i) {
|
||||
if (file_senders[i].active)
|
||||
fclose(file_senders[i].file);
|
||||
}
|
||||
|
||||
free(DATA_FILE);
|
||||
free(SRVLIST_FILE);
|
||||
free(prompt->stb);
|
||||
free(prompt->promptbuf);
|
||||
tox_kill(m);
|
||||
endwin();
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
static void do_toxic(Tox *m, ToxWindow *prompt)
|
||||
{
|
||||
do_connection(m, prompt);
|
||||
draw_active_window(m);
|
||||
do_file_senders(m);
|
||||
|
||||
/* main tox-core loop */
|
||||
tox_do(m);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char *user_config_dir = get_user_config_dir();
|
||||
int config_err = 0;
|
||||
|
||||
f_loadfromfile = 1;
|
||||
int f_flag = 0;
|
||||
int i = 0;
|
||||
int f_use_ipv4 = 0;
|
||||
|
||||
// Make sure all written files are read/writeable only by the current user.
|
||||
umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
|
||||
|
||||
for (i = 0; i < argc; ++i) {
|
||||
if (argv[i] == NULL)
|
||||
break;
|
||||
else if (argv[i][0] == '-') {
|
||||
if (argv[i][1] == 'f') {
|
||||
if (argv[i + 1] != NULL)
|
||||
DATA_FILE = strdup(argv[i + 1]);
|
||||
else
|
||||
f_flag = -1;
|
||||
} else if (argv[i][1] == 'n') {
|
||||
f_loadfromfile = 0;
|
||||
} else if (argv[i][1] == '4') {
|
||||
f_use_ipv4 = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
config_err = create_user_config_dir(user_config_dir);
|
||||
if (DATA_FILE == NULL ) {
|
||||
if (config_err) {
|
||||
DATA_FILE = strdup("data");
|
||||
} else {
|
||||
DATA_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen("data") + 1);
|
||||
if (DATA_FILE != NULL) {
|
||||
strcpy(DATA_FILE, user_config_dir);
|
||||
strcat(DATA_FILE, CONFIGDIR);
|
||||
strcat(DATA_FILE, "data");
|
||||
} else {
|
||||
endwin();
|
||||
fprintf(stderr, "malloc() failed. Aborting...\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (config_err) {
|
||||
SRVLIST_FILE = strdup(PACKAGE_DATADIR "/DHTservers");
|
||||
} else {
|
||||
SRVLIST_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen("DHTservers") + 1);
|
||||
if (SRVLIST_FILE != NULL) {
|
||||
strcpy(SRVLIST_FILE, user_config_dir);
|
||||
strcat(SRVLIST_FILE, CONFIGDIR);
|
||||
strcat(SRVLIST_FILE, "DHTservers");
|
||||
} else {
|
||||
endwin();
|
||||
fprintf(stderr, "malloc() failed. Aborting...\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
free(user_config_dir);
|
||||
|
||||
init_term();
|
||||
Tox *m = init_tox(f_use_ipv4);
|
||||
|
||||
if (m == NULL) {
|
||||
endwin();
|
||||
fprintf(stderr, "Failed to initialize network. Aborting...\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
prompt = init_windows(m);
|
||||
|
||||
if (f_loadfromfile)
|
||||
load_data(m, DATA_FILE);
|
||||
|
||||
if (f_flag == -1) {
|
||||
attron(COLOR_PAIR(RED) | A_BOLD);
|
||||
wprintw(prompt->window, "You passed '-f' without giving an argument.\n"
|
||||
"defaulting to 'data' for a keyfile...\n");
|
||||
attroff(COLOR_PAIR(RED) | A_BOLD);
|
||||
}
|
||||
|
||||
if (config_err) {
|
||||
attron(COLOR_PAIR(RED) | A_BOLD);
|
||||
wprintw(prompt->window, "Unable to determine configuration directory.\n"
|
||||
"defaulting to 'data' for a keyfile...\n");
|
||||
attroff(COLOR_PAIR(RED) | A_BOLD);
|
||||
}
|
||||
|
||||
prompt_init_statusbar(prompt, m);
|
||||
sort_friendlist_index(m);
|
||||
|
||||
while (true)
|
||||
do_toxic(m, prompt);
|
||||
|
||||
return 0;
|
||||
}
|
156
src/message_queue.c
Normal file
156
src/message_queue.c
Normal file
@ -0,0 +1,156 @@
|
||||
/* 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;
|
||||
|
||||
uint64_t curtime = get_unix_time();
|
||||
|
||||
if (msg->receipt != 0 && !timed_out(msg->last_send_try, curtime, 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 = curtime;
|
||||
msg->receipt = receipt;
|
||||
return;
|
||||
}
|
51
src/message_queue.h
Normal file
51
src/message_queue.h
Normal 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 */
|
456
src/misc_tools.c
456
src/misc_tools.c
@ -1,65 +1,157 @@
|
||||
/*
|
||||
* Toxic -- Tox Curses Client
|
||||
/* misc_tools.c
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <limits.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "toxic_windows.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;
|
||||
|
||||
// XXX: FIX
|
||||
unsigned char *hex_string_to_bin(char hex_string[])
|
||||
static uint64_t current_unix_time;
|
||||
|
||||
void hst_to_net(uint8_t *num, uint16_t numbytes)
|
||||
{
|
||||
size_t len = strlen(hex_string);
|
||||
unsigned char *val = malloc(len);
|
||||
#ifndef WORDS_BIGENDIAN
|
||||
uint32_t i;
|
||||
uint8_t buff[numbytes];
|
||||
|
||||
if (val == NULL) {
|
||||
endwin();
|
||||
fprintf(stderr, "malloc() failed. Aborting...\n");
|
||||
exit(EXIT_FAILURE);
|
||||
for (i = 0; i < numbytes; ++i) {
|
||||
buff[i] = num[numbytes - i - 1];
|
||||
}
|
||||
|
||||
char *pos = hex_string;
|
||||
size_t i;
|
||||
memcpy(num, buff, numbytes);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; ++i, pos += 2)
|
||||
sscanf(pos, "%2hhx", &val[i]);
|
||||
void update_unix_time(void)
|
||||
{
|
||||
current_unix_time = (uint64_t) time(NULL);
|
||||
}
|
||||
|
||||
return val;
|
||||
uint64_t get_unix_time(void)
|
||||
{
|
||||
return current_unix_time;
|
||||
}
|
||||
|
||||
/* Returns 1 if connection has timed out, 0 otherwise */
|
||||
int timed_out(uint64_t timestamp, uint64_t curtime, uint64_t timeout)
|
||||
{
|
||||
return timestamp + timeout <= curtime;
|
||||
}
|
||||
|
||||
/* Get the current local time */
|
||||
struct tm *get_time(void)
|
||||
{
|
||||
struct tm *timeinfo;
|
||||
time_t now;
|
||||
time(&now);
|
||||
timeinfo = localtime(&now);
|
||||
uint64_t t = get_unix_time();
|
||||
timeinfo = localtime((const time_t*) &t);
|
||||
return timeinfo;
|
||||
}
|
||||
|
||||
/* Prints the time to given window */
|
||||
void print_time(WINDOW *window)
|
||||
/*Puts the current time in buf in the format of [HH:mm:ss] */
|
||||
void get_time_str(char *buf, int bufsize)
|
||||
{
|
||||
struct tm *timeinfo = get_time();
|
||||
if (user_settings->timestamps == TIMESTAMPS_OFF) {
|
||||
buf[0] = '\0';
|
||||
return;
|
||||
}
|
||||
|
||||
wattron(window, COLOR_PAIR(BLUE));
|
||||
wprintw(window, "[%02d:%02d:%02d] ", timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec);
|
||||
wattroff(window,COLOR_PAIR(BLUE));
|
||||
const char *t = user_settings->timestamp_format;
|
||||
strftime(buf, bufsize, t, get_time());
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
if (!secs)
|
||||
return;
|
||||
|
||||
long int seconds = secs % 60;
|
||||
long int minutes = (secs % 3600) / 60;
|
||||
long int hours = secs / 3600;
|
||||
|
||||
if (!minutes && !hours)
|
||||
snprintf(buf, bufsize, "%.2ld", seconds);
|
||||
else if (!hours)
|
||||
snprintf(buf, bufsize, "%ld:%.2ld", minutes, seconds);
|
||||
else
|
||||
snprintf(buf, bufsize, "%ld:%.2ld:%.2ld", hours, minutes, seconds);
|
||||
}
|
||||
|
||||
char *hex_string_to_bin(const char *hex_string)
|
||||
{
|
||||
size_t len = strlen(hex_string);
|
||||
char *val = malloc(len);
|
||||
|
||||
if (val == NULL)
|
||||
exit_toxic_err("failed in hex_string_to_bin", FATALERR_MEMORY);
|
||||
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < len; ++i, hex_string += 2)
|
||||
sscanf(hex_string, "%2hhx", &val[i]);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
int hex_string_to_bytes(char *buf, int size, const char *keystr)
|
||||
{
|
||||
if (size % 2 != 0)
|
||||
return -1;
|
||||
|
||||
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)
|
||||
{
|
||||
return string[0] == '\0';
|
||||
}
|
||||
|
||||
/* convert a multibyte string to a wide character string (must provide buffer) */
|
||||
int mbs_to_wcs_buf(wchar_t *buf, const uint8_t *string, size_t n)
|
||||
/* convert a multibyte string to a wide character string and puts in buf. */
|
||||
int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n)
|
||||
{
|
||||
size_t len = mbstowcs(NULL, string, 0) + 1;
|
||||
|
||||
@ -72,9 +164,8 @@ int mbs_to_wcs_buf(wchar_t *buf, const uint8_t *string, size_t n)
|
||||
return len;
|
||||
}
|
||||
|
||||
/* converts wide character string into a multibyte string.
|
||||
Same thing as wcs_to_mbs() but caller must provide its own buffer */
|
||||
int wcs_to_mbs_buf(uint8_t *buf, const wchar_t *string, size_t n)
|
||||
/* converts wide character string into a multibyte string and puts in buf. */
|
||||
int wcs_to_mbs_buf(char *buf, const wchar_t *string, size_t n)
|
||||
{
|
||||
size_t len = wcstombs(NULL, string, 0) + 1;
|
||||
|
||||
@ -87,132 +178,235 @@ int wcs_to_mbs_buf(uint8_t *buf, const wchar_t *string, size_t n)
|
||||
return len;
|
||||
}
|
||||
|
||||
/* convert wide characters to multibyte string: string returned must be free'd */
|
||||
uint8_t *wcs_to_mbs(wchar_t *string)
|
||||
{
|
||||
uint8_t *ret = NULL;
|
||||
size_t len = wcstombs(NULL, string, 0);
|
||||
|
||||
if (len != (size_t) -1) {
|
||||
ret = malloc(++len);
|
||||
|
||||
if (ret != NULL) {
|
||||
if (wcstombs(ret, string, len) == (size_t) -1)
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
ret = malloc(2);
|
||||
|
||||
if (ret != NULL) {
|
||||
ret[0] = ' ';
|
||||
ret[1] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == NULL) {
|
||||
endwin();
|
||||
fprintf(stderr, "malloc() failed. Aborting...\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* convert a wide char to multibyte string */
|
||||
char *wc_to_char(wchar_t ch)
|
||||
{
|
||||
static char ret[MB_LEN_MAX + 1];
|
||||
int len = wctomb(ret, ch);
|
||||
|
||||
if (len == -1) {
|
||||
ret[0] = ' ';
|
||||
ret[1] = '\0';
|
||||
} else {
|
||||
ret[len] = '\0';
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Returns true if connection has timed out, false otherwise */
|
||||
bool timed_out(uint64_t timestamp, uint64_t curtime, uint64_t timeout)
|
||||
{
|
||||
return timestamp + timeout <= curtime;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
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 true if nick is valid. A valid toxic nick:
|
||||
/* Returns 1 if nick is valid, 0 if not. A valid toxic nick:
|
||||
- cannot be empty
|
||||
- cannot start with a space
|
||||
- must not contain contiguous spaces */
|
||||
bool valid_nick(uint8_t *nick)
|
||||
- must not contain a forward slash (for logfile naming purposes)
|
||||
- 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 false;
|
||||
return 0;
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; nick[i]; ++i) {
|
||||
if (nick[i] == ' ' && nick[i+1] == ' ')
|
||||
return false;
|
||||
if ((nick[i] == ' ' && nick[i + 1] == ' ')
|
||||
|| nick[i] == '/'
|
||||
|| nick[i] == '\n'
|
||||
|| nick[i] == '\t'
|
||||
|| nick[i] == '\v'
|
||||
|| nick[i] == '\r')
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Moves cursor to the end of the line in given window */
|
||||
void mv_curs_end(WINDOW *w, size_t len, int max_y, int max_x)
|
||||
/* 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 end_y = (len / max_x) + (max_y - CURS_Y_OFFSET);
|
||||
int end_x = len % max_x;
|
||||
wmove(w, end_y, end_x);
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < len; ++i) {
|
||||
if (str[i] == '\n' || str[i] == '\r' || str[i] == '\t' || str[i] == '\v')
|
||||
str[i] = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
/* gets base file name from path or original file name if no path is supplied */
|
||||
void get_file_name(uint8_t *pathname, uint8_t *namebuf)
|
||||
void get_file_name(char *namebuf, int bufsize, const char *pathname)
|
||||
{
|
||||
int idx = strlen(pathname) - 1;
|
||||
char *path = strdup(pathname);
|
||||
|
||||
if (path == NULL)
|
||||
exit_toxic_err("failed in get_file_name", FATALERR_MEMORY);
|
||||
|
||||
while (idx >= 0 && pathname[idx] == '/')
|
||||
pathname[idx--] = '\0';
|
||||
path[idx--] = '\0';
|
||||
|
||||
uint8_t *filename = strrchr(pathname, '/'); // Try unix style paths
|
||||
|
||||
if (filename != NULL) {
|
||||
if (!strlen(++filename))
|
||||
filename = pathname;
|
||||
} else {
|
||||
filename = strrchr(pathname, '\\'); // Try windows style paths
|
||||
char *finalname = strdup(path);
|
||||
|
||||
if (filename == NULL)
|
||||
filename = pathname;
|
||||
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, MAX_STR_SIZE, "%s", filename);
|
||||
snprintf(namebuf, bufsize, "%s", finalname);
|
||||
free(finalname);
|
||||
free(path);
|
||||
}
|
||||
|
||||
/* converts str to all lowercase */
|
||||
void str_to_lower(char *str)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; str[i]; ++i)
|
||||
str[i] = tolower(str[i]);
|
||||
}
|
||||
|
||||
/* puts friendnum's nick in buf, truncating at TOXIC_MAX_NAME_LENGTH if necessary.
|
||||
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)
|
||||
{
|
||||
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 or 0 on error */
|
||||
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);
|
||||
}
|
||||
|
140
src/misc_tools.h
140
src/misc_tools.h
@ -1,52 +1,140 @@
|
||||
/*
|
||||
* Toxic -- Tox Curses Client
|
||||
/* misc_tools.h
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#ifndef MISC_TOOLS_H
|
||||
#define MISC_TOOLS_H
|
||||
|
||||
// #define MIN(x, y) (((x) < (y)) ? (x) : (y))
|
||||
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "windows.h"
|
||||
#include "toxic.h"
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
|
||||
#endif
|
||||
|
||||
#ifndef MAX
|
||||
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
|
||||
#endif
|
||||
|
||||
#ifndef net_to_host
|
||||
#define net_to_host(x, y) hst_to_net(x, y)
|
||||
#endif
|
||||
|
||||
void hst_to_net(uint8_t *num, uint16_t numbytes);
|
||||
|
||||
/* convert a hex string to binary */
|
||||
unsigned char *hex_string_to_bin(char hex_string[]);
|
||||
char *hex_string_to_bin(const char *hex_string);
|
||||
|
||||
/* 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 */
|
||||
uint64_t get_unix_time(void);
|
||||
|
||||
/* Puts the current time in buf in the format of [HH:mm:ss] */
|
||||
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 */
|
||||
struct tm *get_time(void);
|
||||
|
||||
/* Prints the time to given window */
|
||||
void print_time(WINDOW *window);
|
||||
/* updates current unix time (should be run once per do_toxic loop) */
|
||||
void update_unix_time(void);
|
||||
|
||||
/* Returns 1 if the string is empty, 0 otherwise */
|
||||
int string_is_empty(char *string);
|
||||
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 uint8_t *string, size_t n);
|
||||
int char_to_wcs_buf(wchar_t *buf, const char *string, size_t n);
|
||||
|
||||
/* converts wide character string into a multibyte string.
|
||||
Same thing as wcs_to_mbs() but caller must provide its own buffer */
|
||||
int wcs_to_mbs_buf(uint8_t *buf, const wchar_t *string, size_t n);
|
||||
/* converts wide character string into a multibyte string and puts in buf. */
|
||||
int wcs_to_mbs_buf(char *buf, const wchar_t *string, size_t n);
|
||||
|
||||
/* convert wide characters to multibyte string: string returned must be free'd */
|
||||
uint8_t *wcs_to_mbs(wchar_t *string);
|
||||
/* convert a multibyte string to a wide character string and puts in buf) */
|
||||
int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n);
|
||||
|
||||
/* convert a wide char to multibyte char */
|
||||
char *wc_to_char(wchar_t ch);
|
||||
|
||||
/* Returns true if connection has timed out, false otherwise */
|
||||
bool timed_out(uint64_t timestamp, uint64_t timeout, uint64_t curtime);
|
||||
/* Returns 1 if connection has timed out, 0 otherwise */
|
||||
int timed_out(uint64_t timestamp, uint64_t timeout, uint64_t curtime);
|
||||
|
||||
/* Colours the window tab according to type. Beeps if is_beep is true */
|
||||
void alert_window(ToxWindow *self, int type, bool is_beep);
|
||||
|
||||
/* 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 true if nick is valid. A valid toxic nick:
|
||||
/* Returns 1 if nick is valid, 0 if not. A valid toxic nick:
|
||||
- cannot be empty
|
||||
- cannot start with a space
|
||||
- must not contain contiguous spaces */
|
||||
bool valid_nick(uint8_t *nick);
|
||||
- must not contain a forward slash (for logfile naming purposes)
|
||||
- must not contain contiguous spaces
|
||||
- must not contain a newline or tab seqeunce */
|
||||
int valid_nick(const char *nick);
|
||||
|
||||
/* Moves the cursor to the end of the line in given window */
|
||||
void mv_curs_end(WINDOW *w, size_t len, int max_y, int max_x);
|
||||
/* 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(uint8_t *pathname, uint8_t *namebuf);
|
||||
void get_file_name(char *namebuf, int 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 */
|
||||
size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum);
|
||||
|
||||
/* 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 or 0 on error */
|
||||
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 */
|
||||
|
774
src/notify.c
Normal file
774
src/notify.c
Normal file
@ -0,0 +1,774 @@
|
||||
/* 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[24];
|
||||
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)
|
||||
{
|
||||
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;
|
||||
|
||||
bool m_open_device()
|
||||
{
|
||||
last_opened_update = get_unix_time();
|
||||
|
||||
if (device_opened) return true;
|
||||
|
||||
/* Blah error check */
|
||||
open_primary_device(output, &Control.device_idx, 48000, 20, 1);
|
||||
|
||||
return (device_opened = true);
|
||||
}
|
||||
|
||||
bool m_close_device()
|
||||
{
|
||||
if (!device_opened) return true;
|
||||
|
||||
close_device(output, Control.device_idx);
|
||||
|
||||
return !(device_opened = false);
|
||||
}
|
||||
|
||||
/* Terminate all sounds but wait for them to finish first */
|
||||
void graceful_clear()
|
||||
{
|
||||
int i;
|
||||
control_lock();
|
||||
while (1) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void* do_playing(void* _p)
|
||||
{
|
||||
(void)_p;
|
||||
int i;
|
||||
|
||||
bool has_looping = false;
|
||||
|
||||
while(Control.poll_active) {
|
||||
control_lock();
|
||||
|
||||
|
||||
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 && get_unix_time() >= 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 &&
|
||||
(get_unix_time() - 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;
|
||||
int i;
|
||||
while(Control.poll_active) {
|
||||
control_lock();
|
||||
for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
|
||||
if (actives[i].box && get_unix_time() >= 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;
|
||||
|
||||
pthread_t thread;
|
||||
|
||||
if (pthread_create(&thread, NULL, do_playing, NULL) != 0 || pthread_detach(thread) != 0 ) {
|
||||
pthread_mutex_destroy(Control.poll_mutex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
Control.poll_active = 1;
|
||||
#endif
|
||||
Control.cooldown = get_unix_time() + 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)
|
||||
if ( !Control.poll_active ) return;
|
||||
Control.poll_active = 0;
|
||||
|
||||
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) && user_settings->alerts == ALERTS_ENABLED)
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
|
||||
strncpy(actives[id].title, title, 24);
|
||||
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
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
strncpy(actives[id].title, title, 24);
|
||||
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
80
src/notify.h
Normal 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 */
|
637
src/prompt.c
637
src/prompt.c
@ -1,106 +1,162 @@
|
||||
/*
|
||||
* Toxic -- Tox Curses Client
|
||||
/* prompt.c
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE /* needed for wcswidth() */
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include "toxic_windows.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "prompt.h"
|
||||
#include "execute.h"
|
||||
#include "misc_tools.h"
|
||||
#include "toxic_strings.h"
|
||||
#include "log.h"
|
||||
#include "line_info.h"
|
||||
#include "settings.h"
|
||||
#include "input.h"
|
||||
#include "help.h"
|
||||
#include "notify.h"
|
||||
#include "autocomplete.h"
|
||||
|
||||
uint8_t pending_frnd_requests[MAX_FRIENDS_NUM][TOX_CLIENT_ID_SIZE] = {0};
|
||||
uint8_t num_frnd_requests = 0;
|
||||
extern ToxWindow *prompt;
|
||||
extern struct user_settings *user_settings;
|
||||
extern struct Winthread Winthread;
|
||||
|
||||
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 uint8_t 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 AUDIO
|
||||
|
||||
{ "/lsdev" },
|
||||
{ "/sdev" },
|
||||
|
||||
#endif /* AUDIO */
|
||||
};
|
||||
|
||||
/* prevents input string from eating system messages: call this prior to printing a prompt message
|
||||
TODO: This is only a partial fix */
|
||||
void prep_prompt_win(void)
|
||||
void kill_prompt_window(ToxWindow *self)
|
||||
{
|
||||
PromptBuf *prt = prompt->promptbuf;
|
||||
ChatContext *ctx = self->chatwin;
|
||||
StatusBar *statusbar = self->stb;
|
||||
|
||||
if (prt->len <= 0)
|
||||
return;
|
||||
log_disable(ctx->log);
|
||||
line_info_cleanup(ctx->hst);
|
||||
|
||||
wprintw(prompt->window, "\n");
|
||||
delwin(ctx->linewin);
|
||||
delwin(ctx->history);
|
||||
delwin(statusbar->topline);
|
||||
|
||||
if (!prt->at_bottom) {
|
||||
wmove(prompt->window, prt->orig_y - 1, X_OFST);
|
||||
++prt->orig_y;
|
||||
} else {
|
||||
wmove(prompt->window, prt->orig_y - 2, X_OFST);
|
||||
}
|
||||
free(ctx->log);
|
||||
free(ctx);
|
||||
free(self->help);
|
||||
free(statusbar);
|
||||
|
||||
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, uint8_t *nick, uint16_t len)
|
||||
void prompt_update_nick(ToxWindow *prompt, const char *nick)
|
||||
{
|
||||
StatusBar *statusbar = prompt->stb;
|
||||
snprintf(statusbar->nick, sizeof(statusbar->nick), "%s", nick);
|
||||
statusbar->nick_len = len;
|
||||
statusbar->nick_len = strlen(statusbar->nick);
|
||||
}
|
||||
|
||||
/* Updates own statusmessage in prompt statusbar */
|
||||
void prompt_update_statusmessage(ToxWindow *prompt, uint8_t *statusmsg, uint16_t len)
|
||||
/* 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);
|
||||
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, TOX_USERSTATUS 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)
|
||||
/* Adds friend request to pending friend requests.
|
||||
Returns request number on success, -1 if queue is full. */
|
||||
static int add_friend_request(const char *public_key, const char *data)
|
||||
{
|
||||
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(uint8_t *public_key)
|
||||
{
|
||||
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;
|
||||
}
|
||||
@ -109,330 +165,321 @@ static int add_friend_request(uint8_t *public_key)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key)
|
||||
static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
||||
{
|
||||
PromptBuf *prt = self->promptbuf;
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
int x, y, y2, x2;
|
||||
getyx(self->window, y, x);
|
||||
getmaxyx(self->window, y2, x2);
|
||||
|
||||
/* BACKSPACE key: Remove one character from line */
|
||||
if (key == 0x107 || key == 0x8 || key == 0x7f) {
|
||||
if (prt->pos > 0) {
|
||||
del_char_buf_bck(prt->line, &prt->pos, &prt->len);
|
||||
wmove(self->window, y, x-1); /* not necessary but fixes a display glitch */
|
||||
prt->scroll = false;
|
||||
} else {
|
||||
beep();
|
||||
}
|
||||
if (x2 <= 0)
|
||||
return;
|
||||
|
||||
/* ignore non-menu related input if active */
|
||||
if (self->help->active) {
|
||||
help_onKey(self, key);
|
||||
return;
|
||||
}
|
||||
|
||||
else if (key == KEY_DC) { /* DEL key: Remove character at pos */
|
||||
if (prt->pos != prt->len) {
|
||||
del_char_buf_frnt(prt->line, &prt->pos, &prt->len);
|
||||
prt->scroll = false;
|
||||
} else {
|
||||
beep();
|
||||
}
|
||||
if (ltr) { /* char is printable */
|
||||
input_new_char(self, key, x, y, x2, y2);
|
||||
return;
|
||||
}
|
||||
|
||||
else if (key == T_KEY_DISCARD) { /* CTRL-U: Delete entire line behind pos */
|
||||
if (prt->pos > 0) {
|
||||
wmove(self->window, prt->orig_y, X_OFST);
|
||||
wclrtobot(self->window);
|
||||
discard_buf(prt->line, &prt->pos, &prt->len);
|
||||
} else {
|
||||
beep();
|
||||
}
|
||||
}
|
||||
if (line_info_onKey(self, key))
|
||||
return;
|
||||
|
||||
else if (key == T_KEY_KILL) { /* CTRL-K: Delete entire line in front of pos */
|
||||
if (prt->len != prt->pos)
|
||||
kill_buf(prt->line, &prt->pos, &prt->len);
|
||||
else
|
||||
beep();
|
||||
}
|
||||
input_handle(self, key, x, y, x2, y2);
|
||||
|
||||
else if (key == KEY_HOME) { /* HOME key: Move cursor to beginning of line */
|
||||
if (prt->pos != 0)
|
||||
prt->pos = 0;
|
||||
}
|
||||
if (key == '\t') { /* TAB key: auto-completes command */
|
||||
if (ctx->len > 1 && ctx->line[0] == '/') {
|
||||
int diff = -1;
|
||||
|
||||
else if (key == KEY_END) { /* END key: move cursor to end of line */
|
||||
if (prt->pos != prt->len)
|
||||
prt->pos = prt->len;
|
||||
}
|
||||
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);
|
||||
|
||||
else if (key == KEY_LEFT) {
|
||||
if (prt->pos > 0)
|
||||
--prt->pos;
|
||||
else
|
||||
beep();
|
||||
}
|
||||
|
||||
else if (key == KEY_RIGHT) {
|
||||
if (prt->pos < prt->len)
|
||||
++prt->pos;
|
||||
else
|
||||
beep();
|
||||
}
|
||||
|
||||
else if (key == KEY_UP) { /* fetches previous item in history */
|
||||
wmove(self->window, prt->orig_y, X_OFST);
|
||||
fetch_hist_item(prt->line, &prt->pos, &prt->len, prt->ln_history, prt->hst_tot,
|
||||
&prt->hst_pos, LN_HIST_MV_UP);
|
||||
|
||||
/* adjust line y origin appropriately when window scrolls down */
|
||||
if (prt->at_bottom && prt->len >= x2 - X_OFST) {
|
||||
int px2 = prt->len >= x2 ? x2 : x2 - X_OFST;
|
||||
int p_ofst = px2 != x2 ? 0 : X_OFST;
|
||||
|
||||
if (px2 <= 0)
|
||||
return;
|
||||
|
||||
int k = prt->orig_y + ((prt->len + p_ofst) / px2);
|
||||
|
||||
if (k >= y2) {
|
||||
wprintw(self->window, "\n");
|
||||
--prt->orig_y;
|
||||
if (diff != -1) {
|
||||
if (x + diff > x2 - 1) {
|
||||
int wlen = wcswidth(ctx->line, sizeof(ctx->line));
|
||||
ctx->start = wlen < x2 ? 0 : wlen - x2 + 1;
|
||||
}
|
||||
} else {
|
||||
sound_notify(self, notif_error, 0, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (key == KEY_DOWN) { /* fetches next item in history */
|
||||
wmove(self->window, prt->orig_y, X_OFST);
|
||||
fetch_hist_item(prt->line, &prt->pos, &prt->len, prt->ln_history, prt->hst_tot,
|
||||
&prt->hst_pos, LN_HIST_MV_DWN);
|
||||
}
|
||||
|
||||
else if (key == '\t') { /* TAB key: completes command */
|
||||
if (prt->len > 1 && prt->line[0] == '/') {
|
||||
if (complete_line(prt->line, &prt->pos, &prt->len, glob_cmd_list, AC_NUM_GLOB_COMMANDS,
|
||||
MAX_CMDNAME_SIZE) == -1)
|
||||
beep();
|
||||
} else {
|
||||
beep();
|
||||
sound_notify(self, notif_error, 0, NULL);
|
||||
}
|
||||
}
|
||||
} else if (key == '\n') {
|
||||
rm_trailing_spaces_buf(ctx);
|
||||
|
||||
else
|
||||
#if HAVE_WIDECHAR
|
||||
if (iswprint(key))
|
||||
#else
|
||||
if (isprint(key))
|
||||
#endif
|
||||
{
|
||||
if (prt->len < (MAX_STR_SIZE-1)) {
|
||||
add_char_to_buf(prt->line, &prt->pos, &prt->len, key);
|
||||
prt->scroll = true;
|
||||
}
|
||||
}
|
||||
/* RETURN key: execute command */
|
||||
else if (key == '\n') {
|
||||
wprintw(self->window, "\n");
|
||||
uint8_t line[MAX_STR_SIZE];
|
||||
char line[MAX_STR_SIZE] = {0};
|
||||
|
||||
if (wcs_to_mbs_buf(line, prt->line, MAX_STR_SIZE) == -1)
|
||||
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1)
|
||||
memset(&line, 0, sizeof(line));
|
||||
|
||||
if (!string_is_empty(line))
|
||||
add_line_to_hist(prt->line, prt->len, prt->ln_history, &prt->hst_tot, &prt->hst_pos);
|
||||
add_line_to_hist(ctx);
|
||||
|
||||
execute(self->window, self, m, line, GLOBAL_COMMAND_MODE);
|
||||
reset_buf(prt->line, &prt->pos, &prt->len);
|
||||
line_info_add(self, NULL, NULL, NULL, PROMPT, 0, 0, "%s", line);
|
||||
execute(ctx->history, self, m, line, GLOBAL_COMMAND_MODE);
|
||||
|
||||
wclear(ctx->linewin);
|
||||
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
|
||||
reset_buf(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static void prompt_onDraw(ToxWindow *self, Tox *m)
|
||||
{
|
||||
PromptBuf *prt = self->promptbuf;
|
||||
int x2, y2;
|
||||
getmaxyx(self->window, y2, x2);
|
||||
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
line_info_print(self);
|
||||
wclear(ctx->linewin);
|
||||
|
||||
curs_set(1);
|
||||
int x, y, x2, y2;
|
||||
getyx(self->window, y, x);
|
||||
getmaxyx(self->window, y2, x2);
|
||||
wclrtobot(self->window);
|
||||
|
||||
/* if len is >= screen width offset max x by X_OFST to account for prompt char */
|
||||
int px2 = prt->len >= x2 ? x2 : x2 - X_OFST;
|
||||
|
||||
if (px2 <= 0)
|
||||
return;
|
||||
|
||||
/* len offset to account for prompt char (0 if len is < width of screen) */
|
||||
int p_ofst = px2 != x2 ? 0 : X_OFST;
|
||||
|
||||
if (prt->len > 0) {
|
||||
uint8_t line[MAX_STR_SIZE];
|
||||
|
||||
if (wcs_to_mbs_buf(line, prt->line, MAX_STR_SIZE) == -1)
|
||||
reset_buf(prt->line, &prt->pos, &prt->len);
|
||||
else
|
||||
mvwprintw(self->window, prt->orig_y, X_OFST, line);
|
||||
|
||||
int k = prt->orig_y + ((prt->len + p_ofst) / px2);
|
||||
|
||||
prt->at_bottom = k == y2 - 1;
|
||||
bool botm = k == y2;
|
||||
bool edge = (prt->len + p_ofst) % px2 == 0;
|
||||
|
||||
/* move point of line origin up when input scrolls screen down */
|
||||
if (prt->scroll && edge && botm) {
|
||||
--prt->orig_y;
|
||||
prt->scroll = false;
|
||||
}
|
||||
|
||||
} else { /* Mark point of origin for new line */
|
||||
prt->orig_y = y;
|
||||
}
|
||||
|
||||
wattron(self->window, COLOR_PAIR(GREEN));
|
||||
mvwprintw(self->window, prt->orig_y, 0, "$ ");
|
||||
wattroff(self->window, COLOR_PAIR(GREEN));
|
||||
if (ctx->len > 0)
|
||||
mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]);
|
||||
|
||||
StatusBar *statusbar = self->stb;
|
||||
werase(statusbar->topline);
|
||||
mvwhline(statusbar->topline, 1, 0, ACS_HLINE, x2);
|
||||
wmove(statusbar->topline, 0, 0);
|
||||
|
||||
if (statusbar->is_online) {
|
||||
int colour = WHITE;
|
||||
char *status_text = "Unknown";
|
||||
if (statusbar->connection != TOX_CONNECTION_NONE) {
|
||||
int colour = MAGENTA;
|
||||
const char *status_text = "ERROR";
|
||||
|
||||
switch (statusbar->status) {
|
||||
case TOX_USERSTATUS_NONE:
|
||||
status_text = "Online";
|
||||
colour = GREEN;
|
||||
break;
|
||||
case TOX_USERSTATUS_AWAY:
|
||||
status_text = "Away";
|
||||
colour = YELLOW;
|
||||
break;
|
||||
case TOX_USERSTATUS_BUSY:
|
||||
status_text = "Busy";
|
||||
colour = RED;
|
||||
break;
|
||||
case TOX_USER_STATUS_NONE:
|
||||
status_text = "Online";
|
||||
colour = GREEN;
|
||||
break;
|
||||
case TOX_USER_STATUS_AWAY:
|
||||
status_text = "Away";
|
||||
colour = YELLOW;
|
||||
break;
|
||||
case TOX_USER_STATUS_BUSY:
|
||||
status_text = "Busy";
|
||||
colour = RED;
|
||||
break;
|
||||
}
|
||||
|
||||
wattron(statusbar->topline, A_BOLD);
|
||||
wprintw(statusbar->topline, " %s ", statusbar->nick);
|
||||
wattron(statusbar->topline, A_BOLD);
|
||||
wattron(statusbar->topline, COLOR_PAIR(colour) | A_BOLD);
|
||||
wprintw(statusbar->topline, "[%s]", status_text);
|
||||
wprintw(statusbar->topline, " [%s]", status_text);
|
||||
wattroff(statusbar->topline, COLOR_PAIR(colour) | A_BOLD);
|
||||
} else {
|
||||
|
||||
wattron(statusbar->topline, A_BOLD);
|
||||
wprintw(statusbar->topline, " %s ", statusbar->nick);
|
||||
wprintw(statusbar->topline, " %s", statusbar->nick);
|
||||
wattroff(statusbar->topline, A_BOLD);
|
||||
} else {
|
||||
wprintw(statusbar->topline, " [Offline]");
|
||||
wattron(statusbar->topline, A_BOLD);
|
||||
wprintw(statusbar->topline, " %s", statusbar->nick);
|
||||
wattroff(statusbar->topline, A_BOLD);
|
||||
wprintw(statusbar->topline, "[Offline]");
|
||||
}
|
||||
|
||||
wattron(statusbar->topline, A_BOLD);
|
||||
wprintw(statusbar->topline, " - %s", statusbar->statusmsg);
|
||||
wattroff(statusbar->topline, A_BOLD);
|
||||
/* Reset statusbar->statusmsg on window resize */
|
||||
if (x2 != self->x) {
|
||||
char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH];
|
||||
|
||||
/* put cursor back in correct spot */
|
||||
int y_m = prt->orig_y + ((prt->pos + p_ofst) / px2);
|
||||
int x_m = (prt->pos + X_OFST) % x2;
|
||||
wmove(self->window, y_m, x_m);
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
tox_self_get_status_message(m, (uint8_t *) statusmsg);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg);
|
||||
statusbar->statusmsg_len = strlen(statusbar->statusmsg);
|
||||
}
|
||||
|
||||
self->x = x2;
|
||||
|
||||
/* Truncate note if it doesn't fit in statusbar */
|
||||
uint16_t maxlen = x2 - getcurx(statusbar->topline) - 3;
|
||||
|
||||
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);
|
||||
|
||||
mvwhline(self->window, y2 - CHATBOX_HEIGHT, 0, ACS_HLINE, x2);
|
||||
|
||||
int y, x;
|
||||
getyx(self->window, y, x);
|
||||
(void) x;
|
||||
|
||||
int new_x = ctx->start ? x2 - 1 : wcswidth(ctx->line, ctx->pos);
|
||||
wmove(self->window, y + 1, new_x);
|
||||
|
||||
wrefresh(self->window);
|
||||
|
||||
if (self->help->active)
|
||||
help_onDraw(self);
|
||||
}
|
||||
|
||||
static void prompt_onInit(ToxWindow *self, Tox *m)
|
||||
{
|
||||
scrollok(self->window, true);
|
||||
execute(self->window, self, m, "/help", GLOBAL_COMMAND_MODE);
|
||||
wclrtoeol(self->window);
|
||||
}
|
||||
|
||||
static void prompt_onConnectionChange(ToxWindow *self, Tox *m, int friendnum , uint8_t status)
|
||||
static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnum , TOX_CONNECTION connection_status)
|
||||
{
|
||||
if (friendnum < 0)
|
||||
return;
|
||||
|
||||
prep_prompt_win();
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
|
||||
|
||||
if (tox_get_name(m, friendnum, nick) == -1)
|
||||
return;
|
||||
char nick[TOX_MAX_NAME_LENGTH] = {0}; /* stop removing this initiation */
|
||||
get_nick_truncate(m, nick, friendnum);
|
||||
|
||||
if (!nick[0])
|
||||
snprintf(nick, sizeof(nick), "%s", UNKNOWN_NAME);
|
||||
|
||||
wprintw(self->window, "\n");
|
||||
print_time(self->window);
|
||||
char timefrmt[TIME_STR_SIZE];
|
||||
get_time_str(timefrmt, sizeof(timefrmt));
|
||||
const char *msg;
|
||||
|
||||
if (status == 1) {
|
||||
wattron(self->window, COLOR_PAIR(GREEN));
|
||||
wattron(self->window, A_BOLD);
|
||||
wprintw(self->window, "* %s ", nick);
|
||||
wattroff(self->window, A_BOLD);
|
||||
wprintw(self->window, "has come online\n");
|
||||
wattroff(self->window, COLOR_PAIR(GREEN));
|
||||
if (connection_status != TOX_CONNECTION_NONE) {
|
||||
msg = "has come online";
|
||||
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);
|
||||
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 {
|
||||
wattron(self->window, COLOR_PAIR(RED));
|
||||
wattron(self->window, A_BOLD);
|
||||
wprintw(self->window, "* %s ", nick);
|
||||
wattroff(self->window, A_BOLD);
|
||||
wprintw(self->window, "has gone offline\n");
|
||||
wattroff(self->window, COLOR_PAIR(RED));
|
||||
msg = "has gone offline";
|
||||
line_info_add(self, timefrmt, nick, NULL, 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, uint8_t *key, uint8_t *data, uint16_t length)
|
||||
static void prompt_onFriendRequest(ToxWindow *self, Tox *m, const char *key, const char *data, size_t length)
|
||||
{
|
||||
// make sure message data is null-terminated
|
||||
data[length - 1] = 0;
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
prep_prompt_win();
|
||||
wprintw(self->window, "\n");
|
||||
print_time(self->window);
|
||||
wprintw(self->window, "Friend request with the message: '%s'\n", data);
|
||||
char timefrmt[TIME_STR_SIZE];
|
||||
get_time_str(timefrmt, sizeof(timefrmt));
|
||||
|
||||
int n = add_friend_request(key);
|
||||
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, data);
|
||||
|
||||
if (n == -1) {
|
||||
wprintw(self->window, "Friend request queue is full. Discarding request.\n");
|
||||
const char *errmsg = "Friend request queue is full. Discarding request.";
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
|
||||
return;
|
||||
}
|
||||
|
||||
wprintw(self->window, "Type \"/accept %d\" to accept it.\n", n);
|
||||
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)
|
||||
{
|
||||
int x, y;
|
||||
getmaxyx(self->window, y, x);
|
||||
int x2, y2;
|
||||
getmaxyx(self->window, y2, x2);
|
||||
(void) y2;
|
||||
|
||||
/* 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;
|
||||
|
||||
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
|
||||
tox_get_self_name(m, nick, TOX_MAX_NAME_LENGTH);
|
||||
snprintf(statusbar->nick, sizeof(statusbar->nick), "%s", nick);
|
||||
char nick[TOX_MAX_NAME_LENGTH];
|
||||
char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH];
|
||||
|
||||
/* temporary until statusmessage saving works */
|
||||
uint8_t ver[strlen(TOXICVER) + 1];
|
||||
uint8_t statusmsg[MAX_STR_SIZE];
|
||||
strcpy(ver, TOXICVER);
|
||||
uint8_t *toxic_ver = strtok(ver, "_");
|
||||
size_t n_len = tox_self_get_name_size(m);
|
||||
tox_self_get_name(m, (uint8_t *) nick);
|
||||
|
||||
if (toxic_ver != NULL)
|
||||
snprintf(statusmsg, MAX_STR_SIZE, "Toxing on Toxic v.%s", toxic_ver);
|
||||
else
|
||||
snprintf(statusmsg, MAX_STR_SIZE, "Toxing on Toxic hacker edition");
|
||||
size_t s_len = tox_self_get_status_message_size(m);
|
||||
tox_self_get_status_message(m, (uint8_t *) statusmsg);
|
||||
|
||||
m_set_statusmessage(m, statusmsg, strlen(statusmsg) + 1);
|
||||
snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg);
|
||||
TOX_USER_STATUS status = tox_self_get_status(m);
|
||||
|
||||
nick[n_len] = '\0';
|
||||
statusmsg[s_len] = '\0';
|
||||
|
||||
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, m, statusmsg);
|
||||
prompt_update_status(prompt, status);
|
||||
prompt_update_nick(prompt, nick);
|
||||
|
||||
/* Init statusbar subwindow */
|
||||
statusbar->topline = subwin(self->window, 2, x, 0, 0);
|
||||
statusbar->topline = subwin(self->window, 2, x2, 0, 0);
|
||||
}
|
||||
|
||||
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, " |_| \\___/_/\\_\\___\\____| v." TOXICVER);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
|
||||
|
||||
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, SYS_MSG, 1, CYAN, msg);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
|
||||
}
|
||||
|
||||
static void prompt_onInit(ToxWindow *self, Tox *m)
|
||||
{
|
||||
curs_set(1);
|
||||
int y2, x2;
|
||||
getmaxyx(self->window, y2, x2);
|
||||
|
||||
ChatContext *ctx = self->chatwin;
|
||||
ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0);
|
||||
ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0);
|
||||
|
||||
ctx->log = calloc(1, sizeof(struct chatlog));
|
||||
ctx->hst = calloc(1, sizeof(struct history));
|
||||
|
||||
if (ctx->log == NULL || ctx->hst == NULL)
|
||||
exit_toxic_err("failed in prompt_onInit", FATALERR_MEMORY);
|
||||
|
||||
line_info_init(ctx->hst);
|
||||
|
||||
if (user_settings->autolog == AUTOLOG_ON) {
|
||||
char myid[TOX_ADDRESS_SIZE];
|
||||
tox_self_get_address(m, (uint8_t *) myid);
|
||||
log_enable(self->name, myid, NULL, ctx->log, LOG_PROMPT);
|
||||
}
|
||||
|
||||
scrollok(ctx->history, 0);
|
||||
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
|
||||
|
||||
if (user_settings->show_welcome_msg == SHOW_WELCOME_MSG_ON)
|
||||
print_welcome_msg(self);
|
||||
}
|
||||
|
||||
ToxWindow new_prompt(void)
|
||||
@ -441,6 +488,7 @@ ToxWindow new_prompt(void)
|
||||
memset(&ret, 0, sizeof(ret));
|
||||
|
||||
ret.active = true;
|
||||
ret.is_prompt = true;
|
||||
|
||||
ret.onKey = &prompt_onKey;
|
||||
ret.onDraw = &prompt_onDraw;
|
||||
@ -448,19 +496,20 @@ ToxWindow new_prompt(void)
|
||||
ret.onConnectionChange = &prompt_onConnectionChange;
|
||||
ret.onFriendRequest = &prompt_onFriendRequest;
|
||||
|
||||
strcpy(ret.name, "prompt");
|
||||
strcpy(ret.name, "home");
|
||||
|
||||
PromptBuf *promptbuf = calloc(1, sizeof(PromptBuf));
|
||||
ChatContext *chatwin = calloc(1, sizeof(ChatContext));
|
||||
StatusBar *stb = calloc(1, sizeof(StatusBar));
|
||||
Help *help = calloc(1, sizeof(Help));
|
||||
|
||||
if (stb != NULL && promptbuf != NULL) {
|
||||
ret.promptbuf = promptbuf;
|
||||
ret.stb = stb;
|
||||
} else {
|
||||
endwin();
|
||||
fprintf(stderr, "calloc() failed. Aborting...\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (stb == NULL || chatwin == NULL || help == NULL)
|
||||
exit_toxic_err("failed in new_prompt", FATALERR_MEMORY);
|
||||
|
||||
ret.chatwin = chatwin;
|
||||
ret.stb = stb;
|
||||
ret.help = help;
|
||||
|
||||
ret.active_box = -1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
55
src/prompt.h
55
src/prompt.h
@ -1,20 +1,55 @@
|
||||
/*
|
||||
* Toxic -- Tox Curses Client
|
||||
/* prompt.h
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef PROMPT_H_UZYGWFFL
|
||||
#define PROMPT_H_UZYGWFFL
|
||||
#ifndef PROMPT_H
|
||||
#define PROMPT_H
|
||||
|
||||
#define X_OFST 2 /* offset to account for prompt char */
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
#define AC_NUM_GLOB_COMMANDS 14
|
||||
#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, uint8_t *nick, uint16_t len);
|
||||
void prompt_update_statusmessage(ToxWindow *prompt, uint8_t *statusmsg, uint16_t len);
|
||||
void prompt_update_status(ToxWindow *prompt, TOX_USERSTATUS status);
|
||||
void prompt_update_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 */
|
||||
|
465
src/settings.c
Normal file
465
src/settings.c
Normal file
@ -0,0 +1,465 @@
|
||||
/* settings.c
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <libconfig.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "configdir.h"
|
||||
#include "notify.h"
|
||||
#include "misc_tools.h"
|
||||
|
||||
#ifdef AUDIO
|
||||
#include "device.h"
|
||||
#endif /* AUDIO */
|
||||
|
||||
#include "settings.h"
|
||||
#include "line_info.h"
|
||||
|
||||
#ifndef PACKAGE_DATADIR
|
||||
#define PACKAGE_DATADIR "."
|
||||
#endif
|
||||
|
||||
#define NO_SOUND "silent"
|
||||
|
||||
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 ui_defaults(struct user_settings* settings)
|
||||
{
|
||||
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);
|
||||
|
||||
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 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",
|
||||
};
|
||||
|
||||
/* 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 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",
|
||||
};
|
||||
|
||||
static void tox_defaults(struct user_settings* settings)
|
||||
{
|
||||
strcpy(settings->download_path, "");
|
||||
strcpy(settings->chatlogs_path, "");
|
||||
strcpy(settings->avatar_path, "");
|
||||
}
|
||||
|
||||
#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",
|
||||
};
|
||||
|
||||
static void audio_defaults(struct user_settings* settings)
|
||||
{
|
||||
settings->audio_in_dev = 0;
|
||||
settings->audio_out_dev = 0;
|
||||
settings->VAD_treshold = 40.0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#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
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (strncasecmp(*bind, "tab", 3) == 0)
|
||||
return T_KEY_TAB;
|
||||
|
||||
if (strncasecmp(*bind, "page", 4) == 0)
|
||||
return len == 6 ? KEY_PPAGE : KEY_NPAGE;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* ui */
|
||||
if ((setting = config_lookup(cfg, ui_strings.self)) != NULL) {
|
||||
config_setting_lookup_bool(setting, ui_strings.timestamps, &s->timestamps);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
112
src/settings.h
Normal file
112
src/settings.h
Normal file
@ -0,0 +1,112 @@
|
||||
/* settings.h
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SETTINGS_H
|
||||
#define SETTINGS_H
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#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 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 */
|
||||
int show_typing_self; /* boolean */
|
||||
int show_typing_other; /* boolean */
|
||||
int show_welcome_msg; /* boolean */
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
enum {
|
||||
AUTOLOG_OFF = 0,
|
||||
AUTOLOG_ON = 1,
|
||||
|
||||
TIMESTAMPS_OFF = 0,
|
||||
TIMESTAMPS_ON = 1,
|
||||
|
||||
ALERTS_DISABLED = 0,
|
||||
ALERTS_ENABLED = 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;
|
||||
|
||||
#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"
|
||||
|
||||
int settings_load(struct user_settings *s, const char *patharg);
|
||||
#endif /* #define SETTINGS_H */
|
390
src/term_mplex.c
Normal file
390
src/term_mplex.c
Normal file
@ -0,0 +1,390 @@
|
||||
/* 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;
|
||||
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);
|
||||
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 */
|
||||
strcpy (mplex_data, 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);
|
||||
tox_self_get_status_message (m, (uint8_t*) prev_note);
|
||||
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
35
src/term_mplex.h
Normal 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 */
|
1108
src/toxic.c
Normal file
1108
src/toxic.c
Normal file
File diff suppressed because it is too large
Load Diff
129
src/toxic.h
Normal file
129
src/toxic.h
Normal file
@ -0,0 +1,129 @@
|
||||
/* toxic.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 TOXIC_H
|
||||
#define TOXIC_H
|
||||
|
||||
#ifndef TOXICVER
|
||||
#define TOXICVER "NOVERS" /* Use the -D flag to set this */
|
||||
#endif
|
||||
|
||||
#ifndef SIGWINCH
|
||||
#define SIGWINCH 28
|
||||
#endif
|
||||
|
||||
#ifndef SIGINT
|
||||
#define SIGINT 2
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <curses.h>
|
||||
|
||||
#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_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 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_KILL 0x0B /* ctrl-k */
|
||||
#define T_KEY_DISCARD 0x15 /* ctrl-u */
|
||||
#define T_KEY_NEXT 0x10 /* ctrl-p */
|
||||
#define T_KEY_PREV 0x0F /* ctrl-o */
|
||||
#define T_KEY_C_E 0x05 /* ctrl-e */
|
||||
#define T_KEY_C_A 0x01 /* ctrl-a */
|
||||
#define T_KEY_C_RB 0x1D /* ctrl-] */
|
||||
#define T_KEY_C_LB 0x1B /* ctrl-[ */
|
||||
#define T_KEY_C_V 0x16 /* ctrl-v */
|
||||
#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, /* 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_NOT_SET = -6, /* system locale not set */
|
||||
FATALERR_STORE_DATA = -7, /* store_data failed in critical section */
|
||||
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, const char *path);
|
||||
|
||||
/* 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, 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_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);
|
||||
|
||||
#ifdef AUDIO
|
||||
void write_device_callback_group(Tox *m, int groupnum, int peernum, const int16_t *pcm, unsigned int samples,
|
||||
uint8_t channels, unsigned int sample_rate, void *arg);
|
||||
#endif /* AUDIO */
|
||||
|
||||
#endif /* #define TOXIC_H */
|
@ -1,227 +1,244 @@
|
||||
/*
|
||||
* Toxic -- Tox Curses Client
|
||||
/* toxic_strings.c
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include "toxic_windows.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "misc_tools.h"
|
||||
#include "toxic_strings.h"
|
||||
#include "notify.h"
|
||||
|
||||
/* Adds char to buffer at pos */
|
||||
void add_char_to_buf(wchar_t *buf, size_t *pos, size_t *len, wint_t ch)
|
||||
/* 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)
|
||||
{
|
||||
if (*pos < 0 || *len >= MAX_STR_SIZE)
|
||||
return;
|
||||
if (ctx->len >= MAX_STR_SIZE - 1)
|
||||
return -1;
|
||||
|
||||
/* move all chars including null in front of pos one space forward and insert char in pos */
|
||||
int i;
|
||||
wmemmove(&ctx->line[ctx->pos + 1], &ctx->line[ctx->pos], ctx->len - ctx->pos);
|
||||
ctx->line[ctx->pos++] = ch;
|
||||
ctx->line[++ctx->len] = L'\0';
|
||||
|
||||
for (i = *len; i >= *pos && i >= 0; --i)
|
||||
buf[i+1] = buf[i];
|
||||
|
||||
buf[(*pos)++] = ch;
|
||||
++(*len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Deletes the character before pos */
|
||||
void del_char_buf_bck(wchar_t *buf, size_t *pos, size_t *len)
|
||||
/* Deletes the character before pos. Return 0 on success, -1 if nothing to delete */
|
||||
int del_char_buf_bck(ChatContext *ctx)
|
||||
{
|
||||
if (*pos <= 0)
|
||||
if (ctx->pos <= 0)
|
||||
return -1;
|
||||
|
||||
wmemmove(&ctx->line[ctx->pos - 1], &ctx->line[ctx->pos], ctx->len - ctx->pos);
|
||||
--ctx->pos;
|
||||
ctx->line[--ctx->len] = L'\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Deletes the character at pos. Return 0 on success, -1 if nothing to delete. */
|
||||
int del_char_buf_frnt(ChatContext *ctx)
|
||||
{
|
||||
if (ctx->pos >= ctx->len)
|
||||
return -1;
|
||||
|
||||
wmemmove(&ctx->line[ctx->pos], &ctx->line[ctx->pos + 1], ctx->len - ctx->pos - 1);
|
||||
ctx->line[--ctx->len] = L'\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Deletes the line from beginning to pos and puts discarded portion in yank buffer.
|
||||
Return 0 on success, -1 if noting to discard. */
|
||||
int discard_buf(ChatContext *ctx)
|
||||
{
|
||||
if (ctx->pos <= 0)
|
||||
return -1;
|
||||
|
||||
ctx->yank_len = ctx->pos;
|
||||
wmemcpy(ctx->yank, ctx->line, ctx->yank_len);
|
||||
ctx->yank[ctx->yank_len] = L'\0';
|
||||
|
||||
wmemmove(ctx->line, &ctx->line[ctx->pos], ctx->len - ctx->pos);
|
||||
ctx->len -= ctx->pos;
|
||||
ctx->pos = 0;
|
||||
ctx->start = 0;
|
||||
ctx->line[ctx->len] = L'\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Deletes the line from pos to len and puts killed portion in yank buffer.
|
||||
Return 0 on success, -1 if nothing to kill. */
|
||||
int kill_buf(ChatContext *ctx)
|
||||
{
|
||||
if (ctx->len <= ctx->pos)
|
||||
return -1;
|
||||
|
||||
ctx->yank_len = ctx->len - ctx->pos;
|
||||
wmemcpy(ctx->yank, &ctx->line[ctx->pos], ctx->yank_len);
|
||||
ctx->yank[ctx->yank_len] = L'\0';
|
||||
|
||||
ctx->line[ctx->pos] = L'\0';
|
||||
ctx->len = ctx->pos;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Inserts string in ctx->yank into line at pos.
|
||||
Return 0 on success, -1 if yank buffer is empty or too long */
|
||||
int yank_buf(ChatContext *ctx)
|
||||
{
|
||||
if (!ctx->yank[0])
|
||||
return -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);
|
||||
wmemcpy(&ctx->line[ctx->pos], ctx->yank, ctx->yank_len);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* nulls line and sets pos, len and start to 0 */
|
||||
void reset_buf(ChatContext *ctx)
|
||||
{
|
||||
ctx->line[0] = L'\0';
|
||||
ctx->pos = 0;
|
||||
ctx->len = 0;
|
||||
ctx->start = 0;
|
||||
}
|
||||
|
||||
/* Removes trailing spaces from line. */
|
||||
void rm_trailing_spaces_buf(ChatContext *ctx)
|
||||
{
|
||||
if (ctx->len <= 0)
|
||||
return;
|
||||
|
||||
if (ctx->line[ctx->len - 1] != ' ')
|
||||
return;
|
||||
|
||||
int i;
|
||||
|
||||
/* similar to add_char_to_buf but deletes a char */
|
||||
for (i = *pos-1; i <= *len; ++i)
|
||||
buf[i] = buf[i+1];
|
||||
for (i = ctx->len - 1; i >= 0; --i) {
|
||||
if (ctx->line[i] != ' ')
|
||||
break;
|
||||
}
|
||||
|
||||
--(*pos);
|
||||
--(*len);
|
||||
}
|
||||
|
||||
/* Deletes the character at pos */
|
||||
void del_char_buf_frnt(wchar_t *buf, size_t *pos, size_t *len)
|
||||
{
|
||||
if (*pos < 0 || *pos >= *len)
|
||||
return;
|
||||
|
||||
int i;
|
||||
|
||||
for (i = *pos; i < *len; ++i)
|
||||
buf[i] = buf[i+1];
|
||||
|
||||
--(*len);
|
||||
}
|
||||
|
||||
/* Deletes the line from beginning to pos */
|
||||
void discard_buf(wchar_t *buf, size_t *pos, size_t *len)
|
||||
{
|
||||
if (*pos <= 0)
|
||||
return;
|
||||
|
||||
int i;
|
||||
int c = 0;
|
||||
|
||||
for (i = *pos; i <= *len; ++i)
|
||||
buf[c++] = buf[i];
|
||||
|
||||
*pos = 0;
|
||||
*len = c - 1;
|
||||
}
|
||||
|
||||
/* Deletes the line from pos to len */
|
||||
void kill_buf(wchar_t *buf, size_t *pos, size_t *len)
|
||||
{
|
||||
if (*len == *pos)
|
||||
return;
|
||||
|
||||
buf[*pos] = L'\0';
|
||||
*len = *pos;
|
||||
}
|
||||
|
||||
/* nulls buf and sets pos and len to 0 */
|
||||
void reset_buf(wchar_t *buf, size_t *pos, size_t *len)
|
||||
{
|
||||
buf[0] = L'\0';
|
||||
*pos = 0;
|
||||
*len = 0;
|
||||
ctx->len = i + 1;
|
||||
ctx->pos = MIN(ctx->pos, ctx->len);
|
||||
ctx->line[ctx->len] = L'\0';
|
||||
}
|
||||
|
||||
#define HIST_PURGE MAX_LINE_HIST / 4
|
||||
|
||||
/* shifts hist items back and makes room for HIST_PURGE new entries */
|
||||
static void shift_hist_back(wchar_t (*hst)[MAX_STR_SIZE], int *hst_tot)
|
||||
static void shift_hist_back(ChatContext *ctx)
|
||||
{
|
||||
int i;
|
||||
int n = MAX_LINE_HIST - HIST_PURGE;
|
||||
|
||||
for (i = 0; i < n; ++i)
|
||||
wmemcpy(hst[i], hst[i+HIST_PURGE], MAX_STR_SIZE);
|
||||
wmemcpy(ctx->ln_history[i], ctx->ln_history[i + HIST_PURGE], MAX_STR_SIZE);
|
||||
|
||||
*hst_tot = n;
|
||||
ctx->hst_tot = n;
|
||||
}
|
||||
|
||||
/* adds a line to the ln_history buffer at hst_pos and sets hst_pos to end of history. */
|
||||
void add_line_to_hist(const wchar_t *buf, size_t len, wchar_t (*hst)[MAX_STR_SIZE], int *hst_tot,
|
||||
int *hst_pos)
|
||||
void add_line_to_hist(ChatContext *ctx)
|
||||
{
|
||||
if (len > MAX_STR_SIZE)
|
||||
if (ctx->len >= MAX_STR_SIZE)
|
||||
return;
|
||||
|
||||
if (*hst_tot >= MAX_LINE_HIST)
|
||||
shift_hist_back(hst, hst_tot);
|
||||
if (ctx->hst_tot >= MAX_LINE_HIST)
|
||||
shift_hist_back(ctx);
|
||||
|
||||
++(*hst_tot);
|
||||
*hst_pos = *hst_tot;
|
||||
++ctx->hst_tot;
|
||||
ctx->hst_pos = ctx->hst_tot;
|
||||
|
||||
wmemcpy(hst[*hst_tot-1], buf, len + 1);
|
||||
wmemcpy(ctx->ln_history[ctx->hst_tot - 1], ctx->line, ctx->len + 1);
|
||||
}
|
||||
|
||||
/* copies history item at hst_pos to buf. Sets pos and len to the len of the history item.
|
||||
hst_pos is decremented or incremented depending on key_dir. */
|
||||
void fetch_hist_item(wchar_t *buf, size_t *pos, size_t *len, wchar_t (*hst)[MAX_STR_SIZE],
|
||||
int hst_tot, int *hst_pos, int key_dir)
|
||||
/* copies history item at hst_pos to line. Sets pos and len to the len of the history item.
|
||||
hst_pos is decremented or incremented depending on key_dir.
|
||||
|
||||
resets line if at end of history */
|
||||
void fetch_hist_item(ChatContext *ctx, int key_dir)
|
||||
{
|
||||
if (key_dir == LN_HIST_MV_UP) {
|
||||
if (--(*hst_pos) < 0) {
|
||||
++(*hst_pos);
|
||||
beep();
|
||||
if (key_dir == KEY_UP) {
|
||||
if (--ctx->hst_pos < 0) {
|
||||
ctx->hst_pos = 0;
|
||||
sound_notify(NULL, notif_error, NT_ALWAYS, NULL);
|
||||
}
|
||||
} else {
|
||||
if (++(*hst_pos) >= hst_tot) {
|
||||
--(*hst_pos);
|
||||
beep();
|
||||
if (++ctx->hst_pos >= ctx->hst_tot) {
|
||||
ctx->hst_pos = ctx->hst_tot;
|
||||
reset_buf(ctx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const wchar_t *hst_line = hst[*hst_pos];
|
||||
const wchar_t *hst_line = ctx->ln_history[ctx->hst_pos];
|
||||
size_t h_len = wcslen(hst_line);
|
||||
|
||||
wmemcpy(buf, hst_line, h_len + 1);
|
||||
|
||||
*pos = h_len;
|
||||
*len = h_len;
|
||||
}
|
||||
|
||||
/* looks for the first instance in list that begins with the last entered word in buf according to pos,
|
||||
then fills buf with the complete word. e.g. "Hello jo" would complete the buffer
|
||||
with "Hello john".
|
||||
|
||||
list is a pointer to the list of strings being compared, n_items is the number of items
|
||||
in the list, and size is the size of each item in the list.
|
||||
|
||||
Returns the difference between the old len and new len of buf on success, -1 if error */
|
||||
int complete_line(wchar_t *buf, size_t *pos, size_t *len, const void *list, int n_items, int size)
|
||||
{
|
||||
if (*pos <= 0 || *len <= 0 || *len >= MAX_STR_SIZE)
|
||||
return -1;
|
||||
|
||||
const uint8_t *L = (uint8_t *) list;
|
||||
|
||||
uint8_t ubuf[MAX_STR_SIZE];
|
||||
/* work with multibyte string copy of buf for simplicity */
|
||||
if (wcs_to_mbs_buf(ubuf, buf, MAX_STR_SIZE) == -1)
|
||||
return -1;
|
||||
|
||||
/* isolate substring from space behind pos to pos */
|
||||
uint8_t tmp[MAX_STR_SIZE];
|
||||
snprintf(tmp, sizeof(tmp), "%s", ubuf);
|
||||
tmp[*pos] = '\0';
|
||||
uint8_t *sub = strrchr(tmp, ' ');
|
||||
int n_endchrs = 1; /* 1 = append space to end of match, 2 = append ": " */
|
||||
|
||||
if (!sub++) {
|
||||
sub = tmp;
|
||||
if (sub[0] != '/') /* make sure it's not a command */
|
||||
n_endchrs = 2;
|
||||
}
|
||||
|
||||
if (string_is_empty(sub))
|
||||
return -1;
|
||||
|
||||
int s_len = strlen(sub);
|
||||
const uint8_t *match;
|
||||
bool is_match = false;
|
||||
int i;
|
||||
|
||||
/* look for a match in list */
|
||||
for (i = 0; i < n_items; ++i) {
|
||||
match = &L[i*size];
|
||||
if (is_match = strncasecmp(match, sub, s_len) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!is_match)
|
||||
return -1;
|
||||
|
||||
/* put match in correct spot in buf and append endchars (space or ": ") */
|
||||
const uint8_t *endchrs = n_endchrs == 1 ? " " : ": ";
|
||||
int m_len = strlen(match);
|
||||
int strt = (int) *pos - s_len;
|
||||
int diff = m_len - s_len + n_endchrs;
|
||||
|
||||
if (*len + diff > MAX_STR_SIZE)
|
||||
return -1;
|
||||
|
||||
uint8_t tmpend[MAX_STR_SIZE];
|
||||
strcpy(tmpend, &ubuf[*pos]);
|
||||
strcpy(&ubuf[strt], match);
|
||||
strcpy(&ubuf[strt+m_len], endchrs);
|
||||
strcpy(&ubuf[strt+m_len+n_endchrs], tmpend);
|
||||
|
||||
/* convert to widechar and copy back to original buf */
|
||||
wchar_t newbuf[MAX_STR_SIZE];
|
||||
|
||||
if (mbs_to_wcs_buf(newbuf, ubuf, MAX_STR_SIZE) == -1)
|
||||
return -1;
|
||||
|
||||
wcscpy(buf, newbuf);
|
||||
|
||||
*len += (size_t) diff;
|
||||
*pos += (size_t) diff;
|
||||
|
||||
return diff;
|
||||
wmemcpy(ctx->line, hst_line, h_len + 1);
|
||||
ctx->pos = h_len;
|
||||
ctx->len = h_len;
|
||||
}
|
||||
|
@ -1,45 +1,69 @@
|
||||
/*
|
||||
* Toxic -- Tox Curses Client
|
||||
/* toxic_strings.h
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Adds char to buffer at pos */
|
||||
void add_char_to_buf(wchar_t *buf, size_t *pos, size_t *len, wint_t ch);
|
||||
#ifndef TOXIC_STRINGS_H
|
||||
#define TOXIC_STRINGS_H
|
||||
|
||||
/* Deletes the character before pos */
|
||||
void del_char_buf_bck(wchar_t *buf, size_t *pos, size_t *len);
|
||||
#include "windows.h"
|
||||
|
||||
/* Deletes the character at pos */
|
||||
void del_char_buf_frnt(wchar_t *buf, size_t *pos, size_t *len);
|
||||
/* 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);
|
||||
|
||||
/* Deletes the line from beginning to pos */
|
||||
void discard_buf(wchar_t *buf, size_t *pos, size_t *len);
|
||||
/* Deletes the character before pos. Return 0 on success, -1 if nothing to delete */
|
||||
int del_char_buf_bck(ChatContext *ctx);
|
||||
|
||||
/* Deletes the line from pos to len */
|
||||
void kill_buf(wchar_t *buf, size_t *pos, size_t *len);
|
||||
/* Deletes the character at pos. Return 0 on success, -1 if nothing to delete. */
|
||||
int del_char_buf_frnt(ChatContext *ctx);
|
||||
|
||||
/* nulls buf and sets pos and len to 0 */
|
||||
void reset_buf(wchar_t *buf, size_t *pos, size_t *len);
|
||||
/* Deletes the line from beginning to pos and puts discarded portion in yank buffer.
|
||||
Return 0 on success, -1 if noting to discard */
|
||||
int discard_buf(ChatContext *ctx);
|
||||
|
||||
/* looks for the first instance in list that begins with the last entered word in buf according to pos,
|
||||
then fills buf with the complete word. e.g. "Hello jo" would complete the buffer
|
||||
with "Hello john".
|
||||
/* Deletes the line from pos to len and puts killed portion in yank buffer.
|
||||
Return 0 on success, -1 if nothing to kill. */
|
||||
int kill_buf(ChatContext *ctx);
|
||||
|
||||
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.
|
||||
/* nulls line and sets pos, len and start to 0 */
|
||||
void reset_buf(ChatContext *ctx);
|
||||
|
||||
Returns the difference between the old len and new len of buf on success, -1 if error */
|
||||
int complete_line(wchar_t *buf, size_t *pos, size_t *len, const void *list, int n_items, int size);
|
||||
/* Inserts string in ctx->yank into line at pos.
|
||||
Return 0 on success, -1 if yank buffer is empty or too long */
|
||||
int yank_buf(ChatContext *ctx);
|
||||
|
||||
enum {
|
||||
LN_HIST_MV_UP,
|
||||
LN_HIST_MV_DWN,
|
||||
};
|
||||
/* 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);
|
||||
|
||||
/* adds a line to the ln_history buffer at hst_pos and sets hst_pos to last history item. */
|
||||
void add_line_to_hist(const wchar_t *buf, size_t len, wchar_t (*hst)[MAX_STR_SIZE], int *hst_tot,
|
||||
int *hst_pos);
|
||||
void add_line_to_hist(ChatContext *ctx);
|
||||
|
||||
/* copies history item at hst_pos to buf. Sets pos and len to the len of the history item.
|
||||
hst_pos is decremented or incremented depending on key_dir. */
|
||||
void fetch_hist_item(wchar_t *buf, size_t *pos, size_t *len, wchar_t (*hst)[MAX_STR_SIZE],
|
||||
int hst_tot, int *hst_pos, int key_dir);
|
||||
/* copies history item at hst_pos to line. Sets pos and len to the len of the history item.
|
||||
hst_pos is decremented or incremented depending on key_dir.
|
||||
|
||||
resets line if at end of history */
|
||||
void fetch_hist_item(ChatContext *ctx, int key_dir);
|
||||
|
||||
#endif /* #define TOXIC_STRINGS_H */
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user