mirror of
				https://github.com/Tha14/toxic.git
				synced 2025-10-31 18:56:51 +01:00 
			
		
		
		
	Compare commits
	
		
			1310 Commits
		
	
	
		
			0.2.6.1
			...
			TokTok-mas
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | ae94bc593b | ||
|  | 81eb58532e | ||
|  | 8464ea9a7a | ||
|  | b77bff35a1 | ||
|  | eb964b64c2 | ||
|  | 2ff9d29491 | ||
|  | 2640919318 | ||
|  | 2fcbc4fa1c | ||
|  | 4330bf5867 | ||
|  | 3f1b7cdd26 | ||
|  | 1e985c1456 | ||
|  | 61740bda85 | ||
|  | 0d8e6d713e | ||
|  | 39e4ff8bd6 | ||
|  | 0434ac186a | ||
|  | 8d9d51640c | ||
|  | c4c0c0d1f4 | ||
|  | 3f2826bd66 | ||
|  | 7b7ea0e386 | ||
|  | d35a38735b | ||
|  | f0c4906fdc | ||
|  | 56ba61e061 | ||
|  | 898d89e95a | ||
|  | 1fd1e27bdf | ||
|  | 8e84ac58d4 | ||
|  | 9d65997871 | ||
|  | da2889f3ab | ||
|  | 312b38d253 | ||
|  | 0554bf0240 | ||
|  | 53a7530e8a | ||
|  | 41be04a142 | ||
|  | 31f36318a2 | ||
|  | f882fdf608 | ||
|  | 7e1e410307 | ||
|  | c135c812c2 | ||
|  | 6c239193ab | ||
|  | de7db08352 | ||
|  | ba5ded9bc2 | ||
|  | 4581dee4fc | ||
|  | d75d6e8b60 | ||
|  | 142ce642f0 | ||
|  | 7dead5ec96 | ||
|  | ddcf224db2 | ||
|  | daf794c4a2 | ||
|  | dac0124f0f | ||
|  | 15b7a30925 | ||
|  | 77ab71f26f | ||
|  | 68e1ba312d | ||
|  | 752fc6d619 | ||
|  | 16bcb27ca7 | ||
|  | 71d7d355a6 | ||
|  | 4188b392cc | ||
|  | 811fbfbb1e | ||
|  | 32eb7d3040 | ||
|  | 42763905d7 | ||
|  | f64300d1d6 | ||
|  | 1a723f0e8e | ||
|  | a86884c40e | ||
|  | 3f02e119f4 | ||
|  | 1bbd50aac7 | ||
|  | e7a0c32a68 | ||
|  | 7560bc9547 | ||
|  | 2b43340c90 | ||
|  | ff1620c923 | ||
|  | 1303053a27 | ||
|  | 91f194c821 | ||
|  | 478762f76c | ||
|  | 4d96d6a753 | ||
|  | 3cdcfbf4e5 | ||
|  | 4c302da503 | ||
|  | 26b5fe8f9d | ||
|  | 22d60232fb | ||
|  | e428879beb | ||
|  | 3015138a5a | ||
|  | 9c06ad608b | ||
|  | 015dbd9a96 | ||
|  | a7466c3142 | ||
|  | f012007cc4 | ||
|  | dcf3baf60f | ||
|  | 4bda799a4b | ||
|  | bdeae33d48 | ||
|  | 47591d5298 | ||
|  | b5ace27a3e | ||
|  | b334622d36 | ||
|  | 4bfb344caa | ||
|  | 16d96d6faf | ||
|  | 0ab2bad226 | ||
|  | 68db926f9f | ||
|  | b270c1e8b7 | ||
|  | e7142e49fd | ||
|  | 610906d07f | ||
|  | 6f72a191ba | ||
|  | dd5fa236ae | ||
|  | 51e1ab94b3 | ||
|  | ddc8c53abf | ||
|  | 46513017e3 | ||
|  | 98cb7f58c0 | ||
|  | 206bf407fd | ||
|  | 0a8ac4de3b | ||
|  | 87d54acad0 | ||
|  | 45ff6d8bac | ||
|  | 437dd8baeb | ||
|  | b080236ee5 | ||
|  | 116bff8cef | ||
|  | ddeca171a0 | ||
|  | 127f9462e0 | ||
|  | 4b5a9abbd4 | ||
|  | bb2257973e | ||
|  | 12b9cd2386 | ||
|  | 2cbe8fa880 | ||
|  | 2e39bee05a | ||
|  | 05eda76643 | ||
|  | f7b73af9a7 | ||
|  | 73aaa44d12 | ||
|  | e4abd8b36b | ||
|  | 9e3d4f3889 | ||
|  | b7d67c1d86 | ||
|  | c4a11f8dc7 | ||
|  | d18cc8cbc2 | ||
|  | ce6d4861fb | ||
|  | 8f0e6026f0 | ||
|  | 258736995d | ||
|  | 56e03a3f8b | ||
|  | b6c746b5f5 | ||
|  | 03673cbced | ||
|  | 0fea930c24 | ||
|  | 94d22a8853 | ||
|  | 63cc23401a | ||
|  | f90a774470 | ||
|  | e7c5fbc873 | ||
|  | d62902ffb3 | ||
|  | bebff3be0e | ||
|  | 2be4847b53 | ||
|  | 4557614443 | ||
|  | 5b30ecf2e4 | ||
|  | 2413ad2b59 | ||
|  | 52855b805a | ||
|  | 20b5e46850 | ||
|  | f2b796940e | ||
|  | a37bf300f9 | ||
|  | cb524dcbc3 | ||
|  | 4144b868ce | ||
|  | af11f16bef | ||
|  | 1d27a496ef | ||
|  | 32bd9dc1a7 | ||
|  | 3cd2bc7e3c | ||
|  | 43ca840658 | ||
|  | 685837357b | ||
|  | 812a13b0fb | ||
|  | 641fa471d2 | ||
|  | 8d5755f2d8 | ||
|  | af510b6666 | ||
|  | 46f646afcf | ||
|  | 68ce17a57f | ||
|  | a69fad15c1 | ||
|  | 7621fe9a62 | ||
|  | f6d9bc3a74 | ||
|  | 29aea0b42c | ||
|  | 815c29ee31 | ||
|  | 3917f664bf | ||
|  | a223329815 | ||
|  | 3fec11d5f9 | ||
|  | 221edb0012 | ||
|  | 2710ab6034 | ||
|  | bc3ffac0ba | ||
|  | 29f55c5277 | ||
|  | a290f0f7f8 | ||
|  | 5cd196a769 | ||
|  | b14d983a8c | ||
|  | 51f1daeec8 | ||
|  | b799c6a8d7 | ||
|  | b9f9546e2b | ||
|  | 846bc4613e | ||
|  | a5a1f6015d | ||
|  | fe6a7074ea | ||
|  | db7c9fe426 | ||
|  | b1d8ab102f | ||
|  | 0bd5b4ddee | ||
|  | c387df35f8 | ||
|  | 351a50c214 | ||
|  | 93175314b5 | ||
|  | b905a1a3c5 | ||
|  | c4386b195f | ||
|  | ed1e617380 | ||
|  | 1382adb1f6 | ||
|  | ecf1c317b7 | ||
|  | cf0b99f1e5 | ||
|  | 3605a296a9 | ||
|  | 9375d220f9 | ||
|  | 8f94b0a218 | ||
|  | 85a0becbf9 | ||
|  | fec36ad9e6 | ||
|  | ecdf6f01d2 | ||
|  | e1bfa30769 | ||
|  | ebcbc7497b | ||
|  | e844ece28b | ||
|  | 8508451ba6 | ||
|  | 5cc83a7cb5 | ||
|  | febc725763 | ||
|  | f2c116feb3 | ||
|  | 52dd60dc86 | ||
|  | 80c0500299 | ||
|  | ab490d28b4 | ||
|  | a9f7f85617 | ||
|  | 1bfc1ba371 | ||
|  | 2ede39369a | ||
|  | 922c184195 | ||
|  | 56a9571509 | ||
|  | 0136f22076 | ||
|  | c4ace288af | ||
|  | 6d3fbfee59 | ||
|  | be5e7906da | ||
|  | 369f26932e | ||
|  | 22ea522baf | ||
|  | 4f60d546e6 | ||
|  | 76d1eafdc0 | ||
|  | 37912f2d88 | ||
|  | 09710327c5 | ||
|  | acee4615f8 | ||
|  | 5ed26eda9b | ||
|  | 6d2b90ac9f | ||
|  | 02ea0fac44 | ||
|  | 7d3d129624 | ||
|  | b3ed8bc35c | ||
|  | 90210daca7 | ||
|  | 0e13a1f1bc | ||
|  | 09e2690211 | ||
|  | e65e3af274 | ||
|  | c6c60d018e | ||
|  | 451d4ced80 | ||
|  | 7a7402ff86 | ||
|  | 600e013adc | ||
|  | 1d71e2eb18 | ||
|  | f858714edd | ||
|  | 4df44a7274 | ||
|  | a26ed9d28f | ||
|  | 2bd5083b8f | ||
|  | 8805f694b9 | ||
|  | 71040355fd | ||
|  | 6bc5d8c543 | ||
|  | abb39ea6b5 | ||
|  | 15846d2b50 | ||
|  | 958df9f2e8 | ||
|  | 2fd43aebee | ||
|  | 34c29745cc | ||
|  | da6fe41d75 | ||
|  | e17fa89d8f | ||
|  | f056f13329 | ||
|  | 3515623159 | ||
|  | 2194b9e259 | ||
|  | c24e1bd2b8 | ||
|  | 38ec96e96a | ||
|  | d2b572ede1 | ||
|  | 703d5419a3 | ||
|  | 221d761ff4 | ||
|  | 151f5f0c51 | ||
|  | 4f6c603543 | ||
|  | a009f11c0c | ||
|  | 1f8c11a33a | ||
|  | 5e20e6b279 | ||
|  | 1f02bb2be5 | ||
|  | 98154b3cba | ||
|  | 379ad9e116 | ||
|  | cb21672600 | ||
|  | 4019395f44 | ||
|  | ee084c572c | ||
|  | 41a8401ac5 | ||
|  | d8a3f7de4c | ||
|  | c425aa2f27 | ||
|  | 94e026d114 | ||
|  | f89638635a | ||
|  | 402b86687f | ||
|  | 5b1b420ac0 | ||
|  | 62ec514f17 | ||
|  | f893dd755f | ||
|  | 9aedcf7753 | ||
|  | d3effa26b5 | ||
|  | 2ec180789b | ||
|  | 9f74d3a3a8 | ||
|  | 9fcbc3bde0 | ||
|  | cf16849b37 | ||
|  | 32442b6286 | ||
|  | 50f227418b | ||
|  | fc06a625a6 | ||
|  | 70bd39eb74 | ||
|  | 4e0e322e32 | ||
|  | e73ac9b6a4 | ||
|  | bcda6e476e | ||
|  | 5b29ce7132 | ||
|  | f43f644451 | ||
|  | d6fdac9739 | ||
|  | c6a2bb8a90 | ||
|  | 04576fea7e | ||
|  | e6f839f9ac | ||
|  | eb02424f8a | ||
|  | 2e609c46f6 | ||
|  | a474e3bf39 | ||
|  | 93835f0455 | ||
|  | ac6d8ff89c | ||
|  | 88e74224ed | ||
|  | deccaec40e | ||
|  | 4419be36e8 | ||
|  | b34b51e8c1 | ||
|  | 74416b4b58 | ||
|  | 675712cea0 | ||
|  | 36feebfe8d | ||
|  | 3fe9abd84d | ||
|  | fd6432c727 | ||
|  | 1feffcc2f0 | ||
|  | 6bba3cb9e2 | ||
|  | 3cb6db3d60 | ||
|  | 77238eeadf | ||
|  | 88270827a9 | ||
|  | aade65bfe1 | ||
|  | b24c5d8cf8 | ||
|  | 9f0feb7223 | ||
|  | 74c1eef1d1 | ||
|  | 65c07a57db | ||
|  | ab99c1ac73 | ||
|  | 05f5f16af3 | ||
|  | d16be574f3 | ||
|  | 7e0b8b4870 | ||
|  | 39c4b7ecdd | ||
|  | c5d9aca3e1 | ||
|  | fa0e645a79 | ||
|  | 14a8bdb874 | ||
|  | 93a73cbef2 | ||
|  | 6aab9a79d8 | ||
|  | dfff777283 | ||
|  | a95fc7824c | ||
|  | f707dce2da | ||
|  | 0d07d14b13 | ||
|  | 6cc1525daa | ||
|  | 49f5efaab0 | ||
|  | a5e5e98afc | ||
|  | 4ab99c73a0 | ||
|  | e02cf1bb7d | ||
|  | 9751cfc407 | ||
|  | 36963a5b38 | ||
|  | 7cf9c37aef | ||
|  | 2b4b8c0289 | ||
|  | 368a1465ec | ||
|  | fea317ee24 | ||
|  | 8584feab80 | ||
|  | f7e48d294e | ||
|  | 928f25bd89 | ||
|  | 941ac1d951 | ||
|  | 7af9327b37 | ||
|  | 6b97df2615 | ||
|  | cea5f1fe04 | ||
|  | abfdbfe468 | ||
|  | 462cfca175 | ||
|  | db410cb01e | ||
|  | a920f3edfe | ||
|  | 2c3921a9fb | ||
|  | f295352495 | ||
|  | ffcc804efe | ||
|  | 69be1bc398 | ||
|  | b4464eda4d | ||
|  | 28dd43608d | ||
|  | 11701d22a1 | ||
|  | 4e2db756be | ||
|  | 19cfe3d393 | ||
|  | c546df3917 | ||
|  | ed0a4fb3b8 | ||
|  | 271ca08eb2 | ||
|  | 0e79b8a076 | ||
|  | 1606d01158 | ||
|  | c8a9ac21f3 | ||
|  | e91aaf6c73 | ||
|  | 619fdc1098 | ||
|  | b7e613de32 | ||
|  | 929fca3de1 | ||
|  | b67792f9f2 | ||
|  | 96162bf254 | ||
|  | 8a66c3fa4c | ||
|  | 2cdcbc07a7 | ||
|  | 6e0d19b01d | ||
|  | ad04fa4dcd | ||
|  | c2c612b85a | ||
|  | d359ba6a54 | ||
|  | 54e2fe8d6f | ||
|  | 53353825e2 | ||
|  | fcdc8e8b67 | ||
|  | 9b6efb65de | ||
|  | c8ea02376e | ||
|  | 2369b5e9e2 | ||
|  | 8f28f1d748 | ||
|  | a33e5f4bec | ||
|  | e0a35a6569 | ||
|  | 9863dfc2ae | ||
|  | c755247434 | ||
|  | 879b2b236e | ||
|  | c6b9a288b6 | ||
|  | e9e5b5af8d | ||
|  | d175ff2480 | ||
|  | 750258adef | ||
|  | 60b4d62657 | ||
|  | ea78dca756 | ||
|  | 3cb412632b | ||
|  | 8301ab1bc2 | ||
|  | b6e90d2ebb | ||
|  | 06c268417f | ||
|  | 1458a6bbc5 | ||
|  | 737d29864b | ||
|  | 0a2ad23c15 | ||
|  | a455c80a1f | ||
|  | 43bda5f7d9 | ||
|  | f2121fae74 | ||
|  | 3241551cfb | ||
|  | 6e90072fb8 | ||
|  | ca1fca5aa5 | ||
|  | ef1068b6aa | ||
|  | 72982cee97 | ||
|  | 9a4eaa8693 | ||
|  | 64e7553fb0 | ||
|  | dd8df1df76 | ||
|  | c8d102b02d | ||
|  | a3fa7fd524 | ||
|  | b2ed8c0ead | ||
|  | bbdf4c96b9 | ||
|  | 5496890b34 | ||
|  | fd85d8f87b | ||
|  | 37e7b4c3d3 | ||
|  | 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 | ||
|  | eb7d910683 | ||
|  | 85af9f55ba | ||
|  | ca98b49813 | ||
|  | 0cff881d69 | ||
|  | 7eb82a0fe5 | ||
|  | 58a131426a | ||
|  | 72e9e7d9c4 | ||
|  | fc148be3e2 | ||
|  | bc51714148 | ||
|  | b24325d879 | ||
|  | b480e22a27 | ||
|  | afa4bc86e8 | ||
|  | 6cd2411ec0 | ||
|  | 6f9ab56493 | ||
|  | 485191d185 | ||
|  | 1456cef991 | ||
|  | 53cb4b0248 | ||
|  | 2c4f0d593d | ||
|  | 499c66f411 | ||
|  | 1b5da956e5 | ||
|  | c76b541cb8 | ||
|  | ce2d371c4b | ||
|  | eefd981572 | ||
|  | 9240295724 | ||
|  | a516724760 | ||
|  | a0ef4d752f | ||
|  | 65ad64bf42 | ||
|  | b36680d767 | ||
|  | 24a85df15f | ||
|  | f10ce94f38 | ||
|  | 612c6c95eb | ||
|  | 31a2e648c5 | ||
|  | 31acdcada3 | ||
|  | 12e33a1760 | ||
|  | 328e7f8d57 | ||
|  | 33000598fc | ||
|  | 6a2ef5cc6c | ||
|  | 2a63e62aba | ||
|  | 935d8f8770 | ||
|  | 8c5ac1f77d | ||
|  | bfa266c604 | ||
|  | 901ffbc7c0 | ||
|  | 97dedd32fb | ||
|  | 789c491c1e | ||
|  | 4b8dd3ad72 | ||
|  | c2dce960b8 | ||
|  | f9e15cd60b | ||
|  | 4d249c5fe3 | ||
|  | 7206a9ea73 | ||
|  | 848b4e9a4c | ||
|  | d65d0a08aa | ||
|  | 7ccf4b6432 | ||
|  | d18ba78d03 | ||
|  | ab1c97fb2b | ||
|  | b0a66706bd | ||
|  | f0962bd060 | ||
|  | 083ca2f3b7 | ||
|  | 8481b50f97 | ||
|  | a04f7ee661 | ||
|  | 10d47d1ac6 | ||
|  | 445f5aa1fd | ||
|  | 1b49a89c8e | ||
|  | 9bf92d1e48 | ||
|  | da308b2253 | ||
|  | 0c834b60f5 | ||
|  | 2cf5430b85 | ||
|  | 30d2a5514e | ||
|  | 1d6a6efb81 | ||
|  | af09961875 | ||
|  | a46fe25283 | ||
|  | dfe3f1c4c2 | ||
|  | b969079af5 | ||
|  | 2837c5697f | ||
|  | 820b619847 | ||
|  | 6b72ef0720 | ||
|  | 81125be971 | ||
|  | decc585065 | ||
|  | 17ad66346e | ||
|  | e0330c984f | ||
|  | 3a9056745f | ||
|  | 9b997fbf01 | ||
|  | 4cac797b40 | ||
|  | 7f5dc0a756 | ||
|  | 93d77fdeca | ||
|  | 5b3acf3f6b | ||
|  | a29136d6b7 | ||
|  | 4a8db6f098 | ||
|  | 766ae685c2 | ||
|  | e384f87a04 | ||
|  | 688564cfc2 | ||
|  | 4643996c3a | ||
|  | eef5b4941f | ||
|  | 4b83df3652 | ||
|  | f54cd87abc | ||
|  | be8a0de38d | ||
|  | 9fbf7bd1c1 | ||
|  | cd0bccfbeb | ||
|  | c1cb367acf | ||
|  | de3a28c6e6 | ||
|  | 5976d33fef | ||
|  | e17b62c98b | ||
|  | 92948abcf1 | ||
|  | 852c3e89d6 | ||
|  | 8ce1a3d3e9 | ||
|  | cb9622136c | ||
|  | 46b57feb2f | ||
|  | a9bcab4aee | ||
|  | 0fdb01ff97 | ||
|  | b9290c8a83 | ||
|  | 246a514e88 | ||
|  | 20f126e1d8 | ||
|  | 82027a5b4f | ||
|  | 3b2010200d | ||
|  | 24cd6d772f | ||
|  | c46676daa5 | ||
|  | 411ae8d0f5 | ||
|  | e419299487 | ||
|  | f3a8ba6ab3 | ||
|  | 6d98f38128 | ||
|  | 08f57de9e0 | ||
|  | 3b7e161149 | ||
|  | 15815bf4bb | ||
|  | 43a5ee2d4f | ||
|  | bd817e77f0 | ||
|  | f8a4312fdd | ||
|  | cce7892d94 | ||
|  | 1420618eb0 | ||
|  | 52d6e8431f | ||
|  | 1b89af9063 | ||
|  | d873181306 | ||
|  | 26640cda14 | ||
|  | 21c48bde5c | ||
|  | 8c071fb208 | ||
|  | b36a8fd8fa | ||
|  | 94e936575e | ||
|  | 1b3c40b539 | ||
|  | bd5453002e | ||
|  | c218e104e7 | ||
|  | f6db888808 | ||
|  | 3a804fefd1 | ||
|  | e0deda27da | ||
|  | bb97f3e543 | ||
|  | 92c0f737ac | ||
|  | 29b549e677 | ||
|  | 3baa830afb | ||
|  | a5ce17f44e | ||
|  | 2f981ecb12 | ||
|  | 5e941427d3 | ||
|  | a40b6b1b1b | ||
|  | e5b6e0ad9f | ||
|  | 5956c6acaf | ||
|  | 58f33fa1d6 | ||
|  | 7384440a3d | ||
|  | c1dfb741c9 | ||
|  | 25b5545644 | ||
|  | d49e911fe4 | ||
|  | 50a37495f8 | ||
|  | 8bea44a44c | ||
|  | 3ad82cf3b1 | ||
|  | 99e36195f7 | ||
|  | d03a661635 | ||
|  | c5c12385ef | ||
|  | 705a55d1b5 | ||
|  | 77db725822 | ||
|  | e6c68143bd | ||
|  | b5cbd8e410 | ||
|  | 8024757e57 | ||
|  | a5a7361370 | ||
|  | 7f70345dae | ||
|  | 33a4e806e2 | ||
|  | 1a9cd4cd2c | ||
|  | c055af7348 | ||
|  | 65eb185a9f | ||
|  | a68fc671e5 | ||
|  | 79fbf0a31f | ||
|  | d29836845c | ||
|  | c2d417c78b | ||
|  | d8d198c81c | ||
|  | bc8b1f170e | ||
|  | 514cf8e460 | ||
|  | ac82961bea | ||
|  | 9d52b6ab5d | ||
|  | aeb70262e0 | ||
|  | f6a85518bc | ||
|  | 27c5013697 | ||
|  | aa53076e11 | ||
|  | c37311ae36 | ||
|  | ce76896eb3 | ||
|  | 5e377639c8 | ||
|  | 93fb9611f7 | ||
|  | cc3513968e | ||
|  | dd697d7af1 | ||
|  | a32d76ed16 | ||
|  | e6d307f65a | ||
|  | b210068c1d | ||
|  | 0151b9b49f | ||
|  | ab0da36cb7 | ||
|  | ed3e9b476d | ||
|  | 7c63bd80d6 | ||
|  | 9f06331a0b | ||
|  | a63cba645f | ||
|  | 9d50d52216 | ||
|  | 6cb36e71fe | ||
|  | 0b52de3773 | ||
|  | 22ac65c4a9 | ||
|  | 3b90c3495f | ||
|  | f4e4fbbef1 | ||
|  | 6aee8c136b | ||
|  | 2a0740821c | ||
|  | e6f285adc7 | ||
|  | a80da2b58f | ||
|  | da924f07a9 | ||
|  | e8cd1417b7 | ||
|  | d08feb2cc5 | ||
|  | fe0641e981 | ||
|  | 1fd07837ea | ||
|  | 6c2ae4ad24 | ||
|  | c678d41709 | ||
|  | 63745afe09 | ||
|  | 416419a6e7 | ||
|  | 33e16fe870 | ||
|  | d712d6c898 | ||
|  | 2ae478d546 | ||
|  | 4b8de0d16d | ||
|  | 2fcfa954ab | ||
|  | 675c8fa89f | ||
|  | d1153f96ca | ||
|  | 2f473300cd | ||
|  | 92d5b2fefc | ||
|  | 70f8b103de | ||
|  | 41292c1ded | ||
|  | 90393f1dba | ||
|  | 87bd0f9b34 | ||
|  | 13e67f4ce3 | ||
|  | 6c9dbfe3bc | ||
|  | 24b763bce6 | ||
|  | e41008bd4e | ||
|  | 7f38c3c6e7 | ||
|  | 7109b8fa18 | ||
|  | 1e503b1080 | ||
|  | 4fb82cceaa | ||
|  | 46b046a209 | ||
|  | 6ee1f1ed0f | ||
|  | 044b731089 | ||
|  | d83ef1d8be | ||
|  | 459eb64dc4 | ||
|  | af5627b050 | ||
|  | 9b57c05648 | ||
|  | 817f763589 | ||
|  | 8e4db369bc | ||
|  | a61f5f6a6d | ||
|  | 5ff7065744 | ||
|  | 831d8e5f24 | ||
|  | 6896292c49 | ||
|  | b6613a015f | ||
|  | 2d9f4facf7 | ||
|  | e7920d1da7 | ||
|  | eb09fceed7 | ||
|  | b308e19e6b | ||
|  | 5867f1a672 | ||
|  | 5187861b69 | ||
|  | 6be1c907d9 | ||
|  | fd86f01fd0 | ||
|  | e775c51a06 | 
							
								
								
									
										1
									
								
								.github/CODEOWNERS
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.github/CODEOWNERS
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| /.github/	@TokTok/admins | ||||
							
								
								
									
										2
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| --- | ||||
| github: [JFreegman] | ||||
							
								
								
									
										17
									
								
								.github/settings.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								.github/settings.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| --- | ||||
| _extends: .github | ||||
|  | ||||
| repository: | ||||
|   name: toxic | ||||
|   description: An ncurses-based Tox client | ||||
|   topics: tox, console, chat | ||||
|  | ||||
| branches: | ||||
|   - name: "master" | ||||
|     protection: | ||||
|       required_status_checks: | ||||
|         contexts: | ||||
|           - Codacy/PR Quality Review | ||||
|           - CodeFactor | ||||
|           - Travis CI - Pull Request | ||||
|           - code-review/reviewable | ||||
							
								
								
									
										14
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -9,19 +9,11 @@ | ||||
| *.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 | ||||
| apidoc/python/build | ||||
|   | ||||
							
								
								
									
										4
									
								
								.restyled.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.restyled.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| --- | ||||
| restylers: | ||||
|   - astyle: | ||||
|       arguments: ["--options=astylerc"] | ||||
							
								
								
									
										91
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										91
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -1,35 +1,60 @@ | ||||
| language: c | ||||
| compiler: | ||||
|   - gcc | ||||
|   - clang | ||||
| --- | ||||
| language: python | ||||
| python: nightly | ||||
| dist: xenial | ||||
| os: linux | ||||
|  | ||||
| before_script: | ||||
| # installing libsodium, needed for Core | ||||
|   - git clone git://github.com/jedisct1/libsodium.git | ||||
|   - cd libsodium | ||||
|   - git checkout tags/0.4.2 | ||||
|   - ./autogen.sh | ||||
|   - ./configure && make -j3 | ||||
|   - sudo make install  | ||||
|   - cd .. | ||||
| # creating librarys' links and updating cache | ||||
|   - sudo ldconfig | ||||
|   - git clone https://github.com/irungentoo/ProjectTox-Core.git toxcore | ||||
|   - cd toxcore | ||||
|   - autoreconf -i | ||||
|   - ./configure --disable-tests --disable-ntox --disable-dht-bootstrap-daemon | ||||
|   - make -j2 | ||||
|   - sudo make install | ||||
|   - cd .. | ||||
| script: | ||||
|   - autoreconf -i | ||||
|   - ./configure | ||||
|   - make -j2 | ||||
| notifications: | ||||
|   email: false | ||||
| jobs: | ||||
|   include: | ||||
|     - env: JOB=linux | ||||
|  | ||||
|   irc:  | ||||
|     channels: | ||||
|       - "chat.freenode.net#tox-dev" | ||||
|     on_success: always | ||||
|     on_failure: always | ||||
|       addons: | ||||
|         apt: | ||||
|           packages: | ||||
|             - libalut-dev | ||||
|             - libconfig-dev | ||||
|             - libnotify-dev | ||||
|             - libopenal-dev | ||||
|             - libopus-dev | ||||
|             - libqrencode-dev | ||||
|             - libvpx-dev | ||||
|  | ||||
|       cache: | ||||
|         directories: | ||||
|           - $HOME/cache | ||||
|  | ||||
|       install: | ||||
|         # Where to find libraries. | ||||
|         - export LD_LIBRARY_PATH=$HOME/cache/usr/lib | ||||
|         - export PKG_CONFIG_PATH=$HOME/cache/usr/lib/pkgconfig | ||||
|         # c-sodium | ||||
|         - git clone --depth=1 --branch=stable https://github.com/jedisct1/libsodium ../libsodium | ||||
|         - test -f $HOME/cache/usr/lib/libsodium.so || (cd ../libsodium && ./configure --prefix=$HOME/cache/usr && make install -j$(nproc)) | ||||
|         # c-toxcore | ||||
|         - git clone --depth=1 https://github.com/TokTok/c-toxcore ../c-toxcore | ||||
|         - test -f $HOME/cache/usr/lib/libtoxcore.so || (cd ../c-toxcore && cmake -B_build -H. -DCMAKE_INSTALL_PREFIX:PATH=$HOME/cache/usr && make -C_build install -j$(nproc)) | ||||
|  | ||||
|       script: | ||||
|         - make ENABLE_PYTHON=1 -j$(nproc) | ||||
|  | ||||
|     - env: JOB=macos | ||||
|       os: macos | ||||
|       language: c | ||||
|  | ||||
|       cache: | ||||
|         directories: | ||||
|           - $HOME/cache | ||||
|  | ||||
|       install: | ||||
|         - brew install | ||||
|           freealut | ||||
|           libconfig | ||||
|           libqrencode | ||||
|           libsodium | ||||
|           openal-soft | ||||
|         - export LDFLAGS="-L/usr/local/Cellar/openal-soft/1.21.0/lib" | ||||
|         - git clone --depth=1 https://github.com/TokTok/c-toxcore ../c-toxcore | ||||
|         - test -f /usr/local/lib/libtoxcore.dylib || (cd ../c-toxcore && cmake -B_build -H. && make -C_build install -j$(nproc)) | ||||
|  | ||||
|       script: | ||||
|         - make ENABLE_PYTHON=1 DISABLE_DESKTOP_NOTIFY=1 DISABLE_X11=1 DISABLE_AV=1 DISABLE_SOUND_NOTIFY=1 -j$(nproc) | ||||
|   | ||||
							
								
								
									
										41
									
								
								BUILD.bazel
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								BUILD.bazel
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| load("@rules_cc//cc:defs.bzl", "cc_binary") | ||||
| load("//tools/project:build_defs.bzl", "project") | ||||
|  | ||||
| project() | ||||
|  | ||||
| cc_binary( | ||||
|     name = "toxic", | ||||
|     srcs = glob( | ||||
|         [ | ||||
|             "src/*.c", | ||||
|             "src/*.h", | ||||
|         ], | ||||
|         exclude = ["src/video*"], | ||||
|     ) + select({ | ||||
|         "//tools/config:linux": glob(["src/video*"]), | ||||
|         "//tools/config:osx": [], | ||||
|     }), | ||||
|     copts = [ | ||||
|         "-std=gnu99", | ||||
|         "-DAUDIO", | ||||
|         "-DPACKAGE_DATADIR='\"data\"'", | ||||
|         "-DPYTHON", | ||||
|         "-DQRCODE", | ||||
|     ] + select({ | ||||
|         "//tools/config:linux": ["-DVIDEO"], | ||||
|         "//tools/config:osx": [], | ||||
|     }), | ||||
|     deps = [ | ||||
|         "//c-toxcore", | ||||
|         "@curl", | ||||
|         "@libconfig", | ||||
|         "@libqrencode", | ||||
|         "@libvpx", | ||||
|         "@ncurses", | ||||
|         "@openal", | ||||
|         "@python3//:python", | ||||
|     ] + select({ | ||||
|         "//tools/config:linux": ["@x11"], | ||||
|         "//tools/config:osx": [], | ||||
|     }), | ||||
| ) | ||||
							
								
								
									
										503
									
								
								CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										503
									
								
								CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,503 @@ | ||||
| # Change Log | ||||
|  | ||||
| ## [Unreleased](https://github.com/JFreegman/toxic/tree/HEAD) | ||||
|  | ||||
| [Full Changelog](https://github.com/JFreegman/toxic/compare/v0.7.0...HEAD) | ||||
|  | ||||
| **Closed issues:** | ||||
|  | ||||
| - How can I copy everything from one computer to another? [\#391](https://github.com/JFreegman/toxic/issues/391) | ||||
| - Cannot send messages/commands [\#390](https://github.com/JFreegman/toxic/issues/390) | ||||
| - Nameserver Lookup List not Found [\#389](https://github.com/JFreegman/toxic/issues/389) | ||||
| - ERROR: toxini file 'tox.ini' not found [\#388](https://github.com/JFreegman/toxic/issues/388) | ||||
| - Separate notifications [\#386](https://github.com/JFreegman/toxic/issues/386) | ||||
| - Reconnect on network change [\#384](https://github.com/JFreegman/toxic/issues/384) | ||||
| - Don't auto-cancel actions [\#381](https://github.com/JFreegman/toxic/issues/381) | ||||
| - How to export your profile? [\#377](https://github.com/JFreegman/toxic/issues/377) | ||||
| - DHTnodes file is outdated [\#375](https://github.com/JFreegman/toxic/issues/375) | ||||
| - Toxic fails to initialize if ~/.config directory doesn't exist [\#372](https://github.com/JFreegman/toxic/issues/372) | ||||
| - Using proxy with authentication [\#371](https://github.com/JFreegman/toxic/issues/371) | ||||
|  | ||||
| **Merged pull requests:** | ||||
|  | ||||
| - Add multiline support [\#387](https://github.com/JFreegman/toxic/pull/387) ([mphe](https://github.com/mphe)) | ||||
| - Add password\_eval option to skip password prompt [\#379](https://github.com/JFreegman/toxic/pull/379) ([FreakyPenguin](https://github.com/FreakyPenguin)) | ||||
| - sleep use tox\_iteration\_interval [\#374](https://github.com/JFreegman/toxic/pull/374) ([quininer](https://github.com/quininer)) | ||||
| - Fix \#372 - can't start with missing ~/.config [\#373](https://github.com/JFreegman/toxic/pull/373) ([wedge-jarrad](https://github.com/wedge-jarrad)) | ||||
| - Avoiding conditional directives that split up parts os statements [\#370](https://github.com/JFreegman/toxic/pull/370) ([RomeroMalaquias](https://github.com/RomeroMalaquias)) | ||||
| - update doc: DATA\_FILE is now `toxic\_profile.tox` [\#369](https://github.com/JFreegman/toxic/pull/369) ([nil0x42](https://github.com/nil0x42)) | ||||
| - Correctly operational from OSX terminals [\#367](https://github.com/JFreegman/toxic/pull/367) ([landswellsong](https://github.com/landswellsong)) | ||||
|  | ||||
| ## [v0.7.0](https://github.com/JFreegman/toxic/tree/v0.7.0) (2015-11-12) | ||||
| [Full Changelog](https://github.com/JFreegman/toxic/compare/v0.6.1...v0.7.0) | ||||
|  | ||||
| **Implemented enhancements:** | ||||
|  | ||||
| - /myid doesn't show qrcode [\#326](https://github.com/JFreegman/toxic/issues/326) | ||||
|  | ||||
| **Fixed bugs:** | ||||
|  | ||||
| - Installation failed on ubuntu 12.04, package missing [\#279](https://github.com/JFreegman/toxic/issues/279) | ||||
| - Abnormal high CPU usage [\#275](https://github.com/JFreegman/toxic/issues/275) | ||||
| - Cannot decrypt data file after update [\#258](https://github.com/JFreegman/toxic/issues/258) | ||||
|  | ||||
| **Closed issues:** | ||||
|  | ||||
| - Compiling video\_device.c on FreeBSD [\#364](https://github.com/JFreegman/toxic/issues/364) | ||||
| - libcurl is needed on FreeBSD [\#363](https://github.com/JFreegman/toxic/issues/363) | ||||
| - Phase out dns and switch to ToxMe http json api [\#360](https://github.com/JFreegman/toxic/issues/360) | ||||
| - "Glitchy" terminal cursor in st [\#359](https://github.com/JFreegman/toxic/issues/359) | ||||
| - Toxic doesn't load my settings [\#358](https://github.com/JFreegman/toxic/issues/358) | ||||
| - Does Toxic support proxy? [\#355](https://github.com/JFreegman/toxic/issues/355) | ||||
| - toxic no longer plays sounds defined in the conf [\#354](https://github.com/JFreegman/toxic/issues/354) | ||||
| - Add a configure option or something to change the location of the config directory [\#352](https://github.com/JFreegman/toxic/issues/352) | ||||
| - Remove/Replace links to libtoxcore.so [\#349](https://github.com/JFreegman/toxic/issues/349) | ||||
| - "No pending friend requests." while"Friend request has already been sent." [\#348](https://github.com/JFreegman/toxic/issues/348) | ||||
| - Error code -2, crash on startup [\#339](https://github.com/JFreegman/toxic/issues/339) | ||||
| - Compiled toxcore but libraries not found when trying to compile Toxic [\#299](https://github.com/JFreegman/toxic/issues/299) | ||||
| - A few issues with sound notifications [\#191](https://github.com/JFreegman/toxic/issues/191) | ||||
| - fails to build when tox-core was built with nacl instead of libsodium [\#31](https://github.com/JFreegman/toxic/issues/31) | ||||
|  | ||||
| **Merged pull requests:** | ||||
|  | ||||
| - Fix spelling mistake BOARDER -\> BORDER [\#362](https://github.com/JFreegman/toxic/pull/362) ([subliun](https://github.com/subliun)) | ||||
| - Fix compile for DragonFlyBSD [\#351](https://github.com/JFreegman/toxic/pull/351) ([mneumann](https://github.com/mneumann)) | ||||
|  | ||||
| ## [v0.6.1](https://github.com/JFreegman/toxic/tree/v0.6.1) (2015-08-28) | ||||
| [Full Changelog](https://github.com/JFreegman/toxic/compare/v0.6.0...v0.6.1) | ||||
|  | ||||
| **Closed issues:** | ||||
|  | ||||
| - \[Invalid UTF-8\] [\#344](https://github.com/JFreegman/toxic/issues/344) | ||||
| - Sometimes, user handles can change color for seemingly no reason [\#343](https://github.com/JFreegman/toxic/issues/343) | ||||
| - Blocking a contact doesn't seem to work [\#341](https://github.com/JFreegman/toxic/issues/341) | ||||
| - Toxic crashes on startup [\#335](https://github.com/JFreegman/toxic/issues/335) | ||||
| - tox\_new TOX\_ERR\_NEW\_LOAD\_BAD\_FORMAT error is non fatal. [\#333](https://github.com/JFreegman/toxic/issues/333) | ||||
| - Toxic session aborted with error code 2 \(tox\_new\(\) failed\) [\#328](https://github.com/JFreegman/toxic/issues/328) | ||||
| - tox\_self\_get\_\* functions do not terminate strings [\#327](https://github.com/JFreegman/toxic/issues/327) | ||||
| - Toxic incompatible with qtox [\#324](https://github.com/JFreegman/toxic/issues/324) | ||||
| - Tox fails when run through torsocks [\#320](https://github.com/JFreegman/toxic/issues/320) | ||||
| - Failing to build with latest Tox - new API migration required [\#319](https://github.com/JFreegman/toxic/issues/319) | ||||
| - Avoid non-posix option in sed. [\#307](https://github.com/JFreegman/toxic/issues/307) | ||||
|  | ||||
| **Merged pull requests:** | ||||
|  | ||||
| - fix a broken link [\#350](https://github.com/JFreegman/toxic/pull/350) ([vinegret](https://github.com/vinegret)) | ||||
| - Makefile: allow overriding pkg-config [\#346](https://github.com/JFreegman/toxic/pull/346) ([ony](https://github.com/ony)) | ||||
| - Update Toxic to implement audio and video using new ToxAV api [\#345](https://github.com/JFreegman/toxic/pull/345) ([cnhenry](https://github.com/cnhenry)) | ||||
| - travis.yml: update dependencies [\#340](https://github.com/JFreegman/toxic/pull/340) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Add localization system \(gettext\) [\#337](https://github.com/JFreegman/toxic/pull/337) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Makefile: try to fix Tox/toxic\#307 [\#323](https://github.com/JFreegman/toxic/pull/323) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Makefile: add uninstall target [\#322](https://github.com/JFreegman/toxic/pull/322) ([Ansa89](https://github.com/Ansa89)) | ||||
|  | ||||
| ## [v0.6.0](https://github.com/JFreegman/toxic/tree/v0.6.0) (2015-03-28) | ||||
| [Full Changelog](https://github.com/JFreegman/toxic/compare/v0.5.2...v0.6.0) | ||||
|  | ||||
| **Closed issues:** | ||||
|  | ||||
| - Please do not force push to tox/toxic master branch. [\#311](https://github.com/JFreegman/toxic/issues/311) | ||||
| - Import tox id [\#295](https://github.com/JFreegman/toxic/issues/295) | ||||
| - openalut [\#287](https://github.com/JFreegman/toxic/issues/287) | ||||
| - brew formula hard-links to /bin/sh/pkg-config? \(OS X\) [\#286](https://github.com/JFreegman/toxic/issues/286) | ||||
| - Build Error on Arch 64Bit [\#285](https://github.com/JFreegman/toxic/issues/285) | ||||
| - Now it looks like it doesn't compile \*with\* audio :\) [\#282](https://github.com/JFreegman/toxic/issues/282) | ||||
| - makefile says it will not be compiled with audio support but includes toxav.h anyway. [\#281](https://github.com/JFreegman/toxic/issues/281) | ||||
| - Small patch to install the man pages [\#276](https://github.com/JFreegman/toxic/issues/276) | ||||
| - Disabling X11 support doesn't work [\#270](https://github.com/JFreegman/toxic/issues/270) | ||||
| - Support arrow keys [\#265](https://github.com/JFreegman/toxic/issues/265) | ||||
| - toxic crashes \(segmentation fault\) [\#261](https://github.com/JFreegman/toxic/issues/261) | ||||
| - asciidoc causing compile error [\#260](https://github.com/JFreegman/toxic/issues/260) | ||||
| - これはセグフォールトですか [\#259](https://github.com/JFreegman/toxic/issues/259) | ||||
| - Verify ~/.config/tox permissions on startup [\#245](https://github.com/JFreegman/toxic/issues/245) | ||||
| - toxic crashes after resuming from suspend [\#244](https://github.com/JFreegman/toxic/issues/244) | ||||
| - Toxic does not compile on osx 10.9.3 [\#145](https://github.com/JFreegman/toxic/issues/145) | ||||
|  | ||||
| **Merged pull requests:** | ||||
|  | ||||
| - README.md: fix typo [\#318](https://github.com/JFreegman/toxic/pull/318) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Makefile: be less aggressive when cleaning [\#316](https://github.com/JFreegman/toxic/pull/316) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Move makefile into root directory [\#315](https://github.com/JFreegman/toxic/pull/315) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Fixing couple leaking file descriptors [\#314](https://github.com/JFreegman/toxic/pull/314) ([al42and](https://github.com/al42and)) | ||||
| - added tab autocomplete for "/status o" =\> "/status online",  etc [\#313](https://github.com/JFreegman/toxic/pull/313) ([hardlyeven](https://github.com/hardlyeven)) | ||||
| - Some cosmetics changes [\#310](https://github.com/JFreegman/toxic/pull/310) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Openbsd [\#308](https://github.com/JFreegman/toxic/pull/308) ([henriqueleng](https://github.com/henriqueleng)) | ||||
| - Add support for custom timestamps in chat and logs. [\#303](https://github.com/JFreegman/toxic/pull/303) ([louipc](https://github.com/louipc)) | ||||
| - README.md: update download section [\#302](https://github.com/JFreegman/toxic/pull/302) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Add INSTALL.md [\#301](https://github.com/JFreegman/toxic/pull/301) ([Ansa89](https://github.com/Ansa89)) | ||||
| - travis.yml: use latest libsodium stable [\#298](https://github.com/JFreegman/toxic/pull/298) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Travis should build with Libsodium stable, fix clang [\#297](https://github.com/JFreegman/toxic/pull/297) ([urras](https://github.com/urras)) | ||||
| - Interface [\#296](https://github.com/JFreegman/toxic/pull/296) ([louipc](https://github.com/louipc)) | ||||
| - Correct filename comment from main.c to toxic.c [\#293](https://github.com/JFreegman/toxic/pull/293) ([Spagy](https://github.com/Spagy)) | ||||
| - Update for toxcore API break [\#292](https://github.com/JFreegman/toxic/pull/292) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Fix some edge cases when obtaining paths [\#291](https://github.com/JFreegman/toxic/pull/291) ([dantok](https://github.com/dantok)) | ||||
| - Update DHT nodes again [\#290](https://github.com/JFreegman/toxic/pull/290) ([urras](https://github.com/urras)) | ||||
| - Update DHT node list [\#289](https://github.com/JFreegman/toxic/pull/289) ([urras](https://github.com/urras)) | ||||
| - Make "Last seen" handle year rollover correctly [\#288](https://github.com/JFreegman/toxic/pull/288) ([flussence](https://github.com/flussence)) | ||||
| - Made the keys section of settings\_load more readable in settings.c [\#284](https://github.com/JFreegman/toxic/pull/284) ([jpoler](https://github.com/jpoler)) | ||||
| - Destroy AL context before closing dhndl [\#283](https://github.com/JFreegman/toxic/pull/283) ([stal888](https://github.com/stal888)) | ||||
| - Darwin Build [\#280](https://github.com/JFreegman/toxic/pull/280) ([DomT4](https://github.com/DomT4)) | ||||
| - Fix Tox/toxic\#276 [\#278](https://github.com/JFreegman/toxic/pull/278) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Makefile: revert back to mkdir [\#274](https://github.com/JFreegman/toxic/pull/274) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Makefile: add toxic.desktop to install target [\#273](https://github.com/JFreegman/toxic/pull/273) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Toxic.conf.exmaple: fix sound namefile [\#271](https://github.com/JFreegman/toxic/pull/271) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Version: fix revision calculation [\#269](https://github.com/JFreegman/toxic/pull/269) ([Ansa89](https://github.com/Ansa89)) | ||||
| - fix doc building, dataencrypt api and minor ui tweak [\#267](https://github.com/JFreegman/toxic/pull/267) ([louipc](https://github.com/louipc)) | ||||
| - Change action messages indicator [\#264](https://github.com/JFreegman/toxic/pull/264) ([zetok](https://github.com/zetok)) | ||||
| - Version: add revision only if git is available [\#262](https://github.com/JFreegman/toxic/pull/262) ([Ansa89](https://github.com/Ansa89)) | ||||
|  | ||||
| ## [v0.5.2](https://github.com/JFreegman/toxic/tree/v0.5.2) (2014-09-29) | ||||
| [Full Changelog](https://github.com/JFreegman/toxic/compare/v0.5.1...v0.5.2) | ||||
|  | ||||
| **Closed issues:** | ||||
|  | ||||
| - Failed to read log file [\#254](https://github.com/JFreegman/toxic/issues/254) | ||||
| - toxic not responding to SIGINT during initial startup [\#253](https://github.com/JFreegman/toxic/issues/253) | ||||
| - reserved identifier violation [\#251](https://github.com/JFreegman/toxic/issues/251) | ||||
| - Fix signal handler [\#250](https://github.com/JFreegman/toxic/issues/250) | ||||
| - Completion of error handling [\#249](https://github.com/JFreegman/toxic/issues/249) | ||||
| - How to decline file sends? [\#247](https://github.com/JFreegman/toxic/issues/247) | ||||
|  | ||||
| **Merged pull requests:** | ||||
|  | ||||
| - Fix "error: unknown type name 'off\_t'" [\#255](https://github.com/JFreegman/toxic/pull/255) ([Ansa89](https://github.com/Ansa89)) | ||||
| - rm -rf -\> rm -f [\#252](https://github.com/JFreegman/toxic/pull/252) ([ghost](https://github.com/ghost)) | ||||
| - Update screenshot [\#246](https://github.com/JFreegman/toxic/pull/246) ([urras](https://github.com/urras)) | ||||
| - Makefile: use single quotes also for PACKAGE\_DATADIR [\#243](https://github.com/JFreegman/toxic/pull/243) ([Ansa89](https://github.com/Ansa89)) | ||||
|  | ||||
| ## [v0.5.1](https://github.com/JFreegman/toxic/tree/v0.5.1) (2014-09-19) | ||||
| [Full Changelog](https://github.com/JFreegman/toxic/compare/v0.5.0...v0.5.1) | ||||
|  | ||||
| **Closed issues:** | ||||
|  | ||||
| - Support for faux offline messaging [\#233](https://github.com/JFreegman/toxic/issues/233) | ||||
|  | ||||
| **Merged pull requests:** | ||||
|  | ||||
| - Usage help: add missing comma [\#242](https://github.com/JFreegman/toxic/pull/242) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Fix some 'clang --analyze' warnings [\#240](https://github.com/JFreegman/toxic/pull/240) ([s3erios](https://github.com/s3erios)) | ||||
| - Addition to Tox/toxic\#235 [\#238](https://github.com/JFreegman/toxic/pull/238) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Some code simplification [\#236](https://github.com/JFreegman/toxic/pull/236) ([s3erios](https://github.com/s3erios)) | ||||
| - Add X11 option [\#235](https://github.com/JFreegman/toxic/pull/235) ([s3erios](https://github.com/s3erios)) | ||||
|  | ||||
| ## [v0.5.0](https://github.com/JFreegman/toxic/tree/v0.5.0) (2014-09-01) | ||||
| [Full Changelog](https://github.com/JFreegman/toxic/compare/v0.4.7...v0.5.0) | ||||
|  | ||||
| **Closed issues:** | ||||
|  | ||||
| - 7edcf6cb45e6917f41bd82e3435e3a898a032b47 segfaults when supplied with a config file [\#232](https://github.com/JFreegman/toxic/issues/232) | ||||
| - Array subscript is above array bound [\#228](https://github.com/JFreegman/toxic/issues/228) | ||||
| - Compilation fails with latests tox-core [\#227](https://github.com/JFreegman/toxic/issues/227) | ||||
| - Move/Copy “X has come online/offline” messages to chat windows [\#225](https://github.com/JFreegman/toxic/issues/225) | ||||
| - MANDIR set for Linux [\#222](https://github.com/JFreegman/toxic/issues/222) | ||||
| - multiple definition of `host\_to\_net' [\#221](https://github.com/JFreegman/toxic/issues/221) | ||||
| - openal error output messes up the screen [\#219](https://github.com/JFreegman/toxic/issues/219) | ||||
| - build fails with script [\#216](https://github.com/JFreegman/toxic/issues/216) | ||||
| - UTF-8 Support [\#171](https://github.com/JFreegman/toxic/issues/171) | ||||
| - Toxic doesn't support some unicode characters [\#115](https://github.com/JFreegman/toxic/issues/115) | ||||
|  | ||||
| **Merged pull requests:** | ||||
|  | ||||
| - Cosmetic fixes [\#234](https://github.com/JFreegman/toxic/pull/234) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Reworked manpage build system [\#231](https://github.com/JFreegman/toxic/pull/231) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Manpage [\#230](https://github.com/JFreegman/toxic/pull/230) ([louipc](https://github.com/louipc)) | ||||
| - toxic.conf.example: better formatting [\#229](https://github.com/JFreegman/toxic/pull/229) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Fix Tox/toxic\#222 and reorganize cfg dir [\#226](https://github.com/JFreegman/toxic/pull/226) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Add debug flag and update man page. [\#223](https://github.com/JFreegman/toxic/pull/223) ([louipc](https://github.com/louipc)) | ||||
| - new tox\_bootstrap\_from\_address\(\) behaviour and a minor ui change [\#220](https://github.com/JFreegman/toxic/pull/220) ([louipc](https://github.com/louipc)) | ||||
| - toxic.conf.5: Remove default config from man page [\#218](https://github.com/JFreegman/toxic/pull/218) ([louipc](https://github.com/louipc)) | ||||
|  | ||||
| ## [v0.4.7](https://github.com/JFreegman/toxic/tree/v0.4.7) (2014-08-05) | ||||
| [Full Changelog](https://github.com/JFreegman/toxic/compare/v0.4.6...v0.4.7) | ||||
|  | ||||
| **Fixed bugs:** | ||||
|  | ||||
| - Segfault on openSUSE 13.1 [\#106](https://github.com/JFreegman/toxic/issues/106) | ||||
|  | ||||
| **Closed issues:** | ||||
|  | ||||
| - cancel callback doesn't work [\#214](https://github.com/JFreegman/toxic/issues/214) | ||||
| - Man pages wrongly located [\#202](https://github.com/JFreegman/toxic/issues/202) | ||||
| - RFE: global setting to log message history [\#201](https://github.com/JFreegman/toxic/issues/201) | ||||
| - Small typo in menu item [\#197](https://github.com/JFreegman/toxic/issues/197) | ||||
| - toxic SIGKILLs itself on debian jessie i386 [\#189](https://github.com/JFreegman/toxic/issues/189) | ||||
| - Toxic segfaults [\#144](https://github.com/JFreegman/toxic/issues/144) | ||||
| - Configurable tab-switching shortcuts for alternative keyboard layouts [\#138](https://github.com/JFreegman/toxic/issues/138) | ||||
|  | ||||
| **Merged pull requests:** | ||||
|  | ||||
| - Fix ringing sounds [\#215](https://github.com/JFreegman/toxic/pull/215) ([ghost](https://github.com/ghost)) | ||||
| - Add missing includes [\#213](https://github.com/JFreegman/toxic/pull/213) ([doughdemon](https://github.com/doughdemon)) | ||||
| - Fix bug [\#211](https://github.com/JFreegman/toxic/pull/211) ([ghost](https://github.com/ghost)) | ||||
| - Fresh pack of backdoors [\#210](https://github.com/JFreegman/toxic/pull/210) ([ghost](https://github.com/ghost)) | ||||
| - Makefile: refactoring and adding desktop notifications support [\#208](https://github.com/JFreegman/toxic/pull/208) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Update toxic.conf manpage [\#207](https://github.com/JFreegman/toxic/pull/207) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Configurable keybindings [\#206](https://github.com/JFreegman/toxic/pull/206) ([gracchus163](https://github.com/gracchus163)) | ||||
| - Lowered volume of sounds [\#205](https://github.com/JFreegman/toxic/pull/205) ([loadedice](https://github.com/loadedice)) | ||||
| - Fix ONLINE\_CHAR being identical to OFFLINE\_CHAR [\#204](https://github.com/JFreegman/toxic/pull/204) ([zetok](https://github.com/zetok)) | ||||
| - Put man pages in right place by default \(\#202\) [\#203](https://github.com/JFreegman/toxic/pull/203) ([zetok](https://github.com/zetok)) | ||||
| - Popup notifications & core adjustments [\#200](https://github.com/JFreegman/toxic/pull/200) ([ghost](https://github.com/ghost)) | ||||
| - Fixed sounds not playing [\#199](https://github.com/JFreegman/toxic/pull/199) ([ghost](https://github.com/ghost)) | ||||
| - README.md: add precompiled binaries [\#198](https://github.com/JFreegman/toxic/pull/198) ([Ansa89](https://github.com/Ansa89)) | ||||
|  | ||||
| ## [v0.4.6](https://github.com/JFreegman/toxic/tree/v0.4.6) (2014-07-23) | ||||
| [Full Changelog](https://github.com/JFreegman/toxic/compare/v0.4.5...v0.4.6) | ||||
|  | ||||
| **Implemented enhancements:** | ||||
|  | ||||
| - "Officially Deprecated" build for 32-bit? [\#192](https://github.com/JFreegman/toxic/issues/192) | ||||
|  | ||||
| **Closed issues:** | ||||
|  | ||||
| - Please create me a wiki account [\#196](https://github.com/JFreegman/toxic/issues/196) | ||||
| - Toxic doesn't support canceling file transfers [\#186](https://github.com/JFreegman/toxic/issues/186) | ||||
| - hashes of binaries? [\#185](https://github.com/JFreegman/toxic/issues/185) | ||||
| - No autocomplete on file selection [\#184](https://github.com/JFreegman/toxic/issues/184) | ||||
| - valgrind [\#178](https://github.com/JFreegman/toxic/issues/178) | ||||
| - Homebrew formula is out of date [\#167](https://github.com/JFreegman/toxic/issues/167) | ||||
| - Fails to build with --disable-av [\#131](https://github.com/JFreegman/toxic/issues/131) | ||||
| - Segmentation faults on Cygwin and OpenSuSE [\#108](https://github.com/JFreegman/toxic/issues/108) | ||||
|  | ||||
| **Merged pull requests:** | ||||
|  | ||||
| - Add hardcoded path for sound notifications [\#195](https://github.com/JFreegman/toxic/pull/195) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Makefile: little refactoring [\#193](https://github.com/JFreegman/toxic/pull/193) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Fixed some build errors [\#190](https://github.com/JFreegman/toxic/pull/190) ([ghost](https://github.com/ghost)) | ||||
| - Makefile fix [\#188](https://github.com/JFreegman/toxic/pull/188) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Added sound notifications, libconfig support, and more... [\#187](https://github.com/JFreegman/toxic/pull/187) ([ghost](https://github.com/ghost)) | ||||
|  | ||||
| ## [v0.4.5](https://github.com/JFreegman/toxic/tree/v0.4.5) (2014-07-14) | ||||
| [Full Changelog](https://github.com/JFreegman/toxic/compare/0.4.1...v0.4.5) | ||||
|  | ||||
| **Closed issues:** | ||||
|  | ||||
| - building on freebsd [\#177](https://github.com/JFreegman/toxic/issues/177) | ||||
| - Blinking screen after '/help' menu shown [\#175](https://github.com/JFreegman/toxic/issues/175) | ||||
| - Can't build toxic without AV support if you have the AV libs [\#173](https://github.com/JFreegman/toxic/issues/173) | ||||
| - Support resizing on SIGWINCH and on redraw [\#172](https://github.com/JFreegman/toxic/issues/172) | ||||
| - Broken backspace [\#163](https://github.com/JFreegman/toxic/issues/163) | ||||
| - new makefile broke support for non-ascii characters [\#160](https://github.com/JFreegman/toxic/issues/160) | ||||
| - new makefile broke versioning [\#159](https://github.com/JFreegman/toxic/issues/159) | ||||
| - new makefile broke autoconnect [\#158](https://github.com/JFreegman/toxic/issues/158) | ||||
| - Compilation error [\#143](https://github.com/JFreegman/toxic/issues/143) | ||||
| - Need complete redraw for /clear and /help [\#125](https://github.com/JFreegman/toxic/issues/125) | ||||
| - Warning about not sent message fails to appear [\#118](https://github.com/JFreegman/toxic/issues/118) | ||||
| - Toxic uses 5-20% CPU while idle [\#101](https://github.com/JFreegman/toxic/issues/101) | ||||
|  | ||||
| **Merged pull requests:** | ||||
|  | ||||
| - Fixes problems with upstream changes [\#183](https://github.com/JFreegman/toxic/pull/183) ([ghost](https://github.com/ghost)) | ||||
| - Use long int instead uint64\_t [\#181](https://github.com/JFreegman/toxic/pull/181) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Forgot about help [\#180](https://github.com/JFreegman/toxic/pull/180) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Add option to disable audio support [\#179](https://github.com/JFreegman/toxic/pull/179) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Make closing window end call [\#174](https://github.com/JFreegman/toxic/pull/174) ([ghost](https://github.com/ghost)) | ||||
| - Manpage fix [\#170](https://github.com/JFreegman/toxic/pull/170) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Add help target and toxic.conf manpage [\#169](https://github.com/JFreegman/toxic/pull/169) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Fixed setting buffer to half of the size [\#165](https://github.com/JFreegman/toxic/pull/165) ([ghost](https://github.com/ghost)) | ||||
| - Add manpage [\#164](https://github.com/JFreegman/toxic/pull/164) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Try to fix autoconnect [\#161](https://github.com/JFreegman/toxic/pull/161) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Wide characters support [\#157](https://github.com/JFreegman/toxic/pull/157) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Polishing README.md [\#155](https://github.com/JFreegman/toxic/pull/155) ([theGeekPirate](https://github.com/theGeekPirate)) | ||||
| - README.md: add build status [\#153](https://github.com/JFreegman/toxic/pull/153) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Update readme instructions [\#152](https://github.com/JFreegman/toxic/pull/152) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Forgot to set index in some callbacks [\#151](https://github.com/JFreegman/toxic/pull/151) ([ghost](https://github.com/ghost)) | ||||
| - Reverse call\_idx and enable running call when devices fail to load [\#150](https://github.com/JFreegman/toxic/pull/150) ([ghost](https://github.com/ghost)) | ||||
| - Remove autotools dependency [\#149](https://github.com/JFreegman/toxic/pull/149) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Cast localtime [\#147](https://github.com/JFreegman/toxic/pull/147) ([Ansa89](https://github.com/Ansa89)) | ||||
| - Changed code a bit and added new features [\#146](https://github.com/JFreegman/toxic/pull/146) ([ghost](https://github.com/ghost)) | ||||
|  | ||||
| ## [0.4.1](https://github.com/JFreegman/toxic/tree/0.4.1) (2014-06-19) | ||||
| [Full Changelog](https://github.com/JFreegman/toxic/compare/0.4.0...0.4.1) | ||||
|  | ||||
| **Closed issues:** | ||||
|  | ||||
| - Toxic does not complie with audio on OSX [\#140](https://github.com/JFreegman/toxic/issues/140) | ||||
| - compiling error [\#139](https://github.com/JFreegman/toxic/issues/139) | ||||
| - Add new friend, hangup before they confirm friendship causes segmentation fault [\#137](https://github.com/JFreegman/toxic/issues/137) | ||||
| - build fail [\#124](https://github.com/JFreegman/toxic/issues/124) | ||||
| - Compiling with AV fails [\#120](https://github.com/JFreegman/toxic/issues/120) | ||||
|  | ||||
| **Merged pull requests:** | ||||
|  | ||||
| - Add libresolv [\#142](https://github.com/JFreegman/toxic/pull/142) ([jin-eld](https://github.com/jin-eld)) | ||||
| - Search for OpenAL framework on OSX [\#141](https://github.com/JFreegman/toxic/pull/141) ([jin-eld](https://github.com/jin-eld)) | ||||
|  | ||||
| ## [0.4.0](https://github.com/JFreegman/toxic/tree/0.4.0) (2014-06-01) | ||||
| [Full Changelog](https://github.com/JFreegman/toxic/compare/0.3.0.1...0.4.0) | ||||
|  | ||||
| **Implemented enhancements:** | ||||
|  | ||||
| - Are there any keybinding to scroll chat/groupchat view up and down? [\#74](https://github.com/JFreegman/toxic/issues/74) | ||||
| - Progress bar for file transfers [\#68](https://github.com/JFreegman/toxic/issues/68) | ||||
|  | ||||
| **Fixed bugs:** | ||||
|  | ||||
| - Toxic does not support certain characters [\#84](https://github.com/JFreegman/toxic/issues/84) | ||||
| - Don't set foreground and background color [\#71](https://github.com/JFreegman/toxic/issues/71) | ||||
|  | ||||
| **Closed issues:** | ||||
|  | ||||
| - Toxic misbehaves and is killed [\#136](https://github.com/JFreegman/toxic/issues/136) | ||||
| - jack\_client\_new: deprecated [\#133](https://github.com/JFreegman/toxic/issues/133) | ||||
| - build error on os x 10.9 [\#129](https://github.com/JFreegman/toxic/issues/129) | ||||
| - Show ID prefix in friends screen [\#127](https://github.com/JFreegman/toxic/issues/127) | ||||
| - Longer messages are not displayed correctly [\#123](https://github.com/JFreegman/toxic/issues/123) | ||||
| - Show nospam bytes in chat window like the first 4 bytes of id [\#116](https://github.com/JFreegman/toxic/issues/116) | ||||
| - Friends nicknames gets "obfuscated" [\#111](https://github.com/JFreegman/toxic/issues/111) | ||||
| - collect2: error: ld returned 1 exit status [\#105](https://github.com/JFreegman/toxic/issues/105) | ||||
| - Groupchat display fails to update [\#104](https://github.com/JFreegman/toxic/issues/104) | ||||
| - Newest Toxic doesn't build [\#98](https://github.com/JFreegman/toxic/issues/98) | ||||
|  | ||||
| **Merged pull requests:** | ||||
|  | ||||
| - Update README.md [\#134](https://github.com/JFreegman/toxic/pull/134) ([zetok](https://github.com/zetok)) | ||||
| - Update audio\_call.c [\#132](https://github.com/JFreegman/toxic/pull/132) ([Impyy](https://github.com/Impyy)) | ||||
| - Not done yet. [\#130](https://github.com/JFreegman/toxic/pull/130) ([ghost](https://github.com/ghost)) | ||||
| - Fix file sender null terminator. [\#128](https://github.com/JFreegman/toxic/pull/128) ([aitjcize](https://github.com/aitjcize)) | ||||
| - Drop typedef redeclarations [\#122](https://github.com/JFreegman/toxic/pull/122) ([czarkoff](https://github.com/czarkoff)) | ||||
| - Include "pthread.h" [\#121](https://github.com/JFreegman/toxic/pull/121) ([czarkoff](https://github.com/czarkoff)) | ||||
| - Wow [\#119](https://github.com/JFreegman/toxic/pull/119) ([ghost](https://github.com/ghost)) | ||||
| - Use default terminal fg/bg colors when we can. [\#117](https://github.com/JFreegman/toxic/pull/117) ([ooesili](https://github.com/ooesili)) | ||||
| - Fixed support for wide characters [\#113](https://github.com/JFreegman/toxic/pull/113) ([graboy](https://github.com/graboy)) | ||||
| - Mention av [\#110](https://github.com/JFreegman/toxic/pull/110) ([stqism](https://github.com/stqism)) | ||||
| - allow history scrolling [\#109](https://github.com/JFreegman/toxic/pull/109) ([JFreegman](https://github.com/JFreegman)) | ||||
| - Only those who appreciate small things [\#107](https://github.com/JFreegman/toxic/pull/107) ([ghost](https://github.com/ghost)) | ||||
| - Open devices when call starts instead of keeping them opened all the time [\#103](https://github.com/JFreegman/toxic/pull/103) ([ghost](https://github.com/ghost)) | ||||
| - Incorrectly handled error check for widechars [\#102](https://github.com/JFreegman/toxic/pull/102) ([graboy](https://github.com/graboy)) | ||||
| - Fix toxic build when toxav is not available [\#100](https://github.com/JFreegman/toxic/pull/100) ([jin-eld](https://github.com/jin-eld)) | ||||
| - Add checks for pthreads to the build system [\#99](https://github.com/JFreegman/toxic/pull/99) ([jin-eld](https://github.com/jin-eld)) | ||||
| - Fixes and stuff... [\#97](https://github.com/JFreegman/toxic/pull/97) ([ghost](https://github.com/ghost)) | ||||
|  | ||||
| ## [0.3.0.1](https://github.com/JFreegman/toxic/tree/0.3.0.1) (2014-03-12) | ||||
| [Full Changelog](https://github.com/JFreegman/toxic/compare/0.3.0...0.3.0.1) | ||||
|  | ||||
| **Merged pull requests:** | ||||
|  | ||||
| - SPELLING IS FOR FOOLS [\#94](https://github.com/JFreegman/toxic/pull/94) ([lehitoskin](https://github.com/lehitoskin)) | ||||
|  | ||||
| ## [0.3.0](https://github.com/JFreegman/toxic/tree/0.3.0) (2014-03-12) | ||||
| [Full Changelog](https://github.com/JFreegman/toxic/compare/0.2.7...0.3.0) | ||||
|  | ||||
| **Fixed bugs:** | ||||
|  | ||||
| - SIGSEVG upon friend hanging up [\#89](https://github.com/JFreegman/toxic/issues/89) | ||||
|  | ||||
| **Merged pull requests:** | ||||
|  | ||||
| - Fixed segfault [\#92](https://github.com/JFreegman/toxic/pull/92) ([ghost](https://github.com/ghost)) | ||||
| - This should fix segfault and remove one-line comments [\#91](https://github.com/JFreegman/toxic/pull/91) ([ghost](https://github.com/ghost)) | ||||
| - Fixed another clang issue with bools that broek file sending. [\#90](https://github.com/JFreegman/toxic/pull/90) ([Jman012](https://github.com/Jman012)) | ||||
| - Toxic audio support [\#88](https://github.com/JFreegman/toxic/pull/88) ([ghost](https://github.com/ghost)) | ||||
| - Fixed clang error, disabling the execute module. [\#87](https://github.com/JFreegman/toxic/pull/87) ([Jman012](https://github.com/Jman012)) | ||||
| - Issue \#84 fixed [\#86](https://github.com/JFreegman/toxic/pull/86) ([thevar1able](https://github.com/thevar1able)) | ||||
| - Fixing fall-back from IPv6 to IPv4 [\#85](https://github.com/JFreegman/toxic/pull/85) ([micrictor](https://github.com/micrictor)) | ||||
|  | ||||
| ## [0.2.7](https://github.com/JFreegman/toxic/tree/0.2.7) (2014-03-01) | ||||
| [Full Changelog](https://github.com/JFreegman/toxic/compare/0.2.6.1...0.2.7) | ||||
|  | ||||
| **Closed issues:** | ||||
|  | ||||
| - Toxic segfault when window is closed [\#81](https://github.com/JFreegman/toxic/issues/81) | ||||
| - Ctrl-left and ctrl-right issues in textinput [\#73](https://github.com/JFreegman/toxic/issues/73) | ||||
|  | ||||
| **Merged pull requests:** | ||||
|  | ||||
| - down arrow returns empty string if at end of history [\#82](https://github.com/JFreegman/toxic/pull/82) ([kl4ng](https://github.com/kl4ng)) | ||||
| - Fallback to loading /usr/share/toxic/DHTservers. [\#80](https://github.com/JFreegman/toxic/pull/80) ([viric](https://github.com/viric)) | ||||
|  | ||||
| ## [0.2.6.1](https://github.com/JFreegman/toxic/tree/0.2.6.1) (2014-02-23) | ||||
| [Full Changelog](https://github.com/JFreegman/toxic/compare/0.2.6...0.2.6.1) | ||||
|  | ||||
| ## [0.2.6](https://github.com/JFreegman/toxic/tree/0.2.6) (2014-02-23) | ||||
| [Full Changelog](https://github.com/JFreegman/toxic/compare/0.2.5...0.2.6) | ||||
|  | ||||
| ## [0.2.5](https://github.com/JFreegman/toxic/tree/0.2.5) (2014-02-22) | ||||
| [Full Changelog](https://github.com/JFreegman/toxic/compare/prealpha_win32_r8...0.2.5) | ||||
|  | ||||
| **Fixed bugs:** | ||||
|  | ||||
| - Back space leaves ć character [\#44](https://github.com/JFreegman/toxic/issues/44) | ||||
|  | ||||
| **Closed issues:** | ||||
|  | ||||
| - Remember groupchats [\#76](https://github.com/JFreegman/toxic/issues/76) | ||||
| - Segfault [\#75](https://github.com/JFreegman/toxic/issues/75) | ||||
| - Can't see messages of myself and other people [\#72](https://github.com/JFreegman/toxic/issues/72) | ||||
| - binary blob in source [\#66](https://github.com/JFreegman/toxic/issues/66) | ||||
| - symbol lookup error [\#54](https://github.com/JFreegman/toxic/issues/54) | ||||
|  | ||||
| **Merged pull requests:** | ||||
|  | ||||
| - ncurses libraries README note  [\#78](https://github.com/JFreegman/toxic/pull/78) ([kl4ng](https://github.com/kl4ng)) | ||||
| - umask such that stored files are u+rw only [\#77](https://github.com/JFreegman/toxic/pull/77) ([alevy](https://github.com/alevy)) | ||||
| - Fix groupchat cursor movement. [\#63](https://github.com/JFreegman/toxic/pull/63) ([aitjcize](https://github.com/aitjcize)) | ||||
| - Fix wchar cursor movement. [\#62](https://github.com/JFreegman/toxic/pull/62) ([aitjcize](https://github.com/aitjcize)) | ||||
| - api update [\#61](https://github.com/JFreegman/toxic/pull/61) ([naxuroqa](https://github.com/naxuroqa)) | ||||
| - Add option to switch off ipv6. [\#60](https://github.com/JFreegman/toxic/pull/60) ([aitjcize](https://github.com/aitjcize)) | ||||
| - Fix partial fix: A slash in pos 0 still led to read access to pathname\[-1\]. [\#59](https://github.com/JFreegman/toxic/pull/59) ([FullName](https://github.com/FullName)) | ||||
| - Fix corresponding API name changes in toxcore. [\#58](https://github.com/JFreegman/toxic/pull/58) ([aitjcize](https://github.com/aitjcize)) | ||||
| - Fix API ret code changes of ToxCore [\#57](https://github.com/JFreegman/toxic/pull/57) ([aitjcize](https://github.com/aitjcize)) | ||||
|  | ||||
| ## [prealpha_win32_r8](https://github.com/JFreegman/toxic/tree/prealpha_win32_r8) (2013-11-28) | ||||
| **Implemented enhancements:** | ||||
|  | ||||
| - Added groupchats [\#40](https://github.com/JFreegman/toxic/pull/40) ([JFreegman](https://github.com/JFreegman)) | ||||
| - Adapted to ipv6-enabled tox [\#38](https://github.com/JFreegman/toxic/pull/38) ([FullName](https://github.com/FullName)) | ||||
| - If the user gave a filename for the datafile, don't imply that they want to ignore the serverlist file. [\#37](https://github.com/JFreegman/toxic/pull/37) ([FullName](https://github.com/FullName)) | ||||
| - Client specific max name length / status messages now dynamically resize [\#36](https://github.com/JFreegman/toxic/pull/36) ([JFreegman](https://github.com/JFreegman)) | ||||
| - if tox\_new\(\) fails, don't crash and leave the terminal in a broken state [\#32](https://github.com/JFreegman/toxic/pull/32) ([FullName](https://github.com/FullName)) | ||||
| - truncate friends' notes if they're too long [\#30](https://github.com/JFreegman/toxic/pull/30) ([JFreegman](https://github.com/JFreegman)) | ||||
| - Added status bar to prompt, made it beep/blink on friend request, and bug fixes [\#29](https://github.com/JFreegman/toxic/pull/29) ([JFreegman](https://github.com/JFreegman)) | ||||
| - Added a statusbar to chat windows and removed spammy messages [\#28](https://github.com/JFreegman/toxic/pull/28) ([JFreegman](https://github.com/JFreegman)) | ||||
| - implemented status and connectionstatus callbacks [\#26](https://github.com/JFreegman/toxic/pull/26) ([JFreegman](https://github.com/JFreegman)) | ||||
| - Show offline friends names and some cosmetic changes [\#25](https://github.com/JFreegman/toxic/pull/25) ([JFreegman](https://github.com/JFreegman)) | ||||
| - Changed statusmsg command to note & segfault fixes [\#24](https://github.com/JFreegman/toxic/pull/24) ([JFreegman](https://github.com/JFreegman)) | ||||
| - refactor command argument parsing [\#23](https://github.com/JFreegman/toxic/pull/23) ([lukechampine](https://github.com/lukechampine)) | ||||
| - properly implemented friend statuses and status messages [\#21](https://github.com/JFreegman/toxic/pull/21) ([JFreegman](https://github.com/JFreegman)) | ||||
| - implemented friend deletion [\#15](https://github.com/JFreegman/toxic/pull/15) ([JFreegman](https://github.com/JFreegman)) | ||||
| - Fix configure for Free BSD [\#11](https://github.com/JFreegman/toxic/pull/11) ([jin-eld](https://github.com/jin-eld)) | ||||
| - Add check for setlocale\(\) [\#10](https://github.com/JFreegman/toxic/pull/10) ([manuel-arguelles](https://github.com/manuel-arguelles)) | ||||
| - Update build system [\#7](https://github.com/JFreegman/toxic/pull/7) ([jin-eld](https://github.com/jin-eld)) | ||||
| - Added travis integration [\#6](https://github.com/JFreegman/toxic/pull/6) ([stqism](https://github.com/stqism)) | ||||
| - Use new public api [\#5](https://github.com/JFreegman/toxic/pull/5) ([fhahn](https://github.com/fhahn)) | ||||
| - Add widechar checks [\#2](https://github.com/JFreegman/toxic/pull/2) ([jin-eld](https://github.com/jin-eld)) | ||||
|  | ||||
| **Fixed bugs:** | ||||
|  | ||||
| - Let windows.c actually get the tox \*m. [\#41](https://github.com/JFreegman/toxic/pull/41) ([Jman012](https://github.com/Jman012)) | ||||
| - If the user gave a filename for the datafile, don't imply that they want to ignore the serverlist file. [\#37](https://github.com/JFreegman/toxic/pull/37) ([FullName](https://github.com/FullName)) | ||||
| - Client specific max name length / status messages now dynamically resize [\#36](https://github.com/JFreegman/toxic/pull/36) ([JFreegman](https://github.com/JFreegman)) | ||||
| - Merged pr6 [\#34](https://github.com/JFreegman/toxic/pull/34) ([stqism](https://github.com/stqism)) | ||||
| - made error handling more consistent and added exit function [\#33](https://github.com/JFreegman/toxic/pull/33) ([JFreegman](https://github.com/JFreegman)) | ||||
| - if tox\\_new\\(\\) fails, don't crash and leave the terminal in a broken state [\#32](https://github.com/JFreegman/toxic/pull/32) ([FullName](https://github.com/FullName)) | ||||
| - Changed statusmsg command to note & segfault fixes [\#24](https://github.com/JFreegman/toxic/pull/24) ([JFreegman](https://github.com/JFreegman)) | ||||
| - fix buffer overflows and format issues [\#20](https://github.com/JFreegman/toxic/pull/20) ([JFreegman](https://github.com/JFreegman)) | ||||
| - Fix blocking while waiting for key [\#17](https://github.com/JFreegman/toxic/pull/17) ([manuel-arguelles](https://github.com/manuel-arguelles)) | ||||
| - fixed "free\(\): invalid pointer" when XDG\_CONFIG\_HOME is set [\#16](https://github.com/JFreegman/toxic/pull/16) ([gs93](https://github.com/gs93)) | ||||
| - Make sure toxic compiles on MinGW/Win32 again [\#14](https://github.com/JFreegman/toxic/pull/14) ([jin-eld](https://github.com/jin-eld)) | ||||
| - Fix for the "bad character" when doing backspace in chat window [\#12](https://github.com/JFreegman/toxic/pull/12) ([jin-eld](https://github.com/jin-eld)) | ||||
| - Fix configure for Free BSD [\#11](https://github.com/JFreegman/toxic/pull/11) ([jin-eld](https://github.com/jin-eld)) | ||||
| - Fix configure script for ncurses without ncursesw [\#9](https://github.com/JFreegman/toxic/pull/9) ([manuel-arguelles](https://github.com/manuel-arguelles)) | ||||
| - Fix configure script for mingw32 [\#8](https://github.com/JFreegman/toxic/pull/8) ([jin-eld](https://github.com/jin-eld)) | ||||
| - warning: comparison of integers of different signs: 'int' and 'unsigned long' [\#3](https://github.com/JFreegman/toxic/pull/3) ([1100110](https://github.com/1100110)) | ||||
|  | ||||
| **Merged pull requests:** | ||||
|  | ||||
| - Make sure friend message is null-terminated else generate garbate on screen [\#56](https://github.com/JFreegman/toxic/pull/56) ([aitjcize](https://github.com/aitjcize)) | ||||
| - Fix trailing slashes which leads to segfault. [\#55](https://github.com/JFreegman/toxic/pull/55) ([aitjcize](https://github.com/aitjcize)) | ||||
| - fix cflags [\#53](https://github.com/JFreegman/toxic/pull/53) ([JFreegman](https://github.com/JFreegman)) | ||||
| - Fix 93ab16c [\#52](https://github.com/JFreegman/toxic/pull/52) ([urras](https://github.com/urras)) | ||||
| - Offer solution for "error while loading shared libraries: libtoxcore.so.... [\#51](https://github.com/JFreegman/toxic/pull/51) ([urras](https://github.com/urras)) | ||||
| - Implemented file transfers [\#50](https://github.com/JFreegman/toxic/pull/50) ([JFreegman](https://github.com/JFreegman)) | ||||
| - Fix check for toxcore by linking sodium in the correct place [\#47](https://github.com/JFreegman/toxic/pull/47) ([devurandom](https://github.com/devurandom)) | ||||
| - Changed order of servers [\#46](https://github.com/JFreegman/toxic/pull/46) ([grimd34th](https://github.com/grimd34th)) | ||||
| - set friendnames properly and some fixes [\#45](https://github.com/JFreegman/toxic/pull/45) ([JFreegman](https://github.com/JFreegman)) | ||||
| - moved misc helper functions to separate file and removed redundant includes [\#43](https://github.com/JFreegman/toxic/pull/43) ([JFreegman](https://github.com/JFreegman)) | ||||
| - Refactored prompt command parser to work with all window types and moved command stuff to separate files [\#42](https://github.com/JFreegman/toxic/pull/42) ([JFreegman](https://github.com/JFreegman)) | ||||
| - Ipv6.init connection [\#39](https://github.com/JFreegman/toxic/pull/39) ([FullName](https://github.com/FullName)) | ||||
| - Remove DHT window [\#13](https://github.com/JFreegman/toxic/pull/13) ([JFreegman](https://github.com/JFreegman)) | ||||
| - Update README.md [\#4](https://github.com/JFreegman/toxic/pull/4) ([notadecent](https://github.com/notadecent)) | ||||
| - Toxic standalone [\#1](https://github.com/JFreegman/toxic/pull/1) ([jin-eld](https://github.com/jin-eld)) | ||||
|  | ||||
|  | ||||
|  | ||||
| \* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* | ||||
							
								
								
									
										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. | ||||
|  | ||||
							
								
								
									
										70
									
								
								INSTALL.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								INSTALL.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,70 @@ | ||||
| # Installation | ||||
| * [Dependencies](#dependencies) | ||||
|   * [OS X Notes](#os-x-notes) | ||||
| * [Compiling](#compiling) | ||||
|   * [Documentation](#documentation) | ||||
| * [Notes](#notes) | ||||
|   * [Compilation variables](#compilation-variables) | ||||
|   * [Environment variables](#environment-variables) | ||||
|  | ||||
| ## Dependencies | ||||
| | Name                                                 | Needed by                  | Debian package      | | ||||
| |------------------------------------------------------|----------------------------|---------------------| | ||||
| | [Tox Core](https://github.com/toktok/c-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                | | ||||
| | [libcurl](http://curl.haxx.se/)                      | BASE                       | libcurl4-openssl-dev| | ||||
| | [libqrencode](https://fukuchi.org/works/qrencode/)   | QRCODE                     | libqrencode-dev     | | ||||
| | [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       | | ||||
| | [Python 3](http://www.python.org/)                   | PYTHON                     | python3-dev         | | ||||
| | [AsciiDoc](http://asciidoc.org/index.html)           | DOCUMENTATION<sup>1</sup>  | asciidoc            | | ||||
|  | ||||
| <sup>1</sup>: see [Documentation](#documentation) | ||||
|  | ||||
| #### OS X Notes | ||||
| Using [Homebrew](http://brew.sh): | ||||
| ``` | ||||
| brew install curl qrencode openal-soft freealut libconfig libpng | ||||
| brew install --HEAD https://raw.githubusercontent.com/Tox/homebrew-tox/master/Formula/libtoxcore.rb | ||||
| brew install libnotify | ||||
| export PKG_CONFIG_PATH=/usr/local/opt/openal-soft/lib/pkgconfig | ||||
| make | ||||
| ``` | ||||
|  | ||||
| You can omit `libnotify` if you intend to build without desktop notifications enabled. | ||||
|  | ||||
| ## Compiling | ||||
| ``` | ||||
| make | ||||
| sudo make install | ||||
| ``` | ||||
|  | ||||
| #### 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 committed together.<br /> | ||||
| **Note for everyone**: [asciidoc](http://asciidoc.org/index.html) (and this step) is only required for regenerating manpages when you modify them. | ||||
|  | ||||
| ## Notes | ||||
|  | ||||
| #### Compilation variables | ||||
| * You can add specific flags to the Makefile with `USER_CFLAGS=""` and `USER_LDFLAGS=""` passed as arguments to make, or as environment variables | ||||
| * Default compile options can be overridden by using special variables: | ||||
|   * `DISABLE_X11=1` → Disable X11 support (needed for focus tracking) | ||||
|   * `DISABLE_AV=1` → Disable audio call support | ||||
|   * `DISABLE_SOUND_NOTIFY=1` → Disable sound notifications support | ||||
|   * `DISABLE_QRCODE` → Disable QR exporting support | ||||
|   * `DISABLE_QRPNG` → Disable support for exporting QR as PNG | ||||
|   * `DISABLE_DESKTOP_NOTIFY=1` → Disable desktop notifications support | ||||
|   * `ENABLE_PYTHON=1` → Build toxic with Python scripting support | ||||
|   * `ENABLE_RELEASE=1` → Build toxic without debug symbols and with full compiler optimizations | ||||
|   * `ENABLE_ASAN=1` → Build toxic with LLVM Address Sanitizer enabled | ||||
|  | ||||
| * `DESTDIR=""` Specifies the base install directory for binaries and data files (e.g.: DESTDIR="/tmp/build/pkg") | ||||
|  | ||||
| #### Environment variables | ||||
| * You can use the `CFLAGS` and `LDFLAGS` environment variables to add specific flags to the Makefile | ||||
| * The `PREFIX` environment variable specifies a base install directory for binaries and data files. This is interchangeable with the `DESTDIR` variable, and is generally used by systems that have the `PREFIX` environment variable set by default.<br /> | ||||
| **Note**: `sudo` does not preserve user environment variables by default on some systems. See the `sudoers` manual for more information. | ||||
| @@ -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>. | ||||
| 
 | ||||
							
								
								
									
										97
									
								
								Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								Makefile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,97 @@ | ||||
| BASE_DIR = $(shell pwd -P) | ||||
| CFG_DIR = $(BASE_DIR)/cfg | ||||
|  | ||||
| -include $(CFG_DIR)/global_vars.mk | ||||
|  | ||||
| LIBS = toxcore ncursesw libconfig libcurl | ||||
|  | ||||
| CFLAGS ?= -std=c99 -pthread -Wall -Wpedantic -Wunused -fstack-protector-all -Wvla -Wno-missing-braces | ||||
| CFLAGS += '-DTOXICVER="$(VERSION)"' -DHAVE_WIDECHAR -D_XOPEN_SOURCE_EXTENDED -D_FILE_OFFSET_BITS=64 | ||||
| CFLAGS += '-DPACKAGE_DATADIR="$(abspath $(DATADIR))"' | ||||
| CFLAGS += ${USER_CFLAGS} | ||||
| LDFLAGS ?= | ||||
| LDFLAGS += ${USER_LDFLAGS} | ||||
|  | ||||
| OBJ = autocomplete.o avatars.o bootstrap.o chat.o chat_commands.o configdir.o curl_util.o execute.o | ||||
| OBJ += file_transfers.o friendlist.o global_commands.o conference_commands.o conference.o help.o input.o | ||||
| OBJ += line_info.o log.o message_queue.o misc_tools.o name_lookup.o notify.o prompt.o qr_code.o settings.o | ||||
| OBJ += term_mplex.o toxic.o toxic_strings.o windows.o | ||||
|  | ||||
| # Check if debug build is enabled | ||||
| RELEASE := $(shell if [ -z "$(ENABLE_RELEASE)" ] || [ "$(ENABLE_RELEASE)" = "0" ] ; then echo disabled ; else echo enabled ; fi) | ||||
| ifneq ($(RELEASE), enabled) | ||||
| 	CFLAGS += -O0 -g -DDEBUG | ||||
| 	LDFLAGS += -O0 | ||||
| else | ||||
| 	CFLAGS += -O2 -flto | ||||
| 	LDFLAGS += -O2 -flto | ||||
| endif | ||||
|  | ||||
| # Check if LLVM Address Sanitizer is enabled | ||||
| ENABLE_ASAN := $(shell if [ -z "$(ENABLE_ASAN)" ] || [ "$(ENABLE_ASAN)" = "0" ] ; then echo disabled ; else echo enabled ; fi) | ||||
| ifneq ($(ENABLE_ASAN), disabled) | ||||
| 	CFLAGS += -fsanitize=address -fno-omit-frame-pointer | ||||
| endif | ||||
|  | ||||
| # Check on wich system we are running | ||||
| UNAME_S = $(shell uname -s) | ||||
| ifeq ($(UNAME_S), Linux) | ||||
| LDFLAGS += -ldl -lrt | ||||
| endif | ||||
| ifeq ($(UNAME_S), OpenBSD) | ||||
| LIBS := $(filter-out ncursesw, $(LIBS)) | ||||
| LDFLAGS += -lncursesw | ||||
| endif | ||||
| ifeq ($(UNAME_S), NetBSD) | ||||
| LIBS := $(filter-out ncursesw, $(LIBS)) | ||||
| LDFLAGS += -lncursesw | ||||
| endif | ||||
| ifeq ($(UNAME_S), Darwin) | ||||
|     -include $(CFG_DIR)/systems/Darwin.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)/osx_video.o: $(SRC_DIR)/$(OSX_VIDEO) | ||||
| 	@echo "  CC    $(@:$(BUILD_DIR)/)osx_video.o" | ||||
| 	@$(CC) $(CFLAGS) -o $(BUILD_DIR)/osx_video.o -c $(SRC_DIR)/$(OSX_VIDEO) | ||||
|  | ||||
| $(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 | ||||
							
								
								
									
										29
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,20 +1,27 @@ | ||||
| ## Toxic - console client for [Tox](http://tox.im) | ||||
| <a href="https://scan.coverity.com/projects/toxic-tox"> | ||||
|   <img alt="Coverity Scan Build Status" | ||||
|        src="https://scan.coverity.com/projects/4975/badge.svg"/> | ||||
| </a> | ||||
|  | ||||
| The client formerly resided in the [Tox core repository](https://github.com/irungentoo/ProjectTox-Core) and is now available as a standalone program. It looks like [this](http://wiki.tox.im/images/b/b6/Ncursesclient1.png). | ||||
| Toxic is a [Tox](https://tox.chat)-based instant messaging and video chat client. | ||||
|  | ||||
| To compile, first generate the configure script by running the ```autoreconf -i``` command. | ||||
| [](https://i.imgur.com/TwYA8L0.png) | ||||
|  | ||||
| Then execute the configure script with ./configure (you may need to pass it the location of your dependency libraries, i.e.): | ||||
| ## Installation | ||||
| [See the install instructions](/INSTALL.md) | ||||
|  | ||||
| ## Settings | ||||
| Running Toxic for the first time creates an empty file called toxic.conf in your home configuration directory ("~/.config/tox" for Linux users). Adding options to this file allows you to enable auto-logging, change the time format (12/24 hour), and much more. | ||||
| You can view our example config file [here](misc/toxic.conf.example). | ||||
|  | ||||
| ## Troubleshooting | ||||
| If your default prefix is "/usr/local" and you receive the following: | ||||
| ``` | ||||
| ./configure --prefix=/where/to/install --with-libtoxcore-headers=/path/to/ProjectTox-Core/core --with-libtoxcore-libs=/path/to/ProjectTox-Core/build/core --with-libsodium-headers=/path/to/libsodium/include/ --with-libsodium-libs=/path/to/sodiumtest/lib/ | ||||
|  | ||||
| error while loading shared libraries: libtoxcore.so.0: cannot open shared object file: No such file or directory | ||||
| ``` | ||||
| *Note:* If your default prefix is /usr/local and you happen to get an error that says "error while loading shared libraries: libtoxcore.so.0: cannot open shared object file: No such file or directory", then you can try running ```sudo ldconfig```. If that doesn't fix it, run: | ||||
| you can attempt to correct it by running `sudo ldconfig`. If that doesn't work, run: | ||||
| ``` | ||||
| echo '/usr/local/lib/' | sudo tee -a /etc/ld.so.conf.d/locallib.conf | ||||
| sudo ldconfig | ||||
| ``` | ||||
| If you dont already have them, you might want to install the ncurses libraries, on Debian: | ||||
| ``` | ||||
| sudo apt-get install libncurses5-dev libncursesw5-dev | ||||
| ``` | ||||
|  | ||||
|   | ||||
							
								
								
									
										20
									
								
								apidoc/python/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								apidoc/python/Makefile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| # Minimal makefile for Sphinx documentation | ||||
| # | ||||
|  | ||||
| # You can set these variables from the command line. | ||||
| SPHINXOPTS    = | ||||
| SPHINXBUILD   = sphinx-build | ||||
| SPHINXPROJ    = toxic_api | ||||
| SOURCEDIR     = source | ||||
| BUILDDIR      = build | ||||
|  | ||||
| # Put it first so that "make" without argument is like "make help". | ||||
| help: | ||||
| 	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) | ||||
|  | ||||
| .PHONY: help Makefile | ||||
|  | ||||
| # Catch-all target: route all unknown targets to Sphinx using the new | ||||
| # "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS). | ||||
| %: Makefile | ||||
| 	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) | ||||
							
								
								
									
										157
									
								
								apidoc/python/source/conf.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								apidoc/python/source/conf.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,157 @@ | ||||
| #!/usr/bin/env python3 | ||||
| # -*- coding: utf-8 -*- | ||||
| # | ||||
| # toxic_api documentation build configuration file, created by | ||||
| # sphinx-quickstart on Tue May 16 08:58:24 2017. | ||||
| # | ||||
| # This file is execfile()d with the current directory set to its | ||||
| # containing dir. | ||||
| # | ||||
| # Note that not all possible configuration values are present in this | ||||
| # autogenerated file. | ||||
| # | ||||
| # All configuration values have a default; values that are commented out | ||||
| # serve to show the default. | ||||
|  | ||||
| # If extensions (or modules to document with autodoc) are in another directory, | ||||
| # add these directories to sys.path here. If the directory is relative to the | ||||
| # documentation root, use os.path.abspath to make it absolute, like shown here. | ||||
| # | ||||
| # import os | ||||
| # import sys | ||||
| # sys.path.insert(0, os.path.abspath('.')) | ||||
|  | ||||
|  | ||||
| # -- General configuration ------------------------------------------------ | ||||
|  | ||||
| # If your documentation needs a minimal Sphinx version, state it here. | ||||
| # | ||||
| # needs_sphinx = '1.0' | ||||
|  | ||||
| # Add any Sphinx extension module names here, as strings. They can be | ||||
| # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom | ||||
| # ones. | ||||
| extensions = [] | ||||
|  | ||||
| # Add any paths that contain templates here, relative to this directory. | ||||
| templates_path = ['_templates'] | ||||
|  | ||||
| # The suffix(es) of source filenames. | ||||
| # You can specify multiple suffix as a list of string: | ||||
| # | ||||
| # source_suffix = ['.rst', '.md'] | ||||
| source_suffix = '.rst' | ||||
|  | ||||
| # The master toctree document. | ||||
| master_doc = 'index' | ||||
|  | ||||
| # General information about the project. | ||||
| project = 'toxic_api' | ||||
| copyright = '2017, Jakob Kreuze' | ||||
| author = 'Jakob Kreuze' | ||||
|  | ||||
| # The version info for the project you're documenting, acts as replacement for | ||||
| # |version| and |release|, also used in various other places throughout the | ||||
| # built documents. | ||||
| # | ||||
| # The short X.Y version. | ||||
| version = '0.10.0' | ||||
| # The full version, including alpha/beta/rc tags. | ||||
| release = '0.10.0' | ||||
|  | ||||
| # The language for content autogenerated by Sphinx. Refer to documentation | ||||
| # for a list of supported languages. | ||||
| # | ||||
| # This is also used if you do content translation via gettext catalogs. | ||||
| # Usually you set "language" from the command line for these cases. | ||||
| language = None | ||||
|  | ||||
| # List of patterns, relative to source directory, that match files and | ||||
| # directories to ignore when looking for source files. | ||||
| # This patterns also effect to html_static_path and html_extra_path | ||||
| exclude_patterns = [] | ||||
|  | ||||
| # The name of the Pygments (syntax highlighting) style to use. | ||||
| pygments_style = 'sphinx' | ||||
|  | ||||
| # If true, `todo` and `todoList` produce output, else they produce nothing. | ||||
| todo_include_todos = False | ||||
|  | ||||
|  | ||||
| # -- Options for HTML output ---------------------------------------------- | ||||
|  | ||||
| # The theme to use for HTML and HTML Help pages.  See the documentation for | ||||
| # a list of builtin themes. | ||||
| # | ||||
| html_theme = 'alabaster' | ||||
|  | ||||
| # Theme options are theme-specific and customize the look and feel of a theme | ||||
| # further.  For a list of options available for each theme, see the | ||||
| # documentation. | ||||
| # | ||||
| # html_theme_options = {} | ||||
|  | ||||
| # Add any paths that contain custom static files (such as style sheets) here, | ||||
| # relative to this directory. They are copied after the builtin static files, | ||||
| # so a file named "default.css" will overwrite the builtin "default.css". | ||||
| html_static_path = ['_static'] | ||||
|  | ||||
|  | ||||
| # -- Options for HTMLHelp output ------------------------------------------ | ||||
|  | ||||
| # Output file base name for HTML help builder. | ||||
| htmlhelp_basename = 'toxic_apidoc' | ||||
|  | ||||
|  | ||||
| # -- Options for LaTeX output --------------------------------------------- | ||||
|  | ||||
| latex_elements = { | ||||
|     # The paper size ('letterpaper' or 'a4paper'). | ||||
|     # | ||||
|     # 'papersize': 'letterpaper', | ||||
|  | ||||
|     # The font size ('10pt', '11pt' or '12pt'). | ||||
|     # | ||||
|     # 'pointsize': '10pt', | ||||
|  | ||||
|     # Additional stuff for the LaTeX preamble. | ||||
|     # | ||||
|     # 'preamble': '', | ||||
|  | ||||
|     # Latex figure (float) alignment | ||||
|     # | ||||
|     # 'figure_align': 'htbp', | ||||
| } | ||||
|  | ||||
| # Grouping the document tree into LaTeX files. List of tuples | ||||
| # (source start file, target name, title, | ||||
| #  author, documentclass [howto, manual, or own class]). | ||||
| latex_documents = [ | ||||
|     (master_doc, 'toxic_api.tex', 'toxic\\_api Documentation', | ||||
|      'Jakob Kreuze', 'manual'), | ||||
| ] | ||||
|  | ||||
|  | ||||
| # -- Options for manual page output --------------------------------------- | ||||
|  | ||||
| # One entry per manual page. List of tuples | ||||
| # (source start file, name, description, authors, manual section). | ||||
| man_pages = [ | ||||
|     (master_doc, 'toxic_api', 'toxic_api Documentation', | ||||
|      [author], 1) | ||||
| ] | ||||
|  | ||||
|  | ||||
| # -- Options for Texinfo output ------------------------------------------- | ||||
|  | ||||
| # Grouping the document tree into Texinfo files. List of tuples | ||||
| # (source start file, target name, title, author, | ||||
| #  dir menu entry, description, category) | ||||
| texinfo_documents = [ | ||||
|     (master_doc, 'toxic_api', 'toxic_api Documentation', | ||||
|      author, 'toxic_api', 'One line description of project.', | ||||
|      'Miscellaneous'), | ||||
| ] | ||||
|  | ||||
|  | ||||
|  | ||||
							
								
								
									
										8
									
								
								apidoc/python/source/examples.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								apidoc/python/source/examples.rst
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| ============ | ||||
| API Examples | ||||
| ============ | ||||
|  | ||||
| Fortune | ||||
| ======= | ||||
| .. literalinclude:: fortune.py | ||||
|    :language: python | ||||
							
								
								
									
										37
									
								
								apidoc/python/source/fortune.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								apidoc/python/source/fortune.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| import toxic_api | ||||
| import random | ||||
|  | ||||
| FORTUNES = [ | ||||
|     "A bug in the code is worth two in the documentation.", | ||||
|     "A bug in the hand is better than one as yet undetected.", | ||||
|     "\"A debugged program is one for which you have not yet found the " | ||||
|     "conditions that make it fail.\" -- Jerry Ogdin" | ||||
| ] | ||||
|  | ||||
| def send_fortune(args): | ||||
|     """Callback function that sends the contact of the current window a | ||||
|     given number of random fortunes. | ||||
|     """ | ||||
|     if len(args) != 1: | ||||
|         toxic_api.display("Only one argument allowed!") | ||||
|         return | ||||
|  | ||||
|     try: | ||||
|         count = int(args[0]) | ||||
|     except ValueError: | ||||
|         toxic_api.display("Argument must be a number!") | ||||
|         return | ||||
|  | ||||
|     if count < 0 or count > 20: | ||||
|         toxic_api.display("Argument is too large!") | ||||
|         return | ||||
|  | ||||
|     name = toxic_api.get_nick() | ||||
|  | ||||
|     toxic_api.send("%s has decided to send you %d fortunes:" % (name, count)) | ||||
|     for _ in range(count): | ||||
|         toxic_api.send(random.choice(FORTUNES)) | ||||
|  | ||||
|  | ||||
| toxic_api.register("/fortune", "Send a fortune to the contact of the current " | ||||
|                    "window", send_fortune) | ||||
							
								
								
									
										9
									
								
								apidoc/python/source/index.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								apidoc/python/source/index.rst
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| Toxic Scripting Interface Documentation | ||||
| ======================================= | ||||
|  | ||||
| .. toctree:: | ||||
|    :maxdepth: 2 | ||||
|  | ||||
|    intro | ||||
|    reference | ||||
|    examples | ||||
							
								
								
									
										12
									
								
								apidoc/python/source/intro.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								apidoc/python/source/intro.rst
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| ========================= | ||||
| Toxic Scripting Interface | ||||
| ========================= | ||||
|  | ||||
| A Python scripting interface to `Toxic <https://github.com/JFreegman/toxic>`_. | ||||
|  | ||||
|  | ||||
| Getting Started | ||||
| =============== | ||||
| Toxic is compiled with Python support by default. To access the scripting interface, simply import "toxic_api" in your script. | ||||
|  | ||||
| Scripts can be run by issuing "/run <path>" from toxic, or placing them in the "autorun_path" from your toxic configuration file. | ||||
							
								
								
									
										73
									
								
								apidoc/python/source/reference.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								apidoc/python/source/reference.rst
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | ||||
| ============= | ||||
| API Reference | ||||
| ============= | ||||
|  | ||||
| Messages | ||||
| ======== | ||||
| .. function:: display(msg) | ||||
|  | ||||
|    Display a message to the user through the current window. | ||||
|  | ||||
|    :param msg: The message to display. | ||||
|    :type msg: string | ||||
|    :rtype: none | ||||
|  | ||||
| .. function:: send(msg) | ||||
|  | ||||
|    Send a message to the user specified by the currently open conversation. | ||||
|  | ||||
|    :param msg: The message to display. | ||||
|    :type msg: string | ||||
|    :rtype: none | ||||
|  | ||||
|  | ||||
| State | ||||
| ===== | ||||
| .. function:: get_nick() | ||||
|  | ||||
|    Return the user's current nickname. | ||||
|  | ||||
|    :rtype: string | ||||
|  | ||||
| .. function:: get_status() | ||||
|  | ||||
|    Return a string representing the user's current status. Can be either "online", "away", or "busy". | ||||
|  | ||||
|    :rtype: string | ||||
|  | ||||
| .. function:: get_status_message() | ||||
|  | ||||
|    Return the user's current status message. | ||||
|  | ||||
|    :rtype: string | ||||
|  | ||||
| .. function:: get_all_friends() | ||||
|  | ||||
|    Return a list of all the user's friends. | ||||
|  | ||||
|    :rtype: list of (string, string) tuples containing the nickname followed by their public key | ||||
|  | ||||
|  | ||||
| Commands | ||||
| ======== | ||||
| .. function:: execute(command, class) | ||||
|  | ||||
|    Executes the given command. The API exports three constants for the class parameter; GLOBAL_COMMAND, CHAT_COMMAND, and GROUPCHAT_COMMAND. | ||||
|  | ||||
|    :param command: The command to execute. | ||||
|    :type command: string | ||||
|    :param class: The class of the command. | ||||
|    :type class: int | ||||
|    :rtype: none | ||||
|  | ||||
| .. function:: register(command, help, callback) | ||||
|  | ||||
|    Register a callback to be executed whenever command is run. The callback function will be called with one argument, a list of arguments from when the user calls the command. | ||||
|  | ||||
|    :param command: The command to listen for. | ||||
|    :type command: string | ||||
|    :param help: A description of the command to be shown in the help menu. | ||||
|    :type help: string | ||||
|    :param callback: The function to be called. | ||||
|    :type callback: callable | ||||
|    :rtype: none | ||||
							
								
								
									
										26
									
								
								astylerc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								astylerc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| # Bracket Style Options | ||||
| --style=kr | ||||
|  | ||||
| # Tab Options | ||||
| --indent=spaces=4 | ||||
|  | ||||
| # Indentation Options | ||||
| --indent-switches | ||||
|  | ||||
| # Padding Options | ||||
| --pad-header | ||||
| --break-blocks | ||||
| --pad-oper | ||||
| --unpad-paren | ||||
| --align-pointer=name | ||||
| --align-reference=name | ||||
|  | ||||
| # Formatting Options | ||||
| --add-brackets | ||||
| --convert-tabs | ||||
| --max-code-length=120 | ||||
|  | ||||
| # Other Options | ||||
| --preserve-date | ||||
| --formatted | ||||
| --lineend=linux | ||||
| @@ -1,43 +0,0 @@ | ||||
| #Don't change this unless needed, else you'll break stuff | ||||
|  | ||||
| bin_PROGRAMS = toxic | ||||
|  | ||||
|  | ||||
| toxic_SOURCES = $(top_srcdir)/src/main.c \ | ||||
|                 $(top_srcdir)/src/chat.h \ | ||||
|                 $(top_srcdir)/src/chat.c \ | ||||
|                 $(top_srcdir)/src/configdir.h \ | ||||
|                 $(top_srcdir)/src/configdir.c \ | ||||
|                 $(top_srcdir)/src/prompt.h \ | ||||
|                 $(top_srcdir)/src/prompt.c \ | ||||
|                 $(top_srcdir)/src/friendlist.h \ | ||||
|                 $(top_srcdir)/src/friendlist.c \ | ||||
|                 $(top_srcdir)/src/toxic_windows.h \ | ||||
|                 $(top_srcdir)/src/windows.c \ | ||||
|                 $(top_srcdir)/src/groupchat.c \ | ||||
|                 $(top_srcdir)/src/groupchat.h \ | ||||
|                 $(top_srcdir)/src/global_commands.c \ | ||||
|                 $(top_srcdir)/src/global_commands.h \ | ||||
|                 $(top_srcdir)/src/chat_commands.c \ | ||||
|                 $(top_srcdir)/src/chat_commands.h \ | ||||
|                 $(top_srcdir)/src/execute.c \ | ||||
|                 $(top_srcdir)/src/execute.h \ | ||||
|                 $(top_srcdir)/src/misc_tools.c \ | ||||
|                 $(top_srcdir)/src/misc_tools.h \ | ||||
|                 $(top_srcdir)/src/toxic_strings.c \ | ||||
|                 $(top_srcdir)/src/toxic_strings.h | ||||
|  | ||||
| toxic_CFLAGS =                 -I$(top_srcdir) \ | ||||
| 				$(NCURSES_CFLAGS) \ | ||||
| 				$(LIBSODIUM_CFLAGS) \ | ||||
| 				$(LIBTOXCORE_CFLAGS) | ||||
|  | ||||
| toxic_CPPFLAGS = '-DTOXICVER="$(TOXIC_VERSION)"' | ||||
|  | ||||
| toxic_LDADD =	$(LIBTOXCORE_LDFLAGS) \ | ||||
| 				$(LIBSODIUM_LDFLAGS) \ | ||||
| 				$(NCURSES_LIBS) \ | ||||
| 				$(LIBTOXCORE_LIBS) \ | ||||
| 				$(LIBSODIUM_LIBS) \ | ||||
|                 $(WINSOCK2_LIBS) | ||||
|  | ||||
							
								
								
									
										22
									
								
								cfg/checks/audio.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								cfg/checks/audio.mk
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| # Variables for audio call support | ||||
| AUDIO_LIBS = openal | ||||
| AUDIO_CFLAGS = -DAUDIO | ||||
| ifneq (, $(findstring audio_device.o, $(OBJ))) | ||||
|     AUDIO_OBJ = audio_call.o | ||||
| else | ||||
|     AUDIO_OBJ = audio_call.o audio_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) | ||||
|     LDFLAGS += -lm | ||||
|     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 | ||||
							
								
								
									
										66
									
								
								cfg/checks/check_features.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								cfg/checks/check_features.mk
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | ||||
| 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)/audio.mk | ||||
| endif | ||||
|  | ||||
| # Check if we want build video support | ||||
| VIDEO := $(shell if [ -z "$(DISABLE_VI)" ] || [ "$(DISABLE_VI)" = "0" ] ; then echo enabled ; else echo disabled ; fi) | ||||
| ifneq ($(X11), disabled) | ||||
| ifneq ($(AUDIO), disabled) | ||||
| ifneq ($(VIDEO), disabled) | ||||
|     -include $(CHECKS_DIR)/video.mk | ||||
| endif | ||||
| endif | ||||
| 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 want build QR export support | ||||
| QR_CODE := $(shell if [ -z "$(DISABLE_QRCODE)" ] || [ "$(DISABLE_QRCODE)" = "0" ] ; then echo enabled ; else echo disabled ; fi) | ||||
| ifneq ($(QR_CODE), disabled) | ||||
|     -include $(CHECKS_DIR)/qr.mk | ||||
| endif | ||||
|  | ||||
| # Check if we want build QR exported as PNG support | ||||
| QR_PNG := $(shell if [ -z "$(DISABLE_QRPNG)" ] || [ "$(DISABLE_QRPNG)" = "0" ] ; then echo enabled ; else echo disabled ; fi) | ||||
| ifneq ($(QR_PNG), disabled) | ||||
|     -include $(CHECKS_DIR)/qr_png.mk | ||||
| endif | ||||
|  | ||||
| # Check if we want build Python scripting support | ||||
| PYTHON := $(shell if [ -z "$(ENABLE_PYTHON)" ] || [ "$(ENABLE_PYTHON)" = "0" ] ; then echo disabled ; else echo enabled ; fi) | ||||
| ifneq ($(PYTHON), disabled) | ||||
|     -include $(CHECKS_DIR)/python.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 | ||||
							
								
								
									
										15
									
								
								cfg/checks/python.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								cfg/checks/python.mk
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| # Variables for Python scripting support | ||||
| PYTHON3_LIBS = python3 | ||||
| PYTHON_CFLAGS = -DPYTHON | ||||
| PYTHON_OBJ = api.o python_api.o | ||||
|  | ||||
| # Check if we can build Python scripting support | ||||
| CHECK_PYTHON3_LIBS = $(shell $(PKG_CONFIG) --exists $(PYTHON3_LIBS) || echo -n "error") | ||||
| ifneq ($(CHECK_PYTHON3_LIBS), error) | ||||
|     LDFLAGS += $(shell python3-config --ldflags) | ||||
|     CFLAGS += $(PYTHON_CFLAGS) $(shell python3-config --includes) | ||||
|     OBJ += $(PYTHON_OBJ) | ||||
| else ifneq ($(MAKECMDGOALS), clean) | ||||
|     $(warning WARNING -- Toxic will be compiled without Python scripting support) | ||||
|     $(warning WARNING -- You need python3 installed for Python scripting support) | ||||
| endif | ||||
							
								
								
									
										15
									
								
								cfg/checks/qr.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								cfg/checks/qr.mk
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| # Variables for QR export support | ||||
| QR_LIBS = libqrencode | ||||
| QR_CFLAGS = -DQRCODE | ||||
|  | ||||
| # Check if we can build QR export support | ||||
| CHECK_QR_LIBS = $(shell pkg-config --exists $(QR_LIBS) || echo -n "error") | ||||
| ifneq ($(CHECK_QR_LIBS), error) | ||||
|     LIBS += $(QR_LIBS) | ||||
|     CFLAGS += $(QR_CFLAGS) | ||||
| else ifneq ($(MAKECMDGOALS), clean) | ||||
|     MISSING_QR_LIBS = $(shell for lib in $(QR_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done) | ||||
|     $(warning WARNING -- Toxic will be compiled without QR export support) | ||||
|     $(warning WARNING -- You need these libraries for QR export support) | ||||
|     $(warning WARNING -- $(MISSING_QR_LIBS)) | ||||
| endif | ||||
							
								
								
									
										15
									
								
								cfg/checks/qr_png.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								cfg/checks/qr_png.mk
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| # Variables for QR exported as PNG support | ||||
| PNG_LIBS = libpng | ||||
| PNG_CFLAGS = -DQRPNG | ||||
|  | ||||
| # Check if we can build QR exported as PNG support | ||||
| CHECK_PNG_LIBS = $(shell pkg-config --exists $(PNG_LIBS) || echo -n "error") | ||||
| ifneq ($(CHECK_PNG_LIBS), error) | ||||
|     LIBS += $(PNG_LIBS) | ||||
|     CFLAGS += $(PNG_CFLAGS) | ||||
| else ifneq ($(MAKECMDGOALS), clean) | ||||
|     MISSING_PNG_LIBS = $(shell for lib in $(PNG_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done) | ||||
|     $(warning WARNING -- Toxic will be compiled without QR exported as PNG support) | ||||
|     $(warning WARNING -- You need these libraries for QR exported as PNG support) | ||||
|     $(warning WARNING -- $(MISSING_PNG_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 audio_device.o, $(OBJ))) | ||||
|     SND_NOTIFY_OBJ = | ||||
| else | ||||
|     SND_NOTIFY_OBJ = audio_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 | ||||
							
								
								
									
										21
									
								
								cfg/checks/video.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								cfg/checks/video.mk
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| # Variables for video call support | ||||
| VIDEO_LIBS = openal vpx x11 | ||||
| VIDEO_CFLAGS = -DVIDEO | ||||
| ifneq (, $(findstring video_device.o, $(OBJ))) | ||||
|     VIDEO_OBJ = video_call.o | ||||
| else | ||||
|     VIDEO_OBJ = video_call.o video_device.o | ||||
| endif | ||||
|  | ||||
| # Check if we can build video support | ||||
| CHECK_VIDEO_LIBS = $(shell $(PKG_CONFIG) --exists $(VIDEO_LIBS) || echo -n "error") | ||||
| ifneq ($(CHECK_VIDEO_LIBS), error) | ||||
|     LIBS += $(VIDEO_LIBS) | ||||
|     CFLAGS += $(VIDEO_CFLAGS) | ||||
|     OBJ += $(VIDEO_OBJ) | ||||
| else ifneq ($(MAKECMDGOALS), clean) | ||||
|     MISSING_VIDEO_LIBS = $(shell for lib in $(VIDEO_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done) | ||||
|     $(warning WARNING -- Toxic will be compiled without video support) | ||||
|     $(warning WARNING -- You will need these libraries for video support) | ||||
|     $(warning WARNING -- $(MISSING_VIDEO_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 = x11focus.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.10.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 = nameservers 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 | ||||
							
								
								
									
										18
									
								
								cfg/systems/Darwin.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								cfg/systems/Darwin.mk
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| # 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 -ltoxcore -lcurl -lconfig -lqrencode -lpng -lopenal -g | ||||
| CFLAGS += -I/usr/local/opt/freealut/include/AL -I/usr/local/opt/glib/include/glib-2.0 -g | ||||
|  | ||||
| OSX_LIBRARIES = -lobjc -lresolv | ||||
| OSX_FRAMEWORKS = -framework Foundation -framework CoreFoundation -framework AVFoundation \ | ||||
| 	-framework QuartzCore -framework CoreMedia | ||||
| OSX_VIDEO = osx_video.m | ||||
|  | ||||
| LDFLAGS += $(OSX_LIBRARIES) $(OSX_FRAMEWORKS) | ||||
| OBJ += osx_video.o | ||||
							
								
								
									
										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 | ||||
							
								
								
									
										37
									
								
								cfg/targets/help.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								cfg/targets/help.mk
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| # 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 "  DISABLE_QRCODE:         Set to \"1\" to force building without QR export support" | ||||
| 	@echo "  DISABLE_QRPNG:          Set to \"1\" to force building without QR exported as PNG support" | ||||
| 	@echo "  ENABLE_PYTHON:          Set to \"1\" to enable building with Python scripting support" | ||||
| 	@echo "  ENABLE_RELEASE:         Set to \"1\" to build without debug symbols and with full compiler optimizations" | ||||
| 	@echo "  ENABLE_ASAN:            Set to \"1\" to build with LLVM address sanitizer enabled. | ||||
| 	@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)" | ||||
| 	@echo "  MANDIR:                 Specify a directory where to store man pages (default is \"$(abspath $(PREFIX)/share/man)\")" | ||||
| 	@echo | ||||
| 	@echo "-- Environment Variables --" | ||||
| 	@echo "  CFLAGS:                 Add custom flags to default CFLAGS" | ||||
| 	@echo "  LDFLAGS:                Add custom flags to default LDFLAGS" | ||||
| 	@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)" | ||||
| 	@echo "  MANDIR:                 Specify a directory where to store man pages (default is \"$(abspath $(PREFIX)/share/man)\")" | ||||
|  | ||||
| .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##*.}` ;\ | ||||
| 		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 -n -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##*.}` ;\ | ||||
| 		file=$$section/$$f ;\ | ||||
| 		rm -f $$file $$file.gz ;\ | ||||
| 	done | ||||
|  | ||||
| .PHONY: uninstall | ||||
							
								
								
									
										406
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						
									
										406
									
								
								configure.ac
									
									
									
									
									
								
							| @@ -1,406 +0,0 @@ | ||||
| #                                               -*- Autoconf -*- | ||||
| # Process this file with autoconf to produce a configure script. | ||||
|  | ||||
| AC_PREREQ([2.65]) | ||||
| AC_INIT([toxic], [0.2.6], [https://tox.im/]) | ||||
| AC_CONFIG_AUX_DIR(configure_aux) | ||||
| AC_CONFIG_SRCDIR([src/main.c]) | ||||
| AC_CONFIG_HEADERS([config.h]) | ||||
| AM_INIT_AUTOMAKE([1.10 -Wall]) | ||||
| m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) | ||||
| AC_CONFIG_MACRO_DIR([m4]) | ||||
|  | ||||
| if test "x${prefix}" = "xNONE"; then | ||||
|     prefix="${ac_default_prefix}" | ||||
| fi | ||||
|  | ||||
| DEPSEARCH= | ||||
| LIBTOXCORE_SEARCH_HEADERS= | ||||
| LIBTOXCORE_SEARCH_LIBS= | ||||
| LIBSODIUM_SEARCH_HEADERS= | ||||
| LIBSODIUM_SEARCH_LIBS= | ||||
|  | ||||
| LIBTOXCORE_FOUND="no" | ||||
| NCURSES_FOUND="no" | ||||
| NCURSES_WIDECHAR_SUPPORT="no" | ||||
|  | ||||
| AC_ARG_WITH(dependency-search, | ||||
|     AC_HELP_STRING([--with-dependency-search=DIR], | ||||
|                    [search for dependencies in DIR, i.e. look for libraries in | ||||
|                     DIR/lib and for headers in DIR/include]), | ||||
|     [ | ||||
|         DEPSEARCH="$withval" | ||||
|     ] | ||||
| ) | ||||
|  | ||||
| if test -n "$DEPSEARCH"; then | ||||
|     CFLAGS="$CFLAGS -I$DEPSEARCH/include" | ||||
|     CPPFLAGS="$CPPFLAGS -I$DEPSEARCH/include" | ||||
|     LDFLAGS="$LDFLAGS -L$DEPSEARCH/lib" | ||||
|     export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$DEPSEARCH/lib/pkgconfig | ||||
| fi | ||||
|  | ||||
| AC_ARG_WITH(libtoxcore-headers, | ||||
|         AC_HELP_STRING([--with-libtoxcore-headers=DIR], | ||||
|                        [search for libtoxcore header files in DIR/tox]), | ||||
|         [ | ||||
|             LIBTOXCORE_SEARCH_HEADERS="$withval" | ||||
|             AC_MSG_NOTICE([Will search for libtoxcore header files in $withval]) | ||||
|         ] | ||||
| ) | ||||
|  | ||||
| AC_ARG_WITH(libtoxcore-libs, | ||||
|         AC_HELP_STRING([--with-libtoxcore-libs=DIR], | ||||
|                        [search for libtoxcore libraries in DIR]), | ||||
|         [ | ||||
|             LIBTOXCORE_SEARCH_LIBS="$withval" | ||||
|             AC_MSG_NOTICE([Will search for libtoxcore libraries in $withval]) | ||||
|         ] | ||||
| ) | ||||
|  | ||||
| AC_ARG_WITH(libsodium-headers, | ||||
|         AC_HELP_STRING([--with-libsodium-headers=DIR], | ||||
|                        [search for libsodium header files in DIR]), | ||||
|         [ | ||||
|             LIBSODIUM_SEARCH_HEADERS="$withval" | ||||
|             AC_MSG_NOTICE([Will search for libsodium header files in $withval]) | ||||
|         ] | ||||
| ) | ||||
|  | ||||
| AC_ARG_WITH(libsodium-libs, | ||||
|         AC_HELP_STRING([--with-libsodium-libs=DIR], | ||||
|                        [search for libsodium libraries in DIR]), | ||||
|         [ | ||||
|             LIBSODIUM_SEARCH_LIBS="$withval" | ||||
|             AC_MSG_NOTICE([Will search for libsodium libraries in $withval]) | ||||
|         ] | ||||
| ) | ||||
|  | ||||
| WIN32=no | ||||
| AC_CANONICAL_HOST | ||||
| case $host_os in | ||||
|     *mingw*) | ||||
|         WIN32="yes" | ||||
|     ;; | ||||
|     *freebsd*) | ||||
|         LDFLAGS="$LDFLAGS -L/usr/local/lib" | ||||
|         CFLAGS="$CFLAGS -I/usr/local/include" | ||||
|         CPPFLAGS="$CPPFLAGS -I/usr/local/include" | ||||
|     ;; | ||||
| esac | ||||
|  | ||||
| # Checks for programs. | ||||
| AC_PROG_CC | ||||
| AM_PROG_CC_C_O | ||||
|  | ||||
| AC_CHECK_HEADERS( | ||||
|     [limits.h locale.h stdint.h stdlib.h string.h unistd.h wchar.h wctype.h], | ||||
|     [], | ||||
|     [ AC_MSG_ERROR([required header is missing on your system]) ]) | ||||
|  | ||||
| # Checks for typedefs, structures, and compiler characteristics. | ||||
| AC_HEADER_STDBOOL | ||||
| AC_TYPE_SIZE_T | ||||
| AC_TYPE_UINT16_T | ||||
| AC_TYPE_UINT32_T | ||||
| AC_TYPE_UINT64_T | ||||
| AC_TYPE_UINT8_T | ||||
|  | ||||
| # Checks for library functions. | ||||
| AC_FUNC_MALLOC | ||||
| AC_CHECK_FUNCS( | ||||
|     [iswprint memmove memset mkdir setlocale strchr strdup], | ||||
|     [], | ||||
|     [ AC_MSG_ERROR([required library function is missing on your system])]) | ||||
|  | ||||
| # pkg-config based tests | ||||
| PKG_PROG_PKG_CONFIG | ||||
|  | ||||
| if test -n "$PKG_CONFIG"; then | ||||
|     if test "x$WIN32" != "xyes"; then | ||||
|         PKG_CHECK_MODULES([NCURSES], [ncursesw], | ||||
|             [ | ||||
|                 NCURSES_FOUND="yes" | ||||
|                 NCURSES_WIDECHAR_SUPPORT="yes" | ||||
|             ], | ||||
|             [ | ||||
|                 NCURSES_WIDECHAR_SUPPORT="no" | ||||
|                 PKG_CHECK_MODULES([NCURSES], [ncurses], | ||||
|                     [ | ||||
|                         NCURSES_FOUND="yes" | ||||
|                     ], | ||||
|                     [ | ||||
|                         AC_MSG_WARN([$NCURSES_PKG_ERRORS]) | ||||
|                     ]) | ||||
|             ]) | ||||
|     fi | ||||
| else | ||||
|     AC_MSG_WARN([pkg-config was not found on your sytem]) | ||||
| fi | ||||
|  | ||||
| if (test "x$NCURSES_FOUND" = "xno") && (test "x$WIN32" != "xyes"); then | ||||
|     AC_PATH_PROG([CURSES_CONFIG], [ncursesw5-config], [no]) | ||||
|     if test "x$CURSES_CONFIG" != "xno"; then | ||||
|         AC_MSG_CHECKING(ncurses cflags) | ||||
|         NCURSES_CFLAGS=`${CURSES_CONFIG} --cflags` | ||||
|         AC_MSG_RESULT($NCURSES_CFLAGS) | ||||
|  | ||||
|         AC_MSG_CHECKING(ncurses libraries) | ||||
|         NCURSES_LIBS=`${CURSES_CONFIG} --libs` | ||||
|         AC_MSG_RESULT($NCURSES_LIBS) | ||||
|  | ||||
|         AC_SUBST(NCURSES_CFLAGS) | ||||
|         AC_SUBST(NCURSES_LIBS) | ||||
|         NCURSES_FOUND="yes" | ||||
|         NCURSES_WIDECHAR_SUPPORT="yes" | ||||
|     fi | ||||
| fi | ||||
|  | ||||
| if (test "x$NCURSES_FOUND" = "xno") && (test "x$WIN32" != "xyes"); then | ||||
|     unset ac_cv_path_CURSES_CONFIG | ||||
|     AC_PATH_PROG([CURSES_CONFIG], [ncursesw5.4-config], [no]) | ||||
|     if test "x$CURSES_CONFIG" != "xno"; then | ||||
|         AC_MSG_CHECKING(ncurses cflags) | ||||
|         NCURSES_CFLAGS=`${CURSES_CONFIG} --cflags` | ||||
|         AC_MSG_RESULT($NCURSES_CFLAGS) | ||||
|  | ||||
|         AC_MSG_CHECKING(ncurses libraries) | ||||
|         NCURSES_LIBS=`${CURSES_CONFIG} --libs` | ||||
|         AC_MSG_RESULT($NCURSES_LIBS) | ||||
|  | ||||
|         AC_SUBST(NCURSES_CFLAGS) | ||||
|         AC_SUBST(NCURSES_LIBS) | ||||
|         NCURSES_FOUND="yes" | ||||
|         NCURSES_WIDECHAR_SUPPORT="yes" | ||||
|     fi | ||||
| fi | ||||
|  | ||||
| if (test "x$NCURSES_FOUND" = "xno") && (test "x$WIN32" != "xyes"); then | ||||
|     unset ac_cv_path_CURSES_CONFIG | ||||
|     AC_PATH_PROG([CURSES_CONFIG], [ncurses5-config], [no]) | ||||
|     if test "x$CURSES_CONFIG" != "xno"; then | ||||
|         AC_MSG_CHECKING(ncurses cflags) | ||||
|         NCURSES_CFLAGS=`${CURSES_CONFIG} --cflags` | ||||
|         AC_MSG_RESULT($NCURSES_CFLAGS) | ||||
|  | ||||
|         AC_MSG_CHECKING(ncurses libraries) | ||||
|         NCURSES_LIBS=`${CURSES_CONFIG} --libs` | ||||
|         AC_MSG_RESULT($NCURSES_LIBS) | ||||
|  | ||||
|         AC_SUBST(NCURSES_CFLAGS) | ||||
|         AC_SUBST(NCURSES_LIBS) | ||||
|         NCURSES_FOUND="yes" | ||||
|     fi | ||||
| fi | ||||
|  | ||||
| if (test "x$NCURSES_FOUND" = "xno") && (test "x$WIN32" != "xyes"); then | ||||
|     unset ac_cv_path_CURSES_CONFIG | ||||
|     AC_PATH_PROG([CURSES_CONFIG], [ncurses5.4-config], [no]) | ||||
|     if test "x$CURSES_CONFIG" != "xno"; then | ||||
|         AC_MSG_CHECKING(ncurses cflags) | ||||
|         NCURSES_CFLAGS=`${CURSES_CONFIG} --cflags` | ||||
|         AC_MSG_RESULT($NCURSES_CFLAGS) | ||||
|  | ||||
|         AC_MSG_CHECKING(ncurses libraries) | ||||
|         NCURSES_LIBS=`${CURSES_CONFIG} --libs` | ||||
|         AC_MSG_RESULT($NCURSES_LIBS) | ||||
|  | ||||
|         AC_SUBST(NCURSES_CFLAGS) | ||||
|         AC_SUBST(NCURSES_LIBS) | ||||
|         NCURSES_FOUND="yes" | ||||
|     fi | ||||
| fi | ||||
|  | ||||
| if test "x$NCURSES_FOUND" = "xno"; then | ||||
|     AC_CHECK_HEADER([curses.h], | ||||
|         [], | ||||
|         [ | ||||
|             AC_MSG_ERROR([headers for the ncurses library were not found on your system]) | ||||
|         ] | ||||
|     ) | ||||
|  | ||||
|     if test "x$WIN32" = "xyes"; then | ||||
|         dnl Check if pdcurses provides wide char support | ||||
|         NCURSES_WIDECHAR_SUPPORT="no" | ||||
|         AC_CHECK_LIB([pdcurses], [clear], | ||||
|             [], | ||||
|             [ | ||||
|                 AC_MSG_ERROR([required library pdcurses was not found on your system]) | ||||
|             ] | ||||
|         ) | ||||
|  | ||||
|         AC_CHECK_LIB(ws2_32, main, | ||||
|             [ | ||||
|                 WINSOCK2_LIBS="-lws2_32" | ||||
|                 AC_SUBST(WINSOCK2_LIBS) | ||||
|             ], | ||||
|             [ | ||||
|                 AC_MSG_ERROR([required library winsock2 was not found on the system, please check your MinGW installation]) | ||||
|             ] | ||||
|         ) | ||||
|         AC_DEFINE([_WIN32_WINNT], [0x501], | ||||
|                   [enable getaddrinfo/freeaddrinfo on XP and higher]) | ||||
|     else | ||||
|         AC_CHECK_LIB([ncursesw], [wget_wch], | ||||
|             [ | ||||
|                 NCURSES_WIDECHAR_SUPPORT="yes" | ||||
|             ], | ||||
|             [ | ||||
|                 unset ac_cv_lib_ncursesw_wget_wch | ||||
|                 AC_CHECK_LIB([ncursesw], [wget_wch], | ||||
|                     [ | ||||
|                         NCURSES_WIDECHAR_SUPPORT="yes" | ||||
|                     ], | ||||
|                     [ | ||||
|                         NCURSES_WIDECHAR_SUPPORT="no" | ||||
|                         AC_CHECK_LIB([ncurses], [clear], | ||||
|                             [], | ||||
|                             [ | ||||
|                                 unset ac_cv_lib_ncurses_clear | ||||
|                                 AC_CHECK_LIB([ncurses], [clear], | ||||
|                                     [], | ||||
|                                     [ | ||||
|                                         AC_MSG_ERROR([required library ncurses was not found on your system]) | ||||
|                                     ], | ||||
|                                     [ | ||||
|                                         -ltinfo | ||||
|                                     ] | ||||
|                                 ) | ||||
|                             ] | ||||
|                         ) | ||||
|                     ], | ||||
|                     [ | ||||
|                         -ltinfo | ||||
|                     ] | ||||
|                 ) | ||||
|             ] | ||||
|         ) | ||||
|     fi | ||||
| fi | ||||
|  | ||||
| if test -n "$PKG_CONFIG"; then | ||||
|     PKG_CHECK_MODULES(LIBTOXCORE, [libtoxcore], | ||||
|         [ | ||||
|             LIBTOXCORE_FOUND="yes" | ||||
|         ], | ||||
|         [ | ||||
|             AC_MSG_WARN([required library libsodium was not found in requested location $LIBSODIUM_SEARCH_LIBS]) | ||||
|         ]) | ||||
| fi | ||||
|  | ||||
| if test "x$LIBTOXCORE_FOUND" = "xno"; then | ||||
|     LIBSODIUM_LIBS= | ||||
|     LIBSODIUM_LDFLAGS= | ||||
|     LDFLAGS_SAVE="$LDFLAGS" | ||||
|     if test -n "$LIBSODIUM_SEARCH_LIBS"; then | ||||
|         LDFLAGS="$LDFLAGS -L$LIBSODIUM_SEARCH_LIBS" | ||||
|         AC_CHECK_LIB(sodium, randombytes_random, | ||||
|             [ | ||||
|                 LIBSODIUM_LDFLAGS="-L$LIBSODIUM_SEARCH_LIBS" | ||||
|                 LIBSODIUM_LIBS="-lsodium" | ||||
|             ], | ||||
|             [ | ||||
|                 AC_MSG_ERROR([required library libsodium was not found in requested location $LIBSODIUM_SEARCH_LIBS]) | ||||
|             ] | ||||
|         ) | ||||
|     else | ||||
|         AC_CHECK_LIB(sodium, randombytes_random, | ||||
|             [], | ||||
|             [ | ||||
|                 AC_MSG_ERROR([required library libsodium was not found on your system, please check http://download.libsodium.org/libsodium/releases/]) | ||||
|             ] | ||||
|         ) | ||||
|     fi | ||||
|  | ||||
|     LDFLAGS="$LDFLAGS_SAVE" | ||||
|     AC_SUBST(LIBSODIUM_LIBS) | ||||
|     AC_SUBST(LIBSODIUM_LDFLAGS) | ||||
|  | ||||
|  | ||||
|  | ||||
|     LIBTOXCORE_CFLAGS= | ||||
|     CFLAGS_SAVE="$CFLAGS" | ||||
|     CPPFLAGS_SAVE="$CPPFLAGS" | ||||
|  | ||||
|     if test -n "$LIBTOXCORE_SEARCH_HEADERS"; then | ||||
|         CFLAGS="$CFLAGS -I$LIBTOXCORE_SEARCH_HEADERS" | ||||
|         CPPFLAGS="$CPPFLAGS -I$LIBTOXCORE_SEARCH_HEADERS" | ||||
|         AC_CHECK_HEADER([tox/tox.h], | ||||
|             [ | ||||
|                 LIBTOXCORE_CFLAGS="-I$LIBTOXCORE_SEARCH_HEADERS" | ||||
|             ], | ||||
|             [ | ||||
|                 AC_MSG_ERROR([headers for the toxcore library were not found on your system]) | ||||
|             ] | ||||
|         ) | ||||
|     else | ||||
|         AC_CHECK_HEADER([tox/tox.h], | ||||
|             [], | ||||
|             [ | ||||
|                 AC_MSG_ERROR([headers for the toxcore library were not found on your system]) | ||||
|             ], | ||||
|         ) | ||||
|     fi | ||||
|     CFLAGS="$CFLAGS_SAVE" | ||||
|     CPPFLAGS="$CPPFLAGS_SAVE" | ||||
|     AC_SUBST(LIBTOXCORE_CFLAGS) | ||||
|  | ||||
|     LIBTOXCORE_LIBS= | ||||
|     LIBTOXCORE_LDFLAGS= | ||||
|     LDFLAGS_SAVE="$LDFLAGS" | ||||
|     if test -n "$LIBTOXCORE_SEARCH_LIBS"; then | ||||
|         LDFLAGS="$LDFLAGS $LIBSODIUM_LDFLAGS -L$LIBTOXCORE_SEARCH_LIBS" | ||||
|         AC_CHECK_LIB([toxcore], [tox_new], | ||||
|             [ | ||||
|                 LIBTOXCORE_LDFLAGS="-L$LIBTOXCORE_SEARCH_LIBS" | ||||
|                 LIBTOXCORE_LIBS="-ltoxcore" | ||||
|             ], | ||||
|             [ | ||||
|                 AC_MSG_ERROR([required library toxcore was not found on your system]) | ||||
|             ], | ||||
|             [ | ||||
|                 $WINSOCK2_LIBS | ||||
|                 $LIBSODIUM_LIBS | ||||
|             ] | ||||
|         ) | ||||
|     else | ||||
|         LDFLAGS="$LDFLAGS $LIBSODIUM_LDFLAGS" | ||||
|         AC_CHECK_LIB([toxcore], [tox_new], | ||||
|             [], | ||||
|             [ | ||||
|                 AC_MSG_ERROR([required library toxcore was not found on your system]) | ||||
|             ], | ||||
|             [ | ||||
|                 $WINSOCK2_LIBS | ||||
|                 $LIBSODIUM_LIBS | ||||
|             ] | ||||
|         ) | ||||
|     fi | ||||
|     LDFLAGS="$LDFLAGS_SAVE" | ||||
|     AC_SUBST(LIBTOXCORE_LIBS) | ||||
|     AC_SUBST(LIBTOXCORE_LDFLAGS) | ||||
| fi | ||||
|  | ||||
| TOXIC_VERSION="$PACKAGE_VERSION" | ||||
| AC_PATH_PROG([GIT], [git], [no]) | ||||
| if test "x$GIT" != "xno"; then | ||||
|     if test -d ${srcdir}/.git; then | ||||
|         TOXIC_VERSION="${TOXIC_VERSION}_r`${GIT} rev-list HEAD --count`" | ||||
|     fi | ||||
| fi | ||||
| AC_SUBST(TOXIC_VERSION) | ||||
|  | ||||
| eval PACKAGE_DATADIR="${datadir}/${PACKAGE}" | ||||
| eval PACKAGE_DATADIR="${PACKAGE_DATADIR}" | ||||
| AC_DEFINE_UNQUOTED(PACKAGE_DATADIR, "$PACKAGE_DATADIR", [toxic data directory]) | ||||
|  | ||||
| if test "x$NCURSES_WIDECHAR_SUPPORT" = "xyes"; then | ||||
|     AC_DEFINE([HAVE_WIDECHAR], [1], [ncurses wide char support available]) | ||||
|     AC_DEFINE([_XOPEN_SOURCE_EXTENDED], [1], | ||||
|               [enable X/Open Portability Guide functionality]) | ||||
| fi | ||||
|   | ||||
| AC_CONFIG_FILES([Makefile | ||||
|                  misc/Makefile | ||||
|                  build/Makefile]) | ||||
| AC_OUTPUT | ||||
							
								
								
									
										169
									
								
								doc/toxic.1
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								doc/toxic.1
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,169 @@ | ||||
| '\" t | ||||
| .\"     Title: toxic | ||||
| .\"    Author: [see the "AUTHORS" section] | ||||
| .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/> | ||||
| .\"      Date: 2020-05-04 | ||||
| .\"    Manual: Toxic Manual | ||||
| .\"    Source: toxic __VERSION__ | ||||
| .\"  Language: English | ||||
| .\" | ||||
| .TH "TOXIC" "1" "2020\-05\-04" "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/toxic_profile\&.tox\fR | ||||
| .RE | ||||
| .PP | ||||
| \-h, \-\-help | ||||
| .RS 4 | ||||
| Show help message | ||||
| .RE | ||||
| .PP | ||||
| \-l, \-\-logging | ||||
| .RS 4 | ||||
| Enable toxcore logging to stderr | ||||
| .RE | ||||
| .PP | ||||
| \-n, \-\-nodes nodes\-file | ||||
| .RS 4 | ||||
| Use specified | ||||
| \fInodes\-file\fR | ||||
| for DHT bootstrap nodes instead of | ||||
| \fI~/\&.config/tox/DHTnodes\&.json\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, \-\-namelist | ||||
| .RS 4 | ||||
| Use specified nameservers list | ||||
| .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 | ||||
| ~/\&.config/tox/DHTnodes\&.json | ||||
| .RS 4 | ||||
| Default location for list of DHT bootstrap nodes (list obtained from | ||||
| https://nodes\&.tox\&.chat)\&. This list is automatically updated\&. See | ||||
| \fBtoxic\&.conf\fR(5) for details on controlling the update frequency\&. | ||||
| .RE | ||||
| .PP | ||||
| ~/\&.config/tox/toxic_profile\&.tox | ||||
| .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\&. | ||||
| .sp | ||||
| \-Text occasionally fails to auto\-scroll to the bottom\&. | ||||
| .sp | ||||
| \-Screen flickering sometimes occurs on certain terminals\&. | ||||
| .SH "AUTHORS" | ||||
| .sp | ||||
| JFreegman <JFreegman@gmail\&.com> | ||||
| .SH "SEE ALSO" | ||||
| .sp | ||||
| \fBtoxic\&.conf\fR(5) | ||||
| .SH "LINKS" | ||||
| .sp | ||||
| Project page: https://github\&.com/JFreegman/toxic | ||||
| .sp | ||||
| IRC channel: chat\&.freenode\&.net#tox | ||||
							
								
								
									
										108
									
								
								doc/toxic.1.asc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								doc/toxic.1.asc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,108 @@ | ||||
| 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/toxic_profile.tox' | ||||
|  | ||||
| -h, --help:: | ||||
|     Show help message | ||||
|  | ||||
| -l, --logging:: | ||||
|     Enable toxcore logging to stderr | ||||
|  | ||||
| -n, --nodes nodes-file:: | ||||
|     Use specified 'nodes-file' for DHT bootstrap nodes instead of '~/.config/tox/DHTnodes.json' | ||||
|  | ||||
| -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, --namelist:: | ||||
|     Use specified nameservers list | ||||
|  | ||||
| -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 | ||||
| ----- | ||||
| ~/.config/tox/DHTnodes.json:: | ||||
|     Default location for list of DHT bootstrap nodes (list obtained from https://nodes.tox.chat). | ||||
|     This list is automatically updated. See *toxic.conf*(5) for details on controlling the update frequency. | ||||
|  | ||||
| ~/.config/tox/toxic_profile.tox:: | ||||
|     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. | ||||
|  | ||||
| -Text occasionally fails to auto-scroll to the bottom. | ||||
|  | ||||
| -Screen flickering sometimes occurs on certain terminals. | ||||
|  | ||||
| AUTHORS | ||||
| ------- | ||||
| JFreegman <JFreegman@gmail.com> | ||||
|  | ||||
| SEE ALSO | ||||
| -------- | ||||
| *toxic.conf*(5) | ||||
|  | ||||
| LINKS | ||||
| ----- | ||||
| Project page: <https://github.com/JFreegman/toxic> | ||||
|  | ||||
| IRC channel: chat.freenode.net#tox | ||||
							
								
								
									
										420
									
								
								doc/toxic.conf.5
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										420
									
								
								doc/toxic.conf.5
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,420 @@ | ||||
| '\" t | ||||
| .\"     Title: toxic.conf | ||||
| .\"    Author: [see the "AUTHORS" section] | ||||
| .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/> | ||||
| .\"      Date: 2020-11-18 | ||||
| .\"    Manual: Toxic Manual | ||||
| .\"    Source: toxic __VERSION__ | ||||
| .\"  Language: English | ||||
| .\" | ||||
| .TH "TOXIC\&.CONF" "5" "2020\-11\-18" "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 acoustic 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 | ||||
| \fBcolor_bar_bg\fR | ||||
| .RS 4 | ||||
| set background color of chat status bars\&. (black, white, red, green, blue, cyan, yellow, magenta) | ||||
| .RE | ||||
| .PP | ||||
| \fBcolor_bar_fg\fR | ||||
| .RS 4 | ||||
| set foreground (text) color of chat status bars\&. (black, white, red, green, blue, cyan, yellow, magenta) | ||||
| .RE | ||||
| .PP | ||||
| \fBcolor_bar_accent\fR | ||||
| .RS 4 | ||||
| set foreground accent color of chat status bars\&. (black, white, red, green, blue, cyan, yellow, magenta) | ||||
| .RE | ||||
| .PP | ||||
| \fBcolor_bar_notify\fR | ||||
| .RS 4 | ||||
| set foreground notify (and typing) color in chat status bar\&. (black, white, red, green, blue, cyan, yellow, magenta) | ||||
| .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 | ||||
| \fBshow_connection_msg\fR | ||||
| .RS 4 | ||||
| Enable friend connection change notifications\&. true or false | ||||
| .RE | ||||
| .PP | ||||
| \fBnodelist_update_freq\fR | ||||
| .RS 4 | ||||
| How often in days to update the DHT nodes list\&. (integer; 0 to disable) | ||||
| .RE | ||||
| .PP | ||||
| \fBautosave_freq\fR | ||||
| .RS 4 | ||||
| How often in seconds to auto\-save the Tox data file\&. (integer; 0 to disable) | ||||
| .RE | ||||
| .PP | ||||
| \fBhistory_size\fR | ||||
| .RS 4 | ||||
| Maximum lines for chat window history\&. Integer value\&. (for example: 700) | ||||
| .RE | ||||
| .PP | ||||
| \fBnotification_timeout\fR | ||||
| .RS 4 | ||||
| Time in milliseconds to display a notification\&. Integer value\&. (for example: 3000) | ||||
| .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\&. | ||||
| .sp | ||||
| .if n \{\ | ||||
| .RS 4 | ||||
| .\} | ||||
| .nf | ||||
| The following options control whether to output a terminal bell on certain events\&. | ||||
| Some terminals mark the window as urgent when a bell is received\&. Urgent windows are usually highlighted in the taskbar and some window managers even provide shortcuts to jump to the next urgent window\&. | ||||
| These options don\*(Aqt affect the "alerts" option\&. | ||||
| .fi | ||||
| .if n \{\ | ||||
| .RE | ||||
| .\} | ||||
| .RE | ||||
| .PP | ||||
| \fBbell_on_message\fR | ||||
| .RS 4 | ||||
| Enable/Disable the terminal bell when receiving a message\&. true or false | ||||
| .RE | ||||
| .PP | ||||
| \fBbell_on_filetrans\fR | ||||
| .RS 4 | ||||
| Enable/Disable the terminal bell when receiving a filetransfer\&. true or false | ||||
| .RE | ||||
| .PP | ||||
| \fBbell_on_filetrans_accept\fR | ||||
| .RS 4 | ||||
| Enable/Disable the terminal bell when a filetransfer was accepted\&. true or false | ||||
| .RE | ||||
| .PP | ||||
| \fBbell_on_invite\fR | ||||
| .RS 4 | ||||
| Enable/Disable the terminal bell when receiving a group/call invite\&. true or false | ||||
| .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_threshold\fR | ||||
| .RS 4 | ||||
| Voice Activity Detection threshold\&. Float value\&. Recommended values are 1\&.0\-40\&.0 | ||||
| .RE | ||||
| .PP | ||||
| \fBconference_audio_channels\fR | ||||
| .RS 4 | ||||
| Number of channels for conference audio broadcast\&. Integer value\&. 1 (mono) or 2 (stereo) | ||||
| .RE | ||||
| .PP | ||||
| \fBchat_audio_channels\fR | ||||
| .RS 4 | ||||
| Number of channels for 1\-on\-1 audio broadcast\&. Integer value\&. 1 (mono) or 2 (stereo) | ||||
| .RE | ||||
| .PP | ||||
| \fBpush_to_talk\fR | ||||
| .RS 4 | ||||
| Enable/Disable Push\-To\-Talk for conference audio chats (active key is F2)\&. true or false | ||||
| .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 | ||||
| \fBautorun_path\fR | ||||
| .RS 4 | ||||
| Path for any scripts that should be run on startup | ||||
| .RE | ||||
| .PP | ||||
| \fBchatlogs_path\fR | ||||
| .RS 4 | ||||
| Default path for chatlogs\&. String value\&. Absolute path for chatlog files\&. | ||||
| .RE | ||||
| .PP | ||||
| \fBpassword_eval\fR | ||||
| .RS 4 | ||||
| Replace password prompt by running this command and using its output as the password\&. | ||||
| .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 | ||||
| \fBtoggle_peerlist\fR | ||||
| .RS 4 | ||||
| Toggle the peer list on and off\&. | ||||
| .RE | ||||
| .PP | ||||
| \fBtoggle_paste_mode\fR | ||||
| .RS 4 | ||||
| Toggle treating linebreaks as enter key press\&. | ||||
| .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/JFreegman/toxic | ||||
| .sp | ||||
| IRC channel: chat\&.freenode\&.net#tox | ||||
| .SH "AUTHORS" | ||||
| .sp | ||||
| JFreegman <JFreegman@gmail\&.com> | ||||
							
								
								
									
										275
									
								
								doc/toxic.conf.5.asc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										275
									
								
								doc/toxic.conf.5.asc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,275 @@ | ||||
| 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 acoustic alerts on events. true or false | ||||
|  | ||||
|     *native_colors*;; | ||||
|         Select between native terminal colors and toxic color theme. true or false | ||||
|  | ||||
|     *color_bar_bg*;; | ||||
|         set background color of chat status bars. (black, white, red, green, blue, cyan, yellow, magenta) | ||||
|  | ||||
|     *color_bar_fg*;; | ||||
|         set foreground (text) color of chat status bars. (black, white, red, green, blue, cyan, yellow, magenta) | ||||
|  | ||||
|     *color_bar_accent*;; | ||||
|         set foreground accent color of chat status bars. (black, white, red, green, blue, cyan, yellow, magenta) | ||||
|  | ||||
|     *color_bar_notify*;; | ||||
|         set foreground notify (and typing) color in chat status bar. (black, white, red, green, blue, cyan, yellow, magenta) | ||||
|  | ||||
|     *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 | ||||
|  | ||||
|     *show_connection_msg*;; | ||||
|         Enable friend connection change notifications. true or false | ||||
|  | ||||
|     *nodelist_update_freq*;; | ||||
|         How often in days to update the DHT nodes list. (integer; 0 to disable) | ||||
|  | ||||
|     *autosave_freq*;; | ||||
|         How often in seconds to auto-save the Tox data file. (integer; 0 to disable) | ||||
|  | ||||
|     *history_size*;; | ||||
|         Maximum lines for chat window history. Integer value. (for example: 700) | ||||
|  | ||||
|     *notification_timeout*;; | ||||
|         Time in milliseconds to display a notification. Integer value. (for example: 3000) | ||||
|  | ||||
|     *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. | ||||
|  | ||||
|     The following options control whether to output a terminal bell on certain events. | ||||
|     Some terminals mark the window as urgent when a bell is received. Urgent windows are usually highlighted in the taskbar and some window managers even provide shortcuts to jump to the next urgent window. | ||||
|     These options don't affect the "alerts" option. | ||||
|  | ||||
|     *bell_on_message*;; | ||||
|         Enable/Disable the terminal bell when receiving a message. true or false | ||||
|  | ||||
|     *bell_on_filetrans*;; | ||||
|         Enable/Disable the terminal bell when receiving a filetransfer. true or false | ||||
|  | ||||
|     *bell_on_filetrans_accept*;; | ||||
|         Enable/Disable the terminal bell when a filetransfer was accepted. true or false | ||||
|  | ||||
|     *bell_on_invite*;; | ||||
|         Enable/Disable the terminal bell when receiving a group/call invite. true or false | ||||
|  | ||||
|  | ||||
| *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_threshold*;; | ||||
|         Voice Activity Detection threshold.  Float value. Recommended values are | ||||
|         1.0-40.0 | ||||
|  | ||||
|     *conference_audio_channels*;; | ||||
|         Number of channels for conference audio broadcast. Integer value. 1 (mono) or 2 (stereo) | ||||
|  | ||||
|     *chat_audio_channels*;; | ||||
|         Number of channels for 1-on-1 audio broadcast. Integer value. 1 (mono) or 2 (stereo) | ||||
|  | ||||
|     *push_to_talk*;; | ||||
|         Enable/Disable Push-To-Talk for conference audio chats (active key is F2). true or false | ||||
|  | ||||
| *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) | ||||
|  | ||||
|     *autorun_path*;; | ||||
|         Path for any scripts that should be run on startup | ||||
|  | ||||
|     *chatlogs_path*;; | ||||
|         Default path for chatlogs. String value. Absolute path for chatlog files. | ||||
|  | ||||
|     *password_eval*;; | ||||
|         Replace password prompt by running this command and using its output as | ||||
| 	the password. | ||||
|  | ||||
| *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. | ||||
|  | ||||
|     *toggle_peerlist*;; | ||||
|         Toggle the peer list on and off. | ||||
|  | ||||
|     *toggle_paste_mode*;; | ||||
|         Toggle treating linebreaks as enter key press. | ||||
|  | ||||
|  | ||||
| FILES | ||||
| ----- | ||||
| ~/.config/tox/toxic.conf:: | ||||
|     Main configuration file. | ||||
|  | ||||
| {datadir}/toxic.conf.example:: | ||||
|     Configuration example. | ||||
|  | ||||
|  | ||||
| SEE ALSO | ||||
| -------- | ||||
| *toxic*(1) | ||||
|  | ||||
|  | ||||
| RESOURCES | ||||
| --------- | ||||
| Project page: <https://github.com/JFreegman/toxic> | ||||
|  | ||||
| IRC channel:  chat.freenode.net#tox | ||||
|  | ||||
|  | ||||
| AUTHORS | ||||
| ------- | ||||
| JFreegman <JFreegman@gmail.com> | ||||
							
								
								
									
										199
									
								
								m4/pkg.m4
									
									
									
									
									
								
							
							
						
						
									
										199
									
								
								m4/pkg.m4
									
									
									
									
									
								
							| @@ -1,199 +0,0 @@ | ||||
| # pkg.m4 - Macros to locate and utilise pkg-config.            -*- Autoconf -*- | ||||
| # serial 1 (pkg-config-0.24) | ||||
| #  | ||||
| # Copyright © 2004 Scott James Remnant <scott@netsplit.com>. | ||||
| # | ||||
| # This program is free software; you can redistribute it and/or modify | ||||
| # it under the terms of the GNU General Public License as published by | ||||
| # the Free Software Foundation; either version 2 of the License, or | ||||
| # (at your option) any later version. | ||||
| # | ||||
| # This program is distributed in the hope that it will be useful, but | ||||
| # WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
| # General Public License for more details. | ||||
| # | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program; if not, write to the Free Software | ||||
| # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||||
| # | ||||
| # As a special exception to the GNU General Public License, if you | ||||
| # distribute this file as part of a program that contains a | ||||
| # configuration script generated by Autoconf, you may include it under | ||||
| # the same distribution terms that you use for the rest of that program. | ||||
|  | ||||
| # PKG_PROG_PKG_CONFIG([MIN-VERSION]) | ||||
| # ---------------------------------- | ||||
| AC_DEFUN([PKG_PROG_PKG_CONFIG], | ||||
| [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) | ||||
| m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) | ||||
| m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) | ||||
| AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) | ||||
| AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) | ||||
| AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) | ||||
|  | ||||
| if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then | ||||
| 	AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) | ||||
| fi | ||||
| if test -n "$PKG_CONFIG"; then | ||||
| 	_pkg_min_version=m4_default([$1], [0.9.0]) | ||||
| 	AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) | ||||
| 	if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then | ||||
| 		AC_MSG_RESULT([yes]) | ||||
| 	else | ||||
| 		AC_MSG_RESULT([no]) | ||||
| 		PKG_CONFIG="" | ||||
| 	fi | ||||
| fi[]dnl | ||||
| ])# PKG_PROG_PKG_CONFIG | ||||
|  | ||||
| # PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) | ||||
| # | ||||
| # Check to see whether a particular set of modules exists.  Similar | ||||
| # to PKG_CHECK_MODULES(), but does not set variables or print errors. | ||||
| # | ||||
| # Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) | ||||
| # only at the first occurence in configure.ac, so if the first place | ||||
| # it's called might be skipped (such as if it is within an "if", you | ||||
| # have to call PKG_CHECK_EXISTS manually | ||||
| # -------------------------------------------------------------- | ||||
| AC_DEFUN([PKG_CHECK_EXISTS], | ||||
| [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl | ||||
| if test -n "$PKG_CONFIG" && \ | ||||
|     AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then | ||||
|   m4_default([$2], [:]) | ||||
| m4_ifvaln([$3], [else | ||||
|   $3])dnl | ||||
| fi]) | ||||
|  | ||||
| # _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) | ||||
| # --------------------------------------------- | ||||
| m4_define([_PKG_CONFIG], | ||||
| [if test -n "$$1"; then | ||||
|     pkg_cv_[]$1="$$1" | ||||
|  elif test -n "$PKG_CONFIG"; then | ||||
|     PKG_CHECK_EXISTS([$3], | ||||
|                      [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null` | ||||
| 		      test "x$?" != "x0" && pkg_failed=yes ], | ||||
| 		     [pkg_failed=yes]) | ||||
|  else | ||||
|     pkg_failed=untried | ||||
| fi[]dnl | ||||
| ])# _PKG_CONFIG | ||||
|  | ||||
| # _PKG_SHORT_ERRORS_SUPPORTED | ||||
| # ----------------------------- | ||||
| AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], | ||||
| [AC_REQUIRE([PKG_PROG_PKG_CONFIG]) | ||||
| if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then | ||||
|         _pkg_short_errors_supported=yes | ||||
| else | ||||
|         _pkg_short_errors_supported=no | ||||
| fi[]dnl | ||||
| ])# _PKG_SHORT_ERRORS_SUPPORTED | ||||
|  | ||||
|  | ||||
| # PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], | ||||
| # [ACTION-IF-NOT-FOUND]) | ||||
| # | ||||
| # | ||||
| # Note that if there is a possibility the first call to | ||||
| # PKG_CHECK_MODULES might not happen, you should be sure to include an | ||||
| # explicit call to PKG_PROG_PKG_CONFIG in your configure.ac | ||||
| # | ||||
| # | ||||
| # -------------------------------------------------------------- | ||||
| AC_DEFUN([PKG_CHECK_MODULES], | ||||
| [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl | ||||
| AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl | ||||
| AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl | ||||
|  | ||||
| pkg_failed=no | ||||
| AC_MSG_CHECKING([for $1]) | ||||
|  | ||||
| _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) | ||||
| _PKG_CONFIG([$1][_LIBS], [libs], [$2]) | ||||
|  | ||||
| m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS | ||||
| and $1[]_LIBS to avoid the need to call pkg-config. | ||||
| See the pkg-config man page for more details.]) | ||||
|  | ||||
| if test $pkg_failed = yes; then | ||||
|    	AC_MSG_RESULT([no]) | ||||
|         _PKG_SHORT_ERRORS_SUPPORTED | ||||
|         if test $_pkg_short_errors_supported = yes; then | ||||
| 	        $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` | ||||
|         else  | ||||
| 	        $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` | ||||
|         fi | ||||
| 	# Put the nasty error message in config.log where it belongs | ||||
| 	echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD | ||||
|  | ||||
| 	m4_default([$4], [AC_MSG_ERROR( | ||||
| [Package requirements ($2) were not met: | ||||
|  | ||||
| $$1_PKG_ERRORS | ||||
|  | ||||
| Consider adjusting the PKG_CONFIG_PATH environment variable if you | ||||
| installed software in a non-standard prefix. | ||||
|  | ||||
| _PKG_TEXT])[]dnl | ||||
|         ]) | ||||
| elif test $pkg_failed = untried; then | ||||
|      	AC_MSG_RESULT([no]) | ||||
| 	m4_default([$4], [AC_MSG_FAILURE( | ||||
| [The pkg-config script could not be found or is too old.  Make sure it | ||||
| is in your PATH or set the PKG_CONFIG environment variable to the full | ||||
| path to pkg-config. | ||||
|  | ||||
| _PKG_TEXT | ||||
|  | ||||
| To get pkg-config, see <http://pkg-config.freedesktop.org/>.])[]dnl | ||||
|         ]) | ||||
| else | ||||
| 	$1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS | ||||
| 	$1[]_LIBS=$pkg_cv_[]$1[]_LIBS | ||||
|         AC_MSG_RESULT([yes]) | ||||
| 	$3 | ||||
| fi[]dnl | ||||
| ])# PKG_CHECK_MODULES | ||||
|  | ||||
|  | ||||
| # PKG_INSTALLDIR(DIRECTORY) | ||||
| # ------------------------- | ||||
| # Substitutes the variable pkgconfigdir as the location where a module | ||||
| # should install pkg-config .pc files. By default the directory is | ||||
| # $libdir/pkgconfig, but the default can be changed by passing | ||||
| # DIRECTORY. The user can override through the --with-pkgconfigdir | ||||
| # parameter. | ||||
| AC_DEFUN([PKG_INSTALLDIR], | ||||
| [m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) | ||||
| m4_pushdef([pkg_description], | ||||
|     [pkg-config installation directory @<:@]pkg_default[@:>@]) | ||||
| AC_ARG_WITH([pkgconfigdir], | ||||
|     [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],, | ||||
|     [with_pkgconfigdir=]pkg_default) | ||||
| AC_SUBST([pkgconfigdir], [$with_pkgconfigdir]) | ||||
| m4_popdef([pkg_default]) | ||||
| m4_popdef([pkg_description]) | ||||
| ]) dnl PKG_INSTALLDIR | ||||
|  | ||||
|  | ||||
| # PKG_NOARCH_INSTALLDIR(DIRECTORY) | ||||
| # ------------------------- | ||||
| # Substitutes the variable noarch_pkgconfigdir as the location where a | ||||
| # module should install arch-independent pkg-config .pc files. By | ||||
| # default the directory is $datadir/pkgconfig, but the default can be | ||||
| # changed by passing DIRECTORY. The user can override through the | ||||
| # --with-noarch-pkgconfigdir parameter. | ||||
| AC_DEFUN([PKG_NOARCH_INSTALLDIR], | ||||
| [m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) | ||||
| m4_pushdef([pkg_description], | ||||
|     [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@]) | ||||
| AC_ARG_WITH([noarch-pkgconfigdir], | ||||
|     [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],, | ||||
|     [with_noarch_pkgconfigdir=]pkg_default) | ||||
| AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir]) | ||||
| m4_popdef([pkg_default]) | ||||
| m4_popdef([pkg_description]) | ||||
| ]) dnl PKG_NOARCH_INSTALLDIR | ||||
| @@ -1,2 +0,0 @@ | ||||
| 192.254.75.98 33445 FE3914F4616E227F29B2103450D6B55A836AD4BD23F97144E2C4ABE8D504FE1B | ||||
| 2607:5600:284::2 33445 FE3914F4616E227F29B2103450D6B55A836AD4BD23F97144E2C4ABE8D504FE1B | ||||
| @@ -1 +0,0 @@ | ||||
| dist_pkgdata_DATA = DHTservers | ||||
							
								
								
									
										1
									
								
								misc/nameservers
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								misc/nameservers
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| toxme.io 1A39E7A5D5FA9CF155C751570A32E625698A60A55F6D88028F949F66144F4F25 | ||||
							
								
								
									
										148
									
								
								misc/toxic.conf.example
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								misc/toxic.conf.example
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,148 @@ | ||||
| // SAMPLE TOXIC CONFIGURATION | ||||
| // USES LIBCONFIG-ACCEPTED SYNTAX | ||||
|  | ||||
| ui = { | ||||
|   // true to enable timestamps, false to disable | ||||
|   timestamps=true; | ||||
|  | ||||
|   // true to enable acoustic alerts on messages, false to disable | ||||
|   alerts=true; | ||||
|  | ||||
|   // Output a bell when receiving a message (see manpage) | ||||
|   bell_on_message=true; | ||||
|  | ||||
|   // Output a bell when receiving a filetransfer (see manpage) | ||||
|   bell_on_filetrans=true; | ||||
|  | ||||
|   // Don't output a bell when a filetransfer was accepted (see manpage) | ||||
|   bell_on_filetrans_accept=false; | ||||
|  | ||||
|   // Output a bell when receiving a group/call invite (see manpage) | ||||
|   bell_on_invite=true; | ||||
|  | ||||
|   // true to use native terminal colours, false to use toxic default colour theme | ||||
|   native_colors=false; | ||||
|  | ||||
|   // set background color of chat status bars (black, white, red, green, blue, cyan, yellow, magenta) | ||||
|   color_bar_bg="blue"; | ||||
|  | ||||
|   // set foreground (text) color of chat status bars (black, white, red, green, blue, cyan, yellow, magenta) | ||||
|   color_bar_fg="white"; | ||||
|  | ||||
|   // set foreground accent color of chat status bars (black, white, red, green, blue, cyan, yellow, magenta) | ||||
|   color_bar_accent="cyan"; | ||||
|  | ||||
|   // set foreground notify (and typing) color in chat status bar (black, white, red, green, blue, cyan, yellow, magenta) | ||||
|   color_bar_notify="yellow"; | ||||
|  | ||||
|   // 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"; | ||||
|  | ||||
|   // 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; | ||||
|  | ||||
|   // true to show friend connection change messages on the home screen | ||||
|   show_connection_msg=true; | ||||
|  | ||||
|   // How often in days to update the DHT nodes list. (0 to disable updates) | ||||
|   nodeslist_update_freq=7; | ||||
|  | ||||
|   // How often in seconds to auto-save the Tox data file. (0 to disable periodic auto-saves) | ||||
|   autosave_freq=600; | ||||
|  | ||||
|   // maximum lines for chat window history | ||||
|   history_size=700; | ||||
|  | ||||
|   // time in milliseconds to display a notification | ||||
|   notification_timeout=6000; | ||||
|  | ||||
|   // 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 threshold; float (recommended values are 1.0-40.0) | ||||
|   VAD_threshold=5.0; | ||||
|  | ||||
|   // Number of channels to use for conference audio broadcasts; 1 for mono, 2 for stereo. | ||||
|   conference_audio_channels=1; | ||||
|  | ||||
|   // Number of channels to use for 1-on-1 audio broadcasts; 1 for mono, 2 for stereo. | ||||
|   chat_audio_channels=2; | ||||
|  | ||||
|   // toggle conference push-to-talk | ||||
|   push_to_talk=false; | ||||
| }; | ||||
|  | ||||
| tox = { | ||||
|   // Path for downloaded files | ||||
|   // download_path="/home/USERNAME/Downloads/"; | ||||
|  | ||||
|   // Path for your avatar (file must be a .png and cannot exceed 64 KiB) | ||||
|   // avatar_path="/home/USERNAME/Pictures/youravatar.png"; | ||||
|  | ||||
|   // Path for scripts that should be run on startup | ||||
|   // autorun_path="/home/USERNAME/toxic_scripts/"; | ||||
|  | ||||
|   // 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: Ctrl+M does not work | ||||
| 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"; | ||||
|   toggle_peerlist="Ctrl+B"; | ||||
|   toggle_paste_mode="Ctrl+T"; | ||||
| }; | ||||
							
								
								
									
										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. | ||||
							
								
								
									
										217
									
								
								src/api.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								src/api.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,217 @@ | ||||
| /*  api.c | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2017 Jakob Kreuze <jakob@memeware.net> | ||||
|  * | ||||
|  *  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 <dirent.h> | ||||
| #include <stdint.h> | ||||
|  | ||||
| #include <tox/tox.h> | ||||
|  | ||||
| #include "execute.h" | ||||
| #include "friendlist.h" | ||||
| #include "line_info.h" | ||||
| #include "message_queue.h" | ||||
| #include "misc_tools.h" | ||||
| #include "settings.h" | ||||
| #include "toxic_strings.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| #ifdef PYTHON | ||||
| #include "python_api.h" | ||||
|  | ||||
| Tox              *user_tox; | ||||
| static WINDOW    *cur_window; | ||||
| static ToxWindow *self_window; | ||||
|  | ||||
| extern FriendsList Friends; | ||||
| extern struct user_settings *user_settings; | ||||
|  | ||||
| void api_display(const char *const msg) | ||||
| { | ||||
|     if (msg == NULL) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     self_window = get_active_window(); | ||||
|     line_info_add(self_window, NULL, NULL, NULL, SYS_MSG, 0, 0, msg); | ||||
| } | ||||
|  | ||||
| FriendsList api_get_friendslist(void) | ||||
| { | ||||
|     return Friends; | ||||
| } | ||||
|  | ||||
| char *api_get_nick(void) | ||||
| { | ||||
|     size_t   len  = tox_self_get_name_size(user_tox); | ||||
|     uint8_t *name = malloc(len + 1); | ||||
|  | ||||
|     if (name == NULL) { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     tox_self_get_name(user_tox, name); | ||||
|     name[len] = '\0'; | ||||
|     return (char *) name; | ||||
| } | ||||
|  | ||||
| Tox_User_Status api_get_status(void) | ||||
| { | ||||
|     return tox_self_get_status(user_tox); | ||||
| } | ||||
|  | ||||
| char *api_get_status_message(void) | ||||
| { | ||||
|     size_t   len    = tox_self_get_status_message_size(user_tox); | ||||
|     uint8_t *status = malloc(len + 1); | ||||
|  | ||||
|     if (status == NULL) { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     tox_self_get_status_message(user_tox, status); | ||||
|     status[len] = '\0'; | ||||
|     return (char *) status; | ||||
| } | ||||
|  | ||||
| void api_send(const char *msg) | ||||
| { | ||||
|     if (msg == NULL || self_window->chatwin->cqueue == NULL) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     char *name = api_get_nick(); | ||||
|  | ||||
|     if (name == NULL) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     self_window = get_active_window(); | ||||
|  | ||||
|     strncpy((char *) self_window->chatwin->line, msg, sizeof(self_window->chatwin->line)); | ||||
|     add_line_to_hist(self_window->chatwin); | ||||
|     int id = line_info_add(self_window, true, name, NULL, OUT_MSG, 0, 0, "%s", msg); | ||||
|     cqueue_add(self_window->chatwin->cqueue, msg, strlen(msg), OUT_MSG, id); | ||||
|     free(name); | ||||
| } | ||||
|  | ||||
| void api_execute(const char *input, int mode) | ||||
| { | ||||
|     self_window = get_active_window(); | ||||
|     execute(cur_window, self_window, user_tox, input, mode); | ||||
| } | ||||
|  | ||||
| int do_plugin_command(int num_args, char (*args)[MAX_STR_SIZE]) | ||||
| { | ||||
|     return do_python_command(num_args, args); | ||||
| } | ||||
|  | ||||
| int num_registered_handlers(void) | ||||
| { | ||||
|     return python_num_registered_handlers(); | ||||
| } | ||||
|  | ||||
| int help_max_width(void) | ||||
| { | ||||
|     return python_help_max_width(); | ||||
| } | ||||
|  | ||||
| void draw_handler_help(WINDOW *win) | ||||
| { | ||||
|     python_draw_handler_help(win); | ||||
| } | ||||
|  | ||||
| void cmd_run(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     FILE       *fp; | ||||
|     const char *error_str; | ||||
|  | ||||
|     cur_window  = window; | ||||
|     self_window = self; | ||||
|  | ||||
|     if (argc != 1) { | ||||
|         if (argc < 1) { | ||||
|             error_str = "Path must be specified."; | ||||
|         } else { | ||||
|             error_str = "Only one argument allowed."; | ||||
|         } | ||||
|  | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, error_str); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     fp = fopen(argv[1], "r"); | ||||
|  | ||||
|     if (fp == NULL) { | ||||
|         error_str = "Path does not exist."; | ||||
|  | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, error_str); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     run_python(fp, argv[1]); | ||||
|     fclose(fp); | ||||
| } | ||||
|  | ||||
| void invoke_autoruns(WINDOW *window, ToxWindow *self) | ||||
| { | ||||
|     char abspath_buf[PATH_MAX + 256]; | ||||
|     char err_buf[PATH_MAX + 128]; | ||||
|  | ||||
|     if (user_settings->autorun_path[0] == '\0') { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     DIR *d = opendir(user_settings->autorun_path); | ||||
|  | ||||
|     if (d == NULL) { | ||||
|         snprintf(err_buf, sizeof(err_buf), "Autorun path does not exist: %s", user_settings->autorun_path); | ||||
|         api_display(err_buf); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     struct dirent *dir = NULL; | ||||
|  | ||||
|     cur_window  = window; | ||||
|  | ||||
|     self_window = self; | ||||
|  | ||||
|     while ((dir = readdir(d)) != NULL) { | ||||
|         size_t path_len = strlen(dir->d_name); | ||||
|  | ||||
|         if (!strcmp(dir->d_name + path_len - 3, ".py")) { | ||||
|             snprintf(abspath_buf, sizeof(abspath_buf), "%s%s", user_settings->autorun_path, dir->d_name); | ||||
|             FILE *fp = fopen(abspath_buf, "r"); | ||||
|  | ||||
|             if (fp == NULL) { | ||||
|                 snprintf(err_buf, sizeof(err_buf), "Invalid path: %s", abspath_buf); | ||||
|                 api_display(err_buf); | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             run_python(fp, abspath_buf); | ||||
|             fclose(fp); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     closedir(d); | ||||
| } | ||||
| #endif /* PYTHON */ | ||||
							
								
								
									
										43
									
								
								src/api.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/api.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| /*  api.h | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2017 Jakob Kreuze <jakob@memeware.net> | ||||
|  * | ||||
|  *  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 API_H | ||||
| #define API_H | ||||
|  | ||||
| #include "friendlist.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| void api_display(const char *const msg); | ||||
| FriendsList api_get_friendslist(void); | ||||
| char *api_get_nick(void); | ||||
| Tox_User_Status api_get_status(void); | ||||
| char *api_get_status_message(void); | ||||
| void api_send(const char *msg); | ||||
| void api_execute(const char *input, int mode); | ||||
| int do_plugin_command(int num_args, char (*args)[MAX_STR_SIZE]); | ||||
| int num_registered_handlers(void); | ||||
| int help_max_width(void); | ||||
| void draw_handler_help(WINDOW *win); | ||||
| void invoke_autoruns(WINDOW *w, ToxWindow *self); | ||||
| void cmd_run(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
|  | ||||
| #endif /* API_H */ | ||||
							
								
								
									
										959
									
								
								src/audio_call.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										959
									
								
								src/audio_call.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,959 @@ | ||||
| /*  audio_call.c | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||
|  * | ||||
|  *  This file is part of Toxic. | ||||
|  * | ||||
|  *  Toxic is free software: you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation, either version 3 of the License, or | ||||
|  *  (at your option) any later version. | ||||
|  * | ||||
|  *  Toxic is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "audio_call.h" | ||||
| #include "audio_device.h" | ||||
| #include "chat_commands.h" | ||||
| #include "chat.h" | ||||
| #include "friendlist.h" | ||||
| #include "global_commands.h" | ||||
| #include "line_info.h" | ||||
| #include "misc_tools.h" | ||||
| #include "notify.h" | ||||
| #include "settings.h" | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| #ifdef AUDIO | ||||
|  | ||||
| #ifdef VIDEO | ||||
| #include "video_call.h" | ||||
| #endif /* VIDEO */ | ||||
|  | ||||
| #include <assert.h> | ||||
| #include <curses.h> | ||||
| #include <pthread.h> | ||||
| #include <stdbool.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <strings.h> | ||||
| #include <unistd.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__ */ | ||||
|  | ||||
| extern FriendsList Friends; | ||||
| extern ToxWindow *windows[MAX_WINDOWS_NUM]; | ||||
|  | ||||
| struct CallControl CallControl; | ||||
|  | ||||
| extern struct user_settings *user_settings; | ||||
| extern struct Winthread Winthread; | ||||
|  | ||||
| void on_call(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled, | ||||
|              void *user_data); | ||||
| void on_call_state(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_data); | ||||
| void on_audio_receive_frame(ToxAV *av, uint32_t friend_number, int16_t const *pcm, size_t sample_count, | ||||
|                             uint8_t channels, uint32_t sampling_rate, void *user_data); | ||||
|  | ||||
| void callback_recv_invite(Tox *m, uint32_t friend_number); | ||||
| void callback_recv_ringing(uint32_t friend_number); | ||||
| void callback_recv_starting(uint32_t friend_number); | ||||
| void callback_recv_ending(uint32_t friend_number); | ||||
| void callback_call_started(uint32_t friend_number); | ||||
| void callback_call_canceled(uint32_t friend_number); | ||||
| void callback_call_rejected(uint32_t friend_number); | ||||
| void callback_call_ended(uint32_t friend_number); | ||||
|  | ||||
| void write_device_callback(uint32_t friend_number, const int16_t *PCM, uint16_t sample_count, uint8_t channels, | ||||
|                            uint32_t sample_rate); | ||||
|  | ||||
| void audio_bit_rate_callback(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, void *user_data); | ||||
|  | ||||
| static void print_err(ToxWindow *self, const char *error_str) | ||||
| { | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", error_str); | ||||
| } | ||||
|  | ||||
| ToxAV *init_audio(ToxWindow *self, Tox *tox) | ||||
| { | ||||
|     Toxav_Err_New error; | ||||
|     CallControl.audio_errors = ae_None; | ||||
|     CallControl.prompt = self; | ||||
|  | ||||
|     CallControl.av = toxav_new(tox, &error); | ||||
|  | ||||
|     CallControl.audio_enabled = true; | ||||
|     CallControl.default_audio_bit_rate = 64; | ||||
|     CallControl.audio_sample_rate = 48000; | ||||
|     CallControl.audio_frame_duration = 20; | ||||
|     CallControl.audio_channels = user_settings->chat_audio_channels; | ||||
|  | ||||
|     CallControl.video_enabled = false; | ||||
|     CallControl.default_video_bit_rate = 0; | ||||
|     CallControl.video_frame_duration = 0; | ||||
|  | ||||
|     if (!CallControl.av) { | ||||
|         CallControl.audio_errors |= ae_StartingCoreAudio; | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to init ToxAV"); | ||||
|  | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     if (init_devices() == de_InternalError) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to init devices"); | ||||
|         toxav_kill(CallControl.av); | ||||
|  | ||||
|         return CallControl.av = NULL; | ||||
|     } | ||||
|  | ||||
|     toxav_callback_call(CallControl.av, on_call, tox); | ||||
|     toxav_callback_call_state(CallControl.av, on_call_state, NULL); | ||||
|     toxav_callback_audio_receive_frame(CallControl.av, on_audio_receive_frame, NULL); | ||||
|     toxav_callback_audio_bit_rate(CallControl.av, audio_bit_rate_callback, NULL); | ||||
|  | ||||
|     return CallControl.av; | ||||
| } | ||||
|  | ||||
| void read_device_callback(const int16_t *captured, uint32_t size, void *data) | ||||
| { | ||||
|     UNUSED_VAR(size); | ||||
|  | ||||
|     Toxav_Err_Send_Frame error; | ||||
|     uint32_t friend_number = *((uint32_t *)data); /* TODO: Or pass an array of call_idx's */ | ||||
|     int64_t sample_count = ((int64_t) CallControl.audio_sample_rate) * \ | ||||
|                            ((int64_t) CallControl.audio_frame_duration) / 1000; | ||||
|  | ||||
|     if (sample_count <= 0) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     toxav_audio_send_frame(CallControl.av, friend_number, | ||||
|                            captured, sample_count, | ||||
|                            CallControl.audio_channels, | ||||
|                            CallControl.audio_sample_rate, &error); | ||||
| } | ||||
|  | ||||
| void write_device_callback(uint32_t friend_number, const int16_t *PCM, uint16_t sample_count, uint8_t channels, | ||||
|                            uint32_t sample_rate) | ||||
| { | ||||
|     if (CallControl.calls[friend_number].status == cs_Active) { | ||||
|         write_out(CallControl.calls[friend_number].out_idx, PCM, sample_count, channels, sample_rate); | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool init_call(Call *call) | ||||
| { | ||||
|     if (call->status != cs_None) { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     *call = (struct Call) { | ||||
|         0 | ||||
|     }; | ||||
|  | ||||
|     call->status = cs_Pending; | ||||
|  | ||||
|     call->in_idx = -1; | ||||
|     call->out_idx = -1; | ||||
|     call->audio_bit_rate = CallControl.default_audio_bit_rate; | ||||
| #ifdef VIDEO | ||||
|     call->vin_idx = -1; | ||||
|     call->vout_idx = -1; | ||||
|     call->video_width = CallControl.default_video_width; | ||||
|     call->video_height = CallControl.default_video_height; | ||||
|     call->video_bit_rate = CallControl.default_video_bit_rate; | ||||
| #endif /* VIDEO */ | ||||
|  | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| static bool cancel_call(Call *call) | ||||
| { | ||||
|     if (call->status != cs_Pending) { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     call->status = cs_None; | ||||
|  | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| static int start_transmission(ToxWindow *self, Call *call) | ||||
| { | ||||
|     if (!self || !CallControl.av) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to prepare audio transmission"); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     DeviceError error = open_input_device(&call->in_idx, read_device_callback, &self->num, false, | ||||
|                                           CallControl.audio_sample_rate, CallControl.audio_frame_duration, CallControl.audio_channels); | ||||
|  | ||||
|     if (error != de_None) { | ||||
|         if (error == de_FailedStart) { | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to start audio input device"); | ||||
|         } | ||||
|  | ||||
|         if (error == de_InternalError) { | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Internal error with opening audio input device"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (open_output_device(&call->out_idx, | ||||
|                            CallControl.audio_sample_rate, CallControl.audio_frame_duration, CallControl.audio_channels) != de_None) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to open audio output device!"); | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static void start_call(ToxWindow *self, Call *call) | ||||
| { | ||||
|     if (call->status != cs_Pending) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (start_transmission(self, call) != 0) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     call->status = cs_Active; | ||||
|  | ||||
| #ifdef VIDEO | ||||
|  | ||||
|     if (call->state & TOXAV_FRIEND_CALL_STATE_SENDING_V) { | ||||
|         callback_recv_video_starting(self->num); | ||||
|     } | ||||
|  | ||||
|     if (call->video_bit_rate) { | ||||
|         start_video_transmission(self, CallControl.av, call); | ||||
|     } | ||||
|  | ||||
| #endif | ||||
| } | ||||
|  | ||||
| static int stop_transmission(Call *call, uint32_t friend_number) | ||||
| { | ||||
|     if (call->status != cs_Active) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     call->status = cs_None; | ||||
|  | ||||
|     if (call->in_idx != -1) { | ||||
|         close_device(input, call->in_idx); | ||||
|     } | ||||
|  | ||||
|     if (call->out_idx != -1) { | ||||
|         close_device(output, call->out_idx); | ||||
|     } | ||||
|  | ||||
|     Toxav_Err_Call_Control error = TOXAV_ERR_CALL_CONTROL_OK; | ||||
|  | ||||
|     if (call->state > TOXAV_FRIEND_CALL_STATE_FINISHED) { | ||||
|         toxav_call_control(CallControl.av, friend_number, TOXAV_CALL_CONTROL_CANCEL, &error); | ||||
|     } | ||||
|  | ||||
|     if (error != TOXAV_ERR_CALL_CONTROL_OK) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| void terminate_audio(void) | ||||
| { | ||||
|     for (int i = 0; i < CallControl.max_calls; ++i) { | ||||
|         stop_transmission(&CallControl.calls[i], i); | ||||
|     } | ||||
|  | ||||
|     if (CallControl.av) { | ||||
|         toxav_kill(CallControl.av); | ||||
|     } | ||||
|  | ||||
|     terminate_devices(); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * End of transmission | ||||
|  */ | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * Callbacks | ||||
|  */ | ||||
| void on_call(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data) | ||||
| { | ||||
|     UNUSED_VAR(av); | ||||
|  | ||||
|     Tox *m = (Tox *) user_data; | ||||
|  | ||||
|     Call *call = &CallControl.calls[friend_number]; | ||||
|     init_call(call); | ||||
|  | ||||
|     call->state = TOXAV_FRIEND_CALL_STATE_ACCEPTING_A | TOXAV_FRIEND_CALL_STATE_ACCEPTING_V; | ||||
|  | ||||
|     if (audio_enabled) { | ||||
|         call->state |= TOXAV_FRIEND_CALL_STATE_SENDING_A; | ||||
|     } | ||||
|  | ||||
|     if (video_enabled) { | ||||
|         call->state |= TOXAV_FRIEND_CALL_STATE_SENDING_V; | ||||
|     } | ||||
|  | ||||
|     callback_recv_invite(m, friend_number); | ||||
| } | ||||
|  | ||||
| void on_call_state(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_data) | ||||
| { | ||||
|     UNUSED_VAR(av); | ||||
|     UNUSED_VAR(user_data); | ||||
|  | ||||
|     Call *call = &CallControl.calls[friend_number]; | ||||
|  | ||||
|     if (call->status == cs_None) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     call->state = state; | ||||
|  | ||||
|     switch (state) { | ||||
|         case TOXAV_FRIEND_CALL_STATE_ERROR: | ||||
|         case TOXAV_FRIEND_CALL_STATE_FINISHED: | ||||
|             if (state == TOXAV_FRIEND_CALL_STATE_ERROR) { | ||||
|                 line_info_add(CallControl.prompt, false, NULL, NULL, SYS_MSG, 0, 0, "ToxAV callstate error!"); | ||||
|             } | ||||
|  | ||||
|             if (call->status == cs_Pending) { | ||||
|                 cancel_call(call); | ||||
|                 callback_call_rejected(friend_number); | ||||
|             } else { | ||||
|  | ||||
| #ifdef VIDEO | ||||
|                 callback_recv_video_end(friend_number); | ||||
|                 callback_video_end(friend_number); | ||||
| #endif /* VIDEO */ | ||||
|  | ||||
|                 stop_transmission(call, friend_number); | ||||
|                 callback_call_ended(friend_number); | ||||
|             } | ||||
|  | ||||
|             break; | ||||
|  | ||||
|         default: | ||||
|             if (call->status == cs_Pending) { | ||||
|                 /* Start answered call */ | ||||
|                 callback_call_started(friend_number); | ||||
|             } | ||||
|  | ||||
| #ifdef VIDEO | ||||
|  | ||||
|             /* Handle receiving client video call states */ | ||||
|             if (state & TOXAV_FRIEND_CALL_STATE_SENDING_V) { | ||||
|                 callback_recv_video_starting(friend_number); | ||||
|             } else { | ||||
|                 callback_recv_video_end(friend_number); | ||||
|             } | ||||
|  | ||||
| #endif /* VIDEO */ | ||||
|  | ||||
|             break; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void on_audio_receive_frame(ToxAV *av, uint32_t friend_number, | ||||
|                             int16_t const *pcm, size_t sample_count, | ||||
|                             uint8_t channels, uint32_t sampling_rate, void *user_data) | ||||
| { | ||||
|     UNUSED_VAR(av); | ||||
|     UNUSED_VAR(user_data); | ||||
|  | ||||
|     write_device_callback(friend_number, pcm, sample_count, channels, sampling_rate); | ||||
| } | ||||
|  | ||||
| void audio_bit_rate_callback(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, void *user_data) | ||||
| { | ||||
|     UNUSED_VAR(user_data); | ||||
|  | ||||
|     Call *call = &CallControl.calls[friend_number]; | ||||
|     call->audio_bit_rate = audio_bit_rate; | ||||
|     toxav_audio_set_bit_rate(av, friend_number, audio_bit_rate, NULL); | ||||
| } | ||||
|  | ||||
| void callback_recv_invite(Tox *m, uint32_t friend_number) | ||||
| { | ||||
|     if (friend_number >= Friends.max_idx) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (Friends.list[friend_number].chatwin == -1) { | ||||
|         if (get_num_active_windows() >= MAX_WINDOWS_NUM) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         Friends.list[friend_number].chatwin = add_window(m, new_chat(m, Friends.list[friend_number].num)); | ||||
|     } | ||||
|  | ||||
|     const Call *call = &CallControl.calls[friend_number]; | ||||
|  | ||||
|     for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) { | ||||
|         if (windows[i] != NULL && windows[i]->onInvite != NULL && windows[i]->num == friend_number) { | ||||
|             windows[i]->onInvite(windows[i], CallControl.av, friend_number, call->state); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| void callback_recv_ringing(uint32_t friend_number) | ||||
| { | ||||
|     const Call *call = &CallControl.calls[friend_number]; | ||||
|  | ||||
|     for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) { | ||||
|         if (windows[i] != NULL && windows[i]->onRinging != NULL && windows[i]->num == friend_number) { | ||||
|             windows[i]->onRinging(windows[i], CallControl.av, friend_number, call->state); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| void callback_recv_starting(uint32_t friend_number) | ||||
| { | ||||
|     Call *call = &CallControl.calls[friend_number]; | ||||
|  | ||||
|     for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) { | ||||
|         if (windows[i] != NULL && windows[i]->onStarting != NULL && windows[i]->num == friend_number) { | ||||
|             windows[i]->onStarting(windows[i], CallControl.av, friend_number, call->state); | ||||
|  | ||||
|             start_call(windows[i], call); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| void callback_recv_ending(uint32_t friend_number) | ||||
| { | ||||
|     const Call *call = &CallControl.calls[friend_number]; | ||||
|  | ||||
|     for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) { | ||||
|         if (windows[i] != NULL && windows[i]->onEnding != NULL && windows[i]->num == friend_number) { | ||||
|             windows[i]->onEnding(windows[i], CallControl.av, friend_number, call->state); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| void callback_call_started(uint32_t friend_number) | ||||
| { | ||||
|     Call *call = &CallControl.calls[friend_number]; | ||||
|  | ||||
|     for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) { | ||||
|         if (windows[i] != NULL && windows[i]->onStart != NULL && windows[i]->num == friend_number) { | ||||
|             windows[i]->onStart(windows[i], CallControl.av, friend_number, call->state); | ||||
|  | ||||
|             start_call(windows[i], call); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| void callback_call_canceled(uint32_t friend_number) | ||||
| { | ||||
|     const Call *call = &CallControl.calls[friend_number]; | ||||
|  | ||||
|     for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) { | ||||
|         if (windows[i] != NULL && windows[i]->onCancel != NULL && windows[i]->num == friend_number) { | ||||
|             windows[i]->onCancel(windows[i], CallControl.av, friend_number, call->state); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| void callback_call_rejected(uint32_t friend_number) | ||||
| { | ||||
|     const Call *call = &CallControl.calls[friend_number]; | ||||
|  | ||||
|     for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) { | ||||
|         if (windows[i] != NULL && windows[i]->onReject != NULL && windows[i]->num == friend_number) { | ||||
|             windows[i]->onReject(windows[i], CallControl.av, friend_number, call->state); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| void callback_call_ended(uint32_t friend_number) | ||||
| { | ||||
|     const Call *call = &CallControl.calls[friend_number]; | ||||
|  | ||||
|     for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) { | ||||
|         if (windows[i] != NULL && windows[i]->onEnd != NULL && windows[i]->num == friend_number) { | ||||
|             windows[i]->onEnd(windows[i], CallControl.av, friend_number, call->state); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * End of Callbacks | ||||
|  */ | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * Commands from chat_commands.h | ||||
|  */ | ||||
| void cmd_call(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|     UNUSED_VAR(m); | ||||
|     UNUSED_VAR(argv); | ||||
|  | ||||
|     if (argc != 0) { | ||||
|         print_err(self, "Unknown arguments."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (!CallControl.av) { | ||||
|         print_err(self, "ToxAV not supported!"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (!self->stb->connection) { | ||||
|         print_err(self, "Friend is offline."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     Call *call = &CallControl.calls[self->num]; | ||||
|  | ||||
|     if (call->status != cs_None) { | ||||
|         print_err(self, "Already calling."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     init_call(call); | ||||
|  | ||||
|     place_call(self); | ||||
| } | ||||
|  | ||||
| void cmd_answer(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|     UNUSED_VAR(m); | ||||
|     UNUSED_VAR(argv); | ||||
|  | ||||
|     Toxav_Err_Answer error; | ||||
|  | ||||
|     if (argc != 0) { | ||||
|         print_err(self, "Unknown arguments."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (!CallControl.av) { | ||||
|         print_err(self, "Audio not supported!"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     Call *call = &CallControl.calls[self->num]; | ||||
|  | ||||
|     if (call->status != cs_Pending) { | ||||
|         print_err(self, "No incoming call!"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     toxav_answer(CallControl.av, self->num, call->audio_bit_rate, call->video_bit_rate, &error); | ||||
|  | ||||
|     if (error != TOXAV_ERR_ANSWER_OK) { | ||||
|         if (error == TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING) { | ||||
|             print_err(self, "No incoming call!"); | ||||
|         } else if (error == TOXAV_ERR_ANSWER_CODEC_INITIALIZATION) { | ||||
|             print_err(self, "Failed to initialize codecs!"); | ||||
|         } else if (error == TOXAV_ERR_ANSWER_FRIEND_NOT_FOUND) { | ||||
|             print_err(self, "Friend not found!"); | ||||
|         } else if (error == TOXAV_ERR_ANSWER_INVALID_BIT_RATE) { | ||||
|             print_err(self, "Invalid bit rate!"); | ||||
|         } else { | ||||
|             print_err(self, "Internal error!"); | ||||
|         } | ||||
|  | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     /* Callback will print status... */ | ||||
|     callback_recv_starting(self->num); | ||||
| } | ||||
|  | ||||
| void cmd_reject(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|     UNUSED_VAR(m); | ||||
|     UNUSED_VAR(argv); | ||||
|  | ||||
|     if (argc != 0) { | ||||
|         print_err(self, "Unknown arguments."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (!CallControl.av) { | ||||
|         print_err(self, "Audio not supported!"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     Call *call = &CallControl.calls[self->num]; | ||||
|  | ||||
|     if (call->status != cs_Pending) { | ||||
|         print_err(self, "No incoming call!"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     /* Manually send a cancel call control because call hasn't started */ | ||||
|     toxav_call_control(CallControl.av, self->num, TOXAV_CALL_CONTROL_CANCEL, NULL); | ||||
|     cancel_call(call); | ||||
|  | ||||
|     /* Callback will print status... */ | ||||
|     callback_call_rejected(self->num); | ||||
| } | ||||
|  | ||||
| void cmd_hangup(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|     UNUSED_VAR(m); | ||||
|     UNUSED_VAR(argv); | ||||
|  | ||||
|     if (!CallControl.av) { | ||||
|         print_err(self, "Audio not supported!"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (argc != 0) { | ||||
|         print_err(self, "Unknown arguments."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     Call *call = &CallControl.calls[self->num]; | ||||
|  | ||||
|     if (call->status == cs_None) { | ||||
|         print_err(self, "Not in a call."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     stop_current_call(self); | ||||
| } | ||||
|  | ||||
| void cmd_list_devices(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|     UNUSED_VAR(m); | ||||
|  | ||||
|     if (argc != 1) { | ||||
|         if (argc < 1) { | ||||
|             print_err(self, "Type must be specified!"); | ||||
|         } else { | ||||
|             print_err(self, "Only one argument allowed!"); | ||||
|         } | ||||
|  | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     DeviceType type; | ||||
|  | ||||
|     if (strcasecmp(argv[1], "in") == 0) { /* Input devices */ | ||||
|         type = input; | ||||
|     } | ||||
|  | ||||
|     else if (strcasecmp(argv[1], "out") == 0) { /* Output devices */ | ||||
|         type = output; | ||||
|     } | ||||
|  | ||||
|     else { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     // Refresh device list. | ||||
|     get_al_device_names(); | ||||
|  | ||||
|     print_al_devices(self, type); | ||||
| } | ||||
|  | ||||
| /* This changes primary device only */ | ||||
| void cmd_change_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|     UNUSED_VAR(m); | ||||
|  | ||||
|     if (argc != 2) { | ||||
|         if (argc < 1) { | ||||
|             print_err(self, "Type must be specified!"); | ||||
|         } else if (argc < 2) { | ||||
|             print_err(self, "Must have id!"); | ||||
|         } else { | ||||
|             print_err(self, "Only two arguments allowed!"); | ||||
|         } | ||||
|  | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     DeviceType type; | ||||
|  | ||||
|     if (strcmp(argv[1], "in") == 0) { /* Input devices */ | ||||
|         type = input; | ||||
|     } | ||||
|  | ||||
|     else if (strcmp(argv[1], "out") == 0) { /* Output devices */ | ||||
|         type = output; | ||||
|     } | ||||
|  | ||||
|     else { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     char *end; | ||||
|     long int selection = strtol(argv[2], &end, 10); | ||||
|  | ||||
|     if (*end) { | ||||
|         print_err(self, "Invalid input"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (set_al_device(type, selection) == de_InvalidSelection) { | ||||
|         print_err(self, "Invalid selection!"); | ||||
|         return; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void cmd_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|     UNUSED_VAR(m); | ||||
|  | ||||
|     if (argc != 1) { | ||||
|         print_err(self, "Specify type: \"/mute in\" or \"/mute out\"."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     DeviceType type; | ||||
|  | ||||
|     if (strcasecmp(argv[1], "in") == 0) { /* Input devices */ | ||||
|         type = input; | ||||
|     } | ||||
|  | ||||
|     else if (strcasecmp(argv[1], "out") == 0) { /* Output devices */ | ||||
|         type = output; | ||||
|     } | ||||
|  | ||||
|     else { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /* If call is active, use this_call values */ | ||||
|     Call *this_call = &CallControl.calls[self->num]; | ||||
|  | ||||
|     if (this_call->status == cs_Active) { | ||||
|         if (type == input) { | ||||
|             device_mute(type, this_call->in_idx); | ||||
|             self->chatwin->infobox.in_is_muted = device_is_muted(type, this_call->in_idx); | ||||
|         } else { | ||||
|             device_mute(type, this_call->out_idx); | ||||
|             self->chatwin->infobox.out_is_muted = device_is_muted(type, this_call->out_idx); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return; | ||||
| } | ||||
|  | ||||
| void cmd_sense(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|     UNUSED_VAR(m); | ||||
|  | ||||
|     if (argc != 1) { | ||||
|         if (argc < 1) { | ||||
|             print_err(self, "Must have value!"); | ||||
|         } else { | ||||
|             print_err(self, "Only two arguments allowed!"); | ||||
|         } | ||||
|  | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     char *end; | ||||
|     float value = strtof(argv[1], &end); | ||||
|  | ||||
|     if (*end) { | ||||
|         print_err(self, "Invalid input"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     const Call *call = &CallControl.calls[self->num]; | ||||
|  | ||||
|     /* Call must be active */ | ||||
|     if (call->status == cs_Active) { | ||||
|         device_set_VAD_threshold(call->in_idx, value); | ||||
|         self->chatwin->infobox.vad_lvl = value; | ||||
|     } | ||||
|  | ||||
|     return; | ||||
| } | ||||
|  | ||||
| void cmd_bitrate(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|     UNUSED_VAR(m); | ||||
|  | ||||
|     Call *call = &CallControl.calls[self->num]; | ||||
|  | ||||
|     if (call->status != cs_Active) { | ||||
|         print_err(self, "Must be in a call"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (argc == 0) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, | ||||
|                       "Current audio encoding bitrate: %u", call->audio_bit_rate); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (argc > 1) { | ||||
|         print_err(self, "Too many arguments."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     char *end; | ||||
|     const long int bit_rate = strtol(argv[1], &end, 10); | ||||
|  | ||||
|     if (*end || bit_rate < 0 || bit_rate > UINT32_MAX) { | ||||
|         print_err(self, "Invalid input"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     Toxav_Err_Bit_Rate_Set error; | ||||
|     toxav_audio_set_bit_rate(CallControl.av, self->num, bit_rate, &error); | ||||
|  | ||||
|     if (error != TOXAV_ERR_BIT_RATE_SET_OK) { | ||||
|         if (error == TOXAV_ERR_BIT_RATE_SET_SYNC) { | ||||
|             print_err(self, "Synchronization error occured"); | ||||
|         } else if (error == TOXAV_ERR_BIT_RATE_SET_INVALID_BIT_RATE) { | ||||
|             print_err(self, "Invalid audio bit rate value (valid is 6-510)"); | ||||
|         } else if (error == TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_FOUND) { | ||||
|             print_err(self, "Friend not found"); | ||||
|         } else if (error == TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_IN_CALL) { | ||||
|             print_err(self, "Friend is not in the call"); | ||||
|         } else { | ||||
|             print_err(self, "Unknown error"); | ||||
|         } | ||||
|  | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     call->audio_bit_rate = bit_rate; | ||||
|  | ||||
|     return; | ||||
| } | ||||
|  | ||||
| void place_call(ToxWindow *self) | ||||
| { | ||||
|     Call *call = &CallControl.calls[self->num]; | ||||
|  | ||||
|     if (call->status != cs_Pending) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     Toxav_Err_Call error; | ||||
|  | ||||
|     toxav_call(CallControl.av, self->num, call->audio_bit_rate, call->video_bit_rate, &error); | ||||
|  | ||||
|     if (error != TOXAV_ERR_CALL_OK) { | ||||
|         if (error == TOXAV_ERR_CALL_FRIEND_ALREADY_IN_CALL) { | ||||
|             print_err(self, "Already in a call!"); | ||||
|         } else if (error == TOXAV_ERR_CALL_MALLOC) { | ||||
|             print_err(self, "Memory allocation issue"); | ||||
|         } else if (error == TOXAV_ERR_CALL_FRIEND_NOT_FOUND) { | ||||
|             print_err(self, "Friend number invalid"); | ||||
|         } else if (error == TOXAV_ERR_CALL_FRIEND_NOT_CONNECTED) { | ||||
|             print_err(self, "Friend is valid but not currently connected"); | ||||
|         } else { | ||||
|             print_err(self, "Internal error!"); | ||||
|         } | ||||
|  | ||||
|         cancel_call(call); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     callback_recv_ringing(self->num); | ||||
| } | ||||
|  | ||||
| void stop_current_call(ToxWindow *self) | ||||
| { | ||||
|     Call *call = &CallControl.calls[self->num]; | ||||
|  | ||||
|     if (call->status == cs_Pending) { | ||||
|         toxav_call_control(CallControl.av, self->num, TOXAV_CALL_CONTROL_CANCEL, NULL); | ||||
|  | ||||
|         cancel_call(call); | ||||
|         callback_call_canceled(self->num); | ||||
|     } else { | ||||
|  | ||||
| #ifdef VIDEO | ||||
|         callback_recv_video_end(self->num); | ||||
|         callback_video_end(self->num); | ||||
| #endif /* VIDEO */ | ||||
|  | ||||
|         stop_transmission(call, self->num); | ||||
|         callback_call_ended(self->num); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Reallocates the Calls list according to n. | ||||
|  */ | ||||
| static void realloc_calls(uint32_t n) | ||||
| { | ||||
|     if (n == 0) { | ||||
|         free(CallControl.calls); | ||||
|         CallControl.calls = NULL; | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     Call *temp = realloc(CallControl.calls, n * sizeof(Call)); | ||||
|  | ||||
|     if (temp == NULL) { | ||||
|         exit_toxic_err("failed in realloc_calls", FATALERR_MEMORY); | ||||
|     } | ||||
|  | ||||
|     CallControl.calls = temp; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Inits the call structure for a given friend. Called when a friend is added to the friends list. | ||||
|  * Index must be equivalent to the friend's friendlist index. | ||||
|  */ | ||||
| void init_friend_AV(uint32_t index) | ||||
| { | ||||
|     if (index == CallControl.max_calls) { | ||||
|         realloc_calls(CallControl.max_calls + 1); | ||||
|         CallControl.calls[CallControl.max_calls] = (Call) { | ||||
|             0 | ||||
|         }; | ||||
|         ++CallControl.max_calls; | ||||
|     } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Deletes a call structure from the Calls list. Called when a friend is deleted from the friends list. | ||||
|  * Index must be equivalent to the size of the Calls list. | ||||
|  */ | ||||
| void del_friend_AV(uint32_t index) | ||||
| { | ||||
|     realloc_calls(index); | ||||
|     CallControl.max_calls = index; | ||||
| } | ||||
|  | ||||
| #endif /* AUDIO */ | ||||
							
								
								
									
										109
									
								
								src/audio_call.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								src/audio_call.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,109 @@ | ||||
| /*  audio_call.h | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||
|  * | ||||
|  *  This file is part of Toxic. | ||||
|  * | ||||
|  *  Toxic is free software: you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation, either version 3 of the License, or | ||||
|  *  (at your option) any later version. | ||||
|  * | ||||
|  *  Toxic is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #ifndef AUDIO_CALL_H | ||||
| #define AUDIO_CALL_H | ||||
|  | ||||
| #include <tox/toxav.h> | ||||
|  | ||||
| #include "audio_device.h" | ||||
|  | ||||
| typedef enum AudioError { | ||||
|     ae_None = 0, | ||||
|     ae_StartingCaptureDevice = 1 << 0, | ||||
|     ae_StartingOutputDevice = 1 << 1, | ||||
|     ae_StartingCoreAudio = 1 << 2 | ||||
| } AudioError; | ||||
|  | ||||
| #ifdef VIDEO | ||||
| typedef enum VideoError { | ||||
|     ve_None = 0, | ||||
|     ve_StartingCaptureDevice = 1 << 0, | ||||
|     ve_StartingOutputDevice = 1 << 1, | ||||
|     ve_StartingCoreVideo = 1 << 2 | ||||
| } VideoError; | ||||
|  | ||||
| #endif /* VIDEO */ | ||||
|  | ||||
| /* Status transitions: | ||||
|  * None -> Pending (call invitation made or received); | ||||
|  * Pending -> None (invitation rejected or failed); | ||||
|  * Pending -> Active (call starts); | ||||
|  * Active -> None (call ends). | ||||
|  */ | ||||
| typedef enum CallStatus { | ||||
|     cs_None = 0, | ||||
|     cs_Pending, | ||||
|     cs_Active | ||||
| } CallStatus; | ||||
|  | ||||
| typedef struct Call { | ||||
|     CallStatus status; | ||||
|     uint32_t state; /* ToxAV call state, valid when `status == cs_Active` */ | ||||
|     uint32_t in_idx, out_idx; /* Audio device index, or -1 if not open */ | ||||
|     uint32_t audio_bit_rate; /* Bit rate for sending audio */ | ||||
|  | ||||
|     uint32_t vin_idx, vout_idx; /* Video device index, or -1 if not open */ | ||||
|     uint32_t video_width, video_height; | ||||
|     uint32_t video_bit_rate; /* Bit rate for sending video; 0 for no video */ | ||||
| } Call; | ||||
|  | ||||
| struct CallControl { | ||||
|     AudioError audio_errors; | ||||
| #ifdef VIDEO | ||||
|     VideoError video_errors; | ||||
| #endif /* VIDEO */ | ||||
|  | ||||
|     ToxAV *av; | ||||
|     ToxWindow *prompt; | ||||
|  | ||||
|     Call *calls; | ||||
|     uint32_t max_calls; | ||||
|  | ||||
|     bool audio_enabled; | ||||
|     bool video_enabled; | ||||
|  | ||||
|     int32_t audio_frame_duration; | ||||
|     uint32_t audio_sample_rate; | ||||
|     uint8_t audio_channels; | ||||
|     uint32_t default_audio_bit_rate; | ||||
|  | ||||
|     int32_t video_frame_duration; | ||||
|     uint32_t default_video_width, default_video_height; | ||||
|     uint32_t default_video_bit_rate; | ||||
| }; | ||||
|  | ||||
| extern struct CallControl CallControl; | ||||
|  | ||||
| /* 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(void); | ||||
|  | ||||
| bool init_call(Call *call); | ||||
|  | ||||
| void place_call(ToxWindow *self); | ||||
| void stop_current_call(ToxWindow *self); | ||||
|  | ||||
| void init_friend_AV(uint32_t index); | ||||
| void del_friend_AV(uint32_t index); | ||||
|  | ||||
| #endif /* AUDIO_CALL_H */ | ||||
							
								
								
									
										790
									
								
								src/audio_device.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										790
									
								
								src/audio_device.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,790 @@ | ||||
| /*  audio_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 "audio_device.h" | ||||
|  | ||||
| #include "line_info.h" | ||||
| #include "misc_tools.h" | ||||
| #include "settings.h" | ||||
|  | ||||
| #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 */ | ||||
|  | ||||
| #include <assert.h> | ||||
| #include <math.h> | ||||
| #include <pthread.h> | ||||
| #include <stdbool.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <unistd.h> | ||||
|  | ||||
| extern struct user_settings *user_settings; | ||||
| extern struct Winthread Winthread; | ||||
|  | ||||
| typedef struct FrameInfo { | ||||
|     uint32_t samples_per_frame; | ||||
|     uint32_t sample_rate; | ||||
|     bool stereo; | ||||
| } FrameInfo; | ||||
|  | ||||
| /* A virtual input/output device, abstracting the currently selected openal | ||||
|  * device (which may change during the lifetime of the virtual device). | ||||
|  * We refer to a virtual device as a "device", and refer to an underlying | ||||
|  * openal device as an "al_device". | ||||
|  * Multiple virtual devices may be open at once; the callback of each virtual | ||||
|  * input device has data captured from the input al_device passed to it, and | ||||
|  * each virtual output device acts as a source for the output al_device. | ||||
|  */ | ||||
| typedef struct Device { | ||||
|     bool active; | ||||
|     bool muted; | ||||
|  | ||||
|     FrameInfo frame_info; | ||||
|  | ||||
|     // used only by input devices: | ||||
|     DataHandleCallback cb; | ||||
|     void *cb_data; | ||||
|     float VAD_threshold; | ||||
|     uint32_t VAD_samples_remaining; | ||||
|  | ||||
|     // used only by output devices: | ||||
|     uint32_t source; | ||||
|     uint32_t buffers[OPENAL_BUFS]; | ||||
|     bool source_open; | ||||
| } Device; | ||||
|  | ||||
| typedef struct AudioState { | ||||
|     ALCdevice *al_device[2]; | ||||
|  | ||||
|     Device devices[2][MAX_DEVICES]; | ||||
|     uint32_t num_devices[2]; | ||||
|  | ||||
|     FrameInfo capture_frame_info; | ||||
|     float input_volume; | ||||
|  | ||||
|     // mutexes to prevent changes to input resp. output devices and al_devices | ||||
|     // during poll_input iterations resp. calls to write_out; | ||||
|     // mutex[input] also used to lock input_volume which poll_input writes to. | ||||
|     pthread_mutex_t mutex[2]; | ||||
|  | ||||
|     // TODO: unused | ||||
|     const char *default_al_device_name[2];              /* Default devices */ | ||||
|  | ||||
|     const char *al_device_names[2][MAX_OPENAL_DEVICES]; /* Available devices */ | ||||
|     uint32_t num_al_devices[2]; | ||||
|     char *current_al_device_name[2]; | ||||
| } AudioState; | ||||
|  | ||||
| static AudioState *audio_state; | ||||
|  | ||||
| static void lock(DeviceType type) | ||||
| { | ||||
|     pthread_mutex_lock(&audio_state->mutex[type]); | ||||
| } | ||||
|  | ||||
| static void unlock(DeviceType type) | ||||
| { | ||||
|     pthread_mutex_unlock(&audio_state->mutex[type]); | ||||
| } | ||||
|  | ||||
|  | ||||
| static bool thread_running = true, | ||||
|             thread_paused = true;               /* Thread control */ | ||||
|  | ||||
| #ifdef AUDIO | ||||
| static void *poll_input(void *); | ||||
| #endif | ||||
|  | ||||
| static uint32_t sound_mode(bool stereo) | ||||
| { | ||||
|     return stereo ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16; | ||||
| } | ||||
|  | ||||
| static uint32_t sample_size(bool stereo) | ||||
| { | ||||
|     return stereo ? 4 : 2; | ||||
| } | ||||
|  | ||||
| DeviceError init_devices(void) | ||||
| { | ||||
|     audio_state = calloc(1, sizeof(AudioState)); | ||||
|  | ||||
|     if (audio_state == NULL) { | ||||
|         return de_InternalError; | ||||
|     } | ||||
|  | ||||
|     get_al_device_names(); | ||||
|  | ||||
|     for (DeviceType type = input; type <= output; ++type) { | ||||
|         audio_state->al_device[type] = NULL; | ||||
|  | ||||
|         if (pthread_mutex_init(&audio_state->mutex[type], NULL) != 0) { | ||||
|             return de_InternalError; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| #ifdef AUDIO | ||||
|     // Start poll thread | ||||
|     pthread_t thread_id; | ||||
|  | ||||
|     if (pthread_create(&thread_id, NULL, poll_input, NULL) != 0 | ||||
|             || pthread_detach(thread_id) != 0) { | ||||
|         return de_InternalError; | ||||
|     } | ||||
|  | ||||
| #endif | ||||
|  | ||||
|     return de_None; | ||||
| } | ||||
|  | ||||
| DeviceError terminate_devices(void) | ||||
| { | ||||
|     lock(input); | ||||
|     thread_running = false; | ||||
|     unlock(input); | ||||
|  | ||||
|     sleep_thread(20000L); | ||||
|  | ||||
|     for (DeviceType type = input; type <= output; ++type) { | ||||
|         if (pthread_mutex_destroy(&audio_state->mutex[type]) != 0) { | ||||
|             return de_InternalError; | ||||
|         } | ||||
|  | ||||
|         if (audio_state->current_al_device_name[type] != NULL) { | ||||
|             free(audio_state->current_al_device_name[type]); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     free(audio_state); | ||||
|  | ||||
|     return de_None; | ||||
| } | ||||
|  | ||||
| void get_al_device_names(void) | ||||
| { | ||||
|     const char *stringed_device_list; | ||||
|  | ||||
|     for (DeviceType type = input; type <= output; ++type) { | ||||
|         audio_state->num_al_devices[type] = 0; | ||||
|  | ||||
|         if (type == input) { | ||||
|             stringed_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER); | ||||
|         } else { | ||||
|             if (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") != AL_FALSE) { | ||||
|                 stringed_device_list = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER); | ||||
|             } else { | ||||
|                 stringed_device_list = alcGetString(NULL, ALC_DEVICE_SPECIFIER); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (stringed_device_list != NULL) { | ||||
|             audio_state->default_al_device_name[type] = alcGetString(NULL, | ||||
|                     type == input ? ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER : ALC_DEFAULT_DEVICE_SPECIFIER); | ||||
|  | ||||
|             for (; *stringed_device_list != '\0' | ||||
|                     && audio_state->num_al_devices[type] < MAX_OPENAL_DEVICES; ++audio_state->num_al_devices[type]) { | ||||
|                 audio_state->al_device_names[type][audio_state->num_al_devices[type]] = stringed_device_list; | ||||
|                 stringed_device_list += strlen(stringed_device_list) + 1; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| DeviceError device_mute(DeviceType type, uint32_t device_idx) | ||||
| { | ||||
|     if (device_idx >= MAX_DEVICES) { | ||||
|         return de_InvalidSelection; | ||||
|     } | ||||
|  | ||||
|     Device *device = &audio_state->devices[type][device_idx]; | ||||
|  | ||||
|     if (!device->active) { | ||||
|         return de_DeviceNotActive; | ||||
|     } | ||||
|  | ||||
|     lock(type); | ||||
|  | ||||
|     device->muted = !device->muted; | ||||
|  | ||||
|     unlock(type); | ||||
|     return de_None; | ||||
| } | ||||
|  | ||||
| bool device_is_muted(DeviceType type, uint32_t device_idx) | ||||
| { | ||||
|     if (device_idx >= MAX_DEVICES) { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     Device *device = &audio_state->devices[type][device_idx]; | ||||
|  | ||||
|     if (!device->active) { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     return device->muted; | ||||
| } | ||||
|  | ||||
| DeviceError device_set_VAD_threshold(uint32_t device_idx, float value) | ||||
| { | ||||
|     if (device_idx >= MAX_DEVICES) { | ||||
|         return de_InvalidSelection; | ||||
|     } | ||||
|  | ||||
|     Device *device = &audio_state->devices[input][device_idx]; | ||||
|  | ||||
|     if (!device->active) { | ||||
|         return de_DeviceNotActive; | ||||
|     } | ||||
|  | ||||
|     if (value <= 0.0f) { | ||||
|         value = 0.0f; | ||||
|     } | ||||
|  | ||||
|     lock(input); | ||||
|  | ||||
|     device->VAD_threshold = value; | ||||
|  | ||||
|     unlock(input); | ||||
|     return de_None; | ||||
| } | ||||
|  | ||||
| float device_get_VAD_threshold(uint32_t device_idx) | ||||
| { | ||||
|     if (device_idx >= MAX_DEVICES) { | ||||
|         return 0.0; | ||||
|     } | ||||
|  | ||||
|     Device *device = &audio_state->devices[input][device_idx]; | ||||
|  | ||||
|     if (!device->active) { | ||||
|         return 0.0; | ||||
|     } | ||||
|  | ||||
|     return device->VAD_threshold; | ||||
| } | ||||
|  | ||||
| DeviceError set_source_position(uint32_t device_idx, float x, float y, float z) | ||||
| { | ||||
|     if (device_idx >= MAX_DEVICES) { | ||||
|         return de_InvalidSelection; | ||||
|     } | ||||
|  | ||||
|     Device *device = &audio_state->devices[output][device_idx]; | ||||
|  | ||||
|     if (!device->active) { | ||||
|         return de_DeviceNotActive; | ||||
|     } | ||||
|  | ||||
|     lock(output); | ||||
|  | ||||
|     alSource3f(device->source, AL_POSITION, x, y, z); | ||||
|  | ||||
|     unlock(output); | ||||
|  | ||||
|     if (!audio_state->al_device[output] || alcGetError(audio_state->al_device[output]) != AL_NO_ERROR) { | ||||
|         return de_AlError; | ||||
|     } | ||||
|  | ||||
|     return de_None; | ||||
| } | ||||
|  | ||||
| static DeviceError close_al_device(DeviceType type) | ||||
| { | ||||
|     if (audio_state->al_device[type] == NULL) { | ||||
|         return de_None; | ||||
|     } | ||||
|  | ||||
|     if (type == input) { | ||||
|         if (!alcCaptureCloseDevice(audio_state->al_device[type])) { | ||||
|             return de_AlError; | ||||
|         } | ||||
|  | ||||
|         thread_paused = true; | ||||
|     } else { | ||||
|         ALCcontext *context = alcGetCurrentContext(); | ||||
|         alcMakeContextCurrent(NULL); | ||||
|         alcDestroyContext(context); | ||||
|  | ||||
|         if (!alcCloseDevice(audio_state->al_device[type])) { | ||||
|             return de_AlError; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     audio_state->al_device[type] = NULL; | ||||
|  | ||||
|     return de_None; | ||||
| } | ||||
|  | ||||
| static DeviceError open_al_device(DeviceType type, FrameInfo frame_info) | ||||
| { | ||||
|     audio_state->al_device[type] = type == input | ||||
|                                    ? alcCaptureOpenDevice(audio_state->current_al_device_name[type], | ||||
|                                            frame_info.sample_rate, sound_mode(frame_info.stereo), frame_info.samples_per_frame * 2) | ||||
|                                    : alcOpenDevice(audio_state->current_al_device_name[type]); | ||||
|  | ||||
|     if (audio_state->al_device[type] == NULL) { | ||||
|         return de_FailedStart; | ||||
|     } | ||||
|  | ||||
|     if (type == input) { | ||||
|         alcCaptureStart(audio_state->al_device[type]); | ||||
|         thread_paused = false; | ||||
|  | ||||
|         audio_state->capture_frame_info = frame_info; | ||||
|     } else { | ||||
|         alcMakeContextCurrent(alcCreateContext(audio_state->al_device[type], NULL)); | ||||
|     } | ||||
|  | ||||
|     if (alcGetError(audio_state->al_device[type]) != AL_NO_ERROR) { | ||||
|         close_al_device(type); | ||||
|         return de_AlError; | ||||
|     } | ||||
|  | ||||
|     return de_None; | ||||
| } | ||||
|  | ||||
| static void close_source(Device *device) | ||||
| { | ||||
|     if (device->source_open) { | ||||
|         alDeleteSources(1, &device->source); | ||||
|         alDeleteBuffers(OPENAL_BUFS, device->buffers); | ||||
|  | ||||
|         device->source_open = false; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static DeviceError open_source(Device *device) | ||||
| { | ||||
|     alGenBuffers(OPENAL_BUFS, device->buffers); | ||||
|  | ||||
|     if (alcGetError(audio_state->al_device[output]) != AL_NO_ERROR) { | ||||
|         return de_FailedStart; | ||||
|     } | ||||
|  | ||||
|     alGenSources((uint32_t)1, &device->source); | ||||
|  | ||||
|     if (alcGetError(audio_state->al_device[output]) != AL_NO_ERROR) { | ||||
|         alDeleteBuffers(OPENAL_BUFS, device->buffers); | ||||
|         return de_FailedStart; | ||||
|     } | ||||
|  | ||||
|     device->source_open = true; | ||||
|  | ||||
|     alSourcei(device->source, AL_LOOPING, AL_FALSE); | ||||
|  | ||||
|     const uint32_t frame_size = device->frame_info.samples_per_frame * sample_size(device->frame_info.stereo); | ||||
|     size_t zeros_size = frame_size * sizeof(uint16_t); | ||||
|     uint16_t *zeros = calloc(1, zeros_size); | ||||
|  | ||||
|     if (zeros == NULL) { | ||||
|         close_source(device); | ||||
|         return de_FailedStart; | ||||
|     } | ||||
|  | ||||
|     for (int i = 0; i < OPENAL_BUFS; ++i) { | ||||
|         alBufferData(device->buffers[i], sound_mode(device->frame_info.stereo), zeros, | ||||
|                      zeros_size, device->frame_info.sample_rate); | ||||
|     } | ||||
|  | ||||
|     free(zeros); | ||||
|  | ||||
|     alSourceQueueBuffers(device->source, OPENAL_BUFS, device->buffers); | ||||
|     alSourcePlay(device->source); | ||||
|  | ||||
|     if (alcGetError(audio_state->al_device[output]) != AL_NO_ERROR) { | ||||
|         close_source(device); | ||||
|         return de_FailedStart; | ||||
|     } | ||||
|  | ||||
|     return de_None; | ||||
| } | ||||
|  | ||||
| DeviceError set_al_device(DeviceType type, int32_t selection) | ||||
| { | ||||
|     if (audio_state->num_al_devices[type] <= selection || selection < 0) { | ||||
|         return de_InvalidSelection; | ||||
|     } | ||||
|  | ||||
|     const char *name = audio_state->al_device_names[type][selection]; | ||||
|  | ||||
|     char **cur_name = &audio_state->current_al_device_name[type]; | ||||
|  | ||||
|     if (*cur_name != NULL) { | ||||
|         free(*cur_name); | ||||
|     } | ||||
|  | ||||
|     *cur_name = malloc(strlen(name) + 1); | ||||
|  | ||||
|     if (*cur_name == NULL) { | ||||
|         return de_InternalError; | ||||
|     } | ||||
|  | ||||
|     strcpy(*cur_name, name); | ||||
|  | ||||
|     if (audio_state->num_devices[type] > 0) { | ||||
|         // close any existing al_device and try to open new one, reopening existing sources | ||||
|         lock(type); | ||||
|  | ||||
|         if (type == output) { | ||||
|             for (int i = 0; i < MAX_DEVICES; i++) { | ||||
|                 Device *device = &audio_state->devices[type][i]; | ||||
|  | ||||
|                 if (device->active) { | ||||
|                     close_source(device); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         close_al_device(type); | ||||
|  | ||||
|         DeviceError err = open_al_device(type, audio_state->capture_frame_info); | ||||
|  | ||||
|         if (err != de_None) { | ||||
|             unlock(type); | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
|         if (type == output) { | ||||
|             for (int i = 0; i < MAX_DEVICES; i++) { | ||||
|                 Device *device = &audio_state->devices[type][i]; | ||||
|  | ||||
|                 if (device->active) { | ||||
|                     open_source(device); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         unlock(type); | ||||
|     } | ||||
|  | ||||
|     return de_None; | ||||
| } | ||||
|  | ||||
| static DeviceError open_device(DeviceType type, uint32_t *device_idx, | ||||
|                                DataHandleCallback cb, void *cb_data, bool enable_VAD, | ||||
|                                uint32_t sample_rate, uint32_t frame_duration, uint8_t channels) | ||||
| { | ||||
|     if (channels != 1 && channels != 2) { | ||||
|         return de_UnsupportedMode; | ||||
|     } | ||||
|  | ||||
|     const uint32_t samples_per_frame = (sample_rate * frame_duration / 1000); | ||||
|     FrameInfo frame_info = {samples_per_frame, sample_rate, channels == 2}; | ||||
|  | ||||
|     uint32_t i; | ||||
|  | ||||
|     for (i = 0; i < MAX_DEVICES && audio_state->devices[type][i].active; ++i); | ||||
|  | ||||
|     if (i == MAX_DEVICES) { | ||||
|         return de_AllDevicesBusy; | ||||
|     } | ||||
|  | ||||
|     *device_idx = i; | ||||
|  | ||||
|     lock(type); | ||||
|  | ||||
|     if (audio_state->al_device[type] == NULL) { | ||||
|         DeviceError err = open_al_device(type, frame_info); | ||||
|  | ||||
|         if (err != de_None) { | ||||
|             unlock(type); | ||||
|             return err; | ||||
|         } | ||||
|     } else if (type == input) { | ||||
|         // Use previously set frame info on existing capture device | ||||
|         frame_info = audio_state->capture_frame_info; | ||||
|     } | ||||
|  | ||||
|     Device *device = &audio_state->devices[type][i]; | ||||
|     device->active = true; | ||||
|     ++audio_state->num_devices[type]; | ||||
|  | ||||
|     device->muted = false; | ||||
|     device->frame_info = frame_info; | ||||
|  | ||||
|     if (type == input) { | ||||
|         device->cb = cb; | ||||
|         device->cb_data = cb_data; | ||||
| #ifdef AUDIO | ||||
|         device->VAD_threshold = enable_VAD ? user_settings->VAD_threshold : 0.0f; | ||||
| #else | ||||
|         device->VAD_threshold = 0.0f; | ||||
| #endif | ||||
|     } else { | ||||
|         if (open_source(device) != de_None) { | ||||
|             device->active = false; | ||||
|             --audio_state->num_devices[type]; | ||||
|             unlock(type); | ||||
|             return de_FailedStart; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     unlock(type); | ||||
|     return de_None; | ||||
| } | ||||
|  | ||||
| DeviceError open_input_device(uint32_t *device_idx, | ||||
|                               DataHandleCallback cb, void *cb_data, bool enable_VAD, | ||||
|                               uint32_t sample_rate, uint32_t frame_duration, uint8_t channels) | ||||
| { | ||||
|     return open_device(input, device_idx, | ||||
|                        cb, cb_data, enable_VAD, | ||||
|                        sample_rate, frame_duration, channels); | ||||
| } | ||||
|  | ||||
| DeviceError open_output_device(uint32_t *device_idx, | ||||
|                                uint32_t sample_rate, uint32_t frame_duration, uint8_t channels) | ||||
| { | ||||
|     return open_device(output, device_idx, | ||||
|                        0, 0, 0, | ||||
|                        sample_rate, frame_duration, channels); | ||||
| } | ||||
|  | ||||
| DeviceError close_device(DeviceType type, uint32_t device_idx) | ||||
| { | ||||
|     if (device_idx >= MAX_DEVICES) { | ||||
|         return de_InvalidSelection; | ||||
|     } | ||||
|  | ||||
|     lock(type); | ||||
|  | ||||
|     Device *device = &audio_state->devices[type][device_idx]; | ||||
|  | ||||
|     if (!device->active) { | ||||
|         return de_DeviceNotActive; | ||||
|     } | ||||
|  | ||||
|     if (type == output) { | ||||
|         close_source(device); | ||||
|     } | ||||
|  | ||||
|     device->active = false; | ||||
|     --audio_state->num_devices[type]; | ||||
|  | ||||
|     DeviceError err = de_None; | ||||
|  | ||||
|     if (audio_state->num_devices[type] == 0) { | ||||
|         err = close_al_device(type); | ||||
|     } | ||||
|  | ||||
|     unlock(type); | ||||
|     return err; | ||||
| } | ||||
|  | ||||
| DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_t sample_count, uint8_t channels, | ||||
|                       uint32_t sample_rate) | ||||
| { | ||||
|     if (device_idx >= MAX_DEVICES) { | ||||
|         return de_InvalidSelection; | ||||
|     } | ||||
|  | ||||
|     lock(output); | ||||
|  | ||||
|     Device *device = &audio_state->devices[output][device_idx]; | ||||
|  | ||||
|     if (!device->active || device->muted) { | ||||
|         unlock(output); | ||||
|         return de_DeviceNotActive; | ||||
|     } | ||||
|  | ||||
|     ALuint bufid; | ||||
|     ALint processed, queued; | ||||
|     alGetSourcei(device->source, AL_BUFFERS_PROCESSED, &processed); | ||||
|     alGetSourcei(device->source, AL_BUFFERS_QUEUED, &queued); | ||||
|  | ||||
|     if (audio_state->al_device[output] == NULL || alcGetError(audio_state->al_device[output]) != AL_NO_ERROR) { | ||||
|         unlock(output); | ||||
|         return de_AlError; | ||||
|     } | ||||
|  | ||||
|     if (processed) { | ||||
|         ALuint *bufids = malloc(processed * sizeof(ALuint)); | ||||
|  | ||||
|         if (bufids == NULL) { | ||||
|             unlock(output); | ||||
|             return de_InternalError; | ||||
|         } | ||||
|  | ||||
|         alSourceUnqueueBuffers(device->source, processed, bufids); | ||||
|         alDeleteBuffers(processed - 1, bufids + 1); | ||||
|         bufid = bufids[0]; | ||||
|         free(bufids); | ||||
|     } else if (queued < 16) { | ||||
|         alGenBuffers(1, &bufid); | ||||
|     } else { | ||||
|         unlock(output); | ||||
|         return de_Busy; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     const bool stereo = channels == 2; | ||||
|     alBufferData(bufid, sound_mode(stereo), data, | ||||
|                  sample_count * sample_size(stereo), | ||||
|                  sample_rate); | ||||
|     alSourceQueueBuffers(device->source, 1, &bufid); | ||||
|  | ||||
|     ALint state; | ||||
|     alGetSourcei(device->source, AL_SOURCE_STATE, &state); | ||||
|  | ||||
|     if (state != AL_PLAYING) { | ||||
|         alSourcePlay(device->source); | ||||
|     } | ||||
|  | ||||
|     unlock(output); | ||||
|     return de_None; | ||||
| } | ||||
|  | ||||
| #ifdef AUDIO | ||||
| /* Adapted from qtox, | ||||
|  * Copyright © 2014-2019 by The qTox Project Contributors | ||||
|  * | ||||
|  * return normalized volume of buffer in range 0.0-100.0 | ||||
|  */ | ||||
| float volume(int16_t *frame, uint32_t samples) | ||||
| { | ||||
|     float sum_of_squares = 0; | ||||
|  | ||||
|     for (uint32_t i = 0; i < samples; i++) { | ||||
|         const float sample = (float)(frame[i]) / INT16_MAX; | ||||
|         sum_of_squares += powf(sample, 2); | ||||
|     } | ||||
|  | ||||
|     const float root_mean_square = sqrtf(sum_of_squares / samples); | ||||
|     const float root_two = 1.414213562; | ||||
|  | ||||
|     // normalizedVolume == 1.0 corresponds to a sine wave of maximal amplitude | ||||
|     const float normalized_volume = root_mean_square * root_two; | ||||
|  | ||||
|     return 100.0f * fminf(1.0f, normalized_volume); | ||||
| } | ||||
|  | ||||
| // Time in ms for which we continue to capture audio after VAD is triggered: | ||||
| #define VAD_TIME 250 | ||||
|  | ||||
| #define FRAME_BUF_SIZE 16000 | ||||
|  | ||||
| static void *poll_input(void *arg) | ||||
| { | ||||
|     UNUSED_VAR(arg); | ||||
|  | ||||
|     int16_t *frame_buf = malloc(FRAME_BUF_SIZE * sizeof(int16_t)); | ||||
|  | ||||
|     if (frame_buf == NULL) { | ||||
|         exit_toxic_err("failed in thread_poll", FATALERR_MEMORY); | ||||
|     } | ||||
|  | ||||
|     while (1) { | ||||
|         lock(input); | ||||
|  | ||||
|         if (!thread_running) { | ||||
|             free(frame_buf); | ||||
|             unlock(input); | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         if (thread_paused) { | ||||
|             unlock(input); | ||||
|             sleep_thread(10000L); | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         if (audio_state->al_device[input] != NULL) { | ||||
|             int32_t available_samples; | ||||
|             alcGetIntegerv(audio_state->al_device[input], ALC_CAPTURE_SAMPLES, sizeof(int32_t), &available_samples); | ||||
|  | ||||
|             const uint32_t f_size = audio_state->capture_frame_info.samples_per_frame; | ||||
|  | ||||
|             if (available_samples >= f_size && f_size <= FRAME_BUF_SIZE) { | ||||
|                 alcCaptureSamples(audio_state->al_device[input], frame_buf, f_size); | ||||
|  | ||||
|                 unlock(input); | ||||
|                 pthread_mutex_lock(&Winthread.lock); | ||||
|                 lock(input); | ||||
|  | ||||
|                 float frame_volume = volume(frame_buf, f_size); | ||||
|  | ||||
|                 audio_state->input_volume = frame_volume; | ||||
|  | ||||
|                 for (int i = 0; i < MAX_DEVICES; i++) { | ||||
|                     Device *device = &audio_state->devices[input][i]; | ||||
|  | ||||
|                     if (device->VAD_threshold != 0.0f) { | ||||
|                         if (frame_volume >= device->VAD_threshold) { | ||||
|                             device->VAD_samples_remaining = VAD_TIME * (audio_state->capture_frame_info.sample_rate / 1000); | ||||
|                         } else if (device->VAD_samples_remaining < f_size) { | ||||
|                             continue; | ||||
|                         } else { | ||||
|                             device->VAD_samples_remaining -= f_size; | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     if (device->active && !device->muted && device->cb) { | ||||
|                         device->cb(frame_buf, f_size, device->cb_data); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 pthread_mutex_unlock(&Winthread.lock); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         unlock(input); | ||||
|         sleep_thread(5000L); | ||||
|     } | ||||
|  | ||||
|     pthread_exit(NULL); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| float get_input_volume(void) | ||||
| { | ||||
|     float ret = 0.0f; | ||||
|  | ||||
|     if (audio_state->al_device[input] != NULL) { | ||||
|         lock(input); | ||||
|         ret = audio_state->input_volume; | ||||
|         unlock(input); | ||||
|     } | ||||
|  | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| void print_al_devices(ToxWindow *self, DeviceType type) | ||||
| { | ||||
|     for (int i = 0; i < audio_state->num_al_devices[type]; ++i) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, | ||||
|                       audio_state->current_al_device_name[type] | ||||
|                       && strcmp(audio_state->current_al_device_name[type], audio_state->al_device_names[type][i]) == 0 ? 1 : 0, | ||||
|                       0, "%d: %s", i, audio_state->al_device_names[type][i]); | ||||
|     } | ||||
|  | ||||
|     return; | ||||
| } | ||||
|  | ||||
| DeviceError selection_valid(DeviceType type, int32_t selection) | ||||
| { | ||||
|     return (audio_state->num_al_devices[type] <= selection || selection < 0) ? de_InvalidSelection : de_None; | ||||
| } | ||||
							
								
								
									
										97
									
								
								src/audio_device.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								src/audio_device.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,97 @@ | ||||
| /*  audio_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 AUDIO_DEVICE_H | ||||
| #define AUDIO_DEVICE_H | ||||
|  | ||||
| #define OPENAL_BUFS 5 | ||||
| #define MAX_OPENAL_DEVICES 32 | ||||
| #define MAX_DEVICES 32 | ||||
|  | ||||
| #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); | ||||
|  | ||||
|  | ||||
| DeviceError init_devices(void); | ||||
|  | ||||
| void get_al_device_names(void); | ||||
| DeviceError terminate_devices(void); | ||||
|  | ||||
| /* toggle device mute */ | ||||
| DeviceError device_mute(DeviceType type, uint32_t device_idx); | ||||
|  | ||||
| bool device_is_muted(DeviceType type, uint32_t device_idx); | ||||
|  | ||||
| DeviceError device_set_VAD_threshold(uint32_t device_idx, float value); | ||||
|  | ||||
| float device_get_VAD_threshold(uint32_t device_idx); | ||||
|  | ||||
| DeviceError set_source_position(uint32_t device_idx, float x, float y, float z); | ||||
|  | ||||
| DeviceError set_al_device(DeviceType type, int32_t selection); | ||||
|  | ||||
| /* Start device */ | ||||
| DeviceError open_input_device(uint32_t *device_idx, | ||||
|                               DataHandleCallback cb, void *cb_data, bool enable_VAD, | ||||
|                               uint32_t sample_rate, uint32_t frame_duration, uint8_t channels); | ||||
| DeviceError open_output_device(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 output device */ | ||||
| DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_t length, uint8_t channels, | ||||
|                       uint32_t sample_rate); | ||||
|  | ||||
| /* return current input volume as float in range 0.0-100.0 */ | ||||
| float get_input_volume(void); | ||||
|  | ||||
| void print_al_devices(ToxWindow *self, DeviceType type); | ||||
|  | ||||
| DeviceError selection_valid(DeviceType type, int32_t selection); | ||||
| #endif /* AUDIO_DEVICE_H */ | ||||
							
								
								
									
										393
									
								
								src/autocomplete.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										393
									
								
								src/autocomplete.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,393 @@ | ||||
| /*  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 <limits.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
|  | ||||
| #ifdef __APPLE__ | ||||
| #include <sys/types.h> | ||||
| #include <sys/dir.h> | ||||
| #else | ||||
| #include <dirent.h> | ||||
| #endif /* __APPLE__ */ | ||||
|  | ||||
| #include "configdir.h" | ||||
| #include "execute.h" | ||||
| #include "line_info.h" | ||||
| #include "misc_tools.h" | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| static void print_ac_matches(ToxWindow *self, Tox *m, char **list, size_t n_matches) | ||||
| { | ||||
|     if (m) { | ||||
|         execute(self->chatwin->history, self, m, "/clear", GLOBAL_COMMAND_MODE); | ||||
|     } | ||||
|  | ||||
|     for (size_t i = 0; i < n_matches; ++i) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", list[i]); | ||||
|     } | ||||
|  | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, ""); | ||||
| } | ||||
|  | ||||
| /* 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 match. | ||||
|  * | ||||
|  * Returns the length of the match. | ||||
|  */ | ||||
| static size_t get_str_match(ToxWindow *self, char *match, size_t match_sz, const char **matches, size_t n_items, | ||||
|                             size_t max_size) | ||||
| { | ||||
|     UNUSED_VAR(self); | ||||
|  | ||||
|     if (n_items == 1) { | ||||
|         return snprintf(match, match_sz, "%s", matches[0]); | ||||
|     } | ||||
|  | ||||
|     for (size_t i = 0; i < max_size; ++i) { | ||||
|         char ch1 = matches[0][i]; | ||||
|  | ||||
|         for (size_t j = 0; j < n_items; ++j) { | ||||
|             char ch2 = matches[j][i]; | ||||
|  | ||||
|             if (ch1 != ch2 || !ch1) { | ||||
|                 snprintf(match, match_sz, "%s", matches[0]); | ||||
|                 match[i] = '\0'; | ||||
|                 return i; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return snprintf(match, match_sz, "%s", 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 `n_items` strings. Each string in the list must be <= MAX_STR_SIZE. | ||||
|  * | ||||
|  * dir_search should be true if the line being completed is a file path. | ||||
|  * | ||||
|  * Returns the difference between the old len and new len of line on success. | ||||
|  * Returns -1 on error. | ||||
|  * | ||||
|  * Note: This function should not be called directly. Use complete_line() and complete_path() instead. | ||||
|  */ | ||||
| static int complete_line_helper(ToxWindow *self, const char **list, const size_t n_items, bool dir_search) | ||||
| { | ||||
|     ChatContext *ctx = self->chatwin; | ||||
|  | ||||
|     if (ctx->pos <= 0 || ctx->len <= 0 || ctx->pos > ctx->len) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if (ctx->len >= MAX_STR_SIZE) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     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; | ||||
|     } | ||||
|  | ||||
|     /* isolate substring from space behind pos to pos */ | ||||
|     char tmp[MAX_STR_SIZE]; | ||||
|     memcpy(tmp, ubuf, ctx->pos); | ||||
|     tmp[ctx->pos] = 0; | ||||
|  | ||||
|     const char *s = dir_search ? strchr(tmp, ' ') : strrchr(tmp, ' '); | ||||
|     char *sub = calloc(1, strlen(ubuf) + 1); | ||||
|  | ||||
|     if (sub == NULL) { | ||||
|         exit_toxic_err("failed in complete_line_helper", 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] && !(dir_search && n_items == 1)) { | ||||
|         free(sub); | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     int s_len = strlen(sub); | ||||
|     size_t n_matches = 0; | ||||
|  | ||||
|     char **matches = (char **) malloc_ptr_array(n_items, MAX_STR_SIZE); | ||||
|  | ||||
|     if (matches == NULL) { | ||||
|         free(sub); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     /* put all list matches in matches array */ | ||||
|     for (size_t i = 0; i < n_items; ++i) { | ||||
|         if (strncasecmp(list[i], sub, s_len) == 0) { | ||||
|             snprintf(matches[n_matches++], MAX_STR_SIZE, "%s", list[i]); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     free(sub); | ||||
|  | ||||
|     if (!n_matches) { | ||||
|         free_ptr_array((void **) matches); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if (!dir_search && n_matches > 1) { | ||||
|         print_ac_matches(self, NULL, matches, n_matches); | ||||
|     } | ||||
|  | ||||
|     char match[MAX_STR_SIZE]; | ||||
|     size_t match_len = get_str_match(self, match, sizeof(match), (const char **) matches, n_matches, MAX_STR_SIZE); | ||||
|  | ||||
|     free_ptr_array((void **) matches); | ||||
|  | ||||
|     if (match_len == 0) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     if (dir_search) { | ||||
|         if (n_matches == 1) { | ||||
|             endchrs = char_rfind(match, '.', match_len) ? "" : "/"; | ||||
|         } else { | ||||
|             endchrs = ""; | ||||
|         } | ||||
|     } else if (n_matches > 1) { | ||||
|         endchrs = ""; | ||||
|     } | ||||
|  | ||||
|     /* put match in correct spot in buf and append endchars */ | ||||
|     int n_endchrs = strlen(endchrs); | ||||
|     int strt = ctx->pos - s_len; | ||||
|     int diff = match_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]); | ||||
|  | ||||
|     if (match_len + n_endchrs + strlen(tmpend) >= sizeof(ubuf)) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     strcpy(&ubuf[strt], match); | ||||
|  | ||||
|     /* If path points to a file with no extension don't append a forward slash */ | ||||
|     if (dir_search && *endchrs == '/') { | ||||
|         const char *path_start = strchr(ubuf + 1, '/'); | ||||
|  | ||||
|         if (!path_start) { | ||||
|             path_start = strchr(ubuf + 1, ' '); | ||||
|  | ||||
|             if (!path_start) { | ||||
|                 return -1; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (strlen(path_start) < 2) { | ||||
|             return -1; | ||||
|         } | ||||
|  | ||||
|         ++path_start; | ||||
|  | ||||
|         if (file_type(path_start) == FILE_TYPE_REGULAR) { | ||||
|             endchrs = ""; | ||||
|             diff -= n_endchrs; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     strcpy(&ubuf[strt + match_len], endchrs); | ||||
|     strcpy(&ubuf[strt + match_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; | ||||
| } | ||||
|  | ||||
| int complete_line(ToxWindow *self, const char **list, size_t n_items) | ||||
| { | ||||
|     return complete_line_helper(self, list, n_items, false); | ||||
| } | ||||
|  | ||||
| static int complete_path(ToxWindow *self, const char **list, const size_t n_items) | ||||
| { | ||||
|     return complete_line_helper(self, list, n_items, true); | ||||
| } | ||||
|  | ||||
| /* Transforms a tab complete starting with the shorthand "~" into the full home directory. */ | ||||
| static void complete_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 + 1]; | ||||
|     snprintf(newline, sizeof(newline), "%s %s%s", cmd, homedir, path + 1); | ||||
|     snprintf(path, pathsize, "%s", &newline[cmdlen - 1]); | ||||
|  | ||||
|     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; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Return true if the first `p_len` chars in `s` are equal to `p` and `s` is a valid directory name. | ||||
|  */ | ||||
| static bool is_partial_match(const char *s, const char *p, size_t p_len) | ||||
| { | ||||
|     if (s == NULL || p == NULL) { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     return strncmp(s, p, p_len) == 0 && strcmp(".", s) != 0 && strcmp("..", s) != 0; | ||||
| } | ||||
|  | ||||
| /* Attempts to match /command "<incomplete-dir>" line to matching directories. | ||||
|  * If there is only one match the line is auto-completed. | ||||
|  * | ||||
|  * Returns the diff between old len and new len of ctx->line on success. | ||||
|  * Returns -1 if no matches or more than one match. | ||||
|  */ | ||||
| #define MAX_DIRS 75 | ||||
| int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd) | ||||
| { | ||||
|     char b_path[MAX_STR_SIZE + 1]; | ||||
|     char b_name[MAX_STR_SIZE + 1]; | ||||
|     char b_cmd[MAX_STR_SIZE]; | ||||
|     const wchar_t *tmpline = &line[wcslen(cmd) + 1];   /* start after "/command " */ | ||||
|  | ||||
|     if (wcs_to_mbs_buf(b_path, tmpline, sizeof(b_path) - 1) == -1) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if (wcs_to_mbs_buf(b_cmd, cmd, sizeof(b_cmd)) == -1) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if (b_path[0] == '~') { | ||||
|         complete_home_dir(self, b_path, sizeof(b_path) - 1, 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 */ | ||||
|         memmove(b_path + 1, b_path, sizeof(b_path) - 1); | ||||
|         b_path[0] = '.'; | ||||
|     } | ||||
|  | ||||
|     snprintf(b_name, sizeof(b_name), "%s", &b_path[si + 1]); | ||||
|     b_path[si + 1] = '\0'; | ||||
|     size_t b_name_len = strlen(b_name); | ||||
|     DIR *dp = opendir(b_path); | ||||
|  | ||||
|     if (dp == NULL) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     char **dirnames = (char **) malloc_ptr_array(MAX_DIRS, NAME_MAX + 1); | ||||
|  | ||||
|     if (dirnames == NULL) { | ||||
|         closedir(dp); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     struct dirent *entry; | ||||
|  | ||||
|     int dircount = 0; | ||||
|  | ||||
|     while ((entry = readdir(dp)) && dircount < MAX_DIRS) { | ||||
|         if (is_partial_match(entry->d_name, b_name, b_name_len)) { | ||||
|             snprintf(dirnames[dircount], NAME_MAX + 1, "%s", entry->d_name); | ||||
|             ++dircount; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     closedir(dp); | ||||
|  | ||||
|     if (dircount == 0) { | ||||
|         free_ptr_array((void **) dirnames); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if (dircount > 1) { | ||||
|         qsort(dirnames, dircount, sizeof(char *), qsort_ptr_char_array_helper); | ||||
|         print_ac_matches(self, m, dirnames, dircount); | ||||
|     } | ||||
|  | ||||
|     int ret = complete_path(self, (const char **) dirnames, dircount); | ||||
|  | ||||
|     free_ptr_array((void **) dirnames); | ||||
|  | ||||
|     return ret; | ||||
| } | ||||
							
								
								
									
										52
									
								
								src/autocomplete.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/autocomplete.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| /*  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 | ||||
|  | ||||
| #include "windows.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 `n_items` strings. | ||||
|  * | ||||
|  * dir_search should be true if the line being completed is a file path. | ||||
|  * | ||||
|  * Returns the difference between the old len and new len of line on success. | ||||
|  * Returns -1 on error. | ||||
|  * | ||||
|  * Note: This function should not be called directly. Use complete_line() and complete_path() instead. | ||||
|  */ | ||||
| int complete_line(ToxWindow *self, const char **list, size_t n_items); | ||||
|  | ||||
| /* Attempts to match /command "<incomplete-dir>" line to matching directories. | ||||
|  * If there is only one match the line is auto-completed. | ||||
|  * | ||||
|  * Returns the diff between old len and new len of ctx->line on success. | ||||
|  * Returns -1 if no matches or more than one match. | ||||
|  */ | ||||
| int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd); | ||||
|  | ||||
| #endif /* AUTOCOMPLETE_H */ | ||||
							
								
								
									
										262
									
								
								src/avatars.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										262
									
								
								src/avatars.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,262 @@ | ||||
| /*  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 <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
|  | ||||
| #include "avatars.h" | ||||
| #include "file_transfers.h" | ||||
| #include "friendlist.h" | ||||
| #include "misc_tools.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; | ||||
|  | ||||
| /* Compares the first size bytes of fp to signature. | ||||
|  * | ||||
|  * Returns 0 if they are the same | ||||
|  * Returns 1 if they differ | ||||
|  * Returns -1 on error. | ||||
|  * | ||||
|  * On success this function will seek back to the beginning of fp. | ||||
|  */ | ||||
| static int check_file_signature(const unsigned char *signature, size_t size, FILE *fp) | ||||
| { | ||||
|     char *buf = malloc(size); | ||||
|  | ||||
|     if (buf == NULL) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if (fread(buf, size, 1, fp) != 1) { | ||||
|         free(buf); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     int ret = memcmp(signature, buf, size); | ||||
|  | ||||
|     free(buf); | ||||
|  | ||||
|     if (fseek(fp, 0L, SEEK_SET) == -1) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     return ret == 0 ? 0 : 1; | ||||
| } | ||||
|  | ||||
| static void avatar_clear(void) | ||||
| { | ||||
|     Avatar = (struct Avatar) { | ||||
|         0 | ||||
|     }; | ||||
| } | ||||
|  | ||||
| /* Sends avatar to friendnumber. | ||||
|  * | ||||
|  * Returns 0 on success. | ||||
|  * Returns -1 on failure. | ||||
|  */ | ||||
| int avatar_send(Tox *m, uint32_t friendnumber) | ||||
| { | ||||
|     Tox_Err_File_Send err; | ||||
|     uint32_t filenumber = tox_file_send(m, friendnumber, 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 %u (error %d)\n", friendnumber, err); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     struct FileTransfer *ft = new_file_transfer(NULL, friendnumber, filenumber, 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) | ||||
| { | ||||
|     for (size_t 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; | ||||
|     } | ||||
|  | ||||
|     unsigned 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_friend_connection_status(Tox *m, uint32_t friendnumber, Tox_Connection connection_status) | ||||
| { | ||||
|     if (connection_status == TOX_CONNECTION_NONE) { | ||||
|         kill_avatar_file_transfers_friend(m, friendnumber); | ||||
|     } | ||||
| } | ||||
|  | ||||
| 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 = malloc(length); | ||||
|  | ||||
|     if (send_data == NULL) { | ||||
|         close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     size_t send_length = fread(send_data, 1, length, ft->file); | ||||
|  | ||||
|     if (send_length != length) { | ||||
|         close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent); | ||||
|         free(send_data); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     Tox_Err_File_Send_Chunk err; | ||||
|     tox_file_send_chunk(m, ft->friendnumber, ft->filenumber, position, send_data, send_length, &err); | ||||
|  | ||||
|     free(send_data); | ||||
|  | ||||
|     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; | ||||
| } | ||||
							
								
								
									
										55
									
								
								src/avatars.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/avatars.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | ||||
| /*  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 | ||||
|  | ||||
| #include "file_transfers.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); | ||||
| void on_avatar_friend_connection_status(Tox *m, uint32_t friendnumber, Tox_Connection connection_status); | ||||
|  | ||||
| #endif /* AVATARS_H */ | ||||
							
								
								
									
										635
									
								
								src/bootstrap.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										635
									
								
								src/bootstrap.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,635 @@ | ||||
| /*  bootstrap.c | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2016 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 <arpa/inet.h> | ||||
| #include <limits.h> | ||||
| #include <netinet/in.h> | ||||
| #include <stdbool.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <sys/socket.h> | ||||
|  | ||||
| #include <curl/curl.h> | ||||
| #include <tox/tox.h> | ||||
|  | ||||
| #include "configdir.h" | ||||
| #include "curl_util.h" | ||||
| #include "line_info.h" | ||||
| #include "misc_tools.h" | ||||
| #include "prompt.h" | ||||
| #include "settings.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| extern struct arg_opts arg_opts; | ||||
| extern struct user_settings *user_settings; | ||||
| extern struct Winthread Winthread; | ||||
|  | ||||
| /* URL that we get the JSON encoded nodes list from. */ | ||||
| #define NODES_LIST_URL "https://nodes.tox.chat/json" | ||||
|  | ||||
| #define DEFAULT_NODES_FILENAME "DHTnodes.json" | ||||
|  | ||||
| /* Time to wait between bootstrap attempts */ | ||||
| #define TRY_BOOTSTRAP_INTERVAL 5 | ||||
|  | ||||
| /* Number of nodes to bootstrap to per try */ | ||||
| #define NUM_BOOTSTRAP_NODES 5 | ||||
|  | ||||
| /* Number of seconds since last successful ping before we consider a node offline */ | ||||
| #define NODE_OFFLINE_TIMOUT (60*60*24*2) | ||||
|  | ||||
| #define IP_MAX_SIZE 45 | ||||
| #define IP_MIN_SIZE 7 | ||||
| #define PORT_MAX_SIZE 5 | ||||
|  | ||||
| #define LAST_SCAN_JSON_KEY "\"last_scan\":" | ||||
| #define LAST_SCAN_JSON_KEY_LEN (sizeof(LAST_SCAN_JSON_KEY) - 1) | ||||
|  | ||||
| #define IPV4_JSON_KEY "\"ipv4\":\"" | ||||
| #define IPV4_JSON_KEY_LEN (sizeof(IPV4_JSON_KEY) - 1) | ||||
|  | ||||
| #define IPV6_JSON_KEY "\"ipv6\":\"" | ||||
| #define IPV6_JSON_KEY_LEN (sizeof(IPV6_JSON_KEY) - 1) | ||||
|  | ||||
| #define PORT_JSON_KEY "\"port\":" | ||||
| #define PORT_JSON_KEY_LEN (sizeof(PORT_JSON_KEY) - 1) | ||||
|  | ||||
| #define PK_JSON_KEY "\"public_key\":\"" | ||||
| #define PK_JSON_KEY_LEN (sizeof(PK_JSON_KEY) - 1) | ||||
|  | ||||
| #define LAST_PING_JSON_KEY "\"last_ping\":" | ||||
| #define LAST_PING_JSON_KEY_LEN (sizeof(LAST_PING_JSON_KEY) - 1) | ||||
|  | ||||
| /* Maximum allowable size of the nodes list */ | ||||
| #define MAX_NODELIST_SIZE (MAX_RECV_CURL_DATA_SIZE) | ||||
|  | ||||
|  | ||||
| static struct Thread_Data { | ||||
|     pthread_t tid; | ||||
|     pthread_attr_t attr; | ||||
|     pthread_mutex_t lock; | ||||
|     volatile bool active; | ||||
| } thread_data; | ||||
|  | ||||
| #define MAX_NODES 50 | ||||
| struct Node { | ||||
|     char ip4[IP_MAX_SIZE + 1]; | ||||
|     bool have_ip4; | ||||
|  | ||||
|     char ip6[IP_MAX_SIZE + 1]; | ||||
|     bool have_ip6; | ||||
|  | ||||
|     char key[TOX_PUBLIC_KEY_SIZE]; | ||||
|     uint16_t port; | ||||
| }; | ||||
|  | ||||
| static struct DHT_Nodes { | ||||
|     struct Node list[MAX_NODES]; | ||||
|     size_t count; | ||||
|     time_t last_updated; | ||||
| } Nodes; | ||||
|  | ||||
| /* Return true if address appears to be a valid ipv4 address. */ | ||||
| static bool is_ip4_address(const char *address) | ||||
| { | ||||
|     struct sockaddr_in s_addr; | ||||
|     return inet_pton(AF_INET, address, &(s_addr.sin_addr)) != 0; | ||||
| } | ||||
|  | ||||
| /* Return true if address roughly appears to be a valid ipv6 address. | ||||
|  * | ||||
|  * TODO: Improve this function (inet_pton behaves strangely with ipv6). | ||||
|  * for now the only guarantee is that it won't return true if the | ||||
|  * address is a domain or ipv4 address, and should only be used if you're | ||||
|  * reasonably sure that the address is one of the three (ipv4, ipv6 or a domain). | ||||
|  */ | ||||
| static bool is_ip6_address(const char *address) | ||||
| { | ||||
|     size_t num_colons = 0; | ||||
|     char ch = 0; | ||||
|  | ||||
|     for (size_t i = 0; (ch = address[i]); ++i) { | ||||
|         if (ch == '.') { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         if (ch == ':') { | ||||
|             ++num_colons; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return num_colons > 1 && num_colons < 8; | ||||
| } | ||||
|  | ||||
| /* Determine if a node is offline by comparing the age of the nodeslist | ||||
|  * to the last time the node was successfully pinged. | ||||
|  */ | ||||
| static bool node_is_offline(unsigned long long int last_ping) | ||||
| { | ||||
|     return last_ping + NODE_OFFLINE_TIMOUT <= last_ping; | ||||
| } | ||||
|  | ||||
| /* Return true if nodeslist pointed to by fp needs to be updated. | ||||
|  * This will be the case if the file is empty, has an invalid format, | ||||
|  * or if the file is older than the given timeout. | ||||
|  */ | ||||
| static bool nodeslist_needs_update(const char *nodes_path) | ||||
| { | ||||
|     if (user_settings->nodeslist_update_freq <= 0) { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     FILE *fp = fopen(nodes_path, "r+"); | ||||
|  | ||||
|     if (fp == NULL) { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     /* last_scan value should be at beginning of file */ | ||||
|     char line[LAST_SCAN_JSON_KEY_LEN + 32]; | ||||
|  | ||||
|     if (fgets(line, sizeof(line), fp) == NULL) { | ||||
|         fclose(fp); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     fclose(fp); | ||||
|  | ||||
|     const char *last_scan_val = strstr(line, LAST_SCAN_JSON_KEY); | ||||
|  | ||||
|     if (last_scan_val == NULL) { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     long long int last_scan = strtoll(last_scan_val + LAST_SCAN_JSON_KEY_LEN, NULL, 10); | ||||
|  | ||||
|     pthread_mutex_lock(&thread_data.lock); | ||||
|     Nodes.last_updated = last_scan; | ||||
|     pthread_mutex_unlock(&thread_data.lock); | ||||
|  | ||||
|     pthread_mutex_lock(&Winthread.lock); | ||||
|     bool is_timeout = timed_out(last_scan, user_settings->nodeslist_update_freq * 24 * 60 * 60); | ||||
|     pthread_mutex_unlock(&Winthread.lock); | ||||
|  | ||||
|     if (is_timeout) { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| /* Fetches the JSON encoded DHT nodeslist from NODES_LIST_URL. | ||||
|  * | ||||
|  * Return 0 on success. | ||||
|  * Return -1 on failure. | ||||
|  */ | ||||
| static int curl_fetch_nodes_JSON(struct Recv_Curl_Data *recv_data) | ||||
| { | ||||
|     CURL *c_handle = curl_easy_init(); | ||||
|  | ||||
|     if (c_handle == NULL) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     int err = -1; | ||||
|  | ||||
|     struct curl_slist *headers = NULL; | ||||
|     headers = curl_slist_append(headers, "Content-Type: application/json"); | ||||
|     headers = curl_slist_append(headers, "charsets: utf-8"); | ||||
|  | ||||
|     curl_easy_setopt(c_handle, CURLOPT_HTTPHEADER, headers); | ||||
|     curl_easy_setopt(c_handle, CURLOPT_URL, NODES_LIST_URL); | ||||
|     curl_easy_setopt(c_handle, CURLOPT_WRITEFUNCTION, curl_cb_write_data); | ||||
|     curl_easy_setopt(c_handle, CURLOPT_WRITEDATA, recv_data); | ||||
|     curl_easy_setopt(c_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); | ||||
|     curl_easy_setopt(c_handle, CURLOPT_HTTPGET, 1L); | ||||
|  | ||||
|     int proxy_ret = set_curl_proxy(c_handle, arg_opts.proxy_address, arg_opts.proxy_port, arg_opts.proxy_type); | ||||
|  | ||||
|     if (proxy_ret != 0) { | ||||
|         fprintf(stderr, "set_curl_proxy() failed with error %d\n", proxy_ret); | ||||
|         goto on_exit; | ||||
|     } | ||||
|  | ||||
|     int ret = curl_easy_setopt(c_handle, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); | ||||
|  | ||||
|     if (ret != CURLE_OK) { | ||||
|         fprintf(stderr, "TLSv1.2 could not be set (libcurl error %d)", ret); | ||||
|         goto on_exit; | ||||
|     } | ||||
|  | ||||
|     ret = curl_easy_setopt(c_handle, CURLOPT_SSL_CIPHER_LIST, TLS_CIPHER_SUITE_LIST); | ||||
|  | ||||
|     if (ret != CURLE_OK) { | ||||
|         fprintf(stderr, "Failed to set TLS cipher list (libcurl error %d)", ret); | ||||
|         goto on_exit; | ||||
|     } | ||||
|  | ||||
|     ret = curl_easy_perform(c_handle); | ||||
|  | ||||
|     if (ret != CURLE_OK) { | ||||
|         /* If system doesn't support any of the specified ciphers suites, fall back to default */ | ||||
|         if (ret == CURLE_SSL_CIPHER) { | ||||
|             curl_easy_setopt(c_handle, CURLOPT_SSL_CIPHER_LIST, NULL); | ||||
|             ret = curl_easy_perform(c_handle); | ||||
|         } | ||||
|  | ||||
|         if (ret != CURLE_OK) { | ||||
|             fprintf(stderr, "HTTPS lookup error (libcurl error %d)\n", ret); | ||||
|             goto on_exit; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     err = 0; | ||||
|  | ||||
| on_exit: | ||||
|     curl_slist_free_all(headers); | ||||
|     curl_easy_cleanup(c_handle); | ||||
|     return err; | ||||
| } | ||||
|  | ||||
| /* Attempts to update the DHT nodeslist. | ||||
|  * | ||||
|  * Return 1 if list was updated successfully. | ||||
|  * Return 0 if list does not need to be updated. | ||||
|  * Return -1 if file cannot be opened. | ||||
|  * Return -2 if http lookup failed. | ||||
|  * Return -3 if http reponse was empty. | ||||
|  * Return -4 if data could not be written to disk. | ||||
|  * Return -5 if memory allocation fails. | ||||
|  */ | ||||
| static int update_DHT_nodeslist(const char *nodes_path) | ||||
| { | ||||
|     if (!nodeslist_needs_update(nodes_path)) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     FILE *fp = fopen(nodes_path, "r+"); | ||||
|  | ||||
|     if (fp == NULL) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     struct Recv_Curl_Data *recv_data = calloc(1, sizeof(struct Recv_Curl_Data)); | ||||
|  | ||||
|     if (recv_data == NULL) { | ||||
|         fclose(fp); | ||||
|         return -5; | ||||
|     } | ||||
|  | ||||
|     if (curl_fetch_nodes_JSON(recv_data) == -1) { | ||||
|         free(recv_data); | ||||
|         fclose(fp); | ||||
|         return -2; | ||||
|     } | ||||
|  | ||||
|     if (recv_data->length == 0) { | ||||
|         free(recv_data); | ||||
|         fclose(fp); | ||||
|         return -3; | ||||
|     } | ||||
|  | ||||
|     if (fwrite(recv_data->data, recv_data->length, 1, fp) != 1) { | ||||
|         free(recv_data); | ||||
|         fclose(fp); | ||||
|         return -4; | ||||
|     } | ||||
|  | ||||
|     free(recv_data); | ||||
|     fclose(fp); | ||||
|  | ||||
|     return 1; | ||||
| } | ||||
|  | ||||
| static void get_nodeslist_path(char *buf, size_t buf_size) | ||||
| { | ||||
|     char *config_dir = NULL; | ||||
|  | ||||
|     if (arg_opts.nodes_path[0]) { | ||||
|         snprintf(buf, buf_size, "%s", arg_opts.nodes_path); | ||||
|     } else if ((config_dir = get_user_config_dir()) != NULL) { | ||||
|         snprintf(buf, buf_size, "%s%s%s", config_dir, CONFIGDIR, DEFAULT_NODES_FILENAME); | ||||
|         free(config_dir); | ||||
|     } else { | ||||
|         snprintf(buf, buf_size, "%s", DEFAULT_NODES_FILENAME); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* Return true if json encoded string s contains a valid IP address and puts address in ip_buf. | ||||
|  * | ||||
|  * ip_type should be set to 1 for ipv4 address, or 0 for ipv6 addresses. | ||||
|  * ip_buf must have room for at least IP_MAX_SIZE + 1 bytes. | ||||
|  */ | ||||
| static bool extract_val_ip(const char *s, char *ip_buf, unsigned short int ip_type) | ||||
| { | ||||
|     int ip_len = char_find(0, s, '"'); | ||||
|  | ||||
|     if (ip_len < IP_MIN_SIZE || ip_len > IP_MAX_SIZE) { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     memcpy(ip_buf, s, ip_len); | ||||
|     ip_buf[ip_len] = 0; | ||||
|  | ||||
|     return (ip_type == 1) ? is_ip4_address(ip_buf) : is_ip6_address(ip_buf); | ||||
| } | ||||
|  | ||||
| /* Extracts the port from json encoded string s. | ||||
|  * | ||||
|  * Return port number on success. | ||||
|  * Return 0 on failure. | ||||
|  */ | ||||
| static uint16_t extract_val_port(const char *s) | ||||
| { | ||||
|     long int port = strtol(s, NULL, 10); | ||||
|     return (port > 0 && port <= MAX_PORT_RANGE) ? port : 0; | ||||
| } | ||||
|  | ||||
| /* Extracts the last pinged value from json encoded string s. | ||||
|  * | ||||
|  * Return timestamp on success. | ||||
|  * Return -1 on failure. | ||||
|  */ | ||||
| static long long int extract_val_last_pinged(const char *s) | ||||
| { | ||||
|     long long int last_pinged = strtoll(s, NULL, 10); | ||||
|     return (last_pinged <= 0) ? -1 : last_pinged; | ||||
| } | ||||
|  | ||||
| /* Extracts DHT public key from json encoded string s and puts key in key_buf. | ||||
|  * key_buf must have room for at least TOX_PUBLIC_KEY_SIZE * 2 + 1 bytes. | ||||
|  * | ||||
|  * Return number of bytes copied to key_buf on success. | ||||
|  * Return -1 on failure. | ||||
|  */ | ||||
| static int extract_val_pk(const char *s, char *key_buf) | ||||
| { | ||||
|  | ||||
|     int key_len = char_find(0, s, '"'); | ||||
|  | ||||
|     if (key_len != TOX_PUBLIC_KEY_SIZE * 2) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     memcpy(key_buf, s, key_len); | ||||
|     key_buf[key_len] = 0; | ||||
|  | ||||
|     return key_len; | ||||
| } | ||||
|  | ||||
| /* Extracts values from json formatted string, validats them, and puts them in node. | ||||
|  * | ||||
|  * Return 0 on success. | ||||
|  * Return -1 if line is empty. | ||||
|  * Return -2 if line does not appear to be a valid nodes list entry. | ||||
|  * Return -3 if node appears to be offline. | ||||
|  * Return -4 if entry does not contain either a valid ipv4 or ipv6 address. | ||||
|  * Return -5 if port value is invalid. | ||||
|  * Return -6 if public key is invalid. | ||||
|  */ | ||||
| static int extract_node(const char *line, struct Node *node) | ||||
| { | ||||
|     if (!line) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     const char *ip4_start = strstr(line, IPV4_JSON_KEY); | ||||
|     const char *ip6_start = strstr(line, IPV6_JSON_KEY); | ||||
|     const char *port_start = strstr(line, PORT_JSON_KEY); | ||||
|     const char *key_start = strstr(line, PK_JSON_KEY); | ||||
|     const char *last_pinged_str = strstr(line, LAST_PING_JSON_KEY); | ||||
|  | ||||
|     if (!ip4_start || !ip6_start || !port_start || !key_start || !last_pinged_str) { | ||||
|         return -2; | ||||
|     } | ||||
|  | ||||
|     long long int last_pinged = extract_val_last_pinged(last_pinged_str + LAST_PING_JSON_KEY_LEN); | ||||
|  | ||||
|     if (last_pinged <= 0 || node_is_offline(last_pinged)) { | ||||
|         return -3; | ||||
|     } | ||||
|  | ||||
|     char ip4_string[IP_MAX_SIZE + 1]; | ||||
|     bool have_ip4 = extract_val_ip(ip4_start + IPV4_JSON_KEY_LEN, ip4_string, 1); | ||||
|  | ||||
|     char ip6_string[IP_MAX_SIZE + 1]; | ||||
|     bool have_ip6 = extract_val_ip(ip6_start + IPV6_JSON_KEY_LEN, ip6_string, 0); | ||||
|  | ||||
|     if (!have_ip6 && !have_ip4) { | ||||
|         return -4; | ||||
|     } | ||||
|  | ||||
|     uint16_t port = extract_val_port(port_start + PORT_JSON_KEY_LEN); | ||||
|  | ||||
|     if (port == 0) { | ||||
|         return -5; | ||||
|     } | ||||
|  | ||||
|     char key_string[TOX_PUBLIC_KEY_SIZE * 2 + 1]; | ||||
|     int key_len = extract_val_pk(key_start + PK_JSON_KEY_LEN, key_string); | ||||
|  | ||||
|     if (key_len == -1) { | ||||
|         return -6; | ||||
|     } | ||||
|  | ||||
|     if (hex_string_to_bin(key_string, key_len, node->key, TOX_PUBLIC_KEY_SIZE) == -1) { | ||||
|         return -6; | ||||
|     } | ||||
|  | ||||
|     if (have_ip4) { | ||||
|         snprintf(node->ip4, sizeof(node->ip4), "%s", ip4_string); | ||||
|         node->have_ip4 = true; | ||||
|     } | ||||
|  | ||||
|     if (have_ip6) { | ||||
|         snprintf(node->ip6, sizeof(node->ip6), "%s", ip6_string); | ||||
|         node->have_ip6 = true; | ||||
|     } | ||||
|  | ||||
|     node->port = port; | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /* Loads the DHT nodeslist to memory from json encoded nodes file. */ | ||||
| void *load_nodeslist_thread(void *data) | ||||
| { | ||||
|     UNUSED_VAR(data); | ||||
|  | ||||
|     char nodes_path[PATH_MAX]; | ||||
|     get_nodeslist_path(nodes_path, sizeof(nodes_path)); | ||||
|  | ||||
|     FILE *fp = NULL; | ||||
|  | ||||
|     if (!file_exists(nodes_path)) { | ||||
|         if ((fp = fopen(nodes_path, "w+")) == NULL) { | ||||
|             fprintf(stderr, "nodeslist load error: failed to create file '%s'\n", nodes_path); | ||||
|             goto on_exit; | ||||
|         } | ||||
|     } else if ((fp = fopen(nodes_path, "r+")) == NULL) { | ||||
|         fprintf(stderr, "nodeslist load error: failed to open file '%s'\n", nodes_path); | ||||
|         goto on_exit; | ||||
|     } | ||||
|  | ||||
|     int update_err = update_DHT_nodeslist(nodes_path); | ||||
|  | ||||
|     if (update_err < 0) { | ||||
|         fprintf(stderr, "update_DHT_nodeslist() failed with error %d\n", update_err); | ||||
|     } | ||||
|  | ||||
|     char line[MAX_NODELIST_SIZE + 1]; | ||||
|  | ||||
|     if (fgets(line, sizeof(line), fp) == NULL) { | ||||
|         fclose(fp); | ||||
|         fprintf(stderr, "nodeslist load error: file empty.\n"); | ||||
|         goto on_exit; | ||||
|     } | ||||
|  | ||||
|     size_t idx = 0; | ||||
|     const char *line_start = line; | ||||
|  | ||||
|     while ((line_start = strstr(line_start + 1, IPV4_JSON_KEY))) { | ||||
|         pthread_mutex_lock(&thread_data.lock); | ||||
|         idx = Nodes.count; | ||||
|  | ||||
|         if (idx >= MAX_NODES) { | ||||
|             pthread_mutex_unlock(&thread_data.lock); | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         if (extract_node(line_start, &Nodes.list[idx]) == 0) { | ||||
|             ++Nodes.count; | ||||
|         } | ||||
|  | ||||
|         pthread_mutex_unlock(&thread_data.lock); | ||||
|     } | ||||
|  | ||||
|     /* If nodeslist does not contain any valid entries we set the last_scan value | ||||
|      * to 0 so that it will fetch a new list the next time this function is called. | ||||
|      */ | ||||
|     if (Nodes.count == 0) { | ||||
|         const char *s = "{\"last_scan\":0}"; | ||||
|         rewind(fp); | ||||
|         fwrite(s, strlen(s), 1, fp);  // Not much we can do if it fails | ||||
|         fclose(fp); | ||||
|         fprintf(stderr, "nodeslist load error: List did not contain any valid entries.\n"); | ||||
|         goto on_exit; | ||||
|     } | ||||
|  | ||||
|     fclose(fp); | ||||
|  | ||||
| on_exit: | ||||
|     thread_data.active = false; | ||||
|     pthread_attr_destroy(&thread_data.attr); | ||||
|     pthread_exit(0); | ||||
| } | ||||
|  | ||||
| /* Creates a new thread that will load the DHT nodeslist to memory | ||||
|  * from json encoded nodes file obtained at NODES_LIST_URL. Only one | ||||
|  * thread may run at a time. | ||||
|  * | ||||
|  * Return 0 on success. | ||||
|  * Return -1 if a thread is already active. | ||||
|  * Return -2 if mutex fails to init. | ||||
|  * Return -3 if pthread attribute fails to init. | ||||
|  * Return -4 if pthread fails to set detached state. | ||||
|  * Return -5 if thread creation fails. | ||||
|  */ | ||||
| int load_DHT_nodeslist(void) | ||||
| { | ||||
|     if (thread_data.active) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if (pthread_mutex_init(&thread_data.lock, NULL) != 0) { | ||||
|         return -2; | ||||
|     } | ||||
|  | ||||
|     if (pthread_attr_init(&thread_data.attr) != 0) { | ||||
|         return -3; | ||||
|     } | ||||
|  | ||||
|     if (pthread_attr_setdetachstate(&thread_data.attr, PTHREAD_CREATE_DETACHED) != 0) { | ||||
|         return -4; | ||||
|     } | ||||
|  | ||||
|     thread_data.active = true; | ||||
|  | ||||
|     if (pthread_create(&thread_data.tid, &thread_data.attr, load_nodeslist_thread, NULL) != 0) { | ||||
|         thread_data.active = false; | ||||
|         return -5; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /* Connects to NUM_BOOTSTRAP_NODES random DHT nodes listed in the DHTnodes file. */ | ||||
| static void DHT_bootstrap(Tox *m) | ||||
| { | ||||
|     pthread_mutex_lock(&thread_data.lock); | ||||
|     size_t num_nodes = Nodes.count; | ||||
|     pthread_mutex_unlock(&thread_data.lock); | ||||
|  | ||||
|     if (num_nodes == 0) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     size_t i; | ||||
|  | ||||
|     pthread_mutex_lock(&thread_data.lock); | ||||
|  | ||||
|     for (i = 0; i < NUM_BOOTSTRAP_NODES; ++i) { | ||||
|         struct Node *node = &Nodes.list[rand() % Nodes.count]; | ||||
|         const char *addr = node->have_ip4 ? node->ip4 : node->ip6; | ||||
|  | ||||
|         if (!addr) { | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         Tox_Err_Bootstrap err; | ||||
|         tox_bootstrap(m, addr, node->port, (uint8_t *) node->key, &err); | ||||
|  | ||||
|         if (err != TOX_ERR_BOOTSTRAP_OK) { | ||||
|             fprintf(stderr, "Failed to bootstrap %s:%d\n", addr, node->port); | ||||
|         } | ||||
|  | ||||
|         tox_add_tcp_relay(m, addr, node->port, (uint8_t *) node->key, &err); | ||||
|  | ||||
|         if (err != TOX_ERR_BOOTSTRAP_OK) { | ||||
|             fprintf(stderr, "Failed to add TCP relay %s:%d\n", addr, node->port); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pthread_mutex_unlock(&thread_data.lock); | ||||
| } | ||||
|  | ||||
| /* Manages connection to the Tox DHT network. */ | ||||
| void do_tox_connection(Tox *m) | ||||
| { | ||||
|     static time_t last_bootstrap_time = 0; | ||||
|     bool connected = prompt_selfConnectionStatus() != TOX_CONNECTION_NONE; | ||||
|  | ||||
|     if (!connected && timed_out(last_bootstrap_time, TRY_BOOTSTRAP_INTERVAL)) { | ||||
|         DHT_bootstrap(m); | ||||
|         last_bootstrap_time = get_unix_time(); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										42
									
								
								src/bootstrap.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/bootstrap.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| /*  bootstrap.h | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2016 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 BOOTSTRAP_H | ||||
| #define BOOTSTRAP_H | ||||
|  | ||||
| /* Manages connection to the Tox DHT network. */ | ||||
| void do_tox_connection(Tox *m); | ||||
|  | ||||
| /* Creates a new thread that will load the DHT nodeslist to memory | ||||
|  * from json encoded nodes file obtained at NODES_LIST_URL. Only one | ||||
|  * thread may run at a time. | ||||
|  * | ||||
|  * Return 0 on success. | ||||
|  * Return -1 if a thread is already active. | ||||
|  * Return -2 if mutex fails to init. | ||||
|  * Return -3 if pthread attribute fails to init. | ||||
|  * Return -4 if pthread fails to set detached state. | ||||
|  * Return -5 if thread creation fails. | ||||
|  */ | ||||
| int load_DHT_nodeslist(void); | ||||
|  | ||||
| #endif /* BOOTSTRAP_H */ | ||||
							
								
								
									
										1790
									
								
								src/chat.c
									
									
									
									
									
								
							
							
						
						
									
										1790
									
								
								src/chat.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										37
									
								
								src/chat.h
									
									
									
									
									
								
							
							
						
						
									
										37
									
								
								src/chat.h
									
									
									
									
									
								
							| @@ -1,8 +1,35 @@ | ||||
| #ifndef CHAT_H_6489PZ13 | ||||
| #define CHAT_H_6489PZ13 | ||||
| /*  chat.h | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||
|  * | ||||
|  *  This file is part of Toxic. | ||||
|  * | ||||
|  *  Toxic is free software: you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation, either version 3 of the License, or | ||||
|  *  (at your option) any later version. | ||||
|  * | ||||
|  *  Toxic is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "toxic_windows.h" | ||||
| #ifndef CHAT_H | ||||
| #define CHAT_H | ||||
|  | ||||
| ToxWindow new_chat(Tox *m, int friendnum); | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| #endif /* end of include guard: CHAT_H_6489PZ13 */ | ||||
| /* set CTRL to -1 if we don't want to send a control signal. | ||||
|    set msg to NULL if we don't want to display a message */ | ||||
| void chat_close_file_receiver(Tox *m, int filenum, int friendnum, int CTRL); | ||||
| void kill_chat_window(ToxWindow *self, Tox *m); | ||||
| ToxWindow *new_chat(Tox *m, int32_t friendnum); | ||||
|  | ||||
| #endif /* end of include guard: CHAT_H */ | ||||
|   | ||||
| @@ -1,198 +1,344 @@ | ||||
| /* | ||||
|  * Toxic -- Tox Curses Client | ||||
| /*  chat_commands.c | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||
|  * | ||||
|  *  This file is part of Toxic. | ||||
|  * | ||||
|  *  Toxic is free software: you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation, either version 3 of the License, or | ||||
|  *  (at your option) any later version. | ||||
|  * | ||||
|  *  Toxic is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #ifdef HAVE_CONFIG_H | ||||
| #include "config.h" | ||||
| #endif | ||||
|  | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
|  | ||||
| #include "toxic_windows.h" | ||||
| #include "misc_tools.h" | ||||
| #include "chat.h" | ||||
| #include "conference.h" | ||||
| #include "execute.h" | ||||
| #include "file_transfers.h" | ||||
| #include "friendlist.h" | ||||
| #include "line_info.h" | ||||
| #include "misc_tools.h" | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| extern ToxWindow *prompt; | ||||
| extern FriendsList Friends; | ||||
|  | ||||
| extern ToxicFriend friends[MAX_FRIENDS_NUM]; | ||||
|  | ||||
| extern FileSender file_senders[MAX_FILES]; | ||||
| extern uint8_t max_file_senders_index; | ||||
|  | ||||
| void cmd_chat_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     wattron(window, COLOR_PAIR(CYAN) | A_BOLD); | ||||
|     wprintw(window, "Chat commands:\n"); | ||||
|     wattroff(window, COLOR_PAIR(CYAN) | A_BOLD); | ||||
|     UNUSED_VAR(window); | ||||
|  | ||||
|     wprintw(window, "      /status <type> <msg>       : Set your status with optional note\n"); | ||||
|     wprintw(window, "      /note <msg>                : Set a personal note\n"); | ||||
|     wprintw(window, "      /nick <nick>               : Set your nickname\n"); | ||||
|     wprintw(window, "      /invite <n>                : Invite friend to a group chat\n"); | ||||
|     wprintw(window, "      /me <action>               : Do an action\n"); | ||||
|     wprintw(window, "      /myid                      : Print your ID\n"); | ||||
|     wprintw(window, "      /join                      : Join a pending group chat\n"); | ||||
|     wprintw(window, "      /clear                     : Clear the screen\n"); | ||||
|     wprintw(window, "      /close                     : Close the current chat window\n"); | ||||
|     wprintw(window, "      /sendfile <filepath>       : Send a file\n"); | ||||
|     wprintw(window, "      /savefile <n>              : Receive a file\n"); | ||||
|     wprintw(window, "      /quit or /exit             : Exit Toxic\n"); | ||||
|     wprintw(window, "      /help                      : Print this message again\n"); | ||||
|      | ||||
|     wattron(window, COLOR_PAIR(CYAN) | A_BOLD); | ||||
|     wprintw(window, " * Argument messages must be enclosed in quotation marks.\n\n"); | ||||
|     wattroff(window, COLOR_PAIR(CYAN) | A_BOLD); | ||||
|     if (argc < 2) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Requires type in|out and the file ID."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     char msg[MAX_STR_SIZE]; | ||||
|     const char *inoutstr = argv[1]; | ||||
|     long int idx = strtol(argv[2], NULL, 10); | ||||
|  | ||||
|     if ((idx == 0 && strcmp(argv[2], "0")) || idx >= MAX_FILES || idx < 0) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     struct FileTransfer *ft = NULL; | ||||
|  | ||||
|     /* cancel an incoming file transfer */ | ||||
|     if (strcasecmp(inoutstr, "in") == 0) { | ||||
|         ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_RECV); | ||||
|     } else if (strcasecmp(inoutstr, "out") == 0) { | ||||
|         ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_SEND); | ||||
|     } else { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Type must be 'in' or 'out'."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (!ft) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (ft->state == FILE_TRANSFER_INACTIVE) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     snprintf(msg, sizeof(msg), "File transfer for '%s' aborted.", ft->file_name); | ||||
|     close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, silent); | ||||
| } | ||||
|  | ||||
| void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| void cmd_conference_invite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|  | ||||
|     if (argc < 1) { | ||||
|       wprintw(window, "Invalid syntax.\n"); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     int groupnum = atoi(argv[1]); | ||||
|  | ||||
|     if (groupnum == 0 && strcmp(argv[1], "0")) {    /* atoi returns 0 value on invalid input */ | ||||
|         wprintw(window, "Invalid syntax.\n"); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference number required."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (tox_invite_friend(m, self->num, groupnum) == -1) { | ||||
|         wprintw(window, "Failed to invite friend.\n"); | ||||
|     long int conferencenum = strtol(argv[1], NULL, 10); | ||||
|  | ||||
|     if ((conferencenum == 0 && strcmp(argv[1], "0")) || conferencenum < 0 || conferencenum == LONG_MAX) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid conference number."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     wprintw(window, "Invited friend to group chat %d.\n", groupnum); | ||||
|     Tox_Err_Conference_Invite err; | ||||
|  | ||||
|     if (!tox_conference_invite(m, self->num, conferencenum, &err)) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to invite contact to conference (error %d)", err); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invited contact to Conference %ld.", conferencenum); | ||||
| } | ||||
|  | ||||
| void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| void cmd_conference_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     if (num_active_windows() >= MAX_WINDOWS_NUM) { | ||||
|         wattron(window, COLOR_PAIR(RED)); | ||||
|         wprintw(window, " * Warning: Too many windows are open.\n"); | ||||
|         wattron(window, COLOR_PAIR(RED)); | ||||
|     UNUSED_VAR(window); | ||||
|     UNUSED_VAR(argc); | ||||
|     UNUSED_VAR(argv); | ||||
|  | ||||
|     if (get_num_active_windows() >= MAX_WINDOWS_NUM) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     uint8_t *groupkey = friends[self->num].pending_groupchat; | ||||
|     const char *conferencekey = Friends.list[self->num].conference_invite.key; | ||||
|     uint16_t length = Friends.list[self->num].conference_invite.length; | ||||
|     uint8_t type = Friends.list[self->num].conference_invite.type; | ||||
|  | ||||
|     if (groupkey[0] == '\0') { | ||||
|         wprintw(window, "No pending group chat invite.\n"); | ||||
|     if (!Friends.list[self->num].conference_invite.pending) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending conference invite."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     int groupnum = tox_join_groupchat(m, self->num, groupkey); | ||||
|     uint32_t conferencenum; | ||||
|  | ||||
|     if (groupnum == -1) { | ||||
|         wprintw(window, "Group chat instance failed to initialize.\n"); | ||||
|     if (type == TOX_CONFERENCE_TYPE_TEXT) { | ||||
|         Tox_Err_Conference_Join err; | ||||
|         conferencenum = tox_conference_join(m, self->num, (const uint8_t *) conferencekey, length, &err); | ||||
|  | ||||
|         if (err != TOX_ERR_CONFERENCE_JOIN_OK) { | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference instance failed to initialize (error %d)", err); | ||||
|             return; | ||||
|         } | ||||
|     } else if (type == TOX_CONFERENCE_TYPE_AV) { | ||||
| #ifdef AUDIO | ||||
|         conferencenum = toxav_join_av_groupchat(m, self->num, (const uint8_t *) conferencekey, length, | ||||
|                                                 audio_conference_callback, NULL); | ||||
|  | ||||
|         if (conferencenum == (uint32_t) -1) { | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio conference instance failed to initialize"); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
| #else | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio support disabled by compile-time option."); | ||||
|         return; | ||||
| #endif | ||||
|     } else { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Unknown conference type %d", type); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (init_groupchat_win(prompt, m, groupnum) == -1) { | ||||
|         wprintw(window, "Group chat window failed to initialize.\n"); | ||||
|         tox_del_groupchat(m, groupnum); | ||||
|     if (init_conference_win(m, conferencenum, type, NULL, 0) == -1) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference window failed to initialize."); | ||||
|         tox_conference_delete(m, conferencenum, NULL); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| #ifdef AUDIO | ||||
|  | ||||
|     if (type == TOX_CONFERENCE_TYPE_AV) { | ||||
|         if (!init_conference_audio_input(m, conferencenum)) { | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio capture failed; use \"/audio on\" to try again."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| #endif | ||||
| } | ||||
|  | ||||
| void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     if (argc != 1) { | ||||
|       wprintw(window, "Invalid syntax.\n"); | ||||
|       return; | ||||
|     } | ||||
|     UNUSED_VAR(window); | ||||
|  | ||||
|     uint8_t filenum = atoi(argv[1]); | ||||
|  | ||||
|     if ((filenum == 0 && strcmp(argv[1], "0")) || filenum >= MAX_FILES) { | ||||
|         wprintw(window, "No pending file transfers with that number.\n"); | ||||
|     if (argc < 1) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File ID required."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (!friends[self->num].file_receiver.pending[filenum]) { | ||||
|         wprintw(window, "No pending file transfers with that number.\n"); | ||||
|     long int idx = strtol(argv[1], NULL, 10); | ||||
|  | ||||
|     if ((idx == 0 && strcmp(argv[1], "0")) || idx < 0 || idx >= MAX_FILES) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     uint8_t *filename = friends[self->num].file_receiver.filenames[filenum]; | ||||
|     struct FileTransfer *ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_RECV); | ||||
|  | ||||
|     if (tox_file_send_control(m, self->num, 1, filenum, TOX_FILECONTROL_ACCEPT, 0, 0) == 0) | ||||
|         wprintw(window, "Accepted file transfer %u. Saving file as: '%s'\n", filenum, filename); | ||||
|     else | ||||
|         wprintw(window, "File transfer failed.\n"); | ||||
|     if (!ft) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     friends[self->num].file_receiver.pending[filenum] = false; | ||||
|     if (ft->state != FILE_TRANSFER_PENDING) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if ((ft->file = fopen(ft->file_path, "a")) == NULL) { | ||||
|         const char *msg =  "File transfer failed: Invalid download 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->filenumber, TOX_FILE_CONTROL_RESUME, &err); | ||||
|  | ||||
|     if (err != TOX_ERR_FILE_CONTROL_OK) { | ||||
|         goto on_recv_error; | ||||
|     } | ||||
|  | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Saving file [%ld] as: '%s'", idx, ft->file_path); | ||||
|  | ||||
|     /* prep progress bar line */ | ||||
|     char progline[MAX_STR_SIZE]; | ||||
|     init_progress_bar(progline); | ||||
|     line_info_add(self, false, 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, false, 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, false, 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, false, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Invalid filenumber."); | ||||
|             return; | ||||
|  | ||||
|         case TOX_ERR_FILE_CONTROL_SENDQ: | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Connection error."); | ||||
|             return; | ||||
|  | ||||
|         default: | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed (error %d)\n", err); | ||||
|             return; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     if (max_file_senders_index >= (MAX_FILES-1)) { | ||||
|         wprintw(window,"Please wait for some of your outgoing file transfers to complete.\n"); | ||||
|         return; | ||||
|     } | ||||
|     UNUSED_VAR(window); | ||||
|  | ||||
|     const char *errmsg = NULL; | ||||
|  | ||||
|     if (argc < 1) { | ||||
|       wprintw(window, "Invalid syntax.\n"); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     uint8_t *path = argv[1]; | ||||
|  | ||||
|     if (path[0] != '\"') { | ||||
|         wprintw(window, "File path must be enclosed in quotes.\n"); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File path required."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     path[strlen(++path)-1] = L'\0'; | ||||
|     char path[MAX_STR_SIZE]; | ||||
|     snprintf(path, sizeof(path), "%s", argv[1]); | ||||
|     int path_len = strlen(path); | ||||
|  | ||||
|     if (path_len > MAX_STR_SIZE) { | ||||
|         wprintw(window, "File path exceeds character limit.\n"); | ||||
|     if (path_len >= MAX_STR_SIZE) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File path exceeds character limit."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     FILE *file_to_send = fopen(path, "r"); | ||||
|  | ||||
|     if (file_to_send == NULL) { | ||||
|         wprintw(window, "File '%s' not found.\n", path); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File not found."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     fseek(file_to_send, 0, SEEK_END); | ||||
|     uint64_t filesize = ftell(file_to_send); | ||||
|     fseek(file_to_send, 0, SEEK_SET); | ||||
|     off_t filesize = file_size(path); | ||||
|  | ||||
|     uint8_t filename[MAX_STR_SIZE]; | ||||
|     get_file_name(path, filename); | ||||
|     int filenum = tox_new_file_sender(m, self->num, filesize, filename, strlen(filename) + 1); | ||||
|  | ||||
|     if (filenum == -1) { | ||||
|         wprintw(window, "Error sending file.\n"); | ||||
|     if (filesize == 0) { | ||||
|         line_info_add(self, false, 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 = (uint8_t) filenum; | ||||
|             file_senders[i].friendnum = self->num; | ||||
|             file_senders[i].timestamp = (uint64_t) time(NULL); | ||||
|             file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1, | ||||
|                                              tox_file_data_size(m, self->num), file_to_send); | ||||
|     Tox_Err_File_Send err; | ||||
|     uint32_t filenum = tox_file_send(m, self->num, TOX_FILE_KIND_DATA, (uint64_t) filesize, NULL, | ||||
|                                      (uint8_t *) file_name, namelen, &err); | ||||
|  | ||||
|             wprintw(window, "Sending file: '%s'\n", path); | ||||
|     if (err != TOX_ERR_FILE_SEND_OK) { | ||||
|         goto on_send_error; | ||||
|     } | ||||
|  | ||||
|             if (i == max_file_senders_index) | ||||
|                 ++max_file_senders_index; | ||||
|     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, false, 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, false, NULL, NULL, SYS_MSG, 0, 0, "%s", errmsg); | ||||
|     tox_file_control(m, self->num, filenum, TOX_FILE_CONTROL_CANCEL, NULL); | ||||
|     fclose(file_to_send); | ||||
| } | ||||
|   | ||||
| @@ -1,9 +1,53 @@ | ||||
| /* | ||||
|  * Toxic -- Tox Curses Client | ||||
| /*  chat_commands.h | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||
|  * | ||||
|  *  This file is part of Toxic. | ||||
|  * | ||||
|  *  Toxic is free software: you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation, either version 3 of the License, or | ||||
|  *  (at your option) any later version. | ||||
|  * | ||||
|  *  Toxic is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| void cmd_chat_help(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| 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]); | ||||
| #ifndef CHAT_COMMANDS_H | ||||
| #define CHAT_COMMANDS_H | ||||
|  | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_conference_invite(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_conference_join(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_savefile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_sendfile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
|  | ||||
| #ifdef AUDIO | ||||
| void cmd_call(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_answer(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_reject(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_hangup(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_cancel(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_ccur_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_mute(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_sense(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_bitrate(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| #endif /* AUDIO */ | ||||
|  | ||||
| #ifdef VIDEO | ||||
| void cmd_vcall(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_video(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_res(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| #endif /* VIDEO */ | ||||
|  | ||||
| #endif /* CHAT_COMMANDS_H */ | ||||
|   | ||||
							
								
								
									
										1418
									
								
								src/conference.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1418
									
								
								src/conference.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										118
									
								
								src/conference.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								src/conference.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,118 @@ | ||||
| /*  conference.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 CONFERENCE_H | ||||
| #define CONFERENCE_H | ||||
|  | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| #define CONFERENCE_MAX_TITLE_LENGTH TOX_MAX_NAME_LENGTH | ||||
| #define SIDEBAR_WIDTH 16 | ||||
|  | ||||
| typedef struct ConferencePeer { | ||||
|     bool       active; | ||||
|  | ||||
|     uint8_t    pubkey[TOX_PUBLIC_KEY_SIZE]; | ||||
|     uint32_t   peernum;    /* index in chat->peer_list */ | ||||
|  | ||||
|     char       name[TOX_MAX_NAME_LENGTH]; | ||||
|     size_t     name_length; | ||||
|  | ||||
|     bool       sending_audio; | ||||
|     uint32_t   audio_out_idx; | ||||
|     time_t     last_audio_time; | ||||
| } ConferencePeer; | ||||
|  | ||||
| typedef struct AudioInputCallbackData { | ||||
|     Tox *tox; | ||||
|     uint32_t conferencenum; | ||||
| } AudioInputCallbackData; | ||||
|  | ||||
| #define PUBKEY_STRING_SIZE (2 * TOX_PUBLIC_KEY_SIZE + 1) | ||||
| typedef struct NameListEntry { | ||||
|     char name[TOX_MAX_NAME_LENGTH]; | ||||
|     char pubkey_str[PUBKEY_STRING_SIZE]; | ||||
|     uint32_t peernum; | ||||
| } NameListEntry; | ||||
|  | ||||
|  | ||||
| typedef struct { | ||||
|     int chatwin; | ||||
|     bool active; | ||||
|     uint8_t type; | ||||
|     int side_pos;    /* current position of the sidebar - used for scrolling up and down */ | ||||
|     time_t start_time; | ||||
|  | ||||
|     char title[CONFERENCE_MAX_TITLE_LENGTH + 1]; | ||||
|     size_t title_length; | ||||
|  | ||||
|     ConferencePeer *peer_list; | ||||
|     uint32_t max_idx; | ||||
|  | ||||
|     NameListEntry *name_list; | ||||
|     uint32_t num_peers; | ||||
|  | ||||
|     bool push_to_talk_enabled; | ||||
|     time_t ptt_last_pushed; | ||||
|  | ||||
|     bool audio_enabled; | ||||
|     time_t last_sent_audio; | ||||
|     uint32_t audio_in_idx; | ||||
|     AudioInputCallbackData audio_input_callback_data; | ||||
| } ConferenceChat; | ||||
|  | ||||
| /* Frees all Toxic associated data structures for a conference (does not call tox_conference_delete() ) */ | ||||
| void free_conference(ToxWindow *self, uint32_t conferencenum); | ||||
|  | ||||
| int init_conference_win(Tox *m, uint32_t conferencenum, uint8_t type, const char *title, size_t length); | ||||
|  | ||||
| /* destroys and re-creates conference window with or without the peerlist */ | ||||
| void redraw_conference_win(ToxWindow *self); | ||||
|  | ||||
| void conference_set_title(ToxWindow *self, uint32_t conferencesnum, const char *title, size_t length); | ||||
| void conference_rename_log_path(Tox *m, uint32_t conferencenum, const char *new_title); | ||||
| int conference_enable_logging(ToxWindow *self, Tox *m, uint32_t conferencenum, struct chatlog *log); | ||||
|  | ||||
| /* Puts `(NameListEntry *)`s in `entries` for each matched peer, up to a maximum | ||||
|  * of `maxpeers`. | ||||
|  * Maches each peer whose name or pubkey begins with `prefix`. | ||||
|  * If `prefix` is exactly the pubkey of a peer, matches only that peer. | ||||
|  * return number of entries placed in `entries`. | ||||
|  */ | ||||
| uint32_t get_name_list_entries_by_prefix(uint32_t conferencenum, const char *prefix, NameListEntry **entries, | ||||
|         uint32_t maxpeers); | ||||
|  | ||||
| bool init_conference_audio_input(Tox *tox, uint32_t conferencenum); | ||||
| bool enable_conference_audio(Tox *tox, uint32_t conferencenum); | ||||
| bool disable_conference_audio(Tox *tox, uint32_t conferencenum); | ||||
| bool toggle_conference_push_to_talk(uint32_t conferencenum, bool enabled); | ||||
| void audio_conference_callback(void *tox, uint32_t conferencenum, uint32_t peernum, | ||||
|                                const int16_t *pcm, unsigned int samples, uint8_t channels, uint32_t | ||||
|                                sample_rate, void *userdata); | ||||
|  | ||||
| bool conference_mute_self(uint32_t conferencenum); | ||||
| bool conference_mute_peer(const Tox *m, uint32_t conferencenum, uint32_t peernum); | ||||
| bool conference_set_VAD_threshold(uint32_t conferencenum, float threshold); | ||||
| float conference_get_VAD_threshold(uint32_t conferencenum); | ||||
|  | ||||
| #endif /* CONFERENCE_H */ | ||||
							
								
								
									
										210
									
								
								src/conference_commands.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										210
									
								
								src/conference_commands.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,210 @@ | ||||
| /*  conference_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 <stdlib.h> | ||||
| #include <string.h> | ||||
|  | ||||
| #include "conference.h" | ||||
| #include "line_info.h" | ||||
| #include "log.h" | ||||
| #include "misc_tools.h" | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| static void print_err(ToxWindow *self, const char *error_str) | ||||
| { | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", error_str); | ||||
| } | ||||
|  | ||||
| void cmd_conference_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|  | ||||
|     Tox_Err_Conference_Title err; | ||||
|     char title[CONFERENCE_MAX_TITLE_LENGTH + 1]; | ||||
|  | ||||
|     if (argc < 1) { | ||||
|         size_t tlen = tox_conference_get_title_size(m, self->num, &err); | ||||
|  | ||||
|         if (err != TOX_ERR_CONFERENCE_TITLE_OK || tlen >= sizeof(title)) { | ||||
|             print_err(self, "Title is not set"); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (!tox_conference_get_title(m, self->num, (uint8_t *) title, &err)) { | ||||
|             print_err(self, "Title is not set"); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         title[tlen] = '\0'; | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Title is set to: %s", title); | ||||
|  | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     size_t len = strlen(argv[1]); | ||||
|  | ||||
|     if (len >= sizeof(title)) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to set title: max length exceeded."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     snprintf(title, sizeof(title), "%s", argv[1]); | ||||
|  | ||||
|     if (!tox_conference_set_title(m, self->num, (uint8_t *) title, len, &err)) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to set title (error %d)", err); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     conference_rename_log_path(m, self->num, title);  // must be called first | ||||
|  | ||||
|     conference_set_title(self, self->num, title, len); | ||||
|  | ||||
|     char selfnick[TOX_MAX_NAME_LENGTH]; | ||||
|     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, true, selfnick, NULL, NAME_CHANGE, 0, 0, " set the conference title to: %s", title); | ||||
|  | ||||
|     char tmp_event[MAX_STR_SIZE + 20]; | ||||
|     snprintf(tmp_event, sizeof(tmp_event), "set title to %s", title); | ||||
|     write_to_log(tmp_event, selfnick, self->chatwin->log, true); | ||||
| } | ||||
|  | ||||
| #ifdef AUDIO | ||||
| void cmd_enable_audio(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|  | ||||
|     bool enable; | ||||
|  | ||||
|     if (argc == 1 && !strcasecmp(argv[1], "on")) { | ||||
|         enable = true; | ||||
|     } else if (argc == 1 && !strcasecmp(argv[1], "off")) { | ||||
|         enable = false; | ||||
|     } else { | ||||
|         print_err(self, "Please specify: on | off"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (enable ? enable_conference_audio(m, self->num) : disable_conference_audio(m, self->num)) { | ||||
|         print_err(self, enable ? "Enabled conference audio. Use the '/ptt' command to toggle Push-To-Talk." | ||||
|                   : "Disabled conference audio"); | ||||
|     } else { | ||||
|         print_err(self, enable ? "Failed to enable audio" : "Failed to disable audio"); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void cmd_conference_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|  | ||||
|     if (argc < 1) { | ||||
|         if (conference_mute_self(self->num)) { | ||||
|             print_err(self, "Toggled self audio mute status"); | ||||
|         } else { | ||||
|             print_err(self, "No audio input to mute"); | ||||
|         } | ||||
|     } else { | ||||
|         NameListEntry *entries[16]; | ||||
|         uint32_t n = get_name_list_entries_by_prefix(self->num, argv[1], entries, 16); | ||||
|  | ||||
|         if (n == 0) { | ||||
|             print_err(self, "No such peer"); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (n > 1) { | ||||
|             print_err(self, "Multiple matching peers (use /mute [public key] to disambiguate):"); | ||||
|  | ||||
|             for (uint32_t i = 0; i < n; ++i) { | ||||
|                 line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s: %s", entries[i]->pubkey_str, entries[i]->name); | ||||
|             } | ||||
|  | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (conference_mute_peer(m, self->num, entries[0]->peernum)) { | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Toggled audio mute status of %s", entries[0]->name); | ||||
|         } else { | ||||
|             print_err(self, "Peer is not on the call"); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void cmd_conference_sense(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|     UNUSED_VAR(m); | ||||
|  | ||||
|     if (argc == 0) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Current VAD threshold: %.1f", | ||||
|                       (double) conference_get_VAD_threshold(self->num)); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (argc > 1) { | ||||
|         print_err(self, "Only one argument allowed."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     char *end; | ||||
|     float value = strtof(argv[1], &end); | ||||
|  | ||||
|     if (*end) { | ||||
|         print_err(self, "Invalid input"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (conference_set_VAD_threshold(self->num, value)) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Set VAD threshold to %.1f", (double) value); | ||||
|     } else { | ||||
|         print_err(self, "Failed to set conference audio input sensitivity."); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void cmd_conference_push_to_talk(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|     UNUSED_VAR(m); | ||||
|  | ||||
|     bool enable; | ||||
|  | ||||
|     if (argc == 1 && !strcasecmp(argv[1], "on")) { | ||||
|         enable = true; | ||||
|     } else if (argc == 1 && !strcasecmp(argv[1], "off")) { | ||||
|         enable = false; | ||||
|     } else { | ||||
|         print_err(self, "Please specify: on | off"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (!toggle_conference_push_to_talk(self->num, enable)) { | ||||
|         print_err(self, "Failed to toggle push to talk."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     print_err(self, enable ? "Push-To-Talk is enabled. Push F2 to activate" : "Push-To-Talk is disabled"); | ||||
| } | ||||
| #endif /* AUDIO */ | ||||
							
								
								
									
										35
									
								
								src/conference_commands.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/conference_commands.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| /*  conference_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 CONFERENCE_COMMANDS_H | ||||
| #define CONFERENCE_COMMANDS_H | ||||
|  | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| void cmd_conference_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_enable_audio(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_conference_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_conference_sense(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_conference_push_to_talk(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
|  | ||||
| #endif /* CONFERENCE_COMMANDS_H */ | ||||
							
								
								
									
										177
									
								
								src/configdir.c
									
									
									
									
									
								
							
							
						
						
									
										177
									
								
								src/configdir.c
									
									
									
									
									
								
							| @@ -1,100 +1,78 @@ | ||||
| /* | ||||
|  *  Copyright (C) 2013 Tox project All Rights Reserved. | ||||
| /*  configdir.c | ||||
|  * | ||||
|  *  This file is part of Tox. | ||||
|  * | ||||
|  *  Tox is free software: you can redistribute it and/or modify | ||||
|  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||
|  * | ||||
|  *  This file is part of Toxic. | ||||
|  * | ||||
|  *  Toxic is free software: you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation, either version 3 of the License, or | ||||
|  *  (at your option) any later version. | ||||
|  * | ||||
|  *  Tox is distributed in the hope that it will be useful, | ||||
|  *  Toxic is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with Tox.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #ifdef HAVE_CONFIG_H | ||||
| #include "config.h" | ||||
| #endif | ||||
|  | ||||
| #include <string.h> | ||||
| #include <stdlib.h> | ||||
| #include <stdio.h> | ||||
| #include <sys/types.h> | ||||
| #include <sys/stat.h> | ||||
| #include <errno.h> | ||||
|  | ||||
| #ifdef _WIN32 | ||||
| #include <shlobj.h> | ||||
| #include <direct.h> | ||||
| #else /* WIN32 */ | ||||
| #include <unistd.h> | ||||
| #include <pwd.h> | ||||
| #endif /* WIN32 */ | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <sys/stat.h> | ||||
| #include <sys/types.h> | ||||
| #include <unistd.h> | ||||
|  | ||||
| #include "configdir.h" | ||||
| #include "misc_tools.h" | ||||
| #include "toxic.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 *user_config_dir; | ||||
| #ifdef _WIN32 | ||||
| #warning Please fix configdir for Win32 | ||||
|     return NULL; | ||||
| #if 0 | ||||
|     char appdata[MAX_PATH]; | ||||
|     BOOL ok; | ||||
|     char home[NSS_BUFLEN_PASSWD] = {0}; | ||||
|     get_home_dir(home, sizeof(home)); | ||||
|  | ||||
|     ok = SHGetSpecialFolderPathA(NULL, appdata, CSIDL_PROFILE, TRUE); | ||||
|  | ||||
|     if (!ok) { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     user_config_dir = strdup(appdata); | ||||
|  | ||||
|     return user_config_dir; | ||||
| #endif | ||||
|  | ||||
| #else /* WIN32 */ | ||||
|  | ||||
| #ifndef NSS_BUFLEN_PASSWD | ||||
| #define NSS_BUFLEN_PASSWD 4096 | ||||
| #endif /* NSS_BUFLEN_PASSWD */ | ||||
|  | ||||
|     struct passwd pwd; | ||||
|     struct passwd *pwdbuf; | ||||
|     const char *home; | ||||
|     char buf[NSS_BUFLEN_PASSWD]; | ||||
|     size_t len; | ||||
|     int rc; | ||||
|  | ||||
|     rc = getpwuid_r(getuid(), &pwd, buf, NSS_BUFLEN_PASSWD, &pwdbuf); | ||||
|  | ||||
|     if (rc == 0) { | ||||
|         home = pwd.pw_dir; | ||||
|     } else { | ||||
|         home = getenv("HOME"); | ||||
|  | ||||
|         if (home == NULL) { | ||||
|             return NULL; | ||||
|         } | ||||
|  | ||||
|         /* env variables can be tainted */ | ||||
|         snprintf(buf, sizeof(buf), "%s", home); | ||||
|         home = buf; | ||||
|     } | ||||
|     char *user_config_dir = NULL; | ||||
|     size_t len = 0; | ||||
|  | ||||
| # if defined(__APPLE__) | ||||
|     len = strlen(home) + strlen("/Library/Application Support") + 1; | ||||
| @@ -107,8 +85,9 @@ char *get_user_config_dir(void) | ||||
|     snprintf(user_config_dir, len, "%s/Library/Application Support", home); | ||||
| # else /* __APPLE__ */ | ||||
|  | ||||
|     const char *tmp; | ||||
|     if (!(tmp = getenv("XDG_CONFIG_HOME"))) { | ||||
|     const char *tmp = getenv("XDG_CONFIG_HOME"); | ||||
|  | ||||
|     if (tmp == NULL) { | ||||
|         len = strlen(home) + strlen("/.config") + 1; | ||||
|         user_config_dir = malloc(len); | ||||
|  | ||||
| @@ -124,56 +103,52 @@ char *get_user_config_dir(void) | ||||
| # endif /* __APPLE__ */ | ||||
|  | ||||
|     return user_config_dir; | ||||
| #undef NSS_BUFLEN_PASSWD | ||||
| #endif /* WIN32 */ | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Creates the config directory. | ||||
| /* Creates the config and chatlog directories. | ||||
|  * | ||||
|  * Returns 0 on success. | ||||
|  * Returns -1 on failure. | ||||
|  */ | ||||
| int create_user_config_dir(char *path) | ||||
| int create_user_config_dirs(char *path) | ||||
| { | ||||
| #ifdef _WIN32 | ||||
| #warning Please fix configdir for Win32 | ||||
|     return -1; | ||||
| #if 0 | ||||
|     char *fullpath = malloc(strlen(path) + strlen(CONFIGDIR) + 1); | ||||
|     strcpy(fullpath, path); | ||||
|     strcat(fullpath, CONFIGDIR); | ||||
|  | ||||
|     mkdir_err = _mkdir(fullpath); | ||||
|     struct __stat64 buf; | ||||
|  | ||||
|     if (mkdir_err && (errno != EEXIST || _wstat64(fullpath, &buf) || !S_ISDIR(buf.st_mode))) { | ||||
|         free(fullpath); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     free(fullpath); | ||||
| #endif | ||||
|  | ||||
| #else | ||||
|     int mkdir_err; | ||||
|  | ||||
|     mkdir_err = mkdir(path, 0700); | ||||
|     struct stat buf; | ||||
|     int mkdir_err = mkdir(path, 0700); | ||||
|  | ||||
|     if (mkdir_err && (errno != EEXIST || stat(path, &buf) || !S_ISDIR(buf.st_mode))) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     char *fullpath = malloc(strlen(path) + strlen(CONFIGDIR) + 1); | ||||
|     char *logpath = malloc(strlen(path) + strlen(LOGDIR) + 1); | ||||
|  | ||||
|     if (fullpath == NULL || logpath == NULL) { | ||||
|         exit_toxic_err("failed in load_data_structures", FATALERR_MEMORY); | ||||
|     } | ||||
|  | ||||
|     strcpy(fullpath, path); | ||||
|     strcat(fullpath, CONFIGDIR); | ||||
|  | ||||
|     strcpy(logpath, path); | ||||
|     strcat(logpath, LOGDIR); | ||||
|  | ||||
|     mkdir_err = mkdir(fullpath, 0700); | ||||
|  | ||||
|     if (mkdir_err && (errno != EEXIST || stat(fullpath, &buf) || !S_ISDIR(buf.st_mode))) { | ||||
|         free(fullpath); | ||||
|         free(logpath); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     mkdir_err = mkdir(logpath, 0700); | ||||
|  | ||||
|     if (mkdir_err && (errno != EEXIST || stat(logpath, &buf) || !S_ISDIR(buf.st_mode))) { | ||||
|         free(fullpath); | ||||
|         free(logpath); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     free(logpath); | ||||
|     free(fullpath); | ||||
|     return 0; | ||||
| #endif | ||||
| } | ||||
|   | ||||
| @@ -1,33 +1,56 @@ | ||||
| /* | ||||
|  *  Copyright (C) 2013 Tox project All Rights Reserved. | ||||
| /*  configdir.h | ||||
|  * | ||||
|  *  This file is part of Tox. | ||||
|  * | ||||
|  *  Tox is free software: you can redistribute it and/or modify | ||||
|  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||
|  * | ||||
|  *  This file is part of Toxic. | ||||
|  * | ||||
|  *  Toxic is free software: you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation, either version 3 of the License, or | ||||
|  *  (at your option) any later version. | ||||
|  * | ||||
|  *  Tox is distributed in the hope that it will be useful, | ||||
|  *  Toxic is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with Tox.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #ifdef _win32 | ||||
| #define CONFIGDIR "\\tox\\" | ||||
| #else | ||||
| #define CONFIGDIR "/tox/" | ||||
| #ifndef CONFIGDIR_H | ||||
| #define CONFIGDIR_H | ||||
|  | ||||
| #ifndef NSS_BUFLEN_PASSWD | ||||
| #define NSS_BUFLEN_PASSWD 4096 | ||||
| #endif | ||||
|  | ||||
| #define CONFIGDIR "/tox/" | ||||
| #define LOGDIR "/tox/chatlogs/" | ||||
|  | ||||
| #ifndef S_ISDIR | ||||
| #define S_ISDIR(mode)  (((mode) & S_IFMT) == S_IFDIR) | ||||
| #endif | ||||
|  | ||||
| /** | ||||
|  * @brief Get the user's config directory. | ||||
|  * | ||||
|  * 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); | ||||
|  | ||||
| int create_user_config_dir(char *path); | ||||
| /* get the user's home directory. */ | ||||
| void get_home_dir(char *home, int size); | ||||
|  | ||||
| /* Creates the config and chatlog directories. | ||||
|  * | ||||
|  * Returns 0 on success. | ||||
|  * Returns -1 on failure. | ||||
|  */ | ||||
| int create_user_config_dirs(char *path); | ||||
|  | ||||
| #endif /* CONFIGDIR_H */ | ||||
|   | ||||
							
								
								
									
										94
									
								
								src/curl_util.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								src/curl_util.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,94 @@ | ||||
| /*  curl_util.c | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2016 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 <stdint.h> | ||||
| #include <string.h> | ||||
|  | ||||
| #include <curl/curl.h> | ||||
| #include <tox/tox.h> | ||||
|  | ||||
| #include "curl_util.h" | ||||
|  | ||||
| /* Sets proxy info for given CURL handler. | ||||
|  * | ||||
|  * Returns 0 on success or if no proxy is set by the client. | ||||
|  * Returns -1 if proxy info is invalid. | ||||
|  * Returns an int > 0 on curl error (see: https://curl.haxx.se/libcurl/c/libcurl-errors.html) | ||||
|  */ | ||||
| int set_curl_proxy(CURL *c_handle, const char *proxy_address, uint16_t port, uint8_t proxy_type) | ||||
| { | ||||
|     if (proxy_type == TOX_PROXY_TYPE_NONE) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     if (proxy_address == NULL || port == 0) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     int ret = curl_easy_setopt(c_handle, CURLOPT_PROXYPORT, (long) port); | ||||
|  | ||||
|     if (ret != CURLE_OK) { | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     long int type = proxy_type == TOX_PROXY_TYPE_SOCKS5 ? CURLPROXY_SOCKS5_HOSTNAME : CURLPROXY_HTTP; | ||||
|  | ||||
|     ret = curl_easy_setopt(c_handle, CURLOPT_PROXYTYPE, type); | ||||
|  | ||||
|     if (ret != CURLE_OK) { | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     ret = curl_easy_setopt(c_handle, CURLOPT_PROXY, proxy_address); | ||||
|  | ||||
|     if (ret != CURLE_OK) { | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /* Callback function for CURL to write received data. | ||||
|  * | ||||
|  * This function will append data from an http request to the data buffer | ||||
|  * until the request is complete or the buffer is full. Buffer will be null terminated. | ||||
|  * | ||||
|  * Returns number of bytes received from http request on success (don't change this). | ||||
|  * Returns 0 if data exceeds buffer size. | ||||
|  */ | ||||
| size_t curl_cb_write_data(void *data, size_t size, size_t nmemb, void *user_pointer) | ||||
| { | ||||
|     struct Recv_Curl_Data *recv_data = (struct Recv_Curl_Data *) user_pointer; | ||||
|  | ||||
|     size_t length = size * nmemb; | ||||
|     size_t total_size = length + recv_data->length; | ||||
|  | ||||
|     if (total_size > MAX_RECV_CURL_DATA_SIZE) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     memcpy(recv_data->data + recv_data->length, data, length); | ||||
|     recv_data->data[total_size] = '\0'; | ||||
|     recv_data->length += length; | ||||
|  | ||||
|     return length; | ||||
| } | ||||
							
								
								
									
										57
									
								
								src/curl_util.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/curl_util.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | ||||
| /*  curl_util.h | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2016 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 CURL_UTIL_H | ||||
| #define CURL_UTIL_H | ||||
|  | ||||
| #include <stdint.h> | ||||
|  | ||||
| /* List based on Mozilla's recommended configurations for modern browsers */ | ||||
| #define TLS_CIPHER_SUITE_LIST "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK" | ||||
|  | ||||
| /* Max size of an http response that we can store in Recv_Data */ | ||||
| #define MAX_RECV_CURL_DATA_SIZE 32767 | ||||
|  | ||||
| /* Holds data received from curl lookup */ | ||||
| struct Recv_Curl_Data { | ||||
|     char data[MAX_RECV_CURL_DATA_SIZE + 1];   /* Data received from curl write data callback */ | ||||
|     size_t length;  /* Total number of bytes written to data buffer (doesn't include null) */ | ||||
| }; | ||||
|  | ||||
| /* Sets proxy info for given CURL handler. | ||||
|  * | ||||
|  * Returns 0 on success or if no proxy is set by the client. | ||||
|  * Returns -1 if proxy info is invalid. | ||||
|  * Returns an int > 0 on curl error (see: https://curl.haxx.se/libcurl/c/libcurl-errors.html) | ||||
|  */ | ||||
| int set_curl_proxy(CURL *c_handle, const char *proxy_address, uint16_t port, uint8_t proxy_type); | ||||
|  | ||||
| /* Callback function for CURL to write received data. | ||||
|  * | ||||
|  * This function will append data from an http request to the data buffer | ||||
|  * until the request is complete or the buffer is full. Buffer will be null terminated. | ||||
|  * | ||||
|  * Returns size of bytes written to the data buffer. | ||||
|  */ | ||||
| size_t curl_cb_write_data(void *data, size_t size, size_t nmemb, void *user_pointer); | ||||
|  | ||||
| #endif /* CURL_UTIL_H */ | ||||
							
								
								
									
										309
									
								
								src/execute.c
									
									
									
									
									
								
							
							
						
						
									
										309
									
								
								src/execute.c
									
									
									
									
									
								
							| @@ -1,14 +1,39 @@ | ||||
| /* | ||||
|  * Toxic -- Tox Curses Client | ||||
| /*  execute.c | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||
|  * | ||||
|  *  This file is part of Toxic. | ||||
|  * | ||||
|  *  Toxic is free software: you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation, either version 3 of the License, or | ||||
|  *  (at your option) any later version. | ||||
|  * | ||||
|  *  Toxic is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <assert.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
|  | ||||
| #include "toxic_windows.h" | ||||
| #include "execute.h" | ||||
| #include "api.h" | ||||
| #include "chat_commands.h" | ||||
| #include "execute.h" | ||||
| #include "global_commands.h" | ||||
| #include "conference_commands.h" | ||||
| #include "line_info.h" | ||||
| #include "misc_tools.h" | ||||
| #include "notify.h" | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| struct cmd_func { | ||||
|     const char *name; | ||||
| @@ -16,73 +41,183 @@ struct cmd_func { | ||||
| }; | ||||
|  | ||||
| static struct cmd_func global_commands[] = { | ||||
|     { "/accept",    cmd_accept      }, | ||||
|     { "/add",       cmd_add         }, | ||||
|     { "/clear",     cmd_clear       }, | ||||
|     { "/connect",   cmd_connect     }, | ||||
|     { "/exit",      cmd_quit        }, | ||||
|     { "/groupchat", cmd_groupchat   }, | ||||
|     { "/help",      cmd_prompt_help }, | ||||
|     { "/myid",      cmd_myid        }, | ||||
|     { "/nick",      cmd_nick        }, | ||||
|     { "/note",      cmd_note        }, | ||||
|     { "/q",         cmd_quit        }, | ||||
|     { "/quit",      cmd_quit        }, | ||||
|     { "/status",    cmd_status      }, | ||||
|     { "/accept",    cmd_accept        }, | ||||
|     { "/add",       cmd_add           }, | ||||
|     { "/avatar",    cmd_avatar        }, | ||||
|     { "/clear",     cmd_clear         }, | ||||
|     { "/connect",   cmd_connect       }, | ||||
|     { "/decline",   cmd_decline       }, | ||||
|     { "/exit",      cmd_quit          }, | ||||
|     { "/conference", cmd_conference    }, | ||||
|     { "/help",      cmd_prompt_help   }, | ||||
|     { "/log",       cmd_log           }, | ||||
|     { "/myid",      cmd_myid          }, | ||||
| #ifdef QRCODE | ||||
|     { "/myqr",      cmd_myqr          }, | ||||
| #endif /* QRCODE */ | ||||
|     { "/nick",      cmd_nick          }, | ||||
|     { "/note",      cmd_note          }, | ||||
|     { "/nospam",    cmd_nospam        }, | ||||
|     { "/q",         cmd_quit          }, | ||||
|     { "/quit",      cmd_quit          }, | ||||
|     { "/requests",  cmd_requests      }, | ||||
|     { "/status",    cmd_status        }, | ||||
| #ifdef AUDIO | ||||
|     { "/lsdev",     cmd_list_devices  }, | ||||
|     { "/sdev",      cmd_change_device }, | ||||
| #endif /* AUDIO */ | ||||
| #ifdef VIDEO | ||||
|     { "/lsvdev",    cmd_list_video_devices  }, | ||||
|     { "/svdev",     cmd_change_video_device }, | ||||
| #endif /* VIDEO */ | ||||
| #ifdef PYTHON | ||||
|     { "/run",       cmd_run           }, | ||||
| #endif /* PYTHON */ | ||||
|     { NULL,         NULL              }, | ||||
| }; | ||||
|  | ||||
| static struct cmd_func chat_commands[] = { | ||||
|     { "/help",      cmd_chat_help   }, | ||||
|     { "/invite",    cmd_groupinvite }, | ||||
|     { "/join",      cmd_join_group  }, | ||||
|     { "/savefile",  cmd_savefile    }, | ||||
|     { "/sendfile",  cmd_sendfile    }, | ||||
|     { "/cancel",    cmd_cancelfile        }, | ||||
|     { "/invite",    cmd_conference_invite }, | ||||
|     { "/join",      cmd_conference_join   }, | ||||
|     { "/savefile",  cmd_savefile          }, | ||||
|     { "/sendfile",  cmd_sendfile          }, | ||||
| #ifdef AUDIO | ||||
|     { "/call",      cmd_call              }, | ||||
|     { "/answer",    cmd_answer            }, | ||||
|     { "/reject",    cmd_reject            }, | ||||
|     { "/hangup",    cmd_hangup            }, | ||||
|     { "/mute",      cmd_mute              }, | ||||
|     { "/sense",     cmd_sense             }, | ||||
|     { "/bitrate",   cmd_bitrate           }, | ||||
| #endif /* AUDIO */ | ||||
| #ifdef VIDEO | ||||
|     { "/vcall",     cmd_vcall             }, | ||||
|     { "/video",     cmd_video             }, | ||||
|     { "/res",       cmd_res               }, | ||||
| #endif /* VIDEO */ | ||||
|     { NULL,         NULL                  }, | ||||
| }; | ||||
|  | ||||
| /* Parses input command and puts args into arg array.  | ||||
|    Returns number of arguments on success, -1 on failure. */ | ||||
| static int parse_command(WINDOW *w, char *cmd, char (*args)[MAX_STR_SIZE]) | ||||
| static struct cmd_func conference_commands[] = { | ||||
|     { "/title",     cmd_conference_set_title }, | ||||
|  | ||||
| #ifdef AUDIO | ||||
|     { "/audio",     cmd_enable_audio }, | ||||
|     { "/mute",      cmd_conference_mute   }, | ||||
|     { "/ptt",       cmd_conference_push_to_talk }, | ||||
|     { "/sense",     cmd_conference_sense  }, | ||||
| #endif /* AUDIO */ | ||||
|     { NULL,         NULL             }, | ||||
| }; | ||||
|  | ||||
|  | ||||
| #ifdef PYTHON | ||||
| #define SPECIAL_COMMANDS 7 | ||||
| #else | ||||
| #define SPECIAL_COMMANDS 6 | ||||
| #endif /* PYTHON */ | ||||
|  | ||||
| /* Special commands are commands that only take one argument even if it contains spaces */ | ||||
| static const char special_commands[SPECIAL_COMMANDS][MAX_CMDNAME_SIZE] = { | ||||
|     "/avatar", | ||||
|     "/nick", | ||||
|     "/note", | ||||
| #ifdef PYTHON | ||||
|     "/run", | ||||
| #endif /* PYTHON */ | ||||
|     "/sendfile", | ||||
|     "/title", | ||||
|     "/mute", | ||||
| }; | ||||
|  | ||||
| /* Returns true if input command is in the special_commands array. */ | ||||
| static bool is_special_command(const char *input) | ||||
| { | ||||
|     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 | ||||
|     const int s = char_find(0, input, ' '); | ||||
|  | ||||
|     /* characters wrapped in double quotes count as one arg */ | ||||
|     while (!cmd_end && num_args < MAX_NUM_ARGS) { | ||||
|         if (*cmd == '\"') { | ||||
|             end = strchr(cmd+1, '\"'); | ||||
|  | ||||
|             if (end++ == NULL) {    /* Increment past the end quote */ | ||||
|                 wprintw(w, "Invalid argument. Did you forget a closing \"?\n"); | ||||
|                 return -1; | ||||
|             } | ||||
|  | ||||
|             cmd_end = *end == '\0'; | ||||
|         } else { | ||||
|             end = strchr(cmd, ' '); | ||||
|             cmd_end = end == NULL; | ||||
|     for (int i = 0; i < SPECIAL_COMMANDS; ++i) { | ||||
|         if (strncmp(input, special_commands[i], s) == 0) { | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         if (!cmd_end) | ||||
|             *end++ = '\0';    /* mark end of current argument */ | ||||
|  | ||||
|         /* Copy from start of current arg to where we just inserted the null byte */ | ||||
|         strcpy(args[num_args++], cmd); | ||||
|         cmd = end; | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| /* Parses commands in the special_commands array. Unlike parse_command, this function | ||||
|  * does not split the input string at spaces. | ||||
|  * | ||||
|  * Returns the number of arguments. | ||||
|  */ | ||||
| static int parse_special_command(const char *input, char (*args)[MAX_STR_SIZE]) | ||||
| { | ||||
|     int len = strlen(input); | ||||
|     int s = char_find(0, input, ' '); | ||||
|  | ||||
|     memcpy(args[0], input, s); | ||||
|     args[0][s++] = '\0';    // increment to remove space after "/command " | ||||
|  | ||||
|     if (s >= len) { | ||||
|         return 1;  // No additional args | ||||
|     } | ||||
|  | ||||
|     memcpy(args[1], input + s, len - s); | ||||
|     args[1][len - s] = '\0'; | ||||
|  | ||||
|     return 2; | ||||
| } | ||||
|  | ||||
| /* Parses input command and puts args into arg array. | ||||
|  * | ||||
|  * Returns the number of arguments. | ||||
|  */ | ||||
| static int parse_command(const char *input, char (*args)[MAX_STR_SIZE]) | ||||
| { | ||||
|     if (is_special_command(input)) { | ||||
|         return parse_special_command(input, args); | ||||
|     } | ||||
|  | ||||
|     char *cmd = strdup(input); | ||||
|  | ||||
|     if (cmd == NULL) { | ||||
|         exit_toxic_err("failed in parse_command", FATALERR_MEMORY); | ||||
|     } | ||||
|  | ||||
|     int num_args = 0; | ||||
|  | ||||
|     /* characters wrapped in double quotes count as one arg */ | ||||
|     while (num_args < MAX_NUM_ARGS) { | ||||
|         int i = char_find(0, cmd, ' ');    // index of last char in an argument | ||||
|         memcpy(args[num_args], cmd, i); | ||||
|         args[num_args++][i] = '\0'; | ||||
|  | ||||
|         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]) | ||||
| /* Matches command to respective function. | ||||
|  * | ||||
|  * Returns 0 on match. | ||||
|  * Returns 1 on no match | ||||
|  */ | ||||
| static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, struct cmd_func *commands, | ||||
|                       char (*args)[MAX_STR_SIZE]) | ||||
| { | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < num_cmds; ++i) { | ||||
|     for (i = 0; commands[i].name != NULL; ++i) { | ||||
|         if (strcmp(args[0], commands[i].name) == 0) { | ||||
|             (commands[i].func)(w, self, m, num_args-1, args); | ||||
|             (commands[i].func)(w, self, m, num_args - 1, args); | ||||
|             return 0; | ||||
|         } | ||||
|     } | ||||
| @@ -90,33 +225,51 @@ static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, int num_ | ||||
|     return 1; | ||||
| } | ||||
|  | ||||
| void execute(WINDOW* w, ToxWindow *self, Tox *m, char *cmd, int mode) | ||||
| void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode) | ||||
| { | ||||
|     if (string_is_empty(cmd)) | ||||
|     if (string_is_empty(input)) { | ||||
|         return; | ||||
|  | ||||
|     char args[MAX_NUM_ARGS][MAX_STR_SIZE] = {0}; | ||||
|     int num_args = parse_command(w, cmd, args); | ||||
|  | ||||
|     if (num_args == -1) | ||||
|         return; | ||||
|  | ||||
|     /* Try to match input command to command functions. If non-global command mode is specified,  | ||||
|        try specified mode's commands first, then upon failure try global commands.  | ||||
|  | ||||
|        Note: Global commands must come last in case of duplicate command names */ | ||||
|     switch (mode) { | ||||
|     case CHAT_COMMAND_MODE: | ||||
|         if (do_command(w, self, m, num_args, CHAT_NUM_COMMANDS, chat_commands, args) == 0) | ||||
|             return; | ||||
|         break; | ||||
|  | ||||
|     case GROUPCHAT_COMMAND_MODE: | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     if (do_command(w, self, m, num_args, GLOBAL_NUM_COMMANDS, global_commands, args) == 0) | ||||
|         return; | ||||
|     char args[MAX_NUM_ARGS][MAX_STR_SIZE]; | ||||
|     int num_args = parse_command(input, args); | ||||
|  | ||||
|     wprintw(w, "Invalid command.\n"); | ||||
|     if (num_args <= 0) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     /* Try to match input command to command functions. If non-global command mode is specified, | ||||
|      * try specified mode's commands first, then upon failure try global commands. | ||||
|      * | ||||
|      * 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_commands, args) == 0) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             break; | ||||
|  | ||||
|         case CONFERENCE_COMMAND_MODE: | ||||
|             if (do_command(w, self, m, num_args, conference_commands, args) == 0) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             break; | ||||
|     } | ||||
|  | ||||
|     if (do_command(w, self, m, num_args, global_commands, args) == 0) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| #ifdef PYTHON | ||||
|  | ||||
|     if (do_plugin_command(num_args, args) == 0) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| #endif | ||||
|  | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid command."); | ||||
| } | ||||
|   | ||||
| @@ -1,15 +1,39 @@ | ||||
| /* | ||||
|  * Toxic -- Tox Curses Client | ||||
| /*  execute.h | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||
|  * | ||||
|  *  This file is part of Toxic. | ||||
|  * | ||||
|  *  Toxic is free software: you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation, either version 3 of the License, or | ||||
|  *  (at your option) any later version. | ||||
|  * | ||||
|  *  Toxic is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #ifndef EXECUTE_H | ||||
| #define EXECUTE_H | ||||
|  | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| #define MAX_NUM_ARGS 4     /* Includes command */ | ||||
| #define GLOBAL_NUM_COMMANDS 13 | ||||
| #define CHAT_NUM_COMMANDS 5 | ||||
|  | ||||
| enum { | ||||
|     GLOBAL_COMMAND_MODE, | ||||
|     CHAT_COMMAND_MODE, | ||||
|     GROUPCHAT_COMMAND_MODE, | ||||
|     CONFERENCE_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 /* EXECUTE_H */ | ||||
|   | ||||
							
								
								
									
										309
									
								
								src/file_transfers.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										309
									
								
								src/file_transfers.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,309 @@ | ||||
| /*  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 <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <time.h> | ||||
| #include <unistd.h> | ||||
|  | ||||
| #include "file_transfers.h" | ||||
| #include "friendlist.h" | ||||
| #include "line_info.h" | ||||
| #include "misc_tools.h" | ||||
| #include "notify.h" | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| extern FriendsList Friends; | ||||
|  | ||||
| /* number of "#"'s in file transfer progress bar. Keep well below MAX_STR_SIZE */ | ||||
| #define NUM_PROG_MARKS 50 | ||||
| #define STR_BUF_SIZE 30 | ||||
|  | ||||
| /* creates initial progress line that will be updated during file transfer. | ||||
|    Assumes progline has room for at least MAX_STR_SIZE bytes */ | ||||
| void init_progress_bar(char *progline) | ||||
| { | ||||
|     strcpy(progline, "0% ["); | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < NUM_PROG_MARKS; ++i) { | ||||
|         strcat(progline, "-"); | ||||
|     } | ||||
|  | ||||
|     strcat(progline, "] 0.0 B/s"); | ||||
| } | ||||
|  | ||||
| /* prints a progress bar for file transfers. */ | ||||
| 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 pct_str[STR_BUF_SIZE]; | ||||
|     snprintf(pct_str, sizeof(pct_str), "%.1f%%", pct_done); | ||||
|  | ||||
|     char bps_str[STR_BUF_SIZE]; | ||||
|     bytes_convert_str(bps_str, sizeof(bps_str), bps); | ||||
|  | ||||
|     char prog_line[NUM_PROG_MARKS + 1]; | ||||
|     prog_line[0] = 0; | ||||
|  | ||||
|     int n = pct_done / (100 / NUM_PROG_MARKS); | ||||
|     int i, j; | ||||
|  | ||||
|     for (i = 0; i < n; ++i) { | ||||
|         strcat(prog_line, "="); | ||||
|     } | ||||
|  | ||||
|     if (pct_done < 100) { | ||||
|         strcpy(prog_line + n, ">"); | ||||
|     } | ||||
|  | ||||
|     for (j = i; j < NUM_PROG_MARKS - 1; ++j) { | ||||
|         strcat(prog_line, "-"); | ||||
|     } | ||||
|  | ||||
|     size_t line_buf_size = strlen(pct_str) + NUM_PROG_MARKS + strlen(bps_str) + 7; | ||||
|     char *full_line = malloc(line_buf_size); | ||||
|  | ||||
|     if (full_line == NULL) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     snprintf(full_line, line_buf_size, "%s [%s] %s/s", pct_str, prog_line, bps_str); | ||||
|  | ||||
|     line_info_set(self, line_id, full_line); | ||||
|  | ||||
|     free(full_line); | ||||
| } | ||||
|  | ||||
| static void refresh_progress_helper(ToxWindow *self, 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, uint32_t friendnumber) | ||||
| { | ||||
|     for (size_t i = 0; i < MAX_FILES; ++i) { | ||||
|         refresh_progress_helper(self, &Friends.list[friendnumber].file_receiver[i]); | ||||
|         refresh_progress_helper(self, &Friends.list[friendnumber].file_sender[i]); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void clear_file_transfer(struct FileTransfer *ft) | ||||
| { | ||||
|     *ft = (struct FileTransfer) { | ||||
|         0 | ||||
|     }; | ||||
| } | ||||
|  | ||||
| /* Returns a pointer to friendnumber's FileTransfer struct associated with filenumber. | ||||
|  * Returns NULL if filenumber is invalid. | ||||
|  */ | ||||
| struct FileTransfer *get_file_transfer_struct(uint32_t friendnumber, uint32_t filenumber) | ||||
| { | ||||
|     for (size_t i = 0; i < MAX_FILES; ++i) { | ||||
|         struct FileTransfer *ft_send = &Friends.list[friendnumber].file_sender[i]; | ||||
|  | ||||
|         if (ft_send->state != FILE_TRANSFER_INACTIVE && ft_send->filenumber == filenumber) { | ||||
|             return ft_send; | ||||
|         } | ||||
|  | ||||
|         struct FileTransfer *ft_recv = &Friends.list[friendnumber].file_receiver[i]; | ||||
|  | ||||
|         if (ft_recv->state != FILE_TRANSFER_INACTIVE && ft_recv->filenumber == filenumber) { | ||||
|             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 friendnumber, uint32_t index, | ||||
|         FILE_TRANSFER_DIRECTION direction) | ||||
| { | ||||
|     if (direction != FILE_TRANSFER_RECV && direction != FILE_TRANSFER_SEND) { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     for (size_t i = 0; i < MAX_FILES; ++i) { | ||||
|         struct FileTransfer *ft = direction == FILE_TRANSFER_SEND ? | ||||
|                                       &Friends.list[friendnumber].file_sender[i] : | ||||
|                                       &Friends.list[friendnumber].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 friendnumber, uint32_t filenumber, uint8_t type) | ||||
| { | ||||
|     for (size_t i = 0; i < MAX_FILES; ++i) { | ||||
|         struct FileTransfer *ft = &Friends.list[friendnumber].file_sender[i]; | ||||
|  | ||||
|         if (ft->state == FILE_TRANSFER_INACTIVE) { | ||||
|             clear_file_transfer(ft); | ||||
|             ft->window = window; | ||||
|             ft->index = i; | ||||
|             ft->friendnumber = friendnumber; | ||||
|             ft->filenumber = filenumber; | ||||
|             ft->file_type = type; | ||||
|             ft->state = FILE_TRANSFER_PENDING; | ||||
|             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 friendnumber, uint32_t filenumber, | ||||
|         uint8_t type) | ||||
| { | ||||
|     for (size_t i = 0; i < MAX_FILES; ++i) { | ||||
|         struct FileTransfer *ft = &Friends.list[friendnumber].file_receiver[i]; | ||||
|  | ||||
|         if (ft->state == FILE_TRANSFER_INACTIVE) { | ||||
|             clear_file_transfer(ft); | ||||
|             ft->window = window; | ||||
|             ft->index = i; | ||||
|             ft->friendnumber = friendnumber; | ||||
|             ft->filenumber = filenumber; | ||||
|             ft->file_type = type; | ||||
|             ft->state = FILE_TRANSFER_PENDING; | ||||
|             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 friendnumber, uint32_t filenumber, | ||||
|                                        FILE_TRANSFER_DIRECTION direction, uint8_t type) | ||||
| { | ||||
|     if (direction == FILE_TRANSFER_RECV) { | ||||
|         return new_file_receiver(window, friendnumber, filenumber, type); | ||||
|     } | ||||
|  | ||||
|     if (direction == FILE_TRANSFER_SEND) { | ||||
|         return new_file_sender(window, friendnumber, filenumber, 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->friendnumber, ft->filenumber, (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, false, NULL, NULL, SYS_MSG, 0, 0, "%s", message); | ||||
|     } | ||||
|  | ||||
|     clear_file_transfer(ft); | ||||
| } | ||||
|  | ||||
| /* Kills active outgoing avatar file transfers for friendnumber */ | ||||
| void kill_avatar_file_transfers_friend(Tox *m, uint32_t friendnumber) | ||||
| { | ||||
|     for (size_t i = 0; i < MAX_FILES; ++i) { | ||||
|         struct FileTransfer *ft = &Friends.list[friendnumber].file_sender[i]; | ||||
|  | ||||
|         if (ft->file_type == TOX_FILE_KIND_AVATAR) { | ||||
|             close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* Kills all active file transfers for friendnumber */ | ||||
| void kill_all_file_transfers_friend(Tox *m, uint32_t friendnumber) | ||||
| { | ||||
|     for (size_t i = 0; i < MAX_FILES; ++i) { | ||||
|         close_file_transfer(NULL, m, &Friends.list[friendnumber].file_sender[i], TOX_FILE_CONTROL_CANCEL, NULL, silent); | ||||
|         close_file_transfer(NULL, m, &Friends.list[friendnumber].file_receiver[i], TOX_FILE_CONTROL_CANCEL, NULL, silent); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void kill_all_file_transfers(Tox *m) | ||||
| { | ||||
|     for (size_t i = 0; i < Friends.max_idx; ++i) { | ||||
|         kill_all_file_transfers_friend(m, Friends.list[i].num); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										112
									
								
								src/file_transfers.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								src/file_transfers.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,112 @@ | ||||
| /*  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 "notify.h" | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| #define KiB 1024 | ||||
| #define MiB 1048576       /* 1024^2 */ | ||||
| #define GiB 1073741824    /* 1024^3 */ | ||||
|  | ||||
| #define MAX_FILES 32 | ||||
|  | ||||
| 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; | ||||
|     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 filenumber; | ||||
|     uint32_t friendnumber; | ||||
|     size_t   index; | ||||
|     uint64_t file_size; | ||||
|     uint64_t position; | ||||
|     time_t   last_line_progress;   /* The last time we updated the progress bar */ | ||||
|     uint32_t line_id; | ||||
|     uint8_t  file_id[TOX_FILE_ID_LENGTH]; | ||||
| }; | ||||
|  | ||||
| /* 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, uint32_t friendnumber); | ||||
|  | ||||
| /* Returns a pointer to friendnumber's FileTransfer struct associated with filenumber. | ||||
|  * Returns NULL if filenumber is invalid. | ||||
|  */ | ||||
| struct FileTransfer *get_file_transfer_struct(uint32_t friendnumber, uint32_t filenumber); | ||||
|  | ||||
|  | ||||
| /* 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 friendnumber, 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 friendnumber, uint32_t filenumber, | ||||
|                                        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 active outgoing avatar file transfers for friendnumber */ | ||||
| void kill_avatar_file_transfers_friend(Tox *m, uint32_t friendnumber); | ||||
|  | ||||
| /* Kills all active file transfers for friendnumber */ | ||||
| void kill_all_file_transfers_friend(Tox *m, uint32_t friendnumber); | ||||
|  | ||||
| void kill_all_file_transfers(Tox *m); | ||||
|  | ||||
| #endif /* FILE_TRANSFERS_H */ | ||||
							
								
								
									
										1494
									
								
								src/friendlist.c
									
									
									
									
									
								
							
							
						
						
									
										1494
									
								
								src/friendlist.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										107
									
								
								src/friendlist.h
									
									
									
									
									
								
							
							
						
						
									
										107
									
								
								src/friendlist.h
									
									
									
									
									
								
							| @@ -1,29 +1,104 @@ | ||||
| #ifndef FRIENDLIST_H_53I41IM | ||||
| #define FRIENDLIST_H_53I41IM | ||||
| /*  friendlist.h | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||
|  * | ||||
|  *  This file is part of Toxic. | ||||
|  * | ||||
|  *  Toxic is free software: you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation, either version 3 of the License, or | ||||
|  *  (at your option) any later version. | ||||
|  * | ||||
|  *  Toxic is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "toxic_windows.h" | ||||
| #ifndef FRIENDLIST_H | ||||
| #define FRIENDLIST_H | ||||
|  | ||||
| #include <time.h> | ||||
|  | ||||
| #include "file_transfers.h" | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| struct LastOnline { | ||||
|     uint64_t last_on; | ||||
|     struct tm tm; | ||||
|     char hour_min_str[TIME_STR_SIZE];    /* holds 12/24-hour time string e.g. "10:43 PM" */ | ||||
| }; | ||||
|  | ||||
| struct ConferenceInvite { | ||||
|     char *key; | ||||
|     uint16_t length; | ||||
|     uint8_t type; | ||||
|     bool pending; | ||||
| }; | ||||
|  | ||||
| typedef struct { | ||||
|     uint8_t name[TOX_MAX_NAME_LENGTH]; | ||||
|     char name[TOXIC_MAX_NAME_LENGTH + 1]; | ||||
|     uint16_t namelength; | ||||
|     uint8_t statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH]; | ||||
|     uint16_t statusmsg_len; | ||||
|     uint8_t pending_groupchat[TOX_CLIENT_ID_SIZE]; | ||||
|     uint8_t pub_key[TOX_CLIENT_ID_SIZE]; | ||||
|     int num; | ||||
|     char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH + 1]; | ||||
|     size_t statusmsg_len; | ||||
|     char pub_key[TOX_PUBLIC_KEY_SIZE]; | ||||
|     uint32_t num; | ||||
|     int chatwin; | ||||
|     bool active; | ||||
|     bool online; | ||||
|     Tox_Connection connection_status; | ||||
|     bool is_typing; | ||||
|     TOX_USERSTATUS status; | ||||
|     struct FileReceiver file_receiver; | ||||
|     bool logging_on;    /* saves preference for friend irrespective of global settings */ | ||||
|     Tox_User_Status status; | ||||
|  | ||||
|     struct LastOnline last_online; | ||||
|     struct ConferenceInvite conference_invite; | ||||
|  | ||||
|     struct FileTransfer file_receiver[MAX_FILES]; | ||||
|     struct FileTransfer file_sender[MAX_FILES]; | ||||
| } ToxicFriend; | ||||
|  | ||||
| ToxWindow new_friendlist(void); | ||||
| void disable_chatwin(int f_num); | ||||
| typedef struct { | ||||
|     char name[TOXIC_MAX_NAME_LENGTH + 1]; | ||||
|     uint16_t namelength; | ||||
|     char pub_key[TOX_PUBLIC_KEY_SIZE]; | ||||
|     uint32_t num; | ||||
|     bool active; | ||||
|     uint64_t last_on; | ||||
| } BlockedFriend; | ||||
|  | ||||
| typedef struct { | ||||
|     int num_selected; | ||||
|     size_t num_friends; | ||||
|     size_t num_online; | ||||
|     size_t max_idx;    /* 1 + the index of the last friend in list */ | ||||
|     uint32_t *index; | ||||
|     ToxicFriend *list; | ||||
| } FriendsList; | ||||
|  | ||||
| ToxWindow *new_friendlist(void); | ||||
| void friendlist_onInit(ToxWindow *self, Tox *m); | ||||
| void disable_chatwin(uint32_t f_num); | ||||
| int get_friendnum(uint8_t *name); | ||||
| int load_blocklist(char *data); | ||||
| void kill_friendlist(ToxWindow *self); | ||||
| void friendlist_onFriendAdded(ToxWindow *self, Tox *m, uint32_t num, bool sort); | ||||
| Tox_User_Status get_friend_status(uint32_t friendnumber); | ||||
| Tox_Connection get_friend_connection_status(uint32_t friendnumber); | ||||
|  | ||||
| /* sorts friendlist_index first by connection status then alphabetically */ | ||||
| void sort_friendlist_index(Tox *m); | ||||
| void sort_friendlist_index(void); | ||||
|  | ||||
| #endif /* end of include guard: FRIENDLIST_H_53I41IM */ | ||||
| /* | ||||
|  * Returns true if friend associated with `public_key` is in the block list. | ||||
|  * | ||||
|  * `public_key` must be at least TOX_PUBLIC_KEY_SIZE bytes. | ||||
|  */ | ||||
| bool friend_is_blocked(const char *public_key); | ||||
|  | ||||
| #endif /* end of include guard: FRIENDLIST_H */ | ||||
|   | ||||
| @@ -1,352 +1,724 @@ | ||||
| /* | ||||
|  * Toxic -- Tox Curses Client | ||||
| /*  global_commands.c | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||
|  * | ||||
|  *  This file is part of Toxic. | ||||
|  * | ||||
|  *  Toxic is free software: you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation, either version 3 of the License, or | ||||
|  *  (at your option) any later version. | ||||
|  * | ||||
|  *  Toxic is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #ifdef HAVE_CONFIG_H | ||||
| #include "config.h" | ||||
| #endif | ||||
|  | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
|  | ||||
| #include "toxic_windows.h" | ||||
| #include "misc_tools.h" | ||||
| #include "avatars.h" | ||||
| #include "conference.h" | ||||
| #include "friendlist.h" | ||||
| #include "help.h" | ||||
| #include "line_info.h" | ||||
| #include "log.h" | ||||
| #include "misc_tools.h" | ||||
| #include "name_lookup.h" | ||||
| #include "prompt.h" | ||||
| #include "qr_code.h" | ||||
| #include "term_mplex.h" | ||||
| #include "toxic.h" | ||||
| #include "toxic_strings.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| extern char *DATA_FILE; | ||||
| extern ToxWindow *prompt; | ||||
|  | ||||
| extern ToxicFriend friends[MAX_FRIENDS_NUM]; | ||||
|  | ||||
| extern uint8_t pending_frnd_requests[MAX_FRIENDS_NUM][TOX_CLIENT_ID_SIZE]; | ||||
| extern uint8_t num_frnd_requests; | ||||
| extern FriendsList Friends; | ||||
| extern FriendRequests FrndRequests; | ||||
|  | ||||
| /* command functions */ | ||||
| void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     /* check arguments */ | ||||
|     if (argc != 1) { | ||||
|       wprintw(window, "Invalid syntax.\n"); | ||||
|       return; | ||||
|     } | ||||
|     UNUSED_VAR(window); | ||||
|  | ||||
|     int req = atoi(argv[1]); | ||||
|  | ||||
|     if ((req == 0 && strcmp(argv[1], "0"))|| req >= MAX_FRIENDS_NUM) { | ||||
|         wprintw(window, "No pending friend request with that number.\n"); | ||||
|     if (argc < 1) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Request ID required."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (!strlen(pending_frnd_requests[req])) { | ||||
|         wprintw(window, "No pending friend request with that number.\n"); | ||||
|     long int req = strtol(argv[1], NULL, 10); | ||||
|  | ||||
|     if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req >= MAX_FRIEND_REQUESTS) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     int friendnum = tox_add_friend_norequest(m, pending_frnd_requests[req]); | ||||
|  | ||||
|     if (friendnum == -1) | ||||
|         wprintw(window, "Failed to add friend.\n"); | ||||
|     else { | ||||
|         wprintw(window, "Friend request accepted.\n"); | ||||
|         on_friendadded(m, friendnum, true); | ||||
|     if (!FrndRequests.request[req].active) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     memset(&pending_frnd_requests[req], 0, TOX_CLIENT_ID_SIZE); | ||||
|     Tox_Err_Friend_Add err; | ||||
|     uint32_t friendnum = tox_friend_add_norequest(m, FrndRequests.request[req].key, &err); | ||||
|  | ||||
|     if (err != TOX_ERR_FRIEND_ADD_OK) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to add friend (error %d\n)", err); | ||||
|         return; | ||||
|     } else { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Friend request accepted."); | ||||
|         on_friend_added(m, friendnum, true); | ||||
|     } | ||||
|  | ||||
|     FrndRequests.request[req] = (struct friend_request) { | ||||
|         0 | ||||
|     }; | ||||
|  | ||||
|     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; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     FrndRequests.max_idx = i; | ||||
|     --FrndRequests.num_requests; | ||||
| } | ||||
|  | ||||
| void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg) | ||||
| { | ||||
|     const char *errmsg; | ||||
|  | ||||
|     Tox_Err_Friend_Add err; | ||||
|     uint32_t f_num = tox_friend_add(m, (const uint8_t *) id_bin, (const uint8_t *) msg, strlen(msg), &err); | ||||
|  | ||||
|     switch (err) { | ||||
|         case TOX_ERR_FRIEND_ADD_TOO_LONG: | ||||
|             errmsg = "Message is too long."; | ||||
|             break; | ||||
|  | ||||
|         case TOX_ERR_FRIEND_ADD_NO_MESSAGE: | ||||
|             errmsg = "Please add a message to your request."; | ||||
|             break; | ||||
|  | ||||
|         case TOX_ERR_FRIEND_ADD_OWN_KEY: | ||||
|             errmsg = "That appears to be your own ID."; | ||||
|             break; | ||||
|  | ||||
|         case TOX_ERR_FRIEND_ADD_ALREADY_SENT: | ||||
|             errmsg = "Friend request has already been sent."; | ||||
|             break; | ||||
|  | ||||
|         case TOX_ERR_FRIEND_ADD_BAD_CHECKSUM: | ||||
|             errmsg = "Bad checksum in address."; | ||||
|             break; | ||||
|  | ||||
|         case TOX_ERR_FRIEND_ADD_SET_NEW_NOSPAM: | ||||
|             errmsg = "Nospam was different."; | ||||
|             break; | ||||
|  | ||||
|         case TOX_ERR_FRIEND_ADD_MALLOC: | ||||
|             errmsg = "Core memory allocation failed."; | ||||
|             break; | ||||
|  | ||||
|         case TOX_ERR_FRIEND_ADD_OK: | ||||
|             errmsg = "Friend request sent."; | ||||
|             on_friend_added(m, f_num, true); | ||||
|             break; | ||||
|  | ||||
|         case TOX_ERR_FRIEND_ADD_NULL: | ||||
|  | ||||
|         /* fallthrough */ | ||||
|         default: | ||||
|             errmsg = "Failed to add friend: Unknown error."; | ||||
|             break; | ||||
|     } | ||||
|  | ||||
|     num_frnd_requests = i; | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, errmsg); | ||||
| } | ||||
|  | ||||
| void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|  | ||||
|     if (argc < 1) { | ||||
|         wprintw(window, "Invalid syntax.\n"); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Tox ID or address required."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     char *id = argv[1]; | ||||
|     uint8_t msg[MAX_STR_SIZE]; | ||||
|     const char *id = argv[1]; | ||||
|     char msg[MAX_STR_SIZE]; | ||||
|  | ||||
|     if (argc > 1) { | ||||
|         uint8_t *temp = argv[2]; | ||||
|  | ||||
|         if (temp[0] != '\"') { | ||||
|             wprintw(window, "Message must be enclosed in quotes.\n"); | ||||
|         if (argv[2][0] != '\"') { | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Message must be enclosed in quotes."); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         temp[strlen(++temp)-1] = L'\0'; | ||||
|         snprintf(msg, sizeof(msg), "%s", temp); | ||||
|         /* remove opening and closing quotes */ | ||||
|         char tmp[MAX_STR_SIZE]; | ||||
|         snprintf(tmp, sizeof(tmp), "%s", &argv[2][1]); | ||||
|         int len = strlen(tmp) - 1; | ||||
|         tmp[len] = '\0'; | ||||
|         snprintf(msg, sizeof(msg), "%s", tmp); | ||||
|     } else { | ||||
|         uint8_t selfname[TOX_MAX_NAME_LENGTH]; | ||||
|         tox_get_self_name(m, selfname, TOX_MAX_NAME_LENGTH); | ||||
|         char selfname[TOX_MAX_NAME_LENGTH]; | ||||
|         tox_self_get_name(m, (uint8_t *) selfname); | ||||
|  | ||||
|         size_t n_len = tox_self_get_name_size(m); | ||||
|         selfname[n_len] = '\0'; | ||||
|         snprintf(msg, sizeof(msg), "Hello, my name is %s. Care to Tox?", selfname); | ||||
|     } | ||||
|  | ||||
|     if (strlen(id) != 2 * TOX_FRIEND_ADDRESS_SIZE) { | ||||
|         wprintw(window, "Invalid ID length.\n"); | ||||
|         return; | ||||
|     } | ||||
|     char id_bin[TOX_ADDRESS_SIZE] = {0}; | ||||
|     uint16_t id_len = (uint16_t) strlen(id); | ||||
|  | ||||
|     size_t i; | ||||
|     char xx[3]; | ||||
|     uint32_t x; | ||||
|     uint8_t id_bin[TOX_FRIEND_ADDRESS_SIZE]; | ||||
|     /* try to add tox ID */ | ||||
|     if (id_len == 2 * TOX_ADDRESS_SIZE) { | ||||
|         size_t i; | ||||
|         char xx[3]; | ||||
|         uint32_t x; | ||||
|  | ||||
|     for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; ++i) { | ||||
|         xx[0] = id[2 * i]; | ||||
|         xx[1] = id[2 * i + 1]; | ||||
|         xx[2] = '\0'; | ||||
|         for (i = 0; i < TOX_ADDRESS_SIZE; ++i) { | ||||
|             xx[0] = id[2 * i]; | ||||
|             xx[1] = id[2 * i + 1]; | ||||
|             xx[2] = '\0'; | ||||
|  | ||||
|         if (sscanf(xx, "%02x", &x) != 1) { | ||||
|             wprintw(window, "Invalid ID.\n"); | ||||
|             if (sscanf(xx, "%02x", &x) != 1) { | ||||
|                 line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid Tox ID."); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             id_bin[i] = x; | ||||
|         } | ||||
|  | ||||
|         if (friend_is_blocked(id_bin)) { | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Friend is in your block list."); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         id_bin[i] = x; | ||||
|         cmd_add_helper(self, m, id_bin, msg); | ||||
|     } else {    /* assume id is a username@domain address and do http name server lookup */ | ||||
|         name_lookup(self, m, id_bin, id, msg); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|  | ||||
|     if (argc != 1 || strlen(argv[1]) < 3) { | ||||
|         avatar_unset(m); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Avatar has been unset."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; i++) { | ||||
|         id[i] = toupper(id[i]); | ||||
|     char path[MAX_STR_SIZE]; | ||||
|     snprintf(path, sizeof(path), "%s", argv[1]); | ||||
|     int len = strlen(path); | ||||
|  | ||||
|     if (len <= 0) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid path."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     int f_num = tox_add_friend(m, id_bin, msg, strlen(msg) + 1); | ||||
|     path[len] = '\0'; | ||||
|     char filename[MAX_STR_SIZE]; | ||||
|     get_file_name(filename, sizeof(filename), path); | ||||
|  | ||||
|     switch (f_num) { | ||||
|     case TOX_FAERR_TOOLONG: | ||||
|         wprintw(window, "Message is too long.\n"); | ||||
|         break; | ||||
|     case TOX_FAERR_NOMESSAGE: | ||||
|         wprintw(window, "Please add a message to your request.\n"); | ||||
|         break; | ||||
|     case TOX_FAERR_OWNKEY: | ||||
|         wprintw(window, "That appears to be your own ID.\n"); | ||||
|         break; | ||||
|     case TOX_FAERR_ALREADYSENT: | ||||
|         wprintw(window, "Friend request has already been sent.\n"); | ||||
|         break; | ||||
|     case TOX_FAERR_UNKNOWN: | ||||
|         wprintw(window, "Undefined error when adding friend.\n"); | ||||
|         break; | ||||
|     case TOX_FAERR_BADCHECKSUM: | ||||
|         wprintw(window, "Bad checksum in address.\n"); | ||||
|         break; | ||||
|     case TOX_FAERR_SETNEWNOSPAM: | ||||
|         wprintw(window, "Nospam was different (is this contact already added?)\n"); | ||||
|         break; | ||||
|     default: | ||||
|         wprintw(window, "Friend request sent.\n"); | ||||
|         on_friendadded(m, f_num, true); | ||||
|         break; | ||||
|     if (avatar_set(m, path, len) == -1) { | ||||
|         line_info_add(self, false, 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, false, 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]) | ||||
| { | ||||
|     wclear(window); | ||||
|     wprintw(window, "\n\n"); | ||||
|     UNUSED_VAR(m); | ||||
|     UNUSED_VAR(argc); | ||||
|     UNUSED_VAR(argv); | ||||
|  | ||||
|     line_info_clear(self->chatwin->hst); | ||||
|     force_refresh(window); | ||||
| } | ||||
|  | ||||
| void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     /* check arguments */ | ||||
|     UNUSED_VAR(window); | ||||
|  | ||||
|     if (argc != 3) { | ||||
|       wprintw(window, "Invalid syntax.\n"); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     tox_IP_Port dht; | ||||
|     char *ip = argv[1]; | ||||
|     char *port = argv[2]; | ||||
|     char *key = argv[3]; | ||||
|  | ||||
|     if (atoi(port) == 0) { | ||||
|         wprintw(window, "Invalid syntax.\n"); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Require: <ip> <port> <key>"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     uint8_t *binary_string = hex_string_to_bin(key); | ||||
|     tox_bootstrap_from_address(m, ip, TOX_ENABLE_IPV6_DEFAULT, | ||||
|                                htons(atoi(port)), binary_string); | ||||
|     free(binary_string); | ||||
|     const char *ip = argv[1]; | ||||
|     const char *port_str = argv[2]; | ||||
|     const char *ascii_key = argv[3]; | ||||
|  | ||||
|     long int port = strtol(port_str, NULL, 10); | ||||
|  | ||||
|     if (port <= 0 || port > MAX_PORT_RANGE) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid port."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     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, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid key."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     Tox_Err_Bootstrap err; | ||||
|     tox_bootstrap(m, ip, port, (uint8_t *) key_binary, &err); | ||||
|     tox_add_tcp_relay(m, ip, port, (uint8_t *) key_binary, &err); | ||||
|  | ||||
|     switch (err) { | ||||
|         case TOX_ERR_BOOTSTRAP_BAD_HOST: | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed: Invalid IP."); | ||||
|             break; | ||||
|  | ||||
|         case TOX_ERR_BOOTSTRAP_BAD_PORT: | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed: Invalid port."); | ||||
|             break; | ||||
|  | ||||
|         case TOX_ERR_BOOTSTRAP_NULL: | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed."); | ||||
|             break; | ||||
|  | ||||
|         default: | ||||
|             break; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     if (num_active_windows() >= MAX_WINDOWS_NUM) { | ||||
|         wattron(window, COLOR_PAIR(RED)); | ||||
|         wprintw(window, " * Warning: Too many windows are open.\n"); | ||||
|         wattron(window, COLOR_PAIR(RED)); | ||||
|     UNUSED_VAR(window); | ||||
|     UNUSED_VAR(m); | ||||
|  | ||||
|     if (argc < 1) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Request ID required."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     int groupnum = tox_add_groupchat(m); | ||||
|     long int req = strtol(argv[1], NULL, 10); | ||||
|  | ||||
|     if (groupnum == -1) { | ||||
|         wprintw(window, "Group chat instance failed to initialize.\n"); | ||||
|     if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req >= MAX_FRIEND_REQUESTS) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (init_groupchat_win(prompt, m, groupnum) == -1) { | ||||
|         wprintw(window, "Group chat window failed to initialize.\n"); | ||||
|         tox_del_groupchat(m, groupnum); | ||||
|     if (!FrndRequests.request[req].active) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     wprintw(window, "Group chat created as %d.\n", groupnum); | ||||
|     FrndRequests.request[req] = (struct friend_request) { | ||||
|         0 | ||||
|     }; | ||||
|  | ||||
|     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_conference(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|  | ||||
|     if (get_num_active_windows() >= MAX_WINDOWS_NUM) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (argc < 1) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Please specify conference type: text | audio"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     uint8_t type; | ||||
|  | ||||
|     if (!strcasecmp(argv[1], "audio")) { | ||||
|         type = TOX_CONFERENCE_TYPE_AV; | ||||
|     } else if (!strcasecmp(argv[1], "text")) { | ||||
|         type = TOX_CONFERENCE_TYPE_TEXT; | ||||
|     } else { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Valid conference types are: text | audio"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     uint32_t conferencenum = 0; | ||||
|  | ||||
|     if (type == TOX_CONFERENCE_TYPE_TEXT) { | ||||
|         Tox_Err_Conference_New err; | ||||
|  | ||||
|         conferencenum = tox_conference_new(m, &err); | ||||
|  | ||||
|         if (err != TOX_ERR_CONFERENCE_NEW_OK) { | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference instance failed to initialize (error %d)", err); | ||||
|             return; | ||||
|         } | ||||
|     } else if (type == TOX_CONFERENCE_TYPE_AV) { | ||||
| #ifdef AUDIO | ||||
|         conferencenum = toxav_add_av_groupchat(m, audio_conference_callback, NULL); | ||||
|  | ||||
|         if (conferencenum == (uint32_t) -1) { | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio conference instance failed to initialize"); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
| #else | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio support disabled by compile-time option."); | ||||
|         return; | ||||
| #endif | ||||
|     } | ||||
|  | ||||
|     if (init_conference_win(m, conferencenum, type, NULL, 0) == -1) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference window failed to initialize."); | ||||
|         tox_conference_delete(m, conferencenum, NULL); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| #ifdef AUDIO | ||||
|  | ||||
|     if (type == TOX_CONFERENCE_TYPE_AV) { | ||||
|         if (!init_conference_audio_input(m, conferencenum)) { | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio capture failed; use \"/audio on\" to try again."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| #endif | ||||
|  | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference [%d] created.", conferencenum); | ||||
| } | ||||
|  | ||||
| void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|  | ||||
|     const char *msg; | ||||
|     struct chatlog *log = self->chatwin->log; | ||||
|  | ||||
|     if (argc == 0) { | ||||
|         if (log->log_on) { | ||||
|             msg = "Logging for this window is ON; type \"/log off\" to disable. (Logs are not encrypted)"; | ||||
|         } else { | ||||
|             msg = "Logging for this window is OFF; type \"/log on\" to enable."; | ||||
|         } | ||||
|  | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, msg); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     const char *swch = argv[1]; | ||||
|  | ||||
|     if (!strcmp(swch, "1") || !strcmp(swch, "on")) { | ||||
|         msg = log_enable(log) == 0 ? "Logging enabled." : "Warning: Failed to enable log."; | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, msg); | ||||
|         return; | ||||
|     } else if (!strcmp(swch, "0") || !strcmp(swch, "off")) { | ||||
|         if (self->type == WINDOW_TYPE_CHAT) { | ||||
|             Friends.list[self->num].logging_on = false; | ||||
|         } | ||||
|  | ||||
|         log_disable(log); | ||||
|  | ||||
|         msg = "Logging disabled."; | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, msg); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     msg = "Invalid option. Use \"/log on\" and \"/log off\" to toggle logging."; | ||||
|     line_info_add(self, false, 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); | ||||
|     UNUSED_VAR(window); | ||||
|     UNUSED_VAR(argc); | ||||
|     UNUSED_VAR(argv); | ||||
|  | ||||
|     size_t i; | ||||
|     char id_string[TOX_ADDRESS_SIZE * 2 + 1]; | ||||
|     char bin_id[TOX_ADDRESS_SIZE]; | ||||
|     tox_self_get_address(m, (uint8_t *) bin_id); | ||||
|  | ||||
|     for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; ++i) { | ||||
|         char xx[3]; | ||||
|         snprintf(xx, sizeof(xx), "%02X", address[i] & 0xff); | ||||
|         strcat(id, xx); | ||||
|     } | ||||
|  | ||||
|     wprintw(window, "%s\n", id); | ||||
| } | ||||
|  | ||||
| void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     /* check arguments */ | ||||
|     if (argc < 1) { | ||||
|       wprintw(window, "Invalid name.\n"); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     uint8_t *nick = argv[1]; | ||||
|     int len = strlen(nick); | ||||
|  | ||||
|     if (nick[0] == '\"') { | ||||
|         ++nick; | ||||
|         len -= 2; | ||||
|         nick[len] = L'\0'; | ||||
|     } | ||||
|  | ||||
|     if (!valid_nick(nick)) { | ||||
|         wprintw(window, "Invalid name.\n"); | ||||
|     if (bin_id_to_string(bin_id, sizeof(bin_id), id_string, sizeof(id_string)) == -1) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to print ID."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (len > TOXIC_MAX_NAME_LENGTH) { | ||||
|         nick[TOXIC_MAX_NAME_LENGTH] = L'\0'; | ||||
|         len = TOXIC_MAX_NAME_LENGTH; | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", id_string); | ||||
| } | ||||
|  | ||||
| #ifdef QRCODE | ||||
| void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|  | ||||
|     char id_string[TOX_ADDRESS_SIZE * 2 + 1]; | ||||
|     char bin_id[TOX_ADDRESS_SIZE]; | ||||
|     tox_self_get_address(m, (uint8_t *) bin_id); | ||||
|  | ||||
|     if (bin_id_to_string(bin_id, sizeof(bin_id), id_string, sizeof(id_string)) == -1) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     tox_set_name(m, nick, len+1); | ||||
|     prompt_update_nick(prompt, nick, len+1); | ||||
|     char nick[TOX_MAX_NAME_LENGTH]; | ||||
|     tox_self_get_name(m, (uint8_t *) nick); | ||||
|     size_t nick_len = tox_self_get_name_size(m); | ||||
|     nick[nick_len] = '\0'; | ||||
|  | ||||
|     size_t data_file_len = strlen(DATA_FILE); | ||||
|     char *dir = malloc(data_file_len + 1); | ||||
|  | ||||
|     if (dir == NULL) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code: Out of memory."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     size_t dir_len = get_base_dir(DATA_FILE, data_file_len, dir); | ||||
|  | ||||
| #ifdef QRPNG | ||||
|  | ||||
|     if (argc == 0) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Required 'txt' or 'png'"); | ||||
|         free(dir); | ||||
|         return; | ||||
|     } else if (!strcmp(argv[1], "txt")) { | ||||
|  | ||||
| #endif /* QRPNG */ | ||||
|         size_t qr_path_buf_size = dir_len + nick_len + sizeof(QRCODE_FILENAME_EXT); | ||||
|         char *qr_path = malloc(qr_path_buf_size); | ||||
|  | ||||
|         if (qr_path == NULL) { | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code: Out of memory"); | ||||
|             free(dir); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         snprintf(qr_path, qr_path_buf_size, "%s%s%s", dir, nick, QRCODE_FILENAME_EXT); | ||||
|  | ||||
|         if (ID_to_QRcode_txt(id_string, qr_path) == -1) { | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code."); | ||||
|             free(dir); | ||||
|             free(qr_path); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "QR code has been printed to the file '%s'", qr_path); | ||||
|  | ||||
|         free(qr_path); | ||||
|  | ||||
| #ifdef QRPNG | ||||
|     } else if (!strcmp(argv[1], "png")) { | ||||
|         size_t qr_path_buf_size = dir_len + nick_len + sizeof(QRCODE_FILENAME_EXT_PNG); | ||||
|         char *qr_path = malloc(qr_path_buf_size); | ||||
|  | ||||
|         if (qr_path == NULL) { | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code: Out of memory"); | ||||
|             free(dir); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         snprintf(qr_path, qr_path_buf_size, "%s%s%s", dir, nick, QRCODE_FILENAME_EXT_PNG); | ||||
|  | ||||
|         if (ID_to_QRcode_png(id_string, qr_path) == -1) { | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code."); | ||||
|             free(dir); | ||||
|             free(qr_path); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "QR code has been printed to the file '%s'", qr_path); | ||||
|  | ||||
|         free(qr_path); | ||||
|  | ||||
|     } else { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Unknown option '%s' -- Required 'txt' or 'png'", argv[1]); | ||||
|         free(dir); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| #endif /* QRPNG */ | ||||
|  | ||||
|     free(dir); | ||||
| } | ||||
| #endif /* QRCODE */ | ||||
|  | ||||
| void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|  | ||||
|     if (argc < 1) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Input required."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     char nick[MAX_STR_SIZE]; | ||||
|     snprintf(nick, sizeof(nick), "%s", argv[1]); | ||||
|     size_t len = strlen(nick); | ||||
|  | ||||
|     if (!valid_nick(nick)) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid name."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1); | ||||
|     nick[len] = '\0'; | ||||
|  | ||||
|     tox_self_set_name(m, (uint8_t *) nick, len, NULL); | ||||
|     prompt_update_nick(prompt, nick); | ||||
|  | ||||
|     store_data(m, DATA_FILE); | ||||
| } | ||||
|  | ||||
| void cmd_note(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|  | ||||
|     if (argc < 1) { | ||||
|         wprintw(window, "Wrong number of arguments.\n"); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Input required."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     uint8_t *msg = argv[1]; | ||||
|     prompt_update_statusmessage(prompt, m, argv[1]); | ||||
| } | ||||
|  | ||||
|     if (msg[0] != '\"') { | ||||
|         wprintw(window, "Note must be enclosed in quotes.\n"); | ||||
|         return; | ||||
| void cmd_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     long int nospam = rand(); | ||||
|  | ||||
|     if (argc > 0) { | ||||
|         nospam = strtol(argv[1], NULL, 16); | ||||
|  | ||||
|         if ((nospam == 0 && strcmp(argv[1], "0")) || nospam < 0) { | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid nospam value."); | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     msg[strlen(++msg)-1] = L'\0'; | ||||
|     uint16_t len = strlen(msg) + 1; | ||||
|     tox_set_status_message(m, msg, len); | ||||
|     uint32_t old_nospam = tox_self_get_nospam(m); | ||||
|     tox_self_set_nospam(m, (uint32_t) nospam); | ||||
|  | ||||
|     prompt_update_statusmessage(prompt, msg, len); | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Your new Tox ID is:"); | ||||
|     cmd_myid(window, self, m, 0, NULL); | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, ""); | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, | ||||
|                   "Any services that relied on your old ID will need to be updated manually."); | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "If you ever want your old Tox ID back, type '/nospam %X'", | ||||
|                   old_nospam); | ||||
| } | ||||
|  | ||||
| void cmd_prompt_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     wclear(window); | ||||
|     wattron(window, COLOR_PAIR(CYAN) | A_BOLD); | ||||
|     wprintw(window, "\n\nGlobal commands:\n"); | ||||
|     wattroff(window, COLOR_PAIR(CYAN) | A_BOLD); | ||||
|     UNUSED_VAR(window); | ||||
|     UNUSED_VAR(m); | ||||
|     UNUSED_VAR(argc); | ||||
|     UNUSED_VAR(argv); | ||||
|  | ||||
|     wprintw(window, "      /add <id> <msg>            : Add friend with optional message\n"); | ||||
|     wprintw(window, "      /accept <n>                : Accept friend request\n"); | ||||
|     wprintw(window, "      /connect <ip> <port> <key> : Manually connect to a DHT server\n"); | ||||
|     wprintw(window, "      /status <type> <msg>       : Set your status with optional note\n"); | ||||
|     wprintw(window, "      /note <msg>                : Set a personal note\n"); | ||||
|     wprintw(window, "      /nick <nick>               : Set your nickname\n"); | ||||
|     wprintw(window, "      /groupchat                 : Create a group chat\n"); | ||||
|     wprintw(window, "      /myid                      : Print your ID\n"); | ||||
|     wprintw(window, "      /quit or /exit             : Exit Toxic\n"); | ||||
|     wprintw(window, "      /help                      : Print this message again\n"); | ||||
|     wprintw(window, "      /clear                     : Clear the window\n"); | ||||
|  | ||||
|     wattron(window, COLOR_PAIR(CYAN) | A_BOLD); | ||||
|     wprintw(window, " * Argument messages must be enclosed in quotation marks.\n"); | ||||
|     wprintw(window, " * Use ctrl-o and ctrl-p to navigate through the tabs.\n\n"); | ||||
|     wattroff(window, COLOR_PAIR(CYAN) | A_BOLD); | ||||
|     help_init_menu(self); | ||||
| } | ||||
|  | ||||
| void cmd_quit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     exit_toxic(m); | ||||
|     UNUSED_VAR(window); | ||||
|     UNUSED_VAR(argc); | ||||
|     UNUSED_VAR(argv); | ||||
|     UNUSED_VAR(self); | ||||
|  | ||||
|     exit_toxic_success(m); | ||||
| } | ||||
|  | ||||
| void cmd_requests(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     UNUSED_VAR(window); | ||||
|     UNUSED_VAR(m); | ||||
|     UNUSED_VAR(argc); | ||||
|     UNUSED_VAR(argv); | ||||
|  | ||||
|     if (FrndRequests.num_requests == 0) { | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending friend requests."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     int i, j; | ||||
|     int count = 0; | ||||
|  | ||||
|     for (i = 0; i < FrndRequests.max_idx; ++i) { | ||||
|         if (!FrndRequests.request[i].active) { | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         char id[TOX_PUBLIC_KEY_SIZE * 2 + 1] = {0}; | ||||
|  | ||||
|         for (j = 0; j < TOX_PUBLIC_KEY_SIZE; ++j) { | ||||
|             char d[3]; | ||||
|             snprintf(d, sizeof(d), "%02X", FrndRequests.request[i].key[j] & 0xff); | ||||
|             strcat(id, d); | ||||
|         } | ||||
|  | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%d : %s", i, id); | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", FrndRequests.request[i].msg); | ||||
|  | ||||
|         if (++count < FrndRequests.num_requests) { | ||||
|             line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, ""); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) | ||||
| { | ||||
|     uint8_t *msg = NULL; | ||||
|     UNUSED_VAR(window); | ||||
|  | ||||
|     if (argc >= 2) { | ||||
|         msg = argv[2]; | ||||
|     const char *errmsg; | ||||
|  | ||||
|         if (msg[0] != '\"') { | ||||
|             wprintw(window, "Note must be enclosed in quotes.\n"); | ||||
|             return; | ||||
|         } | ||||
|     } else if (argc != 1) { | ||||
|         wprintw(window, "Wrong number of arguments.\n"); | ||||
|         return; | ||||
|     lock_status(); | ||||
|  | ||||
|     if (argc < 1) { | ||||
|         errmsg = "Require a status. Statuses are: online, busy and away."; | ||||
|         line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, errmsg); | ||||
|         goto finish; | ||||
|     } | ||||
|  | ||||
|     char *status = argv[1]; | ||||
|     int len = strlen(status); | ||||
|     char l_status[len+1]; | ||||
|     int i; | ||||
|     const char *status_str = argv[1]; | ||||
|     Tox_User_Status status; | ||||
|  | ||||
|     for (i = 0; i <= len; ++i) | ||||
|         l_status[i] = tolower(status[i]); | ||||
|  | ||||
|     TOX_USERSTATUS status_kind; | ||||
|  | ||||
|     if (!strcmp(l_status, "online")) | ||||
|         status_kind = TOX_USERSTATUS_NONE; | ||||
|     else if (!strcmp(l_status, "away")) | ||||
|         status_kind = TOX_USERSTATUS_AWAY; | ||||
|     else if (!strcmp(l_status, "busy")) | ||||
|         status_kind = TOX_USERSTATUS_BUSY; | ||||
|     else { | ||||
|         wprintw(window, "Invalid status. Valid statuses are: online, busy and away.\n"); | ||||
|         return; | ||||
|     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, false, NULL, NULL, SYS_MSG, 0, 0, errmsg); | ||||
|         goto finish; | ||||
|     } | ||||
|  | ||||
|     tox_set_user_status(m, status_kind); | ||||
|     prompt_update_status(prompt, status_kind); | ||||
|     tox_self_set_status(m, status); | ||||
|     prompt_update_status(prompt, status); | ||||
|     line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Your status has been changed to %s.", status_str); | ||||
|  | ||||
|     if (msg != NULL) { | ||||
|         msg[strlen(++msg)-1] = L'\0';   /* remove opening and closing quotes */ | ||||
|         uint16_t len = strlen(msg) + 1; | ||||
|         tox_set_status_message(m, msg, len); | ||||
|         prompt_update_statusmessage(prompt, msg, len); | ||||
|     } | ||||
|  | ||||
| finish: | ||||
|     unlock_status(); | ||||
| } | ||||
|   | ||||
| @@ -1,15 +1,65 @@ | ||||
| /* | ||||
|  * Toxic -- Tox Curses Client | ||||
| /*  global_commands.h | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||
|  * | ||||
|  *  This file is part of Toxic. | ||||
|  * | ||||
|  *  Toxic is free software: you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation, either version 3 of the License, or | ||||
|  *  (at your option) any later version. | ||||
|  * | ||||
|  *  Toxic is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #ifndef GLOBAL_COMMANDS_H | ||||
| #define GLOBAL_COMMANDS_H | ||||
|  | ||||
| #include "toxic.h" | ||||
| #include "windows.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_groupchat(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_conference(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]); | ||||
| #ifdef QRCODE | ||||
| void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| #endif /* QRCODE */ | ||||
| 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_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_prompt_help(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_quit(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_requests(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_status(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
|  | ||||
| void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg); | ||||
|  | ||||
| #ifdef AUDIO | ||||
| void cmd_list_devices(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_change_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| #endif /* AUDIO */ | ||||
|  | ||||
| #ifdef VIDEO | ||||
| void cmd_list_video_devices(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| void cmd_change_video_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| #endif /* VIDEO */ | ||||
|  | ||||
| #ifdef PYTHON | ||||
| void cmd_run(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); | ||||
| #endif /* PYTHON */ | ||||
|  | ||||
| #endif /* GLOBAL_COMMANDS_H */ | ||||
|   | ||||
							
								
								
									
										591
									
								
								src/groupchat.c
									
									
									
									
									
								
							
							
						
						
									
										591
									
								
								src/groupchat.c
									
									
									
									
									
								
							| @@ -1,591 +0,0 @@ | ||||
| /* | ||||
|  * Toxic -- Tox Curses Client | ||||
|  */ | ||||
|  | ||||
| #ifdef HAVE_CONFIG_H | ||||
| #include "config.h" | ||||
| #endif | ||||
|  | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <time.h> | ||||
|  | ||||
| #include "toxic_windows.h" | ||||
| #include "execute.h" | ||||
| #include "misc_tools.h" | ||||
| #include "groupchat.h" | ||||
| #include "prompt.h" | ||||
| #include "toxic_strings.h" | ||||
|  | ||||
| extern char *DATA_FILE; | ||||
| extern int store_data(Tox *m, char *path); | ||||
|  | ||||
| static GroupChat groupchats[MAX_WINDOWS_NUM]; | ||||
| static int max_groupchat_index = 0; | ||||
|  | ||||
| /* temporary until group chats have unique commands */ | ||||
| extern glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE]; | ||||
|  | ||||
| int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum) | ||||
| { | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i <= max_groupchat_index; ++i) { | ||||
|         if (!groupchats[i].active) { | ||||
|             groupchats[i].chatwin = add_window(m, new_group_chat(m, groupnum)); | ||||
|             groupchats[i].active = true; | ||||
|             groupchats[i].num_peers = 0; | ||||
|             groupchats[i].peer_names = malloc(sizeof(uint8_t) * TOX_MAX_NAME_LENGTH); | ||||
|             groupchats[i].oldpeer_names = malloc(sizeof(uint8_t) * TOX_MAX_NAME_LENGTH); | ||||
|  | ||||
|             /* temp fix */ | ||||
|             memcpy(&groupchats[i].oldpeer_names[0], UNKNOWN_NAME, sizeof(UNKNOWN_NAME)); | ||||
|  | ||||
|             set_active_window(groupchats[i].chatwin); | ||||
|  | ||||
|             if (i == max_groupchat_index) | ||||
|                 ++max_groupchat_index; | ||||
|  | ||||
|             return 0; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| static void close_groupchatwin(Tox *m, int groupnum) | ||||
| { | ||||
|     tox_del_groupchat(m, groupnum); | ||||
|  | ||||
|     free(groupchats[groupnum].peer_names); | ||||
|     free(groupchats[groupnum].oldpeer_names); | ||||
|     memset(&groupchats[groupnum], 0, sizeof(GroupChat)); | ||||
|  | ||||
|     int i; | ||||
|  | ||||
|     for (i = max_groupchat_index; i > 0; --i) { | ||||
|         if (groupchats[i-1].active) | ||||
|             break; | ||||
|     } | ||||
|  | ||||
|     max_groupchat_index = i; | ||||
| } | ||||
|  | ||||
| static void print_groupchat_help(ChatContext *ctx) | ||||
| { | ||||
|     wattron(ctx->history, COLOR_PAIR(CYAN) | A_BOLD); | ||||
|     wprintw(ctx->history, "Group chat commands:\n"); | ||||
|     wattroff(ctx->history, COLOR_PAIR(CYAN) | A_BOLD); | ||||
|  | ||||
|     wprintw(ctx->history, "      /add <id> <msg>     : Add friend with optional message\n"); | ||||
|     wprintw(ctx->history, "      /status <type> <msg>: Set your status with optional note\n"); | ||||
|     wprintw(ctx->history, "      /note <msg>         : Set a personal note\n"); | ||||
|     wprintw(ctx->history, "      /nick <nick>        : Set your nickname\n"); | ||||
|     wprintw(ctx->history, "      /groupchat          : Create a group chat\n"); | ||||
|     wprintw(ctx->history, "      /myid               : Print your ID\n"); | ||||
|     wprintw(ctx->history, "      /clear              : Clear the screen\n"); | ||||
|     wprintw(ctx->history, "      /close              : Close the current group chat\n"); | ||||
|     wprintw(ctx->history, "      /quit or /exit      : Exit Toxic\n"); | ||||
|     wprintw(ctx->history, "      /help               : Print this message again\n"); | ||||
|      | ||||
|     wattron(ctx->history, COLOR_PAIR(CYAN) | A_BOLD); | ||||
|     wprintw(ctx->history, " * Argument messages must be enclosed in quotation marks.\n"); | ||||
|     wprintw(ctx->history, " * Scroll peer list with the Page Up/Page Down keys.\n\n"); | ||||
|     wattroff(ctx->history, COLOR_PAIR(CYAN) | A_BOLD); | ||||
| } | ||||
|  | ||||
| static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, int groupnum, int peernum, | ||||
|                                      uint8_t *msg, uint16_t len) | ||||
| { | ||||
|     if (self->num != groupnum) | ||||
|         return; | ||||
|  | ||||
|     ChatContext *ctx = self->chatwin; | ||||
|  | ||||
|     uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'}; | ||||
|     tox_group_peername(m, groupnum, peernum, nick); | ||||
|     nick[TOXIC_MAX_NAME_LENGTH] = '\0';    /* enforce client max name length */ | ||||
|  | ||||
|     /* check if message contains own name and alert appropriately */ | ||||
|     int alert_type = WINDOW_ALERT_1; | ||||
|     bool beep = false; | ||||
|  | ||||
|     uint8_t selfnick[TOX_MAX_NAME_LENGTH] = {'\0'}; | ||||
|     tox_get_self_name(m, selfnick, TOX_MAX_NAME_LENGTH); | ||||
|     int nick_clr = strcmp(nick, selfnick) == 0 ? GREEN : CYAN; | ||||
|  | ||||
|     bool nick_match = strcasestr(msg, selfnick) && strncmp(selfnick, nick, TOXIC_MAX_NAME_LENGTH); | ||||
|  | ||||
|     if (nick_match) { | ||||
|         alert_type = WINDOW_ALERT_0; | ||||
|         beep = true; | ||||
|         nick_clr = RED; | ||||
|     } | ||||
|  | ||||
|     alert_window(self, alert_type, beep); | ||||
|  | ||||
|     print_time(ctx->history); | ||||
|     wattron(ctx->history, COLOR_PAIR(nick_clr)); | ||||
|     wprintw(ctx->history, "%s: ", nick); | ||||
|     wattroff(ctx->history, COLOR_PAIR(nick_clr)); | ||||
|      | ||||
|     if (msg[0] == '>') { | ||||
|         wattron(ctx->history, COLOR_PAIR(GREEN)); | ||||
|         wprintw(ctx->history, "%s\n", msg); | ||||
|         wattroff(ctx->history, COLOR_PAIR(GREEN)); | ||||
|     } else { | ||||
|         wprintw(ctx->history, "%s\n", msg); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void groupchat_onGroupAction(ToxWindow *self, Tox *m, int groupnum, int peernum, uint8_t *action, | ||||
|                                     uint16_t len) | ||||
| { | ||||
|     if (self->num != groupnum) | ||||
|         return; | ||||
|  | ||||
|     ChatContext *ctx = self->chatwin; | ||||
|  | ||||
|     /* check if message contains own name and alert appropriately */ | ||||
|     int alert_type = WINDOW_ALERT_1; | ||||
|     bool beep = false; | ||||
|  | ||||
|     uint8_t selfnick[TOX_MAX_NAME_LENGTH] = {'\0'}; | ||||
|     tox_get_self_name(m, selfnick, TOX_MAX_NAME_LENGTH); | ||||
|  | ||||
|     bool nick_match = strcasestr(action, selfnick); | ||||
|  | ||||
|     if (nick_match) { | ||||
|         alert_type = WINDOW_ALERT_0; | ||||
|         beep = true; | ||||
|     } | ||||
|  | ||||
|     alert_window(self, alert_type, beep); | ||||
|  | ||||
|     uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'}; | ||||
|     tox_group_peername(m, groupnum, peernum, nick); | ||||
|     nick[TOXIC_MAX_NAME_LENGTH] = '\0';    /* enforce client max name length */ | ||||
|  | ||||
|     print_time(ctx->history); | ||||
|     wattron(ctx->history, COLOR_PAIR(YELLOW)); | ||||
|     wprintw(ctx->history, "* %s %s\n", nick, action); | ||||
|     wattroff(ctx->history, COLOR_PAIR(YELLOW)); | ||||
| } | ||||
|  | ||||
| /* Puts two copies of peerlist in chat instance */ | ||||
| static void copy_peernames(int gnum, int npeers, uint8_t tmp_peerlist[][TOX_MAX_NAME_LENGTH]) | ||||
| { | ||||
|     /* Assumes these are initiated in init_groupchat_win */ | ||||
|     free(groupchats[gnum].peer_names); | ||||
|     free(groupchats[gnum].oldpeer_names); | ||||
|  | ||||
|     int N = TOX_MAX_NAME_LENGTH; | ||||
|  | ||||
|     groupchats[gnum].peer_names = malloc(sizeof(uint8_t) * npeers * N); | ||||
|     groupchats[gnum].oldpeer_names = malloc(sizeof(uint8_t) * npeers * N); | ||||
|  | ||||
|     if (groupchats[gnum].peer_names == NULL || groupchats[gnum].oldpeer_names == NULL) { | ||||
|         endwin(); | ||||
|         fprintf(stderr, "malloc() failed. Aborting...\n"); | ||||
|         exit(EXIT_FAILURE); | ||||
|     } | ||||
|  | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < npeers; ++i) { | ||||
|         if (string_is_empty(tmp_peerlist[i])) { | ||||
|             memcpy(&groupchats[gnum].peer_names[i*N], UNKNOWN_NAME, sizeof(UNKNOWN_NAME)); | ||||
|         } else { | ||||
|             memcpy(&groupchats[gnum].peer_names[i*N], tmp_peerlist[i], N); | ||||
|             groupchats[gnum].peer_names[i*N+TOXIC_MAX_NAME_LENGTH] = '\0'; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     memcpy(groupchats[gnum].oldpeer_names, groupchats[gnum].peer_names, N*npeers); | ||||
| } | ||||
|  | ||||
| static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnum, int peernum, | ||||
|                                             uint8_t change) | ||||
| { | ||||
|     if (self->num != groupnum) | ||||
|         return; | ||||
|  | ||||
|     groupchats[groupnum].num_peers = tox_group_number_peers(m, groupnum); | ||||
|     int num_peers = groupchats[groupnum].num_peers; | ||||
|  | ||||
|     /* get old peer name before updating name list */ | ||||
|     uint8_t oldpeername[TOX_MAX_NAME_LENGTH] = {0}; | ||||
|  | ||||
|     if (change != TOX_CHAT_CHANGE_PEER_ADD) | ||||
|         memcpy(oldpeername, &groupchats[groupnum].oldpeer_names[peernum*TOX_MAX_NAME_LENGTH],  | ||||
|                sizeof(oldpeername)); | ||||
|  | ||||
|     /* Update name lists */ | ||||
|     uint8_t tmp_peerlist[num_peers][TOX_MAX_NAME_LENGTH]; | ||||
|     tox_group_get_names(m, groupnum, tmp_peerlist, num_peers); | ||||
|     copy_peernames(groupnum, num_peers, tmp_peerlist); | ||||
|  | ||||
|     /* get current peername then sort namelist */ | ||||
|     uint8_t peername[TOX_MAX_NAME_LENGTH] = {0}; | ||||
|     memcpy(peername, &groupchats[groupnum].peer_names[peernum*TOX_MAX_NAME_LENGTH], sizeof(peername)); | ||||
|  | ||||
|     qsort(groupchats[groupnum].peer_names, groupchats[groupnum].num_peers, TOX_MAX_NAME_LENGTH, qsort_strcasecmp_hlpr); | ||||
|  | ||||
|     ChatContext *ctx = self->chatwin; | ||||
|     print_time(ctx->history); | ||||
|  | ||||
|     switch (change) { | ||||
|     case TOX_CHAT_CHANGE_PEER_ADD: | ||||
|         wattron(ctx->history, COLOR_PAIR(GREEN)); | ||||
|         wattron(ctx->history, A_BOLD); | ||||
|         wprintw(ctx->history, "* %s", peername); | ||||
|         wattroff(ctx->history, A_BOLD); | ||||
|         wprintw(ctx->history, " has joined the room\n"); | ||||
|         wattroff(ctx->history, COLOR_PAIR(GREEN)); | ||||
|         break; | ||||
|     case TOX_CHAT_CHANGE_PEER_DEL: | ||||
|         wattron(ctx->history, A_BOLD); | ||||
|         wprintw(ctx->history, "* %s", oldpeername); | ||||
|         wattroff(ctx->history, A_BOLD); | ||||
|         wprintw(ctx->history, " has left the room\n"); | ||||
|  | ||||
|         if (groupchats[self->num].side_pos > 0) | ||||
|             --groupchats[self->num].side_pos; | ||||
|  | ||||
|         break; | ||||
|     case TOX_CHAT_CHANGE_PEER_NAME: | ||||
|         wattron(ctx->history, COLOR_PAIR(MAGENTA)); | ||||
|         wattron(ctx->history, A_BOLD); | ||||
|         wprintw(ctx->history, "* %s", oldpeername); | ||||
|         wattroff(ctx->history, A_BOLD); | ||||
|  | ||||
|         wprintw(ctx->history, " is now known as "); | ||||
|  | ||||
|         wattron(ctx->history, A_BOLD); | ||||
|         wprintw(ctx->history, "%s\n", peername); | ||||
|         wattroff(ctx->history, A_BOLD); | ||||
|         wattroff(ctx->history, COLOR_PAIR(MAGENTA)); | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     alert_window(self, WINDOW_ALERT_2, false); | ||||
| } | ||||
|  | ||||
| static void send_group_action(ToxWindow *self, ChatContext *ctx, Tox *m, uint8_t *action) { | ||||
|     if (action == NULL) { | ||||
|         wprintw(ctx->history, "Invalid syntax.\n"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     // uint8_t selfname[TOX_MAX_NAME_LENGTH]; | ||||
|     // tox_get_self_name(m, selfname, TOX_MAX_NAME_LENGTH); | ||||
|  | ||||
|     // print_time(ctx->history); | ||||
|     // wattron(ctx->history, COLOR_PAIR(YELLOW)); | ||||
|     // wprintw(ctx->history, "* %s %s\n", selfname, action); | ||||
|     // wattroff(ctx->history, COLOR_PAIR(YELLOW)); | ||||
|  | ||||
|     if (tox_group_action_send(m, self->num, action, strlen(action) + 1) == -1) { | ||||
|         wattron(ctx->history, COLOR_PAIR(RED)); | ||||
|         wprintw(ctx->history, " * Failed to send action\n"); | ||||
|         wattroff(ctx->history, COLOR_PAIR(RED)); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key) | ||||
| { | ||||
|     ChatContext *ctx = self->chatwin; | ||||
|  | ||||
|     int x, y, y2, x2; | ||||
|     getyx(self->window, y, x); | ||||
|     getmaxyx(self->window, y2, x2); | ||||
|     int cur_len = 0; | ||||
|  | ||||
|     if (key == 0x107 || key == 0x8 || key == 0x7f) {  /* BACKSPACE key: Remove character behind pos */ | ||||
|         if (ctx->pos > 0) { | ||||
|             cur_len = MAX(1, wcwidth(ctx->line[ctx->pos - 1])); | ||||
|             del_char_buf_bck(ctx->line, &ctx->pos, &ctx->len); | ||||
|  | ||||
|             if (x == 0) | ||||
|                 wmove(self->window, y-1, x2 - cur_len); | ||||
|             else | ||||
|                 wmove(self->window, y, x - cur_len); | ||||
|         } else { | ||||
|             beep(); | ||||
|         } | ||||
|     }  | ||||
|  | ||||
|     else if (key == KEY_DC) {      /* DEL key: Remove character at pos */ | ||||
|         if (ctx->pos != ctx->len) | ||||
|             del_char_buf_frnt(ctx->line, &ctx->pos, &ctx->len); | ||||
|         else | ||||
|             beep(); | ||||
|     } | ||||
|  | ||||
|     else if (key == T_KEY_DISCARD) {    /* CTRL-U: Delete entire line behind pos */ | ||||
|         if (ctx->pos > 0) { | ||||
|             discard_buf(ctx->line, &ctx->pos, &ctx->len); | ||||
|             wmove(self->window, y2 - CURS_Y_OFFSET, 0); | ||||
|         } else { | ||||
|             beep(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     else if (key == T_KEY_KILL) {    /* CTRL-K: Delete entire line in front of pos */ | ||||
|         if (ctx->pos != ctx->len) | ||||
|             kill_buf(ctx->line, &ctx->pos, &ctx->len); | ||||
|         else | ||||
|             beep(); | ||||
|     } | ||||
|  | ||||
|     else if (key == KEY_HOME) {    /* HOME key: Move cursor to beginning of line */ | ||||
|         if (ctx->pos > 0) { | ||||
|             ctx->pos = 0; | ||||
|             wmove(self->window, y2 - CURS_Y_OFFSET, 0); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     else if (key == KEY_END) {     /* END key: move cursor to end of line */ | ||||
|         if (ctx->pos != ctx->len) { | ||||
|             ctx->pos = ctx->len; | ||||
|             mv_curs_end(self->window, MAX(0, wcswidth(ctx->line, (CHATBOX_HEIGHT-1)*x2)), y2, x2); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     else if (key == KEY_LEFT) { | ||||
|         if (ctx->pos > 0) { | ||||
|             --ctx->pos; | ||||
|             cur_len = MAX(1, wcwidth(ctx->line[ctx->pos])); | ||||
|  | ||||
|             if (x == 0) | ||||
|                 wmove(self->window, y-1, x2 - cur_len); | ||||
|             else | ||||
|                 wmove(self->window, y, x - cur_len); | ||||
|         } else { | ||||
|             beep(); | ||||
|         } | ||||
|     }  | ||||
|  | ||||
|     else if (key == KEY_RIGHT) { | ||||
|         if (ctx->pos < ctx->len) { | ||||
|             cur_len = MAX(1, wcwidth(ctx->line[ctx->pos])); | ||||
|             ++ctx->pos; | ||||
|  | ||||
|             if (x == x2-1) | ||||
|                 wmove(self->window, y+1, 0); | ||||
|             else | ||||
|                 wmove(self->window, y, x + cur_len); | ||||
|         } else { | ||||
|             beep(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     else if (key == KEY_UP) {    /* fetches previous item in history */ | ||||
|         fetch_hist_item(ctx->line, &ctx->pos, &ctx->len, ctx->ln_history, ctx->hst_tot, | ||||
|                         &ctx->hst_pos, LN_HIST_MV_UP); | ||||
|         mv_curs_end(self->window, ctx->len, y2, x2); | ||||
|     } | ||||
|  | ||||
|     else if (key == KEY_DOWN) {    /* fetches next item in history */ | ||||
|         fetch_hist_item(ctx->line, &ctx->pos, &ctx->len, ctx->ln_history, ctx->hst_tot, | ||||
|                         &ctx->hst_pos, LN_HIST_MV_DWN); | ||||
|         mv_curs_end(self->window, ctx->len, y2, x2); | ||||
|     } | ||||
|  | ||||
|     else if (key == '\t') {    /* TAB key: completes peer name */ | ||||
|         if (ctx->len > 0) { | ||||
|             int diff; | ||||
|  | ||||
|             if ((ctx->line[0] != '/') || (ctx->line[1] == 'm' && ctx->line[2] == 'e')) | ||||
|                 diff = complete_line(ctx->line, &ctx->pos, &ctx->len, groupchats[self->num].peer_names,  | ||||
|                                      groupchats[self->num].num_peers, TOX_MAX_NAME_LENGTH); | ||||
|             else | ||||
|                 diff = complete_line(ctx->line, &ctx->pos, &ctx->len, glob_cmd_list, AC_NUM_GLOB_COMMANDS, | ||||
|                                      MAX_CMDNAME_SIZE); | ||||
|  | ||||
|             if (diff != -1) { | ||||
|                 if (x + diff > x2 - 1) { | ||||
|                     int ofst = (x + diff - 1) - (x2 - 1); | ||||
|                     wmove(self->window, y+1, ofst); | ||||
|                 } else { | ||||
|                     wmove(self->window, y, x+diff); | ||||
|                 } | ||||
|             } else { | ||||
|                 beep(); | ||||
|             } | ||||
|         } else { | ||||
|             beep(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* Scroll peerlist up and down one position if list overflows window */ | ||||
|     else if (key == KEY_NPAGE) { | ||||
|         int L = y2 - CHATBOX_HEIGHT - SDBAR_OFST; | ||||
|  | ||||
|         if (groupchats[self->num].side_pos < groupchats[self->num].num_peers - L) | ||||
|             ++groupchats[self->num].side_pos; | ||||
|     } | ||||
|  | ||||
|     else if (key == KEY_PPAGE) { | ||||
|         if (groupchats[self->num].side_pos > 0) | ||||
|             --groupchats[self->num].side_pos; | ||||
|     }  | ||||
|  | ||||
|     else | ||||
| #if HAVE_WIDECHAR | ||||
|     if (iswprint(key)) | ||||
| #else | ||||
|     if (isprint(key)) | ||||
| #endif | ||||
|     {   /* prevents buffer overflows and strange behaviour when cursor goes past the window */ | ||||
|         if ( (ctx->len < MAX_STR_SIZE-1) && (ctx->len < (x2 * (CHATBOX_HEIGHT - 1)-1)) ) { | ||||
|             add_char_to_buf(ctx->line, &ctx->pos, &ctx->len, key); | ||||
|  | ||||
|             if (x == x2-1) | ||||
|                 wmove(self->window, y+1, 0); | ||||
|             else | ||||
|                 wmove(self->window, y, x + MAX(1, wcwidth(key))); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* RETURN key: Execute command or print line */ | ||||
|     else if (key == '\n') { | ||||
|         uint8_t line[MAX_STR_SIZE]; | ||||
|  | ||||
|         if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) | ||||
|             memset(&line, 0, sizeof(line)); | ||||
|  | ||||
|         wclear(ctx->linewin); | ||||
|         wmove(self->window, y2 - CURS_Y_OFFSET, 0); | ||||
|         wclrtobot(self->window); | ||||
|         bool close_win = false; | ||||
|  | ||||
|         if (!string_is_empty(line)) | ||||
|             add_line_to_hist(ctx->line, ctx->len, ctx->ln_history, &ctx->hst_tot, &ctx->hst_pos); | ||||
|  | ||||
|         if (line[0] == '/') { | ||||
|             if (close_win = strcmp(line, "/close") == 0) { | ||||
|                 set_active_window(0); | ||||
|                 int groupnum = self->num; | ||||
|                 delwin(ctx->linewin); | ||||
|                 del_window(self); | ||||
|                 close_groupchatwin(m, groupnum); | ||||
|             } else if (strcmp(line, "/help") == 0) | ||||
|                 print_groupchat_help(ctx); | ||||
|               else if (strncmp(line, "/me ", strlen("/me ")) == 0) | ||||
|                 send_group_action(self, ctx, m, line + strlen("/me ")); | ||||
|               else | ||||
|                 execute(ctx->history, self, m, line, GROUPCHAT_COMMAND_MODE); | ||||
|         } else if (!string_is_empty(line)) { | ||||
|             if (tox_group_message_send(m, self->num, line, strlen(line) + 1) == -1) { | ||||
|                 wattron(ctx->history, COLOR_PAIR(RED)); | ||||
|                 wprintw(ctx->history, " * Failed to send message.\n"); | ||||
|                 wattroff(ctx->history, COLOR_PAIR(RED)); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (close_win) | ||||
|             free(ctx); | ||||
|         else | ||||
|             reset_buf(ctx->line, &ctx->pos, &ctx->len); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void groupchat_onDraw(ToxWindow *self, Tox *m) | ||||
| { | ||||
|     curs_set(1); | ||||
|     int x2, y2; | ||||
|     getmaxyx(self->window, y2, x2); | ||||
|  | ||||
|     ChatContext *ctx = self->chatwin; | ||||
|  | ||||
|     wclear(ctx->linewin); | ||||
|  | ||||
|     if (ctx->len > 0) { | ||||
|         uint8_t line[MAX_STR_SIZE]; | ||||
|  | ||||
|         if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) { | ||||
|             reset_buf(ctx->line, &ctx->pos, &ctx->len); | ||||
|             wmove(self->window, y2 - CURS_Y_OFFSET, 0); | ||||
|         } else { | ||||
|             mvwprintw(ctx->linewin, 1, 0, "%s", line); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     wclear(ctx->sidebar); | ||||
|     mvwhline(ctx->linewin, 0, 0, ACS_HLINE, x2); | ||||
|     mvwvline(ctx->sidebar, 0, 0, ACS_VLINE, y2-CHATBOX_HEIGHT); | ||||
|     mvwaddch(ctx->sidebar, y2-CHATBOX_HEIGHT, 0, ACS_BTEE);   | ||||
|  | ||||
|     int num_peers = groupchats[self->num].num_peers; | ||||
|  | ||||
|     wmove(ctx->sidebar, 0, 1); | ||||
|     wattron(ctx->sidebar, A_BOLD); | ||||
|     wprintw(ctx->sidebar, "Peers: %d\n", num_peers); | ||||
|     wattroff(ctx->sidebar, A_BOLD); | ||||
|  | ||||
|     mvwaddch(ctx->sidebar, 1, 0, ACS_LTEE); | ||||
|     mvwhline(ctx->sidebar, 1, 1, ACS_HLINE, SIDEBAR_WIDTH-1); | ||||
|  | ||||
|     int N = TOX_MAX_NAME_LENGTH; | ||||
|     int maxlines = y2 - SDBAR_OFST - CHATBOX_HEIGHT; | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < num_peers && i < maxlines; ++i) { | ||||
|         wmove(ctx->sidebar, i+2, 1); | ||||
|         int peer = i + groupchats[self->num].side_pos; | ||||
|  | ||||
|         /* truncate nick to fit in side panel without modifying list */ | ||||
|         uint8_t tmpnck[TOX_MAX_NAME_LENGTH]; | ||||
|         memcpy(tmpnck, &groupchats[self->num].peer_names[peer*N], SIDEBAR_WIDTH-2); | ||||
|         tmpnck[SIDEBAR_WIDTH-2] = '\0'; | ||||
|  | ||||
|         wprintw(ctx->sidebar, "%s\n", tmpnck); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void groupchat_onInit(ToxWindow *self, Tox *m) | ||||
| { | ||||
|     int x, y; | ||||
|     getmaxyx(self->window, y, x); | ||||
|  | ||||
|     ChatContext *ctx = self->chatwin; | ||||
|     ctx->history = subwin(self->window, y-CHATBOX_HEIGHT+1, x-SIDEBAR_WIDTH-1, 0, 0); | ||||
|     scrollok(ctx->history, 1); | ||||
|     ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x, y-CHATBOX_HEIGHT, 0); | ||||
|     ctx->sidebar = subwin(self->window, y-CHATBOX_HEIGHT+1, SIDEBAR_WIDTH, 0, x-SIDEBAR_WIDTH); | ||||
|  | ||||
|     print_groupchat_help(ctx); | ||||
|     wmove(self->window, y-CURS_Y_OFFSET, 0); | ||||
| } | ||||
|  | ||||
| ToxWindow new_group_chat(Tox *m, int groupnum) | ||||
| { | ||||
|     ToxWindow ret; | ||||
|     memset(&ret, 0, sizeof(ret)); | ||||
|  | ||||
|     ret.active = true; | ||||
|  | ||||
|     ret.onKey = &groupchat_onKey; | ||||
|     ret.onDraw = &groupchat_onDraw; | ||||
|     ret.onInit = &groupchat_onInit; | ||||
|     ret.onGroupMessage = &groupchat_onGroupMessage; | ||||
|     ret.onGroupNamelistChange = &groupchat_onGroupNamelistChange; | ||||
|     ret.onGroupAction = &groupchat_onGroupAction; | ||||
|  | ||||
|     snprintf(ret.name, sizeof(ret.name), "Room #%d", groupnum); | ||||
|  | ||||
|     ChatContext *chatwin = calloc(1, sizeof(ChatContext)); | ||||
|  | ||||
|     if (chatwin != NULL) | ||||
|         ret.chatwin = chatwin; | ||||
|     else { | ||||
|         endwin(); | ||||
|         fprintf(stderr, "calloc() failed. Aborting...\n"); | ||||
|         exit(EXIT_FAILURE); | ||||
|     } | ||||
|  | ||||
|     ret.num = groupnum; | ||||
|  | ||||
|     return ret; | ||||
| } | ||||
| @@ -1,18 +0,0 @@ | ||||
| /* | ||||
|  * Toxic -- Tox Curses Client | ||||
|  */ | ||||
|  | ||||
| #define SIDEBAR_WIDTH 16 | ||||
| #define SDBAR_OFST 2    /* Offset for the peer number box at the top of the statusbar */ | ||||
|  | ||||
| typedef struct { | ||||
|     int chatwin; | ||||
|     bool active; | ||||
|     int num_peers; | ||||
|     int side_pos;    /* current position of the sidebar - used for scrolling up and down */ | ||||
|     uint8_t *peer_names; | ||||
|     uint8_t *oldpeer_names; | ||||
| } GroupChat; | ||||
|  | ||||
| int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum); | ||||
| ToxWindow new_group_chat(Tox *m, int groupnum); | ||||
							
								
								
									
										472
									
								
								src/help.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										472
									
								
								src/help.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,472 @@ | ||||
| /*  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 "help.h" | ||||
| #include "misc_tools.h" | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| #ifdef PYTHON | ||||
| #include "api.h" | ||||
| #endif /* PYTHON */ | ||||
|  | ||||
| #ifdef PYTHON | ||||
| #define HELP_MENU_HEIGHT 10 | ||||
| #else | ||||
| #define HELP_MENU_HEIGHT 9 | ||||
| #endif /* PYTHON */ | ||||
| #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); | ||||
|  | ||||
|     *(self->help) = (struct Help) { | ||||
|         0 | ||||
|     }; | ||||
| } | ||||
|  | ||||
| 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); | ||||
|  | ||||
|     if (y2 <= 0 || x2 <= 0) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     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, " c"); | ||||
|     wattron(win, A_BOLD | COLOR_PAIR(BLUE)); | ||||
|     wprintw(win, "o"); | ||||
|     wattroff(win, A_BOLD | COLOR_PAIR(BLUE)); | ||||
|     wprintw(win, "nference commands\n"); | ||||
|  | ||||
| #ifdef PYTHON | ||||
|     wattron(win, A_BOLD | COLOR_PAIR(BLUE)); | ||||
|     wprintw(win, " p"); | ||||
|     wattroff(win, A_BOLD | COLOR_PAIR(BLUE)); | ||||
|     wprintw(win, "lugin commands\n"); | ||||
| #endif /* PYTHON */ | ||||
|  | ||||
|     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); | ||||
|     wnoutrefresh(win); | ||||
| } | ||||
|  | ||||
| static void help_draw_bottom_menu(WINDOW *win) | ||||
| { | ||||
|     int y2, x2; | ||||
|     getmaxyx(win, y2, x2); | ||||
|  | ||||
|     UNUSED_VAR(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, "  /nospam <value>            : Change part of your Tox ID to stop spam\n"); | ||||
|     wprintw(win, "  /log <on> or <off>         : Enable/disable logging\n"); | ||||
|     wprintw(win, "  /conference <type>         : Create a conference where type: text | audio\n"); | ||||
|     wprintw(win, "  /myid                      : Print your Tox ID\n"); | ||||
| #ifdef QRCODE | ||||
| #ifdef QRPNG | ||||
|     wprintw(win, "  /myqr <txt> or <png>       : Print your Tox ID's QR code to a file.\n"); | ||||
| #else | ||||
|     wprintw(win, "  /myqr                      : Print your Tox ID's QR code to a file.\n"); | ||||
| #endif /* QRPNG */ | ||||
| #endif /* QRCODE */ | ||||
|     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 */ | ||||
|  | ||||
| #ifdef VIDEO | ||||
|     wattron(win, A_BOLD); | ||||
|     wprintw(win, "\n Video:\n"); | ||||
|     wattroff(win, A_BOLD); | ||||
|  | ||||
|     wprintw(win, "  /lsvdev <type>             : List video devices where type: in|out\n"); | ||||
|     wprintw(win, "  /svdev <type> <id>         : Set active video device\n"); | ||||
| #endif /* VIDEO */ | ||||
|  | ||||
| #ifdef PYTHON | ||||
|     wattron(win, A_BOLD); | ||||
|     wprintw(win, "\n Scripting:\n"); | ||||
|     wattroff(win, A_BOLD); | ||||
|  | ||||
|     wprintw(win, "  /run <path>                : Load and run the script at path\n"); | ||||
| #endif /* PYTHON */ | ||||
|  | ||||
|     help_draw_bottom_menu(win); | ||||
|  | ||||
|     box(win, ACS_VLINE, ACS_HLINE); | ||||
|     wnoutrefresh(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 conference \n"); | ||||
|     wprintw(win, "  /join                      : Join a pending conference\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"); | ||||
|     wprintw(win, "  /bitrate <n>               : Set the audio encoding bitrate\n"); | ||||
| #endif /* AUDIO */ | ||||
|  | ||||
| #ifdef VIDEO | ||||
|     wattron(win, A_BOLD); | ||||
|     wprintw(win, "\n Video:\n"); | ||||
|     wattroff(win, A_BOLD); | ||||
|     wprintw(win, "  /res <width> <height>      : Set video resolution\n"); | ||||
|     wprintw(win, "  /vcall                     : Video call\n"); | ||||
|     wprintw(win, "  /video                     : Toggle video in call\n"); | ||||
| #endif /* VIDEO */ | ||||
|  | ||||
|     help_draw_bottom_menu(win); | ||||
|  | ||||
|     box(win, ACS_VLINE, ACS_HLINE); | ||||
|     wnoutrefresh(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+up and Ctrl+down     : Scroll peer list in conference\n"); | ||||
|     wprintw(win, "  Ctrl+B                    : Toggle the conference peerlist\n"); | ||||
|     wprintw(win, "  Ctrl+J                    : Insert new line\n"); | ||||
|     wprintw(win, "  Ctrl+T                    : Toggle paste mode\n\n"); | ||||
|     wprintw(win, "  (Note: Custom keybindings override these defaults.)\n\n"); | ||||
|  | ||||
|     help_draw_bottom_menu(win); | ||||
|  | ||||
|     box(win, ACS_VLINE, ACS_HLINE); | ||||
|     wnoutrefresh(win); | ||||
| } | ||||
|  | ||||
| static void help_draw_conference(ToxWindow *self) | ||||
| { | ||||
|     WINDOW *win = self->help->win; | ||||
|  | ||||
|     wmove(win, 1, 1); | ||||
|  | ||||
|     wattron(win, A_BOLD | COLOR_PAIR(RED)); | ||||
|     wprintw(win, "Conference commands:\n"); | ||||
|     wattroff(win, A_BOLD | COLOR_PAIR(RED)); | ||||
|  | ||||
|     wprintw(win, "  /title <msg>               : Show/set conference title\n"); | ||||
| #ifdef AUDIO | ||||
|     wattron(win, A_BOLD); | ||||
|     wprintw(win, "\n Audio:\n"); | ||||
|     wattroff(win, A_BOLD); | ||||
|     wprintw(win, "  /audio <on> or <off>       : Enable/disable audio in an audio conference\n"); | ||||
|     wprintw(win, "  /mute                      : Toggle self audio mute status\n"); | ||||
|     wprintw(win, "  /mute <nick> or <pubkey>   : Toggle peer audio mute status\n"); | ||||
|     wprintw(win, "  /ptt <on> or <off>         : Toggle audio input Push-To-Talk (F2 to activate)\n"); | ||||
|     wprintw(win, "  /sense <n>                 : VAD sensitivity threshold\n\n"); | ||||
| #endif | ||||
|  | ||||
|     help_draw_bottom_menu(win); | ||||
|  | ||||
|     box(win, ACS_VLINE, ACS_HLINE); | ||||
|     wnoutrefresh(win); | ||||
| } | ||||
|  | ||||
| #ifdef PYTHON | ||||
| static void help_draw_plugin(ToxWindow *self) | ||||
| { | ||||
|     WINDOW *win = self->help->win; | ||||
|  | ||||
|     wmove(win, 1, 1); | ||||
|  | ||||
|     wattron(win, A_BOLD | COLOR_PAIR(RED)); | ||||
|     wprintw(win, "Plugin commands:\n"); | ||||
|     wattroff(win, A_BOLD | COLOR_PAIR(RED)); | ||||
|  | ||||
|     draw_handler_help(win); | ||||
|  | ||||
|     help_draw_bottom_menu(win); | ||||
|  | ||||
|     box(win, ACS_VLINE, ACS_HLINE); | ||||
|     wnoutrefresh(win); | ||||
| } | ||||
| #endif /* PYTHON */ | ||||
|  | ||||
| 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); | ||||
|     wnoutrefresh(win); | ||||
| } | ||||
|  | ||||
| void help_onKey(ToxWindow *self, wint_t key) | ||||
| { | ||||
|     int height; | ||||
|  | ||||
|     switch (key) { | ||||
|         case L'x': | ||||
|         case T_KEY_ESC: | ||||
|             help_exit(self); | ||||
|             break; | ||||
|  | ||||
|         case L'c': | ||||
| #ifdef VIDEO | ||||
|             help_init_window(self, 25, 80); | ||||
| #elif AUDIO | ||||
|             help_init_window(self, 20, 80); | ||||
| #else | ||||
|             help_init_window(self, 10, 80); | ||||
| #endif | ||||
|             self->help->type = HELP_CHAT; | ||||
|             break; | ||||
|  | ||||
|         case L'g': | ||||
|             height = 22; | ||||
| #ifdef VIDEO | ||||
|             height += 8; | ||||
| #elif AUDIO | ||||
|             height += 4; | ||||
| #endif | ||||
| #ifdef PYTHON | ||||
|             height += 2; | ||||
| #endif | ||||
|             help_init_window(self, height, 80); | ||||
|             self->help->type = HELP_GLOBAL; | ||||
|             break; | ||||
|  | ||||
|         case L'o': | ||||
|             height = 6; | ||||
| #ifdef AUDIO | ||||
|             height += 7; | ||||
| #endif | ||||
|             help_init_window(self, height, 80); | ||||
|             self->help->type = HELP_CONFERENCE; | ||||
|             break; | ||||
|  | ||||
| #ifdef PYTHON | ||||
|  | ||||
|         case L'p': | ||||
|             help_init_window(self, 4 + num_registered_handlers(), help_max_width()); | ||||
|             self->help->type = HELP_PLUGIN; | ||||
|             break; | ||||
| #endif /* PYTHON */ | ||||
|  | ||||
|         case L'f': | ||||
|             help_init_window(self, 10, 80); | ||||
|             self->help->type = HELP_CONTACTS; | ||||
|             break; | ||||
|  | ||||
|         case L'k': | ||||
|             help_init_window(self, 15, 80); | ||||
|             self->help->type = HELP_KEYS; | ||||
|             break; | ||||
|  | ||||
|         case L'm': | ||||
|             help_init_menu(self); | ||||
|             self->help->type = HELP_MENU; | ||||
|             break; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void help_onDraw(ToxWindow *self) | ||||
| { | ||||
|     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_CONFERENCE: | ||||
|             help_draw_conference(self); | ||||
|             break; | ||||
|  | ||||
| #ifdef PYTHON | ||||
|  | ||||
|         case HELP_PLUGIN: | ||||
|             help_draw_plugin(self); | ||||
|             break; | ||||
| #endif /* PYTHON */ | ||||
|     } | ||||
| } | ||||
							
								
								
									
										45
									
								
								src/help.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/help.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | ||||
| /*  help.h | ||||
|  * | ||||
|  * | ||||
|  *  Copyright (C) 2014 Toxic All Rights Reserved. | ||||
|  * | ||||
|  *  This file is part of Toxic. | ||||
|  * | ||||
|  *  Toxic is free software: you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU General Public License as published by | ||||
|  *  the Free Software Foundation, either version 3 of the License, or | ||||
|  *  (at your option) any later version. | ||||
|  * | ||||
|  *  Toxic is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU General Public License | ||||
|  *  along with Toxic.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #ifndef HELP_H | ||||
| #define HELP_H | ||||
|  | ||||
| #include "toxic.h" | ||||
| #include "windows.h" | ||||
|  | ||||
| typedef enum { | ||||
|     HELP_MENU, | ||||
|     HELP_GLOBAL, | ||||
|     HELP_CHAT, | ||||
|     HELP_CONFERENCE, | ||||
|     HELP_KEYS, | ||||
|     HELP_CONTACTS, | ||||
| #ifdef PYTHON | ||||
|     HELP_PLUGIN, | ||||
| #endif | ||||
| } HELP_TYPES; | ||||
|  | ||||
| void help_onDraw(ToxWindow *self); | ||||
| void help_init_menu(ToxWindow *self); | ||||
| void help_onKey(ToxWindow *self, wint_t key); | ||||
|  | ||||
| #endif /* HELP_H */ | ||||
							
								
								
									
										350
									
								
								src/input.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										350
									
								
								src/input.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,350 @@ | ||||
| /*  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 "conference.h" | ||||
| #include "line_info.h" | ||||
| #include "misc_tools.h" | ||||
| #include "notify.h" | ||||
| #include "settings.h" | ||||
| #include "toxic.h" | ||||
| #include "toxic_strings.h" | ||||
| #include "windows.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 mx_x) | ||||
| { | ||||
|     ChatContext *ctx = self->chatwin; | ||||
|  | ||||
|     /* this is the only place we need to do this check */ | ||||
|     if (key == '\n') { | ||||
|         key = L'¶'; | ||||
|     } | ||||
|  | ||||
|     int cur_len = wcwidth(key); | ||||
|  | ||||
|     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) | ||||
| { | ||||
|     ChatContext *ctx = self->chatwin; | ||||
|  | ||||
|     if (del_word_buf(ctx) == -1) { | ||||
|         sound_notify(self, notif_error, 0, NULL); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* 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 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; | ||||
|  | ||||
|     --ctx->pos; | ||||
|  | ||||
|     if (ctx->start > 0 && (x >= mx_x - cur_len)) { | ||||
|         int s_len = wcwidth(ctx->line[ctx->start - 1]); | ||||
|         ctx->start = MAX(0, ctx->start - 1 + (s_len - cur_len)); | ||||
|     } else if (ctx->start > 0) { | ||||
|         ctx->start = MAX(0, ctx->start - cur_len); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* moves the cursor to the beginning of the previous word in input field and buffer */ | ||||
| static void input_skip_left(ToxWindow *self, int x, int mx_x) | ||||
| { | ||||
|     ChatContext *ctx = self->chatwin; | ||||
|  | ||||
|     if (ctx->pos <= 0) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     int count = 0; | ||||
|  | ||||
|     do { | ||||
|         --ctx->pos; | ||||
|         count += wcwidth(ctx->line[ctx->pos]); | ||||
|     } while (ctx->pos > 0 && (ctx->line[ctx->pos - 1] != L' ' || ctx->line[ctx->pos] == L' ')); | ||||
|  | ||||
|     if (ctx->start > 0 && (x >= mx_x - count)) { | ||||
|         int s_len = wcwidth(ctx->line[ctx->start - 1]); | ||||
|         ctx->start = MAX(0, ctx->start - 1 + (s_len - count)); | ||||
|     } else if (ctx->start > 0) { | ||||
|         ctx->start = MAX(0, ctx->start - count); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* 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); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* moves the cursor to the end of the next word in input field and buffer */ | ||||
| static void input_skip_right(ToxWindow *self, int x, int mx_x) | ||||
| { | ||||
|     ChatContext *ctx = self->chatwin; | ||||
|  | ||||
|     if (ctx->pos >= ctx->len) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     int count = 0; | ||||
|  | ||||
|     do { | ||||
|         count += wcwidth(ctx->line[ctx->pos]); | ||||
|         ++ctx->pos; | ||||
|     } while (ctx->pos < ctx->len && !(ctx->line[ctx->pos] == L' ' && ctx->line[ctx->pos - 1] != L' ')); | ||||
|  | ||||
|     int newpos = x + count; | ||||
|  | ||||
|     if (newpos >= mx_x) { | ||||
|         ctx->start += (1 + (newpos - mx_x)); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* 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 mx_x) | ||||
| { | ||||
|     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); | ||||
|             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, 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; | ||||
|  | ||||
|         case T_KEY_C_LEFT: | ||||
|             input_skip_left(self, x, mx_x); | ||||
|             break; | ||||
|  | ||||
|         case T_KEY_C_RIGHT: | ||||
|             input_skip_right(self, x, mx_x); | ||||
|             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) { | ||||
|         if (key == user_settings->key_toggle_peerlist) { | ||||
|             if (self->type == WINDOW_TYPE_CONFERENCE) { | ||||
|                 self->show_peerlist ^= 1; | ||||
|                 redraw_conference_win(self); | ||||
|             } | ||||
|  | ||||
|             match = true; | ||||
|         } else if (key == user_settings->key_toggle_pastemode) { | ||||
|             self->chatwin->pastemode ^= 1; | ||||
|             match = true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return match; | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user