mirror of
https://github.com/Tha14/toxic.git
synced 2025-12-06 14:36:34 +01:00
Compare commits
628 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
69be1bc398 | ||
|
|
b4464eda4d | ||
|
|
28dd43608d | ||
|
|
11701d22a1 | ||
|
|
92d76c7f99 | ||
|
|
2a787c1097 | ||
|
|
327259c4c8 | ||
|
|
f173f4275e | ||
|
|
48eaf8a14f | ||
|
|
083611f18e | ||
|
|
48ffae68a9 | ||
|
|
c39f8909cd | ||
|
|
32e541bd1c | ||
|
|
f559bdabfe | ||
|
|
0047ba0e9f | ||
|
|
ecefc19b23 | ||
|
|
e83b397494 | ||
|
|
688ea927f8 | ||
|
|
904f58c0e8 | ||
|
|
035420e5c7 | ||
|
|
444d8e7a74 | ||
|
|
84a0276668 | ||
|
|
312d0c3f42 | ||
|
|
d8eca8393c | ||
|
|
374b78c763 | ||
|
|
409e4ddd96 | ||
|
|
1beb35025b | ||
|
|
51a1c660b4 | ||
|
|
85d3c18ba6 | ||
|
|
d0a7ca17d2 | ||
|
|
36640224af | ||
|
|
231078b6b9 | ||
|
|
414f58d896 | ||
|
|
4d73f8b241 | ||
|
|
82e76a3b5b | ||
|
|
0bc610e18d | ||
|
|
02e6d2db3c | ||
|
|
5a2c341259 | ||
|
|
1a7eaeddba | ||
|
|
f656d0a722 | ||
|
|
09c1ad4566 | ||
|
|
8b9e34db75 | ||
|
|
dd9186e834 | ||
|
|
5ff1517b28 | ||
|
|
bbb639c5aa | ||
|
|
860db2f612 | ||
|
|
523f205646 | ||
|
|
e998c8a866 | ||
|
|
eaea68c33e | ||
|
|
4780cfeafc | ||
|
|
bdb0951c84 | ||
|
|
e3130c92c0 | ||
|
|
12c880ab51 | ||
|
|
522aabd4e4 | ||
|
|
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 | ||
|
|
7ccf4b6432 | ||
|
|
d18ba78d03 |
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
|
||||
|
||||
54
.travis.yml
54
.travis.yml
@@ -4,60 +4,48 @@ compiler:
|
||||
- clang
|
||||
|
||||
before_script:
|
||||
#installing libsodium, needed for Core
|
||||
- git clone git://github.com/jedisct1/libsodium.git > /dev/null
|
||||
# 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 > /dev/null
|
||||
- git checkout tags/1.0.3 > /dev/null
|
||||
- ./autogen.sh > /dev/null
|
||||
- ./configure > /dev/null
|
||||
- make check -j3 > /dev/null
|
||||
- sudo make install >/dev/null
|
||||
- cd ..
|
||||
#installing yasm, needed for compiling vpx
|
||||
- sudo apt-get install yasm > /dev/null
|
||||
#installing libconfig, needed for DHT_bootstrap_daemon
|
||||
- wget http://www.hyperrealm.com/libconfig/libconfig-1.4.9.tar.gz > /dev/null
|
||||
- tar -xvzf libconfig-1.4.9.tar.gz > /dev/null
|
||||
- cd libconfig-1.4.9
|
||||
- ./configure > /dev/null
|
||||
- make -j3 > /dev/null
|
||||
- make check -j2 || make check || exit 1 > /dev/null
|
||||
- sudo make install > /dev/null
|
||||
- cd ..
|
||||
#installing libopus, needed for audio encoding/decoding
|
||||
- wget http://downloads.xiph.org/releases/opus/opus-1.0.3.tar.gz > /dev/null
|
||||
- tar xzf opus-1.0.3.tar.gz > /dev/null
|
||||
- cd opus-1.0.3
|
||||
# Installing libopus, needed for audio encoding/decoding
|
||||
- wget http://downloads.xiph.org/releases/opus/opus-1.1.tar.gz
|
||||
- tar xzf opus-1.1.tar.gz > /dev/null
|
||||
- cd opus-1.1
|
||||
- ./configure > /dev/null
|
||||
- make -j3 > /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 > /dev/null
|
||||
# Installing vpx
|
||||
- git clone https://chromium.googlesource.com/webm/libvpx libvpx
|
||||
- cd libvpx
|
||||
- ./configure --enable-shared > /dev/null
|
||||
- make -j3 >/dev/null
|
||||
- make -j2 || make || exit 1 > /dev/null
|
||||
- sudo make install > /dev/null
|
||||
- cd ..
|
||||
#creating libraries links and updating cache
|
||||
# Creating libraries links and updating cache
|
||||
- sudo ldconfig > /dev/null
|
||||
# creating librarys' links and updating cache
|
||||
- sudo ldconfig
|
||||
- git clone https://github.com/irungentoo/ProjectTox-Core.git toxcore
|
||||
# Installing toxcore
|
||||
- git clone https://github.com/irungentoo/toxcore.git toxcore
|
||||
- cd toxcore
|
||||
- autoreconf -i
|
||||
- ./configure --disable-tests --disable-ntox --disable-daemon --enable-av
|
||||
- make -j2
|
||||
- make -j2 || make || exit 1
|
||||
- sudo make install
|
||||
- cd ..
|
||||
- sudo apt-get install libopenal-dev -yq
|
||||
script:
|
||||
- autoreconf -i
|
||||
- ./configure
|
||||
- make -j2
|
||||
- make -j2 || make || exit 1
|
||||
notifications:
|
||||
email: false
|
||||
|
||||
irc:
|
||||
|
||||
irc:
|
||||
channels:
|
||||
- "chat.freenode.net#tox-dev"
|
||||
on_success: always
|
||||
|
||||
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 avatars.o
|
||||
|
||||
# Check on wich system we are running
|
||||
UNAME_S = $(shell uname -s)
|
||||
ifeq ($(UNAME_S), Linux)
|
||||
-include $(CFG_DIR)/systems/Linux.mk
|
||||
endif
|
||||
ifeq ($(UNAME_S), FreeBSD)
|
||||
-include $(CFG_DIR)/systems/FreeBSD.mk
|
||||
endif
|
||||
ifeq ($(UNAME_S), OpenBSD)
|
||||
-include $(CFG_DIR)/systems/FreeBSD.mk
|
||||
endif
|
||||
ifeq ($(UNAME_S), Darwin)
|
||||
-include $(CFG_DIR)/systems/Darwin.mk
|
||||
endif
|
||||
ifeq ($(UNAME_S), Solaris)
|
||||
-include $(CFG_DIR)/systems/Solaris.mk
|
||||
endif
|
||||
|
||||
# Check on which platform we are running
|
||||
UNAME_M = $(shell uname -m)
|
||||
ifeq ($(UNAME_M), x86_64)
|
||||
-include $(CFG_DIR)/platforms/x86_64.mk
|
||||
endif
|
||||
ifneq ($(filter %86, $(UNAME_M)),)
|
||||
-include $(CFG_DIR)/platforms/x86.mk
|
||||
endif
|
||||
ifneq ($(filter arm%, $(UNAME_M)),)
|
||||
-include $(CFG_DIR)/platforms/arm.mk
|
||||
endif
|
||||
|
||||
# Include all needed checks
|
||||
-include $(CFG_DIR)/checks/check_features.mk
|
||||
|
||||
# Fix path for object files
|
||||
OBJ := $(addprefix $(BUILD_DIR)/, $(OBJ))
|
||||
|
||||
# Targets
|
||||
all: $(BUILD_DIR)/toxic
|
||||
|
||||
$(BUILD_DIR)/toxic: $(OBJ)
|
||||
@echo " LD $(@:$(BUILD_DIR)/%=%)"
|
||||
@$(CC) $(CFLAGS) -o $(BUILD_DIR)/toxic $(OBJ) $(LDFLAGS)
|
||||
|
||||
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
|
||||
@if [ ! -e $(BUILD_DIR) ]; then \
|
||||
mkdir -p $(BUILD_DIR) ;\
|
||||
fi
|
||||
@echo " CC $(@:$(BUILD_DIR)/%=%)"
|
||||
@$(CC) $(CFLAGS) -o $(BUILD_DIR)/$*.o -c $(SRC_DIR)/$*.c
|
||||
@$(CC) -MM $(CFLAGS) $(SRC_DIR)/$*.c > $(BUILD_DIR)/$*.d
|
||||
|
||||
clean:
|
||||
rm -f $(BUILD_DIR)/*.d $(BUILD_DIR)/*.o $(BUILD_DIR)/toxic
|
||||
|
||||
-include $(BUILD_DIR)/$(OBJ:.o=.d)
|
||||
|
||||
-include $(CFG_DIR)/targets/*.mk
|
||||
|
||||
.PHONY: clean all
|
||||
@@ -1,3 +0,0 @@
|
||||
SUBDIRS = build misc
|
||||
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
33
README.md
33
README.md
@@ -1,25 +1,24 @@
|
||||
# Toxic
|
||||
# Toxic [](https://travis-ci.org/Tox/toxic)
|
||||
Toxic is a [Tox](https://tox.chat)-based instant messenging client which formerly resided in the [Tox core repository](https://github.com/irungentoo/toxcore), and is now available as a standalone application.
|
||||
|
||||
[](https://i.imgur.com/san99Z2.png)
|
||||
|
||||
Toxic is an ncurses based instant messaging client for [Tox](http://tox.im) which formerly resided in the [Tox core repository](https://github.com/irungentoo/ProjectTox-Core) and is now available as a standalone program. It looks like [this](http://i.imgur.com/hL7WhVl.png).
|
||||
## Installation
|
||||
* Generate the configure script by running the ```autoreconf -i``` command.
|
||||
[Use our repositories](https://wiki.tox.chat/binaries#other_linux)<br />
|
||||
[Compile it yourself](/INSTALL.md)
|
||||
|
||||
* Execute the configure script with ```./configure``` (you may need to pass it the location of your dependency libraries, i.e.):
|
||||
```./configure --prefix=/where/to/install --with-libtoxcore-headers=/path/to/ProjectTox-Core/toxcore --with-libtoxcore-libs=/path/to/ProjectTox-Core/build/.libs --with-libsodium-headers=/path/to/libsodium/include/ --with-libsodium-libs=/path/to/sodiumtest/lib/ ```
|
||||
## Settings
|
||||
Running Toxic for the first time creates an empty file called toxic.conf in your home configuration directory ("~/.config/tox" for Linux users). Adding options to this file allows you to enable auto-logging, change the time format (12/24 hour), and much more.
|
||||
You can view our example config file [here](misc/toxic.conf.example).
|
||||
|
||||
* Audio calling support requires openal installed
|
||||
* Compile with --disable-av to build without audio call support
|
||||
* Compile and install the program with ```make && sudo make install```
|
||||
|
||||
#### Notes
|
||||
If your default prefix is /usr/local and you get the error: "error while loading shared libraries: libtoxcore.so.0: cannot open shared object file: No such file or directory", then you can try running ```sudo ldconfig```. If that doesn't fix it, run:
|
||||
## Troubleshooting
|
||||
If your default prefix is "/usr/local" and you receive the following:
|
||||
```
|
||||
error while loading shared libraries: libtoxcore.so.0: cannot open shared object file: No such file or directory
|
||||
```
|
||||
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 may need to install the ncurses libraries. For Debian based systems:
|
||||
```
|
||||
sudo apt-get install libncurses5-dev libncursesw5-dev
|
||||
```
|
||||
## Settings
|
||||
After running Toxic for the first time an empty file called toxic.conf should reside in your home configuration directory (~/.config/tox for Linux users). For an example on how to use this config file to save settings such as auto-logging and time format see: toxic/misc/toxic.conf
|
||||
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
#Don't change this unless needed, else you'll break stuff
|
||||
|
||||
bin_PROGRAMS = toxic
|
||||
|
||||
|
||||
toxic_SOURCES = $(top_srcdir)/src/toxic.c \
|
||||
$(top_srcdir)/src/toxic.h \
|
||||
$(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/windows.c \
|
||||
$(top_srcdir)/src/windows.h \
|
||||
$(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 \
|
||||
$(top_srcdir)/src/log.c \
|
||||
$(top_srcdir)/src/log.h \
|
||||
$(top_srcdir)/src/file_senders.c \
|
||||
$(top_srcdir)/src/file_senders.h \
|
||||
$(top_srcdir)/src/line_info.c \
|
||||
$(top_srcdir)/src/line_info.h \
|
||||
$(top_srcdir)/src/settings.c \
|
||||
$(top_srcdir)/src/settings.h \
|
||||
$(top_srcdir)/src/dns.c \
|
||||
$(top_srcdir)/src/dns.h
|
||||
|
||||
toxic_CFLAGS = -I$(top_srcdir) \
|
||||
$(NCURSES_CFLAGS) \
|
||||
$(LIBSODIUM_CFLAGS) \
|
||||
$(LIBTOXCORE_CFLAGS) \
|
||||
$(PTHREAD_CFLAGS)
|
||||
|
||||
toxic_CPPFLAGS = '-DTOXICVER="$(TOXIC_VERSION)"'
|
||||
|
||||
toxic_LDADD = $(LIBTOXCORE_LDFLAGS) \
|
||||
$(LIBSODIUM_LDFLAGS) \
|
||||
$(NCURSES_LIBS) \
|
||||
$(LIBTOXCORE_LIBS) \
|
||||
$(LIBSODIUM_LIBS) \
|
||||
$(WINSOCK2_LIBS) \
|
||||
$(PTHREAD_LIBS)
|
||||
|
||||
|
||||
# For audio support
|
||||
if BUILD_AV
|
||||
|
||||
toxic_SOURCES += $(top_srcdir)/src/audio_call.c \
|
||||
$(top_srcdir)/src/audio_call.h
|
||||
|
||||
toxic_CFLAGS += $(LIBTOXAV_CFLAGS) \
|
||||
$(OPENAL_CFLAGS)
|
||||
|
||||
toxic_LDADD += $(LIBTOXAV_LIBS) \
|
||||
$(OPENAL_LIBS)
|
||||
endif
|
||||
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
|
||||
33
cfg/global_vars.mk
Normal file
33
cfg/global_vars.mk
Normal file
@@ -0,0 +1,33 @@
|
||||
# Version
|
||||
TOXIC_VERSION = 0.6.0
|
||||
REV = $(shell git rev-list HEAD --count 2>/dev/null || echo -n "error")
|
||||
ifneq (, $(findstring error, $(REV)))
|
||||
VERSION = $(TOXIC_VERSION)
|
||||
else
|
||||
VERSION = $(TOXIC_VERSION)_r$(REV)
|
||||
endif
|
||||
|
||||
# Project directories
|
||||
BUILD_DIR = $(BASE_DIR)/build
|
||||
DOC_DIR = $(BASE_DIR)/doc
|
||||
SRC_DIR = $(BASE_DIR)/src
|
||||
SND_DIR = $(BASE_DIR)/sounds
|
||||
MISC_DIR = $(BASE_DIR)/misc
|
||||
|
||||
# Project files
|
||||
MANFILES = toxic.1 toxic.conf.5
|
||||
DATAFILES = DHTnodes DNSservers toxic.conf.example
|
||||
DESKFILE = toxic.desktop
|
||||
SNDFILES = ToxicContactOnline.wav ToxicContactOffline.wav ToxicError.wav
|
||||
SNDFILES += ToxicRecvMessage.wav ToxicOutgoingCall.wav ToxicIncomingCall.wav
|
||||
SNDFILES += ToxicTransferComplete.wav ToxicTransferStart.wav
|
||||
|
||||
# Install directories
|
||||
PREFIX = /usr/local
|
||||
BINDIR = $(PREFIX)/bin
|
||||
DATADIR = $(PREFIX)/share/toxic
|
||||
MANDIR = $(PREFIX)/share/man
|
||||
APPDIR = $(PREFIX)/share/applications
|
||||
|
||||
# Platform tools
|
||||
PKG_CONFIG = pkg-config
|
||||
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
|
||||
22
cfg/targets/help.mk
Normal file
22
cfg/targets/help.mk
Normal file
@@ -0,0 +1,22 @@
|
||||
# Help target
|
||||
help:
|
||||
@echo "-- Targets --"
|
||||
@echo " all: Build toxic and documentation [DEFAULT]"
|
||||
@echo " toxic: Build toxic"
|
||||
@echo " doc: Build documentation"
|
||||
@echo " install: Build toxic and install it in PREFIX (default PREFIX is \"$(abspath $(PREFIX))\")"
|
||||
@echo " uninstall: Remove toxic from PREFIX (default PREFIX is \"$(abspath $(PREFIX))\")"
|
||||
@echo " clean: Remove built files"
|
||||
@echo " help: This help"
|
||||
@echo
|
||||
@echo "-- Variables --"
|
||||
@echo " DISABLE_X11: Set to \"1\" to force building without X11 support"
|
||||
@echo " DISABLE_AV: Set to \"1\" to force building without audio call support"
|
||||
@echo " DISABLE_SOUND_NOTIFY: Set to \"1\" to force building without sound notification support"
|
||||
@echo " DISABLE_DESKTOP_NOTIFY: Set to \"1\" to force building without desktop notifications support"
|
||||
@echo " USER_CFLAGS: Add custom flags to default CFLAGS"
|
||||
@echo " USER_LDFLAGS: Add custom flags to default LDFLAGS"
|
||||
@echo " PREFIX: Specify a prefix directory for binaries, data files,... (default is \"$(abspath $(PREFIX))\")"
|
||||
@echo " DESTDIR: Specify a directory where to store installed files (mainly for packaging purpose)"
|
||||
|
||||
.PHONY: help
|
||||
41
cfg/targets/install.mk
Normal file
41
cfg/targets/install.mk
Normal file
@@ -0,0 +1,41 @@
|
||||
# Install target
|
||||
install: $(BUILD_DIR)/toxic
|
||||
@echo "Installing toxic executable"
|
||||
@mkdir -p $(abspath $(DESTDIR)/$(BINDIR))
|
||||
@install -m 0755 $(BUILD_DIR)/toxic $(abspath $(DESTDIR)/$(BINDIR)/toxic)
|
||||
|
||||
@echo "Installing desktop file"
|
||||
@mkdir -p $(abspath $(DESTDIR)/$(APPDIR))
|
||||
@install -m 0644 $(MISC_DIR)/$(DESKFILE) $(abspath $(DESTDIR)/$(APPDIR)/$(DESKFILE))
|
||||
|
||||
@echo "Installing data files"
|
||||
@mkdir -p $(abspath $(DESTDIR)/$(DATADIR))
|
||||
@for f in $(DATAFILES) ; do \
|
||||
install -m 0644 $(MISC_DIR)/$$f $(abspath $(DESTDIR)/$(DATADIR)/$$f) ;\
|
||||
file=$(abspath $(DESTDIR)/$(DATADIR)/$$f) ;\
|
||||
sed -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$file > temp_file && \
|
||||
mv temp_file $$file ;\
|
||||
done
|
||||
@mkdir -p $(abspath $(DESTDIR)/$(DATADIR))/sounds
|
||||
@for f in $(SNDFILES) ; do \
|
||||
install -m 0644 $(SND_DIR)/$$f $(abspath $(DESTDIR)/$(DATADIR)/sounds/$$f) ;\
|
||||
done
|
||||
|
||||
@echo "Installing man pages"
|
||||
@mkdir -p $(abspath $(DESTDIR)/$(MANDIR))
|
||||
@for f in $(MANFILES) ; do \
|
||||
if [ ! -e "$(DOC_DIR)/$$f" ]; then \
|
||||
continue ;\
|
||||
fi ;\
|
||||
section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $$f | rev | cut -d "." -f 1` ;\
|
||||
file=$$section/$$f ;\
|
||||
mkdir -p $$section ;\
|
||||
install -m 0644 $(DOC_DIR)/$$f $$file ;\
|
||||
sed -e 's:__VERSION__:'$(VERSION)':g' $$file > temp_file && \
|
||||
mv temp_file $$file ;\
|
||||
sed -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$file > temp_file && \
|
||||
mv temp_file $$file ;\
|
||||
gzip -f -9 $$file ;\
|
||||
done
|
||||
|
||||
.PHONY: install
|
||||
24
cfg/targets/uninstall.mk
Normal file
24
cfg/targets/uninstall.mk
Normal file
@@ -0,0 +1,24 @@
|
||||
# Uninstall target
|
||||
uninstall:
|
||||
@echo "Removing toxic executable"
|
||||
@rm -f $(abspath $(DESTDIR)/$(BINDIR)/toxic)
|
||||
|
||||
@echo "Removing desktop file"
|
||||
@rm -f $(abspath $(DESTDIR)/$(APPDIR)/$(DESKFILE))
|
||||
|
||||
@echo "Removing data files"
|
||||
@for f in $(DATAFILES) ; do \
|
||||
rm -f $(abspath $(DESTDIR)/$(DATADIR)/$$f) ;\
|
||||
done
|
||||
@for f in $(SNDFILES) ; do \
|
||||
rm -f $(abspath $(DESTDIR)/$(DATADIR)/sounds/$$f) ;\
|
||||
done
|
||||
|
||||
@echo "Removing man pages"
|
||||
@for f in $(MANFILES) ; do \
|
||||
section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $$f | rev | cut -d "." -f 1` ;\
|
||||
file=$$section/$$f ;\
|
||||
rm -f $$file $$file.gz ;\
|
||||
done
|
||||
|
||||
.PHONY: uninstall
|
||||
506
configure.ac
506
configure.ac
@@ -1,506 +0,0 @@
|
||||
# -*- Autoconf -*-
|
||||
# Process this file with autoconf to produce a configure script.
|
||||
|
||||
AC_PREREQ([2.65])
|
||||
AC_INIT([toxic], [0.4.1], [https://tox.im/])
|
||||
AC_CONFIG_AUX_DIR(configure_aux)
|
||||
AC_CONFIG_SRCDIR([src/toxic.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:/usr/local/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
|
||||
MACH=no
|
||||
AC_CANONICAL_HOST
|
||||
case $host_os in
|
||||
*mingw*)
|
||||
WIN32="yes"
|
||||
;;
|
||||
darwin*)
|
||||
MACH=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])])
|
||||
|
||||
AX_PTHREAD( [], [ AC_MSG_ERROR([pthreads not found 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
|
||||
|
||||
AC_CHECK_HEADER([resolv.h], [],
|
||||
[
|
||||
AC_MSG_ERROR([resolv.h header was not found on your system])
|
||||
])
|
||||
|
||||
AC_CHECK_LIB(resolv, __res_init, [],
|
||||
[
|
||||
AC_MSG_ERROR([libresolv library was not found on your system])
|
||||
])
|
||||
|
||||
####
|
||||
#### A/V Stuff
|
||||
|
||||
AV_SEARCH_DIR=
|
||||
BUILD_AV="yes"
|
||||
|
||||
AC_ARG_WITH(libtoxav-prefix,
|
||||
AC_HELP_STRING([--with-libtoxav-prefix=DIR],
|
||||
[search for libtoxav in DIR, i.e. look for libraries in
|
||||
DIR/lib and for headers in DIR/include]),
|
||||
[
|
||||
AV_SEARCH_DIR="$withval"
|
||||
]
|
||||
)
|
||||
|
||||
if test -n "$AV_SEARCH_DIR"; then
|
||||
CFLAGS="$CFLAGS -I$AV_SEARCH_DIR/include"
|
||||
CPPFLAGS="$CPPFLAGS -I$AV_SEARCH_DIR/include"
|
||||
LDFLAGS="$LDFLAGS -L$AV_SEARCH_DIR/lib"
|
||||
export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$AV_SEARCH_DIR/lib/pkgconfig
|
||||
fi
|
||||
|
||||
# Check if specified enable
|
||||
AC_ARG_ENABLE([av],
|
||||
[AC_HELP_STRING([--disable-av], [build AV support libraries (default: auto)]) ],
|
||||
[
|
||||
if test "x$enableval" = "xno"; then
|
||||
BUILD_AV="no"
|
||||
elif test "x$enableval" = "xyes"; then
|
||||
BUILD_AV="yes"
|
||||
fi
|
||||
]
|
||||
)
|
||||
|
||||
# Check for A/V library
|
||||
|
||||
if test "x$BUILD_AV" = "xyes"; then
|
||||
PKG_CHECK_MODULES([OPENAL], [openal],
|
||||
[],
|
||||
[
|
||||
if test "x$MACH" = "xyes"; then
|
||||
CFLAGS="$CFLAGS -framework OpenAL"
|
||||
AC_CHECK_HEADER([OpenAL/al.h],
|
||||
[
|
||||
OPENAL_CFLAGS="-framework OpenAL"
|
||||
OPENAL_LIBS="-framework OpenAL"
|
||||
AC_SUBST(OPENAL_CFLAGS)
|
||||
AC_SUBST(OPENAL_LIBS)
|
||||
],
|
||||
[
|
||||
AC_MSG_NOTICE([No openal framework; disabling A/V support])
|
||||
BUILD_AV="no"
|
||||
]
|
||||
)
|
||||
CFLAGS="$CFLAGS_SAVE"
|
||||
else
|
||||
AC_MSG_NOTICE([No openal library; disabling A/V support])
|
||||
BUILD_AV="no"
|
||||
fi
|
||||
])
|
||||
fi
|
||||
|
||||
if test "x$BUILD_AV" = "xyes"; then
|
||||
PKG_CHECK_MODULES([LIBTOXAV], [libtoxav],
|
||||
[
|
||||
AC_CHECK_HEADER([tox/toxav.h],
|
||||
[
|
||||
# Place define for audio support
|
||||
AC_DEFINE([_SUPPORT_AUDIO], [], [Is audio supported])
|
||||
AC_MSG_NOTICE([Building with audio support])
|
||||
],
|
||||
[
|
||||
AC_MSG_NOTICE([No A/V headers; disabling A/V support])
|
||||
BUILD_AV="no"
|
||||
],)
|
||||
],
|
||||
[
|
||||
AC_MSG_NOTICE([No A/V library; disabling A/V support])
|
||||
BUILD_AV="no"
|
||||
])
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(BUILD_AV, test "x$BUILD_AV" = "xyes")
|
||||
|
||||
TOXIC_VERSION="$PACKAGE_VERSION"
|
||||
AC_PATH_PROG([GIT], [git], [no])
|
||||
if test "x$GIT" != "xno"; then
|
||||
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
|
||||
158
doc/toxic.1
Normal file
158
doc/toxic.1
Normal file
@@ -0,0 +1,158 @@
|
||||
'\" t
|
||||
.\" Title: toxic
|
||||
.\" Author: [see the "AUTHORS" section]
|
||||
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
|
||||
.\" Date: 2015-03-28
|
||||
.\" Manual: Toxic Manual
|
||||
.\" Source: toxic __VERSION__
|
||||
.\" Language: English
|
||||
.\"
|
||||
.TH "TOXIC" "1" "2015\-03\-28" "toxic __VERSION__" "Toxic Manual"
|
||||
.\" -----------------------------------------------------------------
|
||||
.\" * Define some portability stuff
|
||||
.\" -----------------------------------------------------------------
|
||||
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
.\" http://bugs.debian.org/507673
|
||||
.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
|
||||
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.\" -----------------------------------------------------------------
|
||||
.\" * set default formatting
|
||||
.\" -----------------------------------------------------------------
|
||||
.\" disable hyphenation
|
||||
.nh
|
||||
.\" disable justification (adjust text to left margin only)
|
||||
.ad l
|
||||
.\" -----------------------------------------------------------------
|
||||
.\" * MAIN CONTENT STARTS HERE *
|
||||
.\" -----------------------------------------------------------------
|
||||
.SH "NAME"
|
||||
toxic \- CLI client for Tox
|
||||
.SH "SYNOPSIS"
|
||||
.sp
|
||||
\fBtoxic\fR [\-f \fIdata\-file\fR] [\-x] [\-4] [\-c \fIconfig\-file\fR] [\-n \fInodes\-file\fR] [\-h]
|
||||
.SH "DESCRIPTION"
|
||||
.sp
|
||||
toxic is an ncurses\-based instant messaging client for Tox which formerly resided in the Tox core repository, and is now available as a standalone application\&.
|
||||
.SH "OPTIONS"
|
||||
.PP
|
||||
\-4, \-\-ipv4
|
||||
.RS 4
|
||||
Force IPv4 connection
|
||||
.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
|
||||
\-T, \-\-tcp\-relay
|
||||
.RS 4
|
||||
Act as a TCP relay server for the network (Note: this uses significantly more bandwidth)
|
||||
.RE
|
||||
.PP
|
||||
\-u, \-\-unencrypt\-data
|
||||
.RS 4
|
||||
Unencrypt a data file\&. A warning will appear if this option is used with a data file that is already unencrypted\&.
|
||||
.RE
|
||||
.SH "FILES"
|
||||
.PP
|
||||
__DATADIR__/DHTnodes
|
||||
.RS 4
|
||||
Default list of DHT bootstrap nodes\&.
|
||||
.RE
|
||||
.PP
|
||||
~/\&.config/tox/data
|
||||
.RS 4
|
||||
Savestate which contains your personal info (nickname, Tox ID, contacts, etc)
|
||||
.RE
|
||||
.PP
|
||||
~/\&.config/tox/toxic\&.conf
|
||||
.RS 4
|
||||
Configuration file\&. See
|
||||
\fBtoxic\&.conf\fR(5) for more details\&.
|
||||
.RE
|
||||
.PP
|
||||
__DATADIR__/toxic\&.conf\&.example
|
||||
.RS 4
|
||||
Configuration example\&.
|
||||
.RE
|
||||
.SH "BUGS"
|
||||
.sp
|
||||
Unicode characters with a width larger than 1 column may cause strange behaviour\&. Expect more bugs and bad behaviour: this software is in a pre\-alpha stage\&.
|
||||
.SH "AUTHORS"
|
||||
.sp
|
||||
JFreegman <JFreegman@gmail\&.com>
|
||||
.SH "SEE ALSO"
|
||||
.sp
|
||||
\fBtoxic\&.conf\fR(5)
|
||||
.SH "LINKS"
|
||||
.sp
|
||||
Project page: https://github\&.com/Tox/toxic
|
||||
.sp
|
||||
IRC channel: chat\&.freenode\&.net#tox
|
||||
102
doc/toxic.1.asc
Normal file
102
doc/toxic.1.asc
Normal file
@@ -0,0 +1,102 @@
|
||||
toxic(1)
|
||||
========
|
||||
|
||||
NAME
|
||||
----
|
||||
toxic - CLI client for Tox
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
*toxic* [-f 'data-file'] [-x] [-4] [-c 'config-file'] [-n 'nodes-file'] [-h]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
toxic is an ncurses-based instant messaging client for Tox which formerly
|
||||
resided in the Tox core repository, and is now available as a standalone
|
||||
application.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
-4, --ipv4::
|
||||
Force IPv4 connection
|
||||
|
||||
-b, --debug::
|
||||
Enable stderr for debugging. Redirect output to
|
||||
avoid breaking the curses interface and better capture messages.
|
||||
|
||||
-c, --config config-file::
|
||||
Use specified 'config-file' instead of '~/.config/tox/toxic.conf'
|
||||
|
||||
-d, --default-locale::
|
||||
Use default locale
|
||||
|
||||
-e, --encrypt-data::
|
||||
Encrypt an unencrypted data file. An error will occur if this option
|
||||
is used with an encrypted data file.
|
||||
|
||||
-f, --file data-file::
|
||||
Use specified 'data-file' instead of '~/.config/tox/data'
|
||||
|
||||
-h, --help::
|
||||
Show help message
|
||||
|
||||
-n, --nodes nodes-file::
|
||||
Use specified 'nodes-file' for DHT bootstrap nodes, instead of
|
||||
'{datadir}/DHTnodes'
|
||||
|
||||
-o, --noconnect::
|
||||
Do not connect to the DHT network
|
||||
|
||||
-p, --SOCKS5-proxy::
|
||||
Use a SOCKS5 proxy: Requires [IP] [port]
|
||||
|
||||
-P, --HTTP-proxy::
|
||||
Use a HTTP proxy: Requires [IP] [port]
|
||||
|
||||
-r, --dnslist::
|
||||
Use specified DNSservers file
|
||||
|
||||
-t, --force-tcp::
|
||||
Force TCP connection (use this with proxies)
|
||||
|
||||
-T, --tcp-relay::
|
||||
Act as a TCP relay server for the network (Note: this uses significantly more bandwidth)
|
||||
|
||||
-u, --unencrypt-data::
|
||||
Unencrypt a data file. A warning will appear if this option is used
|
||||
with a data file that is already unencrypted.
|
||||
|
||||
FILES
|
||||
-----
|
||||
{datadir}/DHTnodes::
|
||||
Default list of DHT bootstrap nodes.
|
||||
|
||||
~/.config/tox/data::
|
||||
Savestate which contains your personal info (nickname, Tox ID, contacts,
|
||||
etc)
|
||||
|
||||
~/.config/tox/toxic.conf::
|
||||
Configuration file. See *toxic.conf*(5) for more details.
|
||||
|
||||
{datadir}/toxic.conf.example::
|
||||
Configuration example.
|
||||
|
||||
BUGS
|
||||
----
|
||||
Unicode characters with a width larger than 1 column may cause strange
|
||||
behaviour. Expect more bugs and bad behaviour: this software is in a
|
||||
pre-alpha stage.
|
||||
|
||||
AUTHORS
|
||||
-------
|
||||
JFreegman <JFreegman@gmail.com>
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
*toxic.conf*(5)
|
||||
|
||||
LINKS
|
||||
-----
|
||||
Project page: <https://github.com/Tox/toxic>
|
||||
|
||||
IRC channel: chat.freenode.net#tox
|
||||
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-03-28
|
||||
.\" Manual: Toxic Manual
|
||||
.\" Source: toxic __VERSION__
|
||||
.\" Language: English
|
||||
.\"
|
||||
.TH "TOXIC\&.CONF" "5" "2015\-03\-28" "toxic __VERSION__" "Toxic Manual"
|
||||
.\" -----------------------------------------------------------------
|
||||
.\" * Define some portability stuff
|
||||
.\" -----------------------------------------------------------------
|
||||
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
.\" http://bugs.debian.org/507673
|
||||
.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
|
||||
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.\" -----------------------------------------------------------------
|
||||
.\" * set default formatting
|
||||
.\" -----------------------------------------------------------------
|
||||
.\" disable hyphenation
|
||||
.nh
|
||||
.\" disable justification (adjust text to left margin only)
|
||||
.ad l
|
||||
.\" -----------------------------------------------------------------
|
||||
.\" * MAIN CONTENT STARTS HERE *
|
||||
.\" -----------------------------------------------------------------
|
||||
.SH "NAME"
|
||||
toxic.conf \- Configuration file for toxic
|
||||
.SH "SYNOPSIS"
|
||||
.sp
|
||||
~/\&.config/tox/toxic\&.conf
|
||||
.SH "DESCRIPTION"
|
||||
.sp
|
||||
The \fItoxic\&.conf\fR file is the main configuration file for \fBtoxic\fR(1) client\&. It uses syntax accepted by \fBlibconfig\fR\&. Lines starting with "//" are comments and will be ignored\&.
|
||||
.SH "EXAMPLE"
|
||||
.sp
|
||||
.if n \{\
|
||||
.RS 4
|
||||
.\}
|
||||
.nf
|
||||
// Configuration for interface
|
||||
ui = {
|
||||
timestamps = true;
|
||||
alerts = false;
|
||||
};
|
||||
|
||||
// Configuration for audio
|
||||
audio = {
|
||||
input_device = 1;
|
||||
};
|
||||
.fi
|
||||
.if n \{\
|
||||
.RE
|
||||
.\}
|
||||
.SH "OPTIONS"
|
||||
.PP
|
||||
\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>
|
||||
@@ -1,52 +0,0 @@
|
||||
--- /src/misc_tools.c
|
||||
+++ /src/misc_tools.c
|
||||
@@ -54,24 +54,11 @@
|
||||
return val;
|
||||
}
|
||||
|
||||
-/* Get the current local time */
|
||||
-struct tm *get_time(void)
|
||||
-{
|
||||
- struct tm *timeinfo;
|
||||
- time_t now;
|
||||
- time(&now);
|
||||
- timeinfo = localtime(&now);
|
||||
- return timeinfo;
|
||||
-}
|
||||
-
|
||||
/* Prints the time to given window */
|
||||
void print_time(WINDOW *window)
|
||||
{
|
||||
- uint8_t s[MAX_STR_SIZE];
|
||||
- strftime(s, MAX_STR_SIZE, "[%H:%M:%S] ", get_time());
|
||||
-
|
||||
wattron(window, COLOR_PAIR(BLUE));
|
||||
- wprintw(window, "%s", s);
|
||||
+ wprintw(window, "[%d] ", (int)time(NULL));
|
||||
wattroff(window,COLOR_PAIR(BLUE));
|
||||
}
|
||||
|
||||
--- /src/log.c
|
||||
+++ /src/log.c
|
||||
@@ -51,9 +51,7 @@
|
||||
sprintf(&ident[2], "%02X", key[2] & 0xff);
|
||||
ident[KEY_IDENT_DIGITS*2+1] = '\0';
|
||||
} else {
|
||||
- uint8_t s[MAX_STR_SIZE];
|
||||
- strftime(s, MAX_STR_SIZE, "%Y-%m-%d[%H:%M:%S]", get_time());
|
||||
- snprintf(ident, sizeof(ident), "%s", s);
|
||||
+ snprintf(ident, sizeof(ident), "[%d]", (int)time(NULL));
|
||||
path_len += strlen(ident) + 1;
|
||||
}
|
||||
|
||||
@@ -95,9 +93,7 @@
|
||||
else
|
||||
snprintf(name_frmt, sizeof(name_frmt), "%s:", name);
|
||||
|
||||
- uint8_t s[MAX_STR_SIZE];
|
||||
- strftime(s, MAX_STR_SIZE, "%Y/%m/%d [%H:%M:%S]", get_time());
|
||||
- fprintf(log->file,"%s %s %s\n", s, name_frmt, msg);
|
||||
+ fprintf(log->file,"[%d]\n", (int)time(NULL), name_frmt, msg);
|
||||
|
||||
uint64_t curtime = (uint64_t) time(NULL);
|
||||
|
||||
317
m4/ax_pthread.m4
317
m4/ax_pthread.m4
@@ -1,317 +0,0 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_pthread.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# This macro figures out how to build C programs using POSIX threads. It
|
||||
# sets the PTHREAD_LIBS output variable to the threads library and linker
|
||||
# flags, and the PTHREAD_CFLAGS output variable to any special C compiler
|
||||
# flags that are needed. (The user can also force certain compiler
|
||||
# flags/libs to be tested by setting these environment variables.)
|
||||
#
|
||||
# Also sets PTHREAD_CC to any special C compiler that is needed for
|
||||
# multi-threaded programs (defaults to the value of CC otherwise). (This
|
||||
# is necessary on AIX to use the special cc_r compiler alias.)
|
||||
#
|
||||
# NOTE: You are assumed to not only compile your program with these flags,
|
||||
# but also link it with them as well. e.g. you should link with
|
||||
# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
|
||||
#
|
||||
# If you are only building threads programs, you may wish to use these
|
||||
# variables in your default LIBS, CFLAGS, and CC:
|
||||
#
|
||||
# LIBS="$PTHREAD_LIBS $LIBS"
|
||||
# CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||
# CC="$PTHREAD_CC"
|
||||
#
|
||||
# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
|
||||
# has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name
|
||||
# (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
|
||||
#
|
||||
# Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the
|
||||
# PTHREAD_PRIO_INHERIT symbol is defined when compiling with
|
||||
# PTHREAD_CFLAGS.
|
||||
#
|
||||
# ACTION-IF-FOUND is a list of shell commands to run if a threads library
|
||||
# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it
|
||||
# is not found. If ACTION-IF-FOUND is not specified, the default action
|
||||
# will define HAVE_PTHREAD.
|
||||
#
|
||||
# Please let the authors know if this macro fails on any platform, or if
|
||||
# you have any other suggestions or comments. This macro was based on work
|
||||
# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help
|
||||
# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by
|
||||
# Alejandro Forero Cuervo to the autoconf macro repository. We are also
|
||||
# grateful for the helpful feedback of numerous users.
|
||||
#
|
||||
# Updated for Autoconf 2.68 by Daniel Richard G.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
|
||||
# Copyright (c) 2011 Daniel Richard G. <skunk@iSKUNK.ORG>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by the
|
||||
# Free Software Foundation, either version 3 of the License, or (at your
|
||||
# option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
# Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# As a special exception, the respective Autoconf Macro's copyright owner
|
||||
# gives unlimited permission to copy, distribute and modify the configure
|
||||
# scripts that are the output of Autoconf when processing the Macro. You
|
||||
# need not follow the terms of the GNU General Public License when using
|
||||
# or distributing such scripts, even though portions of the text of the
|
||||
# Macro appear in them. The GNU General Public License (GPL) does govern
|
||||
# all other use of the material that constitutes the Autoconf Macro.
|
||||
#
|
||||
# This special exception to the GPL applies to versions of the Autoconf
|
||||
# Macro released by the Autoconf Archive. When you make and distribute a
|
||||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 20
|
||||
|
||||
AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
|
||||
AC_DEFUN([AX_PTHREAD], [
|
||||
AC_REQUIRE([AC_CANONICAL_HOST])
|
||||
AC_LANG_PUSH([C])
|
||||
ax_pthread_ok=no
|
||||
|
||||
# We used to check for pthread.h first, but this fails if pthread.h
|
||||
# requires special compiler flags (e.g. on True64 or Sequent).
|
||||
# It gets checked for in the link test anyway.
|
||||
|
||||
# First of all, check if the user has set any of the PTHREAD_LIBS,
|
||||
# etcetera environment variables, and if threads linking works using
|
||||
# them:
|
||||
if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
|
||||
save_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||
save_LIBS="$LIBS"
|
||||
LIBS="$PTHREAD_LIBS $LIBS"
|
||||
AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
|
||||
AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes)
|
||||
AC_MSG_RESULT($ax_pthread_ok)
|
||||
if test x"$ax_pthread_ok" = xno; then
|
||||
PTHREAD_LIBS=""
|
||||
PTHREAD_CFLAGS=""
|
||||
fi
|
||||
LIBS="$save_LIBS"
|
||||
CFLAGS="$save_CFLAGS"
|
||||
fi
|
||||
|
||||
# We must check for the threads library under a number of different
|
||||
# names; the ordering is very important because some systems
|
||||
# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
|
||||
# libraries is broken (non-POSIX).
|
||||
|
||||
# Create a list of thread flags to try. Items starting with a "-" are
|
||||
# C compiler flags, and other items are library names, except for "none"
|
||||
# which indicates that we try without any flags at all, and "pthread-config"
|
||||
# which is a program returning the flags for the Pth emulation library.
|
||||
|
||||
ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
|
||||
|
||||
# The ordering *is* (sometimes) important. Some notes on the
|
||||
# individual items follow:
|
||||
|
||||
# pthreads: AIX (must check this before -lpthread)
|
||||
# none: in case threads are in libc; should be tried before -Kthread and
|
||||
# other compiler flags to prevent continual compiler warnings
|
||||
# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
|
||||
# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
|
||||
# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
|
||||
# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
|
||||
# -pthreads: Solaris/gcc
|
||||
# -mthreads: Mingw32/gcc, Lynx/gcc
|
||||
# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
|
||||
# doesn't hurt to check since this sometimes defines pthreads too;
|
||||
# also defines -D_REENTRANT)
|
||||
# ... -mt is also the pthreads flag for HP/aCC
|
||||
# pthread: Linux, etcetera
|
||||
# --thread-safe: KAI C++
|
||||
# pthread-config: use pthread-config program (for GNU Pth library)
|
||||
|
||||
case ${host_os} in
|
||||
solaris*)
|
||||
|
||||
# On Solaris (at least, for some versions), libc contains stubbed
|
||||
# (non-functional) versions of the pthreads routines, so link-based
|
||||
# tests will erroneously succeed. (We need to link with -pthreads/-mt/
|
||||
# -lpthread.) (The stubs are missing pthread_cleanup_push, or rather
|
||||
# a function called by this macro, so we could check for that, but
|
||||
# who knows whether they'll stub that too in a future libc.) So,
|
||||
# we'll just look for -pthreads and -lpthread first:
|
||||
|
||||
ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags"
|
||||
;;
|
||||
|
||||
darwin*)
|
||||
ax_pthread_flags="-pthread $ax_pthread_flags"
|
||||
;;
|
||||
esac
|
||||
|
||||
if test x"$ax_pthread_ok" = xno; then
|
||||
for flag in $ax_pthread_flags; do
|
||||
|
||||
case $flag in
|
||||
none)
|
||||
AC_MSG_CHECKING([whether pthreads work without any flags])
|
||||
;;
|
||||
|
||||
-*)
|
||||
AC_MSG_CHECKING([whether pthreads work with $flag])
|
||||
PTHREAD_CFLAGS="$flag"
|
||||
;;
|
||||
|
||||
pthread-config)
|
||||
AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no)
|
||||
if test x"$ax_pthread_config" = xno; then continue; fi
|
||||
PTHREAD_CFLAGS="`pthread-config --cflags`"
|
||||
PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
|
||||
;;
|
||||
|
||||
*)
|
||||
AC_MSG_CHECKING([for the pthreads library -l$flag])
|
||||
PTHREAD_LIBS="-l$flag"
|
||||
;;
|
||||
esac
|
||||
|
||||
save_LIBS="$LIBS"
|
||||
save_CFLAGS="$CFLAGS"
|
||||
LIBS="$PTHREAD_LIBS $LIBS"
|
||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||
|
||||
# Check for various functions. We must include pthread.h,
|
||||
# since some functions may be macros. (On the Sequent, we
|
||||
# need a special flag -Kthread to make this header compile.)
|
||||
# We check for pthread_join because it is in -lpthread on IRIX
|
||||
# while pthread_create is in libc. We check for pthread_attr_init
|
||||
# due to DEC craziness with -lpthreads. We check for
|
||||
# pthread_cleanup_push because it is one of the few pthread
|
||||
# functions on Solaris that doesn't have a non-functional libc stub.
|
||||
# We try pthread_create on general principles.
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>
|
||||
static void routine(void *a) { a = 0; }
|
||||
static void *start_routine(void *a) { return a; }],
|
||||
[pthread_t th; pthread_attr_t attr;
|
||||
pthread_create(&th, 0, start_routine, 0);
|
||||
pthread_join(th, 0);
|
||||
pthread_attr_init(&attr);
|
||||
pthread_cleanup_push(routine, 0);
|
||||
pthread_cleanup_pop(0) /* ; */])],
|
||||
[ax_pthread_ok=yes],
|
||||
[])
|
||||
|
||||
LIBS="$save_LIBS"
|
||||
CFLAGS="$save_CFLAGS"
|
||||
|
||||
AC_MSG_RESULT($ax_pthread_ok)
|
||||
if test "x$ax_pthread_ok" = xyes; then
|
||||
break;
|
||||
fi
|
||||
|
||||
PTHREAD_LIBS=""
|
||||
PTHREAD_CFLAGS=""
|
||||
done
|
||||
fi
|
||||
|
||||
# Various other checks:
|
||||
if test "x$ax_pthread_ok" = xyes; then
|
||||
save_LIBS="$LIBS"
|
||||
LIBS="$PTHREAD_LIBS $LIBS"
|
||||
save_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||
|
||||
# Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
|
||||
AC_MSG_CHECKING([for joinable pthread attribute])
|
||||
attr_name=unknown
|
||||
for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>],
|
||||
[int attr = $attr; return attr /* ; */])],
|
||||
[attr_name=$attr; break],
|
||||
[])
|
||||
done
|
||||
AC_MSG_RESULT($attr_name)
|
||||
if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
|
||||
AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name,
|
||||
[Define to necessary symbol if this constant
|
||||
uses a non-standard name on your system.])
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING([if more special flags are required for pthreads])
|
||||
flag=no
|
||||
case ${host_os} in
|
||||
aix* | freebsd* | darwin*) flag="-D_THREAD_SAFE";;
|
||||
osf* | hpux*) flag="-D_REENTRANT";;
|
||||
solaris*)
|
||||
if test "$GCC" = "yes"; then
|
||||
flag="-D_REENTRANT"
|
||||
else
|
||||
flag="-mt -D_REENTRANT"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
AC_MSG_RESULT(${flag})
|
||||
if test "x$flag" != xno; then
|
||||
PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT],
|
||||
ax_cv_PTHREAD_PRIO_INHERIT, [
|
||||
AC_LINK_IFELSE([
|
||||
AC_LANG_PROGRAM([[#include <pthread.h>]], [[int i = PTHREAD_PRIO_INHERIT;]])],
|
||||
[ax_cv_PTHREAD_PRIO_INHERIT=yes],
|
||||
[ax_cv_PTHREAD_PRIO_INHERIT=no])
|
||||
])
|
||||
AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"],
|
||||
AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], 1, [Have PTHREAD_PRIO_INHERIT.]))
|
||||
|
||||
LIBS="$save_LIBS"
|
||||
CFLAGS="$save_CFLAGS"
|
||||
|
||||
# More AIX lossage: compile with *_r variant
|
||||
if test "x$GCC" != xyes; then
|
||||
case $host_os in
|
||||
aix*)
|
||||
AS_CASE(["x/$CC"],
|
||||
[x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6],
|
||||
[#handle absolute path differently from PATH based program lookup
|
||||
AS_CASE(["x$CC"],
|
||||
[x/*],
|
||||
[AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])],
|
||||
[AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])])
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
fi
|
||||
|
||||
test -n "$PTHREAD_CC" || PTHREAD_CC="$CC"
|
||||
|
||||
AC_SUBST(PTHREAD_LIBS)
|
||||
AC_SUBST(PTHREAD_CFLAGS)
|
||||
AC_SUBST(PTHREAD_CC)
|
||||
|
||||
# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
|
||||
if test x"$ax_pthread_ok" = xyes; then
|
||||
ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
|
||||
:
|
||||
else
|
||||
ax_pthread_ok=no
|
||||
$2
|
||||
fi
|
||||
AC_LANG_POP
|
||||
])dnl AX_PTHREAD
|
||||
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
|
||||
@@ -1,8 +1,25 @@
|
||||
192.254.75.98 33445 951C88B7E75C867418ACDB5D273821372BB5BD652740BCDF623A4FA293E75D2F
|
||||
37.187.46.132 33445 A9D98212B3F972BD11DA52BEB0658C326FCCC1BFD49F347F9C2D3D8B61E1B927
|
||||
192.254.75.102 33445 951C88B7E75C867418ACDB5D273821372BB5BD652740BCDF623A4FA293E75D2F
|
||||
144.76.60.215 33445 04119E835DF3E78BACF0F84235B300546AF8B936F035185E2A8E9E0A67C8924F
|
||||
23.226.230.47 33445 A09162D68618E742FFBCA1C2C70385E6679604B2D80EA6E84AD0996A1AC8A074
|
||||
37.187.20.216 33445 4FD54CFD426A338399767E56FD0F44F5E35FA8C38C8E87C8DC3FEAC0160F8E17
|
||||
54.199.139.199 33445 7F9C31FE850E97CEFD4C4591DF93FC757C7C12549DDD55F8EEAECC34FE76C029
|
||||
109.169.46.133 33445 7F31BFC93B8E4016A902144D0B110C3EA97CB7D43F1C4D21BCAE998A7C838821
|
||||
192.210.149.121 33445 F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67
|
||||
178.62.125.224 33445 10B20C49ACBD968D7C80F2E8438F92EA51F189F4E70CFBBB2C2C8C799E97F03E
|
||||
178.21.112.187 33445 4B2C19E924972CB9B57732FB172F8A8604DE13EEDA2A6234E348983344B23057
|
||||
195.154.119.113 33445 E398A69646B8CEACA9F0B84F553726C1C49270558C57DF5F3C368F05A7D71354
|
||||
192.210.149.121 33445 F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67
|
||||
76.191.23.96 33445 93574A3FAB7D612FEA29FD8D67D3DD10DFD07A075A5D62E8AF3DD9F5D0932E11
|
||||
46.38.239.179 33445 F5A1A38EFB6BD3C2C8AF8B10D85F0F89E931704D349F1D0720C3C4059AF2440A
|
||||
178.62.250.138 33445 788236D34978D1D5BD822F0A5BEBD2C53C64CC31CD3149350EE27D4D9A2F9B6B
|
||||
78.225.128.39 33445 7A2306BFBA665E5480AE59B31E116BE9C04DCEFE04D9FE25082316FA34B4DA0C
|
||||
130.133.110.14 33445 461FA3776EF0FA655F1A05477DF1B3B614F7D6B124F7DB1DD4FE3C08B03B640F
|
||||
104.167.101.29 33445 5918AC3C06955962A75AD7DF4F80A5D7C34F7DB9E1498D2E0495DE35B3FE8A57
|
||||
195.154.109.148 33445 391C96CB67AE893D4782B8E4495EB9D89CF1031F48460C06075AA8CE76D50A21
|
||||
192.3.173.88 33445 3E1FFDEB667BFF549F619EC6737834762124F50A89C8D0DBF1DDF64A2DD6CD1B
|
||||
205.185.116.116 33445 A179B09749AC826FF01F37A9613F6B57118AE014D4196A0E1105A98F93A54702
|
||||
198.98.51.198 33445 1D5A5F2F5D6233058BF0259B09622FB40B482E4FA0931EB8FD3AB8E7BF7DAF6F
|
||||
80.232.246.79 33445 A7A060D553B017D9D8F038E265C7AFB6C70BAAC55070197F9C007432D0038E0F
|
||||
108.61.165.198 33445 8E7D0B859922EF569298B4D261A8CCB5FEA14FB91ED412A7603A585A25698832
|
||||
212.71.252.109 33445 C4CEB8C7AC607C6B374E2E782B3C00EA3A63B80D4910B8649CCACDD19F260819
|
||||
194.249.212.109 33445 3CEE1F054081E7A011234883BC4FC39F661A55B73637A5AC293DDF1251D9432B
|
||||
194.249.212.109 443 3CEE1F054081E7A011234883BC4FC39F661A55B73637A5AC293DDF1251D9432B
|
||||
103.38.216.87 33445 601AEE6FC8C17E8CD8F8F1FFC4D4AD84E59A73BE451F037194E7A404E3795320
|
||||
185.25.116.107 33445 DA4E4ED4B697F2E9B000EEFE3A34B554ACD3F45F5C96EAEA2516DD7FF9AF7B43
|
||||
192.99.168.140 33445 6A4D0607A296838434A6A7DDF99F50EF9D60A2C510BBF31FE538A25CB6B4652F
|
||||
|
||||
2
misc/DNSservers
Normal file
2
misc/DNSservers
Normal file
@@ -0,0 +1,2 @@
|
||||
utox.org d3154f65d28a5b41a05d4ac7e4b39c6b1c233cc857fb365c56e8392737462a12
|
||||
toxme.se 5d72c517df6aec54f1e977a6b6f25914ea4cf7277a85027cd9f5196df17e0b13
|
||||
@@ -1 +0,0 @@
|
||||
dist_pkgdata_DATA = DHTnodes
|
||||
@@ -1,23 +0,0 @@
|
||||
# 24 or 12 hour time
|
||||
time:24;
|
||||
|
||||
# 1 to enable autologging, 0 to disable
|
||||
autolog:0;
|
||||
|
||||
# 1 to disbale terminal alerts on messages, 0 to enable
|
||||
disable_alerts:0;
|
||||
|
||||
# maximum lines for chat window history
|
||||
history_size:700;
|
||||
|
||||
# 1 to use native terminal colours, 0 to use toxic default colour theme
|
||||
colour_theme:0;
|
||||
|
||||
# preferred audio input device; numbers correspond to /lsdev in
|
||||
audio_in_dev:0;
|
||||
|
||||
# preferred audio output device; numbers correspond to /lsdev out
|
||||
audio_out_dev:0;
|
||||
|
||||
# preferred path for downloads
|
||||
download_path:/home/USERNAME/Downloads/;
|
||||
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.
|
||||
923
src/audio_call.c
923
src/audio_call.c
File diff suppressed because it is too large
Load Diff
@@ -20,37 +20,32 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _audio_h
|
||||
#define _audio_h
|
||||
#ifndef AUDIO_H
|
||||
#define AUDIO_H
|
||||
|
||||
#include <tox/toxav.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
#define MAX_DEVICES 32
|
||||
#include "device.h"
|
||||
|
||||
typedef enum _AudioError {
|
||||
NoError = 0,
|
||||
ErrorStartingCaptureDevice = 1 << 0,
|
||||
ErrorStartingOutputDevice = 1 << 1,
|
||||
ErrorStartingCoreAudio = 1 << 2
|
||||
ae_None = 0,
|
||||
ae_StartingCaptureDevice = 1 << 0,
|
||||
ae_StartingOutputDevice = 1 << 1,
|
||||
ae_StartingCoreAudio = 1 << 2
|
||||
} AudioError;
|
||||
|
||||
typedef enum _Devices {
|
||||
input,
|
||||
output,
|
||||
} _Devices;
|
||||
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 otherwise undefined behaviour will
|
||||
*/
|
||||
/* 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);
|
||||
|
||||
int errors();
|
||||
|
||||
int start_transmission(ToxWindow *self);
|
||||
int device_set(ToxWindow *self, _Devices type, long int selection);
|
||||
|
||||
#endif /* _audio_h */
|
||||
#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->pos > ctx->len || ctx->len >= MAX_STR_SIZE || size > MAX_STR_SIZE)
|
||||
return -1;
|
||||
|
||||
const char *L = (char *) list;
|
||||
const char *endchrs = " ";
|
||||
char ubuf[MAX_STR_SIZE];
|
||||
|
||||
/* work with multibyte string copy of buf for simplicity */
|
||||
if (wcs_to_mbs_buf(ubuf, ctx->line, sizeof(ubuf)) == -1)
|
||||
return -1;
|
||||
|
||||
/* TODO: generalize this */
|
||||
bool dir_search = !strncmp(ubuf, "/sendfile", strlen("/sendfile"))
|
||||
|| !strncmp(ubuf, "/avatar", strlen("/avatar"));
|
||||
|
||||
/* isolate substring from space behind pos to pos */
|
||||
char tmp[MAX_STR_SIZE];
|
||||
snprintf(tmp, sizeof(tmp), "%s", ubuf);
|
||||
tmp[ctx->pos] = '\0';
|
||||
|
||||
const char *s = dir_search ? strchr(tmp, '\"') : strrchr(tmp, ' ');
|
||||
char *sub = malloc(strlen(ubuf) + 1);
|
||||
|
||||
if (sub == NULL)
|
||||
exit_toxic_err("failed in complete_line", FATALERR_MEMORY);
|
||||
|
||||
if (!s && !dir_search) {
|
||||
strcpy(sub, tmp);
|
||||
|
||||
if (sub[0] != '/')
|
||||
endchrs = ": ";
|
||||
} else if (s) {
|
||||
strcpy(sub, &s[1]);
|
||||
|
||||
if (dir_search) {
|
||||
int sub_len = strlen(sub);
|
||||
int si = char_rfind(sub, '/', sub_len);
|
||||
|
||||
if (si || *sub == '/')
|
||||
memmove(sub, &sub[si + 1], sub_len - si);
|
||||
}
|
||||
}
|
||||
|
||||
if (!sub[0]) {
|
||||
free(sub);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int s_len = strlen(sub);
|
||||
const char *str;
|
||||
int n_matches = 0;
|
||||
char matches[n_items][MAX_STR_SIZE];
|
||||
int i = 0;
|
||||
|
||||
/* put all list matches in matches array */
|
||||
for (i = 0; i < n_items; ++i) {
|
||||
str = &L[i * size];
|
||||
|
||||
if (strncasecmp(str, sub, s_len) == 0)
|
||||
strcpy(matches[n_matches++], str);
|
||||
}
|
||||
|
||||
free(sub);
|
||||
|
||||
if (!n_matches)
|
||||
return -1;
|
||||
|
||||
if (!dir_search && n_matches > 1)
|
||||
print_matches(self, NULL, matches, n_matches, MAX_STR_SIZE);
|
||||
|
||||
char match[MAX_STR_SIZE];
|
||||
get_str_match(self, match, matches, n_matches);
|
||||
|
||||
if (dir_search) {
|
||||
if (n_matches == 1)
|
||||
endchrs = char_rfind(match, '.', strlen(match)) ? "\"" : "/";
|
||||
else
|
||||
endchrs = "";
|
||||
} else if (n_matches > 1) {
|
||||
endchrs = "";
|
||||
}
|
||||
|
||||
/* put match in correct spot in buf and append endchars */
|
||||
int n_endchrs = strlen(endchrs);
|
||||
int m_len = strlen(match);
|
||||
int strt = ctx->pos - s_len;
|
||||
int diff = m_len - s_len + n_endchrs;
|
||||
|
||||
if (ctx->len + diff >= MAX_STR_SIZE)
|
||||
return -1;
|
||||
|
||||
char tmpend[MAX_STR_SIZE];
|
||||
snprintf(tmpend, sizeof(tmpend), "%s", &ubuf[ctx->pos]);
|
||||
strcpy(&ubuf[strt], match);
|
||||
strcpy(&ubuf[strt + m_len], endchrs);
|
||||
strcpy(&ubuf[strt + m_len + n_endchrs], tmpend);
|
||||
|
||||
/* convert to widechar and copy back to original buf */
|
||||
wchar_t newbuf[MAX_STR_SIZE];
|
||||
|
||||
if (mbs_to_wcs_buf(newbuf, ubuf, sizeof(newbuf) / sizeof(wchar_t)) == -1)
|
||||
return -1;
|
||||
|
||||
wcscpy(ctx->line, newbuf);
|
||||
|
||||
ctx->len += diff;
|
||||
ctx->pos += diff;
|
||||
|
||||
return diff;
|
||||
}
|
||||
|
||||
/* transforms a tab complete starting with the shorthand "~" into the full home directory.*/
|
||||
static void complt_home_dir(ToxWindow *self, char *path, int pathsize, const char *cmd, int cmdlen)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
char homedir[MAX_STR_SIZE] = {0};
|
||||
get_home_dir(homedir, sizeof(homedir));
|
||||
|
||||
char newline[MAX_STR_SIZE];
|
||||
snprintf(newline, sizeof(newline), "%s \"%s%s", cmd, homedir, path + 1);
|
||||
snprintf(path, pathsize, "%s", &newline[cmdlen]);
|
||||
|
||||
wchar_t wline[MAX_STR_SIZE];
|
||||
|
||||
if (mbs_to_wcs_buf(wline, newline, sizeof(wline) / sizeof(wchar_t)) == -1)
|
||||
return;
|
||||
|
||||
int newlen = wcslen(wline);
|
||||
|
||||
if (ctx->len + newlen >= MAX_STR_SIZE)
|
||||
return;
|
||||
|
||||
wmemcpy(ctx->line, wline, newlen + 1);
|
||||
ctx->pos = newlen;
|
||||
ctx->len = ctx->pos;
|
||||
}
|
||||
|
||||
/* attempts to match /command "<incomplete-dir>" line to matching directories.
|
||||
|
||||
if only one match, auto-complete line.
|
||||
return diff between old len and new len of ctx->line, -1 if no matches or > 1 match */
|
||||
#define MAX_DIRS 512
|
||||
|
||||
int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd)
|
||||
{
|
||||
char b_path[MAX_STR_SIZE];
|
||||
char b_name[MAX_STR_SIZE];
|
||||
char b_cmd[MAX_STR_SIZE];
|
||||
const wchar_t *tmpline = &line[wcslen(cmd) + 2]; /* start after "/command \"" */
|
||||
|
||||
if (wcs_to_mbs_buf(b_path, tmpline, sizeof(b_path)) == -1)
|
||||
return -1;
|
||||
|
||||
if (wcs_to_mbs_buf(b_cmd, cmd, sizeof(b_cmd)) == -1)
|
||||
return -1;
|
||||
|
||||
if (b_path[0] == '~')
|
||||
complt_home_dir(self, b_path, sizeof(b_path), b_cmd, strlen(b_cmd) + 2);
|
||||
|
||||
int si = char_rfind(b_path, '/', strlen(b_path));
|
||||
|
||||
if (!b_path[0]) { /* list everything in pwd */
|
||||
b_path[0] = '.';
|
||||
b_path[1] = '\0';
|
||||
} else if (!si && b_path[0] != '/') { /* look for matches in pwd */
|
||||
char tmp[MAX_STR_SIZE];
|
||||
snprintf(tmp, sizeof(tmp), ".%s", b_path);
|
||||
snprintf(b_path, sizeof(b_path), "%s", tmp);
|
||||
}
|
||||
|
||||
snprintf(b_name, sizeof(b_name), "%s", &b_path[si + 1]);
|
||||
b_path[si + 1] = '\0';
|
||||
int b_name_len = strlen(b_name);
|
||||
DIR *dp = opendir(b_path);
|
||||
|
||||
if (dp == NULL)
|
||||
return -1;
|
||||
|
||||
char dirnames[MAX_DIRS][NAME_MAX];
|
||||
struct dirent *entry;
|
||||
int dircount = 0;
|
||||
|
||||
while ((entry = readdir(dp)) && dircount < MAX_DIRS) {
|
||||
if (strncmp(entry->d_name, b_name, b_name_len) == 0
|
||||
&& strcmp(".", entry->d_name) && strcmp("..", entry->d_name)) {
|
||||
snprintf(dirnames[dircount], sizeof(dirnames[dircount]), "%s", entry->d_name);
|
||||
++dircount;
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dp);
|
||||
|
||||
if (dircount == 0)
|
||||
return -1;
|
||||
|
||||
if (dircount > 1) {
|
||||
qsort(dirnames, dircount, NAME_MAX, qsort_strcasecmp_hlpr);
|
||||
print_matches(self, m, dirnames, dircount, NAME_MAX);
|
||||
}
|
||||
|
||||
return complete_line(self, dirnames, dircount, NAME_MAX);
|
||||
}
|
||||
42
src/autocomplete.h
Normal file
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 */
|
||||
205
src/avatars.c
Normal file
205
src/avatars.c
Normal file
@@ -0,0 +1,205 @@
|
||||
/* avatars.c
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2015 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "misc_tools.h"
|
||||
#include "file_transfers.h"
|
||||
#include "friendlist.h"
|
||||
#include "avatars.h"
|
||||
|
||||
extern FriendsList Friends;
|
||||
|
||||
static struct Avatar {
|
||||
char name[TOX_MAX_FILENAME_LENGTH + 1];
|
||||
size_t name_len;
|
||||
char path[PATH_MAX + 1];
|
||||
size_t path_len;
|
||||
off_t size;
|
||||
} Avatar;
|
||||
|
||||
|
||||
static void avatar_clear(void)
|
||||
{
|
||||
memset(&Avatar, 0, sizeof(struct Avatar));
|
||||
}
|
||||
|
||||
/* Sends avatar to friendnum.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
int avatar_send(Tox *m, uint32_t friendnum)
|
||||
{
|
||||
TOX_ERR_FILE_SEND err;
|
||||
uint32_t filenum = tox_file_send(m, friendnum, TOX_FILE_KIND_AVATAR, (size_t) Avatar.size,
|
||||
NULL, (uint8_t *) Avatar.name, Avatar.name_len, &err);
|
||||
if (Avatar.size == 0)
|
||||
return 0;
|
||||
|
||||
if (err != TOX_ERR_FILE_SEND_OK) {
|
||||
fprintf(stderr, "tox_file_send failed for friendnumber %d (error %d)\n", friendnum, err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct FileTransfer *ft = new_file_transfer(NULL, friendnum, filenum, FILE_TRANSFER_SEND, TOX_FILE_KIND_AVATAR);
|
||||
|
||||
if (!ft)
|
||||
return -1;
|
||||
|
||||
ft->file = fopen(Avatar.path, "r");
|
||||
|
||||
if (ft->file == NULL)
|
||||
return -1;
|
||||
|
||||
snprintf(ft->file_name, sizeof(ft->file_name), "%s", Avatar.name);
|
||||
ft->file_size = Avatar.size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Sends avatar to all friends */
|
||||
static void avatar_send_all(Tox *m)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < Friends.max_idx; ++i) {
|
||||
if (Friends.list[i].connection_status != TOX_CONNECTION_NONE)
|
||||
avatar_send(m, Friends.list[i].num);
|
||||
}
|
||||
}
|
||||
|
||||
/* Sets avatar to path and sends it to all online contacts.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
int avatar_set(Tox *m, const char *path, size_t path_len)
|
||||
{
|
||||
if (path_len == 0 || path_len >= sizeof(Avatar.path))
|
||||
return -1;
|
||||
|
||||
FILE *fp = fopen(path, "rb");
|
||||
|
||||
if (fp == NULL)
|
||||
return -1;
|
||||
|
||||
char PNG_signature[8] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
|
||||
|
||||
if (check_file_signature(PNG_signature, sizeof(PNG_signature), fp) != 0) {
|
||||
fclose(fp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
off_t size = file_size(path);
|
||||
|
||||
if (size == 0 || size > MAX_AVATAR_FILE_SIZE)
|
||||
return -1;
|
||||
|
||||
get_file_name(Avatar.name, sizeof(Avatar.name), path);
|
||||
Avatar.name_len = strlen(Avatar.name);
|
||||
snprintf(Avatar.path, sizeof(Avatar.path), "%s", path);
|
||||
Avatar.path_len = path_len;
|
||||
Avatar.size = size;
|
||||
|
||||
avatar_send_all(m);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Unsets avatar and sends to all online contacts.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
void avatar_unset(Tox *m)
|
||||
{
|
||||
avatar_clear();
|
||||
avatar_send_all(m);
|
||||
}
|
||||
|
||||
void on_avatar_file_control(Tox *m, struct FileTransfer *ft, TOX_FILE_CONTROL control)
|
||||
{
|
||||
switch (control) {
|
||||
case TOX_FILE_CONTROL_RESUME:
|
||||
if (ft->state == FILE_TRANSFER_PENDING) {
|
||||
ft->state = FILE_TRANSFER_STARTED;
|
||||
} else if (ft->state == FILE_TRANSFER_PAUSED) {
|
||||
ft->state = FILE_TRANSFER_STARTED;
|
||||
}
|
||||
break;
|
||||
|
||||
case TOX_FILE_CONTROL_PAUSE:
|
||||
ft->state = FILE_TRANSFER_PAUSED;
|
||||
break;
|
||||
|
||||
case TOX_FILE_CONTROL_CANCEL:
|
||||
close_file_transfer(NULL, m, ft, -1, NULL, silent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void on_avatar_chunk_request(Tox *m, struct FileTransfer *ft, uint64_t position, size_t length)
|
||||
{
|
||||
if (ft->state != FILE_TRANSFER_STARTED)
|
||||
return;
|
||||
|
||||
if (length == 0) {
|
||||
close_file_transfer(NULL, m, ft, -1, NULL, silent);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ft->file == NULL) {
|
||||
close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ft->position != position) {
|
||||
if (fseek(ft->file, position, SEEK_SET) == -1) {
|
||||
close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent);
|
||||
return;
|
||||
}
|
||||
|
||||
ft->position = position;
|
||||
}
|
||||
|
||||
uint8_t send_data[length];
|
||||
size_t send_length = fread(send_data, 1, sizeof(send_data), ft->file);
|
||||
|
||||
if (send_length != length) {
|
||||
close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent);
|
||||
return;
|
||||
}
|
||||
|
||||
TOX_ERR_FILE_SEND_CHUNK err;
|
||||
tox_file_send_chunk(m, ft->friendnum, ft->filenum, position, send_data, send_length, &err);
|
||||
|
||||
if (err != TOX_ERR_FILE_SEND_CHUNK_OK)
|
||||
fprintf(stderr, "tox_file_send_chunk failed in avatar callback (error %d)\n", err);
|
||||
|
||||
ft->position += send_length;
|
||||
ft->last_keep_alive = get_unix_time();
|
||||
}
|
||||
52
src/avatars.h
Normal file
52
src/avatars.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/* avatars.h
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2015 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef AVATARS_H
|
||||
#define AVATARS_H
|
||||
|
||||
#define MAX_AVATAR_FILE_SIZE 65536
|
||||
|
||||
/* Sends avatar to friendnum.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
int avatar_send(Tox *m, uint32_t friendnum);
|
||||
|
||||
/* Sets avatar to path and sends it to all online contacts.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
int avatar_set(Tox *m, const char *path, size_t length);
|
||||
|
||||
/* Unsets avatar and sends to all online contacts.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
void avatar_unset(Tox *m);
|
||||
|
||||
void on_avatar_chunk_request(Tox *m, struct FileTransfer *ft, uint64_t position, size_t length);
|
||||
void on_avatar_file_control(Tox *m, struct FileTransfer *ft, TOX_FILE_CONTROL control);
|
||||
|
||||
#endif /* AVATARS_H */
|
||||
1354
src/chat.c
1354
src/chat.c
File diff suppressed because it is too large
Load Diff
11
src/chat.h
11
src/chat.h
@@ -20,13 +20,16 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CHAT_H_6489PZ13
|
||||
#define CHAT_H_6489PZ13
|
||||
#ifndef CHAT_H
|
||||
#define CHAT_H
|
||||
|
||||
#include "windows.h"
|
||||
#include "toxic.h"
|
||||
|
||||
void kill_chat_window(ToxWindow *self);
|
||||
/* set CTRL to -1 if we don't want to send a control signal.
|
||||
set msg to NULL if we don't want to display a message */
|
||||
void chat_close_file_receiver(Tox *m, int filenum, int friendnum, int CTRL);
|
||||
void kill_chat_window(ToxWindow *self, Tox *m);
|
||||
ToxWindow new_chat(Tox *m, int32_t friendnum);
|
||||
|
||||
#endif /* end of include guard: CHAT_H_6489PZ13 */
|
||||
#endif /* end of include guard: CHAT_H */
|
||||
|
||||
@@ -20,10 +20,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -33,252 +29,282 @@
|
||||
#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])
|
||||
{
|
||||
struct history *hst = self->chatwin->hst;
|
||||
line_info_clear(hst);
|
||||
struct line_info *start = hst->line_start;
|
||||
|
||||
if (argc == 1) {
|
||||
if (!strcmp(argv[1], "global")) {
|
||||
execute(window, self, m, "/help", GLOBAL_COMMAND_MODE);
|
||||
return;
|
||||
}
|
||||
if (argc < 2) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Requires type in|out and the file ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t *msg = "Chat commands:";
|
||||
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 1, CYAN);
|
||||
char msg[MAX_STR_SIZE];
|
||||
const char *inoutstr = argv[1];
|
||||
int idx = atoi(argv[2]);
|
||||
|
||||
#ifdef _SUPPORT_AUDIO
|
||||
#define NUMLINES 13
|
||||
#else
|
||||
#define NUMLINES 9
|
||||
#endif
|
||||
if (idx >= MAX_FILES || idx < 0) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t lines[NUMLINES][MAX_STR_SIZE] = {
|
||||
struct FileTransfer *ft = NULL;
|
||||
|
||||
#ifdef _SUPPORT_AUDIO
|
||||
{ " /call : Audio call" },
|
||||
{ " /cancel : Cancel call" },
|
||||
{ " /answer : Answer incomming call" },
|
||||
{ " /reject : Reject incoming call" },
|
||||
{ " /hangup : Hangup active call" },
|
||||
#endif /* _SUPPORT_AUDIO */
|
||||
{ " /invite <n> : Invite friend to a group chat" },
|
||||
{ " /join : Join a pending group chat" },
|
||||
{ " /log <on> or <off> : Enable/disable logging" },
|
||||
{ " /sendfile <filepath> : Send a file" },
|
||||
{ " /savefile <n> : Receive a file" },
|
||||
{ " /close : Close the current chat window" },
|
||||
{ " /help : Print this message again" },
|
||||
{ " /help global : Show a list of global commands" },
|
||||
};
|
||||
/* cancel an incoming file transfer */
|
||||
if (strcasecmp(inoutstr, "in") == 0) {
|
||||
ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_RECV);
|
||||
} else if (strcasecmp(inoutstr, "out") == 0) {
|
||||
ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_SEND);
|
||||
} else {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type must be 'in' or 'out'.");
|
||||
return;
|
||||
}
|
||||
|
||||
int i;
|
||||
if (!ft) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < NUMLINES; ++i)
|
||||
line_info_add(self, NULL, NULL, NULL, lines[i], SYS_MSG, 0, 0);
|
||||
if (ft->state == FILE_TRANSFER_INACTIVE) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
msg = " * Use Page Up/Page Down to scroll chat history\n";
|
||||
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 1, CYAN);
|
||||
|
||||
hst->line_start = start;
|
||||
snprintf(msg, sizeof(msg), "File transfer for '%s' aborted.", ft->file_name);
|
||||
close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, silent);
|
||||
}
|
||||
|
||||
void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
uint8_t *errmsg;
|
||||
|
||||
if (argc < 1) {
|
||||
errmsg = "Invalid syntax";
|
||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group number required.");
|
||||
return;
|
||||
}
|
||||
|
||||
int groupnum = atoi(argv[1]);
|
||||
|
||||
if (groupnum == 0 && strcmp(argv[1], "0")) { /* atoi returns 0 value on invalid input */
|
||||
errmsg = "Invalid syntax.";
|
||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid group number.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (tox_invite_friend(m, self->num, groupnum) == -1) {
|
||||
errmsg = "Failed to invite friend.";
|
||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to invite contact to group.");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t msg[MAX_STR_SIZE];
|
||||
snprintf(msg, sizeof(msg), "Invited friend to Room #%d.", groupnum);
|
||||
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||
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])
|
||||
{
|
||||
uint8_t *errmsg;
|
||||
|
||||
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
|
||||
errmsg = " * Warning: Too many windows are open.";
|
||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, RED);
|
||||
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') {
|
||||
errmsg = "No pending group chat invite.";
|
||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||
if (!Friends.list[self->num].group_invite.pending) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending group chat invite.");
|
||||
return;
|
||||
}
|
||||
|
||||
int groupnum = tox_join_groupchat(m, self->num, groupkey);
|
||||
int groupnum = -1;
|
||||
|
||||
if (type == TOX_GROUPCHAT_TYPE_TEXT)
|
||||
groupnum = tox_join_groupchat(m, self->num, (uint8_t *) groupkey, length);
|
||||
#ifdef AUDIO
|
||||
else
|
||||
groupnum = toxav_join_av_groupchat(m, self->num, (uint8_t *) groupkey, length,
|
||||
NULL, NULL);
|
||||
#endif
|
||||
|
||||
if (groupnum == -1) {
|
||||
errmsg = "Group chat instance failed to initialize.";
|
||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (init_groupchat_win(prompt, m, groupnum) == -1) {
|
||||
errmsg = "Group chat window failed to initialize.";
|
||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||
if (init_groupchat_win(prompt, m, groupnum, type) == -1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize.");
|
||||
tox_del_groupchat(m, groupnum);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
uint8_t *errmsg;
|
||||
|
||||
if (argc != 1) {
|
||||
errmsg = "Invalid syntax.";
|
||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||
if (argc < 1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File ID required.");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t filenum = atoi(argv[1]);
|
||||
int idx = atoi(argv[1]);
|
||||
|
||||
if ((filenum == 0 && strcmp(argv[1], "0")) || filenum >= MAX_FILES) {
|
||||
errmsg = "No pending file transfers with that number.";
|
||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||
if ((idx == 0 && strcmp(argv[1], "0")) || idx >= MAX_FILES) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!friends[self->num].file_receiver.pending[filenum]) {
|
||||
errmsg = "No pending file transfers with that number.";
|
||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||
struct FileTransfer *ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_RECV);
|
||||
|
||||
if (!ft) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t *filename = friends[self->num].file_receiver.filenames[filenum];
|
||||
|
||||
if (tox_file_send_control(m, self->num, 1, filenum, TOX_FILECONTROL_ACCEPT, 0, 0) == 0) {
|
||||
uint8_t msg[MAX_STR_SIZE];
|
||||
snprintf(msg, sizeof(msg), "Saving file as: '%s' (%.1f%%)", filename, 0.0);
|
||||
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||
friends[self->num].file_receiver.line_id[filenum] = self->chatwin->hst->line_end->id + 1;
|
||||
|
||||
if ((friends[self->num].file_receiver.files[filenum] = fopen(filename, "a")) == NULL) {
|
||||
errmsg = "* Error writing to file.";
|
||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, RED);
|
||||
tox_file_send_control(m, self->num, 1, filenum, TOX_FILECONTROL_KILL, 0, 0);
|
||||
}
|
||||
} else {
|
||||
errmsg = "File transfer failed.";
|
||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||
if (ft->state != FILE_TRANSFER_PENDING) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
friends[self->num].file_receiver.pending[filenum] = false;
|
||||
if ((ft->file = fopen(ft->file_path, "a")) == NULL) {
|
||||
const char *msg = "File transfer failed: Invalid file path.";
|
||||
close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
|
||||
return;
|
||||
}
|
||||
|
||||
TOX_ERR_FILE_CONTROL err;
|
||||
tox_file_control(m, self->num, ft->filenum, TOX_FILE_CONTROL_RESUME, &err);
|
||||
|
||||
if (err != TOX_ERR_FILE_CONTROL_OK)
|
||||
goto on_recv_error;
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Saving file [%d] as: '%s'", idx, ft->file_path);
|
||||
|
||||
/* prep progress bar line */
|
||||
char progline[MAX_STR_SIZE];
|
||||
init_progress_bar(progline);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", progline);
|
||||
|
||||
ft->line_id = self->chatwin->hst->line_end->id + 2;
|
||||
ft->state = FILE_TRANSFER_STARTED;
|
||||
|
||||
return;
|
||||
|
||||
on_recv_error:
|
||||
switch (err) {
|
||||
case TOX_ERR_FILE_CONTROL_FRIEND_NOT_FOUND:
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Friend not found.");
|
||||
return;
|
||||
|
||||
case TOX_ERR_FILE_CONTROL_FRIEND_NOT_CONNECTED:
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Friend is not online.");
|
||||
return;
|
||||
|
||||
case TOX_ERR_FILE_CONTROL_NOT_FOUND:
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Invalid filenumber.");
|
||||
return;
|
||||
|
||||
case TOX_ERR_FILE_CONTROL_SENDQ:
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Connection error.");
|
||||
return;
|
||||
|
||||
default:
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed (error %d)\n", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
uint8_t *errmsg;
|
||||
|
||||
if (max_file_senders_index >= (MAX_FILES - 1)) {
|
||||
errmsg = "Please wait for some of your outgoing file transfers to complete.";
|
||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||
return;
|
||||
}
|
||||
const char *errmsg = NULL;
|
||||
|
||||
if (argc < 1) {
|
||||
errmsg = "Invalid syntax.";
|
||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path required.");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t *path = argv[1];
|
||||
|
||||
if (path[0] != '\"') {
|
||||
errmsg = "File path must be enclosed in quotes.";
|
||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||
if (argv[1][0] != '\"') {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path must be enclosed in quotes.");
|
||||
return;
|
||||
}
|
||||
|
||||
path[strlen(++path) - 1] = L'\0';
|
||||
int path_len = strlen(path);
|
||||
/* remove opening and closing quotes */
|
||||
char path[MAX_STR_SIZE];
|
||||
snprintf(path, sizeof(path), "%s", &argv[1][1]);
|
||||
int path_len = strlen(path) - 1;
|
||||
path[path_len] = '\0';
|
||||
|
||||
if (path_len > MAX_STR_SIZE) {
|
||||
errmsg = "File path exceeds character limit.";
|
||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||
if (path_len >= MAX_STR_SIZE) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path exceeds character limit.");
|
||||
return;
|
||||
}
|
||||
|
||||
FILE *file_to_send = fopen(path, "r");
|
||||
|
||||
if (file_to_send == NULL) {
|
||||
errmsg = "File not found.";
|
||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
fseek(file_to_send, 0, SEEK_END);
|
||||
uint64_t filesize = ftell(file_to_send);
|
||||
fseek(file_to_send, 0, SEEK_SET);
|
||||
off_t filesize = file_size(path);
|
||||
|
||||
uint8_t filename[MAX_STR_SIZE];
|
||||
get_file_name(filename, path);
|
||||
int filenum = tox_new_file_sender(m, self->num, filesize, filename, strlen(filename));
|
||||
|
||||
if (filenum == -1) {
|
||||
errmsg = "Error sending file.";
|
||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||
if (filesize == 0) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file.");
|
||||
fclose(file_to_send);
|
||||
return;
|
||||
}
|
||||
|
||||
int i;
|
||||
char file_name[TOX_MAX_FILENAME_LENGTH];
|
||||
size_t namelen = get_file_name(file_name, sizeof(file_name), path);
|
||||
|
||||
for (i = 0; i < MAX_FILES; ++i) {
|
||||
if (!file_senders[i].active) {
|
||||
memcpy(file_senders[i].pathname, path, path_len + 1);
|
||||
file_senders[i].active = true;
|
||||
file_senders[i].toxwin = self;
|
||||
file_senders[i].file = file_to_send;
|
||||
file_senders[i].filenum = filenum;
|
||||
file_senders[i].friendnum = self->num;
|
||||
file_senders[i].timestamp = get_unix_time();
|
||||
file_senders[i].size = filesize;
|
||||
file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1,
|
||||
tox_file_data_size(m, self->num), file_to_send);
|
||||
TOX_ERR_FILE_SEND err;
|
||||
uint32_t filenum = tox_file_send(m, self->num, TOX_FILE_KIND_DATA, (uint64_t) filesize, NULL,
|
||||
(uint8_t *) file_name, namelen, &err);
|
||||
|
||||
uint8_t msg[MAX_STR_SIZE];
|
||||
snprintf(msg, sizeof(msg), "Sending file: '%s'", path);
|
||||
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||
if (err != TOX_ERR_FILE_SEND_OK)
|
||||
goto on_send_error;
|
||||
|
||||
if (i == max_file_senders_index)
|
||||
++max_file_senders_index;
|
||||
struct FileTransfer *ft = new_file_transfer(self, self->num, filenum, FILE_TRANSFER_SEND, TOX_FILE_KIND_DATA);
|
||||
|
||||
return;
|
||||
}
|
||||
if (!ft) {
|
||||
err = TOX_ERR_FILE_SEND_TOO_MANY;
|
||||
goto on_send_error;
|
||||
}
|
||||
|
||||
memcpy(ft->file_name, file_name, namelen + 1);
|
||||
ft->file = file_to_send;
|
||||
ft->file_size = filesize;
|
||||
tox_file_get_file_id(m, self->num, filenum, ft->file_id, NULL);
|
||||
|
||||
char sizestr[32];
|
||||
bytes_convert_str(sizestr, sizeof(sizestr), filesize);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Sending file [%d]: '%s' (%s)", filenum, file_name, sizestr);
|
||||
|
||||
return;
|
||||
|
||||
on_send_error:
|
||||
switch (err) {
|
||||
case TOX_ERR_FILE_SEND_FRIEND_NOT_FOUND:
|
||||
errmsg = "File transfer failed: Invalid friend.";
|
||||
break;
|
||||
|
||||
case TOX_ERR_FILE_SEND_FRIEND_NOT_CONNECTED:
|
||||
errmsg = "File transfer failed: Friend is offline.";
|
||||
break;
|
||||
|
||||
case TOX_ERR_FILE_SEND_NAME_TOO_LONG:
|
||||
errmsg = "File transfer failed: Filename is too long.";
|
||||
break;
|
||||
|
||||
case TOX_ERR_FILE_SEND_TOO_MANY:
|
||||
errmsg = "File transfer failed: Too many concurrent file transfers.";
|
||||
break;
|
||||
|
||||
default:
|
||||
errmsg = "File transfer failed.";
|
||||
break;
|
||||
}
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", errmsg);
|
||||
tox_file_control(m, self->num, filenum, TOX_FILE_CONTROL_CANCEL, NULL);
|
||||
fclose(file_to_send);
|
||||
}
|
||||
|
||||
@@ -20,25 +20,27 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _chat_commands_h
|
||||
#define _chat_commands_h
|
||||
#ifndef CHAT_COMMANDS_H
|
||||
#define CHAT_COMMANDS_H
|
||||
|
||||
#include "windows.h"
|
||||
#include "toxic.h"
|
||||
|
||||
void cmd_chat_help(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_groupinvite(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_join_group(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_savefile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_sendfile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
|
||||
|
||||
#ifdef _SUPPORT_AUDIO
|
||||
#ifdef AUDIO
|
||||
void cmd_call(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_answer(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_reject(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_hangup(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_cancel(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
#endif /* _SUPPORT_AUDIO */
|
||||
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 */
|
||||
#endif /* #define CHAT_COMMANDS_H */
|
||||
|
||||
@@ -20,10 +20,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
@@ -33,53 +29,55 @@
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "configdir.h"
|
||||
|
||||
/* get the user's home directory */
|
||||
void get_home_dir(char *home, int size)
|
||||
{
|
||||
struct passwd pwd;
|
||||
struct passwd *pwdbuf;
|
||||
const char *hmstr;
|
||||
char buf[NSS_BUFLEN_PASSWD];
|
||||
|
||||
int rc = getpwuid_r(getuid(), &pwd, buf, NSS_BUFLEN_PASSWD, &pwdbuf);
|
||||
|
||||
if (rc == 0) {
|
||||
hmstr = pwd.pw_dir;
|
||||
} else {
|
||||
hmstr = getenv("HOME");
|
||||
|
||||
if (hmstr == NULL)
|
||||
return;
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s", hmstr);
|
||||
hmstr = buf;
|
||||
}
|
||||
|
||||
snprintf(home, size, "%s", hmstr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the users config directory.
|
||||
* @brief Get the user's config directory.
|
||||
*
|
||||
* This is without a trailing slash.
|
||||
* This is without a trailing slash. Resulting string must be freed.
|
||||
*
|
||||
* @return The users config dir or NULL on error.
|
||||
*/
|
||||
char *get_user_config_dir(void)
|
||||
{
|
||||
char home[NSS_BUFLEN_PASSWD] = {0};
|
||||
get_home_dir(home, sizeof(home));
|
||||
|
||||
char *user_config_dir;
|
||||
|
||||
#ifndef NSS_BUFLEN_PASSWD
|
||||
#define NSS_BUFLEN_PASSWD 4096
|
||||
#endif /* NSS_BUFLEN_PASSWD */
|
||||
|
||||
struct passwd pwd;
|
||||
struct passwd *pwdbuf;
|
||||
const char *home;
|
||||
char buf[NSS_BUFLEN_PASSWD];
|
||||
size_t len;
|
||||
int rc;
|
||||
|
||||
rc = getpwuid_r(getuid(), &pwd, buf, NSS_BUFLEN_PASSWD, &pwdbuf);
|
||||
|
||||
if (rc == 0) {
|
||||
home = pwd.pw_dir;
|
||||
} else {
|
||||
home = getenv("HOME");
|
||||
|
||||
if (home == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* env variables can be tainted */
|
||||
snprintf(buf, sizeof(buf), "%s", home);
|
||||
home = buf;
|
||||
}
|
||||
|
||||
# if defined(__APPLE__)
|
||||
len = strlen(home) + strlen("/Library/Application Support") + 1;
|
||||
user_config_dir = malloc(len);
|
||||
|
||||
if (user_config_dir == NULL) {
|
||||
if (user_config_dir == NULL)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
snprintf(user_config_dir, len, "%s/Library/Application Support", home);
|
||||
# else /* __APPLE__ */
|
||||
@@ -90,9 +88,8 @@ char *get_user_config_dir(void)
|
||||
len = strlen(home) + strlen("/.config") + 1;
|
||||
user_config_dir = malloc(len);
|
||||
|
||||
if (user_config_dir == NULL) {
|
||||
if (user_config_dir == NULL)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
snprintf(user_config_dir, len, "%s/.config", home);
|
||||
} else {
|
||||
@@ -102,34 +99,48 @@ char *get_user_config_dir(void)
|
||||
# endif /* __APPLE__ */
|
||||
|
||||
return user_config_dir;
|
||||
#undef NSS_BUFLEN_PASSWD
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates the config directory.
|
||||
* Creates the config and chatlog directories.
|
||||
*/
|
||||
int create_user_config_dir(char *path)
|
||||
int create_user_config_dirs(char *path)
|
||||
{
|
||||
int mkdir_err;
|
||||
|
||||
mkdir_err = mkdir(path, 0700);
|
||||
struct stat buf;
|
||||
int mkdir_err = mkdir(path, 0700);
|
||||
|
||||
if (mkdir_err && (errno != EEXIST || stat(path, &buf) || !S_ISDIR(buf.st_mode))) {
|
||||
if (mkdir_err && (errno != EEXIST || stat(path, &buf) || !S_ISDIR(buf.st_mode)))
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *fullpath = malloc(strlen(path) + strlen(CONFIGDIR) + 1);
|
||||
char *logpath = malloc(strlen(path) + strlen(LOGDIR) + 1);
|
||||
|
||||
if (fullpath == NULL || logpath == NULL)
|
||||
exit_toxic_err("failed in load_data_structures", FATALERR_MEMORY);
|
||||
|
||||
strcpy(fullpath, path);
|
||||
strcat(fullpath, CONFIGDIR);
|
||||
|
||||
strcpy(logpath, path);
|
||||
strcat(logpath, LOGDIR);
|
||||
|
||||
mkdir_err = mkdir(fullpath, 0700);
|
||||
|
||||
if (mkdir_err && (errno != EEXIST || stat(fullpath, &buf) || !S_ISDIR(buf.st_mode))) {
|
||||
free(fullpath);
|
||||
free(logpath);
|
||||
return -1;
|
||||
}
|
||||
|
||||
mkdir_err = mkdir(logpath, 0700);
|
||||
|
||||
if (mkdir_err && (errno != EEXIST || stat(logpath, &buf) || !S_ISDIR(buf.st_mode))) {
|
||||
free(fullpath);
|
||||
free(logpath);
|
||||
return -1;
|
||||
}
|
||||
|
||||
free(logpath);
|
||||
free(fullpath);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -20,17 +20,22 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _configdir_h
|
||||
#define _configdir_h
|
||||
#ifndef CONFIGDIR_H
|
||||
#define CONFIGDIR_H
|
||||
|
||||
#ifndef NSS_BUFLEN_PASSWD
|
||||
#define NSS_BUFLEN_PASSWD 4096
|
||||
#endif
|
||||
|
||||
#define CONFIGDIR "/tox/"
|
||||
#define LOGDIR "/tox/chatlogs/"
|
||||
|
||||
#ifndef S_ISDIR
|
||||
#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
|
||||
#endif
|
||||
|
||||
char *get_user_config_dir(void);
|
||||
void get_home_dir(char *home, int size);
|
||||
int create_user_config_dirs(char *path);
|
||||
|
||||
int create_user_config_dir(char *path);
|
||||
|
||||
#endif /* #define _configdir_h */
|
||||
#endif /* #define CONFIGDIR_H */
|
||||
|
||||
479
src/device.c
Normal file
479
src/device.c
Normal file
@@ -0,0 +1,479 @@
|
||||
/* 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 */
|
||||
lock;
|
||||
thread_running = false;
|
||||
unlock;
|
||||
|
||||
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 (true)
|
||||
{
|
||||
lock;
|
||||
if (!thread_running) {
|
||||
unlock;
|
||||
break;
|
||||
}
|
||||
unlock;
|
||||
|
||||
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 */
|
||||
320
src/dns.c
320
src/dns.c
@@ -20,16 +20,18 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h> /* for u_char */
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/nameser.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"
|
||||
@@ -38,20 +40,26 @@
|
||||
#include "dns.h"
|
||||
#include "global_commands.h"
|
||||
#include "misc_tools.h"
|
||||
#include "configdir.h"
|
||||
|
||||
#define MAX_DNS_REQST_SIZE 256
|
||||
#define NUM_DNS3_SERVERS 2 /* must correspond to number of items in dns3_servers array */
|
||||
#define DNS3_KEY_SIZE 32
|
||||
#define MAX_DNS_REQST_SIZE 255
|
||||
#define TOX_DNS3_TXT_PREFIX "v=tox3;id="
|
||||
#define DNS3_KEY_SZ 32
|
||||
|
||||
/* TODO: process keys from key file instead of hard-coding like a noob */
|
||||
static struct dns3_server {
|
||||
uint8_t *name;
|
||||
uint8_t key[DNS3_KEY_SZ];
|
||||
} dns3_servers[] = {
|
||||
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",
|
||||
{
|
||||
"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
|
||||
}
|
||||
@@ -65,80 +73,134 @@ static struct dns3_server {
|
||||
},
|
||||
};
|
||||
|
||||
static struct _thread_data {
|
||||
static struct thread_data {
|
||||
ToxWindow *self;
|
||||
uint8_t id_bin[TOX_FRIEND_ADDRESS_SIZE];
|
||||
uint8_t addr[MAX_STR_SIZE];
|
||||
uint8_t msg[MAX_STR_SIZE];
|
||||
char id_bin[TOX_ADDRESS_SIZE];
|
||||
char addr[MAX_STR_SIZE];
|
||||
char msg[MAX_STR_SIZE];
|
||||
uint8_t busy;
|
||||
Tox *m;
|
||||
} t_data;
|
||||
|
||||
static struct _dns_thread {
|
||||
static struct dns_thread {
|
||||
pthread_t tid;
|
||||
pthread_mutex_t lock;
|
||||
pthread_attr_t attr;
|
||||
} dns_thread;
|
||||
|
||||
static int dns_error(ToxWindow *self, uint8_t *errmsg)
|
||||
{
|
||||
uint8_t msg[MAX_STR_SIZE];
|
||||
snprintf(msg, sizeof(msg), "DNS lookup failed: %s", errmsg);
|
||||
|
||||
pthread_mutex_lock(&dns_thread.lock);
|
||||
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||
pthread_mutex_unlock(&dns_thread.lock);
|
||||
#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 kill_dns_thread(void *dns_obj)
|
||||
static void killdns_thread(void *dns_obj)
|
||||
{
|
||||
if (dns_obj)
|
||||
tox_dns3_kill(dns_obj);
|
||||
|
||||
memset(&t_data, 0, sizeof(struct _thread_data));
|
||||
pthread_exit(0);
|
||||
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, uint8_t *buf)
|
||||
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;
|
||||
uint8_t exp_ans[PACKETSZ];
|
||||
|
||||
char exp_ans[PACKETSZ];
|
||||
|
||||
int len = dn_expand(answer, ans_end, ans_pt, exp_ans, sizeof(exp_ans));
|
||||
|
||||
if (len == -1)
|
||||
return dns_error(self, "dn_expand failed.");
|
||||
return dns_error(self, "dn_expand failed.");
|
||||
|
||||
ans_pt += len;
|
||||
|
||||
if (ans_pt > ans_end - 4)
|
||||
return dns_error(self, "Reply was too short.");
|
||||
return dns_error(self, "DNS reply was too short.");
|
||||
|
||||
int type;
|
||||
GETSHORT(type, ans_pt);
|
||||
|
||||
if (type != T_TXT)
|
||||
return dns_error(self, "Broken reply.");
|
||||
|
||||
return dns_error(self, "Broken DNS reply.");
|
||||
|
||||
|
||||
ans_pt += INT16SZ; /* class */
|
||||
uint32_t size = 0;
|
||||
|
||||
/* recurse through CNAME rr's */
|
||||
do {
|
||||
do {
|
||||
ans_pt += size;
|
||||
len = dn_expand(answer, ans_end, ans_pt, exp_ans, sizeof(exp_ans));
|
||||
|
||||
if (len == -1)
|
||||
return dns_error(self, "Second dn_expand failed.");
|
||||
return dns_error(self, "Second dn_expand failed.");
|
||||
|
||||
ans_pt += len;
|
||||
|
||||
if (ans_pt > ans_end - 10)
|
||||
return dns_error(self, "Reply was too short.");
|
||||
return dns_error(self, "DNS reply was too short.");
|
||||
|
||||
GETSHORT(type, ans_pt);
|
||||
ans_pt += INT16SZ;
|
||||
@@ -146,18 +208,21 @@ static int parse_dns_response(ToxWindow *self, u_char *answer, int ans_len, uint
|
||||
GETSHORT(size, ans_pt);
|
||||
|
||||
if (ans_pt + size < answer || ans_pt + size > ans_end)
|
||||
return dns_error(self, "RR overflow.");
|
||||
return dns_error(self, "RR overflow.");
|
||||
|
||||
} while (type == T_CNAME);
|
||||
|
||||
if (type != T_TXT)
|
||||
return dns_error(self, "Not a TXT record.");
|
||||
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);
|
||||
@@ -165,16 +230,20 @@ static int parse_dns_response(ToxWindow *self, u_char *answer, int ans_len, uint
|
||||
return txt_len;
|
||||
}
|
||||
|
||||
/* Takes address addr in the form "username@domain", puts the username in namebuf,
|
||||
/* Takes address addr in the form "username@domain", puts the username in namebuf,
|
||||
and the domain in dombuf.
|
||||
|
||||
return length of username on success, -1 on failure */
|
||||
static int parse_addr(uint8_t *addr, uint8_t *namebuf, uint8_t *dombuf)
|
||||
static int parse_addr(const char *addr, char *namebuf, size_t namebuf_sz, char *dombuf, size_t dombuf_sz)
|
||||
{
|
||||
uint8_t tmpaddr[MAX_STR_SIZE];
|
||||
uint8_t *tmpname, *tmpdom;
|
||||
if (strlen(addr) >= MAX_STR_SIZE)
|
||||
return -1;
|
||||
|
||||
strcpy(tmpaddr, addr);
|
||||
char tmpaddr[MAX_STR_SIZE];
|
||||
char *tmpname = NULL;
|
||||
char *tmpdom = NULL;
|
||||
|
||||
snprintf(tmpaddr, sizeof(tmpaddr), "%s", addr);
|
||||
tmpname = strtok(tmpaddr, "@");
|
||||
tmpdom = strtok(NULL, "");
|
||||
|
||||
@@ -182,123 +251,182 @@ static int parse_addr(uint8_t *addr, uint8_t *namebuf, uint8_t *dombuf)
|
||||
return -1;
|
||||
|
||||
str_to_lower(tmpdom);
|
||||
strcpy(namebuf, tmpname);
|
||||
strcpy(dombuf, tmpdom);
|
||||
snprintf(namebuf, namebuf_sz, "%s", tmpname);
|
||||
snprintf(dombuf, dombuf_sz, "%s", tmpdom);
|
||||
|
||||
return strlen(namebuf);
|
||||
}
|
||||
|
||||
/* matches input domain name with domains in list and obtains key. Return 0 on success, -1 on failure */
|
||||
static int get_domain_match(char *pubkey, char *domain, const char *inputdomain)
|
||||
{
|
||||
/* check server list first */
|
||||
int i;
|
||||
bool match = false;
|
||||
|
||||
for (i = 0; i < dns3_servers.lines; ++i) {
|
||||
if (strcmp(dns3_servers.names[i], inputdomain) == 0) {
|
||||
memcpy(pubkey, dns3_servers.keys[i], DNS3_KEY_SIZE);
|
||||
snprintf(domain, MAX_DOMAIN_SIZE, "%s", dns3_servers.names[i]);
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* fall back to hard-coded domains on server list failure */
|
||||
if (!match) {
|
||||
for (i = 0; i < NUM_DNS3_BACKUP_SERVERS; ++i) {
|
||||
if (strcmp(dns3_servers_backup[i].name, inputdomain) == 0) {
|
||||
memcpy(pubkey, dns3_servers_backup[i].key, DNS3_KEY_SIZE);
|
||||
snprintf(domain, MAX_DOMAIN_SIZE, "%s", dns3_servers_backup[i].name);
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!match)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Does DNS lookup for addr and puts resulting tox id in id_bin. */
|
||||
void *dns3_lookup_thread(void *data)
|
||||
{
|
||||
ToxWindow *self = t_data.self;
|
||||
|
||||
uint8_t domain[MAX_STR_SIZE];
|
||||
uint8_t name[MAX_STR_SIZE];
|
||||
char inputdomain[MAX_STR_SIZE];
|
||||
char name[MAX_STR_SIZE];
|
||||
|
||||
int namelen = parse_addr(t_data.addr, name, domain);
|
||||
int namelen = parse_addr(t_data.addr, name, sizeof(name), inputdomain, sizeof(inputdomain));
|
||||
|
||||
if (namelen == -1) {
|
||||
dns_error(self, "Must be a Tox ID or an address in the form username@domain");
|
||||
kill_dns_thread(NULL);
|
||||
killdns_thread(NULL);
|
||||
}
|
||||
|
||||
/* get domain name/pub key */
|
||||
uint8_t *DNS_pubkey, *domname = NULL;
|
||||
int i;
|
||||
char DNS_pubkey[DNS3_KEY_SIZE];
|
||||
char domain[MAX_DOMAIN_SIZE];
|
||||
|
||||
for (i = 0; i < NUM_DNS3_SERVERS; ++i) {
|
||||
if (strcmp(dns3_servers[i].name, domain) == 0) {
|
||||
DNS_pubkey = dns3_servers[i].key;
|
||||
domname = dns3_servers[i].name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
int match = get_domain_match(DNS_pubkey, domain, inputdomain);
|
||||
|
||||
if (domname == NULL) {
|
||||
if (match == -1) {
|
||||
dns_error(self, "Domain not found.");
|
||||
kill_dns_thread(NULL);
|
||||
killdns_thread(NULL);
|
||||
}
|
||||
|
||||
void *dns_obj = tox_dns3_new(DNS_pubkey);
|
||||
void *dns_obj = tox_dns3_new((uint8_t *) DNS_pubkey);
|
||||
|
||||
if (dns_obj == NULL) {
|
||||
dns_error(self, "Core failed to create DNS object.");
|
||||
kill_dns_thread(NULL);
|
||||
killdns_thread(NULL);
|
||||
}
|
||||
|
||||
uint8_t string[MAX_DNS_REQST_SIZE];
|
||||
char string[MAX_DNS_REQST_SIZE + 1];
|
||||
uint32_t request_id;
|
||||
|
||||
int str_len = tox_generate_dns3_string(dns_obj, string, sizeof(string), &request_id, name, namelen);
|
||||
int str_len = tox_generate_dns3_string(dns_obj, (uint8_t *) string, sizeof(string), &request_id,
|
||||
(uint8_t *) name, namelen);
|
||||
|
||||
if (str_len == -1) {
|
||||
dns_error(self, "Core failed to generate dns3 string.");
|
||||
kill_dns_thread(dns_obj);
|
||||
dns_error(self, "Core failed to generate DNS3 string.");
|
||||
killdns_thread(dns_obj);
|
||||
}
|
||||
|
||||
string[str_len] = '\0';
|
||||
|
||||
u_char answer[PACKETSZ];
|
||||
uint8_t d_string[MAX_DNS_REQST_SIZE];
|
||||
char d_string[MAX_DOMAIN_SIZE + MAX_DNS_REQST_SIZE + 10];
|
||||
|
||||
/* format string and create dns query */
|
||||
snprintf(d_string, sizeof(d_string), "_%s._tox.%s", string, domname);
|
||||
snprintf(d_string, sizeof(d_string), "_%s._tox.%s", string, domain);
|
||||
int ans_len = res_query(d_string, C_IN, T_TXT, answer, sizeof(answer));
|
||||
|
||||
if (ans_len <= 0) {
|
||||
dns_error(self, "Query failed.");
|
||||
kill_dns_thread(dns_obj);
|
||||
dns_error(self, "DNS query failed.");
|
||||
killdns_thread(dns_obj);
|
||||
}
|
||||
|
||||
uint8_t ans_id[MAX_DNS_REQST_SIZE];
|
||||
char ans_id[MAX_DNS_REQST_SIZE + 1];
|
||||
|
||||
/* extract TXT from DNS response */
|
||||
if (parse_dns_response(self, answer, ans_len, ans_id) == -1)
|
||||
kill_dns_thread(dns_obj);
|
||||
killdns_thread(dns_obj);
|
||||
|
||||
uint8_t encrypted_id[MAX_DNS_REQST_SIZE];
|
||||
char encrypted_id[MAX_DNS_REQST_SIZE + 1];
|
||||
int prfx_len = strlen(TOX_DNS3_TXT_PREFIX);
|
||||
|
||||
/* extract the encrypted ID from TXT response */
|
||||
if (strncmp(ans_id, TOX_DNS3_TXT_PREFIX, prfx_len) != 0) {
|
||||
dns_error(self, "Bad dns3 TXT response.");
|
||||
kill_dns_thread(dns_obj);
|
||||
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, t_data.id_bin, encrypted_id, strlen(encrypted_id), request_id) == -1) {
|
||||
dns_error(self, "Core failed to decrypt response.");
|
||||
kill_dns_thread(dns_obj);
|
||||
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(&dns_thread.lock);
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
cmd_add_helper(self, t_data.m, t_data.id_bin, t_data.msg);
|
||||
pthread_mutex_unlock(&dns_thread.lock);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
kill_dns_thread(dns_obj);
|
||||
killdns_thread(dns_obj);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* creates new thread for dns3 lookup. Only allows one lookup at a time. */
|
||||
void dns3_lookup(ToxWindow *self, Tox *m, uint8_t *id_bin, uint8_t *addr, uint8_t *msg)
|
||||
void dns3_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *msg)
|
||||
{
|
||||
if (t_data.busy) {
|
||||
uint8_t *err = "Please wait for previous user lookup to finish.";
|
||||
line_info_add(self, NULL, NULL, NULL, err, SYS_MSG, 0, 0);
|
||||
if (arg_opts.proxy_type != TOX_PROXY_TYPE_NONE && arg_opts.force_tcp) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "DNS lookups are disabled.");
|
||||
return;
|
||||
}
|
||||
|
||||
t_data.self = self;
|
||||
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_create(&dns_thread.tid, NULL, dns3_lookup_thread, NULL) != 0)
|
||||
exit_toxic_err("failed in dns3_lookup", FATALERR_THREAD_CREATE);
|
||||
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_mutex_init(&dns_thread.lock, NULL) != 0)
|
||||
exit_toxic_err("failed in dns3_lookup", FATALERR_MUTEX_INIT);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,10 +23,10 @@
|
||||
/* Does DNS lookup for addr and puts resulting tox id in id_bin.
|
||||
Return 0 on success, -1 on failure. */
|
||||
|
||||
#ifndef _dns_h
|
||||
#define _dns_h
|
||||
#ifndef DNS_H
|
||||
#define DNS_H
|
||||
|
||||
/* creates new thread for dns3 lookup. Only allows one lookup at a time. */
|
||||
void dns3_lookup(ToxWindow *self, Tox *m, uint8_t *id_bin, uint8_t *addr, uint8_t *msg);
|
||||
void dns3_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *msg);
|
||||
|
||||
#endif /* #define _dns_h */
|
||||
#endif /* #define DNS_H */
|
||||
|
||||
107
src/execute.c
107
src/execute.c
@@ -20,10 +20,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
@@ -33,7 +29,10 @@
|
||||
#include "execute.h"
|
||||
#include "chat_commands.h"
|
||||
#include "global_commands.h"
|
||||
#include "group_commands.h"
|
||||
#include "line_info.h"
|
||||
#include "misc_tools.h"
|
||||
#include "notify.h"
|
||||
|
||||
struct cmd_func {
|
||||
const char *name;
|
||||
@@ -43,10 +42,12 @@ struct cmd_func {
|
||||
static struct cmd_func global_commands[] = {
|
||||
{ "/accept", cmd_accept },
|
||||
{ "/add", cmd_add },
|
||||
{ "/avatar", cmd_avatar },
|
||||
{ "/clear", cmd_clear },
|
||||
{ "/connect", cmd_connect },
|
||||
{ "/decline", cmd_decline },
|
||||
{ "/exit", cmd_quit },
|
||||
{ "/groupchat", cmd_groupchat },
|
||||
{ "/group", cmd_groupchat },
|
||||
{ "/help", cmd_prompt_help },
|
||||
{ "/log", cmd_log },
|
||||
{ "/myid", cmd_myid },
|
||||
@@ -54,73 +55,94 @@ static struct cmd_func global_commands[] = {
|
||||
{ "/note", cmd_note },
|
||||
{ "/q", cmd_quit },
|
||||
{ "/quit", cmd_quit },
|
||||
{ "/requests", cmd_requests },
|
||||
{ "/status", cmd_status },
|
||||
|
||||
#ifdef _SUPPORT_AUDIO
|
||||
#ifdef AUDIO
|
||||
{ "/lsdev", cmd_list_devices },
|
||||
{ "/sdev", cmd_change_device },
|
||||
#endif /* _SUPPORT_AUDIO */
|
||||
#endif /* AUDIO */
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
static struct cmd_func chat_commands[] = {
|
||||
{ "/help", cmd_chat_help },
|
||||
{ "/cancel", cmd_cancelfile },
|
||||
{ "/invite", cmd_groupinvite },
|
||||
{ "/join", cmd_join_group },
|
||||
{ "/savefile", cmd_savefile },
|
||||
{ "/sendfile", cmd_sendfile },
|
||||
|
||||
#ifdef _SUPPORT_AUDIO
|
||||
#ifdef AUDIO
|
||||
{ "/call", cmd_call },
|
||||
{ "/cancel", cmd_cancel },
|
||||
{ "/answer", cmd_answer },
|
||||
{ "/reject", cmd_reject },
|
||||
{ "/hangup", cmd_hangup },
|
||||
#endif /* _SUPPORT_AUDIO */
|
||||
{ "/mute", cmd_mute },
|
||||
{ "/sense", cmd_sense },
|
||||
#endif /* AUDIO */
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
static struct cmd_func group_commands[] = {
|
||||
{ "/title", cmd_set_title },
|
||||
|
||||
#ifdef AUDIO
|
||||
{ "/mute", cmd_mute },
|
||||
{ "/sense", cmd_sense },
|
||||
#endif /* AUDIO */
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
/* Parses input command and puts args into arg array.
|
||||
Returns number of arguments on success, -1 on failure. */
|
||||
static int parse_command(WINDOW *w, ToxWindow *self, char *cmd, char (*args)[MAX_STR_SIZE])
|
||||
static int parse_command(WINDOW *w, ToxWindow *self, const char *input, char (*args)[MAX_STR_SIZE])
|
||||
{
|
||||
char *cmd = strdup(input);
|
||||
|
||||
if (cmd == NULL)
|
||||
exit_toxic_err("failed in parse_command", FATALERR_MEMORY);
|
||||
|
||||
int num_args = 0;
|
||||
bool cmd_end = false; /* flags when we get to the end of cmd */
|
||||
char *end; /* points to the end of the current arg */
|
||||
int i = 0; /* index of last char in an argument */
|
||||
|
||||
/* characters wrapped in double quotes count as one arg */
|
||||
while (!cmd_end && num_args < MAX_NUM_ARGS) {
|
||||
if (*cmd == '\"') {
|
||||
end = strchr(cmd + 1, '\"');
|
||||
while (num_args < MAX_NUM_ARGS) {
|
||||
int qt_ofst = 0; /* set to 1 to offset index for quote char at end of arg */
|
||||
|
||||
if (end++ == NULL) { /* Increment past the end quote */
|
||||
uint8_t *errmsg = "Invalid argument. Did you forget a closing \"?";
|
||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||
if (*cmd == '\"') {
|
||||
qt_ofst = 1;
|
||||
i = char_find(1, cmd, '\"');
|
||||
|
||||
if (cmd[i] == '\0') {
|
||||
const char *errmsg = "Invalid argument. Did you forget a closing \"?";
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
|
||||
free(cmd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
cmd_end = *end == '\0';
|
||||
} else {
|
||||
end = strchr(cmd, ' ');
|
||||
cmd_end = end == NULL;
|
||||
i = char_find(0, cmd, ' ');
|
||||
}
|
||||
|
||||
if (!cmd_end)
|
||||
*end++ = '\0'; /* mark end of current argument */
|
||||
memcpy(args[num_args], cmd, i + qt_ofst);
|
||||
args[num_args++][i + qt_ofst] = '\0';
|
||||
|
||||
/* Copy from start of current arg to where we just inserted the null byte */
|
||||
strcpy(args[num_args++], cmd);
|
||||
cmd = end;
|
||||
if (cmd[i] == '\0') /* no more args */
|
||||
break;
|
||||
|
||||
char tmp[MAX_STR_SIZE];
|
||||
snprintf(tmp, sizeof(tmp), "%s", &cmd[i + 1]);
|
||||
strcpy(cmd, tmp); /* tmp will always fit inside cmd */
|
||||
}
|
||||
|
||||
free(cmd);
|
||||
return num_args;
|
||||
}
|
||||
|
||||
/* Matches command to respective function. Returns 0 on match, 1 on no match */
|
||||
static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, int num_cmds,
|
||||
struct cmd_func *commands, char (*args)[MAX_STR_SIZE])
|
||||
static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, struct cmd_func *commands,
|
||||
char (*args)[MAX_STR_SIZE])
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_cmds; ++i) {
|
||||
for (i = 0; commands[i].name != NULL; ++i) {
|
||||
if (strcmp(args[0], commands[i].name) == 0) {
|
||||
(commands[i].func)(w, self, m, num_args - 1, args);
|
||||
return 0;
|
||||
@@ -130,13 +152,13 @@ static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, int num_
|
||||
return 1;
|
||||
}
|
||||
|
||||
void execute(WINDOW *w, ToxWindow *self, Tox *m, char *cmd, int mode)
|
||||
void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode)
|
||||
{
|
||||
if (string_is_empty(cmd))
|
||||
if (string_is_empty(input))
|
||||
return;
|
||||
|
||||
char args[MAX_NUM_ARGS][MAX_STR_SIZE] = {0};
|
||||
int num_args = parse_command(w, self, cmd, args);
|
||||
char args[MAX_NUM_ARGS][MAX_STR_SIZE];
|
||||
int num_args = parse_command(w, self, input, args);
|
||||
|
||||
if (num_args == -1)
|
||||
return;
|
||||
@@ -147,18 +169,19 @@ void execute(WINDOW *w, ToxWindow *self, Tox *m, char *cmd, int mode)
|
||||
Note: Global commands must come last in case of duplicate command names */
|
||||
switch (mode) {
|
||||
case CHAT_COMMAND_MODE:
|
||||
if (do_command(w, self, m, num_args, CHAT_NUM_COMMANDS, chat_commands, args) == 0)
|
||||
if (do_command(w, self, m, num_args, chat_commands, args) == 0)
|
||||
return;
|
||||
|
||||
break;
|
||||
|
||||
case GROUPCHAT_COMMAND_MODE:
|
||||
if (do_command(w, self, m, num_args, group_commands, args) == 0)
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
if (do_command(w, self, m, num_args, GLOBAL_NUM_COMMANDS, global_commands, args) == 0)
|
||||
if (do_command(w, self, m, num_args, global_commands, args) == 0)
|
||||
return;
|
||||
|
||||
uint8_t *errmsg = "Invalid command.";
|
||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid command.");
|
||||
}
|
||||
|
||||
@@ -20,28 +20,20 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _execute_h
|
||||
#define _execute_h
|
||||
#ifndef EXECUTE_H
|
||||
#define EXECUTE_H
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
#define MAX_NUM_ARGS 4 /* Includes command */
|
||||
|
||||
#ifdef _SUPPORT_AUDIO
|
||||
#define GLOBAL_NUM_COMMANDS 16
|
||||
#define CHAT_NUM_COMMANDS 10
|
||||
#else
|
||||
#define GLOBAL_NUM_COMMANDS 14
|
||||
#define CHAT_NUM_COMMANDS 5
|
||||
#endif /* _SUPPORT_AUDIO */
|
||||
|
||||
enum {
|
||||
GLOBAL_COMMAND_MODE,
|
||||
CHAT_COMMAND_MODE,
|
||||
GROUPCHAT_COMMAND_MODE,
|
||||
};
|
||||
|
||||
void execute(WINDOW *w, ToxWindow *self, Tox *m, char *cmd, int mode);
|
||||
void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode);
|
||||
|
||||
#endif /* #define _execute_h */
|
||||
#endif /* #define EXECUTE_H */
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
/* file_senders.c
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "file_senders.h"
|
||||
#include "line_info.h"
|
||||
|
||||
FileSender file_senders[MAX_FILES];
|
||||
uint8_t max_file_senders_index;
|
||||
|
||||
static void close_file_sender(int i)
|
||||
{
|
||||
fclose(file_senders[i].file);
|
||||
memset(&file_senders[i], 0, sizeof(FileSender));
|
||||
|
||||
int j;
|
||||
|
||||
for (j = max_file_senders_index; j > 0; --j) {
|
||||
if (file_senders[j - 1].active)
|
||||
break;
|
||||
}
|
||||
|
||||
max_file_senders_index = j;
|
||||
}
|
||||
|
||||
/* Should only be called on exit */
|
||||
void close_all_file_senders(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < max_file_senders_index; ++i) {
|
||||
if (file_senders[i].active)
|
||||
fclose(file_senders[i].file);
|
||||
}
|
||||
}
|
||||
|
||||
void do_file_senders(Tox *m)
|
||||
{
|
||||
uint8_t msg[MAX_STR_SIZE];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < max_file_senders_index; ++i) {
|
||||
if (!file_senders[i].active)
|
||||
continue;
|
||||
|
||||
ToxWindow *self = file_senders[i].toxwin;
|
||||
uint8_t *pathname = file_senders[i].pathname;
|
||||
int filenum = file_senders[i].filenum;
|
||||
int32_t friendnum = file_senders[i].friendnum;
|
||||
FILE *fp = file_senders[i].file;
|
||||
uint64_t current_time = get_unix_time();
|
||||
|
||||
/* If file transfer has timed out kill transfer and send kill control */
|
||||
if (timed_out(file_senders[i].timestamp, current_time, TIMEOUT_FILESENDER)) {
|
||||
if (self->chatwin != NULL) {
|
||||
snprintf(msg, sizeof(msg), "File transfer for '%s' timed out.", pathname);
|
||||
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||
alert_window(file_senders[i].toxwin, WINDOW_ALERT_2, true);
|
||||
}
|
||||
|
||||
tox_file_send_control(m, friendnum, 0, filenum, TOX_FILECONTROL_KILL, 0, 0);
|
||||
close_file_sender(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (tox_file_send_data(m, friendnum, filenum, file_senders[i].nextpiece,
|
||||
file_senders[i].piecelen) == -1)
|
||||
break;
|
||||
|
||||
file_senders[i].timestamp = current_time;
|
||||
file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1,
|
||||
tox_file_data_size(m, friendnum), fp);
|
||||
|
||||
/* refresh line with percentage complete */
|
||||
if (self->chatwin != NULL) {
|
||||
uint64_t size = file_senders[i].size;
|
||||
long double remain = (long double) tox_file_data_remaining(m, friendnum, filenum, 0);
|
||||
long double pct_remain = 100;
|
||||
|
||||
if (remain)
|
||||
pct_remain = (1 - (remain / size)) * 100;
|
||||
|
||||
const uint8_t *name = file_senders[i].pathname;
|
||||
snprintf(msg, sizeof(msg), "File transfer for '%s' accepted (%.1Lf%%)", name, pct_remain);
|
||||
line_info_set(self, file_senders[i].line_id, msg);
|
||||
}
|
||||
|
||||
if (file_senders[i].piecelen == 0) {
|
||||
if (self->chatwin != NULL) {
|
||||
snprintf(msg, sizeof(msg), "File '%s' successfuly sent.", pathname);
|
||||
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||
alert_window(file_senders[i].toxwin, WINDOW_ALERT_2, true);
|
||||
}
|
||||
|
||||
tox_file_send_control(m, friendnum, 0, filenum, TOX_FILECONTROL_FINISHED, 0, 0);
|
||||
close_file_sender(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
316
src/file_transfers.c
Normal file
316
src/file_transfers.c
Normal file
@@ -0,0 +1,316 @@
|
||||
/* file_transfers.c
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "friendlist.h"
|
||||
#include "file_transfers.h"
|
||||
#include "line_info.h"
|
||||
#include "misc_tools.h"
|
||||
#include "notify.h"
|
||||
|
||||
extern FriendsList Friends;
|
||||
|
||||
/* number of "#"'s in file transfer progress bar. Keep well below MAX_STR_SIZE */
|
||||
#define NUM_PROG_MARKS 50
|
||||
|
||||
/* Checks for timed out file transfers and closes them. */
|
||||
#define CHECK_FILE_TIMEOUT_INTERAVAL 5
|
||||
void check_file_transfer_timeouts(Tox *m)
|
||||
{
|
||||
char msg[MAX_STR_SIZE];
|
||||
static uint64_t last_check = 0;
|
||||
|
||||
if (!timed_out(last_check, CHECK_FILE_TIMEOUT_INTERAVAL))
|
||||
return;
|
||||
|
||||
last_check = get_unix_time();
|
||||
|
||||
size_t i, j;
|
||||
|
||||
for (i = 0; i < Friends.max_idx; ++i) {
|
||||
if (!Friends.list[i].active)
|
||||
continue;
|
||||
|
||||
for (j = 0; j < MAX_FILES; ++j) {
|
||||
struct FileTransfer *ft_send = &Friends.list[i].file_sender[j];
|
||||
|
||||
if (ft_send->state > FILE_TRANSFER_PAUSED) {
|
||||
if (timed_out(ft_send->last_keep_alive, TIMEOUT_FILESENDER)) {
|
||||
snprintf(msg, sizeof(msg), "File transfer for '%s' timed out.", ft_send->file_name);
|
||||
close_file_transfer(ft_send->window, m, ft_send, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
|
||||
}
|
||||
}
|
||||
|
||||
struct FileTransfer *ft_recv = &Friends.list[i].file_receiver[j];
|
||||
|
||||
if (ft_recv->state > FILE_TRANSFER_PAUSED) {
|
||||
if (timed_out(ft_recv->last_keep_alive, TIMEOUT_FILESENDER)) {
|
||||
snprintf(msg, sizeof(msg), "File transfer for '%s' timed out.", ft_recv->file_name);
|
||||
close_file_transfer(ft_recv->window, m, ft_recv, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* creates initial progress line that will be updated during file transfer.
|
||||
Assumes progline is of size MAX_STR_SIZE */
|
||||
void init_progress_bar(char *progline)
|
||||
{
|
||||
strcpy(progline, "0.0 B/s [");
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NUM_PROG_MARKS; ++i)
|
||||
strcat(progline, "-");
|
||||
|
||||
strcat(progline, "] 0%");
|
||||
}
|
||||
|
||||
/* prints a progress bar for file transfers.
|
||||
if friendnum is -1 we're sending the file, otherwise we're receiving. */
|
||||
void print_progress_bar(ToxWindow *self, double bps, double pct_done, uint32_t line_id)
|
||||
{
|
||||
if (bps < 0 || pct_done < 0 || pct_done > 100)
|
||||
return;
|
||||
|
||||
char msg[MAX_STR_SIZE];
|
||||
bytes_convert_str(msg, sizeof(msg), bps);
|
||||
strcat(msg, "/s [");
|
||||
|
||||
int n = pct_done / (100 / NUM_PROG_MARKS);
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < n; ++i)
|
||||
strcat(msg, "#");
|
||||
|
||||
for (j = i; j < NUM_PROG_MARKS; ++j)
|
||||
strcat(msg, "-");
|
||||
|
||||
strcat(msg, "] ");
|
||||
|
||||
char pctstr[16];
|
||||
const char *frmt = pct_done == 100 ? "%.f%%" : "%.1f%%";
|
||||
snprintf(pctstr, sizeof(pctstr), frmt, pct_done);
|
||||
strcat(msg, pctstr);
|
||||
|
||||
line_info_set(self, line_id, msg);
|
||||
}
|
||||
|
||||
static void refresh_progress_helper(ToxWindow *self, Tox *m, struct FileTransfer *ft)
|
||||
{
|
||||
if (ft->state == FILE_TRANSFER_INACTIVE)
|
||||
return;
|
||||
|
||||
/* Timeout must be set to 1 second to show correct bytes per second */
|
||||
if (!timed_out(ft->last_line_progress, 1))
|
||||
return;
|
||||
|
||||
double remain = ft->file_size - ft->position;
|
||||
double pct_done = remain > 0 ? (1 - (remain / ft->file_size)) * 100 : 100;
|
||||
print_progress_bar(self, ft->bps, pct_done, ft->line_id);
|
||||
|
||||
ft->bps = 0;
|
||||
ft->last_line_progress = get_unix_time();
|
||||
}
|
||||
|
||||
/* refreshes active file transfer status bars. */
|
||||
void refresh_file_transfer_progress(ToxWindow *self, Tox *m, uint32_t friendnum)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < MAX_FILES; ++i) {
|
||||
refresh_progress_helper(self, m, &Friends.list[friendnum].file_receiver[i]);
|
||||
refresh_progress_helper(self, m, &Friends.list[friendnum].file_sender[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns a pointer to friendnum's FileTransfer struct associated with filenum.
|
||||
* Returns NULL if filenum is invalid.
|
||||
*/
|
||||
struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filenum)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < MAX_FILES; ++i) {
|
||||
struct FileTransfer *ft_send = &Friends.list[friendnum].file_sender[i];
|
||||
|
||||
if (ft_send->state != FILE_TRANSFER_INACTIVE && ft_send->filenum == filenum)
|
||||
return ft_send;
|
||||
|
||||
struct FileTransfer *ft_recv = &Friends.list[friendnum].file_receiver[i];
|
||||
|
||||
if (ft_recv->state != FILE_TRANSFER_INACTIVE && ft_recv->filenum == filenum)
|
||||
return ft_recv;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Returns a pointer to the FileTransfer struct associated with index with the direction specified.
|
||||
* Returns NULL on failure.
|
||||
*/
|
||||
struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index,
|
||||
FILE_TRANSFER_DIRECTION direction)
|
||||
{
|
||||
if (direction != FILE_TRANSFER_RECV && direction != FILE_TRANSFER_SEND)
|
||||
return NULL;
|
||||
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < MAX_FILES; ++i) {
|
||||
struct FileTransfer *ft = direction == FILE_TRANSFER_SEND ?
|
||||
&Friends.list[friendnum].file_sender[i] :
|
||||
&Friends.list[friendnum].file_receiver[i];
|
||||
|
||||
if (ft->state != FILE_TRANSFER_INACTIVE && ft->index == index)
|
||||
return ft;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Returns a pointer to an unused file sender.
|
||||
* Returns NULL if all file senders are in use.
|
||||
*/
|
||||
static struct FileTransfer *new_file_sender(ToxWindow *window, uint32_t friendnum, uint32_t filenum, uint8_t type)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < MAX_FILES; ++i) {
|
||||
struct FileTransfer *ft = &Friends.list[friendnum].file_sender[i];
|
||||
|
||||
if (ft->state == FILE_TRANSFER_INACTIVE) {
|
||||
memset(ft, 0, sizeof(struct FileTransfer));
|
||||
ft->window = window;
|
||||
ft->index = i;
|
||||
ft->friendnum = friendnum;
|
||||
ft->filenum = filenum;
|
||||
ft->file_type = type;
|
||||
ft->last_keep_alive = get_unix_time();
|
||||
ft->state = FILE_TRANSFER_PENDING;
|
||||
ft->direction = FILE_TRANSFER_SEND;
|
||||
return ft;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Returns a pointer to an unused file receiver.
|
||||
* Returns NULL if all file receivers are in use.
|
||||
*/
|
||||
static struct FileTransfer *new_file_receiver(ToxWindow *window, uint32_t friendnum, uint32_t filenum, uint8_t type)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < MAX_FILES; ++i) {
|
||||
struct FileTransfer *ft = &Friends.list[friendnum].file_receiver[i];
|
||||
|
||||
if (ft->state == FILE_TRANSFER_INACTIVE) {
|
||||
memset(ft, 0, sizeof(struct FileTransfer));
|
||||
ft->window = window;
|
||||
ft->index = i;
|
||||
ft->friendnum = friendnum;
|
||||
ft->filenum = filenum;
|
||||
ft->file_type = type;
|
||||
ft->last_keep_alive = get_unix_time();
|
||||
ft->state = FILE_TRANSFER_PENDING;
|
||||
ft->direction = FILE_TRANSFER_RECV;
|
||||
return ft;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Initializes an unused file transfer and returns its pointer.
|
||||
* Returns NULL on failure.
|
||||
*/
|
||||
struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnum, uint32_t filenum,
|
||||
FILE_TRANSFER_DIRECTION direction, uint8_t type)
|
||||
{
|
||||
if (direction == FILE_TRANSFER_RECV)
|
||||
return new_file_receiver(window, friendnum, filenum, type);
|
||||
|
||||
if (direction == FILE_TRANSFER_SEND)
|
||||
return new_file_sender(window, friendnum, filenum, type);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Closes file transfer ft.
|
||||
*
|
||||
* Set CTRL to -1 if we don't want to send a control signal.
|
||||
* Set message or self to NULL if we don't want to display a message.
|
||||
*/
|
||||
void close_file_transfer(ToxWindow *self, Tox *m, struct FileTransfer *ft, int CTRL, const char *message,
|
||||
Notification sound_type)
|
||||
{
|
||||
if (!ft)
|
||||
return;
|
||||
|
||||
if (ft->state == FILE_TRANSFER_INACTIVE)
|
||||
return;
|
||||
|
||||
if (ft->file)
|
||||
fclose(ft->file);
|
||||
|
||||
if (CTRL >= 0)
|
||||
tox_file_control(m, ft->friendnum, ft->filenum, (TOX_FILE_CONTROL) CTRL, NULL);
|
||||
|
||||
if (message && self) {
|
||||
if (self->active_box != -1 && sound_type != silent)
|
||||
box_notify2(self, sound_type, NT_NOFOCUS | NT_WNDALERT_2, self->active_box, "%s", message);
|
||||
else
|
||||
box_notify(self, sound_type, NT_NOFOCUS | NT_WNDALERT_2, &self->active_box, self->name, "%s", message);
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", message);
|
||||
}
|
||||
|
||||
memset(ft, 0, sizeof(struct FileTransfer));
|
||||
}
|
||||
|
||||
/* Kills all active file transfers for friendnum */
|
||||
void kill_all_file_transfers_friend(Tox *m, uint32_t friendnum)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < MAX_FILES; ++i) {
|
||||
close_file_transfer(NULL, m, &Friends.list[friendnum].file_sender[i], TOX_FILE_CONTROL_CANCEL, NULL, silent);
|
||||
close_file_transfer(NULL, m, &Friends.list[friendnum].file_receiver[i], TOX_FILE_CONTROL_CANCEL, NULL, silent);
|
||||
}
|
||||
}
|
||||
|
||||
void kill_all_file_transfers(Tox *m)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < Friends.max_idx; ++i)
|
||||
kill_all_file_transfers_friend(m, Friends.list[i].num);
|
||||
}
|
||||
115
src/file_transfers.h
Normal file
115
src/file_transfers.h
Normal file
@@ -0,0 +1,115 @@
|
||||
/* file_transfers.h
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef FILE_TRANSFERS_H
|
||||
#define FILE_TRANSFERS_H
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "notify.h"
|
||||
|
||||
#define KiB 1024
|
||||
#define MiB 1048576 /* 1024^2 */
|
||||
#define GiB 1073741824 /* 1024^3 */
|
||||
|
||||
#define MAX_FILES 32
|
||||
#define TIMEOUT_FILESENDER 120
|
||||
|
||||
typedef enum FILE_TRANSFER_STATE {
|
||||
FILE_TRANSFER_INACTIVE,
|
||||
FILE_TRANSFER_PAUSED,
|
||||
FILE_TRANSFER_PENDING,
|
||||
FILE_TRANSFER_STARTED,
|
||||
} FILE_TRANSFER_STATE;
|
||||
|
||||
typedef enum FILE_TRANSFER_DIRECTION {
|
||||
FILE_TRANSFER_SEND,
|
||||
FILE_TRANSFER_RECV
|
||||
} FILE_TRANSFER_DIRECTION;
|
||||
|
||||
struct FileTransfer {
|
||||
ToxWindow *window;
|
||||
FILE *file;
|
||||
FILE_TRANSFER_STATE state;
|
||||
FILE_TRANSFER_DIRECTION direction;
|
||||
uint8_t file_type;
|
||||
char file_name[TOX_MAX_FILENAME_LENGTH + 1];
|
||||
char file_path[PATH_MAX + 1]; /* Not used by senders */
|
||||
double bps;
|
||||
uint32_t filenum;
|
||||
uint32_t friendnum;
|
||||
size_t index;
|
||||
uint64_t file_size;
|
||||
uint64_t position;
|
||||
uint64_t last_line_progress; /* The last time we updated the progress bar */
|
||||
uint64_t last_keep_alive; /* The last time we sent or received data */
|
||||
uint32_t line_id;
|
||||
uint8_t file_id[TOX_FILE_ID_LENGTH];
|
||||
};
|
||||
|
||||
/* Checks for timed out file transfers and closes them. */
|
||||
void check_file_transfer_timeouts(Tox *m);
|
||||
|
||||
/* creates initial progress line that will be updated during file transfer.
|
||||
progline must be at lesat MAX_STR_SIZE bytes */
|
||||
void init_progress_bar(char *progline);
|
||||
|
||||
/* prints a progress bar for file transfers */
|
||||
void print_progress_bar(ToxWindow *self, double pct_done, double bps, uint32_t line_id);
|
||||
|
||||
/* refreshes active file transfer status bars. */
|
||||
void refresh_file_transfer_progress(ToxWindow *self, Tox *m, uint32_t friendnum);
|
||||
|
||||
/* Returns a pointer to friendnum's FileTransfer struct associated with filenum.
|
||||
* Returns NULL if filenum is invalid.
|
||||
*/
|
||||
struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filenum);
|
||||
|
||||
|
||||
/* Returns a pointer to the FileTransfer struct associated with index with the direction specified.
|
||||
* Returns NULL on failure.
|
||||
*/
|
||||
struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index,
|
||||
FILE_TRANSFER_DIRECTION direction);
|
||||
|
||||
/* Initializes an unused file transfer and returns its pointer.
|
||||
* Returns NULL on failure.
|
||||
*/
|
||||
struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnum, uint32_t filenum,
|
||||
FILE_TRANSFER_DIRECTION direction, uint8_t type);
|
||||
|
||||
/* Closes file transfer ft.
|
||||
*
|
||||
* Set CTRL to -1 if we don't want to send a control signal.
|
||||
* Set message or self to NULL if we don't want to display a message.
|
||||
*/
|
||||
void close_file_transfer(ToxWindow *self, Tox *m, struct FileTransfer *ft, int CTRL, const char *message,
|
||||
Notification sound_type);
|
||||
|
||||
/* Kills all active file transfers for friendnum */
|
||||
void kill_all_file_transfers_friend(Tox *m, uint32_t friendnum);
|
||||
|
||||
void kill_all_file_transfers(Tox *m);
|
||||
|
||||
#endif /* #define FILE_TRANSFERS_H */
|
||||
1101
src/friendlist.c
1101
src/friendlist.c
File diff suppressed because it is too large
Load Diff
@@ -20,56 +20,75 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef FRIENDLIST_H_53I41IM
|
||||
#define FRIENDLIST_H_53I41IM
|
||||
#ifndef FRIENDLIST_H
|
||||
#define FRIENDLIST_H
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "file_senders.h"
|
||||
|
||||
#define TIME_STR_SIZE 16
|
||||
|
||||
struct FileReceiver {
|
||||
uint8_t filenames[MAX_FILES][MAX_STR_SIZE];
|
||||
FILE *files[MAX_FILES];
|
||||
bool pending[MAX_FILES];
|
||||
uint64_t size[MAX_FILES];
|
||||
uint32_t line_id[MAX_FILES];
|
||||
};
|
||||
#include "file_transfers.h"
|
||||
|
||||
struct LastOnline {
|
||||
uint64_t last_on;
|
||||
struct tm tm;
|
||||
uint8_t hour_min_str[TIME_STR_SIZE]; /* holds 12-hour time string e.g. "10:43 PM" */
|
||||
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];
|
||||
int32_t 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;
|
||||
uint8_t is_typing;
|
||||
bool logging_on; /* saves preference for friend irrespective of chat windows */
|
||||
TOX_CONNECTION connection_status;
|
||||
bool is_typing;
|
||||
bool logging_on; /* saves preference for friend irrespective of global settings */
|
||||
uint8_t status;
|
||||
|
||||
struct LastOnline last_online;
|
||||
struct FileReceiver file_receiver;
|
||||
struct GroupChatInvite group_invite;
|
||||
|
||||
struct FileTransfer file_receiver[MAX_FILES];
|
||||
struct FileTransfer file_sender[MAX_FILES];
|
||||
} ToxicFriend;
|
||||
|
||||
ToxWindow new_friendlist(void);
|
||||
void disable_chatwin(int32_t f_num);
|
||||
int get_friendnum(uint8_t *name);
|
||||
typedef struct {
|
||||
char name[TOXIC_MAX_NAME_LENGTH + 1];
|
||||
int namelength;
|
||||
char pub_key[TOX_PUBLIC_KEY_SIZE];
|
||||
uint32_t num;
|
||||
bool active;
|
||||
uint64_t last_on;
|
||||
} BlockedFriend;
|
||||
|
||||
void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int32_t num, bool sort);
|
||||
typedef struct {
|
||||
int num_selected;
|
||||
size_t num_friends;
|
||||
size_t num_online;
|
||||
size_t max_idx; /* 1 + the index of the last friend in list */
|
||||
uint32_t *index;
|
||||
ToxicFriend *list;
|
||||
} FriendsList;
|
||||
|
||||
ToxWindow new_friendlist(void);
|
||||
void disable_chatwin(uint32_t f_num);
|
||||
int get_friendnum(uint8_t *name);
|
||||
int load_blocklist(char *data);
|
||||
void kill_friendlist(void);
|
||||
void friendlist_onFriendAdded(ToxWindow *self, Tox *m, uint32_t num, bool sort);
|
||||
|
||||
/* sorts friendlist_index first by connection status then alphabetically */
|
||||
void sort_friendlist_index(void);
|
||||
|
||||
#endif /* end of include guard: FRIENDLIST_H_53I41IM */
|
||||
#endif /* end of include guard: FRIENDLIST_H */
|
||||
|
||||
@@ -20,12 +20,9 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
@@ -34,153 +31,160 @@
|
||||
#include "log.h"
|
||||
#include "line_info.h"
|
||||
#include "dns.h"
|
||||
#include "groupchat.h"
|
||||
#include "prompt.h"
|
||||
#include "help.h"
|
||||
#include "term_mplex.h"
|
||||
#include "avatars.h"
|
||||
|
||||
extern char *DATA_FILE;
|
||||
extern ToxWindow *prompt;
|
||||
|
||||
extern ToxicFriend friends[MAX_FRIENDS_NUM];
|
||||
|
||||
extern 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])
|
||||
{
|
||||
uint8_t *msg;
|
||||
|
||||
if (argc != 1) {
|
||||
msg = "Invalid syntax.";
|
||||
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||
if (argc < 1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Request ID required.");
|
||||
return;
|
||||
}
|
||||
|
||||
int req = atoi(argv[1]);
|
||||
|
||||
if ((req == 0 && strcmp(argv[1], "0")) || req >= MAX_FRIENDS_NUM) {
|
||||
msg = "No pending friend request with that number.";
|
||||
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||
if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req > MAX_FRIEND_REQUESTS) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!strlen(pending_frnd_requests[req])) {
|
||||
msg = "No pending friend request with that number.";
|
||||
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||
if (!FrndRequests.request[req].active) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t friendnum = tox_add_friend_norequest(m, pending_frnd_requests[req]);
|
||||
TOX_ERR_FRIEND_ADD err;
|
||||
uint32_t friendnum = tox_friend_add_norequest(m, FrndRequests.request[req].key, &err);
|
||||
|
||||
if (friendnum == -1)
|
||||
msg = "Failed to add friend.";
|
||||
else {
|
||||
msg = "Friend request accepted.";
|
||||
if (err != TOX_ERR_FRIEND_ADD_OK) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to add friend (error %d\n)", err);
|
||||
return;
|
||||
} else {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Friend request accepted.");
|
||||
on_friendadded(m, friendnum, true);
|
||||
}
|
||||
|
||||
memset(&pending_frnd_requests[req], 0, TOX_CLIENT_ID_SIZE);
|
||||
memset(&FrndRequests.request[req], 0, sizeof(struct friend_request));
|
||||
|
||||
int i;
|
||||
|
||||
for (i = num_frnd_requests; i > 0; --i) {
|
||||
if (!strlen(pending_frnd_requests[i - 1]))
|
||||
for (i = FrndRequests.max_idx; i > 0; --i) {
|
||||
if (FrndRequests.request[i - 1].active)
|
||||
break;
|
||||
}
|
||||
|
||||
num_frnd_requests = i;
|
||||
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||
FrndRequests.max_idx = i;
|
||||
--FrndRequests.num_requests;
|
||||
|
||||
}
|
||||
|
||||
void cmd_add_helper(ToxWindow *self, Tox *m, uint8_t *id_bin, uint8_t *msg)
|
||||
void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg)
|
||||
{
|
||||
uint8_t *errmsg;
|
||||
int32_t f_num = tox_add_friend(m, id_bin, msg, strlen(msg));
|
||||
const char *errmsg;
|
||||
|
||||
switch (f_num) {
|
||||
case TOX_FAERR_TOOLONG:
|
||||
TOX_ERR_FRIEND_ADD err;
|
||||
uint32_t f_num = tox_friend_add(m, (uint8_t *) id_bin, (uint8_t *) msg, strlen(msg), &err);
|
||||
|
||||
switch (err) {
|
||||
case TOX_ERR_FRIEND_ADD_TOO_LONG:
|
||||
errmsg = "Message is too long.";
|
||||
break;
|
||||
|
||||
case TOX_FAERR_NOMESSAGE:
|
||||
case TOX_ERR_FRIEND_ADD_NO_MESSAGE:
|
||||
errmsg = "Please add a message to your request.";
|
||||
break;
|
||||
|
||||
case TOX_FAERR_OWNKEY:
|
||||
case TOX_ERR_FRIEND_ADD_OWN_KEY:
|
||||
errmsg = "That appears to be your own ID.";
|
||||
break;
|
||||
|
||||
case TOX_FAERR_ALREADYSENT:
|
||||
case TOX_ERR_FRIEND_ADD_ALREADY_SENT:
|
||||
errmsg = "Friend request has already been sent.";
|
||||
break;
|
||||
|
||||
case TOX_FAERR_UNKNOWN:
|
||||
errmsg = "Undefined error when adding friend.";
|
||||
break;
|
||||
|
||||
case TOX_FAERR_BADCHECKSUM:
|
||||
case TOX_ERR_FRIEND_ADD_BAD_CHECKSUM:
|
||||
errmsg = "Bad checksum in address.";
|
||||
break;
|
||||
|
||||
case TOX_FAERR_SETNEWNOSPAM:
|
||||
case TOX_ERR_FRIEND_ADD_SET_NEW_NOSPAM:
|
||||
errmsg = "Nospam was different.";
|
||||
break;
|
||||
|
||||
default:
|
||||
case TOX_ERR_FRIEND_ADD_MALLOC:
|
||||
errmsg = "Core memory allocation failed.";
|
||||
break;
|
||||
|
||||
case TOX_ERR_FRIEND_ADD_OK:
|
||||
errmsg = "Friend request sent.";
|
||||
on_friendadded(m, f_num, true);
|
||||
break;
|
||||
|
||||
case TOX_ERR_FRIEND_ADD_NULL:
|
||||
/* fallthrough */
|
||||
default:
|
||||
errmsg = "Faile to add friend: Unknown error.";
|
||||
break;
|
||||
}
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
|
||||
}
|
||||
|
||||
void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
uint8_t *errmsg;
|
||||
|
||||
if (argc < 1) {
|
||||
errmsg = "Invalid syntax.";
|
||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Tox ID or address required.");
|
||||
return;
|
||||
}
|
||||
|
||||
char *id = argv[1];
|
||||
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] != '\"') {
|
||||
errmsg = "Message must be enclosed in quotes.";
|
||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||
if (argv[2][0] != '\"') {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Message must be enclosed in quotes.");
|
||||
return;
|
||||
}
|
||||
|
||||
temp[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];
|
||||
uint16_t n_len = tox_get_self_name(m, selfname);
|
||||
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);
|
||||
}
|
||||
|
||||
uint8_t id_bin[TOX_FRIEND_ADDRESS_SIZE] = {0};
|
||||
uint16_t id_len = strlen(id);
|
||||
char id_bin[TOX_ADDRESS_SIZE] = {0};
|
||||
uint16_t id_len = (uint16_t) strlen(id);
|
||||
|
||||
/* try to add tox ID */
|
||||
if (id_len == 2 * TOX_FRIEND_ADDRESS_SIZE) {
|
||||
if (id_len == 2 * TOX_ADDRESS_SIZE) {
|
||||
size_t i;
|
||||
char xx[3];
|
||||
uint32_t x;
|
||||
|
||||
for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; ++i) {
|
||||
for (i = 0; i < TOX_ADDRESS_SIZE; ++i) {
|
||||
xx[0] = id[2 * i];
|
||||
xx[1] = id[2 * i + 1];
|
||||
xx[2] = '\0';
|
||||
|
||||
if (sscanf(xx, "%02x", &x) != 1) {
|
||||
errmsg = "Invalid ID.";
|
||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid Tox ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -193,78 +197,173 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
if (argc < 2 || strlen(argv[1]) < 3) {
|
||||
avatar_unset(m);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Avatar is not set.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (argv[1][0] != '\"') {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Path must be enclosed in quotes.");
|
||||
return;
|
||||
}
|
||||
|
||||
/* remove opening and closing quotes */
|
||||
char path[MAX_STR_SIZE];
|
||||
snprintf(path, sizeof(path), "%s", &argv[1][1]);
|
||||
int len = strlen(path) - 1;
|
||||
|
||||
if (len <= 0) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid path.");
|
||||
return;
|
||||
}
|
||||
|
||||
path[len] = '\0';
|
||||
char filename[MAX_STR_SIZE];
|
||||
get_file_name(filename, sizeof(filename), path);
|
||||
|
||||
if (avatar_set(m, path, len) == -1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,
|
||||
"Failed to set avatar. Avatars must be in PNG format and may not exceed %d bytes.",
|
||||
MAX_AVATAR_FILE_SIZE);
|
||||
return;
|
||||
}
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Avatar set to '%s'", filename);
|
||||
}
|
||||
|
||||
void cmd_clear(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
line_info_clear(self->chatwin->hst);
|
||||
wclear(window);
|
||||
|
||||
if (self->is_prompt) {
|
||||
int y2, x2;
|
||||
getmaxyx(window, y2, x2);
|
||||
wmove(self->chatwin->history, y2 - 1, 2);
|
||||
}
|
||||
force_refresh(window);
|
||||
}
|
||||
|
||||
void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
uint8_t *errmsg;
|
||||
|
||||
/* check arguments */
|
||||
if (argc != 3) {
|
||||
errmsg = "Invalid syntax.";
|
||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Require: <ip> <port> <key>");
|
||||
return;
|
||||
}
|
||||
|
||||
char *ip = argv[1];
|
||||
char *port = argv[2];
|
||||
char *key = argv[3];
|
||||
const char *ip = argv[1];
|
||||
const char *port = argv[2];
|
||||
const char *ascii_key = argv[3];
|
||||
|
||||
if (atoi(port) == 0) {
|
||||
errmsg = "Invalid syntax.";
|
||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid port.");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t *binary_string = hex_string_to_bin(key);
|
||||
tox_bootstrap_from_address(m, ip, TOX_ENABLE_IPV6_DEFAULT,
|
||||
htons(atoi(port)), binary_string);
|
||||
free(binary_string);
|
||||
char key_binary[TOX_PUBLIC_KEY_SIZE * 2 + 1];
|
||||
if (hex_string_to_bin(ascii_key, strlen(ascii_key), key_binary, TOX_PUBLIC_KEY_SIZE) == -1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid key.");
|
||||
return;
|
||||
}
|
||||
|
||||
TOX_ERR_BOOTSTRAP err;
|
||||
tox_bootstrap(m, ip, atoi(port), (uint8_t *) key_binary, &err);
|
||||
tox_add_tcp_relay(m, ip, atoi(port), (uint8_t *) key_binary, &err);
|
||||
|
||||
switch (err) {
|
||||
case TOX_ERR_BOOTSTRAP_BAD_HOST:
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed: Invalid IP.");
|
||||
break;
|
||||
|
||||
case TOX_ERR_BOOTSTRAP_BAD_PORT:
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed: Invalid port.");
|
||||
break;
|
||||
|
||||
case TOX_ERR_BOOTSTRAP_NULL:
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed.");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
if (argc < 1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Request ID required.");
|
||||
return;
|
||||
}
|
||||
|
||||
int req = atoi(argv[1]);
|
||||
|
||||
if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req > MAX_FRIEND_REQUESTS) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!FrndRequests.request[req].active) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
memset(&FrndRequests.request[req], 0, sizeof(struct friend_request));
|
||||
|
||||
int i;
|
||||
|
||||
for (i = FrndRequests.max_idx; i > 0; --i) {
|
||||
if (FrndRequests.request[i - 1].active)
|
||||
break;
|
||||
}
|
||||
|
||||
FrndRequests.max_idx = i;
|
||||
--FrndRequests.num_requests;
|
||||
}
|
||||
|
||||
void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
uint8_t *errmsg;
|
||||
|
||||
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
|
||||
errmsg = " * Warning: Too many windows are open.";
|
||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, RED);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open.");
|
||||
return;
|
||||
}
|
||||
|
||||
int groupnum = tox_add_groupchat(m);
|
||||
if (argc < 1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Please specify group type: text | audio");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t type;
|
||||
|
||||
if (!strcasecmp(argv[1], "audio"))
|
||||
type = TOX_GROUPCHAT_TYPE_AV;
|
||||
else if (!strcasecmp(argv[1], "text"))
|
||||
type = TOX_GROUPCHAT_TYPE_TEXT;
|
||||
else {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Valid group types are: text | audio");
|
||||
return;
|
||||
}
|
||||
|
||||
int groupnum = -1;
|
||||
|
||||
if (type == TOX_GROUPCHAT_TYPE_TEXT)
|
||||
groupnum = tox_add_groupchat(m);
|
||||
#ifdef AUDIO
|
||||
else
|
||||
groupnum = toxav_add_av_groupchat(m, NULL, NULL);
|
||||
#endif
|
||||
|
||||
if (groupnum == -1) {
|
||||
errmsg = "Group chat instance failed to initialize.";
|
||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (init_groupchat_win(prompt, m, groupnum) == -1) {
|
||||
errmsg = "Group chat window failed to initialize.";
|
||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||
if (init_groupchat_win(prompt, m, groupnum, type) == -1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize.");
|
||||
tox_del_groupchat(m, groupnum);
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t msg[MAX_STR_SIZE];
|
||||
snprintf(msg, sizeof(msg), "Group chat created as %d.", groupnum);
|
||||
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||
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])
|
||||
{
|
||||
uint8_t *msg;
|
||||
const char *msg;
|
||||
struct chatlog *log = self->chatwin->log;
|
||||
|
||||
if (argc == 0) {
|
||||
@@ -273,167 +372,119 @@ void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
|
||||
else
|
||||
msg = "Logging for this window is OFF. Type \"/log on\" to enable.";
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t *swch = argv[1];
|
||||
const char *swch = argv[1];
|
||||
|
||||
if (!strcmp(swch, "1") || !strcmp(swch, "on")) {
|
||||
char myid[TOX_ADDRESS_SIZE];
|
||||
tox_self_get_address(m, (uint8_t *) myid);
|
||||
|
||||
int log_ret = -1;
|
||||
|
||||
if (self->is_chat) {
|
||||
friends[self->num].logging_on = true;
|
||||
log_enable(self->name, friends[self->num].pub_key, log);
|
||||
Friends.list[self->num].logging_on = true;
|
||||
log_ret = log_enable(self->name, myid, Friends.list[self->num].pub_key, log, LOG_CHAT);
|
||||
} else if (self->is_prompt) {
|
||||
uint8_t myid[TOX_FRIEND_ADDRESS_SIZE];
|
||||
tox_get_address(m, myid);
|
||||
log_enable(self->name, myid, log);
|
||||
log_ret = log_enable(self->name, myid, NULL, log, LOG_PROMPT);
|
||||
} else if (self->is_groupchat) {
|
||||
log_enable(self->name, NULL, log);
|
||||
log_ret = log_enable(self->name, myid, NULL, log, LOG_GROUP);
|
||||
}
|
||||
|
||||
msg = "Logging enabled";
|
||||
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||
msg = log_ret == 0 ? "Logging enabled." : "Warning: Log failed to initialize.";
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
|
||||
return;
|
||||
} else if (!strcmp(swch, "0") || !strcmp(swch, "off")) {
|
||||
if (self->is_chat)
|
||||
friends[self->num].logging_on = false;
|
||||
Friends.list[self->num].logging_on = false;
|
||||
|
||||
log_disable(log);
|
||||
|
||||
msg = "Logging disabled";
|
||||
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||
msg = "Logging disabled.";
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
|
||||
return;
|
||||
}
|
||||
|
||||
msg = "Invalid option. Use \"/log on\" and \"/log off\" to toggle logging.";
|
||||
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
|
||||
}
|
||||
|
||||
void cmd_myid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
char id[TOX_FRIEND_ADDRESS_SIZE * 2 + 1] = {0};
|
||||
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);
|
||||
}
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, id, SYS_MSG, 0, 0);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", id);
|
||||
}
|
||||
|
||||
void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
uint8_t *errmsg;
|
||||
|
||||
/* check arguments */
|
||||
if (argc < 1) {
|
||||
errmsg = "Invalid name.";
|
||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Input required.");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t *nick = argv[1];
|
||||
int len = strlen(nick);
|
||||
char nick[MAX_STR_SIZE];
|
||||
size_t len = 0;
|
||||
|
||||
if (nick[0] == '\"') {
|
||||
++nick;
|
||||
len -= 2;
|
||||
nick[len] = L'\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);
|
||||
}
|
||||
|
||||
if (!valid_nick(nick)) {
|
||||
errmsg = "Invalid name.";
|
||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid name.");
|
||||
return;
|
||||
}
|
||||
|
||||
len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
|
||||
nick[len] = '\0';
|
||||
|
||||
nick[len] = L'\0';
|
||||
|
||||
tox_set_name(m, nick, len);
|
||||
prompt_update_nick(prompt, nick, len);
|
||||
tox_self_set_name(m, (uint8_t *) nick, len, NULL);
|
||||
prompt_update_nick(prompt, nick);
|
||||
|
||||
store_data(m, DATA_FILE);
|
||||
}
|
||||
|
||||
void cmd_note(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
uint8_t *errmsg;
|
||||
|
||||
if (argc < 1) {
|
||||
errmsg = "Wrong number of arguments.";
|
||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Input required.");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t *msg = argv[1];
|
||||
|
||||
if (msg[0] != '\"') {
|
||||
errmsg = "Note must be enclosed in quotes.";
|
||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||
if (argv[1][0] != '\"') {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Note must be enclosed in quotes.");
|
||||
return;
|
||||
}
|
||||
|
||||
msg[strlen(++msg) - 1] = L'\0';
|
||||
uint16_t len = strlen(msg);
|
||||
tox_set_status_message(m, msg, len);
|
||||
prompt_update_statusmessage(prompt, 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, m, msg);
|
||||
}
|
||||
|
||||
void cmd_prompt_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
struct history *hst = self->chatwin->hst;
|
||||
line_info_clear(hst);
|
||||
struct line_info *start = hst->line_start;
|
||||
|
||||
uint8_t *msg = "Global commands:";
|
||||
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 1, CYAN);
|
||||
|
||||
#ifdef _SUPPORT_AUDIO
|
||||
#define NUMLINES 14
|
||||
#else
|
||||
#define NUMLINES 12
|
||||
#endif
|
||||
|
||||
uint8_t lines[NUMLINES][MAX_STR_SIZE] = {
|
||||
|
||||
{ " /add <id> <msg> : Add friend with optional message" },
|
||||
{ " /accept <n> : Accept friend request" },
|
||||
{ " /connect <ip> <port> <key> : Manually connect to a DHT node" },
|
||||
{ " /status <type> <msg> : Set status with optional note" },
|
||||
{ " /note <msg> : Set a personal note" },
|
||||
{ " /nick <nick> : Set your nickname" },
|
||||
{ " /log <on> or <off> : Enable/disable logging" },
|
||||
{ " /groupchat : Create a group chat" },
|
||||
{ " /myid : Print your ID" },
|
||||
{ " /help : Print this message again" },
|
||||
{ " /clear : Clear window history" },
|
||||
{ " /quit or /exit : Exit Toxic" },
|
||||
#ifdef _SUPPORT_AUDIO
|
||||
{ " /lsdev <type> : List devices where type: in|out" },
|
||||
{ " /sdev <type> <id> : Set active device" },
|
||||
#endif /* _SUPPORT_AUDIO */
|
||||
|
||||
};
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NUMLINES; ++i)
|
||||
line_info_add(self, NULL, NULL, NULL, lines[i], SYS_MSG, 0, 0);
|
||||
|
||||
msg = " * Argument messages must be enclosed in quotation marks.";
|
||||
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 1, CYAN);
|
||||
msg = " * Use ctrl-o and ctrl-p to navigate through the tabs.";
|
||||
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 1, CYAN);
|
||||
line_info_add(self, NULL, NULL, NULL, "", SYS_MSG, 0, 0);
|
||||
|
||||
hst->line_start = start;
|
||||
help_init_menu(self);
|
||||
}
|
||||
|
||||
void cmd_quit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
@@ -441,50 +492,84 @@ void cmd_quit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
|
||||
exit_toxic_success(m);
|
||||
}
|
||||
|
||||
void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
void cmd_requests(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
uint8_t *msg = NULL;
|
||||
uint8_t *errmsg;
|
||||
if (FrndRequests.num_requests == 0) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend requests.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (argc >= 2) {
|
||||
msg = argv[2];
|
||||
int i, j;
|
||||
int count = 0;
|
||||
|
||||
if (msg[0] != '\"') {
|
||||
errmsg = "Note must be enclosed in quotes.";
|
||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||
return;
|
||||
for (i = 0; i < FrndRequests.max_idx; ++i) {
|
||||
if (!FrndRequests.request[i].active)
|
||||
continue;
|
||||
|
||||
char id[TOX_PUBLIC_KEY_SIZE * 2 + 1] = {0};
|
||||
|
||||
for (j = 0; j < TOX_PUBLIC_KEY_SIZE; ++j) {
|
||||
char d[3];
|
||||
snprintf(d, sizeof(d), "%02X", FrndRequests.request[i].key[j] & 0xff);
|
||||
strcat(id, d);
|
||||
}
|
||||
} else if (argc != 1) {
|
||||
errmsg = "Wrong number of arguments.";
|
||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
char *status = argv[1];
|
||||
str_to_lower(status);
|
||||
int len = strlen(status);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%d : %s", i, id);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", FrndRequests.request[i].msg);
|
||||
|
||||
TOX_USERSTATUS status_kind;
|
||||
|
||||
if (!strcmp(status, "online"))
|
||||
status_kind = TOX_USERSTATUS_NONE;
|
||||
else if (!strcmp(status, "away"))
|
||||
status_kind = TOX_USERSTATUS_AWAY;
|
||||
else if (!strcmp(status, "busy"))
|
||||
status_kind = TOX_USERSTATUS_BUSY;
|
||||
else {
|
||||
errmsg = "Invalid status. Valid statuses are: online, busy and away.";
|
||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
tox_set_user_status(m, status_kind);
|
||||
prompt_update_status(prompt, status_kind);
|
||||
|
||||
if (msg != NULL) {
|
||||
msg[strlen(++msg) - 1] = L'\0'; /* remove opening and closing quotes */
|
||||
uint16_t len = strlen(msg);
|
||||
tox_set_status_message(m, msg, len);
|
||||
prompt_update_statusmessage(prompt, msg, len);
|
||||
if (++count < FrndRequests.num_requests)
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
bool have_note = false;
|
||||
const char *errmsg;
|
||||
|
||||
lock_status ();
|
||||
|
||||
if (argc >= 2) {
|
||||
have_note = true;
|
||||
} else if (argc < 1) {
|
||||
errmsg = "Require a status. Statuses are: online, busy and away.";
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
const char *status_str = argv[1];
|
||||
TOX_USER_STATUS status;
|
||||
|
||||
if (!strcasecmp(status_str, "online"))
|
||||
status = TOX_USER_STATUS_NONE;
|
||||
else if (!strcasecmp(status_str, "away"))
|
||||
status = TOX_USER_STATUS_AWAY;
|
||||
else if (!strcasecmp(status_str, "busy"))
|
||||
status = TOX_USER_STATUS_BUSY;
|
||||
else {
|
||||
errmsg = "Invalid status. Valid statuses are: online, busy and away.";
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
tox_self_set_status(m, status);
|
||||
prompt_update_status(prompt, status);
|
||||
|
||||
if (have_note) {
|
||||
if (argv[2][0] != '\"') {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Note must be enclosed in quotes.");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* remove opening and closing quotes */
|
||||
char msg[MAX_STR_SIZE];
|
||||
snprintf(msg, sizeof(msg), "%s", &argv[2][1]);
|
||||
int len = strlen(msg) - 1;
|
||||
msg[len] = '\0';
|
||||
|
||||
prompt_update_statusmessage(prompt, m, msg);
|
||||
}
|
||||
|
||||
finish:
|
||||
unlock_status ();
|
||||
}
|
||||
|
||||
@@ -20,16 +20,18 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _global_commands_h
|
||||
#define _global_commands_h
|
||||
#ifndef GLOBAL_COMMANDS_H
|
||||
#define GLOBAL_COMMANDS_H
|
||||
|
||||
#include "windows.h"
|
||||
#include "toxic.h"
|
||||
|
||||
void cmd_accept(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_add(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_clear(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_connect(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_groupchat(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_log(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_myid(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
@@ -37,13 +39,14 @@ void cmd_nick(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]
|
||||
void cmd_note(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_prompt_help(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_quit(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_requests(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_status(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
|
||||
void cmd_add_helper(ToxWindow *self, Tox *m, uint8_t *id_bin, uint8_t *msg);
|
||||
void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg);
|
||||
|
||||
#ifdef _SUPPORT_AUDIO
|
||||
#ifdef AUDIO
|
||||
void cmd_list_devices(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_change_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
#endif /* _SUPPORT_AUDIO */
|
||||
#endif /* AUDIO */
|
||||
|
||||
#endif /* #define _global_commands_h */
|
||||
#endif /* #define GLOBAL_COMMANDS_H */
|
||||
|
||||
79
src/group_commands.c
Normal file
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 */
|
||||
902
src/groupchat.c
902
src/groupchat.c
File diff suppressed because it is too large
Load Diff
@@ -20,29 +20,67 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _groupchat_h
|
||||
#define _groupchat_h
|
||||
#ifndef GROUPCHAT_H
|
||||
#define GROUPCHAT_H
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
#ifdef AUDIO
|
||||
#include "audio_call.h"
|
||||
#endif
|
||||
|
||||
#ifdef AUDIO
|
||||
#ifdef __APPLE__
|
||||
#include <OpenAL/al.h>
|
||||
#include <OpenAL/alc.h>
|
||||
#else
|
||||
#include <AL/al.h>
|
||||
#include <AL/alc.h>
|
||||
/* compatibility with older versions of OpenAL */
|
||||
#ifndef ALC_ALL_DEVICES_SPECIFIER
|
||||
#include <AL/alext.h>
|
||||
#endif /* ALC_ALL_DEVICES_SPECIFIER */
|
||||
#endif /* __APPLE__ */
|
||||
#endif /* AUDIO */
|
||||
|
||||
#define SIDEBAR_WIDTH 16
|
||||
#define SDBAR_OFST 2 /* Offset for the peer number box at the top of the statusbar */
|
||||
#define MAX_GROUPCHAT_NUM MAX_WINDOWS_NUM
|
||||
#define MAX_GROUPCHAT_NUM MAX_WINDOWS_NUM - 2
|
||||
#define GROUP_EVENT_WAIT 3
|
||||
|
||||
#ifdef AUDIO
|
||||
struct GAudio {
|
||||
ALCdevice *dvhandle; /* Handle of device selected/opened */
|
||||
ALCcontext *dvctx;
|
||||
ALuint source;
|
||||
ALuint buffers[OPENAL_BUFS];
|
||||
};
|
||||
#endif /* AUDIO */
|
||||
|
||||
typedef struct {
|
||||
int chatwin;
|
||||
bool active;
|
||||
uint8_t type;
|
||||
int num_peers;
|
||||
int side_pos; /* current position of the sidebar - used for scrolling up and down */
|
||||
uint64_t start_time;
|
||||
uint8_t *peer_names;
|
||||
uint8_t *oldpeer_names;
|
||||
uint16_t *peer_name_lengths;
|
||||
uint16_t *oldpeer_name_lengths;
|
||||
|
||||
#ifdef AUDIO
|
||||
struct GAudio audio;
|
||||
#endif
|
||||
} GroupChat;
|
||||
|
||||
void kill_groupchat_window(ToxWindow *self);
|
||||
int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum);
|
||||
void close_groupchat(ToxWindow *self, Tox *m, int groupnum);
|
||||
int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum, uint8_t type);
|
||||
|
||||
/* destroys and re-creates groupchat window with or without the peerlist */
|
||||
void redraw_groupchat_win(ToxWindow *self);
|
||||
|
||||
ToxWindow new_group_chat(Tox *m, int groupnum);
|
||||
|
||||
#endif /* #define _groupchat_h */
|
||||
#endif /* #define GROUPCHAT_H */
|
||||
|
||||
353
src/help.c
Normal file
353
src/help.c
Normal file
@@ -0,0 +1,353 @@
|
||||
/* 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 an avatar (leave path empty to unset)\n");
|
||||
wprintw(win, " /decline <id> : Decline friend request\n");
|
||||
wprintw(win, " /requests : List pending friend requests\n");
|
||||
wprintw(win, " /connect <ip> <port> <key> : Manually connect to a DHT node\n");
|
||||
wprintw(win, " /status <type> <msg> : Set status with optional note\n");
|
||||
wprintw(win, " /note <msg> : Set a personal note\n");
|
||||
wprintw(win, " /nick <nick> : Set your nickname\n");
|
||||
wprintw(win, " /log <on> or <off> : Enable/disable logging\n");
|
||||
wprintw(win, " /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");
|
||||
|
||||
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;
|
||||
|
||||
case 'r':
|
||||
help_init_window(self, 6, 80);
|
||||
self->help->type = HELP_GROUP;
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
help_init_window(self, 10, 80);
|
||||
self->help->type = HELP_CONTACTS;
|
||||
break;
|
||||
|
||||
case 'k':
|
||||
help_init_window(self, 13, 80);
|
||||
self->help->type = HELP_KEYS;
|
||||
break;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
/* file_senders.h
|
||||
/* help.h
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
@@ -20,33 +20,23 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _filesenders_h
|
||||
#define _filesenders_h
|
||||
#ifndef HELP_H
|
||||
#define HELP_H
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
#define FILE_PIECE_SIZE 2048 /* must be >= (MAX_CRYPTO_DATA_SIZE - 2) in toxcore/net_crypto.h */
|
||||
#define MAX_FILES 256
|
||||
#define TIMEOUT_FILESENDER 300
|
||||
typedef enum {
|
||||
HELP_MENU,
|
||||
HELP_GLOBAL,
|
||||
HELP_CHAT,
|
||||
HELP_GROUP,
|
||||
HELP_KEYS,
|
||||
HELP_CONTACTS,
|
||||
} HELP_TYPES;
|
||||
|
||||
typedef struct {
|
||||
FILE *file;
|
||||
ToxWindow *toxwin;
|
||||
int32_t friendnum;
|
||||
bool active;
|
||||
int filenum;
|
||||
uint8_t nextpiece[FILE_PIECE_SIZE];
|
||||
uint16_t piecelen;
|
||||
uint8_t pathname[MAX_STR_SIZE];
|
||||
uint64_t timestamp;
|
||||
uint64_t size;
|
||||
uint32_t line_id;
|
||||
} FileSender;
|
||||
void help_onDraw(ToxWindow *self);
|
||||
void help_init_menu(ToxWindow *self);
|
||||
void help_onKey(ToxWindow *self, wint_t key);
|
||||
|
||||
/* Should only be called on exit */
|
||||
void close_all_file_senders(void);
|
||||
|
||||
void do_file_senders(Tox *m);
|
||||
|
||||
#endif /* #define _filesenders_h */
|
||||
#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 = MAX(0, 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 = MAX(0, wcswidth(ctx->line, sizeof(ctx->line) / sizeof(wchar_t)));
|
||||
ctx->start = MAX(0, 1 + (mx_x * (wlen / mx_x) - mx_x) + (wlen % mx_x));
|
||||
}
|
||||
|
||||
/* 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 = MAX(0, wcswidth(ctx->line, sizeof(ctx->line) / sizeof(wchar_t)));
|
||||
ctx->start = wlen < mx_x ? 0 : wlen - mx_x + 1;
|
||||
}
|
||||
|
||||
/* Handles non-printable input keys that behave the same for all types of chat windows.
|
||||
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 */
|
||||
275
src/line_info.c
275
src/line_info.c
@@ -20,51 +20,57 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#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 = malloc(sizeof(struct line_info));
|
||||
hst->line_root = calloc(1, sizeof(struct line_info));
|
||||
|
||||
if (hst->line_root == NULL)
|
||||
exit_toxic_err("failed in line_info_init", FATALERR_MEMORY);
|
||||
|
||||
memset(hst->line_root, 0, sizeof(struct line_info));
|
||||
hst->line_start = hst->line_root;
|
||||
hst->line_end = hst->line_start;
|
||||
hst->queue_sz = 0;
|
||||
}
|
||||
|
||||
/* resets line_start */
|
||||
static void line_info_reset_start(ToxWindow *self, struct history *hst)
|
||||
/* resets line_start (moves to end of chat history) */
|
||||
void line_info_reset_start(ToxWindow *self, struct history *hst)
|
||||
{
|
||||
struct line_info *line = hst->line_end;
|
||||
|
||||
if (line->prev == NULL)
|
||||
return;
|
||||
|
||||
int y2, x2;
|
||||
getmaxyx(self->window, y2, x2);
|
||||
|
||||
struct line_info *line = hst->line_end;
|
||||
uint16_t lncnt = 0;
|
||||
int side_offst = self->is_groupchat ? SIDEBAR_WIDTH : 0;
|
||||
int top_offst = self->is_chat ? 3 : 0;
|
||||
int side_offst = self->show_peerlist ? SIDEBAR_WIDTH : 0;
|
||||
int top_offst = self->is_chat || self->is_prompt ? 2 : 0;
|
||||
int max_y = (y2 - CHATBOX_HEIGHT - top_offst);
|
||||
|
||||
while (line->prev && lncnt < max_y) {
|
||||
int curlines = 0;
|
||||
int nxtlines = line->newlines + (line->len / (x2 - side_offst));
|
||||
|
||||
do {
|
||||
curlines += 1 + nxtlines;
|
||||
line = line->prev;
|
||||
lncnt += (1 + line->len / (x2 - side_offst));
|
||||
}
|
||||
nxtlines = line->newlines + (line->len / (x2 - side_offst));
|
||||
} while (line->prev && curlines + nxtlines < max_y);
|
||||
|
||||
hst->line_start = line;
|
||||
}
|
||||
@@ -78,6 +84,15 @@ void line_info_cleanup(struct history *hst)
|
||||
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 */
|
||||
@@ -96,22 +111,13 @@ static void line_info_root_fwd(struct history *hst)
|
||||
hst->line_root = tmp;
|
||||
}
|
||||
|
||||
/* adds a line_info line to queue */
|
||||
static void line_info_add_queue(struct history *hst, struct line_info *line)
|
||||
{
|
||||
if (hst->queue_sz >= MAX_QUEUE)
|
||||
return;
|
||||
|
||||
hst->queue[hst->queue_sz++] = line;
|
||||
}
|
||||
|
||||
/* returns ptr to queue item 0 and removes it from queue */
|
||||
/* returns ptr to queue item 0 and removes it from queue. Returns NULL if queue is empty. */
|
||||
static struct line_info *line_info_ret_queue(struct history *hst)
|
||||
{
|
||||
if (hst->queue_sz <= 0)
|
||||
return NULL;
|
||||
|
||||
struct line_info *ret = hst->queue[0];
|
||||
struct line_info *line = hst->queue[0];
|
||||
|
||||
int i;
|
||||
|
||||
@@ -120,33 +126,64 @@ static struct line_info *line_info_ret_queue(struct history *hst)
|
||||
|
||||
--hst->queue_sz;
|
||||
|
||||
return ret;
|
||||
return line;
|
||||
}
|
||||
|
||||
/* creates new line_info line and puts it in the queue */
|
||||
void line_info_add(ToxWindow *self, uint8_t *tmstmp, uint8_t *name1, uint8_t *name2, uint8_t *msg,
|
||||
uint8_t type, uint8_t bold, uint8_t colour)
|
||||
/* creates new line_info line and puts it in the queue. */
|
||||
void line_info_add(ToxWindow *self, const char *timestr, const char *name1, const char *name2, uint8_t type,
|
||||
uint8_t bold, uint8_t colour, const char *msg, ...)
|
||||
{
|
||||
if (!self)
|
||||
return;
|
||||
|
||||
struct history *hst = self->chatwin->hst;
|
||||
struct line_info *new_line = malloc(sizeof(struct line_info));
|
||||
|
||||
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);
|
||||
|
||||
memset(new_line, 0, sizeof(struct line_info));
|
||||
char frmt_msg[MAX_LINE_INFO_MSG_SIZE] = {0};
|
||||
|
||||
va_list args;
|
||||
va_start(args, msg);
|
||||
vsnprintf(frmt_msg, sizeof(frmt_msg), msg, args);
|
||||
va_end(args);
|
||||
|
||||
int len = 1; /* there will always be a newline */
|
||||
|
||||
/* for type-specific formatting in print function */
|
||||
switch (type) {
|
||||
case ACTION:
|
||||
case IN_ACTION:
|
||||
/* fallthrough */
|
||||
case OUT_ACTION:
|
||||
len += strlen(user_settings->line_normal) + 2;
|
||||
break;
|
||||
|
||||
case IN_MSG:
|
||||
/* fallthrough */
|
||||
case OUT_MSG:
|
||||
len += strlen(user_settings->line_normal) + 3;
|
||||
break;
|
||||
|
||||
case CONNECTION:
|
||||
len += 3;
|
||||
len += strlen(user_settings->line_join) + 2;
|
||||
break;
|
||||
|
||||
case DISCONNECTION:
|
||||
len += strlen(user_settings->line_quit) + 2;
|
||||
break;
|
||||
|
||||
case SYS_MSG:
|
||||
break;
|
||||
|
||||
case NAME_CHANGE:
|
||||
len += strlen(user_settings->line_alert) + 1;
|
||||
break;
|
||||
|
||||
case PROMPT:
|
||||
++len;
|
||||
break;
|
||||
@@ -156,21 +193,21 @@ void line_info_add(ToxWindow *self, uint8_t *tmstmp, uint8_t *name1, uint8_t *na
|
||||
break;
|
||||
}
|
||||
|
||||
if (msg) {
|
||||
snprintf(new_line->msg, sizeof(new_line->msg), "%s", msg);
|
||||
if (frmt_msg[0]) {
|
||||
snprintf(new_line->msg, sizeof(new_line->msg), "%s", frmt_msg);
|
||||
len += strlen(new_line->msg);
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; msg[i]; ++i) {
|
||||
if (msg[i] == '\n')
|
||||
for (i = 0; frmt_msg[i]; ++i) {
|
||||
if (frmt_msg[i] == '\n')
|
||||
++new_line->newlines;
|
||||
}
|
||||
}
|
||||
|
||||
if (tmstmp) {
|
||||
snprintf(new_line->timestamp, sizeof(new_line->timestamp), "%s", tmstmp);
|
||||
len += strlen(new_line->timestamp);
|
||||
if (timestr) {
|
||||
snprintf(new_line->timestr, sizeof(new_line->timestr), "%s", timestr);
|
||||
len += strlen(new_line->timestr) + 1;
|
||||
}
|
||||
|
||||
if (name1) {
|
||||
@@ -187,12 +224,14 @@ void line_info_add(ToxWindow *self, uint8_t *tmstmp, uint8_t *name1, uint8_t *na
|
||||
new_line->type = type;
|
||||
new_line->bold = bold;
|
||||
new_line->colour = colour;
|
||||
new_line->noread_flag = false;
|
||||
new_line->timestamp = get_unix_time();
|
||||
|
||||
line_info_add_queue(hst, new_line);
|
||||
hst->queue[hst->queue_sz++] = new_line;
|
||||
}
|
||||
|
||||
/* adds a single queue item to hst if possible. only called once per call to line_info_print() */
|
||||
static void line_info_check_queue(ToxWindow *self)
|
||||
static void line_info_check_queue(ToxWindow *self)
|
||||
{
|
||||
struct history *hst = self->chatwin->hst;
|
||||
struct line_info *line = line_info_ret_queue(hst);
|
||||
@@ -211,13 +250,14 @@ static void line_info_check_queue(ToxWindow *self)
|
||||
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->is_groupchat ? SIDEBAR_WIDTH : 0; /* offset width of groupchat sidebar */
|
||||
int offst = self->show_peerlist ? SIDEBAR_WIDTH : 0; /* offset width of groupchat sidebar */
|
||||
int lines = 1 + line->newlines + (line->len / (x2 - offst));
|
||||
int max_y = self->is_prompt ? y2 : y2 - CHATBOX_HEIGHT;
|
||||
int max_y = y2 - CHATBOX_HEIGHT;
|
||||
|
||||
/* move line_start forward proportionate to the number of new lines */
|
||||
if (y + lines - 1 >= max_y) {
|
||||
@@ -229,6 +269,8 @@ static void line_info_check_queue(ToxWindow *self)
|
||||
}
|
||||
}
|
||||
|
||||
#define NOREAD_FLAG_TIMEOUT 5 /* seconds before a sent message with no read receipt is flagged as unread */
|
||||
|
||||
void line_info_print(ToxWindow *self)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
@@ -246,9 +288,6 @@ void line_info_print(ToxWindow *self)
|
||||
int y2, x2;
|
||||
getmaxyx(self->window, y2, x2);
|
||||
|
||||
if (self->is_prompt)
|
||||
y2 = user_settings->history_size; /* temporary fix to make prompt scroll */
|
||||
|
||||
if (x2 <= SIDEBAR_WIDTH)
|
||||
return;
|
||||
|
||||
@@ -258,7 +297,6 @@ void line_info_print(ToxWindow *self)
|
||||
wmove(win, 2, 0);
|
||||
|
||||
struct line_info *line = hst->line_start->next;
|
||||
int offst = self->is_groupchat ? SIDEBAR_WIDTH : 0;
|
||||
int numlines = 0;
|
||||
|
||||
while (line && numlines++ <= y2) {
|
||||
@@ -266,9 +304,12 @@ void line_info_print(ToxWindow *self)
|
||||
|
||||
switch (type) {
|
||||
case OUT_MSG:
|
||||
/* fallthrough */
|
||||
case OUT_MSG_READ:
|
||||
/* fallthrough */
|
||||
case IN_MSG:
|
||||
wattron(win, COLOR_PAIR(BLUE));
|
||||
wprintw(win, "%s", line->timestamp);
|
||||
wprintw(win, "%s ", line->timestr);
|
||||
wattroff(win, COLOR_PAIR(BLUE));
|
||||
|
||||
int nameclr = GREEN;
|
||||
@@ -279,34 +320,66 @@ void line_info_print(ToxWindow *self)
|
||||
nameclr = CYAN;
|
||||
|
||||
wattron(win, COLOR_PAIR(nameclr));
|
||||
wprintw(win, "%s: ", line->name1);
|
||||
wprintw(win, "%s %s: ", user_settings->line_normal, line->name1);
|
||||
wattroff(win, COLOR_PAIR(nameclr));
|
||||
|
||||
if (line->msg[0] == '>')
|
||||
wattron(win, COLOR_PAIR(GREEN));
|
||||
else if (line->msg[0] == '<')
|
||||
wattron(win, COLOR_PAIR(RED));
|
||||
|
||||
wprintw(win, "%s\n", line->msg);
|
||||
wprintw(win, "%s", line->msg);
|
||||
|
||||
if (line->msg[0] == '>')
|
||||
wattroff(win, COLOR_PAIR(GREEN));
|
||||
else if (line->msg[0] == '<')
|
||||
wattroff(win, COLOR_PAIR(RED));
|
||||
|
||||
if (type == OUT_MSG && timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) {
|
||||
wattron(win, COLOR_PAIR(RED));
|
||||
wprintw(win, " x", line->msg);
|
||||
wattroff(win, COLOR_PAIR(RED));
|
||||
|
||||
if (line->noread_flag == false) {
|
||||
line->noread_flag = true;
|
||||
line->len += 2;
|
||||
}
|
||||
}
|
||||
|
||||
wprintw(win, "\n", line->msg);
|
||||
break;
|
||||
|
||||
case ACTION:
|
||||
case OUT_ACTION_READ:
|
||||
/* fallthrough */
|
||||
case OUT_ACTION:
|
||||
/* fallthrough */
|
||||
case IN_ACTION:
|
||||
wattron(win, COLOR_PAIR(BLUE));
|
||||
wprintw(win, "%s", line->timestamp);
|
||||
wprintw(win, "%s ", line->timestr);
|
||||
wattroff(win, COLOR_PAIR(BLUE));
|
||||
|
||||
wattron(win, COLOR_PAIR(YELLOW));
|
||||
wprintw(win, "* %s %s\n", line->name1, line->msg);
|
||||
wprintw(win, "%s %s %s", user_settings->line_normal, line->name1, line->msg);
|
||||
wattroff(win, COLOR_PAIR(YELLOW));
|
||||
|
||||
if (type == OUT_ACTION && timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) {
|
||||
wattron(win, COLOR_PAIR(RED));
|
||||
wprintw(win, " x", line->msg);
|
||||
wattroff(win, COLOR_PAIR(RED));
|
||||
|
||||
if (line->noread_flag == false) {
|
||||
line->noread_flag = true;
|
||||
line->len += 2;
|
||||
}
|
||||
}
|
||||
|
||||
wprintw(win, "\n", line->msg);
|
||||
break;
|
||||
|
||||
case SYS_MSG:
|
||||
if (line->timestamp[0]) {
|
||||
if (line->timestr[0]) {
|
||||
wattron(win, COLOR_PAIR(BLUE));
|
||||
wprintw(win, "%s", line->timestamp);
|
||||
wprintw(win, "%s ", line->timestr);
|
||||
wattroff(win, COLOR_PAIR(BLUE));
|
||||
}
|
||||
|
||||
@@ -339,13 +412,33 @@ void line_info_print(ToxWindow *self)
|
||||
|
||||
case CONNECTION:
|
||||
wattron(win, COLOR_PAIR(BLUE));
|
||||
wprintw(win, "%s", line->timestamp);
|
||||
wprintw(win, "%s ", line->timestr);
|
||||
wattroff(win, COLOR_PAIR(BLUE));
|
||||
|
||||
wattron(win, COLOR_PAIR(line->colour));
|
||||
wprintw(win, "%s ", user_settings->line_join);
|
||||
|
||||
wattron(win, A_BOLD);
|
||||
wprintw(win, "* %s ", line->name1);
|
||||
wprintw(win, "%s ", line->name1);
|
||||
wattroff(win, A_BOLD);
|
||||
|
||||
wprintw(win, "%s\n", line->msg);
|
||||
wattroff(win, COLOR_PAIR(line->colour));
|
||||
|
||||
break;
|
||||
|
||||
case DISCONNECTION:
|
||||
wattron(win, COLOR_PAIR(BLUE));
|
||||
wprintw(win, "%s ", line->timestr);
|
||||
wattroff(win, COLOR_PAIR(BLUE));
|
||||
|
||||
wattron(win, COLOR_PAIR(line->colour));
|
||||
wprintw(win, "%s ", user_settings->line_quit);
|
||||
|
||||
wattron(win, A_BOLD);
|
||||
wprintw(win, "%s ", line->name1);
|
||||
wattroff(win, A_BOLD);
|
||||
|
||||
wprintw(win, "%s\n", line->msg);
|
||||
wattroff(win, COLOR_PAIR(line->colour));
|
||||
|
||||
@@ -353,12 +446,13 @@ void line_info_print(ToxWindow *self)
|
||||
|
||||
case NAME_CHANGE:
|
||||
wattron(win, COLOR_PAIR(BLUE));
|
||||
wprintw(win, "%s", line->timestamp);
|
||||
wprintw(win, "%s ", line->timestr);
|
||||
wattroff(win, COLOR_PAIR(BLUE));
|
||||
|
||||
wattron(win, COLOR_PAIR(MAGENTA));
|
||||
wprintw(win, "%s ", user_settings->line_alert);
|
||||
wattron(win, A_BOLD);
|
||||
wprintw(win, "* %s", line->name1);
|
||||
wprintw(win, "%s", line->name1);
|
||||
wattroff(win, A_BOLD);
|
||||
|
||||
wprintw(win, "%s", line->msg);
|
||||
@@ -379,7 +473,8 @@ void line_info_print(ToxWindow *self)
|
||||
line_info_print(self);
|
||||
}
|
||||
|
||||
void line_info_set(ToxWindow *self, uint32_t id, uint8_t *msg)
|
||||
/* 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;
|
||||
|
||||
@@ -393,29 +488,30 @@ void line_info_set(ToxWindow *self, uint32_t id, uint8_t *msg)
|
||||
}
|
||||
}
|
||||
|
||||
static void line_info_goto_root(struct history *hst)
|
||||
/* static void line_info_goto_root(struct history *hst)
|
||||
{
|
||||
hst->line_start = hst->line_root;
|
||||
}
|
||||
} */
|
||||
|
||||
static void line_info_scroll_up(struct history *hst)
|
||||
{
|
||||
if (hst->line_start->prev)
|
||||
hst->line_start = hst->line_start->prev;
|
||||
else beep();
|
||||
else sound_notify(NULL, notif_error, NT_ALWAYS, NULL);
|
||||
}
|
||||
|
||||
static void line_info_scroll_down(struct history *hst)
|
||||
{
|
||||
if (hst->line_start->next)
|
||||
hst->line_start = hst->line_start->next;
|
||||
else beep();
|
||||
else sound_notify(NULL, notif_error, NT_ALWAYS, NULL);
|
||||
}
|
||||
|
||||
static void line_info_page_up(ToxWindow *self, struct history *hst)
|
||||
{
|
||||
int x2, y2;
|
||||
getmaxyx(self->window, y2, x2);
|
||||
(void) x2;
|
||||
int jump_dist = y2 / 2;
|
||||
int i;
|
||||
|
||||
@@ -427,6 +523,7 @@ 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;
|
||||
|
||||
@@ -439,35 +536,23 @@ bool line_info_onKey(ToxWindow *self, wint_t key)
|
||||
struct history *hst = self->chatwin->hst;
|
||||
bool match = true;
|
||||
|
||||
switch (key) {
|
||||
/* TODO: Find good key bindings for all this stuff */
|
||||
case T_KEY_C_H:
|
||||
line_info_page_up(self, hst);
|
||||
break;
|
||||
|
||||
case T_KEY_C_B:
|
||||
line_info_page_down(self, hst);
|
||||
break;
|
||||
|
||||
case KEY_PPAGE:
|
||||
line_info_scroll_up(hst);
|
||||
break;
|
||||
|
||||
case KEY_NPAGE:
|
||||
line_info_scroll_down(hst);
|
||||
break;
|
||||
|
||||
/* case ?:
|
||||
line_info_goto_root(hst);
|
||||
break;
|
||||
|
||||
case ?:
|
||||
line_info_reset_start(self, hst);
|
||||
break; */
|
||||
|
||||
default:
|
||||
match = false;
|
||||
break;
|
||||
if (key == user_settings->key_half_page_up) {
|
||||
line_info_page_up(self, hst);
|
||||
}
|
||||
else if (key == user_settings->key_half_page_down) {
|
||||
line_info_page_down(self, hst);
|
||||
}
|
||||
else if (key == user_settings->key_scroll_line_up) {
|
||||
line_info_scroll_up(hst);
|
||||
}
|
||||
else if (key == user_settings->key_scroll_line_down) {
|
||||
line_info_scroll_down(hst);
|
||||
}
|
||||
else if (key == user_settings->key_page_bottom) {
|
||||
line_info_reset_start(self, hst);
|
||||
}
|
||||
else {
|
||||
match = false;
|
||||
}
|
||||
|
||||
return match;
|
||||
|
||||
@@ -20,36 +20,43 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _line_info_h
|
||||
#define _line_info_h
|
||||
#ifndef LINE_INFO_H
|
||||
#define LINE_INFO_H
|
||||
|
||||
#include "windows.h"
|
||||
#include "toxic.h"
|
||||
|
||||
#define MAX_HISTORY 10000
|
||||
#define MIN_HISTORY 20
|
||||
#define MAX_QUEUE 32
|
||||
#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,
|
||||
ACTION,
|
||||
CONNECTION,
|
||||
DISCONNECTION,
|
||||
NAME_CHANGE,
|
||||
} LINE_TYPE;
|
||||
|
||||
struct line_info {
|
||||
uint8_t timestamp[TIME_STR_SIZE];
|
||||
uint8_t name1[TOXIC_MAX_NAME_LENGTH];
|
||||
uint8_t name2[TOXIC_MAX_NAME_LENGTH];
|
||||
uint8_t msg[TOX_MAX_MESSAGE_LENGTH];
|
||||
char timestr[TIME_STR_SIZE];
|
||||
char name1[TOXIC_MAX_NAME_LENGTH + 1];
|
||||
char name2[TOXIC_MAX_NAME_LENGTH + 1];
|
||||
char msg[MAX_LINE_INFO_MSG_SIZE];
|
||||
uint64_t timestamp;
|
||||
uint8_t type;
|
||||
uint8_t bold;
|
||||
uint8_t colour;
|
||||
uint8_t noread_flag; /* true if a line should be flagged as unread */
|
||||
uint32_t id;
|
||||
uint16_t len; /* combined len of all strings */
|
||||
uint16_t len; /* combined len of entire line */
|
||||
uint8_t newlines;
|
||||
|
||||
struct line_info *prev;
|
||||
@@ -63,13 +70,13 @@ struct history {
|
||||
struct line_info *line_end;
|
||||
uint32_t start_id; /* keeps track of where line_start should be when at bottom of history */
|
||||
|
||||
struct line_info *queue[MAX_QUEUE];
|
||||
struct line_info *queue[MAX_LINE_INFO_QUEUE];
|
||||
int queue_sz;
|
||||
};
|
||||
|
||||
/* creates new line_info line and puts it in the queue */
|
||||
void line_info_add(ToxWindow *self, uint8_t *tmstmp, uint8_t *name1, uint8_t *name2, uint8_t *msg,
|
||||
uint8_t type, uint8_t bold, uint8_t colour);
|
||||
/* creates new line_info line and puts it in the queue. */
|
||||
void line_info_add(ToxWindow *self, const char *timestr, const char *name1, const char *name2, uint8_t type,
|
||||
uint8_t bold, uint8_t colour, const char *msg, ...);
|
||||
|
||||
/* Prints a section of history starting at line_start */
|
||||
void line_info_print(ToxWindow *self);
|
||||
@@ -81,9 +88,12 @@ void line_info_cleanup(struct history *hst);
|
||||
void line_info_clear(struct history *hst);
|
||||
|
||||
/* puts msg in specified line_info msg buffer */
|
||||
void line_info_set(ToxWindow *self, uint32_t id, uint8_t *msg);
|
||||
void line_info_set(ToxWindow *self, uint32_t id, char *msg);
|
||||
|
||||
/* resets line_start (moves to end of chat history) */
|
||||
void line_info_reset_start(ToxWindow *self, struct history *hst);
|
||||
|
||||
void line_info_init(struct history *hst);
|
||||
bool line_info_onKey(ToxWindow *self, wint_t key); /* returns true if key is a match */
|
||||
|
||||
#endif /* #define _line_info_h */
|
||||
#endif /* #define LINE_INFO_H */
|
||||
|
||||
246
src/log.c
246
src/log.c
@@ -20,13 +20,10 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "configdir.h"
|
||||
#include "toxic.h"
|
||||
@@ -34,59 +31,93 @@
|
||||
#include "misc_tools.h"
|
||||
#include "log.h"
|
||||
#include "settings.h"
|
||||
#include "line_info.h"
|
||||
|
||||
extern struct user_settings *user_settings;
|
||||
|
||||
/* Creates/fetches log file by appending to the config dir the name and a pseudo-unique identity */
|
||||
void init_logging_session(uint8_t *name, uint8_t *key, struct chatlog *log)
|
||||
{
|
||||
if (!log->log_on)
|
||||
return;
|
||||
/* There are three types of logs: chat logs, groupchat logs, and prompt logs (see LOG_TYPE in log.h)
|
||||
A prompt log is in the format: LOGDIR/selfkey-home.log
|
||||
A chat log is in the format: LOGDIR/selfkey-friendname-otherkey.log
|
||||
A groupchat log is in the format: LOGDIR/selfkey-groupname-date[time].log
|
||||
|
||||
Only the first (KEY_IDENT_DIGITS * 2) numbers of the key are used.
|
||||
|
||||
Returns 0 on success, -1 if the path is too long */
|
||||
static int get_log_path(char *dest, int destsize, char *name, const char *selfkey, const char *otherkey, int logtype)
|
||||
{
|
||||
if (!valid_nick(name))
|
||||
name = UNKNOWN_NAME;
|
||||
|
||||
const char *namedash = logtype == LOG_PROMPT ? "" : "-";
|
||||
const char *set_path = user_settings->chatlogs_path;
|
||||
|
||||
char *user_config_dir = get_user_config_dir();
|
||||
int path_len = strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(name);
|
||||
int path_len = strlen(name) + strlen(".log") + strlen("-") + strlen(namedash);
|
||||
path_len += strlen(set_path) ? *set_path : strlen(user_config_dir) + strlen(LOGDIR);
|
||||
|
||||
/* use first 4 digits of key as log ident. If no key use a timestamp */
|
||||
uint8_t ident[32];
|
||||
/* first 6 digits of selfkey */
|
||||
char self_id[32];
|
||||
path_len += KEY_IDENT_DIGITS * 2;
|
||||
sprintf(&self_id[0], "%02X", selfkey[0] & 0xff);
|
||||
sprintf(&self_id[2], "%02X", selfkey[1] & 0xff);
|
||||
sprintf(&self_id[4], "%02X", selfkey[2] & 0xff);
|
||||
self_id[KEY_IDENT_DIGITS * 2] = '\0';
|
||||
|
||||
if (key != NULL) {
|
||||
path_len += (KEY_IDENT_DIGITS * 2 + 5);
|
||||
char other_id[32] = {0};
|
||||
|
||||
sprintf(&ident[0], "%02X", key[0] & 0xff);
|
||||
sprintf(&ident[2], "%02X", key[1] & 0xff);
|
||||
ident[KEY_IDENT_DIGITS * 2 + 1] = '\0';
|
||||
} else {
|
||||
strftime(ident, sizeof(ident), "%Y-%m-%d[%H:%M:%S]", get_time());
|
||||
path_len += strlen(ident) + 1;
|
||||
switch (logtype) {
|
||||
case LOG_CHAT:
|
||||
path_len += KEY_IDENT_DIGITS * 2;
|
||||
sprintf(&other_id[0], "%02X", otherkey[0] & 0xff);
|
||||
sprintf(&other_id[2], "%02X", otherkey[1] & 0xff);
|
||||
sprintf(&other_id[4], "%02X", otherkey[2] & 0xff);
|
||||
other_id[KEY_IDENT_DIGITS * 2] = '\0';
|
||||
break;
|
||||
|
||||
case LOG_GROUP:
|
||||
strftime(other_id, sizeof(other_id), "%Y-%m-%d[%H:%M:%S]", get_time());
|
||||
path_len += strlen(other_id);
|
||||
break;
|
||||
}
|
||||
|
||||
if (path_len > MAX_STR_SIZE) {
|
||||
log->log_on = false;
|
||||
if (path_len >= destsize) {
|
||||
free(user_config_dir);
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint8_t log_path[MAX_STR_SIZE];
|
||||
|
||||
snprintf(log_path, MAX_STR_SIZE, "%s%s%s-%s.log",
|
||||
user_config_dir, CONFIGDIR, name, ident);
|
||||
if (!string_is_empty(set_path))
|
||||
snprintf(dest, destsize, "%s%s-%s%s%s.log", set_path, self_id, name, namedash, other_id);
|
||||
else
|
||||
snprintf(dest, destsize, "%s%s%s-%s%s%s.log", user_config_dir, LOGDIR, self_id, name, namedash, other_id);
|
||||
|
||||
free(user_config_dir);
|
||||
|
||||
log->file = fopen(log_path, "a");
|
||||
|
||||
if (log->file == NULL) {
|
||||
log->log_on = false;
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(log->file, "\n*** NEW SESSION ***\n\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void write_to_log(const uint8_t *msg, uint8_t *name, struct chatlog *log, bool event)
|
||||
/* Opens log file or creates a new one */
|
||||
static int init_logging_session(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype)
|
||||
{
|
||||
if (selfkey == NULL || (logtype == LOG_CHAT && otherkey == NULL))
|
||||
return -1;
|
||||
|
||||
char log_path[MAX_STR_SIZE];
|
||||
|
||||
if (get_log_path(log_path, sizeof(log_path), name, selfkey, otherkey, logtype) == -1)
|
||||
return -1;
|
||||
|
||||
log->file = fopen(log_path, "a+");
|
||||
snprintf(log->path, sizeof(log->path), "%s", log_path);
|
||||
|
||||
if (log->file == NULL)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define LOG_FLUSH_LIMIT 1 /* limits calls to fflush to a max of one per LOG_FLUSH_LIMIT seconds */
|
||||
|
||||
void write_to_log(const char *msg, const char *name, struct chatlog *log, bool event)
|
||||
{
|
||||
if (!log->log_on)
|
||||
return;
|
||||
@@ -96,40 +127,143 @@ void write_to_log(const uint8_t *msg, uint8_t *name, struct chatlog *log, bool e
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t name_frmt[TOXIC_MAX_NAME_LENGTH + 3];
|
||||
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->time == TIME_12 ? "%Y/%m/%d [%I:%M:%S %p]" : "%Y/%m/%d [%H:%M:%S]";
|
||||
uint8_t s[MAX_STR_SIZE];
|
||||
const char *t = user_settings->log_timestamp_format;
|
||||
char s[MAX_STR_SIZE];
|
||||
strftime(s, MAX_STR_SIZE, t, get_time());
|
||||
fprintf(log->file, "%s %s %s\n", s, name_frmt, msg);
|
||||
|
||||
uint64_t curtime = get_unix_time();
|
||||
|
||||
if (timed_out(log->lastwrite, curtime, LOG_FLUSH_LIMIT)) {
|
||||
if (timed_out(log->lastwrite, LOG_FLUSH_LIMIT)) {
|
||||
fflush(log->file);
|
||||
log->lastwrite = curtime;
|
||||
log->lastwrite = get_unix_time();
|
||||
}
|
||||
}
|
||||
|
||||
void log_enable(uint8_t *name, uint8_t *key, struct chatlog *log)
|
||||
{
|
||||
log->log_on = true;
|
||||
|
||||
if (log->file == NULL)
|
||||
init_logging_session(name, key, log);
|
||||
}
|
||||
|
||||
void log_disable(struct chatlog *log)
|
||||
{
|
||||
log->log_on = false;
|
||||
|
||||
if (log->file != NULL) {
|
||||
if (log->file != NULL)
|
||||
fclose(log->file);
|
||||
log->file = NULL;
|
||||
}
|
||||
|
||||
memset(log, 0, sizeof(struct chatlog));
|
||||
}
|
||||
|
||||
int log_enable(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype)
|
||||
{
|
||||
log->log_on = true;
|
||||
|
||||
if (log->file != NULL)
|
||||
return 0;
|
||||
|
||||
if (init_logging_session(name, selfkey, otherkey, log, logtype) == -1) {
|
||||
log_disable(log);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Loads previous history from chat log */
|
||||
void load_chat_history(ToxWindow *self, struct chatlog *log)
|
||||
{
|
||||
if (log->file == NULL)
|
||||
return;
|
||||
|
||||
off_t sz = file_size(log->path);
|
||||
|
||||
if (sz <= 0)
|
||||
return;
|
||||
|
||||
char *hstbuf = malloc(sz + 1);
|
||||
|
||||
if (hstbuf == NULL)
|
||||
exit_toxic_err("failed in load_chat_history", FATALERR_MEMORY);
|
||||
|
||||
if (fseek(log->file, 0L, SEEK_SET) == -1) {
|
||||
free(hstbuf);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to read log file");
|
||||
return;
|
||||
}
|
||||
|
||||
if (fread(hstbuf, sz, 1, log->file) != 1) {
|
||||
free(hstbuf);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to read log file");
|
||||
return;
|
||||
}
|
||||
|
||||
hstbuf[sz] = '\0';
|
||||
|
||||
/* Number of history lines to load: must not be larger than MAX_LINE_INFO_QUEUE - 2 */
|
||||
int L = MIN(MAX_LINE_INFO_QUEUE - 2, user_settings->history_size);
|
||||
int start, count = 0;
|
||||
|
||||
/* start at end and backtrace L lines or to the beginning of buffer */
|
||||
for (start = sz - 1; start >= 0 && count < L; --start) {
|
||||
if (hstbuf[start] == '\n')
|
||||
++count;
|
||||
}
|
||||
|
||||
const char *line = strtok(&hstbuf[start + 1], "\n");
|
||||
|
||||
if (line == NULL) {
|
||||
free(hstbuf);
|
||||
return;
|
||||
}
|
||||
|
||||
while (line != NULL && count--) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", line);
|
||||
line = strtok(NULL, "\n");
|
||||
}
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
|
||||
free(hstbuf);
|
||||
}
|
||||
|
||||
/* renames chatlog file replacing src with dest.
|
||||
Returns 0 on success or if no log exists, -1 on failure. */
|
||||
int rename_logfile(char *src, char *dest, const char *selfkey, const char *otherkey, int winnum)
|
||||
{
|
||||
ToxWindow *toxwin = get_window_ptr(winnum);
|
||||
struct chatlog *log = NULL;
|
||||
bool log_on = false;
|
||||
|
||||
/* disable log if necessary and save its state */
|
||||
if (toxwin != NULL) {
|
||||
log = toxwin->chatwin->log;
|
||||
log_on = log->log_on;
|
||||
}
|
||||
|
||||
if (log_on)
|
||||
log_disable(log);
|
||||
|
||||
char newpath[MAX_STR_SIZE];
|
||||
char oldpath[MAX_STR_SIZE];
|
||||
|
||||
if (get_log_path(oldpath, sizeof(oldpath), src, selfkey, otherkey, LOG_CHAT) == -1)
|
||||
goto on_error;
|
||||
|
||||
if (!file_exists(oldpath))
|
||||
return 0;
|
||||
|
||||
if (get_log_path(newpath, sizeof(newpath), dest, selfkey, otherkey, LOG_CHAT) == -1)
|
||||
goto on_error;
|
||||
|
||||
if (rename(oldpath, newpath) != 0)
|
||||
goto on_error;
|
||||
|
||||
if (log_on)
|
||||
log_enable(dest, selfkey, otherkey, log, LOG_CHAT);
|
||||
|
||||
return 0;
|
||||
|
||||
on_error:
|
||||
if (log_on)
|
||||
log_enable(src, selfkey, otherkey, log, LOG_CHAT);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
34
src/log.h
34
src/log.h
@@ -20,28 +20,40 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _log_h
|
||||
#define _log_h
|
||||
|
||||
#define LOG_FLUSH_LIMIT 2 /* limits calls to fflush(logfile) to a max of one per LOG_FLUSH_LIMIT seconds */
|
||||
#ifndef LOG_H
|
||||
#define LOG_H
|
||||
|
||||
struct chatlog {
|
||||
FILE *file;
|
||||
uint64_t lastwrite;
|
||||
int pos;
|
||||
char path[MAX_STR_SIZE];
|
||||
bool log_on; /* specific to current chat window */
|
||||
};
|
||||
|
||||
/* Creates/fetches log file by appending to the config dir the name and a pseudo-unique identity */
|
||||
void init_logging_session(uint8_t *name, uint8_t *key, struct chatlog *log);
|
||||
enum {
|
||||
LOG_GROUP,
|
||||
LOG_PROMPT,
|
||||
LOG_CHAT,
|
||||
} LOG_TYPE;
|
||||
|
||||
/* formats/writes line to log file */
|
||||
void write_to_log(const uint8_t *msg, uint8_t *name, struct chatlog *log, bool event);
|
||||
void write_to_log(const char *msg, const char *name, struct chatlog *log, bool event);
|
||||
|
||||
/* enables logging for specified log and creates/fetches file if necessary */
|
||||
void log_enable(uint8_t *name, uint8_t *key, struct chatlog *log);
|
||||
/* enables logging for specified log and creates/fetches file if necessary.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
int log_enable(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype);
|
||||
|
||||
/* disables logging for specified log and closes file */
|
||||
void log_disable(struct chatlog *log);
|
||||
|
||||
#endif /* #define _log_h */
|
||||
/* Loads previous history from chat log */
|
||||
void load_chat_history(ToxWindow *self, struct chatlog *log);
|
||||
|
||||
/* renames chatlog file replacing src with dest.
|
||||
Returns 0 on success or if no log exists, -1 on failure. */
|
||||
int rename_logfile(char *src, char *dest, const char *selfkey, const char *otherkey, int winnum);
|
||||
|
||||
#endif /* #define LOG_H */
|
||||
|
||||
154
src/message_queue.c
Normal file
154
src/message_queue.c
Normal file
@@ -0,0 +1,154 @@
|
||||
/* message_queue.c
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "message_queue.h"
|
||||
#include "misc_tools.h"
|
||||
#include "line_info.h"
|
||||
#include "log.h"
|
||||
|
||||
void cqueue_cleanup(struct chat_queue *q)
|
||||
{
|
||||
struct cqueue_msg *tmp1 = q->root;
|
||||
|
||||
while (tmp1) {
|
||||
struct cqueue_msg *tmp2 = tmp1->next;
|
||||
free(tmp1);
|
||||
tmp1 = tmp2;
|
||||
}
|
||||
|
||||
free(q);
|
||||
}
|
||||
|
||||
void cqueue_add(struct chat_queue *q, const char *msg, size_t len, uint8_t type, uint32_t line_id)
|
||||
{
|
||||
struct cqueue_msg *new_m = malloc(sizeof(struct cqueue_msg));
|
||||
|
||||
if (new_m == NULL)
|
||||
exit_toxic_err("failed in cqueue_message", FATALERR_MEMORY);
|
||||
|
||||
snprintf(new_m->message, sizeof(new_m->message), "%s", msg);
|
||||
new_m->len = len;
|
||||
new_m->type = type;
|
||||
new_m->line_id = line_id;
|
||||
new_m->last_send_try = 0;
|
||||
new_m->receipt = 0;
|
||||
new_m->next = NULL;
|
||||
|
||||
if (q->root == NULL) {
|
||||
new_m->prev = NULL;
|
||||
q->root = new_m;
|
||||
} else {
|
||||
new_m->prev = q->end;
|
||||
q->end->next = new_m;
|
||||
}
|
||||
|
||||
q->end = new_m;
|
||||
}
|
||||
|
||||
/* update line to show receipt was received after queue removal */
|
||||
static void cqueue_mark_read(ToxWindow *self, struct cqueue_msg *msg)
|
||||
{
|
||||
struct line_info *line = self->chatwin->hst->line_end;
|
||||
|
||||
while (line) {
|
||||
if (line->id != msg->line_id) {
|
||||
line = line->prev;
|
||||
continue;
|
||||
}
|
||||
|
||||
line->type = msg->type == OUT_ACTION ? OUT_ACTION_READ : OUT_MSG_READ;
|
||||
|
||||
if (line->noread_flag == true) {
|
||||
line->len -= 2;
|
||||
line->noread_flag = false;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* removes message with matching receipt from queue, writes to log and updates line to show the message was received. */
|
||||
void cqueue_remove(ToxWindow *self, Tox *m, uint32_t receipt)
|
||||
{
|
||||
struct chat_queue *q = self->chatwin->cqueue;
|
||||
struct cqueue_msg *msg = q->root;
|
||||
|
||||
while (msg) {
|
||||
if (msg->receipt != receipt) {
|
||||
msg = msg->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
char selfname[TOX_MAX_NAME_LENGTH];
|
||||
tox_self_get_name(m, (uint8_t *) selfname);
|
||||
|
||||
size_t len = tox_self_get_name_size(m);
|
||||
selfname[len] = '\0';
|
||||
|
||||
write_to_log(msg->message, selfname, self->chatwin->log, msg->type == OUT_ACTION);
|
||||
cqueue_mark_read(self, msg);
|
||||
|
||||
struct cqueue_msg *next = msg->next;
|
||||
|
||||
if (msg->prev == NULL) { /* root */
|
||||
if (next)
|
||||
next->prev = NULL;
|
||||
|
||||
free(msg);
|
||||
q->root = next;
|
||||
} else {
|
||||
struct cqueue_msg *prev = msg->prev;
|
||||
free(msg);
|
||||
prev->next = next;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#define CQUEUE_TRY_SEND_INTERVAL 60
|
||||
|
||||
/* Tries to send the oldest unsent message in queue. */
|
||||
void cqueue_try_send(ToxWindow *self, Tox *m)
|
||||
{
|
||||
struct chat_queue *q = self->chatwin->cqueue;
|
||||
struct cqueue_msg *msg = q->root;
|
||||
|
||||
if (!msg)
|
||||
return;
|
||||
|
||||
if (msg->receipt != 0 && !timed_out(msg->last_send_try, CQUEUE_TRY_SEND_INTERVAL))
|
||||
return;
|
||||
|
||||
uint32_t receipt = 0;
|
||||
|
||||
TOX_MESSAGE_TYPE type = msg->type == OUT_MSG ? TOX_MESSAGE_TYPE_NORMAL : TOX_MESSAGE_TYPE_ACTION;
|
||||
receipt = tox_friend_send_message(m, self->num, type, (uint8_t *) msg->message, msg->len, NULL);
|
||||
|
||||
msg->last_send_try = get_unix_time();
|
||||
msg->receipt = receipt;
|
||||
return;
|
||||
}
|
||||
51
src/message_queue.h
Normal file
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 */
|
||||
427
src/misc_tools.c
427
src/misc_tools.c
@@ -20,25 +20,41 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <limits.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "misc_tools.h"
|
||||
#include "settings.h"
|
||||
#include "file_transfers.h"
|
||||
|
||||
extern ToxWindow *prompt;
|
||||
extern struct user_settings *user_settings;
|
||||
|
||||
static uint64_t current_unix_time;
|
||||
|
||||
void hst_to_net(uint8_t *num, uint16_t numbytes)
|
||||
{
|
||||
#ifndef WORDS_BIGENDIAN
|
||||
uint32_t i;
|
||||
uint8_t buff[numbytes];
|
||||
|
||||
for (i = 0; i < numbytes; ++i) {
|
||||
buff[i] = num[numbytes - i - 1];
|
||||
}
|
||||
|
||||
memcpy(num, buff, numbytes);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
/* Note: The time functions are not thread safe */
|
||||
void update_unix_time(void)
|
||||
{
|
||||
current_unix_time = (uint64_t) time(NULL);
|
||||
@@ -49,163 +65,140 @@ 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 timeout)
|
||||
{
|
||||
return timestamp + timeout <= get_unix_time();
|
||||
}
|
||||
|
||||
/* Get the current local time */
|
||||
struct tm *get_time(void)
|
||||
{
|
||||
struct tm *timeinfo;
|
||||
uint64_t t = get_unix_time();
|
||||
timeinfo = localtime(&t);
|
||||
timeinfo = localtime((const time_t*) &t);
|
||||
return timeinfo;
|
||||
}
|
||||
|
||||
void get_time_str(uint8_t *buf)
|
||||
/*Puts the current time in buf in the format of [HH:mm:ss] */
|
||||
void get_time_str(char *buf, int bufsize)
|
||||
{
|
||||
const char *t = user_settings->time == TIME_12 ? "[%-I:%M:%S] " : "[%H:%M:%S] ";
|
||||
strftime(buf, TIME_STR_SIZE, t, get_time());
|
||||
}
|
||||
|
||||
char *hex_string_to_bin(const char *hex_string)
|
||||
{
|
||||
size_t len = strlen(hex_string);
|
||||
char *val = malloc(len);
|
||||
|
||||
if (val == NULL) {
|
||||
endwin();
|
||||
fprintf(stderr, "malloc() failed. Aborting...\n");
|
||||
exit(EXIT_FAILURE);
|
||||
if (user_settings->timestamps == TIMESTAMPS_OFF) {
|
||||
buf[0] = '\0';
|
||||
return;
|
||||
}
|
||||
|
||||
size_t i;
|
||||
const char *t = user_settings->timestamp_format;
|
||||
strftime(buf, bufsize, t, get_time());
|
||||
}
|
||||
|
||||
for (i = 0; i < len; ++i, hex_string += 2)
|
||||
sscanf(hex_string, "%2hhx", &val[i]);
|
||||
/* 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;
|
||||
|
||||
return val;
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Converts a hexidecimal string of length hex_len to binary format and puts the result in output.
|
||||
* output_size must be exactly half of hex_len.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
int hex_string_to_bin(const char *hex_string, size_t hex_len, char *output, size_t output_size)
|
||||
{
|
||||
if (output_size == 0 || hex_len != output_size * 2)
|
||||
return -1;
|
||||
|
||||
for (size_t i = 0; i < output_size; ++i) {
|
||||
sscanf(hex_string, "%2hhx", &output[i]);
|
||||
hex_string += 2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (!string)
|
||||
return true;
|
||||
|
||||
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;
|
||||
|
||||
if (n < len)
|
||||
return -1;
|
||||
|
||||
if ((len = mbstowcs(buf, string, n)) == (size_t) - 1)
|
||||
if ((len = mbstowcs(buf, string, n)) == (size_t) -1)
|
||||
return -1;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
if (n < len)
|
||||
return -1;
|
||||
|
||||
if ((len = wcstombs(buf, string, n)) == (size_t) - 1)
|
||||
if ((len = wcstombs(buf, string, n)) == (size_t) -1)
|
||||
return -1;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/* convert wide characters to multibyte string: string returned must be freed */
|
||||
uint8_t *wcs_to_mbs(wchar_t *string)
|
||||
{
|
||||
uint8_t *ret = NULL;
|
||||
size_t len = wcstombs(NULL, string, 0);
|
||||
|
||||
if (len != (size_t) - 1) {
|
||||
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 1 if connection has timed out, 0 otherwise */
|
||||
int 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 && user_settings->alerts == ALERTS_ENABLED)
|
||||
beep();
|
||||
}
|
||||
|
||||
/* case-insensitive string compare function for use with qsort */
|
||||
int qsort_strcasecmp_hlpr(const void *nick1, const void *nick2)
|
||||
int qsort_strcasecmp_hlpr(const void *str1, const void *str2)
|
||||
{
|
||||
return strcasecmp((const char *) nick1, (const char *) nick2);
|
||||
return strcasecmp((const char *) str1, (const char *) str2);
|
||||
}
|
||||
|
||||
/* Returns 1 if nick is valid, 0 if not. A valid toxic nick:
|
||||
- cannot be empty
|
||||
- cannot start with a space
|
||||
- must not contain a forward slash (for logfile naming purposes)
|
||||
- must not contain contiguous spaces */
|
||||
int valid_nick(uint8_t *nick)
|
||||
- must not contain contiguous spaces
|
||||
- must not contain a newline or tab seqeunce */
|
||||
int valid_nick(const char *nick)
|
||||
{
|
||||
if (!nick[0] || nick[0] == ' ')
|
||||
return 0;
|
||||
@@ -213,49 +206,219 @@ int valid_nick(uint8_t *nick)
|
||||
int i;
|
||||
|
||||
for (i = 0; nick[i]; ++i) {
|
||||
if (nick[i] == ' ' && nick[i + 1] == ' ')
|
||||
return 0;
|
||||
if ((nick[i] == ' ' && nick[i + 1] == ' ')
|
||||
|| nick[i] == '/'
|
||||
|| nick[i] == '\n'
|
||||
|| nick[i] == '\t'
|
||||
|| nick[i] == '\v'
|
||||
|| nick[i] == '\r')
|
||||
|
||||
if (nick[i] == '/')
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 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] == '\0')
|
||||
str[i] = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
/* gets base file name from path or original file name if no path is supplied */
|
||||
void get_file_name(uint8_t *namebuf, uint8_t *pathname)
|
||||
/* gets base file name from path or original file name if no path is supplied.
|
||||
* Returns the file name length
|
||||
*/
|
||||
size_t get_file_name(char *namebuf, size_t bufsize, const char *pathname)
|
||||
{
|
||||
int idx = strlen(pathname) - 1;
|
||||
int len = strlen(pathname) - 1;
|
||||
char *path = strdup(pathname);
|
||||
|
||||
while (idx >= 0 && pathname[idx] == '/')
|
||||
pathname[idx--] = '\0';
|
||||
if (path == NULL)
|
||||
exit_toxic_err("failed in get_file_name", FATALERR_MEMORY);
|
||||
|
||||
uint8_t *filename = strrchr(pathname, '/');
|
||||
while (len >= 0 && pathname[len] == '/')
|
||||
path[len--] = '\0';
|
||||
|
||||
if (filename != NULL) {
|
||||
if (!strlen(++filename))
|
||||
filename = pathname;
|
||||
} else {
|
||||
filename = pathname;
|
||||
char *finalname = strdup(path);
|
||||
|
||||
if (finalname == NULL)
|
||||
exit_toxic_err("failed in get_file_name", FATALERR_MEMORY);
|
||||
|
||||
const char *basenm = strrchr(path, '/');
|
||||
|
||||
if (basenm != NULL) {
|
||||
if (basenm[1])
|
||||
strcpy(finalname, &basenm[1]);
|
||||
}
|
||||
|
||||
snprintf(namebuf, MAX_STR_SIZE, "%s", filename);
|
||||
snprintf(namebuf, bufsize, "%s", finalname);
|
||||
free(finalname);
|
||||
free(path);
|
||||
|
||||
return strlen(namebuf);
|
||||
}
|
||||
|
||||
/* converts str to all lowercase */
|
||||
void str_to_lower(uint8_t *str)
|
||||
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. If file doesn't exist returns 0. */
|
||||
off_t file_size(const char *path)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
if (stat(path, &st) == -1)
|
||||
return 0;
|
||||
|
||||
return st.st_size;
|
||||
}
|
||||
|
||||
/* compares the first size bytes of fp to signature.
|
||||
Returns 0 if they are the same, 1 if they differ, and -1 on error.
|
||||
|
||||
On success this function will seek back to the beginning of fp */
|
||||
int check_file_signature(const char *signature, size_t size, FILE *fp)
|
||||
{
|
||||
char buf[size];
|
||||
|
||||
if (fread(buf, size, 1, fp) != 1)
|
||||
return -1;
|
||||
|
||||
int ret = memcmp(signature, buf, size);
|
||||
|
||||
if (fseek(fp, 0L, SEEK_SET) == -1)
|
||||
return -1;
|
||||
|
||||
return ret == 0 ? 0 : 1;
|
||||
}
|
||||
|
||||
/* sets window title in tab bar. */
|
||||
void set_window_title(ToxWindow *self, const char *title, int len)
|
||||
{
|
||||
char cpy[TOXIC_MAX_NAME_LENGTH + 1];
|
||||
|
||||
if (self->is_groupchat) /* keep groupnumber in title */
|
||||
snprintf(cpy, sizeof(cpy), "%d %s", self->num, title);
|
||||
else
|
||||
snprintf(cpy, sizeof(cpy), "%s", title);
|
||||
|
||||
if (len > MAX_WINDOW_NAME_LENGTH) {
|
||||
strcpy(&cpy[MAX_WINDOW_NAME_LENGTH - 3], "...");
|
||||
cpy[MAX_WINDOW_NAME_LENGTH] = '\0';
|
||||
}
|
||||
|
||||
snprintf(self->name, sizeof(self->name), "%s", cpy);
|
||||
}
|
||||
|
||||
110
src/misc_tools.h
110
src/misc_tools.h
@@ -19,8 +19,10 @@
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#ifndef _misc_tools_h
|
||||
#define _misc_tools_h
|
||||
#ifndef MISC_TOOLS_H
|
||||
#define MISC_TOOLS_H
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "windows.h"
|
||||
#include "toxic.h"
|
||||
@@ -33,60 +35,112 @@
|
||||
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
|
||||
#endif
|
||||
|
||||
/* convert a hex string to binary */
|
||||
char *hex_string_to_bin(const char *hex_string);
|
||||
#ifndef net_to_host
|
||||
#define net_to_host(x, y) hst_to_net(x, y)
|
||||
#endif
|
||||
|
||||
/* get the current unix time */
|
||||
void hst_to_net(uint8_t *num, uint16_t numbytes);
|
||||
|
||||
/*
|
||||
* Converts a hexidecimal string of length hex_len to binary format and puts the result in output.
|
||||
* output_size must be exactly half of hex_len.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
int hex_string_to_bin(const char *hex_string, size_t hex_len, char *output, size_t output_size);
|
||||
|
||||
/* convert a hex string to bytes. returns 0 on success, -1 on failure */
|
||||
int hex_string_to_bytes(char *buf, int size, const char *keystr);
|
||||
|
||||
/* get the current unix time (not thread safe) */
|
||||
uint64_t get_unix_time(void);
|
||||
|
||||
/*Puts the current time in buf in the format of [Hour:Min:Sec] */
|
||||
void get_time_str(uint8_t *buf);
|
||||
/* Puts the current time in buf in the format of [HH:mm:ss] (not thread safe) */
|
||||
void get_time_str(char *buf, int bufsize);
|
||||
|
||||
/* get the current local 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);
|
||||
|
||||
/* get the current local time (not thread safe) */
|
||||
struct tm *get_time(void);
|
||||
|
||||
/* updates current unix time (should be run once per do_toxic loop) */
|
||||
void update_unix_time(void);
|
||||
|
||||
/* Returns 1 if the string is empty, 0 otherwise */
|
||||
int string_is_empty(char *string);
|
||||
int string_is_empty(const char *string);
|
||||
|
||||
/* convert a multibyte string to a wide character string (must provide buffer) */
|
||||
int char_to_wcs_buf(wchar_t *buf, const 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 wide char to multibyte char */
|
||||
char *wc_to_char(wchar_t ch);
|
||||
/* 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);
|
||||
|
||||
/* Returns 1 if connection has timed out, 0 otherwise */
|
||||
int timed_out(uint64_t timestamp, uint64_t timeout, uint64_t curtime);
|
||||
int timed_out(uint64_t timestamp, uint64_t timeout);
|
||||
|
||||
/* Colours the window tab according to type. Beeps if is_beep is true */
|
||||
void alert_window(ToxWindow *self, int type, bool is_beep);
|
||||
|
||||
/* case-insensitive string compare function for use with qsort */
|
||||
int qsort_strcasecmp_hlpr(const void *nick1, const void *nick2);
|
||||
int qsort_strcasecmp_hlpr(const void *str1, const void *str2);
|
||||
|
||||
/* Returns 1 if nick is valid, 0 if not. A valid toxic nick:
|
||||
- cannot be empty
|
||||
- cannot start with a space
|
||||
- must not contain a forward slash (for logfile naming purposes)
|
||||
- must not contain contiguous spaces */
|
||||
int valid_nick(uint8_t *nick);
|
||||
- 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 *namebuf, uint8_t *pathname);
|
||||
size_t get_file_name(char *namebuf, size_t bufsize, const char *pathname);
|
||||
|
||||
/* converts str to all uppercase */
|
||||
void str_to_lower(uint8_t *str);
|
||||
/* converts str to all lowercase */
|
||||
void str_to_lower(char *str);
|
||||
|
||||
#endif /* #define _misc_tools_h */
|
||||
/* 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. If file doesn't exist returns 0. */
|
||||
off_t file_size(const char *path);
|
||||
|
||||
/* compares the first size bytes of fp and signature.
|
||||
Returns 0 if they are the same, 1 if they differ, and -1 on error.
|
||||
|
||||
On success this function will seek back to the beginning of fp */
|
||||
int check_file_signature(const char *signature, size_t size, FILE *fp);
|
||||
|
||||
/* sets window title in tab bar. */
|
||||
void set_window_title(ToxWindow *self, const char *title, int len);
|
||||
|
||||
#endif /* #define MISC_TOOLS_H */
|
||||
|
||||
805
src/notify.c
Normal file
805
src/notify.c
Normal file
@@ -0,0 +1,805 @@
|
||||
/* notify.c
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include <stdarg.h>
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "notify.h"
|
||||
#include "device.h"
|
||||
#include "settings.h"
|
||||
#include "line_info.h"
|
||||
#include "misc_tools.h"
|
||||
#include "xtra.h"
|
||||
|
||||
#if defined(AUDIO) || defined(SOUND_NOTIFY)
|
||||
#ifdef __APPLE__
|
||||
#include <OpenAL/al.h>
|
||||
#include <OpenAL/alc.h>
|
||||
#else
|
||||
#include <AL/al.h>
|
||||
#include <AL/alc.h>
|
||||
/* compatibility with older versions of OpenAL */
|
||||
#ifndef ALC_ALL_DEVICES_SPECIFIER
|
||||
#include <AL/alext.h>
|
||||
#endif
|
||||
#endif
|
||||
#ifdef SOUND_NOTIFY
|
||||
#include <AL/alut.h> /* freealut packet */
|
||||
#endif
|
||||
#endif /* AUDIO */
|
||||
|
||||
#ifdef BOX_NOTIFY
|
||||
#include <libnotify/notify.h>
|
||||
#endif
|
||||
|
||||
#define MAX_BOX_MSG_LEN 127
|
||||
#define SOUNDS_SIZE 10
|
||||
#define ACTIVE_NOTIFS_MAX 50
|
||||
|
||||
extern struct user_settings *user_settings;
|
||||
|
||||
struct Control {
|
||||
time_t cooldown;
|
||||
time_t notif_timeout;
|
||||
|
||||
#if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY)
|
||||
pthread_mutex_t poll_mutex[1];
|
||||
bool poll_active;
|
||||
#endif
|
||||
|
||||
#ifdef SOUND_NOTIFY
|
||||
uint32_t device_idx; /* index of output device */
|
||||
char* sounds[SOUNDS_SIZE];
|
||||
#endif /* SOUND_NOTIFY */
|
||||
} Control = {0};
|
||||
|
||||
struct _ActiveNotifications {
|
||||
#ifdef SOUND_NOTIFY
|
||||
uint32_t source;
|
||||
uint32_t buffer;
|
||||
bool looping;
|
||||
#endif
|
||||
bool active;
|
||||
int *id_indicator;
|
||||
#ifdef BOX_NOTIFY
|
||||
NotifyNotification* box;
|
||||
char messages[MAX_BOX_MSG_LEN + 1][MAX_BOX_MSG_LEN + 1];
|
||||
char title[64];
|
||||
size_t size;
|
||||
time_t n_timeout;
|
||||
#endif
|
||||
} actives[ACTIVE_NOTIFS_MAX];
|
||||
/**********************************************************************************/
|
||||
/**********************************************************************************/
|
||||
/**********************************************************************************/
|
||||
/**********************************************************************************/
|
||||
/**********************************************************************************/
|
||||
|
||||
/* coloured tab notifications: primary notification type */
|
||||
static void tab_notify(ToxWindow *self, uint64_t flags)
|
||||
{
|
||||
if (self == NULL)
|
||||
return;
|
||||
|
||||
if (flags & NT_WNDALERT_0)
|
||||
self->alert = WINDOW_ALERT_0;
|
||||
else if ( (flags & NT_WNDALERT_1) && (!self->alert || self->alert > WINDOW_ALERT_0) )
|
||||
self->alert = WINDOW_ALERT_1;
|
||||
else if ( (flags & NT_WNDALERT_2) && (!self->alert || self->alert > WINDOW_ALERT_1) )
|
||||
self->alert = WINDOW_ALERT_2;
|
||||
}
|
||||
|
||||
static bool notifications_are_disabled(uint64_t flags)
|
||||
{
|
||||
if (user_settings->alerts != ALERTS_ENABLED)
|
||||
return true;
|
||||
|
||||
bool res = (flags & NT_RESTOL) && (Control.cooldown > get_unix_time());
|
||||
#ifdef X11
|
||||
return res || ((flags & NT_NOFOCUS) && is_focused());
|
||||
#else
|
||||
return res;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void control_lock()
|
||||
{
|
||||
#if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY)
|
||||
pthread_mutex_lock(Control.poll_mutex);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void control_unlock()
|
||||
{
|
||||
#if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY)
|
||||
pthread_mutex_unlock(Control.poll_mutex);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef SOUND_NOTIFY
|
||||
bool is_playing(int source)
|
||||
{
|
||||
int ready;
|
||||
alGetSourcei(source, AL_SOURCE_STATE, &ready);
|
||||
return ready == AL_PLAYING;
|
||||
}
|
||||
|
||||
/* TODO maybe find better way to do this */
|
||||
/* cooldown is in seconds */
|
||||
#define DEVICE_COOLDOWN 5 /* TODO perhaps load this from config? */
|
||||
static bool device_opened = false;
|
||||
time_t last_opened_update = 0;
|
||||
|
||||
/* Opens primary device. Returns true on succe*/
|
||||
void m_open_device()
|
||||
{
|
||||
last_opened_update = get_unix_time();
|
||||
|
||||
if (device_opened) return;
|
||||
|
||||
/* Blah error check */
|
||||
open_primary_device(output, &Control.device_idx, 48000, 20, 1);
|
||||
|
||||
device_opened = true;
|
||||
}
|
||||
|
||||
void m_close_device()
|
||||
{
|
||||
if (!device_opened) return;
|
||||
|
||||
close_device(output, Control.device_idx);
|
||||
|
||||
device_opened = false;
|
||||
}
|
||||
|
||||
/* Terminate all sounds but wait for them to finish first */
|
||||
void graceful_clear()
|
||||
{
|
||||
control_lock();
|
||||
|
||||
while (1) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
|
||||
if (actives[i].active) {
|
||||
#ifdef BOX_NOTIFY
|
||||
if (actives[i].box) {
|
||||
GError* ignore;
|
||||
notify_notification_close(actives[i].box, &ignore);
|
||||
actives[i].box = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(actives[i].id_indicator)
|
||||
*actives[i].id_indicator = -1; /* reset indicator value */
|
||||
|
||||
if ( actives[i].looping ) {
|
||||
stop_sound(i);
|
||||
} else {
|
||||
if (!is_playing(actives[i].source))
|
||||
memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
|
||||
else break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (i == ACTIVE_NOTIFS_MAX) {
|
||||
m_close_device(); /* In case it's opened */
|
||||
control_unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
usleep(1000);
|
||||
}
|
||||
|
||||
control_unlock();
|
||||
}
|
||||
|
||||
void* do_playing(void* _p)
|
||||
{
|
||||
(void)_p;
|
||||
|
||||
while(true) {
|
||||
control_lock();
|
||||
|
||||
if (!Control.poll_active) {
|
||||
control_unlock();
|
||||
break;
|
||||
}
|
||||
|
||||
bool has_looping = false;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
|
||||
|
||||
if (actives[i].looping) has_looping = true;
|
||||
|
||||
if (actives[i].active && !actives[i].looping
|
||||
#ifdef BOX_NOTIFY
|
||||
&& !actives[i].box
|
||||
#endif
|
||||
) {
|
||||
if(actives[i].id_indicator)
|
||||
*actives[i].id_indicator = -1; /* reset indicator value */
|
||||
|
||||
if (!is_playing(actives[i].source)) {
|
||||
/* Close */
|
||||
alSourceStop(actives[i].source);
|
||||
alDeleteSources(1, &actives[i].source);
|
||||
alDeleteBuffers(1, &actives[i].buffer);
|
||||
memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
|
||||
}
|
||||
}
|
||||
#ifdef BOX_NOTIFY
|
||||
else if (actives[i].box && time(NULL) >= actives[i].n_timeout)
|
||||
{
|
||||
GError* ignore;
|
||||
notify_notification_close(actives[i].box, &ignore);
|
||||
actives[i].box = NULL;
|
||||
if(actives[i].id_indicator)
|
||||
*actives[i].id_indicator = -1; /* reset indicator value */
|
||||
|
||||
if (!actives[i].looping && !is_playing(actives[i].source)) {
|
||||
/* stop source if not looping or playing, just terminate box */
|
||||
alSourceStop(actives[i].source);
|
||||
alDeleteSources(1, &actives[i].source);
|
||||
alDeleteBuffers(1, &actives[i].buffer);
|
||||
memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* device is opened and no activity in under DEVICE_COOLDOWN time, close device*/
|
||||
if (device_opened && !has_looping &&
|
||||
(time(NULL) - last_opened_update) > DEVICE_COOLDOWN) {
|
||||
m_close_device();
|
||||
}
|
||||
has_looping = false;
|
||||
|
||||
control_unlock();
|
||||
usleep(10000);
|
||||
}
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
int play_source(uint32_t source, uint32_t buffer, bool looping)
|
||||
{
|
||||
int i = 0;
|
||||
for (; i < ACTIVE_NOTIFS_MAX && actives[i].active; i ++);
|
||||
if ( i == ACTIVE_NOTIFS_MAX ) {
|
||||
return -1; /* Full */
|
||||
}
|
||||
|
||||
alSourcePlay(source);
|
||||
|
||||
actives[i].active = 1;
|
||||
actives[i].source = source;
|
||||
actives[i].buffer = buffer;
|
||||
actives[i].looping = looping;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
#elif BOX_NOTIFY
|
||||
void* do_playing(void* _p)
|
||||
{
|
||||
(void)_p;
|
||||
|
||||
while(true) {
|
||||
control_lock();
|
||||
|
||||
if (!Control.poll_active) {
|
||||
control_unlock();
|
||||
break;
|
||||
}
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
|
||||
if (actives[i].box && time(NULL) >= actives[i].n_timeout)
|
||||
{
|
||||
GError* ignore;
|
||||
notify_notification_close(actives[i].box, &ignore);
|
||||
actives[i].box = NULL;
|
||||
if(actives[i].id_indicator)
|
||||
*actives[i].id_indicator = -1; /* reset indicator value */
|
||||
|
||||
memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
|
||||
}
|
||||
}
|
||||
control_unlock();
|
||||
usleep(10000);
|
||||
}
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
void graceful_clear()
|
||||
{
|
||||
int i;
|
||||
control_lock();
|
||||
|
||||
for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
|
||||
if (actives[i].box) {
|
||||
GError* ignore;
|
||||
notify_notification_close(actives[i].box, &ignore);
|
||||
actives[i].box = NULL;
|
||||
}
|
||||
|
||||
if (actives[i].id_indicator)
|
||||
*actives[i].id_indicator = -1; /* reset indicator value */
|
||||
|
||||
memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
|
||||
}
|
||||
|
||||
control_unlock();
|
||||
}
|
||||
#endif
|
||||
|
||||
/**********************************************************************************/
|
||||
/**********************************************************************************/
|
||||
/**********************************************************************************/
|
||||
/**********************************************************************************/
|
||||
/**********************************************************************************/
|
||||
|
||||
|
||||
|
||||
/* Opens primary device */
|
||||
int init_notify(int login_cooldown, int notification_timeout)
|
||||
{
|
||||
#ifdef SOUND_NOTIFY
|
||||
alutInitWithoutContext(NULL, NULL);
|
||||
#endif /* SOUND_NOTIFY */
|
||||
|
||||
#if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY)
|
||||
if (pthread_mutex_init(Control.poll_mutex, NULL) != 0)
|
||||
return -1;
|
||||
|
||||
Control.poll_active = 1;
|
||||
pthread_t thread;
|
||||
|
||||
if (pthread_create(&thread, NULL, do_playing, NULL) != 0 || pthread_detach(thread) != 0 ) {
|
||||
pthread_mutex_destroy(Control.poll_mutex);
|
||||
Control.poll_active = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif
|
||||
Control.cooldown = time(NULL) + login_cooldown;
|
||||
|
||||
|
||||
#ifdef BOX_NOTIFY
|
||||
notify_init("Toxic");
|
||||
#endif
|
||||
Control.notif_timeout = notification_timeout;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void terminate_notify()
|
||||
{
|
||||
#if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY)
|
||||
control_lock();
|
||||
|
||||
if ( !Control.poll_active ) {
|
||||
control_unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
Control.poll_active = 0;
|
||||
control_unlock();
|
||||
|
||||
graceful_clear();
|
||||
#endif
|
||||
|
||||
#ifdef SOUND_NOTIFY
|
||||
int i = 0;
|
||||
for (; i < SOUNDS_SIZE; i ++) free(Control.sounds[i]);
|
||||
alutExit();
|
||||
#endif /* SOUND_NOTIFY */
|
||||
|
||||
#ifdef BOX_NOTIFY
|
||||
notify_uninit();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef SOUND_NOTIFY
|
||||
int set_sound(Notification sound, const char* value)
|
||||
{
|
||||
if (sound == silent) return 0;
|
||||
|
||||
free(Control.sounds[sound]);
|
||||
|
||||
size_t len = strlen(value) + 1;
|
||||
Control.sounds[sound] = calloc(len, 1);
|
||||
memcpy(Control.sounds[sound], value, len);
|
||||
|
||||
struct stat buf;
|
||||
return stat(value, &buf) == 0;
|
||||
}
|
||||
|
||||
int play_sound_internal(Notification what, bool loop)
|
||||
{
|
||||
uint32_t source;
|
||||
uint32_t buffer;
|
||||
|
||||
m_open_device();
|
||||
|
||||
alGenSources(1, &source);
|
||||
alGenBuffers(1, &buffer);
|
||||
buffer = alutCreateBufferFromFile(Control.sounds[what]);
|
||||
alSourcei(source, AL_BUFFER, buffer);
|
||||
alSourcei(source, AL_LOOPING, loop);
|
||||
|
||||
int rc = play_source(source, buffer, loop);
|
||||
if (rc < 0) {
|
||||
alSourceStop(source);
|
||||
alDeleteSources(1, &source);
|
||||
alDeleteBuffers(1,&buffer);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int play_notify_sound(Notification notif, uint64_t flags)
|
||||
{
|
||||
int rc = -1;
|
||||
|
||||
if (flags & NT_BEEP) beep();
|
||||
else if (notif != silent) {
|
||||
if ( !Control.poll_active || !Control.sounds[notif] )
|
||||
return -1;
|
||||
|
||||
rc = play_sound_internal(notif, flags & NT_LOOP ? 1 : 0);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
void stop_sound(int id)
|
||||
{
|
||||
if (id >= 0 && id < ACTIVE_NOTIFS_MAX && actives[id].looping && actives[id].active ) {
|
||||
#ifdef BOX_NOTIFY
|
||||
if (actives[id].box) {
|
||||
GError* ignore;
|
||||
notify_notification_close(actives[id].box, &ignore);
|
||||
}
|
||||
#endif
|
||||
if (actives[id].id_indicator)
|
||||
*actives[id].id_indicator = -1;
|
||||
// alSourcei(actives[id].source, AL_LOOPING, false);
|
||||
alSourceStop(actives[id].source);
|
||||
alDeleteSources(1, &actives[id].source);
|
||||
alDeleteBuffers(1,&actives[id].buffer);
|
||||
memset(&actives[id], 0, sizeof(struct _ActiveNotifications));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static int m_play_sound(Notification notif, uint64_t flags)
|
||||
{
|
||||
#ifdef SOUND_NOTIFY
|
||||
return play_notify_sound(notif, flags);
|
||||
#else
|
||||
if (notif != silent)
|
||||
beep();
|
||||
|
||||
return -1;
|
||||
#endif /* SOUND_NOTIFY */
|
||||
}
|
||||
|
||||
#ifdef BOX_NOTIFY
|
||||
void m_notify_action(NotifyNotification *box, char *action, void* data)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
int sound_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indicator)
|
||||
{
|
||||
tab_notify(self, flags);
|
||||
|
||||
if (notifications_are_disabled(flags))
|
||||
return -1;
|
||||
|
||||
int id = -1;
|
||||
control_lock();
|
||||
|
||||
if (self && (!self->stb || self->stb->status != TOX_USER_STATUS_BUSY))
|
||||
id = m_play_sound(notif, flags);
|
||||
else if (flags & NT_ALWAYS)
|
||||
id = m_play_sound(notif, flags);
|
||||
|
||||
#if defined(BOX_NOTIFY) && !defined(SOUND_NOTIFY)
|
||||
|
||||
if (id == -1) {
|
||||
for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].box; id++);
|
||||
if ( id == ACTIVE_NOTIFS_MAX ) {
|
||||
control_unlock();
|
||||
return -1; /* Full */
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if ( id_indicator && id != -1 ) {
|
||||
actives[id].id_indicator = id_indicator;
|
||||
*id_indicator = id;
|
||||
}
|
||||
|
||||
control_unlock();
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
int sound_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id)
|
||||
{
|
||||
tab_notify(self, flags);
|
||||
|
||||
if (notifications_are_disabled(flags))
|
||||
return -1;
|
||||
|
||||
if (id < 0 || id >= ACTIVE_NOTIFS_MAX) return -1;
|
||||
#ifdef SOUND_NOTIFY
|
||||
control_lock();
|
||||
|
||||
if (!actives[id].active || !Control.sounds[notif]) {
|
||||
control_unlock();
|
||||
return -1;
|
||||
}
|
||||
|
||||
m_open_device();
|
||||
|
||||
alSourceStop(actives[id].source);
|
||||
alDeleteSources(1, &actives[id].source);
|
||||
alDeleteBuffers(1,&actives[id].buffer);
|
||||
|
||||
|
||||
alGenSources(1, &actives[id].source);
|
||||
alGenBuffers(1, &actives[id].buffer);
|
||||
actives[id].buffer = alutCreateBufferFromFile(Control.sounds[notif]);
|
||||
alSourcei(actives[id].source, AL_BUFFER, actives[id].buffer);
|
||||
alSourcei(actives[id].source, AL_LOOPING, flags & NT_LOOP);
|
||||
|
||||
alSourcePlay(actives[id].source);
|
||||
|
||||
control_unlock();
|
||||
|
||||
return id;
|
||||
#else
|
||||
if (notif != silent)
|
||||
beep();
|
||||
|
||||
return 0;
|
||||
#endif /* SOUND_NOTIFY */
|
||||
}
|
||||
|
||||
int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indicator, const char* title, const char* format, ...)
|
||||
{
|
||||
if (notifications_are_disabled(flags)) {
|
||||
tab_notify(self, flags);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef BOX_NOTIFY
|
||||
|
||||
int id = sound_notify(self, notif, flags, id_indicator);
|
||||
|
||||
control_lock();
|
||||
|
||||
#ifdef SOUND_NOTIFY
|
||||
if (id == -1) { /* Could not play */
|
||||
|
||||
for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].active; id ++);
|
||||
if ( id == ACTIVE_NOTIFS_MAX ) {
|
||||
control_unlock();
|
||||
return -1; /* Full */
|
||||
}
|
||||
|
||||
actives[id].active = 1;
|
||||
actives[id].id_indicator = id_indicator;
|
||||
if (id_indicator) *id_indicator = id;
|
||||
}
|
||||
#else
|
||||
if (id == -1)
|
||||
return -1;
|
||||
#endif /* SOUND_NOTIFY */
|
||||
|
||||
snprintf(actives[id].title, sizeof(actives[id].title), "%s", title);
|
||||
if (strlen(title) > 23) strcpy(actives[id].title + 20, "...");
|
||||
|
||||
va_list __ARGS__; va_start (__ARGS__, format);
|
||||
vsnprintf (actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__);
|
||||
va_end (__ARGS__);
|
||||
|
||||
if (strlen(actives[id].messages[0]) > MAX_BOX_MSG_LEN - 3)
|
||||
strcpy(actives[id].messages[0] + MAX_BOX_MSG_LEN - 3, "...");
|
||||
|
||||
actives[id].box = notify_notification_new(actives[id].title, actives[id].messages[0], NULL);
|
||||
actives[id].size++;
|
||||
actives[id].n_timeout = get_unix_time() + Control.notif_timeout / 1000;
|
||||
|
||||
notify_notification_set_timeout(actives[id].box, Control.notif_timeout);
|
||||
notify_notification_set_app_name(actives[id].box, "toxic");
|
||||
/*notify_notification_add_action(actives[id].box, "lel", "default", m_notify_action, self, NULL);*/
|
||||
notify_notification_show(actives[id].box, NULL);
|
||||
|
||||
control_unlock();
|
||||
return id;
|
||||
#else
|
||||
return sound_notify(self, notif, flags, id_indicator);
|
||||
#endif /* BOX_NOTIFY */
|
||||
}
|
||||
|
||||
int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, const char* format, ...)
|
||||
{
|
||||
if (notifications_are_disabled(flags)) {
|
||||
tab_notify(self, flags);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef BOX_NOTIFY
|
||||
|
||||
if (sound_notify2(self, notif, flags, id) == -1)
|
||||
return -1;
|
||||
|
||||
control_lock();
|
||||
|
||||
if (!actives[id].box || actives[id].size >= MAX_BOX_MSG_LEN + 1) {
|
||||
control_unlock();
|
||||
return -1;
|
||||
}
|
||||
|
||||
va_list __ARGS__; va_start (__ARGS__, format);
|
||||
vsnprintf (actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__);
|
||||
va_end (__ARGS__);
|
||||
|
||||
if (strlen(actives[id].messages[actives[id].size]) > MAX_BOX_MSG_LEN - 3)
|
||||
strcpy(actives[id].messages[actives[id].size] + MAX_BOX_MSG_LEN - 3, "...");
|
||||
|
||||
actives[id].size++;
|
||||
actives[id].n_timeout = get_unix_time() + Control.notif_timeout / 1000;
|
||||
|
||||
char formated[128 * 129] = {'\0'};
|
||||
|
||||
int i = 0;
|
||||
for (; i <actives[id].size; i ++) {
|
||||
strcat(formated, actives[id].messages[i]);
|
||||
strcat(formated, "\n");
|
||||
}
|
||||
|
||||
formated[strlen(formated) - 1] = '\0';
|
||||
|
||||
notify_notification_update(actives[id].box, actives[id].title, formated, NULL);
|
||||
notify_notification_show(actives[id].box, NULL);
|
||||
|
||||
control_unlock();
|
||||
|
||||
return id;
|
||||
#else
|
||||
return sound_notify2(self, notif, flags, id);
|
||||
#endif
|
||||
}
|
||||
|
||||
int box_silent_notify(ToxWindow* self, uint64_t flags, int* id_indicator, const char* title, const char* format, ...)
|
||||
{
|
||||
tab_notify(self, flags);
|
||||
|
||||
if (notifications_are_disabled(flags))
|
||||
return -1;
|
||||
|
||||
#ifdef BOX_NOTIFY
|
||||
|
||||
control_lock();
|
||||
|
||||
int id;
|
||||
for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].active; id ++);
|
||||
if ( id == ACTIVE_NOTIFS_MAX ) {
|
||||
control_unlock();
|
||||
return -1; /* Full */
|
||||
}
|
||||
|
||||
if (id_indicator) {
|
||||
actives[id].id_indicator = id_indicator;
|
||||
*id_indicator = id;
|
||||
}
|
||||
|
||||
snprintf(actives[id].title, sizeof(actives[id].title), "%s", title);
|
||||
if (strlen(title) > 23) strcpy(actives[id].title + 20, "...");
|
||||
|
||||
va_list __ARGS__; va_start (__ARGS__, format);
|
||||
vsnprintf (actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__);
|
||||
va_end (__ARGS__);
|
||||
|
||||
if (strlen(actives[id].messages[0]) > MAX_BOX_MSG_LEN - 3)
|
||||
strcpy(actives[id].messages[0] + MAX_BOX_MSG_LEN - 3, "...");
|
||||
|
||||
actives[id].active = 1;
|
||||
actives[id].box = notify_notification_new(actives[id].title, actives[id].messages[0], NULL);
|
||||
actives[id].size ++;
|
||||
actives[id].n_timeout = get_unix_time() + Control.notif_timeout / 1000;
|
||||
|
||||
notify_notification_set_timeout(actives[id].box, Control.notif_timeout);
|
||||
notify_notification_set_app_name(actives[id].box, "toxic");
|
||||
/*notify_notification_add_action(actives[id].box, "lel", "default", m_notify_action, self, NULL);*/
|
||||
notify_notification_show(actives[id].box, NULL);
|
||||
|
||||
control_unlock();
|
||||
return id;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
int box_silent_notify2(ToxWindow* self, uint64_t flags, int id, const char* format, ...)
|
||||
{
|
||||
tab_notify(self, flags);
|
||||
|
||||
if (notifications_are_disabled(flags))
|
||||
return -1;
|
||||
|
||||
#ifdef BOX_NOTIFY
|
||||
control_lock();
|
||||
|
||||
if (id < 0 || id >= ACTIVE_NOTIFS_MAX || !actives[id].box || actives[id].size >= MAX_BOX_MSG_LEN + 1 ) {
|
||||
control_unlock();
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
va_list __ARGS__; va_start (__ARGS__, format);
|
||||
vsnprintf (actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__);
|
||||
va_end (__ARGS__);
|
||||
|
||||
if (strlen(actives[id].messages[actives[id].size]) > MAX_BOX_MSG_LEN - 3)
|
||||
strcpy(actives[id].messages[actives[id].size] + MAX_BOX_MSG_LEN - 3, "...");
|
||||
|
||||
actives[id].size ++;
|
||||
actives[id].n_timeout = get_unix_time() + Control.notif_timeout / 1000;
|
||||
|
||||
char formated[128 * 129] = {'\0'};
|
||||
|
||||
int i = 0;
|
||||
for (; i <actives[id].size; i ++) {
|
||||
strcat(formated, actives[id].messages[i]);
|
||||
strcat(formated, "\n");
|
||||
}
|
||||
|
||||
formated[strlen(formated) - 1] = '\0';
|
||||
|
||||
notify_notification_update(actives[id].box, actives[id].title, formated, NULL);
|
||||
notify_notification_show(actives[id].box, NULL);
|
||||
|
||||
control_unlock();
|
||||
|
||||
return id;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
80
src/notify.h
Normal file
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 */
|
||||
553
src/prompt.c
553
src/prompt.c
@@ -20,101 +20,145 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#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.h"
|
||||
#include "windows.h"
|
||||
#include "prompt.h"
|
||||
#include "friendlist.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;
|
||||
struct _Winthread Winthread;
|
||||
|
||||
extern struct user_settings *user_settings;
|
||||
extern struct Winthread Winthread;
|
||||
|
||||
extern FriendsList Friends;
|
||||
FriendRequests FrndRequests;
|
||||
|
||||
#ifdef AUDIO
|
||||
#define AC_NUM_GLOB_COMMANDS 18
|
||||
#else
|
||||
#define AC_NUM_GLOB_COMMANDS 16
|
||||
#endif /* AUDIO */
|
||||
|
||||
/* Array of global command names used for tab completion. */
|
||||
const 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 _SUPPORT_AUDIO
|
||||
#ifdef AUDIO
|
||||
|
||||
{ "/lsdev" },
|
||||
{ "/sdev" },
|
||||
|
||||
#endif /* _SUPPORT_AUDIO */
|
||||
#endif /* AUDIO */
|
||||
};
|
||||
|
||||
void kill_prompt_window(ToxWindow *self)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
StatusBar *statusbar = self->stb;
|
||||
|
||||
log_disable(ctx->log);
|
||||
line_info_cleanup(ctx->hst);
|
||||
|
||||
delwin(ctx->linewin);
|
||||
delwin(ctx->history);
|
||||
delwin(statusbar->topline);
|
||||
|
||||
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 = 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);
|
||||
statusbar->statusmsg_len = strlen(statusbar->statusmsg);
|
||||
size_t len = strlen(statusbar->statusmsg);
|
||||
statusbar->statusmsg_len = len;
|
||||
|
||||
TOX_ERR_SET_INFO err;
|
||||
tox_self_set_status_message(m, (uint8_t *) statusmsg, len, &err);
|
||||
|
||||
if (err != TOX_ERR_SET_INFO_OK)
|
||||
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set note (error %d)\n", err);
|
||||
}
|
||||
|
||||
/* Updates own status in prompt statusbar */
|
||||
void prompt_update_status(ToxWindow *prompt, uint8_t status)
|
||||
void prompt_update_status(ToxWindow *prompt, TOX_USER_STATUS status)
|
||||
{
|
||||
StatusBar *statusbar = prompt->stb;
|
||||
statusbar->status = status;
|
||||
}
|
||||
|
||||
/* Updates own connection status in prompt statusbar */
|
||||
void prompt_update_connectionstatus(ToxWindow *prompt, bool is_connected)
|
||||
{
|
||||
StatusBar *statusbar = prompt->stb;
|
||||
statusbar->is_online = is_connected;
|
||||
}
|
||||
|
||||
/* Adds friend request to pending friend requests.
|
||||
Returns request number on success, -1 if queue is full or other error. */
|
||||
static int add_friend_request(const uint8_t *public_key)
|
||||
Returns request number on success, -1 if queue is full. */
|
||||
static int add_friend_request(const char *public_key, const char *data)
|
||||
{
|
||||
if (num_frnd_requests >= MAX_FRIENDS_NUM)
|
||||
if (FrndRequests.max_idx >= MAX_FRIEND_REQUESTS)
|
||||
return -1;
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; i <= num_frnd_requests; ++i) {
|
||||
if (!strlen(pending_frnd_requests[i])) {
|
||||
memcpy(pending_frnd_requests[i], public_key, TOX_CLIENT_ID_SIZE);
|
||||
for (i = 0; i <= FrndRequests.max_idx; ++i) {
|
||||
if (!FrndRequests.request[i].active) {
|
||||
FrndRequests.request[i].active = true;
|
||||
memcpy(FrndRequests.request[i].key, public_key, TOX_PUBLIC_KEY_SIZE);
|
||||
snprintf(FrndRequests.request[i].msg, sizeof(FrndRequests.request[i].msg), "%s", data);
|
||||
|
||||
if (i == num_frnd_requests)
|
||||
++num_frnd_requests;
|
||||
if (i == FrndRequests.max_idx)
|
||||
++FrndRequests.max_idx;
|
||||
|
||||
++FrndRequests.num_requests;
|
||||
|
||||
return i;
|
||||
}
|
||||
@@ -128,205 +172,123 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
int x, y, y2, x2;
|
||||
getyx(ctx->history, y, x);
|
||||
getmaxyx(ctx->history, y2, x2);
|
||||
getyx(self->window, y, x);
|
||||
getmaxyx(self->window, y2, x2);
|
||||
|
||||
if (ltr) {
|
||||
if (ctx->len < (MAX_STR_SIZE - 1)) {
|
||||
add_char_to_buf(ctx, key);
|
||||
}
|
||||
} else { /* if (!ltr) */
|
||||
if (x2 <= 0)
|
||||
return;
|
||||
|
||||
/* BACKSPACE key: Remove one character from line */
|
||||
if (key == 0x107 || key == 0x8 || key == 0x7f) {
|
||||
if (ctx->pos > 0) {
|
||||
del_char_buf_bck(ctx);
|
||||
wmove(ctx->history, y, x - 1); /* not necessary but fixes a display glitch */
|
||||
} else {
|
||||
beep();
|
||||
}
|
||||
}
|
||||
/* 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 (ctx->pos != ctx->len) {
|
||||
del_char_buf_frnt(ctx);
|
||||
} 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 (ctx->pos > 0) {
|
||||
wmove(ctx->history, ctx->orig_y, X_OFST);
|
||||
wclrtobot(ctx->history);
|
||||
discard_buf(ctx);
|
||||
} 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 (ctx->len != ctx->pos)
|
||||
kill_buf(ctx);
|
||||
else
|
||||
beep();
|
||||
}
|
||||
input_handle(self, key, x, y, x2, y2);
|
||||
|
||||
else if (key == KEY_HOME || key == T_KEY_C_A) { /* HOME/C-a key: Move cursor to start of line */
|
||||
if (ctx->pos != 0)
|
||||
ctx->pos = 0;
|
||||
}
|
||||
if (key == '\t') { /* TAB key: auto-completes command */
|
||||
if (ctx->len > 1 && ctx->line[0] == '/') {
|
||||
int diff = -1;
|
||||
|
||||
else if (key == KEY_END || key == T_KEY_C_E) { /* END/C-e key: move cursor to end of line */
|
||||
if (ctx->pos != ctx->len)
|
||||
ctx->pos = ctx->len;
|
||||
}
|
||||
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 (ctx->pos > 0)
|
||||
--ctx->pos;
|
||||
else
|
||||
beep();
|
||||
}
|
||||
|
||||
else if (key == KEY_RIGHT) {
|
||||
if (ctx->pos < ctx->len)
|
||||
++ctx->pos;
|
||||
else
|
||||
beep();
|
||||
}
|
||||
|
||||
else if (key == KEY_UP) { /* fetches previous item in history */
|
||||
wmove(ctx->history, ctx->orig_y, X_OFST);
|
||||
fetch_hist_item(ctx, MOVE_UP);
|
||||
|
||||
/* adjust line y origin appropriately when window scrolls down */
|
||||
if (ctx->at_bottom && ctx->len >= x2 - X_OFST) {
|
||||
int px2 = ctx->len >= x2 ? x2 : x2 - X_OFST;
|
||||
int p_ofst = px2 != x2 ? 0 : X_OFST;
|
||||
|
||||
if (px2 <= 0)
|
||||
return;
|
||||
|
||||
int k = ctx->orig_y + ((ctx->len + p_ofst) / px2);
|
||||
|
||||
if (k >= y2) {
|
||||
--ctx->orig_y;
|
||||
if (diff != -1) {
|
||||
if (x + diff > x2 - 1) {
|
||||
int wlen = MAX(0, wcswidth(ctx->line, sizeof(ctx->line) / sizeof(wchar_t)));
|
||||
ctx->start = wlen < x2 ? 0 : wlen - x2 + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (key == KEY_DOWN) { /* fetches next item in history */
|
||||
wmove(ctx->history, ctx->orig_y, X_OFST);
|
||||
fetch_hist_item(ctx, MOVE_DOWN);
|
||||
}
|
||||
|
||||
else if (key == '\t') { /* TAB key: completes command */
|
||||
if (ctx->len > 1 && ctx->line[0] == '/') {
|
||||
if (complete_line(ctx, glob_cmd_list, AC_NUM_GLOB_COMMANDS, MAX_CMDNAME_SIZE) == -1)
|
||||
beep();
|
||||
} else {
|
||||
beep();
|
||||
sound_notify(self, notif_error, 0, NULL);
|
||||
}
|
||||
} else {
|
||||
sound_notify(self, notif_error, 0, NULL);
|
||||
}
|
||||
} else if (key == '\n') {
|
||||
rm_trailing_spaces_buf(ctx);
|
||||
|
||||
/* RETURN key: execute command */
|
||||
else if (key == '\n') {
|
||||
rm_trailing_spaces_buf(ctx);
|
||||
char line[MAX_STR_SIZE] = {0};
|
||||
|
||||
wprintw(ctx->history, "\n");
|
||||
uint8_t line[MAX_STR_SIZE] = {0};
|
||||
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1)
|
||||
memset(&line, 0, sizeof(line));
|
||||
|
||||
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1)
|
||||
memset(&line, 0, sizeof(line));
|
||||
if (!string_is_empty(line))
|
||||
add_line_to_hist(ctx);
|
||||
|
||||
if (!string_is_empty(line))
|
||||
add_line_to_hist(ctx);
|
||||
line_info_add(self, NULL, NULL, NULL, PROMPT, 0, 0, "%s", line);
|
||||
execute(ctx->history, self, m, line, GLOBAL_COMMAND_MODE);
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, line, PROMPT, 0, 0);
|
||||
execute(ctx->history, self, m, line, GLOBAL_COMMAND_MODE);
|
||||
reset_buf(ctx);
|
||||
}
|
||||
wclear(ctx->linewin);
|
||||
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
|
||||
reset_buf(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static void prompt_onDraw(ToxWindow *self, Tox *m)
|
||||
{
|
||||
int x2, y2;
|
||||
getmaxyx(self->window, y2, x2);
|
||||
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
int x, y, x2, y2;
|
||||
getyx(ctx->history, y, x);
|
||||
getmaxyx(ctx->history, y2, x2);
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
line_info_print(self);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
wclear(ctx->linewin);
|
||||
|
||||
curs_set(1);
|
||||
scrollok(ctx->history, 1);
|
||||
|
||||
line_info_print(self);
|
||||
|
||||
/* if len is >= screen width offset max x by X_OFST to account for prompt char */
|
||||
int px2 = ctx->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 (ctx->len > 0) {
|
||||
uint8_t line[MAX_STR_SIZE];
|
||||
|
||||
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1)
|
||||
reset_buf(ctx);
|
||||
else
|
||||
mvwprintw(ctx->history, ctx->orig_y, X_OFST, line);
|
||||
|
||||
int k = ctx->orig_y + ((ctx->len + p_ofst) / px2);
|
||||
|
||||
ctx->at_bottom = k == y2 - 1;
|
||||
bool botm = k == y2;
|
||||
bool edge = (ctx->len + p_ofst) % px2 == 0;
|
||||
|
||||
/* move point of line origin up when input scrolls screen down */
|
||||
if (edge && botm)
|
||||
--ctx->orig_y;
|
||||
|
||||
} else { /* Mark point of origin for new line */
|
||||
ctx->orig_y = y;
|
||||
}
|
||||
|
||||
wattron(ctx->history, COLOR_PAIR(GREEN));
|
||||
mvwprintw(ctx->history, ctx->orig_y, 0, "$ ");
|
||||
wattroff(ctx->history, 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;
|
||||
const uint8_t *status_text = "Unknown";
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
TOX_CONNECTION connection = statusbar->connection;
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
switch (statusbar->status) {
|
||||
case TOX_USERSTATUS_NONE:
|
||||
if (connection != TOX_CONNECTION_NONE) {
|
||||
int colour = MAGENTA;
|
||||
const char *status_text = "ERROR";
|
||||
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
TOX_USER_STATUS status = statusbar->status;
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
switch (status) {
|
||||
case TOX_USER_STATUS_NONE:
|
||||
status_text = "Online";
|
||||
colour = GREEN;
|
||||
break;
|
||||
|
||||
case TOX_USERSTATUS_AWAY:
|
||||
case TOX_USER_STATUS_AWAY:
|
||||
status_text = "Away";
|
||||
colour = YELLOW;
|
||||
break;
|
||||
|
||||
case TOX_USERSTATUS_BUSY:
|
||||
case TOX_USER_STATUS_BUSY:
|
||||
status_text = "Busy";
|
||||
colour = RED;
|
||||
break;
|
||||
|
||||
case TOX_USERSTATUS_INVALID:
|
||||
status_text = "ERROR";
|
||||
colour = MAGENTA;
|
||||
break;
|
||||
}
|
||||
|
||||
wattron(statusbar->topline, COLOR_PAIR(colour) | A_BOLD);
|
||||
@@ -334,156 +296,213 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
|
||||
wattroff(statusbar->topline, COLOR_PAIR(colour) | A_BOLD);
|
||||
|
||||
wattron(statusbar->topline, A_BOLD);
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
wprintw(statusbar->topline, " %s", statusbar->nick);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
wattroff(statusbar->topline, A_BOLD);
|
||||
} else {
|
||||
wprintw(statusbar->topline, "[Offline]");
|
||||
wprintw(statusbar->topline, " [Offline]");
|
||||
wattron(statusbar->topline, A_BOLD);
|
||||
wprintw(statusbar->topline, " %s ", statusbar->nick);
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
wprintw(statusbar->topline, " %s", statusbar->nick);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
wattroff(statusbar->topline, A_BOLD);
|
||||
}
|
||||
|
||||
/* Reset statusbar->statusmsg on window resize */
|
||||
if (x2 != self->x) {
|
||||
char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH];
|
||||
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
size_t slen = tox_self_get_status_message_size(m);
|
||||
tox_self_get_status_message (m, (uint8_t*) statusmsg);
|
||||
statusmsg[slen] = '\0';
|
||||
snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg);
|
||||
statusbar->statusmsg_len = strlen(statusbar->statusmsg);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
}
|
||||
|
||||
self->x = x2;
|
||||
|
||||
/* Truncate note if it doesn't fit in statusbar */
|
||||
uint16_t maxlen = x2 - getcurx(statusbar->topline) - 3;
|
||||
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
|
||||
if (statusbar->statusmsg_len > maxlen) {
|
||||
statusbar->statusmsg[maxlen - 3] = '\0';
|
||||
strcat(statusbar->statusmsg, "...");
|
||||
statusbar->statusmsg_len = maxlen;
|
||||
}
|
||||
|
||||
if (statusbar->statusmsg[0])
|
||||
wprintw(statusbar->topline, " - %s", statusbar->statusmsg);
|
||||
wprintw(statusbar->topline, " : %s", statusbar->statusmsg);
|
||||
|
||||
/* put cursor back in correct spot */
|
||||
int y_m = ctx->orig_y + ((ctx->pos + p_ofst) / px2);
|
||||
int x_m = (ctx->pos + X_OFST) % x2;
|
||||
wmove(self->window, y_m, x_m);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
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 : MAX(0, 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_onConnectionChange(ToxWindow *self, Tox *m, int32_t friendnum , uint8_t status)
|
||||
static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnum , TOX_CONNECTION connection_status)
|
||||
{
|
||||
if (friendnum < 0)
|
||||
return;
|
||||
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
uint8_t nick[TOX_MAX_NAME_LENGTH] = {0};
|
||||
int n_len = tox_get_name(m, friendnum, nick);
|
||||
n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH - 1);
|
||||
char nick[TOX_MAX_NAME_LENGTH] = {0}; /* stop removing this initiation */
|
||||
get_nick_truncate(m, nick, friendnum);
|
||||
|
||||
if (!nick[0]) {
|
||||
if (!nick[0])
|
||||
snprintf(nick, sizeof(nick), "%s", UNKNOWN_NAME);
|
||||
n_len = strlen(UNKNOWN_NAME);
|
||||
}
|
||||
|
||||
nick[n_len] = '\0';
|
||||
char timefrmt[TIME_STR_SIZE];
|
||||
get_time_str(timefrmt, sizeof(timefrmt));
|
||||
const char *msg;
|
||||
|
||||
uint8_t timefrmt[TIME_STR_SIZE];
|
||||
get_time_str(timefrmt);
|
||||
uint8_t *msg;
|
||||
|
||||
if (status == 1) {
|
||||
if (connection_status != TOX_CONNECTION_NONE && Friends.list[friendnum].connection_status == TOX_CONNECTION_NONE) {
|
||||
msg = "has come online";
|
||||
line_info_add(self, timefrmt, nick, NULL, msg, CONNECTION, 0, GREEN);
|
||||
line_info_add(self, timefrmt, nick, NULL, CONNECTION, 0, GREEN, msg);
|
||||
write_to_log(msg, nick, ctx->log, true);
|
||||
alert_window(self, WINDOW_ALERT_2, false);
|
||||
} else {
|
||||
|
||||
if (self->active_box != -1)
|
||||
box_notify2(self, user_log_in, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, self->active_box,
|
||||
"%s has come online", nick );
|
||||
else
|
||||
box_notify(self, user_log_in, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, &self->active_box,
|
||||
"Toxic", "%s has come online", nick );
|
||||
}
|
||||
else if (connection_status == TOX_CONNECTION_NONE) {
|
||||
msg = "has gone offline";
|
||||
line_info_add(self, timefrmt, nick, NULL, msg, CONNECTION, 0, RED);
|
||||
line_info_add(self, timefrmt, nick, NULL, DISCONNECTION, 0, RED, msg);
|
||||
write_to_log(msg, nick, ctx->log, true);
|
||||
|
||||
if (self->active_box != -1)
|
||||
box_notify2(self, user_log_out, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, self->active_box,
|
||||
"%s has gone offline", nick );
|
||||
else
|
||||
box_notify(self, user_log_out, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, &self->active_box,
|
||||
"Toxic", "%s has gone offline", nick );
|
||||
}
|
||||
}
|
||||
|
||||
static void prompt_onFriendRequest(ToxWindow *self, Tox *m, const uint8_t *key, const uint8_t *data,
|
||||
uint16_t length)
|
||||
static void prompt_onFriendRequest(ToxWindow *self, Tox *m, const char *key, const char *data, size_t length)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
uint8_t timefrmt[TIME_STR_SIZE];
|
||||
get_time_str(timefrmt);
|
||||
char timefrmt[TIME_STR_SIZE];
|
||||
get_time_str(timefrmt, sizeof(timefrmt));
|
||||
|
||||
uint8_t msg[MAX_STR_SIZE];
|
||||
snprintf(msg, sizeof(msg), "Friend request with the message '%s'", data);
|
||||
line_info_add(self, timefrmt, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||
write_to_log(msg, "", ctx->log, true);
|
||||
line_info_add(self, timefrmt, NULL, NULL, SYS_MSG, 0, 0, "Friend request with the message '%s'", data);
|
||||
write_to_log("Friend request with the message '%s'", "", ctx->log, true);
|
||||
|
||||
int n = add_friend_request(key);
|
||||
int n = add_friend_request(key, data);
|
||||
|
||||
if (n == -1) {
|
||||
uint8_t *errmsg = "Friend request queue is full. Discarding request.";
|
||||
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||
write_to_log(errmsg, "", ctx->log, true);
|
||||
const char *errmsg = "Friend request queue is full. Discarding request.";
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf(msg, sizeof(msg), "Type \"/accept %d\" to accept it.", n);
|
||||
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||
alert_window(self, WINDOW_ALERT_1, true);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type \"/accept %d\" or \"/decline %d\"", n, n);
|
||||
sound_notify(self, generic_message, NT_WNDALERT_1 | NT_NOTIFWND, NULL);
|
||||
}
|
||||
|
||||
void prompt_init_statusbar(ToxWindow *self, Tox *m)
|
||||
{
|
||||
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];
|
||||
uint8_t statusmsg[MAX_STR_SIZE];
|
||||
char nick[TOX_MAX_NAME_LENGTH];
|
||||
char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH];
|
||||
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
uint16_t n_len = tox_get_self_name(m, nick);
|
||||
uint16_t s_len = tox_get_self_status_message(m, statusmsg, MAX_STR_SIZE);
|
||||
uint8_t status = tox_get_self_user_status(m);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
size_t n_len = tox_self_get_name_size(m);
|
||||
tox_self_get_name(m, (uint8_t *) nick);
|
||||
|
||||
size_t s_len = tox_self_get_status_message_size(m);
|
||||
tox_self_get_status_message(m, (uint8_t *) statusmsg);
|
||||
|
||||
TOX_USER_STATUS status = tox_self_get_status(m);
|
||||
|
||||
nick[n_len] = '\0';
|
||||
statusmsg[s_len] = '\0';
|
||||
|
||||
/* load prev status message or show toxic version if it has never been set */
|
||||
uint8_t ver[strlen(TOXICVER) + 1];
|
||||
strcpy(ver, TOXICVER);
|
||||
const uint8_t *toxic_ver = strtok(ver, "_");
|
||||
|
||||
if ( (!strcmp("Online", statusmsg) || !strncmp("Toxing on Toxic", statusmsg, 15)) && toxic_ver != NULL) {
|
||||
snprintf(statusmsg, MAX_STR_SIZE, "Toxing on Toxic v.%s", toxic_ver);
|
||||
if (s_len == 0 || !strncmp(statusmsg, "Toxing on Toxic", strlen("Toxing on Toxic"))) {
|
||||
snprintf(statusmsg, sizeof(statusmsg), "Toxing on Toxic");
|
||||
s_len = strlen(statusmsg);
|
||||
statusmsg[s_len] = '\0';
|
||||
}
|
||||
|
||||
prompt_update_statusmessage(prompt, statusmsg, s_len);
|
||||
prompt_update_statusmessage(prompt, m, statusmsg);
|
||||
prompt_update_status(prompt, status);
|
||||
prompt_update_nick(prompt, nick, n_len);
|
||||
prompt_update_nick(prompt, nick);
|
||||
|
||||
/* Init statusbar subwindow */
|
||||
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)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
curs_set(1);
|
||||
int y2, x2;
|
||||
getmaxyx(self->window, y2, x2);
|
||||
|
||||
ctx->history = subwin(self->window, y2, x2, 0, 0);
|
||||
scrollok(ctx->history, 1);
|
||||
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 = malloc(sizeof(struct chatlog));
|
||||
ctx->hst = malloc(sizeof(struct history));
|
||||
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);
|
||||
|
||||
memset(ctx->log, 0, sizeof(struct chatlog));
|
||||
memset(ctx->hst, 0, sizeof(struct history));
|
||||
|
||||
line_info_init(ctx->hst);
|
||||
|
||||
if (user_settings->autolog == AUTOLOG_ON) {
|
||||
uint8_t myid[TOX_FRIEND_ADDRESS_SIZE];
|
||||
tox_get_address(m, myid);
|
||||
log_enable(self->name, myid, ctx->log);
|
||||
char myid[TOX_ADDRESS_SIZE];
|
||||
tox_self_get_address(m, (uint8_t *) myid);
|
||||
|
||||
if (log_enable(self->name, myid, NULL, ctx->log, LOG_PROMPT) == -1)
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize.");
|
||||
}
|
||||
|
||||
execute(ctx->history, self, m, "/help", GLOBAL_COMMAND_MODE);
|
||||
wmove(ctx->history, y2 - 1, 2);
|
||||
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)
|
||||
@@ -500,16 +519,20 @@ ToxWindow new_prompt(void)
|
||||
ret.onConnectionChange = &prompt_onConnectionChange;
|
||||
ret.onFriendRequest = &prompt_onFriendRequest;
|
||||
|
||||
strcpy(ret.name, "prompt");
|
||||
strcpy(ret.name, "home");
|
||||
|
||||
ChatContext *chatwin = calloc(1, sizeof(ChatContext));
|
||||
StatusBar *stb = calloc(1, sizeof(StatusBar));
|
||||
Help *help = calloc(1, sizeof(Help));
|
||||
|
||||
if (stb == NULL || chatwin == NULL)
|
||||
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;
|
||||
}
|
||||
|
||||
35
src/prompt.h
35
src/prompt.h
@@ -20,26 +20,37 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef PROMPT_H_UZYGWFFL
|
||||
#define PROMPT_H_UZYGWFFL
|
||||
#ifndef PROMPT_H
|
||||
#define PROMPT_H
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
#define X_OFST 2 /* offset to account for prompt char */
|
||||
#define MAX_FRIEND_REQUESTS 32
|
||||
|
||||
#ifdef _SUPPORT_AUDIO
|
||||
#define AC_NUM_GLOB_COMMANDS 17
|
||||
#else
|
||||
#define AC_NUM_GLOB_COMMANDS 15
|
||||
#endif /* _SUPPORT_AUDIO */
|
||||
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, uint8_t status);
|
||||
void prompt_update_nick(ToxWindow *prompt, const char *nick);
|
||||
void prompt_update_statusmessage(ToxWindow *prompt, Tox *m, const char *statusmsg);
|
||||
void prompt_update_status(ToxWindow *prompt, TOX_USER_STATUS status);
|
||||
void prompt_update_connectionstatus(ToxWindow *prompt, bool is_connected);
|
||||
void kill_prompt_window(ToxWindow *self);
|
||||
|
||||
#endif /* end of include guard: PROMPT_H_UZYGWFFL */
|
||||
/* callback: Updates own connection status in prompt statusbar */
|
||||
void prompt_onSelfConnectionChange(Tox *m, TOX_CONNECTION connection_status, void *userdata);
|
||||
|
||||
#endif /* end of include guard: PROMPT_H */
|
||||
|
||||
552
src/settings.c
552
src/settings.c
@@ -20,202 +20,446 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <libconfig.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "configdir.h"
|
||||
#include "notify.h"
|
||||
#include "misc_tools.h"
|
||||
|
||||
#ifdef _SUPPORT_AUDIO
|
||||
#include "audio_call.h"
|
||||
#endif
|
||||
#ifdef AUDIO
|
||||
#include "device.h"
|
||||
#endif /* AUDIO */
|
||||
|
||||
#include "settings.h"
|
||||
#include "line_info.h"
|
||||
|
||||
static void uset_autolog(struct user_settings *s, const char *val);
|
||||
static void uset_time(struct user_settings *s, const char *val);
|
||||
static void uset_alerts(struct user_settings *s, const char *val);
|
||||
static void uset_colours(struct user_settings *s, const char *val);
|
||||
static void uset_hst_size(struct user_settings *s, const char *val);
|
||||
static void uset_dwnld_path(struct user_settings *s, const char *val);
|
||||
|
||||
#ifdef _SUPPORT_AUDIO
|
||||
static void uset_ain_dev(struct user_settings *s, const char *val);
|
||||
static void uset_aout_dev(struct user_settings *s, const char *val);
|
||||
#ifndef PACKAGE_DATADIR
|
||||
#define PACKAGE_DATADIR "."
|
||||
#endif
|
||||
|
||||
struct {
|
||||
const char *key;
|
||||
void (*func)(struct user_settings *s, const char *val);
|
||||
} user_settings_list[] = {
|
||||
{ "autolog", uset_autolog },
|
||||
{ "time", uset_time },
|
||||
{ "disable_alerts", uset_alerts },
|
||||
{ "colour_theme", uset_colours },
|
||||
{ "history_size", uset_hst_size },
|
||||
{ "download_path", uset_dwnld_path },
|
||||
#define NO_SOUND "silent"
|
||||
|
||||
#ifdef _SUPPORT_AUDIO
|
||||
{ "audio_in_dev", uset_ain_dev },
|
||||
{ "audio_out_dev", uset_aout_dev },
|
||||
#endif
|
||||
static struct ui_strings {
|
||||
const char* self;
|
||||
const char* timestamps;
|
||||
const char* time_format;
|
||||
const char* timestamp_format;
|
||||
const char* log_timestamp_format;
|
||||
const char* alerts;
|
||||
const char* native_colors;
|
||||
const char* autolog;
|
||||
const char* history_size;
|
||||
const char* show_typing_self;
|
||||
const char* show_typing_other;
|
||||
const char* show_welcome_msg;
|
||||
|
||||
const char* line_join;
|
||||
const char* line_quit;
|
||||
const char* line_alert;
|
||||
const char* line_normal;
|
||||
|
||||
const char* mplex_away;
|
||||
const char* mplex_away_note;
|
||||
} ui_strings = {
|
||||
"ui",
|
||||
"timestamps",
|
||||
"time_format",
|
||||
"timestamp_format",
|
||||
"log_timestamp_format",
|
||||
"alerts",
|
||||
"native_colors",
|
||||
"autolog",
|
||||
"history_size",
|
||||
"show_typing_self",
|
||||
"show_typing_other",
|
||||
"show_welcome_msg",
|
||||
"line_join",
|
||||
"line_quit",
|
||||
"line_alert",
|
||||
"line_normal",
|
||||
"mplex_away",
|
||||
"mplex_away_note",
|
||||
};
|
||||
|
||||
static void uset_autolog(struct user_settings *s, const char *val)
|
||||
static void ui_defaults(struct user_settings* settings)
|
||||
{
|
||||
int n = atoi(val);
|
||||
settings->timestamps = TIMESTAMPS_ON;
|
||||
snprintf(settings->timestamp_format, sizeof(settings->timestamp_format), "%s", TIMESTAMP_DEFAULT);
|
||||
snprintf(settings->log_timestamp_format, sizeof(settings->log_timestamp_format), "%s", LOG_TIMESTAMP_DEFAULT);
|
||||
|
||||
/* default off if invalid value */
|
||||
s->autolog = n == AUTOLOG_ON ? AUTOLOG_ON : AUTOLOG_OFF;
|
||||
settings->autolog = AUTOLOG_OFF;
|
||||
settings->alerts = ALERTS_ENABLED;
|
||||
settings->colour_theme = DFLT_COLS;
|
||||
settings->history_size = 700;
|
||||
settings->show_typing_self = SHOW_TYPING_ON;
|
||||
settings->show_typing_other = SHOW_TYPING_ON;
|
||||
settings->show_welcome_msg = SHOW_WELCOME_MSG_ON;
|
||||
|
||||
snprintf(settings->line_join, LINE_HINT_MAX + 1, "%s", LINE_JOIN);
|
||||
snprintf(settings->line_quit, LINE_HINT_MAX + 1, "%s", LINE_QUIT);
|
||||
snprintf(settings->line_alert, LINE_HINT_MAX + 1, "%s", LINE_ALERT);
|
||||
snprintf(settings->line_normal, LINE_HINT_MAX + 1, "%s", LINE_NORMAL);
|
||||
|
||||
settings->mplex_away = MPLEX_ON;
|
||||
snprintf (settings->mplex_away_note,
|
||||
sizeof (settings->mplex_away_note),
|
||||
"%s",
|
||||
MPLEX_AWAY_NOTE);
|
||||
}
|
||||
|
||||
static void uset_time(struct user_settings *s, const char *val)
|
||||
{
|
||||
int n = atoi(val);
|
||||
static const struct keys_strings {
|
||||
const char* self;
|
||||
const char* next_tab;
|
||||
const char* prev_tab;
|
||||
const char* scroll_line_up;
|
||||
const char* scroll_line_down;
|
||||
const char* half_page_up;
|
||||
const char* half_page_down;
|
||||
const char* page_bottom;
|
||||
const char* peer_list_up;
|
||||
const char* peer_list_down;
|
||||
const char* toggle_peerlist;
|
||||
} key_strings = {
|
||||
"keys",
|
||||
"next_tab",
|
||||
"prev_tab",
|
||||
"scroll_line_up",
|
||||
"scroll_line_down",
|
||||
"half_page_up",
|
||||
"half_page_down",
|
||||
"page_bottom",
|
||||
"peer_list_up",
|
||||
"peer_list_down",
|
||||
"toggle_peerlist",
|
||||
};
|
||||
|
||||
/* default to 24 hour time if invalid value */
|
||||
s->time = n == TIME_12 ? TIME_12 : TIME_24;
|
||||
/* defines from toxic.h */
|
||||
static void key_defaults(struct user_settings* settings)
|
||||
{
|
||||
settings->key_next_tab = T_KEY_NEXT;
|
||||
settings->key_prev_tab = T_KEY_PREV;
|
||||
settings->key_scroll_line_up = KEY_PPAGE;
|
||||
settings->key_scroll_line_down = KEY_NPAGE;
|
||||
settings->key_half_page_up = T_KEY_C_F;
|
||||
settings->key_half_page_down = T_KEY_C_V;
|
||||
settings->key_page_bottom = T_KEY_C_H;
|
||||
settings->key_peer_list_up = T_KEY_C_LB;
|
||||
settings->key_peer_list_down = T_KEY_C_RB;
|
||||
settings->key_toggle_peerlist = T_KEY_C_B;
|
||||
}
|
||||
|
||||
static void uset_alerts(struct user_settings *s, const char *val)
|
||||
{
|
||||
int n = atoi(val);
|
||||
static const struct tox_strings {
|
||||
const char* self;
|
||||
const char* download_path;
|
||||
const char* chatlogs_path;
|
||||
const char* avatar_path;
|
||||
} tox_strings = {
|
||||
"tox",
|
||||
"download_path",
|
||||
"chatlogs_path",
|
||||
"avatar_path",
|
||||
};
|
||||
|
||||
/* alerts default on if invalid value */
|
||||
s->alerts = n == ALERTS_DISABLED ? ALERTS_DISABLED : ALERTS_ENABLED;
|
||||
static void tox_defaults(struct user_settings* settings)
|
||||
{
|
||||
strcpy(settings->download_path, "");
|
||||
strcpy(settings->chatlogs_path, "");
|
||||
strcpy(settings->avatar_path, "");
|
||||
}
|
||||
|
||||
static void uset_colours(struct user_settings *s, const char *val)
|
||||
{
|
||||
int n = atoi(val);
|
||||
#ifdef AUDIO
|
||||
static const struct audio_strings {
|
||||
const char* self;
|
||||
const char* input_device;
|
||||
const char* output_device;
|
||||
const char* VAD_treshold;
|
||||
} audio_strings = {
|
||||
"audio",
|
||||
"input_device",
|
||||
"output_device",
|
||||
"VAD_treshold",
|
||||
};
|
||||
|
||||
/* use default toxic colours if invalid value */
|
||||
s->colour_theme = n == NATIVE_COLS ? NATIVE_COLS : DFLT_COLS;
|
||||
static void audio_defaults(struct user_settings* settings)
|
||||
{
|
||||
settings->audio_in_dev = 0;
|
||||
settings->audio_out_dev = 0;
|
||||
settings->VAD_treshold = 40.0;
|
||||
}
|
||||
|
||||
#ifdef _SUPPORT_AUDIO
|
||||
|
||||
static void uset_ain_dev(struct user_settings *s, const char *val)
|
||||
{
|
||||
int n = atoi(val);
|
||||
|
||||
if (n < 0 || n > MAX_DEVICES)
|
||||
n = (long int) 0;
|
||||
|
||||
s->audio_in_dev = (long int) n;
|
||||
}
|
||||
|
||||
static void uset_aout_dev(struct user_settings *s, const char *val)
|
||||
{
|
||||
int n = atoi(val);
|
||||
|
||||
if (n < 0 || n > MAX_DEVICES)
|
||||
n = (long int) 0;
|
||||
|
||||
s->audio_out_dev = (long int) n;
|
||||
}
|
||||
|
||||
#endif /* _SUPPORT_AUDIO */
|
||||
|
||||
static void uset_hst_size(struct user_settings *s, const char *val)
|
||||
{
|
||||
int n = atoi(val);
|
||||
|
||||
/* if val is out of range use default history size */
|
||||
s->history_size = (n > MAX_HISTORY || n < MIN_HISTORY) ? DFLT_HST_SIZE : n;
|
||||
}
|
||||
|
||||
static void uset_dwnld_path(struct user_settings *s, const char *val)
|
||||
{
|
||||
memset(s->download_path, 0, sizeof(s->download_path));
|
||||
|
||||
if (val == NULL)
|
||||
return;
|
||||
|
||||
int len = strlen(val);
|
||||
|
||||
if (len >= sizeof(s->download_path) - 2) /* leave room for null and '/' */
|
||||
return;
|
||||
|
||||
FILE *fp = fopen(val, "r");
|
||||
|
||||
if (fp == NULL)
|
||||
return;
|
||||
|
||||
strcpy(s->download_path, val);
|
||||
|
||||
if (val[len - 1] != '/')
|
||||
strcat(s->download_path, "/");
|
||||
}
|
||||
|
||||
static void set_default_settings(struct user_settings *s)
|
||||
{
|
||||
/* see settings_values enum in settings.h for defaults */
|
||||
uset_autolog(s, "0");
|
||||
uset_time(s, "24");
|
||||
uset_alerts(s, "0");
|
||||
uset_colours(s, "0");
|
||||
uset_hst_size(s, "700");
|
||||
uset_dwnld_path(s, NULL);
|
||||
|
||||
#ifdef _SUPPORT_AUDIO
|
||||
uset_ain_dev(s, "0");
|
||||
uset_aout_dev(s, "0");
|
||||
#endif
|
||||
}
|
||||
|
||||
int settings_load(struct user_settings *s, char *path)
|
||||
{
|
||||
char *user_config_dir = get_user_config_dir();
|
||||
FILE *fp = NULL;
|
||||
char dflt_path[MAX_STR_SIZE];
|
||||
#ifdef SOUND_NOTIFY
|
||||
static const struct sound_strings {
|
||||
const char* self;
|
||||
const char* notif_error;
|
||||
const char* self_log_in;
|
||||
const char* self_log_out;
|
||||
const char* user_log_in;
|
||||
const char* user_log_out;
|
||||
const char* call_incoming;
|
||||
const char* call_outgoing;
|
||||
const char* generic_message;
|
||||
const char* transfer_pending;
|
||||
const char* transfer_completed;
|
||||
} sound_strings = {
|
||||
"sounds",
|
||||
"notif_error",
|
||||
"self_log_in",
|
||||
"self_log_out",
|
||||
"user_log_in",
|
||||
"user_log_out",
|
||||
"call_incoming",
|
||||
"call_outgoing",
|
||||
"generic_message",
|
||||
"transfer_pending",
|
||||
"transfer_completed",
|
||||
};
|
||||
#endif
|
||||
|
||||
if (path) {
|
||||
fp = fopen(path, "r");
|
||||
} else {
|
||||
snprintf(dflt_path, sizeof(dflt_path), "%s%stoxic.conf", user_config_dir, CONFIGDIR);
|
||||
fp = fopen(dflt_path, "r");
|
||||
static int key_parse(const char** bind){
|
||||
int len = strlen(*bind);
|
||||
|
||||
if (len > 5) {
|
||||
if(strncasecmp(*bind, "ctrl+", 5) == 0)
|
||||
return toupper(bind[0][5]) - 'A' + 1;
|
||||
}
|
||||
|
||||
free(user_config_dir);
|
||||
if (strncasecmp(*bind, "tab", 3) == 0)
|
||||
return T_KEY_TAB;
|
||||
|
||||
set_default_settings(s);
|
||||
if (strncasecmp(*bind, "page", 4) == 0)
|
||||
return len == 6 ? KEY_PPAGE : KEY_NPAGE;
|
||||
|
||||
if (fp == NULL && !path) {
|
||||
if ((fp = fopen(dflt_path, "w")) == NULL)
|
||||
return -1;
|
||||
} else if (fp == NULL && path) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int settings_load(struct user_settings *s, const char *patharg)
|
||||
{
|
||||
config_t cfg[1];
|
||||
config_setting_t *setting;
|
||||
const char *str = NULL;
|
||||
|
||||
/* Load default settings */
|
||||
ui_defaults(s);
|
||||
tox_defaults(s);
|
||||
key_defaults(s);
|
||||
|
||||
#ifdef AUDIO
|
||||
audio_defaults(s);
|
||||
#endif
|
||||
|
||||
config_init(cfg);
|
||||
|
||||
char path[MAX_STR_SIZE];
|
||||
|
||||
/* use default config file path */
|
||||
if (patharg == NULL) {
|
||||
char *user_config_dir = get_user_config_dir();
|
||||
snprintf(path, sizeof(path), "%s%stoxic.conf", user_config_dir, CONFIGDIR);
|
||||
free(user_config_dir);
|
||||
|
||||
/* make sure path exists or is created on first time running */
|
||||
if (!file_exists(path)) {
|
||||
FILE *fp = fopen(path, "w");
|
||||
|
||||
if (fp == NULL)
|
||||
return -1;
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
} else {
|
||||
snprintf(path, sizeof(path), "%s", patharg);
|
||||
}
|
||||
|
||||
if (!config_read_file(cfg, path)) {
|
||||
config_destroy(cfg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char line[MAX_STR_SIZE];
|
||||
/* ui */
|
||||
if ((setting = config_lookup(cfg, ui_strings.self)) != NULL) {
|
||||
config_setting_lookup_bool(setting, ui_strings.timestamps, &s->timestamps);
|
||||
|
||||
while (fgets(line, sizeof(line), fp)) {
|
||||
if (line[0] == '#' || !line[0])
|
||||
continue;
|
||||
|
||||
const char *key = strtok(line, ":");
|
||||
const char *val = strtok(NULL, ";");
|
||||
|
||||
if (key == NULL || val == NULL)
|
||||
continue;
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NUM_SETTINGS; ++i) {
|
||||
if (!strcmp(user_settings_list[i].key, key)) {
|
||||
(user_settings_list[i].func)(s, val);
|
||||
break;
|
||||
int time = 24;
|
||||
if ( config_setting_lookup_int(setting, ui_strings.time_format, &time) ) {
|
||||
if (time == 12) {
|
||||
snprintf(s->timestamp_format, sizeof(s->timestamp_format), "%s", "%I:%M:%S %p");
|
||||
snprintf(s->log_timestamp_format, sizeof(s->log_timestamp_format), "%s", "%Y/%m/%d [%I:%M:%S %p]");
|
||||
}
|
||||
}
|
||||
|
||||
if ( config_setting_lookup_string(setting, ui_strings.timestamp_format, &str) ) {
|
||||
snprintf(s->timestamp_format, sizeof(s->timestamp_format), "%s", str);
|
||||
}
|
||||
|
||||
if ( config_setting_lookup_string(setting, ui_strings.log_timestamp_format, &str) ) {
|
||||
snprintf(s->log_timestamp_format, sizeof(s->log_timestamp_format), "%s", str);
|
||||
}
|
||||
|
||||
config_setting_lookup_bool(setting, ui_strings.alerts, &s->alerts);
|
||||
config_setting_lookup_bool(setting, ui_strings.autolog, &s->autolog);
|
||||
config_setting_lookup_bool(setting, ui_strings.native_colors, &s->colour_theme);
|
||||
config_setting_lookup_int(setting, ui_strings.history_size, &s->history_size);
|
||||
config_setting_lookup_bool(setting, ui_strings.show_typing_self, &s->show_typing_self);
|
||||
config_setting_lookup_bool(setting, ui_strings.show_typing_other, &s->show_typing_other);
|
||||
config_setting_lookup_bool(setting, ui_strings.show_welcome_msg, &s->show_welcome_msg);
|
||||
|
||||
if ( config_setting_lookup_string(setting, ui_strings.line_join, &str) ) {
|
||||
snprintf(s->line_join, sizeof(s->line_join), "%s", str);
|
||||
}
|
||||
if ( config_setting_lookup_string(setting, ui_strings.line_quit, &str) ) {
|
||||
snprintf(s->line_quit, sizeof(s->line_quit), "%s", str);
|
||||
}
|
||||
if ( config_setting_lookup_string(setting, ui_strings.line_alert, &str) ) {
|
||||
snprintf(s->line_alert, sizeof(s->line_alert), "%s", str);
|
||||
}
|
||||
if ( config_setting_lookup_string(setting, ui_strings.line_normal, &str) ) {
|
||||
snprintf(s->line_normal, sizeof(s->line_normal), "%s", str);
|
||||
}
|
||||
|
||||
config_setting_lookup_bool (setting, ui_strings.mplex_away, &s->mplex_away);
|
||||
|
||||
if (config_setting_lookup_string (setting, ui_strings.mplex_away_note, &str)) {
|
||||
snprintf (s->mplex_away_note, sizeof (s->mplex_away_note), "%s", str);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
/* paths */
|
||||
if ((setting = config_lookup(cfg, tox_strings.self)) != NULL) {
|
||||
if ( config_setting_lookup_string(setting, tox_strings.download_path, &str) ) {
|
||||
snprintf(s->download_path, sizeof(s->download_path), "%s", str);
|
||||
int len = strlen(s->download_path);
|
||||
|
||||
/* make sure path ends with a '/' */
|
||||
if (len >= sizeof(s->download_path) - 2)
|
||||
s->download_path[0] = '\0';
|
||||
else if (s->download_path[len - 1] != '/')
|
||||
strcat(&s->download_path[len - 1], "/");
|
||||
}
|
||||
|
||||
if ( config_setting_lookup_string(setting, tox_strings.chatlogs_path, &str) ) {
|
||||
snprintf(s->chatlogs_path, sizeof(s->chatlogs_path), "%s", str);
|
||||
int len = strlen(s->chatlogs_path);
|
||||
|
||||
if (len >= sizeof(s->chatlogs_path) - 2)
|
||||
s->chatlogs_path[0] = '\0';
|
||||
else if (s->chatlogs_path[len - 1] != '/')
|
||||
strcat(&s->chatlogs_path[len - 1], "/");
|
||||
}
|
||||
|
||||
if ( config_setting_lookup_string(setting, tox_strings.avatar_path, &str) ) {
|
||||
snprintf(s->avatar_path, sizeof(s->avatar_path), "%s", str);
|
||||
int len = strlen(str);
|
||||
|
||||
if (len >= sizeof(s->avatar_path))
|
||||
s->avatar_path[0] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
/* keys */
|
||||
if ((setting = config_lookup(cfg, key_strings.self)) != NULL) {
|
||||
const char* tmp = NULL;
|
||||
if (config_setting_lookup_string(setting, key_strings.next_tab, &tmp))
|
||||
s->key_next_tab = key_parse(&tmp);
|
||||
if (config_setting_lookup_string(setting, key_strings.prev_tab, &tmp))
|
||||
s->key_prev_tab = key_parse(&tmp);
|
||||
if (config_setting_lookup_string(setting, key_strings.scroll_line_up, &tmp))
|
||||
s->key_scroll_line_up = key_parse(&tmp);
|
||||
if (config_setting_lookup_string(setting, key_strings.scroll_line_down, &tmp))
|
||||
s->key_scroll_line_down= key_parse(&tmp);
|
||||
if (config_setting_lookup_string(setting, key_strings.half_page_up, &tmp))
|
||||
s->key_half_page_up = key_parse(&tmp);
|
||||
if (config_setting_lookup_string(setting, key_strings.half_page_down, &tmp))
|
||||
s->key_half_page_down = key_parse(&tmp);
|
||||
if (config_setting_lookup_string(setting, key_strings.page_bottom, &tmp))
|
||||
s->key_page_bottom = key_parse(&tmp);
|
||||
if (config_setting_lookup_string(setting, key_strings.peer_list_up, &tmp))
|
||||
s->key_peer_list_up = key_parse(&tmp);
|
||||
if (config_setting_lookup_string(setting, key_strings.peer_list_down, &tmp))
|
||||
s->key_peer_list_down = key_parse(&tmp);
|
||||
if (config_setting_lookup_string(setting, key_strings.toggle_peerlist, &tmp))
|
||||
s->key_toggle_peerlist = key_parse(&tmp);
|
||||
}
|
||||
|
||||
#ifdef AUDIO
|
||||
if ((setting = config_lookup(cfg, audio_strings.self)) != NULL) {
|
||||
config_setting_lookup_int(setting, audio_strings.input_device, &s->audio_in_dev);
|
||||
s->audio_in_dev = s->audio_in_dev < 0 || s->audio_in_dev > MAX_DEVICES ? 0 : s->audio_in_dev;
|
||||
|
||||
config_setting_lookup_int(setting, audio_strings.output_device, &s->audio_out_dev);
|
||||
s->audio_out_dev = s->audio_out_dev < 0 || s->audio_out_dev > MAX_DEVICES ? 0 : s->audio_out_dev;
|
||||
|
||||
config_setting_lookup_float(setting, audio_strings.VAD_treshold, &s->VAD_treshold);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SOUND_NOTIFY
|
||||
if ((setting = config_lookup(cfg, sound_strings.self)) != NULL) {
|
||||
if ( (config_setting_lookup_string(setting, sound_strings.notif_error, &str) != CONFIG_TRUE) ||
|
||||
!set_sound(notif_error, str) ) {
|
||||
if (str && strcasecmp(str, NO_SOUND) != 0)
|
||||
set_sound(notif_error, PACKAGE_DATADIR "/sounds/ToxicError.wav");
|
||||
}
|
||||
|
||||
if ( !config_setting_lookup_string(setting, sound_strings.user_log_in, &str) ||
|
||||
!set_sound(user_log_in, str) ) {
|
||||
if (str && strcasecmp(str, NO_SOUND) != 0)
|
||||
set_sound(user_log_in, PACKAGE_DATADIR "/sounds/ToxicContactOnline.wav");
|
||||
}
|
||||
|
||||
if ( !config_setting_lookup_string(setting, sound_strings.user_log_out, &str) ||
|
||||
!set_sound(user_log_out, str) ) {
|
||||
if (str && strcasecmp(str, NO_SOUND) != 0)
|
||||
set_sound(user_log_out, PACKAGE_DATADIR "/sounds/ToxicContactOffline.wav");
|
||||
}
|
||||
|
||||
if ( !config_setting_lookup_string(setting, sound_strings.call_incoming, &str) ||
|
||||
!set_sound(call_incoming, str) ) {
|
||||
if (str && strcasecmp(str, NO_SOUND) != 0)
|
||||
set_sound(call_incoming, PACKAGE_DATADIR "/sounds/ToxicIncomingCall.wav");
|
||||
}
|
||||
|
||||
if ( !config_setting_lookup_string(setting, sound_strings.call_outgoing, &str) ||
|
||||
!set_sound(call_outgoing, str) ) {
|
||||
if (str && strcasecmp(str, NO_SOUND) != 0)
|
||||
set_sound(call_outgoing, PACKAGE_DATADIR "/sounds/ToxicOutgoingCall.wav");
|
||||
}
|
||||
|
||||
if ( !config_setting_lookup_string(setting, sound_strings.generic_message, &str) ||
|
||||
!set_sound(generic_message, str) ) {
|
||||
if (str && strcasecmp(str, NO_SOUND) != 0)
|
||||
set_sound(generic_message, PACKAGE_DATADIR "/sounds/ToxicRecvMessage.wav");
|
||||
}
|
||||
|
||||
if ( !config_setting_lookup_string(setting, sound_strings.transfer_pending, &str) ||
|
||||
!set_sound(transfer_pending, str) ) {
|
||||
if (str && strcasecmp(str, NO_SOUND) != 0)
|
||||
set_sound(transfer_pending, PACKAGE_DATADIR "/sounds/ToxicTransferStart.wav");
|
||||
}
|
||||
|
||||
if ( !config_setting_lookup_string(setting, sound_strings.transfer_completed, &str) ||
|
||||
!set_sound(transfer_completed, str) ) {
|
||||
if (str && strcasecmp(str, NO_SOUND) != 0)
|
||||
set_sound(transfer_completed, PACKAGE_DATADIR "/sounds/ToxicTransferComplete.wav");
|
||||
}
|
||||
}
|
||||
else {
|
||||
set_sound(notif_error, PACKAGE_DATADIR "/sounds/ToxicError.wav");
|
||||
set_sound(user_log_in, PACKAGE_DATADIR "/sounds/ToxicContactOnline.wav");
|
||||
set_sound(user_log_out, PACKAGE_DATADIR "/sounds/ToxicContactOffline.wav");
|
||||
set_sound(call_incoming, PACKAGE_DATADIR "/sounds/ToxicIncomingCall.wav");
|
||||
set_sound(call_outgoing, PACKAGE_DATADIR "/sounds/ToxicOutgoingCall.wav");
|
||||
set_sound(generic_message, PACKAGE_DATADIR "/sounds/ToxicRecvMessage.wav");
|
||||
set_sound(transfer_pending, PACKAGE_DATADIR "/sounds/ToxicTransferStart.wav");
|
||||
set_sound(transfer_completed, PACKAGE_DATADIR "/sounds/ToxicTransferComplete.wav");
|
||||
}
|
||||
#endif
|
||||
|
||||
config_destroy(cfg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -20,29 +20,58 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _settings_h
|
||||
#define _settings_h
|
||||
#ifndef SETTINGS_H
|
||||
#define SETTINGS_H
|
||||
|
||||
#include "toxic.h"
|
||||
#include <limits.h>
|
||||
|
||||
#ifdef _SUPPORT_AUDIO
|
||||
#define NUM_SETTINGS 8
|
||||
#else
|
||||
#define NUM_SETTINGS 6
|
||||
#endif
|
||||
#include <tox/tox.h>
|
||||
|
||||
/* Represents line_* hints max strlen */
|
||||
#define LINE_HINT_MAX 3
|
||||
|
||||
/* holds user setting values */
|
||||
struct user_settings {
|
||||
int autolog; /* boolean */
|
||||
int alerts; /* boolean */
|
||||
int time; /* 12 or 24 */
|
||||
|
||||
int timestamps; /* boolean */
|
||||
char timestamp_format[TIME_STR_SIZE];
|
||||
char log_timestamp_format[TIME_STR_SIZE];
|
||||
|
||||
int colour_theme; /* boolean (0 for default toxic colours) */
|
||||
int history_size; /* int between MIN_HISTORY and MAX_HISTORY */
|
||||
char download_path[MAX_STR_SIZE];
|
||||
int show_typing_self; /* boolean */
|
||||
int show_typing_other; /* boolean */
|
||||
int show_welcome_msg; /* boolean */
|
||||
|
||||
#ifdef _SUPPORT_AUDIO
|
||||
long int audio_in_dev;
|
||||
long int audio_out_dev;
|
||||
char line_join[LINE_HINT_MAX + 1];
|
||||
char line_quit[LINE_HINT_MAX + 1];
|
||||
char line_alert[LINE_HINT_MAX + 1];
|
||||
char line_normal[LINE_HINT_MAX + 1];
|
||||
|
||||
char download_path[PATH_MAX];
|
||||
char chatlogs_path[PATH_MAX];
|
||||
char avatar_path[PATH_MAX];
|
||||
|
||||
int key_next_tab;
|
||||
int key_prev_tab;
|
||||
int key_scroll_line_up;
|
||||
int key_scroll_line_down;
|
||||
int key_half_page_up;
|
||||
int key_half_page_down;
|
||||
int key_page_bottom;
|
||||
int key_peer_list_up;
|
||||
int key_peer_list_down;
|
||||
int key_toggle_peerlist;
|
||||
|
||||
int mplex_away; /* boolean (1 for reaction to terminal attach/detach) */
|
||||
char mplex_away_note [TOX_MAX_STATUS_MESSAGE_LENGTH];
|
||||
|
||||
#ifdef AUDIO
|
||||
int audio_in_dev;
|
||||
int audio_out_dev;
|
||||
double VAD_treshold;
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -50,18 +79,34 @@ enum {
|
||||
AUTOLOG_OFF = 0,
|
||||
AUTOLOG_ON = 1,
|
||||
|
||||
TIME_24 = 24,
|
||||
TIME_12 = 12,
|
||||
TIMESTAMPS_OFF = 0,
|
||||
TIMESTAMPS_ON = 1,
|
||||
|
||||
ALERTS_DISABLED = 1,
|
||||
ALERTS_ENABLED = 0,
|
||||
ALERTS_DISABLED = 0,
|
||||
ALERTS_ENABLED = 1,
|
||||
|
||||
NATIVE_COLS = 1,
|
||||
DFLT_COLS = 0,
|
||||
NATIVE_COLS = 1,
|
||||
|
||||
SHOW_TYPING_OFF = 0,
|
||||
SHOW_TYPING_ON = 1,
|
||||
|
||||
SHOW_WELCOME_MSG_OFF = 0,
|
||||
SHOW_WELCOME_MSG_ON = 1,
|
||||
|
||||
DFLT_HST_SIZE = 700,
|
||||
|
||||
MPLEX_OFF = 0,
|
||||
MPLEX_ON = 1,
|
||||
} settings_values;
|
||||
|
||||
int settings_load(struct user_settings *s, char *path);
|
||||
#define LINE_JOIN "-->"
|
||||
#define LINE_QUIT "<--"
|
||||
#define LINE_ALERT "-!-"
|
||||
#define LINE_NORMAL "---"
|
||||
#define TIMESTAMP_DEFAULT "%H:%M:%S"
|
||||
#define LOG_TIMESTAMP_DEFAULT "%Y/%m/%d [%H:%M:%S]"
|
||||
#define MPLEX_AWAY_NOTE "Detached from screen"
|
||||
|
||||
#endif /* #define _settings_h */
|
||||
int settings_load(struct user_settings *s, const char *patharg);
|
||||
#endif /* #define SETTINGS_H */
|
||||
|
||||
398
src/term_mplex.c
Normal file
398
src/term_mplex.c
Normal file
@@ -0,0 +1,398 @@
|
||||
/* term_mplex.c
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2015 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <limits.h> /* PATH_MAX */
|
||||
#include <stdio.h> /* fgets, popen, pclose */
|
||||
#include <stdlib.h> /* malloc, realloc, free, getenv */
|
||||
#include <string.h> /* strlen, strcpy, strstr, strchr, strrchr, strcat, strncmp */
|
||||
|
||||
#include <pthread.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <tox/tox.h>
|
||||
|
||||
#include "global_commands.h"
|
||||
#include "windows.h"
|
||||
#include "term_mplex.h"
|
||||
#include "toxic.h"
|
||||
#include "settings.h"
|
||||
|
||||
extern struct ToxWindow *prompt;
|
||||
extern struct user_settings *user_settings;
|
||||
extern struct Winthread Winthread;
|
||||
|
||||
#if defined(PATH_MAX) && PATH_MAX > 512
|
||||
#define BUFFER_SIZE PATH_MAX
|
||||
#else
|
||||
#define BUFFER_SIZE 512
|
||||
#endif
|
||||
|
||||
#define PATH_SEP_S "/"
|
||||
#define PATH_SEP_C '/'
|
||||
|
||||
typedef enum
|
||||
{
|
||||
MPLEX_NONE,
|
||||
MPLEX_SCREEN,
|
||||
MPLEX_TMUX,
|
||||
} mplex_status;
|
||||
|
||||
/* used for:
|
||||
- storing screen socket name
|
||||
- storing tmux session number in string form */
|
||||
static char mplex_data [BUFFER_SIZE];
|
||||
|
||||
static char buffer [BUFFER_SIZE];
|
||||
|
||||
/* Differentiates between mplex auto-away and manual-away */
|
||||
static bool auto_away_active = false;
|
||||
|
||||
static mplex_status mplex = MPLEX_NONE;
|
||||
static TOX_USER_STATUS prev_status = TOX_USER_STATUS_NONE;
|
||||
static char prev_note [TOX_MAX_STATUS_MESSAGE_LENGTH] = "";
|
||||
|
||||
/* mutex for access to status data, for sync between:
|
||||
- user command /status from ncurses thread
|
||||
- auto-away POSIX timer, which runs from a separate thread
|
||||
after init, should be accessed only by cmd_status()
|
||||
*/
|
||||
static pthread_mutex_t status_lock;
|
||||
static pthread_t mplex_tid;
|
||||
|
||||
void lock_status ()
|
||||
{
|
||||
pthread_mutex_lock (&status_lock);
|
||||
}
|
||||
|
||||
void unlock_status ()
|
||||
{
|
||||
pthread_mutex_unlock (&status_lock);
|
||||
}
|
||||
|
||||
static char *read_into_dyn_buffer (FILE *stream)
|
||||
{
|
||||
const char *input_ptr = NULL;
|
||||
char *dyn_buffer = NULL;
|
||||
int dyn_buffer_size = 1; /* account for the \0 */
|
||||
|
||||
while ((input_ptr = fgets (buffer, BUFFER_SIZE, stream)) != NULL)
|
||||
{
|
||||
int length = dyn_buffer_size + strlen (input_ptr);
|
||||
if (dyn_buffer)
|
||||
dyn_buffer = (char*) realloc (dyn_buffer, length);
|
||||
else
|
||||
dyn_buffer = (char*) malloc (length);
|
||||
strcpy (dyn_buffer + dyn_buffer_size - 1, input_ptr);
|
||||
dyn_buffer_size = length;
|
||||
}
|
||||
|
||||
return dyn_buffer;
|
||||
}
|
||||
|
||||
static char *extract_socket_path (const char *info)
|
||||
{
|
||||
const char *search_str = " Socket";
|
||||
const char *pos = strstr (info, search_str);
|
||||
char *end = NULL;
|
||||
char* path = NULL;
|
||||
|
||||
if (!pos)
|
||||
return NULL;
|
||||
|
||||
pos += strlen (search_str);
|
||||
pos = strchr (pos, PATH_SEP_C);
|
||||
if (!pos)
|
||||
return NULL;
|
||||
|
||||
end = strchr (pos, '\n');
|
||||
if (!end)
|
||||
return NULL;
|
||||
|
||||
*end = '\0';
|
||||
end = strrchr (pos, '.');
|
||||
if (!end)
|
||||
return NULL;
|
||||
|
||||
path = (char*) malloc (end - pos + 1);
|
||||
*end = '\0';
|
||||
return strcpy (path, pos);
|
||||
}
|
||||
|
||||
static int detect_gnu_screen ()
|
||||
{
|
||||
FILE *session_info_stream = NULL;
|
||||
char *socket_name = NULL, *socket_path = NULL;
|
||||
char *dyn_buffer = NULL;
|
||||
|
||||
socket_name = getenv ("STY");
|
||||
if (!socket_name)
|
||||
goto nomplex;
|
||||
|
||||
session_info_stream = popen ("env LC_ALL=C screen -ls", "r");
|
||||
if (!session_info_stream)
|
||||
goto nomplex;
|
||||
|
||||
dyn_buffer = read_into_dyn_buffer (session_info_stream);
|
||||
if (!dyn_buffer)
|
||||
goto nomplex;
|
||||
|
||||
pclose (session_info_stream);
|
||||
session_info_stream = NULL;
|
||||
|
||||
socket_path = extract_socket_path (dyn_buffer);
|
||||
if (!socket_path)
|
||||
goto nomplex;
|
||||
|
||||
free (dyn_buffer);
|
||||
dyn_buffer = NULL;
|
||||
|
||||
if (strlen(socket_path) + strlen(PATH_SEP_S) + strlen(socket_name) >= sizeof(mplex_data))
|
||||
goto nomplex;
|
||||
|
||||
strcpy (mplex_data, socket_path);
|
||||
strcat (mplex_data, PATH_SEP_S);
|
||||
strcat (mplex_data, socket_name);
|
||||
free (socket_path);
|
||||
socket_path = NULL;
|
||||
|
||||
mplex = MPLEX_SCREEN;
|
||||
return 1;
|
||||
|
||||
nomplex:
|
||||
if (session_info_stream)
|
||||
pclose (session_info_stream);
|
||||
if (dyn_buffer)
|
||||
free (dyn_buffer);
|
||||
if (socket_path)
|
||||
free(socket_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int detect_tmux ()
|
||||
{
|
||||
char *tmux_env = getenv ("TMUX"), *pos;
|
||||
if (!tmux_env)
|
||||
return 0;
|
||||
|
||||
/* find second separator */
|
||||
pos = strrchr (tmux_env, ',');
|
||||
if (!pos)
|
||||
return 0;
|
||||
|
||||
/* store the session number string for later use */
|
||||
snprintf (mplex_data, sizeof(mplex_data), "%s", pos + 1);
|
||||
mplex = MPLEX_TMUX;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Checks whether a terminal multiplexer (mplex) is present, and finds
|
||||
its unix socket.
|
||||
|
||||
GNU screen and tmux are supported.
|
||||
|
||||
Returns 1 if present, 0 otherwise. This value can be used to determine
|
||||
whether an auto-away detection timer is needed.
|
||||
*/
|
||||
static int detect_mplex ()
|
||||
{
|
||||
/* try screen, and if fails try tmux */
|
||||
return detect_gnu_screen () || detect_tmux ();
|
||||
}
|
||||
|
||||
/* Detects gnu screen session attached/detached by examining permissions of
|
||||
the session's unix socket.
|
||||
*/
|
||||
static int gnu_screen_is_detached ()
|
||||
{
|
||||
if (mplex != MPLEX_SCREEN)
|
||||
return 0;
|
||||
|
||||
struct stat sb;
|
||||
if (stat (mplex_data, &sb) != 0)
|
||||
return 0;
|
||||
|
||||
/* execution permission (x) means attached */
|
||||
return ! (sb.st_mode & S_IXUSR);
|
||||
}
|
||||
|
||||
/* Detects tmux attached/detached by getting session data and finding the
|
||||
current session's entry. An attached entry ends with "(attached)". Example:
|
||||
|
||||
$ tmux list-sessions
|
||||
0: 1 windows (created Mon Mar 2 21:48:29 2015) [80x23] (attached)
|
||||
1: 2 windows (created Mon Mar 2 21:48:43 2015) [80x23]
|
||||
|
||||
In this example, session 0 is attached and session 1 is detached.
|
||||
*/
|
||||
static int tmux_is_detached ()
|
||||
{
|
||||
if (mplex != MPLEX_TMUX)
|
||||
return 0;
|
||||
|
||||
FILE *session_info_stream = NULL;
|
||||
char *dyn_buffer = NULL, *search_str = NULL;
|
||||
char *entry_pos, *nl_pos, *attached_pos;
|
||||
const int numstr_len = strlen (mplex_data);
|
||||
|
||||
session_info_stream = popen ("env LC_ALL=C tmux list-sessions", "r");
|
||||
if (!session_info_stream)
|
||||
goto fail;
|
||||
|
||||
dyn_buffer = read_into_dyn_buffer (session_info_stream);
|
||||
if (!dyn_buffer)
|
||||
goto fail;
|
||||
|
||||
pclose (session_info_stream);
|
||||
session_info_stream = NULL;
|
||||
|
||||
/* prepare search string, for finding the current session's entry */
|
||||
search_str = (char*) malloc (numstr_len + 4);
|
||||
search_str[0] = '\n';
|
||||
strcpy (search_str + 1, mplex_data);
|
||||
strcat (search_str, ": ");
|
||||
|
||||
/* do the search */
|
||||
if (strncmp (dyn_buffer, search_str + 1, numstr_len + 2) == 0)
|
||||
entry_pos = dyn_buffer;
|
||||
else
|
||||
entry_pos = strstr (dyn_buffer, search_str);
|
||||
|
||||
if (! entry_pos)
|
||||
goto fail;
|
||||
|
||||
/* find the next \n and look for the "(attached)" before it */
|
||||
nl_pos = strchr (entry_pos + 1, '\n');
|
||||
attached_pos = strstr (entry_pos + 1, "(attached)\n");
|
||||
|
||||
free (search_str);
|
||||
search_str = NULL;
|
||||
|
||||
free (dyn_buffer);
|
||||
dyn_buffer = NULL;
|
||||
|
||||
return attached_pos == NULL || attached_pos > nl_pos;
|
||||
|
||||
fail:
|
||||
if (session_info_stream)
|
||||
pclose (session_info_stream);
|
||||
if (dyn_buffer)
|
||||
free (dyn_buffer);
|
||||
if (search_str)
|
||||
free (search_str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Checks whether there is a terminal multiplexer present, but in detached
|
||||
state. Returns 1 if detached, 0 if attached or if there is no terminal
|
||||
multiplexer.
|
||||
|
||||
If detect_mplex_socket() failed to find a mplex, there is no need to call
|
||||
this function. If it did find one, this function can be used to periodically
|
||||
sample its state and update away status according to attached/detached state
|
||||
of the mplex.
|
||||
*/
|
||||
static int mplex_is_detached ()
|
||||
{
|
||||
return gnu_screen_is_detached () || tmux_is_detached ();
|
||||
}
|
||||
|
||||
static void mplex_timer_handler (Tox *m)
|
||||
{
|
||||
TOX_USER_STATUS current_status, new_status;
|
||||
const char *new_note;
|
||||
|
||||
if (mplex == MPLEX_NONE)
|
||||
return;
|
||||
|
||||
int detached = mplex_is_detached ();
|
||||
|
||||
pthread_mutex_lock (&Winthread.lock);
|
||||
current_status = tox_self_get_status (m);
|
||||
pthread_mutex_unlock (&Winthread.lock);
|
||||
|
||||
if (auto_away_active && current_status == TOX_USER_STATUS_AWAY && !detached)
|
||||
{
|
||||
auto_away_active = false;
|
||||
new_status = prev_status;
|
||||
new_note = prev_note;
|
||||
}
|
||||
else
|
||||
if (current_status == TOX_USER_STATUS_NONE && detached)
|
||||
{
|
||||
auto_away_active = true;
|
||||
prev_status = current_status;
|
||||
new_status = TOX_USER_STATUS_AWAY;
|
||||
pthread_mutex_lock (&Winthread.lock);
|
||||
size_t slen = tox_self_get_status_message_size(m);
|
||||
tox_self_get_status_message (m, (uint8_t*) prev_note);
|
||||
prev_note[slen] = '\0';
|
||||
pthread_mutex_unlock (&Winthread.lock);
|
||||
new_note = user_settings->mplex_away_note;
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
||||
char argv[3][MAX_STR_SIZE];
|
||||
strcpy (argv[0], "/status");
|
||||
strcpy (argv[1], (new_status == TOX_USER_STATUS_AWAY ? "away" :
|
||||
new_status == TOX_USER_STATUS_BUSY ? "busy" : "online"));
|
||||
argv[2][0] = '\"';
|
||||
strcpy (argv[2] + 1, new_note);
|
||||
strcat (argv[2], "\"");
|
||||
pthread_mutex_lock (&Winthread.lock);
|
||||
cmd_status (prompt->chatwin->history, prompt, m, 2, argv);
|
||||
pthread_mutex_unlock (&Winthread.lock);
|
||||
}
|
||||
|
||||
/* Time in seconds between calls to mplex_timer_handler */
|
||||
#define MPLEX_TIMER_INTERVAL 5
|
||||
|
||||
void *mplex_timer_thread(void *data)
|
||||
{
|
||||
Tox *m = (Tox *) data;
|
||||
|
||||
while (true) {
|
||||
sleep(MPLEX_TIMER_INTERVAL);
|
||||
mplex_timer_handler(m);
|
||||
}
|
||||
}
|
||||
|
||||
int init_mplex_away_timer (Tox *m)
|
||||
{
|
||||
if (! detect_mplex ())
|
||||
return 0;
|
||||
|
||||
if (! user_settings->mplex_away)
|
||||
return 0;
|
||||
|
||||
/* status access mutex */
|
||||
if (pthread_mutex_init (&status_lock, NULL) != 0)
|
||||
return -1;
|
||||
|
||||
if (pthread_create(&mplex_tid, NULL, mplex_timer_thread, (void *) m) != 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
35
src/term_mplex.h
Normal file
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 */
|
||||
1212
src/toxic.c
1212
src/toxic.c
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user